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 {
|
sub start_connection {
|
||||||
my $self = shift;
|
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}) {
|
if ($ENV{TCPREMOTEIP}) {
|
||||||
# started from tcpserver (or some other superserver which
|
# started from tcpserver (or some other superserver which
|
||||||
@ -38,6 +41,10 @@ sub start_connection {
|
|||||||
$remote_ip = $ENV{TCPREMOTEIP};
|
$remote_ip = $ENV{TCPREMOTEIP};
|
||||||
$remote_host = $ENV{TCPREMOTEHOST} || "[$remote_ip]";
|
$remote_host = $ENV{TCPREMOTEHOST} || "[$remote_ip]";
|
||||||
$remote_info = $ENV{TCPREMOTEINFO} ? "$ENV{TCPREMOTEINFO}\@$remote_host" : $remote_host;
|
$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 {
|
} else {
|
||||||
# Started from inetd or similar.
|
# Started from inetd or similar.
|
||||||
# get info on the remote host from the socket.
|
# get info on the remote host from the socket.
|
||||||
@ -48,6 +55,10 @@ sub start_connection {
|
|||||||
$remote_ip = inet_ntoa($iaddr);
|
$remote_ip = inet_ntoa($iaddr);
|
||||||
$remote_host = gethostbyaddr($iaddr, AF_INET) || "[$remote_ip]";
|
$remote_host = gethostbyaddr($iaddr, AF_INET) || "[$remote_ip]";
|
||||||
$remote_info = $remote_host;
|
$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]");
|
$self->log(LOGNOTICE, "Connection from $remote_info [$remote_ip]");
|
||||||
|
|
||||||
@ -61,8 +72,12 @@ sub start_connection {
|
|||||||
$0 = "$first_0 [$remote_ip : $remote_host : $now]";
|
$0 = "$first_0 [$remote_ip : $remote_host : $now]";
|
||||||
|
|
||||||
$self->SUPER::connection->start(remote_info => $remote_info,
|
$self->SUPER::connection->start(remote_info => $remote_info,
|
||||||
remote_ip => $remote_ip,
|
remote_ip => $remote_ip,
|
||||||
remote_host => $remote_host,
|
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
|
=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
|
=head1 BUGS
|
||||||
|
|
||||||
Database locking is implemented using flock, which may not work on
|
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>.
|
Written by Gavin Carr <gavin@openfusion.com.au>.
|
||||||
|
|
||||||
|
Added p0f section <mattsimerson@cpan.org> (2010-05-03)
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File NDBM_File) }
|
BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File NDBM_File) }
|
||||||
@ -123,22 +142,23 @@ use AnyDBM_File;
|
|||||||
use Fcntl qw(:DEFAULT :flock);
|
use Fcntl qw(:DEFAULT :flock);
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
my $VERSION = '0.07';
|
my $VERSION = '0.08';
|
||||||
|
|
||||||
my $DENYMSG = "This mail is temporarily denied";
|
my $DENYMSG = "This mail is temporarily denied";
|
||||||
my ($QPHOME) = ($0 =~ m!(.*?)/([^/]+)$!);
|
my ($QPHOME) = ($0 =~ m!(.*?)/([^/]+)$!);
|
||||||
my $DB = "denysoft_greylist.dbm";
|
my $DB = "denysoft_greylist.dbm";
|
||||||
my %PERMITTED_ARGS = map { $_ => 1 } qw(per_recipient remote_ip sender recipient
|
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 = (
|
my %DEFAULTS = (
|
||||||
remote_ip => 1,
|
remote_ip => 1,
|
||||||
sender => 0,
|
sender => 0,
|
||||||
recipient => 0,
|
recipient => 0,
|
||||||
black_timeout => 50 * 60,
|
black_timeout => 50 * 60,
|
||||||
grey_timeout => 3 * 3600 + 20 * 60,
|
grey_timeout => 3 * 3600 + 20 * 60,
|
||||||
white_timeout => 36 * 24 * 3600,
|
white_timeout => 36 * 24 * 3600,
|
||||||
mode => 'denysoft',
|
mode => 'denysoft',
|
||||||
|
p0f => undef,
|
||||||
);
|
);
|
||||||
|
|
||||||
sub register {
|
sub register {
|
||||||
@ -206,6 +226,9 @@ sub denysoft_greylist {
|
|||||||
return DECLINED if $self->qp->connection->notes('whitelisthost');
|
return DECLINED if $self->qp->connection->notes('whitelisthost');
|
||||||
return DECLINED if $transaction->notes('whitelistsender');
|
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./_]+)$}) {
|
if ($config->{db_dir} && $config->{db_dir} =~ m{^([-a-zA-Z0-9./_]+)$}) {
|
||||||
$config->{db_dir} = $1;
|
$config->{db_dir} = $1;
|
||||||
}
|
}
|
||||||
@ -214,8 +237,10 @@ sub denysoft_greylist {
|
|||||||
my $dbdir = $transaction->notes('per_rcpt_configdir')
|
my $dbdir = $transaction->notes('per_rcpt_configdir')
|
||||||
if $config->{per_recipient_db};
|
if $config->{per_recipient_db};
|
||||||
for my $d ($dbdir, $config->{db_dir}, "/var/lib/qpsmtpd/greylisting",
|
for my $d ($dbdir, $config->{db_dir}, "/var/lib/qpsmtpd/greylisting",
|
||||||
"$QPHOME/var/db", "$QPHOME/config") {
|
"$QPHOME/var/db", "$QPHOME/config", '.' ) {
|
||||||
last if $dbdir ||= $d && -d $d && $d;
|
last if $dbdir && -d $dbdir;
|
||||||
|
next if ( ! $d || ! -d $d );
|
||||||
|
$dbdir = $d;
|
||||||
}
|
}
|
||||||
my $db = "$dbdir/$DB";
|
my $db = "$dbdir/$DB";
|
||||||
$self->log(LOGINFO,"using $db as greylisting database");
|
$self->log(LOGINFO,"using $db as greylisting database");
|
||||||
@ -292,5 +317,26 @@ sub denysoft_greylist {
|
|||||||
return $config->{mode} eq 'testonly' ? DECLINED : DENYSOFT, $DENYMSG;
|
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
|
# 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.
|
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
|
=cut
|
||||||
|
|
||||||
use IO::Socket;
|
use IO::Socket;
|
||||||
|
Loading…
Reference in New Issue
Block a user