From: Stoiko Ivanov Date: Wed, 13 Mar 2019 20:39:41 +0000 (+0100) Subject: add custom_check handling X-Git-Url: https://git.proxmox.com/?p=pmg-api.git;a=commitdiff_plain;h=89d4d1555a454be5cdb00122cb980b2a01a6a3d7 add custom_check handling This patch enables users to create their own script for analyzing mails. The 'custom_check' needs to be enabled via pmg.conf (optionally the check's executable path ('custom_check_path') can be set, defaulting to '/usr/local/bin/pmg-custom-check'). 'pmg-smtp-filter' calls the check before analyze_virus (which in turn calls clamav or avast). The custom_check 'api' is kept simple: * Input: the check gets 2 arguments: * the 'api-version' (currently 'v1') - for potential future change of the invocation * the 'queue-file-name' - a filename, which contains the complete e-mail as rfc822/eml file * Output: the check needs to return 2 lines on STDOUT: * the 'api-version' (currently 'v1') - see above * one of the following 3 results: * 'OK' - mail is ok * 'VIRUS: ' - mail is treated as if it contained a virus (the virusdescription is logged and added to the mail's headers) * 'SCORE: ' - is added (negative numbers are also possible) to the mail's spamscore * The check will be killed after a 5 minute timeout - and the mail is treated as OK * All output written to STDERR by the check is written to the journal/mail.log (with priority 'err') Signed-off-by: Stoiko Ivanov --- diff --git a/PMG/Config.pm b/PMG/Config.pm index b09a856..21bc204 100755 --- a/PMG/Config.pm +++ b/PMG/Config.pm @@ -102,6 +102,16 @@ sub properties { type => 'boolean', default => 1, }, + custom_check => { + description => "Use Custom Check Script. The script has to take the defined arguments and can return Virus findings or a Spamscore.", + type => 'boolean', + default => 0, + }, + custom_check_path => { + description => "Absolute Path to the Custom Check Script", + type => 'string', pattern => '^/([^/\0]+\/)+[^/\0]+$', + default => '/usr/local/bin/pmg-custom-check', + }, }; } @@ -115,6 +125,8 @@ sub options { demo => { optional => 1 }, email => { optional => 1 }, http_proxy => { optional => 1 }, + custom_check => { optional => 1 }, + custom_check_path => { optional => 1 }, }; } diff --git a/PMG/Utils.pm b/PMG/Utils.pm index 6847054..473a88f 100644 --- a/PMG/Utils.pm +++ b/PMG/Utils.pm @@ -293,6 +293,83 @@ sub reinject_mail { return wantarray ? ($resid, $rescode, $resmess) : $resid; } +sub analyze_custom_check { + my ($queue, $dname, $pmg_cfg) = @_; + + my $enable_custom_check = $pmg_cfg->get('admin', 'custom_check'); + return undef if !$enable_custom_check; + + my $timeout = 60*5; + my $customcheck_exe = $pmg_cfg->get('admin', 'custom_check_path'); + my $customcheck_apiver = 'v1'; + my ($csec, $usec) = gettimeofday(); + + my $vinfo; + my $spam_score; + + eval { + + my $log_err = sub { + my ($errmsg) = @_; + $errmsg =~ s/%/%%/; + syslog('err', $errmsg); + }; + + my $customcheck_output_apiver; + my $have_result; + my $parser = sub { + my ($line) = @_; + + my $result_flag; + if ($line =~ /^v\d$/) { + die "api version already defined!\n" if defined($customcheck_output_apiver); + $customcheck_output_apiver = $line; + die "api version mismatch - expected $customcheck_apiver, got $customcheck_output_apiver !\n" + if ($customcheck_output_apiver ne $customcheck_apiver); + } elsif ($line =~ /^SCORE: (-?[0-9]+|.[0-9]+|[0-9]+.[0-9]+)$/) { + $spam_score = $1; + $result_flag = 1; + } elsif ($line =~ /^VIRUS: (.+)$/) { + $vinfo = $1; + $result_flag = 1; + } elsif ($line =~ /^OK$/) { + $result_flag = 1; + } else { + die "got unexpected output!\n"; + } + die "got more than 1 result outputs\n" if ( $have_result && $result_flag); + $have_result = $result_flag; + }; + + PVE::Tools::run_command([$customcheck_exe, $customcheck_apiver, $dname], + errmsg => "$queue->{logid} custom check error", + errfunc => $log_err, outfunc => $parser, timeout => $timeout); + + die "no api version returned\n" if !defined($customcheck_output_apiver); + die "no result output!\n" if !$have_result; + }; + my $err = $@; + + if ($vinfo) { + syslog('info', "$queue->{logid}: virus detected: $vinfo (custom)"); + } + + my ($csec_end, $usec_end) = gettimeofday(); + $queue->{ptime_custom} = + int (($csec_end-$csec)*1000 + ($usec_end - $usec)/1000); + + if ($err) { + syslog ('err', $err); + $vinfo = undef; + $queue->{errors} = 1; + } + + $queue->{vinfo_custom} = $vinfo; + $queue->{spam_custom} = $spam_score; + + return ($vinfo, $spam_score); +} + sub analyze_virus_clam { my ($queue, $dname, $pmg_cfg) = @_;