diff -Nru amavisd-new-2.11.1/amavisd amavisd-new-2.12.2/amavisd --- amavisd-new-2.11.1/amavisd 2018-10-09 12:41:10.000000000 +0000 +++ amavisd-new-2.12.2/amavisd 2022-02-05 21:57:15.000000000 +0000 @@ -123,6 +123,7 @@ # Amavis::AV # Amavis::SpamControl # Amavis::SpamControl::ExtProg +# Amavis::SpamControl::RspamdClient # Amavis::SpamControl::SpamdClient # Mail::SpamAssassin::Logger::Amavislog # Amavis::SpamControl::SpamAssassin @@ -639,10 +640,14 @@ my(@stat_list) = stat($pem_fh); # soft-link friendly @stat_list or warn "Error accessing $fname: $!"; my($dev,$inode) = @stat_list; - if ($dev && $inode) { + # perl 5.28: On platforms where inode numbers are of a type larger than + # perl's native integer numerical types, stat will preserve the full + # content of large inode numbers by returning them in the form of strings + # of decimal digits. Use eq rather than == for exact comparison of inode. + if (defined $dev && defined $inode) { for my $j (0..$#dkim_signing_keys_storage) { # same file reused? my($k,$dv,$in,$fn) = @{$dkim_signing_keys_storage[$j]}; - if ($dv == $dev && $in == $inode) { $key_storage_ind = $j; last } + if ($dv == $dev && $in eq $inode) { $key_storage_ind = $j; last } } } if (!defined($key_storage_ind)) { @@ -679,7 +684,7 @@ $myprogram_name = $0; # typically 'amavisd' local $1; $myprogram_name =~ s{([^/]*)\z}{$1}s; $myproduct_name = 'amavisd-new'; - $myversion_id = '2.11.1'; $myversion_date = '20181009'; + $myversion_id = '2.12.2'; $myversion_date = '20211013'; $myversion = "$myproduct_name-$myversion_id ($myversion_date)"; $myversion_id_numeric = # x.yyyzzz, allows numerical compare, like Perl $] @@ -1124,6 +1129,10 @@ # [ qw(-e -v)], # -u # mail_body_size_limit => 65000, score_factor => 1.0, # ], + # ['Rspamd', 'Amavis::SpamControl::RspamdClient', + # score_factor => $sa_tag2_level_deflt / 15.0, + # mta_name => 'mail.example.com', + # ], ); $sa_spawned = 0; # true: run SA in a subprocess; false: call SA directly @@ -7650,7 +7659,11 @@ die "TempDir::prepare_dir: $dname is not a directory!!!"; } elsif ($reuse) { # existing directory my($dev,$ino,$mode,$nlink) = @stat_list; - if ($dev != $self->{tempdir_dev} || $ino != $self->{tempdir_ino}) { + # perl 5.28: On platforms where inode numbers are of a type larger than + # perl's native integer numerical types, stat will preserve the full + # content of large inode numbers by returning them in the form of strings + # of decimal digits. Use eq rather than == for exact comparison of inode. + if ($dev != $self->{tempdir_dev} || $ino ne $self->{tempdir_ino}) { do_log(-1,"TempDir::prepare_dir: %s is no longer the same directory!", $dname); ($self->{tempdir_dev}, $self->{tempdir_ino}) = ($dev, $ino); @@ -7683,7 +7696,11 @@ die "TempDir::prepare_file: $fname is not a regular file!!!"; } elsif ($self->{fh_pers}) { my($dev,$ino) = @stat_list; - if ($dev != $self->{file_dev} || $ino != $self->{file_ino}) { + # perl 5.28: On platforms where inode numbers are of a type larger than + # perl's native integer numerical types, stat will preserve the full + # content of large inode numbers by returning them in the form of strings + # of decimal digits. Use eq rather than == for exact comparison of inode. + if ($dev != $self->{file_dev} || $ino ne $self->{file_ino}) { # may happen if some user code has replaced the file, e.g. by altermime undef $self->{fh_pers}; do_log(1,"TempDir::prepare_file: %s is no longer the same file, ". @@ -7778,7 +7795,11 @@ die "TempDir::strip: error accessing directory $dname: $!"; } else { my($dev,$ino) = @stat_list; - if ($dev != $self->{tempdir_dev} || $ino != $self->{tempdir_ino}) { + # perl 5.28: On platforms where inode numbers are of a type larger than + # perl's native integer numerical types, stat will preserve the full + # content of large inode numbers by returning them in the form of strings + # of decimal digits. Use eq rather than == for exact comparison of inode. + if ($dev != $self->{tempdir_dev} || $ino ne $self->{tempdir_ino}) { do_log(-1,"TempDir::strip: %s is no longer the same directory!", $dname); ($self->{tempdir_dev}, $self->{tempdir_ino}) = ($dev, $ino); @@ -8214,7 +8235,7 @@ use Time::HiRes (); use IO::Socket; use IO::Socket::UNIX; -#use IO::Socket::SSL; +use IO::Socket::SSL; # Connect to one of the specified sockets. The $socket_specs may be a # simple string ([inet-host]:port, [inet6-host]:port, or a unix socket name), @@ -8488,7 +8509,8 @@ } elsif ($! == EAGAIN || $! == EINTR) { $self->{last_event} = 'read-intr'.(0+$!); $idle_cnt = 0; - do_log(2, 'rw_loop read interrupted: %s', + do_log($SSL_ERROR == SSL_WANT_READ ? 4 : 2, + 'rw_loop read interrupted: %s', !$self->{ssl_active} ? $! : $sock->errstr.", $!"); Time::HiRes::sleep(0.1); # slow down, just in case # retry @@ -8672,6 +8694,14 @@ $self->{last_event} = 'ssl-upgrade'; $self->{last_event_time} = $self->{last_event_tx_time} = Time::HiRes::time; $self->{ssl_active} = 1; + # An IO::Socket::SSL socket can block a sysread + # even if selected for read. See issue 74 and + # perldoc IO::Socket::SSL `Using Non-Blocking Sockets` + if (defined $sock->blocking(0)) { + do_log(4, "Setting TLS socket to non-blocking"); + } else { + die "Error setting TLS socket to non-blocking: $!"; + } ll(3) && do_log(3,"TLS cipher: %s", $sock->get_cipher); ll(5) && do_log(5,"TLS certif: %s", $sock->dump_peer_certificate); 1; @@ -12115,7 +12145,7 @@ $extra_code_out_smtp $extra_code_out_pipe $extra_code_out_bsmtp $extra_code_out_local $extra_code_p0f $extra_code_antivirus $extra_code_antispam - $extra_code_antispam_extprog + $extra_code_antispam_extprog $extra_code_antispam_rspamc $extra_code_antispam_spamc $extra_code_antispam_sa $extra_code_unpackers $extra_code_dkim $extra_code_tools); @@ -12832,7 +12862,7 @@ $s = $$s if ref($s) eq 'SCALAR'; if (defined $s) { # encode log templates to UTF-8, leave the rest as character strings - safe_encode_utf8_inplace($s) if $n eq 'log_templ' || 'log_recip_templ'; + safe_encode_utf8_inplace($s) if $n eq 'log_templ' || $n eq 'log_recip_templ'; $policy_bank{$bank_name}{$n} = tokenize(\$s); } } @@ -12968,6 +12998,8 @@ $extra_code_antispam_extprog ?'':" NOT"); do_log(1,"ANTI-SPAM-C code %s loaded", $extra_code_antispam_spamc ?'':" NOT"); + do_log(1,"ANTI-SPAM-Rspamd code%s loaded", + $extra_code_antispam_rspamc ?'':" NOT"); do_log(1,"ANTI-SPAM-SA code %s loaded", $extra_code_antispam_sa?'':" NOT"); do_log(1,"Unpackers code %s loaded", $extra_code_unpackers ?'':" NOT"); do_log(1,"DKIM code %s loaded", $extra_code_dkim ?'':" NOT"); @@ -13458,8 +13490,7 @@ my $is_ux = $sock && $sock->UNIVERSAL::can('NS_proto') && $sock->NS_proto eq 'UNIX'; if ($is_ux) { - my $unix_socket_path = Net::Server->VERSION >= 2 ? $sock->NS_port - : $sock->NS_unix_path; + my $unix_socket_path = $sock->hostpath; $unix_socket_path = 'UNKNOWN' if !defined $unix_socket_path; return access_is_allowed($unix_socket_path); } else { @@ -13656,8 +13687,7 @@ $zmq_obj->register_proc(2,0,'b') if $zmq_obj; # begin protocol # $snmp_db->register_proc(2,0,'b') if $snmp_db; if ($ns_proto eq 'UNIX') { - my $path = Net::Server->VERSION >= 2 ? $sock->NS_port - : $sock->NS_unix_path; + my $path = $sock->hostpath; $conn->socket_path($path); # how to test: $ socat stdio unix-connect:/var/amavis/amavisd.sock,crnl } else { # TCP, UDP, UNIXDGRAM, SSLEAY, SSL (Net::Server::Proto modules) @@ -18843,7 +18873,7 @@ [-R chroot_dir | -R ''] [-S helpers_home_dir] [-T tempbase_dir] ( [start] | stop | reload | restart | debug | debug-sa | foreground | showkeys {domains} | testkeys {domains} | genrsa file_name [nbits] - convert_keysfile file_name ) + convert_keysfile file_name | test-config ) where area is a SpamAssassin debug area, e.g. all,util,rules,plugin,dkim,dcc or: $myprogram_name (-h | -V) ... show help or version, then exit @@ -18874,6 +18904,34 @@ $< != 0 or die "Effective UID changed, but Real UID is 0, aborting\n"; } +sub read_configs_and_exit { + my $user = $ENV{AMAVIS_TEST_CONFIG_USER}; + my $group = $ENV{AMAVIS_TEST_CONFIG_GROUP}; + + if ($user && $user ne '') { + drop_priv($user, $group); + } + Amavis::Conf::include_config_files(@config_files); + exit 0; +} + +sub configs_readable($) { + my $amavisd = shift; + local $ENV{AMAVIS_TEST_CONFIG} = 1; + local $ENV{AMAVIS_TEST_CONFIG_USER} = $daemon_user; + local $ENV{AMAVIS_TEST_CONFIG_GROUP} = $daemon_group; + return 0 == system map untaint($_), $amavisd, @ARGV; +} + +sub sig_hup { + my $self = $_[0]; + if (configs_readable($self->commandline->[0])) { + $self->SUPER::sig_hup(@_); + } else { + do_log(-1, 'Rejecting reload, some config files unreadable or erroneous'); + } +} + # # Main program starts here # @@ -18910,7 +18968,7 @@ $extra_code_p0f, $extra_code_redis, $extra_code_sql_base, $extra_code_sql_log, $extra_code_sql_quar, $extra_code_antivirus, $extra_code_antispam, - $extra_code_antispam_extprog, + $extra_code_antispam_extprog, $extra_code_antispam_rspamc, $extra_code_antispam_spamc, $extra_code_antispam_sa, $extra_code_unpackers, $extra_code_dkim, $extra_code_tools) { $_ = ; @@ -19018,7 +19076,7 @@ } my $cmd = lc(shift @argv); if ($cmd !~ /^(?:start|debug|debug-sa|foreground|reload|restart|stop| - showkeys?|testkeys?|genrsa|convert_keysfile)?\z/xs) { + showkeys?|testkeys?|genrsa|convert_keysfile|test-config)?\z/xs) { die "$myversion:\n Unknown command line parameter: $cmd\n\n" . usage(); } elsif (@argv > 0 && $cmd !~ /^(:?showkeys?|testkeys?|genrsa|convert_keysfile)/xs) { @@ -19094,8 +19152,12 @@ } # Read and evaluate config files, which may override default settings +read_configs_and_exit if $ENV{AMAVIS_TEST_CONFIG}; Amavis::Conf::include_config_files(@config_files); Amavis::Conf::supply_after_defaults(); +exit 1 unless $warm_restart || $cmd eq 'stop' || configs_readable($0); +exit 0 if $cmd eq 'test-config'; + update_current_log_level(); add_entropy($Amavis::Conf::myhostname, $Amavis::Conf::myversion_date); @@ -19396,6 +19458,13 @@ } else { undef $extra_code_antispam_extprog; } +if ($spam_scanners_used{'Amavis::SpamControl::RspamdClient'}) { + eval $extra_code_antispam_rspamc or die "Problem in rspamd client code: $@"; + # release memory occupied by source code + undef $extra_code_antispam_rspamc; $extra_code_antispam_rspamc = 1; +} else { + undef $extra_code_antispam_rspamc; +} if ($spam_scanners_used{'Amavis::SpamControl::SpamdClient'}) { eval $extra_code_antispam_spamc or die "Problem in spamd client code: $@"; # release memory occupied by source code @@ -21842,6 +21911,7 @@ Amavis::load_policy_bank($_,$msginfo) for @bank_names_cl; # additional banks from the request Amavis::load_policy_bank(untaint($_),$msginfo) for @$bank_names_ref; + $msginfo->originating(c('originating')); my $sender = $msginfo->sender; if (defined $policy_bank{'MYUSERS'} && $sender ne '' && $msginfo->originating && @@ -22814,6 +22884,7 @@ } # load policy banks from the 'client_ipaddr_policy' lookup Amavis::load_policy_bank($_,$msginfo) for @bank_names_cl; + $msginfo->originating(c('originating')); $msginfo->client_addr($cl_ip); # ADDR $msginfo->client_port($cl_port); # PORT @@ -27408,7 +27479,7 @@ eval { # MySQL does not like a standard iso8601 delimiter 'T' or a timezone # when data type of msgs.time_iso is TIMESTAMP (instead of a string) - my $time_iso = $timestamp_fmt_mysql && $conn_h->driver_name eq 'mysql' + my $time_iso = $timestamp_fmt_mysql && ($conn_h->driver_name eq 'mysql' || $conn_h->driver_name eq 'MariaDB') ? iso8601_utc_timestamp($msginfo->rx_time,1,'') : iso8601_utc_timestamp($msginfo->rx_time); # insert a placeholder msgs record with sender information @@ -27515,16 +27586,14 @@ $_ = !defined($_) ? '' :untaint($_) for ($subj,$from,$m_id,$q_to,$os_fp); for ($subj,$from) { # character set decoding, sanitation chomp; s/\n(?=[ \t])//gs; s/^[ \t]+//s; s/[ \t]+\z//s; # unfold, trim - eval { # convert to UTF-8 octets, truncate to 255 bytes - my $chars = safe_decode_mime($_); # to logical characters - my $octets = safe_encode_utf8($chars); # to bytes, UTF-8 encoded - $octets = truncate_utf_8($octets,255); - # man DBI: Drivers should accept [unicode and non-unicode] strings - # and, if required, convert them to the character set of the - # database being used. Similarly, when fetching from the database - # character data that isn't iso-8859-1 the driver should convert - # it into UTF-8. - $_ = $octets; 1; # pass bytes to SQL, UTF-8, works better + eval { # decode mime and truncate to 255 bytes + my $chars = safe_decode_mime($_); # to logical characters + substr($chars, 255) = '' if length($chars) > 255; + # DBD::mysql will throw an error with native encoding, while + # DBD::MariaDB and DBD::Pg can cope with native as well as utf8. + # Upgrade to be on the safe side. Suggestion via issue#67. + utf8::upgrade($chars); + $_ = $chars; 1; } or do { my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; do_log(1,"save_info_final INFO: header field ". @@ -28856,7 +28925,7 @@ my $f = $bare_fnames->[$k]; my $multi = 0; if ($one_at_a_time) { # glob templates may be substrings anywhere local($1); @query_expanded = @query_template; # start afresh - s{ ( {} (?: / \* )? | \* ) } + s{ ( \{\} (?: / \* )? | \* ) } { $1 eq '{}' ? "$tempdir/parts" : $1 eq '{}/*' ? ($multi=1,"$tempdir/parts/$f") : $1 eq '*' ? ($multi=1,$f) : $1 @@ -30101,6 +30170,260 @@ __DATA__ # +package Amavis::SpamControl::RspamdClient; +use strict; +use re 'taint'; +use warnings; +use warnings FATAL => qw(utf8 void); +no warnings 'uninitialized'; + +=pod + +=head1 Amavis extension module to use Rspamd as a spam checker + +Copyright (c) 2019 Ralph Seichter, partially based on the +SpamdClient extension. Released under GNU General Public +License; see Amavis LICENSE file for details. + +=head2 Example configuration #1 (local Rspamd) + + # Rspamd running on the same machine as Amavis. Default URL + # is http://127.0.0.1:11333/checkv2 , matching Rspamd's + # "normal" worker defaults. + @spam_scanners = ( [ + 'Local Rspamd', 'Amavis::SpamControl::RspamdClient', + + # Adjust scores according to Rspamd's "required score" + # setting (defaults to 15). Scores reported by Rspamd + # will be multiplied with this factor. The following + # adjusts Rspamd scores to SpamAssassin scores. While + # this setting is technically optional, not adjusting + # scores is prone to cause headaches. + score_factor => $sa_tag2_level_deflt / 15.0, + + # MTA name is used to assess validity of existing + # Authentication-Results headers, e.g. if DKIM/DMARC + # validation has already happened. + mta_name => 'mail.example.com', + ] ); + +=head2 Example configuration #2 (remote Rspamd) + + # Rspamd running behind HTTPS-capable proxy using basic + # authentication to control access. + @spam_scanners = ( [ + 'Remote Rspamd', 'Amavis::SpamControl::RspamdClient', + url => 'https://rspamd-proxy.example.com/checkv2', + + # Response timeout in seconds. Default is 60, matching + # Rspamd's standard config for the "normal" worker. + timeout => 42, + + # SSL-options and -credentials passed to LWP::UserAgent, + # see https://metacpan.org/pod/LWP::UserAgent . Default: + # ssl_opts => { verify_hostname => 1 }, + credentials => { + # The following : must match the 'url' + # defined above or credentials won't be transmitted. + netloc => 'rspamd-proxy.example.com:443', + # Remote authentication realm + realm => 'Rspamd restricted access', + username => 'Marco', + password => 'Polo', + }, + + # Don't scan messages remotely if the body size extends + # the following limit (optional setting). + mail_body_size_limit => 32 * 1024, + + score_factor => $sa_tag2_level_deflt / 15.0, + mta_name => 'mail.example.com', + ] ); + +=head2 Requirements + +In addition to Amavis' core requirements, this extension needs +the following additional Perl modules: + + JSON + HTTP::Message + LWP::UserAgent + LWP::Protocol::https + Net::SSLeay + +Should your host OS not provide the necessary packages, these +modules can be obtained via https://www.cpan.org . + +=cut + +BEGIN { + require Exporter; + use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); + $VERSION = '2.412'; + @ISA = qw(Exporter); + import Amavis::Util qw(do_log min prolong_timer); + import Amavis::rfc2821_2822_Tools qw(qquote_rfc2821_local); + import Amavis::Timing qw(section_time); +} + +use JSON qw(decode_json); +use LWP::UserAgent; + +sub new { + my ($class, $scanner_name, $module, @args) = @_; + my (%options) = @args; + bless { scanner_name => $scanner_name, options => \%options }, $class; +} + +# Pass meta information using Rspamd's non-standard HTTP headers. +sub pass_meta { + my ($request, $name, $value) = @_; + if (defined $value && $value ne '') { + $request->header($name => $value); + } +} + +# Invoked by Amavis to spam-check one message. +sub check { + my ($self, $msginfo) = @_; + my ($which_section, $spam_level, $rspamd_action, $rspamd_rscore, + $rspamd_skipped, $rspamd_tests, $rspamd_verdict, $size_limit); + my $scanner_name = $self->{scanner_name}; + my $mbsl = $self->{options}->{'mail_body_size_limit'}; + if (defined $mbsl) { + $size_limit = min(32 * 1024, $msginfo->orig_header_size) + + min($mbsl, $msginfo->orig_body_size); + # Allow slightly oversized messages to pass in full. + undef $size_limit if $msginfo->msg_size < $size_limit + 5 * 1024; + } + my $per_recip_data = $msginfo->per_recip_data; + $per_recip_data = [] if !$per_recip_data; + + my $msg = $msginfo->mail_text; + my $msg_str_ref = $msginfo->mail_text_str; # In-memory copy available? + $msg = $msg_str_ref if ref $msg_str_ref; + eval { + if (!defined $msg) { + do_log(3, "Empty message"); + } + elsif (ref $msg eq 'SCALAR') { + $which_section = 'rspamd_connect'; + my $timeout = $self->{options}->{'timeout'}; + $timeout = 60 unless defined $timeout; + my $url = $self->{options}->{'url'}; + $url = 'http://127.0.0.1:11333/checkv2' unless defined $url; + do_log(3, "connecting to rspamd %s (timeout %s)", $url, $timeout); + + my $request = HTTP::Request->new(POST => $url); + $request->content_type('application/octet-stream'); + $request->content(defined $size_limit ? substr($$msg, 0, $size_limit) : $$msg); + pass_meta($request, 'Helo', $msginfo->client_helo); + pass_meta($request, 'Hostname', $msginfo->client_name); + pass_meta($request, 'IP', $msginfo->client_addr); + pass_meta($request, 'MTA-Name', $self->{options}->{'mta_name'}); + pass_meta($request, 'From', $msginfo->sender_smtp); + pass_meta($request, 'Queue-Id', $msginfo->queue_id); + for my $rcpt (qquote_rfc2821_local(@{$msginfo->recips})) { + pass_meta($request, 'Rcpt', $rcpt); + } + + $which_section = 'rspamd_tx'; + my $ssl_opts = $self->{options}->{'ssl_opts'}; + $ssl_opts = { verify_hostname => 1 } unless defined $ssl_opts; + my $user_agent = LWP::UserAgent->new( + protocols_allowed => [ 'http', 'https' ], + ssl_opts => $ssl_opts + ); + my $credentials = $self->{options}->{'credentials'}; + if (defined $credentials) { + $user_agent->credentials( + $credentials->{'netloc'}, + $credentials->{'realm'}, + $credentials->{'username'}, + $credentials->{'password'}, + ) + } + $user_agent->agent('amavis/' . $VERSION); + $user_agent->timeout($timeout); + + prolong_timer($which_section, undef, undef, $timeout); + my $response = $user_agent->request($request); + $response->is_success or die "Error calling rspamd: " . $response->status_line . ", stopped"; + my $content = $response->content; + defined $content or die "Missing rspamd response, stopped"; + do_log(5, "Rspamd response: " . $content); + my $rspamd = decode_json $content; + $rspamd_skipped = $rspamd->{is_skipped}; + $spam_level = $rspamd->{score}; + $rspamd_rscore = $rspamd->{required_score}; + $rspamd_action = $rspamd->{action}; + my $rspamd_symbols = $rspamd->{symbols}; + if (defined $rspamd_symbols) { + my @tests; + while (my ($ignored, $symbol) = each %$rspamd_symbols) { + my $symbol_name = $symbol->{name}; + $symbol_name =~ tr/=,/__/; + my $t = sprintf("%s=%s", $symbol_name, $symbol->{score}); + push(@tests, $t); + } + $rspamd_tests = join(',', @tests); + } + # Map Rspamd action to Amavis verdict + my %action2verdict = ( + 'add header' => 'Spam', + 'no action' => 'Ham', + 'reject' => 'Spam', + 'rewrite subject' => 'Spam', + # Rspamd 1.9 and later + 'discard' => 'Spam', + 'quarantine' => 'Spam', + ); + $rspamd_verdict = exists $action2verdict{$rspamd_action} ? + $action2verdict{$rspamd_action} : 'Unknown'; + } + else { + do_log(2, "%s skipping message type %s", $scanner_name, ref $msg); + $rspamd_action = 'N/A'; + $rspamd_verdict = 'Unknown'; + $rspamd_skipped = 1; + $rspamd_rscore = 0; + $spam_level = 0; + } + + 1; + + } or do { + my $eval_stat = $@ ne '' ? $@ : "errno=$!"; + chomp $eval_stat; + do_log(-1, "%s client failed: %s", $scanner_name, $eval_stat); + }; + + section_time($which_section); + my $score_factor = $self->{options}->{'score_factor'}; + if (defined $spam_level && defined $score_factor) { + $spam_level *= $score_factor; + $rspamd_rscore *= $score_factor; + } + do_log(2, "%s rspamd %sscore %.2f/%.2f (%s) %s", $scanner_name, + $rspamd_skipped ? 'skipped/' : '', + $spam_level, $rspamd_rscore, $rspamd_action, $rspamd_tests); + $msginfo->supplementary_info('SCORE-' . $scanner_name, $spam_level); + $msginfo->supplementary_info('VERDICT-' . $scanner_name, $rspamd_verdict); + for my $r (@$per_recip_data) { + $r->spam_level(($r->spam_level || 0) + $spam_level); + if (!$r->spam_tests) { + $r->spam_tests([ \$rspamd_tests ]); + } + else { + push(@{$r->spam_tests}, \$rspamd_tests); + } + } +} + +1; + +__DATA__ +# package Amavis::SpamControl::SpamdClient; use strict; use re 'taint'; @@ -33047,7 +33370,11 @@ mkdir("$tempdir/parts/ole",0750) or die "Can't mkdir $tempdir/parts/ole: $!"; my($proc_fh,$pid) = run_command(undef, '&1', $archiver, '-v', '-i', $part->full_name, '-d',"$tempdir/parts/ole"); - collect_results($proc_fh,$pid,$archiver,16384,[0]); + # Not all Microsoft documents contain embedded objects, and we won't know + # until we look. The ripOLE program knows how to check if we do in fact + # have an OLE document; but it exits with code 102 if we don't. This isn't + # really an error, so we add "102" to the list of successful exit codes. + collect_results($proc_fh,$pid,$archiver,16384,[0,102]); undef $proc_fh; undef $pid; my $b = flatten_and_tidy_dir("$tempdir/parts/ole", "$tempdir/parts", $part); if ($b > 0) { @@ -34118,7 +34445,7 @@ } elsif ($all_b_8{$b} > $all_b{$b}) { do_log(2, "generate_AR: not reporting b for collisions: %s", $b); } else { - $str .= "\n header.b=" . substr($b,0,8); + $str .= "\n header.b=" . '"'.substr($b,0,8) .'"'; } } $result_str .= $str; @@ -34357,6 +34684,7 @@ $sig_ind++; } Amavis::load_policy_bank($_,$msginfo) for @bank_names; + $msginfo->originating(c('originating')); $msginfo->dkim_signatures_valid(\@signatures_valid) if @signatures_valid; # if (ll(5) && $sig_ind > 0) { # # show which header fields are covered by which signature @@ -34450,7 +34778,7 @@ my(@tags) = map($_.'='.$key_opts->{$_}, grep(defined $key_opts->{$_}, qw(v g h k s t n))); my $key_size = 8 * $key->size; - printf("; key#%d %d bits, i=%s, d=%s%s\n", + printf("; key#%d %d bits, s=%s, d=%s%s\n", $key_opts->{key_ind} + 1, $key_size, $selector, $domain, defined $fname ? ', '.$fname : ''); diff -Nru amavisd-new-2.11.1/amavisd.conf amavisd-new-2.12.2/amavisd.conf --- amavisd-new-2.11.1/amavisd.conf 2017-12-27 18:25:28.000000000 +0000 +++ amavisd-new-2.12.2/amavisd.conf 2022-02-05 21:57:15.000000000 +0000 @@ -13,6 +13,9 @@ # @bypass_spam_checks_maps = (1); # controls running of anti-spam code # $bypass_decode_parts = 1; # controls running of decoders&dearchivers +# $myprogram_name = $0; # set to 'amavisd' or similar to avoid process name + # truncation in /proc//stat and ps -e output + $max_servers = 2; # num of pre-forked children (2..30 is common), -m $daemon_user = 'vscan'; # (no default; customary: vscan or amavis), -u $daemon_group = 'vscan'; # (no default; customary: vscan or amavis), -g diff -Nru amavisd-new-2.11.1/debian/amavisd-new-cronjob amavisd-new-2.12.2/debian/amavisd-new-cronjob --- amavisd-new-2.11.1/debian/amavisd-new-cronjob 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/amavisd-new-cronjob 2022-02-18 12:14:20.000000000 +0000 @@ -21,7 +21,7 @@ umask 022 if ! perl -MMail::SpamAssassin -e "my \$spamtest = Mail::SpamAssassin->new(); -\$spamtest->compile_now (); \$spamtest->{conf}->{use_bayes} ? exit 0 : exit 1" +\$spamtest->compile_now (); \$spamtest->{conf}->{use_bayes} ? exit 0 : exit 1" >/dev/null 2>&1 then #bayes is disabled - just exit exit diff -Nru amavisd-new-2.11.1/debian/changelog amavisd-new-2.12.2/debian/changelog --- amavisd-new-2.11.1/debian/changelog 2021-07-14 20:22:39.000000000 +0000 +++ amavisd-new-2.12.2/debian/changelog 2022-02-18 12:14:20.000000000 +0000 @@ -1,3 +1,44 @@ +amavisd-new (1:2.12.2-1ubuntu1) jammy; urgency=medium + + * Merge with Debian unstable (LP: #1946841). Remaining changes: + - Add information in README.Debian about Ubuntu specific changes. + - d/README.Debian, d/etc/conf.d/21-ubuntu_defaults: Ubuntu + configuration changes in 21-ubuntu_defaults. + + Reduce email responses for virus/blocked mail so as not to be + a backscatter source by default. + + Enable DKIM checking by default. + - d/README.Debian,d/etc/conf.d/40-policy_banks: Include + policy-bank of known good domains for DKIM whitelisting + in 40-policy_banks. + - d/control: drop altermime and ripole to Suggests after + discussions with the server team. + - d/control,d/amavisd-new-postfix.*: new package + amavisd-new-postfix, configuration for anti-spam/virus. + - do not report FQDN misconfiguration through apport. + (LP #1587695). + + d/control: b-dep on dh_apparmor + + d/rules: enable dh_apparmor + + d/amavisd-new.apport: apport script + + d/amavisd-new.dirs: layout required dirs + + -- Miriam EspaƱa Acebal Fri, 18 Feb 2022 13:14:20 +0100 + +amavisd-new (1:2.12.2-1) unstable; urgency=medium + + [ Sergio Durigan Junior ] + * d/amavisd-new-cronjob: Suppress stderr output on 'perl' invocation. + (LP: #1481579) + + [ Damian Lukowski ] + * New upstream version 2.12.2 + * Add suggestions for RspamdClient + * Update debian patches + * Update debian watchfile + * Use pipeline to check if package builds + * Add test-config pipeline stage + + -- Brian May Wed, 09 Feb 2022 09:00:48 +1100 + amavisd-new (1:2.11.1-5ubuntu1) impish; urgency=medium * Merge with Debian unstable. Remaining changes: diff -Nru amavisd-new-2.11.1/debian/control amavisd-new-2.12.2/debian/control --- amavisd-new-2.11.1/debian/control 2021-07-14 20:22:39.000000000 +0000 +++ amavisd-new-2.12.2/debian/control 2022-02-18 12:14:20.000000000 +0000 @@ -51,9 +51,13 @@ lhasa, libauthen-sasl-perl, libdbi-perl (>= 1.43), + libjson-perl, + liblwp-protocol-https-perl, libmail-dkim-perl (>= 0.31), libnet-ldap-perl (>= 1:0.32), + libnet-ssleay-perl, libsnmp-perl, + libwww-perl, libzeromq-perl, lzop, nomarch, diff -Nru amavisd-new-2.11.1/debian/.gitlab-ci.yml amavisd-new-2.12.2/debian/.gitlab-ci.yml --- amavisd-new-2.11.1/debian/.gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ amavisd-new-2.12.2/debian/.gitlab-ci.yml 2022-02-18 12:14:20.000000000 +0000 @@ -0,0 +1,61 @@ +stages: + - build + - test-config + +.upgrade: &upgrade + - apt-get update + - apt-get -y dist-upgrade + +.build: + stage: build + before_script: *upgrade + script: + - apt-get -y install git-buildpackage + - mk-build-deps --install --tool 'apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' + - git clean -f -d + - gbp buildpackage --git-ignore-branch -uc -us --git-export-dir=build + artifacts: + paths: + - build/* + expire_in: 1 week + +build stable: + extends: .build + image: debian:stable-slim + +build testing: + extends: .build + image: debian:testing-slim + +build unstable: + extends: .build + image: debian:unstable-slim + allow_failure: true + +.test-config: + stage: test-config + before_script: *upgrade + script: + - 'dpkg -i build/*.deb || :' + - apt-get -f -y install + - touch /etc/mailname + - amavisd-new test-config + +test-config stable: + dependencies: + - build stable + extends: .test-config + image: debian:stable-slim + +test-config testing: + dependencies: + - build testing + extends: .test-config + image: debian:testing-slim + +test-config unstable: + dependencies: + - build unstable + extends: .test-config + image: debian:unstable-slim + allow_failure: true diff -Nru amavisd-new-2.11.1/debian/patches/0009-Fix-an-unescaped-brace-in-amavisd.patch amavisd-new-2.12.2/debian/patches/0009-Fix-an-unescaped-brace-in-amavisd.patch --- amavisd-new-2.11.1/debian/patches/0009-Fix-an-unescaped-brace-in-amavisd.patch 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/patches/0009-Fix-an-unescaped-brace-in-amavisd.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -From: Elimar Riesebieter -Date: Fri, 14 Dec 2018 13:04:46 +0100 -Subject: Fix an unescaped brace in amavisd - ---- - amavisd | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/amavisd b/amavisd -index a2e4d8f..5cc2c31 100755 ---- a/amavisd -+++ b/amavisd -@@ -28871,7 +28871,7 @@ sub run_av(@) { - my $f = $bare_fnames->[$k]; my $multi = 0; - if ($one_at_a_time) { # glob templates may be substrings anywhere - local($1); @query_expanded = @query_template; # start afresh -- s{ ( {} (?: / \* )? | \* ) } -+ s{ ( \{ \} (?: / \* )? | \* ) } - { $1 eq '{}' ? "$tempdir/parts" - : $1 eq '{}/*' ? ($multi=1,"$tempdir/parts/$f") - : $1 eq '*' ? ($multi=1,$f) : $1 diff -Nru amavisd-new-2.11.1/debian/patches/0010-fix-882324.patch amavisd-new-2.12.2/debian/patches/0010-fix-882324.patch --- amavisd-new-2.11.1/debian/patches/0010-fix-882324.patch 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/patches/0010-fix-882324.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -Description: honor "originating" configuration flag, -Origin: https://gitlab.com/amavis/amavis/commit/206109d4c21f28dcd2ba3f42a19b7d77e2bbc100.patch -Date: Wed, 10 Oct 2018 16:34:58 +0200 -Subject: [PATCH] Applied DKIM signing patch (#6) -Applied-Upstream: Yes -Last-Update: 2019-04-05 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/amavisd -+++ b/amavisd -@@ -22829,6 +22829,7 @@ - } - # load policy banks from the 'client_ipaddr_policy' lookup - Amavis::load_policy_bank($_,$msginfo) for @bank_names_cl; -+ $msginfo->originating(c('originating')); - - $msginfo->client_addr($cl_ip); # ADDR - $msginfo->client_port($cl_port); # PORT -@@ -34361,6 +34362,7 @@ - $sig_ind++; - } - Amavis::load_policy_bank($_,$msginfo) for @bank_names; -+ $msginfo->originating(c('originating')); - $msginfo->dkim_signatures_valid(\@signatures_valid) if @signatures_valid; - # if (ll(5) && $sig_ind > 0) { - # # show which header fields are covered by which signature diff -Nru amavisd-new-2.11.1/debian/patches/20_safe_path_at_init.patch amavisd-new-2.12.2/debian/patches/20_safe_path_at_init.patch --- amavisd-new-2.11.1/debian/patches/20_safe_path_at_init.patch 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/patches/20_safe_path_at_init.patch 2022-02-18 12:14:20.000000000 +0000 @@ -6,11 +6,9 @@ amavisd | 4 ++++ 1 file changed, 4 insertions(+) -diff --git a/amavisd b/amavisd -index 7f93194..880b793 100755 --- a/amavisd +++ b/amavisd -@@ -18950,6 +18950,10 @@ my $desired_group; # defaults to $desired_user's group +@@ -19014,6 +19014,10 @@ my $desired_user; # username or UID if ($> != 0) { $desired_user = $> } # use effective UID if not root diff -Nru amavisd-new-2.11.1/debian/patches/30_conf.d_support_builtin.patch amavisd-new-2.12.2/debian/patches/30_conf.d_support_builtin.patch --- amavisd-new-2.11.1/debian/patches/30_conf.d_support_builtin.patch 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/patches/30_conf.d_support_builtin.patch 2022-02-18 12:14:20.000000000 +0000 @@ -7,11 +7,9 @@ amavisd | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) -diff --git a/amavisd b/amavisd -index 880b793..54a4ea0 100755 --- a/amavisd +++ b/amavisd -@@ -4341,6 +4341,25 @@ sub read_l10n_templates($;$) { +@@ -4356,6 +4356,25 @@ # map { untaint($_) } @config_files; # } @@ -37,7 +35,7 @@ #use CDB_File; #sub tie_hash($$) { # my($hashref, $filename) = @_; -@@ -19085,10 +19104,9 @@ $Amavis::Conf::map_full_type_to_short_type_re = +@@ -19149,10 +19168,9 @@ # default location of the config file if none specified if (!@config_files) { diff -Nru amavisd-new-2.11.1/debian/patches/85-clarify_fqdn_error.patch amavisd-new-2.12.2/debian/patches/85-clarify_fqdn_error.patch --- amavisd-new-2.11.1/debian/patches/85-clarify_fqdn_error.patch 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/patches/85-clarify_fqdn_error.patch 2022-02-18 12:14:20.000000000 +0000 @@ -6,11 +6,9 @@ amavisd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) -diff --git a/amavisd b/amavisd -index 54a4ea0..a2e4d8f 100755 --- a/amavisd +++ b/amavisd -@@ -19463,7 +19463,8 @@ $myhn =~ /[^.]\.[^.]+\.?\z/s || lc($myhn) eq 'localhost' +@@ -19538,7 +19538,8 @@ The value of variable \$myhostname is \"$myhn\", but should have been a fully qualified domain name; perhaps uname(3) did not provide such. You must explicitly assign a FQDN of this host to variable \$myhostname diff -Nru amavisd-new-2.11.1/debian/patches/series amavisd-new-2.12.2/debian/patches/series --- amavisd-new-2.11.1/debian/patches/series 2020-11-18 19:57:45.000000000 +0000 +++ amavisd-new-2.12.2/debian/patches/series 2022-02-18 12:14:20.000000000 +0000 @@ -6,5 +6,3 @@ 85-clarify_fqdn_error.patch 90_fix_snmp_subagent_warning 95_amavisd_helpers_fixes -0009-Fix-an-unescaped-brace-in-amavisd.patch -0010-fix-882324.patch diff -Nru amavisd-new-2.11.1/debian/watch amavisd-new-2.12.2/debian/watch --- amavisd-new-2.11.1/debian/watch 2021-07-14 20:19:01.000000000 +0000 +++ amavisd-new-2.12.2/debian/watch 2022-02-18 12:14:20.000000000 +0000 @@ -1,2 +1,2 @@ version=4 -opts="dversionmangle=s/\.dfsg//" https://amavis.org/ ./amavisd-new-(\d\.\d+\.\d+).tar.bz2 +opts="dversionmangle=s/\.dfsg//" https://gitlab.com/amavis/amavis/-/tags?sort=updated_desc archive/v@ANY_VERSION@/amavis-v\d\S*@ARCHIVE_EXT@ diff -Nru amavisd-new-2.11.1/p0f-analyzer.pl-old amavisd-new-2.12.2/p0f-analyzer.pl-old --- amavisd-new-2.11.1/p0f-analyzer.pl-old 2014-01-27 18:29:46.000000000 +0000 +++ amavisd-new-2.12.2/p0f-analyzer.pl-old 1970-01-01 00:00:00.000000000 +0000 @@ -1,269 +0,0 @@ -#!/usr/bin/perl -T - -#------------------------------------------------------------------------------ -# This is p0f-analyzer.pl, a program to continuously read log reports from p0f -# utility, keep results in cache for a couple of minutes, and answer queries -# over UDP from some program (like amavisd-new) about collected data. -# -# Author: Mark Martinec -# -# Copyright (c) 2006, Mark Martinec -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation are -# those of the authors and should not be interpreted as representing official -# policies, either expressed or implied, of the Jozef Stefan Institute. - -# (the above license is the 2-clause BSD license, also known as -# a "Simplified BSD License", and pertains to this program only) -# -# Patches and problem reports are welcome. -# The latest version of this program is available at: -# http://www.ijs.si/software/amavisd/ -#------------------------------------------------------------------------------ - - use strict; - use re 'taint'; - use Errno qw(EAGAIN EINTR); - use Socket; - use vars qw($VERSION); - $VERSION = '1.401'; - -# Example usage: -# p0f -i bge0 -l 'tcp dst port 25' 2>&1 | p0f-analyzer.pl 2345 -# -# In the p0f filter expression above specify an IP address of this host where -# your MTA is listening for incoming mail, in place of host.example.com above. -# Match the UDP port number (like 2345 above) with the port to which a client -# will be sending queries ($os_fingerprint_method in amavisd.conf). - - # argument should be a free UDP port where queries will be accepted on - $ARGV[0] =~ /^[0-9]+\z/ or die <<'EOD'; -Specify a valid UDP port as an argument. - -Usage: - p0f-analyzer.pl - -Example usage: - p0f -l 'tcp dst port 25' 2>&1 | p0f-analyzer.pl 2345 -EOD - - my($port) = untaint($ARGV[0]); - -# my($bind_addr) = '0.0.0.0'; # bind to all IPv4 interfaces - my($bind_addr) = '127.0.0.1'; # bind just to a loopback interface - - my(@inet_acl) = qw( 127.0.0.1 ); # list of IP addresses from which queries - # will be accepted, others are ignored - my($retention_time) = 10*60; # time to keep collected information in cache - my($debug) = 0; # nonzero enables log messages to STDERR - - do_log(1, "p0f-analyzer version %s starting", $VERSION); - do_log(1, "listening on UDP port %s, allowed queries from: %s", - $port, join(", ",@inet_acl)); - socket(S, PF_INET, SOCK_DGRAM, getprotobyname('udp')) or die "socket: $!"; - - my($packed_addr); - $packed_addr = inet_aton($bind_addr) - or die "inet_aton: bad IP address [$bind_addr]: $!"; - bind(S, sockaddr_in($port,$packed_addr)) - or die "binding to [$bind_addr] failed: $!"; - my($fn_sock) = fileno(S); my($fn_stdin) = fileno(STDIN); - my($rin,$rout); $rin = ''; - vec($rin,$fn_sock,1) = 1; vec($rin,$fn_stdin,1) = 1; - my(%src); my($cnt_since_cleanup) = 0; - binmode(STDIN) or die "Can't set STDIN binmode: $!"; - for (;;) { - my($nfound,$timeleft) = select($rout=$rin, undef, undef, undef); - my($now) = time; - if (vec($rout,$fn_sock,1)) { - my($port,$iaddr,$paddr,$clientaddr); my($inbuf); - $paddr = recv(S,$inbuf,64,0); - if (!defined($paddr)) { - if ($!==EAGAIN || $!==EINTR) { - # false alarm, nothing can be read - } else { - die "recv: $!"; - } - } else { - ($port,$iaddr) = sockaddr_in($paddr) if $paddr ne ''; - $clientaddr = inet_ntoa($iaddr) if defined $iaddr; - if (!defined($clientaddr)) { - do_log(1, "query from unknown client"); - } elsif (!grep {$_ eq $clientaddr} @inet_acl) { - do_log(1, "query from non-approved client: %s:%s",$clientaddr,$port); - } elsif ($port < 1024 || $port == 2049 || $port > 65535) { - do_log(1, "query from questionable port: %s:%s", $clientaddr,$port); - } elsif ($inbuf !~ /^([^ ]+) (.*)$/s) { - do_log(1, "invalid query syntax from %s:%s", $clientaddr,$port); - } else { - my($query,$nonce) = ($1,$2); my($src_ip,$src_port); - if ($query =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/s) { - $src_ip = $query; $src_port = 0; # old style query - } elsif ($query =~ /^ \[ ([^\]]*) \] (?: : (\d{1,5}) )? \z/xs) { - $src_ip = $1; $src_port = $2; - if ($src_ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/) { - # IPv4 - } elsif ($src_ip =~ /^ - (?: (?: IPv6: )? 0{0,4} (?: : 0{0,4} ){1,4} : FFFF : )? - ( \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} )\z/xsi) { - $src_ip = $1; # IPv4-mapped IPv6 address, alternative dec, form - } elsif ($src_ip =~ /^ (?: IPv6: )? - [0-9a-f]{0,4} (?: : [0-9a-f]{0,4} ){2,7} - \z/xsi) { - $src_ip =~ s/^IPv6://i; - } elsif ($src_ip =~ /^ (?: IPv6: )? - [0-9a-f]{0,4} (?: : [0-9a-f]{0,4} ){2,5} : - \d{1,3} (?: \. \d{1,3} ){3} \z/xsi) { - $src_ip =~ s/^IPv6://i; - } else { undef $src_ip } - } - $src_port = 0 if !defined($src_port); - if (length($nonce) > 1024) { - do_log(1, "invalid query from %s:%s, nonce too long: %d chrs", - $clientaddr,$port,length($nonce)); - } elsif ($nonce !~ /^([\040-\177].*)\z/s) { - do_log(1, "invalid query from %s:%s, forbidden char in nonce", - $clientaddr,$port); - } elsif (!defined($src_ip) || $src_port > 65535) { - do_log(1, "invalid query from %s:%s, bad IP address or port: %s", - $clientaddr,$port,$query); - } else { - do_log(1, "query from %s:%s: %s", $clientaddr,$port,$inbuf); - my($resp) = ''; - if (exists($src{$src_ip})) { - if ($src_port > 0) { # source port known, must match exactly - $resp = $src{"[$src_ip]:$src_port"}{d} - if exists $src{"[$src_ip]:$src_port"}; - } else { # source port not known, find the closest match - for my $e (@{$src{$src_ip}}) { - if ($resp eq '') { $resp = $e->{d} } - elsif ($e->{d} eq $resp) {} - else { # keep the longest common string head - my($j); my($resp_l) = length($resp); - for ($j=0; $j<$resp_l; $j++) - { last if substr($e->{d},$j,1) ne substr($resp,$j,1) } - if ($j < $resp_l) { -# do_log(1, "TRUNCATED to %d: %s %s => /%s/", -# $j, $resp, $e->{d}, substr($resp,0,$j)); - $resp = substr($resp,0,$j); - } - } - last; - } - } - } - $resp = $query.' '.$nonce.' '.$resp; - do_log(1, "response to %s:%s: %s", $clientaddr,$port,$resp); - defined(send(S, $resp."\015\012", 0, $paddr)) or die "send: $!"; - } - } - } - } - if (vec($rout,$fn_stdin,1)) { - $cnt_since_cleanup++; my($line); $! = 0; - my($nbytes) = sysread(STDIN,$line,1024); - if (!defined($nbytes)) { - if ($!==EAGAIN || $!==EINTR) { - # false alarm, nothing can be read - } else { - die "Read: $!"; - } - } elsif ($nbytes < 1) { # sysread returns 0 at eof - last; # eof - } else { - chomp($line); - local($1,$2,$3,$4,$5,$6); - $line =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)[ -]*(.*) - \ ->\ (\d+\.\d+\.\d+\.\d+):(\d+)\s*(.*)$/x or next; - my($src_ip,$src_port,$src_t,$dst_ip,$dst_port,$src_d) = - ($1,$2,$3,$4,$5,$6); - my($descr) = "$src_t, $src_d"; - my($entry) = { t=>$now, p=>$src_port, c=>1, d=>$descr }; - $src{"[$src_ip]:$src_port"} = $entry; - if (!exists($src{$src_ip})) { - do_log(2, "first: [%s]:%s %.70s", $src_ip,$src_port,$descr); - $src{$src_ip} = [ $entry ]; - } else { - my($found) = 0; - for my $e (@{$src{$src_ip}}) { - if ($e->{d} eq $descr) { - $e->{c}++; $e->{p} = '*'; $e->{t} = $now, $found = 1; - do_log(2, "deja-vu: [%s]:%s, cnt=%d %.70s", - $src_ip,$src_port,$e->{c},$descr); - last; - } - } - if (!$found) { - push(@{$src{$src_ip}}, $entry); - do_log(2, "stored: [%s]:%d %.70s", $src_ip,$src_port,$descr); - } - } - } - if ($cnt_since_cleanup > 50) { - for my $k (keys %src) { - if (ref($src{$k}) ne 'ARRAY') { - if ($src{$k}{t} + $retention_time < $now) { - do_log(2, "EXPIRED: %s, age = %d s", $k, $now - $src{$k}{t}); - delete $src{$k}; - } - } else { - my(@kept) = grep { $_->{t} + $retention_time >= $now } @{$src{$k}}; - if (!@kept) { - do_log(2, "EXPIRED: %s, age = %d s", $k, $now - $src{$k}[0]{t}); - delete $src{$k}; - } elsif (@kept != @{$src{$k}}) { - do_log(2, "SHRUNK: %s, %d -> %d", - $k, scalar(@{$src{$k}}), scalar(@kept)); - @{$src{$k}} = @kept; - } - } - } - $cnt_since_cleanup = 0; - } - } - } - do_log(1, "normal termination"); - exit 0; - -# Return untainted copy of a string (argument can be a string or a string ref) -sub untaint($) { - no re 'taint'; - my($str); - if (defined($_[0])) { - local($1); # avoid Perl taint bug: tainted global $1 propagates taintedness - $str = $1 if (ref($_[0]) ? ${$_[0]} : $_[0]) =~ /^(.*)\z/s; - } - $str; -} - -# write log entry -sub do_log($$;@) { - my($level,$errmsg,@args) = @_; - if ($level <= $debug) { - $errmsg = sprintf($errmsg,@args) if @args; - print STDERR $errmsg,"\n"; - } - 1; -} diff -Nru amavisd-new-2.11.1/README_FILES/README.rspamd amavisd-new-2.12.2/README_FILES/README.rspamd --- amavisd-new-2.11.1/README_FILES/README.rspamd 1970-01-01 00:00:00.000000000 +0000 +++ amavisd-new-2.12.2/README_FILES/README.rspamd 2022-02-05 21:57:15.000000000 +0000 @@ -0,0 +1,77 @@ +=pod + +=head1 Using Rspamd as a spam checker for Amavis + +Amavis can use Rspamd as a spam checker, with Rspamd running either on +the same server or on a remote machine. + +=head2 Example configuration #1 (local Rspamd) + + # Rspamd running on the same machine as Amavis. Default URL + # is http://127.0.0.1:11333/checkv2 , matching Rspamd's + # "normal" worker defaults. + @spam_scanners = ( [ + 'Local Rspamd', 'Amavis::SpamControl::RspamdClient', + + # Adjust scores according to Rspamd's "required score" + # setting (defaults to 15). Scores reported by Rspamd + # will be multiplied with this factor. The following + # adjusts Rspamd scores to SpamAssassin scores. While + # this setting is technically optional, not adjusting + # scores is prone to cause headaches. + score_factor => $sa_tag2_level_deflt / 15.0, + + # MTA name is used to assess validity of existing + # Authentication-Results headers, e.g. if DKIM/DMARC + # validation has already happened. + mta_name => 'mail.example.com', + ] ); + +=head2 Example configuration #2 (remote Rspamd) + + # Rspamd running behind HTTPS-capable proxy using basic + # authentication to control access. + @spam_scanners = ( [ + 'Remote Rspamd', 'Amavis::SpamControl::RspamdClient', + url => 'https://rspamd-proxy.example.com/checkv2', + + # Response timeout in seconds. Default is 60, matching + # Rspamd's standard config for the "normal" worker. + timeout => 42, + + # SSL-options and -credentials passed to LWP::UserAgent, + # see https://metacpan.org/pod/LWP::UserAgent . Default: + # ssl_opts => { verify_hostname => 1 }, + credentials => { + # The following : must match the 'url' + # defined above or credentials won't be transmitted. + netloc => 'rspamd-proxy.example.com:443', + # Remote authentication realm + realm => 'Rspamd restricted access', + username => 'Marco', + password => 'Polo', + }, + + # Don't scan messages remotely if the body size extends + # the following limit (optional setting). + mail_body_size_limit => 32 * 1024, + + score_factor => $sa_tag2_level_deflt / 15.0, + mta_name => 'mail.example.com', + ] ); + +=head2 Requirements + +In addition to Amavis' core requirements, this extension needs +the following additional Perl modules: + + JSON + HTTP::Message + LWP::UserAgent + LWP::Protocol::https + Net::SSLeay + +Should your host OS not provide the necessary packages, these +modules can be obtained via https://www.cpan.org . + +=cut diff -Nru amavisd-new-2.11.1/README_FILES/README.sql-mysql amavisd-new-2.12.2/README_FILES/README.sql-mysql --- amavisd-new-2.11.1/README_FILES/README.sql-mysql 2015-09-24 18:11:12.000000000 +0000 +++ amavisd-new-2.12.2/README_FILES/README.sql-mysql 2022-02-05 21:57:15.000000000 +0000 @@ -227,9 +227,9 @@ dsn_sent char(1), -- was DSN sent? Y/N/q (q=quenched) spam_level float, -- SA spam level (no boosts) message_id varchar(255) DEFAULT '', -- mail Message-ID header field - from_addr varchar(255) CHARACTER SET utf8mb4 COLLATE utf8_bin DEFAULT '', + from_addr varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '', -- mail From header field, UTF8 - subject varchar(255) CHARACTER SET utf8mb4 COLLATE utf8_bin DEFAULT '', + subject varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '', -- mail Subject header field, UTF8 host varchar(255) NOT NULL, -- hostname where amavisd is running PRIMARY KEY (partition_tag,mail_id) diff -Nru amavisd-new-2.11.1/RELEASE_NOTES amavisd-new-2.12.2/RELEASE_NOTES --- amavisd-new-2.11.1/RELEASE_NOTES 2018-10-09 12:33:06.000000000 +0000 +++ amavisd-new-2.12.2/RELEASE_NOTES 2022-02-05 21:57:15.000000000 +0000 @@ -1,4 +1,119 @@ --------------------------------------------------------------------------- + October 13, 2021 +amavis-2.12.2 release notes + +BUG FIXES + +- Allow $timestamp_fmt_mysql to be used with the DBD::MariaDB driver. + Reported by Marcel Evenson. + Issue: https://gitlab.com/amavis/amavis/issues/79 + MR: https://gitlab.com/amavis/amavis/merge_requests/78 + +- Resolve utf8mb4 problems when using DBD-MariaDB. + Reported by Marcel Evenson. + Issue: https://gitlab.com/amavis/amavis/issues/67 + MR: https://gitlab.com/amavis/amavis/merge_requests/80 + +- Set correct originating flag when using milter/AM.PDP without policy banks. + Reported by Henrik K. + Issue: https://gitlab.com/amavis/amavis/issues/61 + MR: https://gitlab.com/amavis/amavis/merge_requests/81 + +- Resolve crash on reload with insufficient permissions. + Amavis now tests whether it is able read and evaluate its + configuration files with dropped privileges. + In case it cannot, amavis fails to start and refuses to reload. + Reported by Michael Orlitzky. + Suggestions by Ralph Seichter and Michael Orlitzky. + Issue: https://gitlab.com/amavis/amavis/issues/10 + MR: https://gitlab.com/amavis/amavis/merge_requests/74 + MR: https://gitlab.com/amavis/amavis/merge_requests/75 + +- Resolve SSL client connection hangups with broken pipe + Reported by @kolbma. + Issue: https://gitlab.com/amavis/amavis/issues/73 + MR: https://gitlab.com/amavis/amavis/merge_requests/71 + +--------------------------------------------------------------------------- + November 13, 2020 +amavis-2.12.1 release notes + +BUG FIXES + +- Generate DKIM record comment line including the 's=' (selector) tag + instead of an 'i=' (identifier) tag when using "amavisd showkeys". + Reported by Juan Orti Alcaine. + Issue: https://gitlab.com/amavis/amavis/issues/63 + MR: https://gitlab.com/amavis/amavis/merge_requests/41 + +- Make sure generated Authentication-Results follow RFC specification. + MR: https://gitlab.com/amavis/amavis/merge_requests/57 + +- Prevent re-encoding of notification templates. + Fixed by Henrik Krohns and Michael Weiser. + Issue: https://gitlab.com/amavis/amavis/issues/62 + MR: https://gitlab.com/amavis/amavis/merge_requests/40 + +- Compare inode numbers as strings. + Reported and implemented by Mark Martinec. + Issue: https://gitlab.com/amavis/amavis/issues/48 + MR: https://gitlab.com/amavis/amavis/merge_requests/21 + +- Resolve MySQL invalid utf8mb4 clause. + Reported by Henrik Krohns. + Issue: https://gitlab.com/amavis/amavis/issues/33 + MR: https://gitlab.com/amavis/amavis/merge_requests/14 + +--------------------------------------------------------------------------- + July 25, 2019 +amavis-2.12.0 release notes + +This software is renamed from amavisd-new to amavis. + +NEW FEATURES + +- Introduce Rspamd client extension + With this extension, Amavis can use Rspamd either running on the same + server or remotely. Connections are made using HTTP/HTTPS depending on + configuration, the latter requiring a HTTPS-capable proxy (like NGINX or + Apache) for Rspamd, which does not natively support HTTPS. Basic + authentication with name/password pairs is also available. + Implemented by Ralph Seichter. + MR: https://gitlab.com/amavis/amavis/merge_requests/11 + +BUG FIXES + +- Treat "not an OLE file" as a successful result + Amavis supports calling the ripOLE program to extract embedded objects + from Microsoft OLE documents. However, not all Microsoft documents + contain said objects, and the underlying file format changes when they + do. Since Amavis can't tell the difference, it passes everything to + ripOLE unconditionally. Amavis now treats the "not an OLE file" error + code of ripOLE as a successful result, proceeds normally and scans the + file as a whole. + Fixed by Michael Orlitzky. + MR: https://gitlab.com/amavis/amavis/merge_requests/9 + +- Fix unix socket path extraction that has prevented a socket based + policy bank to be loaded; + Fixed by Boris Gulay. + MR: https://gitlab.com/amavis/amavis/merge_requests/2 + MR: https://gitlab.com/amavis/amavis/merge_requests/19 + +- Fix DKIM signing for outbound messages. + Fixed by Ralph Seichter. + MR: https://gitlab.com/amavis/amavis/merge_requests/1 + +- Fix unescaped left brace regex warning in run_av() subroutine. + Fixed by Ralph Seichter. + MR: https://gitlab.com/amavis/amavis/merge_requests/10 + Issue: https://gitlab.com/amavis/amavis/issues/31 + +- Mention default value for $myprogram_name in minimal amavisd.conf. + MR: https://gitlab.com/amavis/amavis/merge_requests/12 + Issue: https://gitlab.com/amavis/amavis/issues/36 + +--------------------------------------------------------------------------- October 9, 2018 amavisd-new-2.11.1 release notes