initial commit of file from CVS for smeserver-qpsmtpd on Thu 26 Oct 11:25:19 BST 2023
This commit is contained in:
186
root/usr/share/qpsmtpd/plugins/virus/pattern_filter
Normal file
186
root/usr/share/qpsmtpd/plugins/virus/pattern_filter
Normal 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
|
Reference in New Issue
Block a user