Ask Bjørn Hansen escribió:

On Feb 18, 2008, at 4:13 PM, Jared Johnson wrote:

Is there a good reason for the QP sender-permited-from plugin use Mail::SPF::Query rather than Mail::SPF along with using best-guess and trusted forwarder by default, or is it only because nobody has ported it to Mail::SPF?

I'm pretty sure it's just the latter.  Patches welcome!


 - ask


More than a patch, a new implementation, based on the original sender_permitted_from. We didn't want to break anything, and at least give people a chance to fall back to the old plugin ;)

This plugin should be backward compatible, so you just have to change the plugin name from 'sender_permitted_from' to 'spf' and you are running.

sender_permitted_from was using $query->result2() method, which is actually deprecated (see: http://search.cpan.org/~jmehnle/Mail-SPF-Query-1.999.1/lib/Mail/SPF/Query.pm). We haven't done any work on trying to emulate this behaviour, as it is not even recommended (should be done by other plugins).

Feedback is welcome.

Best Regards,

Jose Luis Martinez
[EMAIL PROTECTED]




=head1 NAME

SPF - plugin to implement Sender Permitted From using Mail::SPF

=head1 SYNOPSIS

This plugin is based on "sender_permitted_from", but it uses Mail::SPF instead 
of Mail::SPF::Query because the latest roughly conforms to the final SPFv1 
specification.
Please see http://www.openspf.org/Implementations for more information.

  # in config/plugins
  spf

Or if you wish to issue 5xx on SPF fail:

  spf [spf_deny n] [Mail::SPF::Server constructor args]

spf_deny 0 will not issue 5xx responses

spf_deny 1 will issue 5xx response on SPF FAIL

spf_deny 2 will issue 5xx response on SPF SOFTFAIL and FAIL

Other arguments are valid Mail::SPF::Server arguments.

  B<default_authority_explanation>
  
    Default: 'Please see 
http://www.openspf.org/Why?s=%{_scope}&id=%{S}&ip=%{C}&r=%{R}'
    
  B<hostname>
  
    A string denoting the local system's fully qualified host name that should 
be used for 
    expanding the r macro in explanation strings. Defaults to the system's 
configured host name.
    
  B<dns_resolver>
  
    An optional DNS resolver object. If none is specified, a new 
Net::DNS::Resolver 
    object is used. The resolver object may be of a different class, but it 
must 
    provide an interface similar to Net::DNS::Resolver -- at least the send and 
    errorstring methods must be supported, and the send method must return 
either 
    an object of class Net::DNS::Packet, or, in the case of an error, undef.
  
  ... 
  
  See http://search.cpan.org/~jmehnle/Mail-SPF-v2.005/lib/Mail/SPF/Server.pm 
  (or installed module version) for more information about parameters to the 
  Mail::SPF::Server constructor.   

See also http://www.openspf.org

=head1 DEPENDENCIES

You need B<Mail::SPF> perl module (available from CPAN) installed to use this 
module.
Please see http://search.cpan.org/~jmehnle/Mail-SPF-v2.005/ (or desired version)

=head1 AUTHORS

        Original sender_permitted_from author
        Sergi Pruneda <[EMAIL PROTECTED]>
        Jose Luis Martinez <[EMAIL PROTECTED]>

=cut

use Mail::SPF;

sub register {
  my ($self, $qp, @args) = @_;
  %{$self->{_args}} = @args;
}

sub hook_mail {
  my ($self, $transaction, $sender, %param) = @_;

  return (DECLINED) unless ($sender->format ne "<>"
                            and $sender->host && $sender->user);

  # If we are receving from a relay permitted host, then we are probably
  # not the delivery system, and so we shouldn't check

  return (DECLINED) if $self->qp->connection->relay_client();
  my @relay_clients = $self->qp->config("relayclients");
  my $more_relay_clients = $self->qp->config("morerelayclients", "map");
  my %relay_clients = map { $_ => 1 } @relay_clients;
  my $client_ip = $self->qp->connection->remote_ip;
  while ($client_ip) {
    return (DECLINED) if exists $relay_clients{$client_ip};
    return (DECLINED) if exists $more_relay_clients->{$client_ip};
    $client_ip =~ s/\d+\.?$//; # strip off another 8 bits
  }

  my $host = lc $sender->host;
  my $from = $sender->user . '@' . $host;

  my $ip = $self->qp->connection->remote_ip;
  my $helo = $self->qp->connection->hello_host;

  # We delete the plugin argument and use the rest for Mail::SPF::Server module
  my %spf_args = %{$self->{_args}};
  delete $spf_args{'spf_deny'};
  # Create a Mail::SPF::Server object to process a new request
  my $spf_server = Mail::SPF::Server->new(%spf_args);
  my $request = Mail::SPF::Request->new( versions      => [1,2],
                                         scope         => 'mfrom',
                                         identity      => $from,
                                         ip_address    => $ip,
                                         helo_identity => $helo );

  # Store Mail::SPF::Result in transaction notes
  $transaction->notes('spfquery', $spf_server->process($request));
               
  return (DECLINED);
}

sub hook_rcpt {
  my ($self, $transaction, $rcpt, %param) = @_;
  
  # special addresses don't get SPF-tested.
  return DECLINED if $rcpt and $rcpt->user and $rcpt->user =~ 
/^(?:postmaster|abuse|mailer-daemon|root)$/i;
  
  my $query = $transaction->notes('spfquery');
  return DECLINED if !$query;
  
  my $result = $query->code;
  my $smtp_comment;
  if($result eq 'fail') {
    $smtp_comment = $query->authority_explanation;
  } else {
    $smtp_comment = $query->local_explanation;
  }
  my $comment = $query->local_explanation;
  
  if ($result eq "error") {
    return (DENYSOFT, "SPF error: $smtp_comment");
  }

  if ($result eq "fail" and $self->{_args}{spf_deny}) {
    return (DENY, "SPF forgery: $smtp_comment");
  }

  if ($result eq "softfail" and $self->{_args}{spf_deny} > 1) {
    return (DENY, "SPF probable forgery: $smtp_comment");
  }

  if ($result eq 'fail' or $result eq 'softfail') {
    $self->log(LOGDEBUG, "result was $result: $comment");
  }
   
  return DECLINED;
}

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

  my $query = $transaction->notes('spfquery');
  return DECLINED if !$query;

  my $result = $query->code;
  my $smtp_comment;
  if($result eq 'fail') {
    $smtp_comment = $query->authority_explanation;
  } else {
    $smtp_comment = $query->local_explanation;
  }
  my $comment = $query->local_explanation;
  my $spf_header = $query->received_spf_header;

  $self->log(LOGINFO, "result was $result: $comment") if ($result);

  $transaction->header->add('Received-SPF' => "$spf_header", 0);

  return DECLINED;
}

Reply via email to