initial commit of file from CVS for smeserver-qpsmtpd on Thu 26 Oct 11:25:19 BST 2023

This commit is contained in:
2023-10-26 11:25:19 +01:00
parent c8bfca82cb
commit c45ac2b2d0
197 changed files with 3867 additions and 2 deletions

View File

@@ -0,0 +1,186 @@
=head1 NAME
pattern_filter
=head1 DESCRIPTION
pattern_filter blocks executable (and other) attachments by matching
each line in a message against a set of known signatures. If a match is
found, the email is denied.
Signatures are stored one per line in signature files in the qpsmtpd
config directory. pattern_filter currently supports
'signature_exe' and 'signature_zip' files.
This version is heavily based on Gavin Carr's exe_filter, but implemented
so as not to care about MIME boundaries. This traps mangled MIME mail
which is still interpreted by some mail clients. However, bare lines
which happen to contain the pattern will match.
This approach has the added advantage of reading the spool file directly,
rather than reassembling the message in memory for Email::MIME to parse.
=head1 CONFIG
The following parameters can be passed to pattern_filter, or set in a
'pattern_filter' config file.
=over 4
=item check <suffixes>
where <suffixes> is a comma-separated list of suffixes to check e.g.
check exe,zip
A corresponding 'signature_<suffix>' file should exist for each supplied
suffix.
Default: 'check patterns'.
Note: this argument used to be called 'deny', which is now deprecated but
still functional.
=item action <action>
The action to take when a signature match is found. Valid values are 'deny'
(the default), to DENY the mail, and 'note', to record a transaction note
for some later plugin (and then DECLINE). If action is 'note', the default
note name is 'virus_score', with a default value of 1. These defaults can
be modified using an extended note syntax - 'note:NAME=VALUE' e.g.
action note:virus_score=1 # default settings
action note:pattern_filter=virus_found # random example
Numeric note values are accumulated, not replaced.
Default: 'action deny'.
=back
The following parameter can be passed to pattern_filter in
config/plugins (but not set via a config file):
=over 4
=item per_recipient 1
Allow per-recipient configs to be used (using the per_user_config plugin).
Default: 0.
=back
=head1 BUGS AND LIMITATIONS
pattern_filter is a simple pattern - it does not unpack and scan
archives for executables like a full-blown virus scanner. Likewise, zip
filtering blocks *all* zip files, not just those that contain a virus. You
should use a proper virus scanner if that's what you need.
Because pattern_filter is a post_data plugin, it cannot handle different
configurations in per_recipient mode. This means that if you want to use
per_recipient configurations, you should also enforce that only compatible
recipients occur in a single mail (e.g. using a plugin like
denysoft_multi_rcpt).
=head1 AUTHOR
Written by Gordon Rowell <gordonr@gormand.com.au>, heavily based on
the following:
Written by Gavin Carr <gavin@openfusion.com.au>, inspired by Russ Nelson's
viruscan patch to qmail-smtpd
(http://www.qmail.org/qmail-smtpd-viruscan-1.2.patch).
=head1 COPYRIGHT
Copyright 2005 Gordon Rowell <gordonr@gormand.com.au>
Copyright 2004 Gavin Carr <gavin@openfusion.com.au>
Copyright 2005 Gordon Rowell <gordonr@gormand.com.au>
This software is free software and may be distributed under the same
terms as Perl itself.
=cut
# use Email::MIME;
#my $VERSION = 0.04;
my $VERSION = 0.01;
my %DEFAULTS = ( deny => 'patterns', action => 'deny', per_recipient => 0 );
sub register {
my ($self, $qp, %arg) = @_;
$self->{_config_defaults} = { %DEFAULTS, %arg };
$self->register_hook("rcpt", "setup_config") if $arg{per_recipient};
$self->register_hook("data_post", "pattern_filter");
}
sub setup_config {
my ($self, $transaction, $rcpt) = @_;
# Setup only once
return DECLINED if $self->{_config};
return DECLINED
unless ref $self->{_config_defaults} eq 'HASH';
# Setup config from defaults and per_recipient pattern_filter config
my @config = $self->qp->config('pattern_filter', { rcpt => $rcpt });
$self->{_config} = {
%{$self->{_config_defaults}},
rcpt => $rcpt,
@config ? map { split /\s+/, $_, 2 } @config : ()
};
return DECLINED;
}
sub pattern_filter {
my ($self, $transaction) = @_;
# Setup config parameters if not already done
my $config = $self->{_config};
unless ($config) {
my @config = $self->qp->config('pattern_filter');
$config = {
%{$self->{_config_defaults}},
@config ? map { split /\s+/, $_, 2 } @config : ()
};
};
$config->{check} ||= $config->{deny};
return DECLINED unless $config->{check};
# Load signatures
my @signatures = ();
my $config_arg = $config->{rcpt} ? { rcpt => $config->{rcpt} } : {};
for my $suffix (split /\s*,\s*/, $config->{check}) {
my @sig = $self->qp->config("signatures_$suffix", $config_arg);
$self->log(3, "warning - no signatures_$suffix loaded") unless @sig;
push @signatures, @sig if @sig;
}
return DECLINED unless @signatures;
my $patterns = join("|", @signatures);
my $pat = qr{ ^($patterns) }xmso;
$transaction->body_resetpos;
my ($status,$msg);
while ($_ = $transaction->body_getline)
{
if ( $_ =~ m{$pat} )
{
my $match = $1;
$self->log(6, "the following line matched '$match':\n$_");
return (DENY, "We don't accept email with executable content [$match].");
}
}
return DECLINED;
}
# arch-tag: 3fc272f2-9d52-42d4-893b-032b529ec71d