add TCPLOCAL* variables to $qp->connection
(patch remade against latest rspier/qpsmtpd) added remote_port, local_ip, local_port, and local_host to $qp->connection, as the p0f plugin relies on it. added notes to TcpServer.pm and the p0f plugin noting the dependence, and the lack of support for models other than tcpserver. Signed-off-by: Robert <rspier@pobox.com>
This commit is contained in:
parent
0291260284
commit
671a6953b0
@ -30,7 +30,10 @@ my $first_0;
|
||||
sub start_connection {
|
||||
my $self = shift;
|
||||
|
||||
my ($remote_host, $remote_info, $remote_ip);
|
||||
my (
|
||||
$remote_host, $remote_info, $remote_ip, $remote_port,
|
||||
$local_ip, $local_port, $local_host
|
||||
);
|
||||
|
||||
if ($ENV{TCPREMOTEIP}) {
|
||||
# started from tcpserver (or some other superserver which
|
||||
@ -38,6 +41,10 @@ sub start_connection {
|
||||
$remote_ip = $ENV{TCPREMOTEIP};
|
||||
$remote_host = $ENV{TCPREMOTEHOST} || "[$remote_ip]";
|
||||
$remote_info = $ENV{TCPREMOTEINFO} ? "$ENV{TCPREMOTEINFO}\@$remote_host" : $remote_host;
|
||||
$remote_port = $ENV{TCPREMOTEPORT};
|
||||
$local_ip = $ENV{TCPLOCALIP};
|
||||
$local_port = $ENV{TCPLOCALPORT};
|
||||
$local_host = $ENV{TCPLOCALHOST};
|
||||
} else {
|
||||
# Started from inetd or similar.
|
||||
# get info on the remote host from the socket.
|
||||
@ -48,6 +55,10 @@ sub start_connection {
|
||||
$remote_ip = inet_ntoa($iaddr);
|
||||
$remote_host = gethostbyaddr($iaddr, AF_INET) || "[$remote_ip]";
|
||||
$remote_info = $remote_host;
|
||||
### TODO
|
||||
# set $remote_port, $local_ip, and $local_port. Those values are
|
||||
# required for the p0f plugin to function.
|
||||
### /TODO
|
||||
}
|
||||
$self->log(LOGNOTICE, "Connection from $remote_info [$remote_ip]");
|
||||
|
||||
@ -61,8 +72,12 @@ sub start_connection {
|
||||
$0 = "$first_0 [$remote_ip : $remote_host : $now]";
|
||||
|
||||
$self->SUPER::connection->start(remote_info => $remote_info,
|
||||
remote_ip => $remote_ip,
|
||||
remote_host => $remote_host,
|
||||
remote_ip => $remote_ip,
|
||||
remote_host => $remote_host,
|
||||
remote_port => $remote_port,
|
||||
local_ip => $local_ip,
|
||||
local_port => $local_port,
|
||||
local_host => $local_host,
|
||||
@_);
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,23 @@ directories, if determined, supercede I<db_dir>.
|
||||
|
||||
=back
|
||||
|
||||
=item p0f
|
||||
|
||||
Enable greylisting only when certain p0f criteria is met. The single
|
||||
required argument is a comma delimited list of key/value pairs. The keys
|
||||
are the following p0f TCP fingerprint elements: genre, detail, uptime,
|
||||
link, and distance.
|
||||
|
||||
To greylist emails from computers whose remote OS is windows, you'd use
|
||||
this syntax:
|
||||
|
||||
p0f genre,windows
|
||||
|
||||
To greylist only windows computers on DSL links more than 3 network hops
|
||||
away:
|
||||
|
||||
p0f genre,windows,link,dsl,distance,3
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Database locking is implemented using flock, which may not work on
|
||||
@ -116,6 +133,8 @@ use something like File::NFSLock instead.
|
||||
|
||||
Written by Gavin Carr <gavin@openfusion.com.au>.
|
||||
|
||||
Added p0f section <mattsimerson@cpan.org> (2010-05-03)
|
||||
|
||||
=cut
|
||||
|
||||
BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File NDBM_File) }
|
||||
@ -123,22 +142,23 @@ use AnyDBM_File;
|
||||
use Fcntl qw(:DEFAULT :flock);
|
||||
use strict;
|
||||
|
||||
my $VERSION = '0.07';
|
||||
my $VERSION = '0.08';
|
||||
|
||||
my $DENYMSG = "This mail is temporarily denied";
|
||||
my ($QPHOME) = ($0 =~ m!(.*?)/([^/]+)$!);
|
||||
my $DB = "denysoft_greylist.dbm";
|
||||
my %PERMITTED_ARGS = map { $_ => 1 } qw(per_recipient remote_ip sender recipient
|
||||
black_timeout grey_timeout white_timeout deny_late mode db_dir);
|
||||
black_timeout grey_timeout white_timeout deny_late mode db_dir p0f );
|
||||
|
||||
my %DEFAULTS = (
|
||||
remote_ip => 1,
|
||||
sender => 0,
|
||||
recipient => 0,
|
||||
black_timeout => 50 * 60,
|
||||
grey_timeout => 3 * 3600 + 20 * 60,
|
||||
white_timeout => 36 * 24 * 3600,
|
||||
mode => 'denysoft',
|
||||
remote_ip => 1,
|
||||
sender => 0,
|
||||
recipient => 0,
|
||||
black_timeout => 50 * 60,
|
||||
grey_timeout => 3 * 3600 + 20 * 60,
|
||||
white_timeout => 36 * 24 * 3600,
|
||||
mode => 'denysoft',
|
||||
p0f => undef,
|
||||
);
|
||||
|
||||
sub register {
|
||||
@ -206,6 +226,9 @@ sub denysoft_greylist {
|
||||
return DECLINED if $self->qp->connection->notes('whitelisthost');
|
||||
return DECLINED if $transaction->notes('whitelistsender');
|
||||
|
||||
# do not greylist if p0f matching is selected and message does not match
|
||||
return DECLINED if $config->{'p0f'} && !$self->p0f_match( $config );
|
||||
|
||||
if ($config->{db_dir} && $config->{db_dir} =~ m{^([-a-zA-Z0-9./_]+)$}) {
|
||||
$config->{db_dir} = $1;
|
||||
}
|
||||
@ -214,8 +237,10 @@ sub denysoft_greylist {
|
||||
my $dbdir = $transaction->notes('per_rcpt_configdir')
|
||||
if $config->{per_recipient_db};
|
||||
for my $d ($dbdir, $config->{db_dir}, "/var/lib/qpsmtpd/greylisting",
|
||||
"$QPHOME/var/db", "$QPHOME/config") {
|
||||
last if $dbdir ||= $d && -d $d && $d;
|
||||
"$QPHOME/var/db", "$QPHOME/config", '.' ) {
|
||||
last if $dbdir && -d $dbdir;
|
||||
next if ( ! $d || ! -d $d );
|
||||
$dbdir = $d;
|
||||
}
|
||||
my $db = "$dbdir/$DB";
|
||||
$self->log(LOGINFO,"using $db as greylisting database");
|
||||
@ -292,5 +317,26 @@ sub denysoft_greylist {
|
||||
return $config->{mode} eq 'testonly' ? DECLINED : DENYSOFT, $DENYMSG;
|
||||
}
|
||||
|
||||
sub p0f_match {
|
||||
my $self = shift;
|
||||
my $config = shift;
|
||||
|
||||
my $p0f = $self->connection->notes('p0f');
|
||||
return if !$p0f || !ref $p0f; # p0f fingerprint info not found
|
||||
|
||||
my %valid_matches = map { $_ => 1 } qw( genre detail uptime link distance );
|
||||
my %requested_matches = split(/\,/, $config->{'p0f'} );
|
||||
|
||||
foreach my $key (keys %requested_matches) {
|
||||
next if !defined $valid_matches{$key}; # discard invalid match keys
|
||||
my $value = $requested_matches{$key};
|
||||
return 1 if $key eq 'distance' && $p0f->{$key} > $value;
|
||||
return 1 if $key eq 'genre' && $p0f->{$key} =~ /$value/i;
|
||||
return 1 if $key eq 'uptime' && $p0f->{$key} < $value;
|
||||
return 1 if $key eq 'link' && $p0f->{$key} =~ /$value/i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# arch-tag: 6ef5919e-404b-4c87-bcfe-7e9f383f3901
|
||||
|
||||
|
@ -18,6 +18,14 @@ things based on source OS.
|
||||
|
||||
All code heavily based upon the p0fq.pl included with the p0f distribution.
|
||||
|
||||
=head1 Environment requirements
|
||||
|
||||
p0f requires four pieces of information to look up the p0f fingerprint:
|
||||
local_ip, local_port, remote_ip, and remote_port. TcpServer.pm has been
|
||||
has been updated to provide that information when running under djb's
|
||||
tcpserver. The async, forkserver, and prefork models will likely require
|
||||
some additional changes to make sure these fields are populated.
|
||||
|
||||
=cut
|
||||
|
||||
use IO::Socket;
|
||||
|
Loading…
Reference in New Issue
Block a user