#!/usr/bin/perl

=head1 NAME

verify_not_forged - 
Check headers to make sure that mail can't get through SPF and TMDA when it
says it is coming from our host but it is really coming from some other
host.  This works with SPF to check messages to our host and make sure
mail is getting through the SPF check by sending a valid Return-Path:
address but spoof the From: address thereby getting passed TMDA.

=head1 DESCRIPTION

First, if we are relaying then DECLINE to other plugins.

If we don't have DATA or From: then DENY appropriately.

The main test is to check the "mail from (sender, Return-Path) host" and
the "From: header host" against rcpthosts.

If ( SenderHost ne rcpthost and FromHost eq rcpthost ) then DENY mail.


=head1 CONFIGURATION

None.

=head1 AUTHOR

Written by David Summers.

=head1 LICENSE

Released to the public domain, 2007-10-29.

=cut

use Mail::Field;

sub hook_data_post {
  my ($self, $transaction) = @_;

  # At this point we should have gotten past the SPF checks.
  # If we are relaying then decline and pass on to other plugins.

  if ( $self->qp->connection->relay_client( ) )
  {
    $self->log( LOGINFO, "verify_not_forged: Relaying: DECLINED" );
    return DECLINED;
  }

  # Check to make sure we have a sender address.
  my $sender = $transaction->sender;

  if ( ! $sender )
  {
    return ( DENY, "verify_not_forged: No sender address" );
  }

  # Check to make sure we have data.

  if ( $transaction->data_size == 0 )
  {
    return (DENY, "verify_not_forged: You have to send some data first")
  }

  # Check to make sure we have a From: address.
  my $fromheader = $transaction->header->get( 'From:' );
  $self->log( LOGDEBUG, "verify_not_forged: ORIG From: $fromheader" );

  my $fromaddresses = Mail::Field->new( 'From', $fromheader );
  my @fromaddresses = $fromaddresses->addresses();
  my $from = pop @fromaddresses;

  $self->log( LOGDEBUG, "verify_not_forged: PULL From: \"$from\"" );

  $from = Qpsmtpd::Address->new( "$from" );
  $self->log( LOGDEBUG, "verify_not_forged: ADDRESS From: \"$from\"" );

  if ( ! $from )
  {
    return (DENY, "verify_not_forged: Mail with no From: header not accepted "
      . "here" );
  }

  my $senderhost = $sender->host( );

  my $fromuser = $from->user( );
  my $fromhost = $from->host( );
  $self->log( LOGDEBUG, "verify_not_forged: fromuser=$fromuser "
    . "fromhost=$fromhost" );

  if ( ! $fromhost )
  {
    $self->log( LOGINFO, "verify_not_forged: no from host. DECLINED" );
    return DECLINED;
  }

  my $myhost;
  my @myhosts = $self->qp->config( 'rcpthosts' );
  #push @myhosts, $self->qp->config( 'me' );
  #my @locals = $self->qp->config( 'locals' );
  #push @myhosts, @locals;

  $self->log( LOGDEBUG, "verify_not_forged: myhost list=@myhosts" );

  for $myhost ( @myhosts )
  {
    $self->log( LOGDEBUG, "verify_not_forged: CHECKING: myhost=$myhost, "
      . "senderhost=$senderhost, fromhost=$fromhost" );

    if ( $fromhost eq $myhost and $senderhost ne $myhost )
    {
      $self->log( LOGDEBUG, "verify_not_forged: senderhost=$senderhost "
        . "NOT EQUAL myhost=$myhost AND fromhost=$fromhost EQUAL "
        . "myhost=$myhost: DENY" );
      return( DENY, "Can't send mail From: $from when it really came from "
        . "Return-path: $sender" );
    }
  }

  $self->log( LOGDEBUG, "verify_not_forged: NO PROBLEM: senderhost=$senderhost "
    . "EQUAL myhosts=@myhosts OR senderhost=$senderhost NOT EQUAL "
    . "fromhost=$fromhost: DECLINED" );

  return (DECLINED);
}
