Server IP : 85.214.239.14 / Your IP : 3.138.123.240 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/2/cwd/proc/2/root/proc/2/cwd/proc/3/root/proc/2/cwd/usr/share/perl5/Amavis/ |
Upload File : |
# SPDX-License-Identifier: GPL-2.0-or-later package Amavis::Conf; use strict; use re 'taint'; # constants; intentionally leave value -1 unassigned for compatibility use constant D_TEMPFAIL => -4; use constant D_REJECT => -3; use constant D_BOUNCE => -2; use constant D_DISCARD => 0; use constant D_PASS => 1; # major contents_category constants, in increasing order of importance use constant CC_CATCHALL => 0; use constant CC_CLEAN => 1; # tag_level = "CC_CLEAN,1" use constant CC_MTA => 2; # trouble passing mail back to MTA use constant CC_OVERSIZED => 3; use constant CC_BADH => 4; use constant CC_SPAMMY => 5; # tag2_level (and: tag3_level = CC_SPAMMY,1) use constant CC_SPAM => 6; # kill_level use constant CC_UNCHECKED => 7; use constant CC_BANNED => 8; use constant CC_VIRUS => 9; # # in other words: major_ccat minor_ccat %subject_tag_maps_by_ccat ## if score >= kill level => CC_SPAM 0 ## elsif score >= tag3 level => CC_SPAMMY 1 @spam_subject_tag3_maps ## elsif score >= tag2 level => CC_SPAMMY 0 @spam_subject_tag2_maps ## elsif score >= tag level => CC_CLEAN 1 @spam_subject_tag_maps ## else => CC_CLEAN 0 BEGIN { require Exporter; use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); $VERSION = '2.412'; @ISA = qw(Exporter); %EXPORT_TAGS = ( 'dynamic_confvars' => # per- policy bank settings [qw( $child_timeout $smtpd_timeout $policy_bank_name $protocol $haproxy_target_enabled @inet_acl $myhostname $myauthservid $snmp_contact $snmp_location $myprogram_name $syslog_ident $syslog_facility $log_level $log_templ $log_recip_templ $enable_log_capture_dump $forward_method $notify_method $resend_method $report_format $release_method $requeue_method $release_format $attachment_password $attachment_email_name $attachment_outer_name $mail_digest_algorithm $mail_part_digest_algorithm $os_fingerprint_method $os_fingerprint_dst_ip_and_port $originating @smtpd_discard_ehlo_keywords $soft_bounce $propagate_dsn_if_possible $terminate_dsn_on_notify_success $amavis_auth_user $amavis_auth_pass $auth_reauthenticate_forwarded $auth_required_out $auth_required_inp $auth_required_release @auth_mech_avail $tls_security_level_in $tls_security_level_out $local_client_bind_address $smtpd_message_size_limit $localhost_name $smtpd_greeting_banner $smtpd_quit_banner $mailfrom_to_quarantine $warn_offsite $bypass_decode_parts @decoders @av_scanners @av_scanners_backup @spam_scanners $first_infected_stops_scan $virus_scanners_failure_is_fatal $sa_spam_level_char $sa_mail_body_size_limit $penpals_bonus_score $penpals_halflife $bounce_killer_score $reputation_factor $undecipherable_subject_tag $localpart_is_case_sensitive $recipient_delimiter $replace_existing_extension $hdr_encoding $bdy_encoding $hdr_encoding_qb $allow_disclaimers $outbound_disclaimers_only $prepend_header_fields_hdridx $allow_fixing_improper_header $allow_fixing_improper_header_folding $allow_fixing_long_header_lines %allowed_added_header_fields %prefer_our_added_header_fields %allowed_header_tests $X_HEADER_TAG $X_HEADER_LINE $remove_existing_x_scanned_headers $remove_existing_spam_headers %sql_clause $partition_tag %local_delivery_aliases $banned_namepath_re $per_recip_whitelist_sender_lookup_tables $per_recip_blacklist_sender_lookup_tables @anomy_sanitizer_args @altermime_args_defang @altermime_args_disclaimer @disclaimer_options_bysender_maps %signed_header_fields @dkim_signature_options_bysender_maps $enable_dkim_verification $enable_dkim_signing $dkim_signing_service $dkim_minimum_key_bits $enable_ldap $enable_ip_repu $redis_logging_key $ip_repu_score_limit @local_domains_maps @mynetworks_maps @client_ipaddr_policy @ip_repu_ignore_maps @forward_method_maps @newvirus_admin_maps @banned_filename_maps @spam_quarantine_bysender_to_maps @spam_tag_level_maps @spam_tag2_level_maps @spam_tag3_level_maps @spam_kill_level_maps @spam_subject_tag_maps @spam_subject_tag2_maps @spam_subject_tag3_maps @spam_dsn_cutoff_level_maps @spam_dsn_cutoff_level_bysender_maps @spam_crediblefrom_dsn_cutoff_level_maps @spam_crediblefrom_dsn_cutoff_level_bysender_maps @spam_quarantine_cutoff_level_maps @spam_notifyadmin_cutoff_level_maps @whitelist_sender_maps @blacklist_sender_maps @score_sender_maps @author_to_policy_bank_maps @signer_reputation_maps @message_size_limit_maps @debug_sender_maps @debug_recipient_maps @bypass_virus_checks_maps @bypass_spam_checks_maps @bypass_banned_checks_maps @bypass_header_checks_maps @viruses_that_fake_sender_maps @virus_name_to_spam_score_maps @virus_name_to_policy_bank_maps @remove_existing_spam_headers_maps @sa_userconf_maps @sa_username_maps %final_destiny_maps_by_ccat %forward_method_maps_by_ccat %lovers_maps_by_ccat %defang_maps_by_ccat %subject_tag_maps_by_ccat %quarantine_method_by_ccat %quarantine_to_maps_by_ccat %notify_admin_templ_by_ccat %notify_recips_templ_by_ccat %notify_sender_templ_by_ccat %notify_autoresp_templ_by_ccat %notify_release_templ_by_ccat %notify_report_templ_by_ccat %warnsender_by_ccat %hdrfrom_notify_admin_by_ccat %mailfrom_notify_admin_by_ccat %hdrfrom_notify_recip_by_ccat %mailfrom_notify_recip_by_ccat %hdrfrom_notify_sender_by_ccat %hdrfrom_notify_release_by_ccat %hdrfrom_notify_report_by_ccat %admin_maps_by_ccat %warnrecip_maps_by_ccat %always_bcc_by_ccat %dsn_bcc_by_ccat %addr_extension_maps_by_ccat %addr_rewrite_maps_by_ccat %smtp_reason_by_ccat )], 'confvars' => # global settings (not per-policy, not per-recipient) [qw( $myproduct_name $myversion_id $myversion_id_numeric $myversion_date $myversion $instance_name @additional_perl_modules $MYHOME $TEMPBASE $QUARANTINEDIR $quarantine_subdir_levels $daemonize $courierfilter_shutdown $pid_file $lock_file $db_home $enable_db $enable_zmq @zmq_sockets $mail_id_size_bits $daemon_user @daemon_groups $daemon_chroot_dir $path $DEBUG %i_know_what_i_am_doing $do_syslog $logfile $allow_preserving_evidence $enable_log_capture $log_short_templ $log_verbose_templ $logline_maxlen $nanny_details_level $max_servers $max_requests $min_servers $min_spare_servers $max_spare_servers %current_policy_bank %policy_bank %interface_policy @listen_sockets $inet_socket_port $inet_socket_bind $listen_queue_size $smtpd_recipient_limit $unix_socketname $unix_socket_mode $smtp_connection_cache_on_demand $smtp_connection_cache_enable %smtp_tls_client_verifycn_name_maps %smtp_tls_client_options %smtpd_tls_server_options $smtpd_tls_cert_file $smtpd_tls_key_file $macro_tests_sanity_limit $enforce_smtpd_message_size_limit_64kb_min $MAXLEVELS $MAXFILES $MIN_EXPANSION_QUOTA $MIN_EXPANSION_FACTOR $MAX_EXPANSION_QUOTA $MAX_EXPANSION_FACTOR $database_sessions_persistent $lookup_maps_imply_sql_and_ldap @lookup_sql_dsn @storage_sql_dsn @storage_redis_dsn $storage_redis_ttl $redis_logging_queue_size_limit $sql_schema_version $timestamp_fmt_mysql $sql_quarantine_chunksize_max $sql_allow_8bit_address $sql_lookups_no_at_means_domain $ldap_lookups_no_at_means_domain $sql_store_info_for_all_msgs $default_ldap $trim_trailing_space_in_lookup_result_fields @keep_decoded_original_maps @map_full_type_to_short_type_maps %banned_rules $penpals_threshold_low $penpals_threshold_high %dkim_signing_keys_by_domain @dkim_signing_keys_list @dkim_signing_keys_storage $file $altermime $enable_anomy_sanitizer )], 'sa' => # global SpamAssassin settings [qw( $spamcontrol_obj $sa_num_instances $helpers_home $sa_configpath $sa_siteconfigpath $sa_userprefs_file $sa_local_tests_only $sa_timeout $sa_debug $dspam $sa_spawned )], 'platform' => [qw( $profiling $can_truncate $my_pid $AF_INET6 $have_inet4 $have_inet6 $io_socket_module_name &D_TEMPFAIL &D_REJECT &D_BOUNCE &D_DISCARD &D_PASS &CC_CATCHALL &CC_CLEAN &CC_MTA &CC_OVERSIZED &CC_BADH &CC_SPAMMY &CC_SPAM &CC_UNCHECKED &CC_BANNED &CC_VIRUS %ccat_display_names %ccat_display_names_major )], # other variables settable by user in amavisd.conf, # but not directly accessible to the program 'hidden_confvars' => [qw( $mydomain )], 'legacy_dynamic_confvars' => # the rest of the program does not use these settings directly and they # should not be visible in, or imported to other modules, but may be # referenced indirectly through *_by_ccat variables for compatibility [qw( $final_virus_destiny $final_banned_destiny $final_unchecked_destiny $final_spam_destiny $final_bad_header_destiny @virus_lovers_maps @spam_lovers_maps @unchecked_lovers_maps @banned_files_lovers_maps @bad_header_lovers_maps $always_bcc $dsn_bcc $mailfrom_notify_sender $mailfrom_notify_recip $mailfrom_notify_admin $mailfrom_notify_spamadmin $hdrfrom_notify_sender $hdrfrom_notify_recip $hdrfrom_notify_admin $hdrfrom_notify_spamadmin $hdrfrom_notify_release $hdrfrom_notify_report $notify_virus_admin_templ $notify_spam_admin_templ $notify_virus_recips_templ $notify_spam_recips_templ $notify_virus_sender_templ $notify_spam_sender_templ $notify_sender_templ $notify_release_templ $notify_report_templ $notify_autoresp_templ $warnbannedsender $warnbadhsender $defang_virus $defang_banned $defang_spam $defang_bad_header $defang_undecipherable $defang_all $virus_quarantine_method $banned_files_quarantine_method $unchecked_quarantine_method $spam_quarantine_method $bad_header_quarantine_method $clean_quarantine_method $archive_quarantine_method @virus_quarantine_to_maps @banned_quarantine_to_maps @unchecked_quarantine_to_maps @spam_quarantine_to_maps @bad_header_quarantine_to_maps @clean_quarantine_to_maps @archive_quarantine_to_maps @virus_admin_maps @banned_admin_maps @spam_admin_maps @bad_header_admin_maps @spam_modifies_subj_maps @warnvirusrecip_maps @warnbannedrecip_maps @warnbadhrecip_maps @addr_extension_virus_maps @addr_extension_spam_maps @addr_extension_banned_maps @addr_extension_bad_header_maps )], 'legacy_confvars' => # legacy variables, predeclared for compatibility of amavisd.conf # The rest of the program does not use them directly and they should # not be visible in other modules, but may be referenced through # @*_maps variables for backward compatibility [qw( %local_domains @local_domains_acl $local_domains_re @mynetworks @ip_repu_ignore_networks %bypass_virus_checks @bypass_virus_checks_acl $bypass_virus_checks_re %bypass_spam_checks @bypass_spam_checks_acl $bypass_spam_checks_re %bypass_banned_checks @bypass_banned_checks_acl $bypass_banned_checks_re %bypass_header_checks @bypass_header_checks_acl $bypass_header_checks_re %virus_lovers @virus_lovers_acl $virus_lovers_re %spam_lovers @spam_lovers_acl $spam_lovers_re %banned_files_lovers @banned_files_lovers_acl $banned_files_lovers_re %bad_header_lovers @bad_header_lovers_acl $bad_header_lovers_re %virus_admin %spam_admin $newvirus_admin $virus_admin $banned_admin $bad_header_admin $spam_admin $warnvirusrecip $warnbannedrecip $warnbadhrecip $virus_quarantine_to $banned_quarantine_to $unchecked_quarantine_to $spam_quarantine_to $spam_quarantine_bysender_to $bad_header_quarantine_to $clean_quarantine_to $archive_quarantine_to $keep_decoded_original_re $map_full_type_to_short_type_re $banned_filename_re $viruses_that_fake_sender_re $sa_tag_level_deflt $sa_tag2_level_deflt $sa_tag3_level_deflt $sa_kill_level_deflt $sa_quarantine_cutoff_level @spam_notifyadmin_cutoff_level_maps $sa_dsn_cutoff_level $sa_crediblefrom_dsn_cutoff_level $sa_spam_modifies_subj $sa_spam_subject_tag1 $sa_spam_subject_tag %whitelist_sender @whitelist_sender_acl $whitelist_sender_re %blacklist_sender @blacklist_sender_acl $blacklist_sender_re $addr_extension_virus $addr_extension_spam $addr_extension_banned $addr_extension_bad_header $sql_select_policy $sql_select_white_black_list $gets_addr_in_quoted_form @debug_sender_acl $arc $bzip2 $lzop $lha $unarj $gzip $uncompress $unfreeze $unrar $zoo $pax $cpio $ar $rpm2cpio $cabextract $ripole $tnef $gunzip $bunzip2 $unlzop $unstuff $SYSLOG_LEVEL $syslog_priority $append_header_fields_to_bottom $insert_received_line $notify_xmailer_header $relayhost_is_client $sa_spam_report_header $sa_auto_whitelist $warnvirussender $warnspamsender $enable_global_cache $virus_check_negative_ttl $virus_check_positive_ttl $spam_check_negative_ttl $spam_check_positive_ttl $daemon_group )], ); Exporter::export_tags qw(dynamic_confvars confvars sa platform hidden_confvars legacy_dynamic_confvars legacy_confvars); 1; } # BEGIN use POSIX (); use Carp (); use Errno qw(ENOENT EACCES EBADF); use vars @EXPORT; sub c($); sub cr($); sub ca($); sub dkim_key($$$;@); # prototypes use subs qw(c cr ca dkim_key); # access subroutines to config vars and keys BEGIN { push(@EXPORT,qw(c cr ca dkim_key)) } # access to dynamic config variables, returns a scalar config variable value; # one level of indirection is allowed # sub c($) { my $var = $current_policy_bank{$_[0]}; if (!defined $var) { my $name = $_[0]; if (!exists $current_policy_bank{$name}) { Carp::croak(sprintf('No entry "%s" in policy bank "%s"', $name, $current_policy_bank{'policy_bank_name'})); } } my $r = ref $var; !$r ? $var : $r eq 'SCALAR' || $r eq 'REF' ? $$var : $var; } # return a ref to a config variable value, or undef if var is undefined # sub cr($) { my $var = $current_policy_bank{$_[0]}; if (!defined $var) { my $name = $_[0]; if (!exists $current_policy_bank{$name}) { Carp::croak(sprintf('No entry "%s" in policy bank "%s"', $name, $current_policy_bank{'policy_bank_name'})); } } ref $var ? $var : defined $var ? \$var : undef; } # return a ref to a config variable value (which is supposed to be an array), # converting undef to an empty array, and a scalar to a one-element array # if necessary # sub ca($) { my $var = $current_policy_bank{$_[0]}; if (!defined $var) { my $name = $_[0]; if (!exists $current_policy_bank{$name}) { Carp::croak(sprintf('No entry "%s" in policy bank "%s"', $name, $current_policy_bank{'policy_bank_name'})); } } ref $var ? $var : defined $var ? [$var] : []; } sub deprecate_var($$$) { my($data_type, $var_name, $init_value) = @_; my $code = <<'EOD'; tie(%n, '%p', %v) or die 'Tieing a variable %n failed'; package %p; use strict; use Carp (); sub TIESCALAR { my($class,$val) = @_; bless \$val, $class } sub FETCH { my $self = shift; $$self } sub STORE { my($self,$newv) = @_; my $oldv = $$self; if ((defined $oldv || defined $newv) && (%t)) { Carp::carp('Variable %n was retired, changing its value has no effect.' . " See release notes.\n"); } $$self = $newv; } 1; EOD if ($data_type eq 'bool') { $code =~ s{%t}'($oldv ? 1 : 0) != ($newv ? 1 : 0)'g; } elsif ($data_type eq 'num') { $code =~ s{%t}'!defined $oldv || !defined $newv || $oldv != $newv'g; } elsif ($data_type eq 'str') { $code =~ s{%t}'!defined $oldv || !defined $newv || $oldv ne $newv'g; } else { die "Error deprecating a variable $var_name: bad type $data_type"; } $code =~ s/%n/$var_name/g; $code =~ s/%v/\$init_value/g; my $barename = $var_name; $barename =~ s/^[\$\@%&]//; $code =~ s/%p/Amavis::Deprecate::$barename/g; eval $code or do { chomp $@; die "Error deprecating a variable $var_name: $@" }; } # Store a private DKIM signing key for a given domain and selector. # The argument $key can be a Mail::DKIM::PrivateKey object or a file # name containing a key in a PEM format (e.g. as generated by openssl). # For compatibility with dkim_milter the signing domain can include a '*' # as a wildcard - this is not recommended as this way amavisd could produce # signatures which have no corresponding public key published in DNS. # The proper way is to have one dkim_key entry for each published DNS RR. # Optional arguments can provide additional information about the resource # record (RR) of a public key, i.e. its options according to RFC 6376. # The subroutine is typically called from a configuration file, once for # each signing key available. # sub dkim_key($$$;@) { my($domain,$selector,$key) = @_; shift; shift; shift; @_%2 == 0 or die "dkim_key: a list of key/value pairs expected as options\n"; my(%key_options) = @_; # remaining args are options from a public key RR defined $domain && $domain ne '' or die "dkim_key: domain must not be empty: ($domain,$selector,$key)"; defined $selector && $selector ne '' or die "dkim_key: selector must not be empty: ($domain,$selector,$key)"; my $key_storage_ind; if (ref $key) { # key already preprocessed and provided as an object push(@dkim_signing_keys_storage, [$key]); $key_storage_ind = $#dkim_signing_keys_storage; } else { # assume a name of a file containing a private key in PEM format my $fname = $key; my $pem_fh = IO::File->new; # open a file with a private key $pem_fh->open($fname,'<') or die "Can't open PEM file $fname: $!"; my(@stat_list) = stat($pem_fh); # soft-link friendly @stat_list or warn "Error accessing $fname: $!"; my($dev,$inode) = @stat_list; # 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 eq $inode) { $key_storage_ind = $j; last } } } if (!defined($key_storage_ind)) { # read file and store its contents as a new entry $key = ''; Amavis::Util::read_file($pem_fh,\$key); my $key_fit = $key; # shrink allocated storage size to actual size undef $key; # release storage push(@dkim_signing_keys_storage, [$key_fit, $dev, $inode, $fname]); $key_storage_ind = $#dkim_signing_keys_storage; } $pem_fh->close or die "Error closing file $fname: $!"; $key_options{k} = 'rsa' if defined $key_options{k}; # force RSA } # possibly the $domain is a regexp $domain = Amavis::Util::idn_to_ascii($domain) if !ref $domain; $selector = Amavis::Util::idn_to_ascii($selector); $key_options{domain} = $domain; $key_options{selector} = $selector; $key_options{key_storage_ind} = $key_storage_ind; if (@dkim_signing_keys_list > 100) { # sorry, skip the test to avoid slow O(n^2) searches } else { !grep($_->{domain} eq $domain && $_->{selector} eq $selector, @dkim_signing_keys_list) or die "dkim_key: selector $selector for domain $domain already in use\n"; } $key_options{key_ind} = $#dkim_signing_keys_list + 1; push(@dkim_signing_keys_list, \%key_options); # using a list preserves order } # essential initializations, right at the program start time, may run as root! # use vars qw($read_config_files_depth @actual_config_files); BEGIN { # init_primary: version, base policy bank $myprogram_name = $0; # typically 'amavisd' local $1; $myprogram_name =~ s{([^/]*)\z}{$1}s; $myproduct_name = 'amavis'; $myversion_id = '2.13.0'; $myversion_date = '20230106'; $myversion = "$myproduct_name-$myversion_id ($myversion_date)"; $myversion_id_numeric = # x.yyyzzz, allows numerical compare, like Perl $] sprintf('%8.6f', $1 + ($2 + $3/1000)/1000) if $myversion_id =~ /^(\d+)(?:\.(\d*)(?:\.(\d*))?)?(.*)$/s; $sql_schema_version = $myversion_id_numeric; $read_config_files_depth = 0; # initialize policy bank hash to contain dynamic config settings for my $tag (@EXPORT_TAGS{'dynamic_confvars', 'legacy_dynamic_confvars'}) { for my $v (@$tag) { local($1,$2); if ($v !~ /^([%\$\@])(.*)\z/s) { die "Unsupported variable type: $v" } else { no strict 'refs'; my($type,$name) = ($1,$2); $current_policy_bank{$name} = $type eq '$' ? \${"Amavis::Conf::$name"} : $type eq '@' ? \@{"Amavis::Conf::$name"} : $type eq '%' ? \%{"Amavis::Conf::$name"} : undef; } } } $current_policy_bank{'policy_bank_name'} = ''; # builtin policy $current_policy_bank{'policy_bank_path'} = ''; $policy_bank{''} = { %current_policy_bank }; # copy 1; } # end BEGIN - init_primary # boot-time initializations of simple global settings, may run as root! # BEGIN { # serves only as a quick default for other configuration settings $MYHOME = '/var/amavis'; $mydomain = '!change-mydomain-variable!.example.com';#intentionally bad deflt # Create debugging output - true: log to stderr; false: log to syslog/file $DEBUG = 0; # Is Devel::NYTProf profiler loaded? $profiling = 1 if DB->UNIVERSAL::can('enable_profile'); # In case of trouble, allow preserving temporary files for forensics $allow_preserving_evidence = 1; # Cause Net::Server parameters 'background' and 'setsid' to be set, # resulting in the process to detach itself from the terminal $daemonize = 1; # Net::Server pre-forking settings - defaults, overruled by amavisd.conf $max_servers = 2; # number of pre-forked children $max_requests = 20; # retire a child after that many accepts, 0=unlimited # timeout for our processing: $child_timeout = 8*60; # abort child if it does not complete a task in n sec # timeout for waiting on client input: $smtpd_timeout = 8*60; # disconnect session if client is idle for too long; # $smtpd_timeout should be higher than Postfix's max_idle (default 100s) # Assume STDIN is a courierfilter pipe and shutdown when it becomes readable $courierfilter_shutdown = 0; # Can file be truncated? # Set to 1 if 'truncate' works (it is XPG4-UNIX standard feature, # not required by Posix). # Things will go faster with SMTP-in, otherwise (e.g. with milter) # it makes no difference as file truncation will not be used. $can_truncate = 1; # Customizable notification messages, logging $syslog_ident = 'amavis'; $syslog_facility = 'mail'; $log_level = 0; # should be less than (1023 - prefix), i.e. 980, # to avoid syslog truncating lines; see sub write_log $logline_maxlen = 980; $nanny_details_level = 1; # register_proc verbosity: 0, 1, 2 # $inner_sock_specs in amavis-services should match one of the sockets # in the @zmq_sockets list # @zmq_sockets = ( "ipc://$MYHOME/amavisd-zmq.sock" ); # after-default # $enable_zmq = undef; # load optional module Amavis::ZMQ # # (interface to 0MQ or Crossroads I/O) # $enable_db = undef; # load optional modules Amavis::DB & Amavis::DB::SNMP # $enable_dkim_signing = undef; # $enable_dkim_verification = undef; $enable_ip_repu = 1; # ignored when @storage_redis_dsn is empty # a key (string) for a redis list serving as a queue of json events # for logstash / elasticsearch use; undef or empty or '0' disables # logging of events to redis $redis_logging_key = undef; # e.g. "amavis-log"; # a limit on the length of a redis list - new log events will be dropped # while the queue size limit is exceeded; undef or 0 disables logging; # reasonable value: 100000, takes about 250 MB of memory in a redis server # when noone is pulling events from the list $redis_logging_queue_size_limit = undef; $reputation_factor = 0.2; # DKIM reputation: a value between 0 and 1, # controlling the amount of 'bending' of a calculated spam score # towards a fixed score assigned to a signing domain (its 'reputation') # through @signer_reputation_maps; the formula is: # adjusted_spam_score = f*reputation + (1-f)*spam_score # which has the same semantics as auto_whitelist_factor in SpamAssassin AWL # keep SQL, LDAP and Redis sessions open when idle $database_sessions_persistent = 1; $lookup_maps_imply_sql_and_ldap = 1; # set to 0 to disable # Algorithm name for generating a mail header digest and a mail body digest: # either 'MD5' (will use Digest::MD5, fastest and smallest digest), or # anything else accepted by Digest::SHA->new(), e.g. 'SHA-1' or 'SHA-256'. # The generated digest may end up as part of a quarantine file name # or via macro %b in log or notification templates. # $mail_digest_algorithm = 'MD5'; # or 'SHA-1' or 'SHA-256', ... # Algorithm name for generating digests of decoded MIME parts of a message. # The value is an algorithm name as accepted by Digest::SHA->new(), # e.g. 'SHA-1' or 'SHA-256' or 'sha256', or a string 'MD5' which implies # the MD5 algorithm as implemented by a module Digest::MD5. # For compatibility with SpamAssassin the chosen algorithm should be SHA1, # otherwise bayes tokens won't match those generated by sa-learn. # Undefined value disables generating digests of MIME parts. # $mail_part_digest_algorithm = 'SHA1'; # Where to find SQL server(s) and database to support SQL lookups? # A list of triples: (dsn,user,passw). Specify more than one # for multiple (backup) SQL servers. # #@storage_sql_dsn = #@lookup_sql_dsn = # ( ['DBI:mysql:mail:host1', 'some-username1', 'some-password1'], # ['DBI:mysql:mail:host2', 'some-username2', 'some-password2'] ); # Does a database mail address field with no '@' character represent a # local username or a domain name? By default it implies a username in # SQL and LDAP lookups (but represents a domain in hash and acl lookups), # so domain names in SQL and LDAP should be specified as '@domain'. # Setting these to true will cause 'xxx' to be interpreted as a domain # name, just like in hash or acl lookups. # $sql_lookups_no_at_means_domain = 0; $ldap_lookups_no_at_means_domain = 0; # Maximum size (in bytes) for data written to a field 'quarantine.mail_text' # when quarantining to SQL. Must not exceed size allowed for a data type # on a given SQL server. It also determines a buffer size in amavisd. # Too large a value may exceed process virtual memory limits or just waste # memory, too small a value splits large mail into too many chunks, which # may be less efficient to process. # $sql_quarantine_chunksize_max = 16384; $sql_allow_8bit_address = 0; # the length of mail_id in bits, must be an integral multiple of 24 # (i.e. divisible by 6 and 8); the mail_id is represented externally # as a base64url-encoded string of size $mail_id_size_bits / 6 # $mail_id_size_bits = 72; # 24, 48, 72, 96 # redis data (penpals) expiration - time-to-live in seconds of stored items $storage_redis_ttl = 16*24*60*60; # 16 days (only affects penpals data) $sql_store_info_for_all_msgs = 1; $penpals_bonus_score = undef; # maximal (positive) score value by which spam # score is lowered when sender is known to have previously received mail # from our local user from this mail system. Zero or undef disables # pen pals lookups in Redis or in SQL tables msgs and msgrcpt, and # is a default. $penpals_halflife = 7*24*60*60; # exponential decay time constant in seconds; # pen pal bonus is halved for each halflife period since the last mail # sent by a local user to a current message's sender $penpals_threshold_low = 1.0; # SA score below which pen pals lookups are # not performed to save time; undef lets the threshold be ignored; $penpals_threshold_high = undef; # when (SA_score - $penpals_bonus_score > $penpals_threshold_high) # pen pals lookup will not be performed to save time, as it could not # influence blocking of spam even at maximal penpals bonus (age=0); # usual choice for value would be a kill level or other reasonably high # value; undef lets the threshold be ignored and is a default (useful # for testing and statistics gathering); $bounce_killer_score = 0; # # Receiving mail related # $unix_socketname = '/var/amavis/amavisd.sock'; # e.g. milter or release # $inet_socket_port = 10024; # accept SMTP on this TCP port # $inet_socket_port = [10024,10026,10027]; # ...possibly on more than one $AF_INET6 = eval { require Socket; Socket::AF_INET6() } || eval { require Socket6; Socket6::AF_INET6() }; # prefer using module IO::Socket::IP if available, # otherwise fall back to IO::Socket::INET6 or to IO::Socket::INET # if (eval { require IO::Socket::IP }) { $io_socket_module_name = 'IO::Socket::IP'; } elsif (eval { require IO::Socket::INET6 }) { $io_socket_module_name = 'IO::Socket::INET6'; } elsif (eval { require IO::Socket::INET }) { $io_socket_module_name = 'IO::Socket::INET'; } $have_inet4 = # can we create a PF_INET socket? defined $io_socket_module_name && eval { my $sock = $io_socket_module_name->new(LocalAddr => '0.0.0.0', Proto => 'tcp'); $sock->close or die "error closing socket: $!" if $sock; $sock ? 1 : undef; }; $have_inet6 = # can we create a PF_INET6 socket? defined $io_socket_module_name && $io_socket_module_name ne 'IO::Socket::INET' && eval { my $sock = $io_socket_module_name->new(LocalAddr => '::', Proto => 'tcp'); $sock->close or die "error closing socket: $!" if $sock; $sock ? 1 : undef; }; # if (!$have_inet6 && $io_socket_module_name ne 'IO::Socket::INET') { # # ok, let's stay on proven grounds, use the IO::Socket::INET anyway # if (eval { require IO::Socket::INET }) { # $io_socket_module_name = 'IO::Socket::INET'; # } # } # bind socket to a loopback interface if (Net::Server->VERSION < 2) { $inet_socket_bind = '127.0.0.1'; } else { # requires Net::Server 2 or a patched 0.99 with IPv6 support) $inet_socket_bind = $have_inet4 && $have_inet6 ? ['127.0.0.1', '[::1]'] : $have_inet6 ? '[::1]' : '127.0.0.1'; } @inet_acl = qw( 127.0.0.1 [::1] ); # allow SMTP access only from localhost @mynetworks = qw( 127.0.0.0/8 [::1] 169.254.0.0/16 [fe80::]/10 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 [fc00::]/7 ); # consider also RFC 6598: 100.64.0.0/10 $originating = 0; # a boolean, initially reflects @mynetworks match, # but may be modified later through a policy bank $forward_method = $have_inet6 && !$have_inet4 ? 'smtp:[::1]:10025' : 'smtp:[127.0.0.1]:10025'; $notify_method = $forward_method; $resend_method = undef; # overrides $forward_method on defanging if nonempty $release_method = undef; # overrides $notify_method on releasing # from quarantine if nonempty $requeue_method = # requeuing release from a quarantine $have_inet6 && !$have_inet4 ? 'smtp:[::1]:25' : 'smtp:[127.0.0.1]:25'; $release_format = 'resend'; # (dsn), (arf), attach, plain, resend $report_format = 'arf'; # (dsn), arf, attach, plain, resend # when $release_format is 'attach', the following control the attachment: $attachment_password = ''; # '': no pwd; undef: PIN; code ref; or static str $attachment_email_name = 'msg-%m.eml'; $attachment_outer_name = 'msg-%m.zip'; $virus_quarantine_method = 'local:virus-%m'; $banned_files_quarantine_method = 'local:banned-%m'; $spam_quarantine_method = 'local:spam-%m.gz'; $bad_header_quarantine_method = 'local:badh-%m'; $unchecked_quarantine_method = undef; # 'local:unchecked-%m'; $clean_quarantine_method = undef; # 'local:clean-%m'; $archive_quarantine_method = undef; # 'local:archive-%m.gz'; $prepend_header_fields_hdridx = 0; # normally 0, use 1 for co-existence # with signing DK and DKIM milters $remove_existing_x_scanned_headers = 0; $remove_existing_spam_headers = 1; # fix improper header fields in passed or released mail - this setting # is a pre-condition for $allow_fixing_improper_header_folding and similar # (future) fixups; (desirable, but may break DKIM validation of messages # with illegal header section) $allow_fixing_improper_header = 1; # fix improper folded header fields made up entirely of whitespace, by # removing all-whitespace lines ($allow_fixing_improper_header must be true) $allow_fixing_improper_header_folding = 1; # truncate header section lines longer than 998 characters as limited # by the RFC 5322 ($allow_fixing_improper_header must be true) $allow_fixing_long_header_lines = 1; # encoding (charset in MIME terminology) # to be used in RFC 2047-encoded ... $hdr_encoding = 'UTF-8'; # ... header field bodies $bdy_encoding = 'UTF-8'; # ... notification body text # encoding (encoding in MIME terminology) $hdr_encoding_qb = 'Q'; # quoted-printable (default) # $hdr_encoding_qb = 'B'; # base64 $smtpd_recipient_limit = 1100; # max recipients (RCPT TO) - sanity limit $macro_tests_sanity_limit = 50; # truncation of Tests: [BAYES_999=9,BAYES_99=7,...] # $myhostname is used by SMTP server module in the initial SMTP welcome line, # in inserted Received: lines, Message-ID in notifications, log entries, ... $myhostname = (POSIX::uname)[1]; # should be a FQDN ! $snmp_contact = ''; # a value of sysContact OID $snmp_location = ''; # a value of sysLocation OID $smtpd_greeting_banner = '${helo-name} ${protocol} ${product} service ready'; $smtpd_quit_banner = '${helo-name} ${product} closing transmission channel'; $enforce_smtpd_message_size_limit_64kb_min = 1; # $localhost_name is the name of THIS host running amavisd # (often just 'localhost'). It is used in HELO SMTP command # when reinjecting mail back to MTA via SMTP for final delivery, # and in inserted Received header field $localhost_name = 'localhost'; $propagate_dsn_if_possible = 1; # pass on DSN if MTA announces this # capability; useful to be turned off globally but enabled in # MYNETS policy bank to hide internal mail routing from outsiders $terminate_dsn_on_notify_success = 0; # when true=>handle DSN NOTIFY=SUCCESS # locally, do not let NOTIFY=SUCCESS propagate to MTA (but allow # other DSN options like NOTIFY=NEVER/FAILURE/DELAY, ORCPT, RET, # and ENVID to propagate if possible) #@auth_mech_avail = ('PLAIN','LOGIN'); # empty list disables incoming AUTH #$auth_required_inp = 1; # incoming SMTP authentication required by amavisd? #$auth_required_out = 1; # SMTP authentication required by MTA $auth_required_release = 1; # secret_id is required for a quarantine release $tls_security_level_in = undef; # undef, 'may', 'encrypt', ... $tls_security_level_out = undef; # undef, 'may', 'encrypt', ... # Server side certificate and key: $smtpd_tls_cert_file, $smtpd_tls_key_file. # These two settings are now deprecated, set fields 'SSL_key_file' # and 'SSL_cert_file' directly in %smtpd_tls_server_options instead. # For compatibility with 2.10 the values of $smtpd_tls_cert_file # and $smtpd_tls_key_file are fed into %smtpd_tls_server_options # if fields 'SSL_key_file' and 'SSL_cert_file' are not provided. # # $smtpd_tls_cert_file = undef; # e.g. "$MYHOME/cert/amavisd-cert.pem" # $smtpd_tls_key_file = undef; # e.g. "$MYHOME/cert/amavisd-key.pem" # The following options are passed to IO::Socket::SSL::start_SSL() when # setting up a server side of a TLS session (from MTA). The only options # passed implicitly are SSL_server, SSL_hostname, and SSL_error_trap. # See IO::Socket::SSL documentation. # %smtpd_tls_server_options = ( SSL_verifycn_scheme => 'smtp', SSL_session_cache => 2, # SSL_key_file => $smtpd_tls_key_file, # SSL_cert_file => $smtpd_tls_cert_file, # SSL_dh_file => ... , # SSL_ca_file => ... , # SSL_version => '!SSLv2,!SSLv3', # SSL_cipher_list => 'HIGH:!MD5:!DSS:!aNULL', # SSL_passwd_cb => sub { 'example' }, # ... ); # The following options are passed to IO::Socket::SSL::start_SSL() when # setting up a client side of a TLS session back to MTA. The only options # passed implicitly are SSL_session_cache and SSL_error_trap. # See IO::Socket::SSL documentation. # %smtp_tls_client_options = ( SSL_verifycn_scheme => 'smtp', # SSL_version => '!SSLv2,!SSLv3', # SSL_cipher_list => 'HIGH:!MD5:!DSS:!aNULL', # SSL_client_ca_file => ... , ); $dkim_minimum_key_bits = 1024; # min acceptable DKIM key size (in bits) # for whitelisting # SMTP AUTH username and password for notification submissions # (and reauthentication of forwarded mail if requested) #$amavis_auth_user = undef; # perhaps: 'amavisd' #$amavis_auth_pass = undef; #$auth_reauthenticate_forwarded = undef; # supply our own credentials also # for forwarded (passed) mail $smtp_connection_cache_on_demand = 1; $smtp_connection_cache_enable = 1; # whom quarantined messages appear to be sent from (envelope sender) # $mailfrom_to_quarantine = undef; # orig. sender if undef, or set explicitly # where to send quarantined malware - specify undef to disable, or an # e-mail address containing '@', or just a local part, which will be # mapped by %local_delivery_aliases into local mailbox name or directory. # The lookup key is a recipient address $virus_quarantine_to = 'virus-quarantine'; $banned_quarantine_to = 'banned-quarantine'; $unchecked_quarantine_to = 'unchecked-quarantine'; $spam_quarantine_to = 'spam-quarantine'; $bad_header_quarantine_to = 'bad-header-quarantine'; $clean_quarantine_to = 'clean-quarantine'; $archive_quarantine_to = 'archive-quarantine'; # similar to $spam_quarantine_to, but the lookup key is the sender address: $spam_quarantine_bysender_to = undef; # dflt: no by-sender spam quarantine # quarantine directory or mailbox file or empty # (only used if $*_quarantine_to specifies direct local delivery) $QUARANTINEDIR = undef; # no quarantine unless overridden by config $undecipherable_subject_tag = '***UNCHECKED*** '; # NOTE: all entries can accept mail_body_size_limit and score_factor options @spam_scanners = ( ['SpamAssassin', 'Amavis::SpamControl::SpamAssassin' ], # ['SpamdClient', 'Amavis::SpamControl::SpamdClient', # mail_body_size_limit => 65000, score_factor => 1.0, # ], # ['DSPAM', 'Amavis::SpamControl::ExtProg', $dspam, # [ qw(--stdout --classify --deliver=innocent,spam # --mode=toe --feature noise # --user), $daemon_user ], # mail_body_size_limit => 65000, score_factor => 1.0, # ], # ['CRM114', 'Amavis::SpamControl::ExtProg', 'crm', # [ qw(-u /var/amavis/home/.crm114 mailreaver.crm # --dontstore --report_only --stats_only # --good_threshold=10 --spam_threshold=-10) ], # mail_body_size_limit => 65000, score_factor => -0.20, # lock_file => '/var/amavis/crm114.lock', # lock_type => 'shared', learner_lock_type => 'exclusive', # ], # ['Bogofilter', 'Amavis::SpamControl::ExtProg', 'bogofilter', # [ 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 # string to prepend to Subject header field when message qualifies as spam # $sa_spam_subject_tag1 = undef; # example: '***Possible Spam*** ' # $sa_spam_subject_tag = undef; # example: '***Spam*** ' $sa_spam_level_char = '*'; # character to be used in X-Spam-Level bar; # empty or undef disables adding this header field $sa_num_instances = 1; # number of SA instances, # usually 1, memory-expensive, keep small $sa_local_tests_only = 0; $sa_debug = undef; $sa_timeout = 30; # no longer used since 2.6.5 $file = 'file'; # path to the file(1) utility for classifying contents $altermime = 'altermime'; # path to the altermime utility (optional) @altermime_args_defang = qw(--verbose --removeall); @altermime_args_disclaimer = qw(--disclaimer=/etc/altermime-disclaimer.txt); # @altermime_args_disclaimer = # qw(--disclaimer=/etc/_OPTION_.txt --disclaimer-html=/etc/_OPTION_.html); # @disclaimer_options_bysender_maps = ( 'altermime-disclaimer' ); $MIN_EXPANSION_FACTOR = 5; # times original mail size $MAX_EXPANSION_FACTOR = 500; # times original mail size # $MIN_EXPANSION_QUOTA = ... # bytes, undef=not enforced # $MAX_EXPANSION_QUOTA = ... # bytes, undef=not enforced # See amavisd.conf and README.lookups for details. # What to do with the message (this is independent of quarantining): # Reject: tell MTA to generate a non-delivery notification, MTA gets 5xx # Bounce: generate a non-delivery notification by ourselves, MTA gets 250 # Discard: drop the message and pretend it was delivered, MTA gets 250 # Pass: accept/forward a message, MTA gets 250 # TempFail: temporary failure, client should retry, MTA gets 4xx # # COMPATIBILITY NOTE: the separation of *_destiny values into # D_BOUNCE, D_REJECT, D_DISCARD and D_PASS made settings $warn*sender only # still useful with D_PASS. The combination of D_DISCARD + $warn*sender=1 # is mapped into D_BOUNCE for compatibility. # The following symbolic constants can be used in *destiny settings: # # D_PASS mail will pass to recipients, regardless of contents; # # D_DISCARD mail will not be delivered to its recipients, sender will NOT be # notified. Effectively we lose mail (but it will be quarantined # unless disabled). # # D_BOUNCE mail will not be delivered to its recipients, a non-delivery # notification (bounce) will be sent to the sender by amavis # (unless suppressed). Bounce (DSN) will not be sent if a virus # name matches $viruses_that_fake_sender_maps, or to messages # from mailing lists (Precedence: bulk|list|junk), or for spam # exceeding spam_dsn_cutoff_level # # D_REJECT mail will not be delivered to its recipients, amavisd will # return a 5xx status response. Depending on an MTA/amavisd setup # this will result either in a reject status passed back to a # connecting SMTP client (in a pre-queue setup: proxy or milter), # or an MTA will generate a bounce in a post-queue setup. # If not all recipients agree on rejecting a message (like when # different recipients have different thresholds on bad mail # contents and LMTP is not used) amavisd sends a bounce by itself # (same as D_BOUNCE). # # D_TEMPFAIL indicates a temporary failure, mail will not be delivered to # its recipients, sender should retry the operation later. # # Notes: # D_REJECT and D_BOUNCE are similar,the difference is in who is responsible # for informing the sender about non-delivery, and how informative # the notification can be (amavis knows more than MTA); # With D_REJECT, MTA may reject original SMTP, or send DSN (delivery status # notification, colloquially called 'bounce') - depending on MTA # and its interface to a content checker; best suited for sendmail # milter or other pre-queue filtering setups # With D_BOUNCE, amavis (not MTA) sends DSN (can better explain the # reason for mail non-delivery but unable to reject the original # SMTP session, and is in position to suppress DSN if considered # unsuitable). Best suited for Postfix and other dual-MTA setups. # Exceeded spam cutoff limit or faked virus sender implicitly # turns D_BOUNCE into a D_DISCARD; # D_REJECT, D_BOUNCE, D_DISCARD, D_PASS, D_TEMPFAIL $final_virus_destiny = D_DISCARD; $final_banned_destiny = D_DISCARD; $final_unchecked_destiny = D_PASS; $final_spam_destiny = D_PASS; $final_bad_header_destiny = D_PASS; # If decided to pass viruses (or spam) to certain recipients # by %final_destiny_maps_by_ccat yielding a D_PASS, or %lovers_maps_by_ccat # yielding a true, one may set the corresponding %addr_extension_maps_by_ccat # to some string, and the recipient address will have this string appended # as an address extension to a local-part (mailbox part) of the address. # This extension can be used by a final local delivery agent for example # to place such mail in different folder. Leaving this variable undefined # or an empty string prevents appending address extension. Recipients # which do not match @local_domains_maps are not affected (i.e. non-local # recipients (=outbound mail) do not get address extension appended). # # LDAs usually default to stripping away address extension if no special # handling for it is specified, so having this option enabled normally # does no harm, provided the $recipients_delimiter character matches # the setting at the final MTA's local delivery agent (LDA). # # $addr_extension_virus = 'virus'; # for example # $addr_extension_spam = 'spam'; # $addr_extension_banned = 'banned'; # $addr_extension_bad_header = 'badh'; # Delimiter between local part of the recipient address and address extension # (which can optionally be added, see variable %addr_extension_maps_by_ccat. # E.g. recipient address <user@domain.example> gets # changed to <user+virus@domain.example>. # # Delimiter should match an equivalent (final) MTA delimiter setting. # (e.g. for Postfix add 'recipient_delimiter = +' to main.cf). # Setting it to an empty string or to undef disables this feature # regardless of %addr_extension_maps_by_ccat setting. # $recipient_delimiter = '+'; $replace_existing_extension = 1; # true: replace ext; false: append ext # Affects matching of localpart of e-mail addresses (left of '@') # in lookups: true = case sensitive, false = case insensitive $localpart_is_case_sensitive = 0; # Trim trailing whitespace from SQL fields, LDAP attribute values # and hash righthand-sides as read by read_hash(); disabled by default; # turn it on for compatibility with pre-2.4.0 versions. $trim_trailing_space_in_lookup_result_fields = 0; # since 2.7.0: deprecated some old variables: # deprecate_var('bool', '$insert_received_line', 1); deprecate_var('bool', '$relayhost_is_client', undef); deprecate_var('bool', '$warnvirussender', undef); deprecate_var('bool', '$warnspamsender', undef); deprecate_var('bool', '$sa_spam_report_header', undef); deprecate_var('bool', '$sa_spam_modifies_subj', 1); deprecate_var('bool', '$sa_auto_whitelist', undef); deprecate_var('num', '$sa_timeout', 30); deprecate_var('str', '$syslog_priority', 'debug'); deprecate_var('str', '$SYSLOG_LEVEL', 'mail.debug'); deprecate_var('str', '$notify_xmailer_header', undef); # deprecate_var('array','@spam_modifies_subj_maps'); 1; } # end BEGIN - init_secondary # init structured variables like %sql_clause, $map_full_type_to_short_type_re, # %ccat_display_names, @decoders, build default maps; may run as root! # BEGIN { $allowed_added_header_fields{lc($_)} = 1 for qw( Received DKIM-Signature Authentication-Results VBR-Info X-Quarantine-ID X-Amavis-Alert X-Amavis-Hold X-Amavis-Modified X-Amavis-PenPals X-Amavis-OS-Fingerprint X-Amavis-PolicyBank X-Amavis-Category X-Spam-Status X-Spam-Level X-Spam-Flag X-Spam-Score X-Spam-Report X-Spam-Checker-Version X-Spam-Tests X-CRM114-Status X-CRM114-CacheID X-CRM114-Notice X-CRM114-Action X-DSPAM-Result X-DSPAM-Class X-DSPAM-Signature X-DSPAM-Processed X-DSPAM-Confidence X-DSPAM-Probability X-DSPAM-User X-DSPAM-Factors X-Bogosity ); $allowed_added_header_fields{lc('X-Spam-Report')} = 0; $allowed_added_header_fields{lc('X-Spam-Checker-Version')} = 0; $allowed_added_header_fields{lc('X-Amavis-Category')} = 0; # $allowed_added_header_fields{lc(c(lc $X_HEADER_TAG))}=1; #later:read_config # even though SpamAssassin does provide the following header fields, we # prefer to provide our own version (per-recipient scores, version hiding); # our own non-"X-Spam" header fields are always preferred and need not # be listed here $prefer_our_added_header_fields{lc($_)} = 1 for qw( X-Spam-Status X-Spam-Level X-Spam-Flag X-Spam-Score X-Spam-Report X-Spam-Checker-Version X-CRM114-Status X-CRM114-CacheID X-DSPAM-Result X-DSPAM-Signature ); # controls which header section tests are performed in check_header_validity, # keys correspond to minor contents categories for CC_BADH $allowed_header_tests{lc($_)} = 1 for qw( other mime syntax empty long control 8bit utf8 missing multiple); $allowed_header_tests{'utf8'} = 0; # turn this test off by default # RFC 6376 standard set of header fields to be signed: my(@sign_headers) = qw(From Sender Reply-To Subject Date Message-ID To Cc In-Reply-To References MIME-Version Content-Type Content-Transfer-Encoding Content-ID Content-Description Resent-Date Resent-From Resent-Sender Resent-To Resent-Cc Resent-Message-ID List-Id List-Post List-Owner List-Subscribe List-Unsubscribe List-Help List-Archive); # additional header fields considered appropriate, see also RFC 4021 # and IANA registry "Permanent Message Header Field Names"; # see RFC 3834 for Auto-Submitted; RFC 5518 for VBR-Info (Vouch By Reference) push(@sign_headers, qw(Received Precedence Original-Message-ID Message-Context PICS-Label Sensitivity Solicitation Content-Location Content-Features Content-Disposition Content-Language Content-Alternative Content-Base Content-MD5 Content-Duration Content-Class Accept-Language Auto-Submitted Archived-At VBR-Info)); # note that we are signing Received despite the advise in RFC 6376; # some additional nonstandard header fields: push(@sign_headers, qw(Organization Organisation User-Agent X-Mailer)); $signed_header_fields{lc($_)} = 1 for @sign_headers; # Excluded: # DKIM-Signature DomainKey-Signature Authentication-Results # Keywords Comments Errors-To X-Virus-Scanned X-Archived-At X-No-Archive # Some MTAs are dropping Disposition-Notification-To, exclude: # Disposition-Notification-To Disposition-Notification-Options # Some mail scanners are dropping Return-Receipt-To, exclude it. # Signing a 'Sender' may not be a good idea because when such mail is sent # through a mailing list, this header field is usually replaced by a new one, # invalidating a signature. Long To and Cc address lists are often mangled, # especially when containing non-encoded display names. # Off: Sender - conflicts with mailing lists which must replace a Sender # Off: To, Cc, Resent-To, Resent-Cc - too often get garbled by mailers $signed_header_fields{lc($_)} = 0 for qw(Sender To Cc Resent-To Resent-Cc); # # a value greater than 1 causes signing of one additional null instance of # a header field, thus prohibiting prepending additional occurrences of such # header field without breaking a signature $signed_header_fields{lc($_)} = 2 for qw(From Date Subject Content-Type); # provide names for content categories - to be used only for logging, # SNMP counter names, and display purposes %ccat_display_names = ( CC_CATCHALL, 'CatchAll', # last resort, should not normally appear CC_CLEAN, 'Clean', CC_CLEAN.',1', 'CleanTag', # tag_level CC_MTA, 'MtaFailed', # unable to forward (general) CC_MTA.',1', 'MtaTempFailed', # MTA response was 4xx CC_MTA.',2', 'MtaRejected', # MTA response was 5xx CC_OVERSIZED, 'Oversized', CC_BADH, 'BadHdr', CC_BADH.',1', 'BadHdrMime', CC_BADH.',2', 'BadHdr8bit', CC_BADH.',3', 'BadHdrChar', CC_BADH.',4', 'BadHdrSpace', CC_BADH.',5', 'BadHdrLong', CC_BADH.',6', 'BadHdrSyntax', CC_BADH.',7', 'BadHdrMissing', CC_BADH.',8', 'BadHdrDupl', CC_SPAMMY, 'Spammy', # tag2_level CC_SPAMMY.',1','Spammy3', # tag3_level CC_SPAM, 'Spam', # kill_level CC_UNCHECKED, 'Unchecked', CC_UNCHECKED.',1', 'UncheckedEncrypted', CC_UNCHECKED.',2', 'UncheckedOverLimits', CC_UNCHECKED.',3', 'UncheckedAmbiguousContent', CC_BANNED, 'Banned', CC_VIRUS, 'Virus', ); # provide names for content categories - to be used only for logging, # SNMP counter names, and display purposes, similar to %ccat_display_names # but only major contents category names are listed %ccat_display_names_major = ( CC_CATCHALL, 'CatchAll', # last resort, should not normally appear CC_CLEAN, 'Clean', CC_MTA, 'MtaFailed', # unable to forward CC_OVERSIZED, 'Oversized', CC_BADH, 'BadHdr', CC_SPAMMY, 'Spammy', # tag2_level CC_SPAM, 'Spam', # kill_level CC_UNCHECKED, 'Unchecked', CC_BANNED, 'Banned', CC_VIRUS, 'Virus', ); # $partition_tag is a user-specified SQL field value in tables maddr, msgs, # msgrcpt and quarantine, inserted into new records, but can be useful even # without SQL, accessible through a macro %P and in quarantine templates. # It is usually an integer, but depending on a schema may be of other data # type e.g. a string. May be used to speed up purging of old records by using # partitioned tables (MySQL 5.1+, PostgreSQL 8.1+). A possible usage can # be a week-of-a-year, or some other slowly changing value, allowing to # quickly drop old table partitions without wasting time on deleting # individual records. Mail addresses in table maddr are self-contained # within a partition tag, which means that the same mail address may # appear in more than one maddr partition (using different 'id's), and # that tables msgs and msgrcpt are guaranteed to reference a maddr.id # within their own partition tag. The $partition_tag may be a scalar # (an integer or a string), or a reference to a subroutine, which will be # called with an object of type Amavis::In::Message as argument, and its # result will be used as a partition tag value. Possible usage: # # $partition_tag = # sub { my($msginfo)=@_; iso8601_week($msginfo->rx_time) }; #or: # $partition_tag = # sub { my($msginfo)=@_; iso8601_yearweek($msginfo->rx_time) }; # #or based on a day of a week for short-term cycling (Mo=1, Tu=2,... Su=7): # $partition_tag = # sub { my($msginfo)=@_; iso8601_weekday($msginfo->rx_time) }; # # $spam_quarantine_method = 'local:W%P/spam/%m.gz'; # quar dir by week num # The SQL select clause to fetch per-recipient policy settings. # The %k will be replaced by a comma-separated list of query addresses # for a recipient (e.g. a full address, domain only, catchall), %a will be # replaced by an exact recipient address (same as the first entry in %k, # suitable for pattern matching), %l by a full unmodified localpart, %u by # a lowercased username (a localpart without extension), %e by lowercased # addr extension (which includes a delimiter), and %d for lowercased domain. # Use ORDER if there is a chance that multiple records will match - the # first match wins (i.e. the first returned record). If field names are # not unique (e.g. 'id'), the later field overwrites the earlier in a hash # returned by lookup, which is why we use 'users.*, policy.*, users.id', # i.e. the id is repeated at the end. # This is a legacy variable for upwards compatibility, now only referenced # by the program through a %sql_clause entry 'sel_policy' - newer config # files may assign directly to $sql_clause{'sel_policy'} if preferred. # $sql_select_policy = 'SELECT users.*, policy.*, users.id'. ' FROM users LEFT JOIN policy ON users.policy_id=policy.id'. ' WHERE users.email IN (%k) ORDER BY users.priority DESC'; # Btw, MySQL and PostgreSQL are happy with 'SELECT *, users.id', # but Oracle wants 'SELECT users.*, policy.*, users.id', which is # also acceptable to MySQL and PostgreSQL. # The SQL select clause to check sender in per-recipient whitelist/blacklist. # The first SELECT argument '?' will be users.id from recipient SQL lookup, # the %k will be replaced by a comma-separated list of query addresses # for a sender (e.g. a full address, domain only, catchall), %a will be # replaced by an exact sender address (same as the first entry in %k, # suitable for pattern matching), %l by a full unmodified localpart, %u by # a lowercased username (a localpart without extension), %e by lowercased # addr extension (which includes a delimiter), and %d for lowercased domain. # Only the first occurrence of '?' will be replaced by users.id, # subsequent occurrences of '?' will see empty string as an argument. # There can be zero or more occurrences of each %k, %a, %l, %u, %e, %d, # lookup keys will be replicated accordingly. # This is a separate legacy variable for upwards compatibility, now only # referenced by the program through %sql_clause entry 'sel_wblist' - newer # config files may assign directly to $sql_clause{'sel_wblist'} if preferred. # $sql_select_white_black_list = 'SELECT wb FROM wblist JOIN mailaddr ON wblist.sid=mailaddr.id'. ' WHERE wblist.rid=? AND mailaddr.email IN (%k)'. ' ORDER BY mailaddr.priority DESC'; %sql_clause = ( 'sel_policy' => \$sql_select_policy, 'sel_wblist' => \$sql_select_white_black_list, 'sel_adr' => 'SELECT id FROM maddr WHERE partition_tag=? AND email=?', 'ins_adr' => 'INSERT INTO maddr (partition_tag, email, domain) VALUES (?,?,?)', 'ins_msg' => 'INSERT INTO msgs (partition_tag, mail_id, secret_id, am_id,'. ' time_num, time_iso, sid, policy, client_addr, size, host)'. ' VALUES (?,?,?,?,?,?,?,?,?,?,?)', 'upd_msg' => 'UPDATE msgs SET content=?, quar_type=?, quar_loc=?, dsn_sent=?,'. ' spam_level=?, message_id=?, from_addr=?, subject=?, client_addr=?,'. ' originating=?'. ' WHERE partition_tag=? AND mail_id=?', 'ins_rcp' => 'INSERT INTO msgrcpt (partition_tag, mail_id, rseqnum, rid, is_local,'. ' content, ds, rs, bl, wl, bspam_level, smtp_resp)'. ' VALUES (?,?,?,?,?,?,?,?,?,?,?,?)', 'ins_quar' => 'INSERT INTO quarantine (partition_tag, mail_id, chunk_ind, mail_text)'. ' VALUES (?,?,?,?)', 'sel_msg' => # obtains partition_tag if missing in a release request 'SELECT partition_tag FROM msgs WHERE mail_id=?', 'sel_quar' => 'SELECT mail_text FROM quarantine'. ' WHERE partition_tag=? AND mail_id=?'. ' ORDER BY chunk_ind', 'sel_penpals' => # no message-id references list "SELECT msgs.time_num, msgs.mail_id, subject". " FROM msgs JOIN msgrcpt USING (partition_tag,mail_id)". " WHERE sid=? AND rid=? AND msgs.content!='V' AND ds='P'". " ORDER BY msgs.time_num DESC", # LIMIT 1 'sel_penpals_msgid' => # with a nonempty list of message-id references "SELECT msgs.time_num, msgs.mail_id, subject, message_id, rid". " FROM msgs JOIN msgrcpt USING (partition_tag,mail_id)". " WHERE sid=? AND msgs.content!='V' AND ds='P' AND message_id IN (%m)". " AND rid!=sid". " ORDER BY rid=? DESC, msgs.time_num DESC", # LIMIT 1 ); # NOTE on $sql_clause{'upd_msg'}: MySQL clobbers timestamp on update # (unless DEFAULT 0 is used) setting it to a current local time and # losing the cherishly preserved and prepared timestamp of mail reception. # From the MySQL 4.1 documentation: # * With neither DEFAULT nor ON UPDATE clauses, it is the same as # DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. # * suppress the automatic initialization and update behaviors for the first # TIMESTAMP column by explicitly assigning it a constant DEFAULT value # (for example, DEFAULT 0) # * The first TIMESTAMP column in table row automatically is updated to # the current timestamp when the value of any other column in the row is # changed, unless the TIMESTAMP column explicitly is assigned a value # other than NULL. # maps full string as returned by a file(1) utility into a short string; # the first match wins, more specific entries should precede general ones! # the result may be a string or a ref to a list of strings; # see also sub decompose_part() # prepare an arrayref, later to be converted to an Amavis::Lookup::RE object $map_full_type_to_short_type_re = [ [qr/^empty\z/ => 'empty'], [qr/^directory\z/ => 'dir'], [qr/^can't (stat|read)\b/ => 'dat'], # file(1) diagnostics [qr/^cannot open\b/ => 'dat'], # file(1) diagnostics [qr/^ERROR:/ => 'dat'], # file(1) diagnostics [qr/can't read magic file|couldn't find any magic files/ => 'dat'], [qr/^data\z/ => 'dat'], [qr/^ISO-8859.*\btext\b/ => 'txt'], [qr/^Non-ISO.*ASCII\b.*\btext\b/ => 'txt'], [qr/^Unicode\b.*\btext\b/i => 'txt'], [qr/^UTF.* Unicode text\b/i => 'txt'], [qr/^'diff' output text\b/ => 'txt'], [qr/^GNU message catalog\b/ => 'mo'], [qr/^PGP message [Ss]ignature\b/ => ['pgp','pgp.asc'] ], [qr/^PGP message.*[Ee]ncrypted\b/ => ['pgp','pgp.enc'] ], [qr/^PGP message\z/ => ['pgp','pgp.enc'] ], [qr/^(?:PGP|GPG) encrypted data\b/ => ['pgp','pgp.enc'] ], [qr/^PGP public key\b/ => ['pgp','pgp.asc'] ], [qr/^PGP armored data( signed)? message\b/ => ['pgp','pgp.asc'] ], [qr/^PGP armored\b/ => ['pgp','pgp.asc'] ], [qr/^PGP\b/ => 'pgp' ], ### 'file' is a bit too trigger happy to claim something is 'mail text' # [qr/^RFC 822 mail text\b/ => 'mail'], [qr/^(ASCII|smtp|RFC 822) mail text\b/ => 'txt'], [qr/^JPEG image data\b/ => ['image','jpg'] ], [qr/^GIF image data\b/ => ['image','gif'] ], [qr/^PNG image data\b/ => ['image','png'] ], [qr/^TIFF image data\b/ => ['image','tif'] ], [qr/^PCX\b.*\bimage data\b/ => ['image','pcx'] ], [qr/^PC bitmap data\b/ => ['image','bmp'] ], [qr/^SVG Scalable Vector Graphics image\b/ => ['image','svg'] ], [qr/^MP2\b/ => ['audio','mpa','mp2'] ], [qr/^MP3\b/ => ['audio','mpa','mp3'] ], [qr/\bMPEG ADTS, layer III\b/ => ['audio','mpa','mp3'] ], [qr/^ISO Media, MPEG v4 system, 3GPP\b/=> ['audio','mpa','3gpp'] ], [qr/^ISO Media, MPEG v4 system\b/ => ['audio','mpa','m4a','m4b'] ], [qr/^FLAC audio bitstream data\b/ => ['audio','flac'] ], [qr/^Ogg data, FLAC audio\b/ => ['audio','oga'] ], [qr/^Ogg data\b/ => ['audio','ogg'] ], [qr/^MPEG video stream data\b/ => ['movie','mpv'] ], [qr/^MPEG system stream data\b/ => ['movie','mpg'] ], [qr/^MPEG\b/ => ['movie','mpg'] ], [qr/^Matroska data\b/ => ['movie','mkv'] ], [qr/^Microsoft ASF\b/ => ['movie','wmv'] ], [qr/^RIFF\b.*\bAVI\b/ => ['movie','avi'] ], [qr/^RIFF\b.*\banimated cursor\b/ => ['movie','ani'] ], [qr/^RIFF\b.*\bWAVE audio\b/ => ['audio','wav'] ], [qr/^Macromedia Flash data\b/ => 'swf'], [qr/^HTML document text\b/ => 'html'], [qr/^XML document text\b/ => 'xml'], [qr/^exported SGML document text\b/ => 'sgml'], [qr/^PostScript document text\b/ => 'ps'], [qr/^PDF document\b/ => 'pdf'], [qr/^Rich Text Format data\b/ => 'rtf'], [qr/^Microsoft Office Document\b/i => 'doc'], # OLE2: doc, ppt, xls,... [qr/^Microsoft Word\b/i => 'doc'], [qr/^Microsoft Installer\b/i => 'doc'], # file(1) may misclassify [qr/^Composite Document File V2 Document\b/i => 'cdf-ms'], # Microsoft Office document files (doc, xls, ...) [qr/^ms-windows meta(file|font)\b/i => 'wmf'], [qr/^LaTeX\b.*\bdocument text\b/ => 'lat'], [qr/^TeX DVI file\b/ => 'dvi'], [qr/\bdocument text\b/ => 'txt'], [qr/^compiled Java class data\b/ => 'java'], [qr/^MS Windows 95 Internet shortcut text\b/ => 'url'], [qr/^Compressed Google KML Document\b/ => 'kmz'], [qr/^frozen\b/ => 'F'], [qr/^gzip compressed\b/ => 'gz'], [qr/^bzip compressed\b/ => 'bz'], [qr/^bzip2 compressed\b/ => 'bz2'], [qr/^xz compressed\b/ => 'xz'], [qr/^lzma compressed\b/ => 'lzma'], [qr/^lrz compressed\b/ => 'lrz'], #***(untested) [qr/^lzop compressed\b/ => 'lzo'], [qr/^LZ4 compressed\b/ => 'lz4'], [qr/^compress'd/ => 'Z'], [qr/^Zip archive\b/i => 'zip'], [qr/^7-zip archive\b/i => '7z'], [qr/^RAR archive\b/i => 'rar'], [qr/^LHa.*\barchive\b/i => 'lha'], # (also known as .lzh) [qr/^ARC archive\b/i => 'arc'], [qr/^ARJ archive\b/i => 'arj'], [qr/^ACE archive\b/i => 'ace'], [qr/^Zoo archive\b/i => 'zoo'], [qr/^(\S+\s+)?tar archive\b/i => 'tar'], [qr/^(\S+\s+)?cpio archive\b/i => 'cpio'], [qr/^StuffIt Archive\b/i => 'sit'], [qr/^Debian binary package\b/i => 'deb'], # std. Unix archive (ar) [qr/^current ar archive\b/i => 'a'], # std. Unix archive (ar) [qr/^RPM\b/ => 'rpm'], [qr/^(Transport Neutral Encapsulation Format|TNEF)\b/i => 'tnef'], [qr/^Microsoft Cabinet (file|archive)\b/i => 'cab'], [qr/^InstallShield Cabinet file\b/ => 'installshield'], [qr/^ISO 9660 CD-ROM filesystem\b/i => 'iso'], [qr/^(uuencoded|xxencoded)\b/i => 'uue'], [qr/^binhex\b/i => 'hqx'], [qr/^(ASCII|text)\b/i => 'asc'], [qr/^Emacs.*byte-compiled Lisp data/i => 'asc'], # BinHex with empty line [qr/\bscript\b.* text executable\b/ => 'txt'], [qr/^MS Windows\b.*\bDLL\b/ => ['exe','dll'] ], [qr/\bexecutable for MS Windows\b.*\bDLL\b/ => ['exe','dll'] ], [qr/^MS-DOS executable \(built-in\)/ => 'asc'], # starts with LZ [qr/^(MS-)?DOS executable\b.*\bDLL\b/ => ['exe','dll'] ], [qr/^MS Windows\b.*\bexecutable\b/ => ['exe','exe-ms'] ], [qr/\bexecutable\b.*\bfor MS Windows\b/ => ['exe','exe-ms'] ], [qr/^COM executable for DOS\b/ => 'asc'], # misclassified? [qr/^DOS executable \(COM\)/ => 'asc'], # misclassified? [qr/^(MS-)?DOS executable\b(?!.*\(COM\))/ => ['exe','exe-ms'] ], [qr/^PA-RISC.*\bexecutable\b/ => ['exe','exe-unix'] ], [qr/^ELF .*\bexecutable\b/ => ['exe','exe-unix'] ], [qr/^COFF format .*\bexecutable\b/ => ['exe','exe-unix'] ], [qr/^executable \(RISC System\b/ => ['exe','exe-unix'] ], [qr/^VMS\b.*\bexecutable\b/ => ['exe','exe-vms'] ], [qr/\bexecutable\b/i => 'exe'], [qr/\bshared object, /i => 'so'], [qr/\brelocatable, /i => 'o'], [qr/\btext\b/i => 'asc'], [qr/^Zstandard compressed\b/ => 'zst'], [qr/^/ => 'dat'], # catchall ]; # MS Windows PE 32-bit Intel 80386 GUI executable not relocatable # MS-DOS executable (EXE), OS/2 or MS Windows # MS-DOS executable PE for MS Windows (DLL) (GUI) Intel 80386 32-bit # MS-DOS executable PE for MS Windows (DLL) (GUI) Alpha 32-bit # MS-DOS executable, NE for MS Windows 3.x (driver) # MS-DOS executable (built-in) (any file starting with LZ!) # PE executable for MS Windows (DLL) (GUI) Intel 80386 32-bit # PE executable for MS Windows (GUI) Intel 80386 32-bit # NE executable for MS Windows 3.x # PA-RISC1.1 executable dynamically linked # PA-RISC1.1 shared executable dynamically linked # ELF 64-bit LSB executable, Alpha (unofficial), version 1 (FreeBSD), # for FreeBSD 5.0.1, dynamically linked (uses shared libs), stripped # ELF 64-bit LSB executable, Alpha (unofficial), version 1 (SYSV), # for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped # ELF 64-bit MSB executable, SPARC V9, version 1 (FreeBSD), # for FreeBSD 5.0, dynamically linked (uses shared libs), stripped # ELF 64-bit MSB shared object, SPARC V9, version 1 (FreeBSD), stripped # ELF 32-bit LSB executable, Intel 80386, version 1, dynamically` # ELF 32-bit MSB executable, SPARC, version 1, dynamically linke` # COFF format alpha executable paged stripped - version 3.11-10 # COFF format alpha executable paged dynamically linked stripped` # COFF format alpha demand paged executable or object module # stripped - version 3.11-10 # COFF format alpha paged dynamically linked not stripped shared` # executable (RISC System/6000 V3.1) or obj module # VMS VAX executable # A list of pairs or n-tuples: [short-type, code_ref, optional-args...]. # Maps short types to a decoding routine, the first match wins. # Arguments beyond the first two can be a program path string (or a listref # of paths to be searched) or a reference to a variable containing such # path - which allows for lazy evaluation, making possible to assign values # to legacy configuration variables even after the assignment to @decoders. # @decoders = ( ['mail', \&Amavis::Unpackers::do_mime_decode], # [[qw(asc uue hqx ync)], \&Amavis::Unpackers::do_ascii], # not safe ['F', \&Amavis::Unpackers::do_uncompress, \$unfreeze], # ['unfreeze', 'freeze -d', 'melt', 'fcat'] ], ['Z', \&Amavis::Unpackers::do_uncompress, \$uncompress], # ['uncompress', 'gzip -d', 'zcat'] ], ['gz', \&Amavis::Unpackers::do_uncompress, \$gunzip], ['gz', \&Amavis::Unpackers::do_gunzip], ['bz2', \&Amavis::Unpackers::do_uncompress, \$bunzip2], ['xz', \&Amavis::Unpackers::do_uncompress, ['xzdec', 'xz -dc', 'unxz -c', 'xzcat'] ], ['lzma', \&Amavis::Unpackers::do_uncompress, ['lzmadec', 'xz -dc --format=lzma', 'lzma -dc', 'unlzma -c', 'lzcat', 'lzmadec'] ], ['lrz', \&Amavis::Unpackers::do_uncompress, ['lrzip -q -k -d -o -', 'lrzcat -q -k'] ], ['lzo', \&Amavis::Unpackers::do_uncompress, \$unlzop], ['lz4', \&Amavis::Unpackers::do_uncompress, ['lz4c -d'] ], ['rpm', \&Amavis::Unpackers::do_uncompress, \$rpm2cpio], # ['rpm2cpio.pl', 'rpm2cpio'] ], [['cpio','tar'], \&Amavis::Unpackers::do_pax_cpio, \$pax], # ['/usr/local/heirloom/usr/5bin/pax', 'pax', 'gcpio', 'cpio'] ], # ['tar', \&Amavis::Unpackers::do_tar], # no longer supported ['deb', \&Amavis::Unpackers::do_ar, \$ar], # ['a', \&Amavis::Unpackers::do_ar, \$ar], #unpacking .a seems an overkill ['rar', \&Amavis::Unpackers::do_unrar, \$unrar], # ['unrar', 'rar'] ['arj', \&Amavis::Unpackers::do_unarj, \$unarj], # ['unarj', 'arj'] ['arc', \&Amavis::Unpackers::do_arc, \$arc], # ['nomarch', 'arc'] ['zoo', \&Amavis::Unpackers::do_zoo, \$zoo], # ['zoo', 'unzoo'] ['doc', \&Amavis::Unpackers::do_ole, \$ripole], ['cab', \&Amavis::Unpackers::do_cabextract, \$cabextract], ['tnef', \&Amavis::Unpackers::do_tnef_ext, \$tnef], ['tnef', \&Amavis::Unpackers::do_tnef], # ['lha', \&Amavis::Unpackers::do_lha, \$lha], # not safe, use 7z instead # ['sit', \&Amavis::Unpackers::do_unstuff, \$unstuff], # not safe [['zip','kmz'], \&Amavis::Unpackers::do_7zip, ['7za', '7zz', '7z'] ], [['zip','kmz'], \&Amavis::Unpackers::do_unzip], ['7z', \&Amavis::Unpackers::do_7zip, ['7zr', '7za', '7zz', '7z'] ], [[qw(gz bz2 Z tar)], \&Amavis::Unpackers::do_7zip, ['7za', '7zz', '7z'] ], [[qw(xz lzma jar cpio arj rar swf lha iso cab deb rpm)], \&Amavis::Unpackers::do_7zip, ['7zz', '7z'] ], ['exe', \&Amavis::Unpackers::do_executable, \$unrar, \$lha, \$unarj], ['zst', \&Amavis::Unpackers::do_uncompress, ['unzstd'] ], ); # build_default_maps @local_domains_maps = ( \%local_domains, \@local_domains_acl, \$local_domains_re); @mynetworks_maps = (\@mynetworks); @client_ipaddr_policy = map(($_,'MYNETS'), @mynetworks_maps); @ip_repu_ignore_maps = (\@ip_repu_ignore_networks); @bypass_virus_checks_maps = ( \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re); @bypass_spam_checks_maps = ( \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re); @bypass_banned_checks_maps = ( \%bypass_banned_checks, \@bypass_banned_checks_acl, \$bypass_banned_checks_re); @bypass_header_checks_maps = ( \%bypass_header_checks, \@bypass_header_checks_acl, \$bypass_header_checks_re); @virus_lovers_maps = ( \%virus_lovers, \@virus_lovers_acl, \$virus_lovers_re); @spam_lovers_maps = ( \%spam_lovers, \@spam_lovers_acl, \$spam_lovers_re); @banned_files_lovers_maps = ( \%banned_files_lovers, \@banned_files_lovers_acl, \$banned_files_lovers_re); @bad_header_lovers_maps = ( \%bad_header_lovers, \@bad_header_lovers_acl, \$bad_header_lovers_re); # @unchecked_lovers_maps = (); # empty, new setting, no need for backw compat. @warnvirusrecip_maps = (\$warnvirusrecip); @warnbannedrecip_maps = (\$warnbannedrecip); @warnbadhrecip_maps = (\$warnbadhrecip); @newvirus_admin_maps = (\$newvirus_admin); @virus_admin_maps = (\%virus_admin, \$virus_admin); @banned_admin_maps = (\$banned_admin, \%virus_admin, \$virus_admin); @bad_header_admin_maps= (\$bad_header_admin); @spam_admin_maps = (\%spam_admin, \$spam_admin); @virus_quarantine_to_maps = (\$virus_quarantine_to); @banned_quarantine_to_maps = (\$banned_quarantine_to); @unchecked_quarantine_to_maps = (\$unchecked_quarantine_to); @spam_quarantine_to_maps = (\$spam_quarantine_to); @spam_quarantine_bysender_to_maps = (\$spam_quarantine_bysender_to); @bad_header_quarantine_to_maps = (\$bad_header_quarantine_to); @clean_quarantine_to_maps = (\$clean_quarantine_to); @archive_quarantine_to_maps = (\$archive_quarantine_to); @keep_decoded_original_maps = (\$keep_decoded_original_re); @map_full_type_to_short_type_maps = (\$map_full_type_to_short_type_re); # @banned_filename_maps = ( {'.' => [$banned_filename_re]} ); # @banned_filename_maps = ( {'.' => 'DEFAULT'} );#names mapped by %banned_rules @banned_filename_maps = ( 'DEFAULT' ); # same as above, but shorter @viruses_that_fake_sender_maps = (\$viruses_that_fake_sender_re, 1); @spam_tag_level_maps = (\$sa_tag_level_deflt); # CC_CLEAN,1 @spam_tag2_level_maps = (\$sa_tag2_level_deflt); # CC_SPAMMY @spam_tag3_level_maps = (\$sa_tag3_level_deflt); # CC_SPAMMY,1 @spam_kill_level_maps = (\$sa_kill_level_deflt); # CC_SPAM @spam_dsn_cutoff_level_maps = (\$sa_dsn_cutoff_level); @spam_dsn_cutoff_level_bysender_maps = (\$sa_dsn_cutoff_level); @spam_crediblefrom_dsn_cutoff_level_maps = (\$sa_crediblefrom_dsn_cutoff_level); @spam_crediblefrom_dsn_cutoff_level_bysender_maps = (\$sa_crediblefrom_dsn_cutoff_level); @spam_quarantine_cutoff_level_maps = (\$sa_quarantine_cutoff_level); @spam_subject_tag_maps = (\$sa_spam_subject_tag1); # note: inconsistent name @spam_subject_tag2_maps = (\$sa_spam_subject_tag); # note: inconsistent name # @spam_subject_tag3_maps = (); # new variable, no backward compatib. needed @whitelist_sender_maps = ( \%whitelist_sender, \@whitelist_sender_acl, \$whitelist_sender_re); @blacklist_sender_maps = ( \%blacklist_sender, \@blacklist_sender_acl, \$blacklist_sender_re); @addr_extension_virus_maps = (\$addr_extension_virus); @addr_extension_spam_maps = (\$addr_extension_spam); @addr_extension_banned_maps = (\$addr_extension_banned); @addr_extension_bad_header_maps = (\$addr_extension_bad_header); @debug_sender_maps = (\@debug_sender_acl); # @debug_recipient_maps = (); @remove_existing_spam_headers_maps = (\$remove_existing_spam_headers); # new variables, no backward compatibility needed, empty by default: # @score_sender_maps, @author_to_policy_bank_maps, @signer_reputation_maps, # @message_size_limit_maps # build backward-compatible settings hashes # %final_destiny_maps_by_ccat = ( # value is normally a list of by-recipient lookup tables, but for compa- # tibility with old %final_destiny_by_ccat a value may also be a scalar CC_VIRUS, sub { c('final_virus_destiny') }, CC_BANNED, sub { c('final_banned_destiny') }, CC_UNCHECKED, sub { c('final_unchecked_destiny') }, CC_SPAM, sub { c('final_spam_destiny') }, CC_BADH, sub { c('final_bad_header_destiny') }, CC_MTA.',1', D_TEMPFAIL, # MTA response was 4xx CC_MTA.',2', D_REJECT, # MTA response was 5xx CC_MTA, D_TEMPFAIL, CC_OVERSIZED, D_BOUNCE, CC_CATCHALL, D_PASS, ); %forward_method_maps_by_ccat = ( CC_CATCHALL, sub { ca('forward_method_maps') }, ); %smtp_reason_by_ccat = ( # currently only used for blocked messages only, status 5xx # a multiline message will produce a valid multiline SMTP response CC_VIRUS, 'id=%n - INFECTED: %V', CC_BANNED, 'id=%n - BANNED: %F', CC_UNCHECKED.',1', 'id=%n - UNCHECKED: encrypted', CC_UNCHECKED.',2', 'id=%n - UNCHECKED: over limits', CC_UNCHECKED.',3', 'id=%n - UNCHECKED: ambiguous content', CC_UNCHECKED, 'id=%n - UNCHECKED', CC_SPAM, 'id=%n - spam', CC_SPAMMY.',1', 'id=%n - spammy (tag3)', CC_SPAMMY, 'id=%n - spammy', CC_BADH.',1', 'id=%n - BAD HEADER: MIME error', CC_BADH.',2', 'id=%n - BAD HEADER: nonencoded 8-bit character', CC_BADH.',3', 'id=%n - BAD HEADER: contains invalid control character', CC_BADH.',4', 'id=%n - BAD HEADER: line made up entirely of whitespace', CC_BADH.',5', 'id=%n - BAD HEADER: line longer than RFC 5322 limit', CC_BADH.',6', 'id=%n - BAD HEADER: syntax error', CC_BADH.',7', 'id=%n - BAD HEADER: missing required header field', CC_BADH.',8', 'id=%n - BAD HEADER: duplicate header field', CC_BADH, 'id=%n - BAD HEADER', CC_OVERSIZED, 'id=%n - Message size exceeds recipient\'s size limit', CC_MTA.',1', 'id=%n - Temporary MTA failure on relaying', CC_MTA.',2', 'id=%n - Rejected by next-hop MTA on relaying', CC_MTA, 'id=%n - Unable to relay message back to MTA', CC_CLEAN, 'id=%n - CLEAN', CC_CATCHALL, 'id=%n - OTHER', # should not happen ); %lovers_maps_by_ccat = ( CC_VIRUS, sub { ca('virus_lovers_maps') }, CC_BANNED, sub { ca('banned_files_lovers_maps') }, CC_UNCHECKED, sub { ca('unchecked_lovers_maps') }, CC_SPAM, sub { ca('spam_lovers_maps') }, CC_SPAMMY, sub { ca('spam_lovers_maps') }, CC_BADH, sub { ca('bad_header_lovers_maps') }, ); %defang_maps_by_ccat = ( # compatible with legacy %defang_by_ccat: value may be a scalar CC_VIRUS, sub { c('defang_virus') }, CC_BANNED, sub { c('defang_banned') }, CC_UNCHECKED, sub { c('defang_undecipherable') }, CC_SPAM, sub { c('defang_spam') }, CC_SPAMMY, sub { c('defang_spam') }, # CC_BADH.',3', 1, # NUL or CR character in header section # CC_BADH.',5', 1, # header line longer than 998 characters # CC_BADH.',6', 1, # header field syntax error CC_BADH, sub { c('defang_bad_header') }, ); %subject_tag_maps_by_ccat = ( CC_VIRUS, [ '***INFECTED*** ' ], CC_BANNED, undef, CC_UNCHECKED, sub { [ c('undecipherable_subject_tag') ] }, # not by-recip CC_SPAM, undef, CC_SPAMMY.',1', sub { ca('spam_subject_tag3_maps') }, CC_SPAMMY, sub { ca('spam_subject_tag2_maps') }, CC_CLEAN.',1', sub { ca('spam_subject_tag_maps') }, ); %quarantine_method_by_ccat = ( CC_VIRUS, sub { c('virus_quarantine_method') }, CC_BANNED, sub { c('banned_files_quarantine_method') }, CC_UNCHECKED, sub { c('unchecked_quarantine_method') }, CC_SPAM, sub { c('spam_quarantine_method') }, CC_BADH, sub { c('bad_header_quarantine_method') }, CC_CLEAN, sub { c('clean_quarantine_method') }, ); %quarantine_to_maps_by_ccat = ( CC_VIRUS, sub { ca('virus_quarantine_to_maps') }, CC_BANNED, sub { ca('banned_quarantine_to_maps') }, CC_UNCHECKED, sub { ca('unchecked_quarantine_to_maps') }, CC_SPAM, sub { ca('spam_quarantine_to_maps') }, CC_BADH, sub { ca('bad_header_quarantine_to_maps') }, CC_CLEAN, sub { ca('clean_quarantine_to_maps') }, ); %admin_maps_by_ccat = ( CC_VIRUS, sub { ca('virus_admin_maps') }, CC_BANNED, sub { ca('banned_admin_maps') }, CC_UNCHECKED, sub { ca('virus_admin_maps') }, CC_SPAM, sub { ca('spam_admin_maps') }, CC_BADH, sub { ca('bad_header_admin_maps') }, ); %always_bcc_by_ccat = ( CC_CATCHALL, sub { c('always_bcc') }, ); %dsn_bcc_by_ccat = ( CC_CATCHALL, sub { c('dsn_bcc') }, ); %mailfrom_notify_admin_by_ccat = ( CC_SPAM, sub { c('mailfrom_notify_spamadmin') }, CC_CATCHALL, sub { c('mailfrom_notify_admin') }, ); %hdrfrom_notify_admin_by_ccat = ( CC_SPAM, sub { c('hdrfrom_notify_spamadmin') }, CC_CATCHALL, sub { c('hdrfrom_notify_admin') }, ); %mailfrom_notify_recip_by_ccat = ( CC_CATCHALL, sub { c('mailfrom_notify_recip') }, ); %hdrfrom_notify_recip_by_ccat = ( CC_CATCHALL, sub { c('hdrfrom_notify_recip') }, ); %hdrfrom_notify_sender_by_ccat = ( CC_CATCHALL, sub { c('hdrfrom_notify_sender') }, ); %hdrfrom_notify_release_by_ccat = ( CC_CATCHALL, sub { c('hdrfrom_notify_release') }, ); %hdrfrom_notify_report_by_ccat = ( CC_CATCHALL, sub { c('hdrfrom_notify_report') }, ); %notify_admin_templ_by_ccat = ( CC_SPAM, sub { cr('notify_spam_admin_templ') }, CC_CATCHALL, sub { cr('notify_virus_admin_templ') }, ); %notify_recips_templ_by_ccat = ( CC_SPAM, sub { cr('notify_spam_recips_templ') }, #usually empty CC_CATCHALL, sub { cr('notify_virus_recips_templ') }, ); %notify_sender_templ_by_ccat = ( # bounce templates CC_VIRUS, sub { cr('notify_virus_sender_templ') }, CC_BANNED, sub { cr('notify_virus_sender_templ') }, #historical reason CC_SPAM, sub { cr('notify_spam_sender_templ') }, CC_CATCHALL, sub { cr('notify_sender_templ') }, ); %notify_release_templ_by_ccat = ( CC_CATCHALL, sub { cr('notify_release_templ') }, ); %notify_report_templ_by_ccat = ( CC_CATCHALL, sub { cr('notify_report_templ') }, ); %notify_autoresp_templ_by_ccat = ( CC_CATCHALL, sub { cr('notify_autoresp_templ') }, ); %warnsender_by_ccat = ( # deprecated use, except perhaps for CC_BADH CC_VIRUS, undef, CC_BANNED, sub { c('warnbannedsender') }, CC_SPAM, undef, CC_BADH, sub { c('warnbadhsender') }, ); %warnrecip_maps_by_ccat = ( CC_VIRUS, sub { ca('warnvirusrecip_maps') }, CC_BANNED, sub { ca('warnbannedrecip_maps') }, CC_SPAM, undef, CC_BADH, sub { ca('warnbadhrecip_maps') }, ); %addr_extension_maps_by_ccat = ( CC_VIRUS, sub { ca('addr_extension_virus_maps') }, CC_BANNED, sub { ca('addr_extension_banned_maps') }, CC_SPAM, sub { ca('addr_extension_spam_maps') }, CC_SPAMMY, sub { ca('addr_extension_spam_maps') }, CC_BADH, sub { ca('addr_extension_bad_header_maps') }, # CC_OVERSIZED, 'oversized'; ); %addr_rewrite_maps_by_ccat = ( ); 1; } # end BEGIN - init_tertiary # prototypes sub Amavis::Unpackers::do_mime_decode($$); sub Amavis::Unpackers::do_ascii($$); sub Amavis::Unpackers::do_uncompress($$$); sub Amavis::Unpackers::do_gunzip($$); sub Amavis::Unpackers::do_pax_cpio($$$); #sub Amavis::Unpackers::do_tar($$); # no longer supported sub Amavis::Unpackers::do_ar($$$); sub Amavis::Unpackers::do_unzip($$;$$); sub Amavis::Unpackers::do_7zip($$$;$); sub Amavis::Unpackers::do_unrar($$$;$); sub Amavis::Unpackers::do_unarj($$$;$); sub Amavis::Unpackers::do_arc($$$); sub Amavis::Unpackers::do_zoo($$$); sub Amavis::Unpackers::do_lha($$$;$); sub Amavis::Unpackers::do_ole($$$); sub Amavis::Unpackers::do_cabextract($$$); sub Amavis::Unpackers::do_tnef($$); sub Amavis::Unpackers::do_tnef_ext($$$); sub Amavis::Unpackers::do_unstuff($$$); sub Amavis::Unpackers::do_executable($$@); no warnings 'once'; # Define alias names or shortcuts in this module to make it simpler # to call these routines from amavisd.conf *read_l10n_templates = \&Amavis::Util::read_l10n_templates; *read_text = \&Amavis::Util::read_text; *read_hash = \&Amavis::Util::read_hash; *read_array = \&Amavis::Util::read_array; *read_cidr = \&Amavis::Util::read_cidr; *idn_to_ascii = \&Amavis::Util::idn_to_ascii; # RFC 3490: ToASCII *idn_to_utf8 = \&Amavis::Util::idn_to_utf8; # RFC 3490: ToUnicode *mail_idn_to_ascii = \&Amavis::Util::mail_addr_idn_to_ascii; *dump_hash = \&Amavis::Util::dump_hash; *dump_array = \&Amavis::Util::dump_array; *ask_daemon = \&Amavis::AV::ask_daemon; *ask_clamav = \&Amavis::AV::ask_clamav; # deprecated, use ask_daemon *do_mime_decode = \&Amavis::Unpackers::do_mime_decode; *do_ascii = \&Amavis::Unpackers::do_ascii; *do_uncompress = \&Amavis::Unpackers::do_uncompress; *do_gunzip = \&Amavis::Unpackers::do_gunzip; *do_pax_cpio = \&Amavis::Unpackers::do_pax_cpio; *do_tar = \&Amavis::Unpackers::do_tar; # no longer supported *do_ar = \&Amavis::Unpackers::do_ar; *do_unzip = \&Amavis::Unpackers::do_unzip; *do_unrar = \&Amavis::Unpackers::do_unrar; *do_7zip = \&Amavis::Unpackers::do_7zip; *do_unarj = \&Amavis::Unpackers::do_unarj; *do_arc = \&Amavis::Unpackers::do_arc; *do_zoo = \&Amavis::Unpackers::do_zoo; *do_lha = \&Amavis::Unpackers::do_lha; *do_ole = \&Amavis::Unpackers::do_ole; *do_cabextract = \&Amavis::Unpackers::do_cabextract; *do_tnef_ext = \&Amavis::Unpackers::do_tnef_ext; *do_tnef = \&Amavis::Unpackers::do_tnef; *do_unstuff = \&Amavis::Unpackers::do_unstuff; *do_executable = \&Amavis::Unpackers::do_executable; *iso8601_week = \&Amavis::rfc2821_2822_Tools::iso8601_week; *iso8601_yearweek = \&Amavis::rfc2821_2822_Tools::iso8601_yearweek; *iso8601_year_and_week = \&Amavis::rfc2821_2822_Tools::iso8601_year_and_week; *iso8601_weekday = \&Amavis::rfc2821_2822_Tools::iso8601_weekday; *iso8601_timestamp = \&Amavis::rfc2821_2822_Tools::iso8601_timestamp; *iso8601_utc_timestamp = \&Amavis::rfc2821_2822_Tools::iso8601_utc_timestamp; # a shorthand for creating a regexp-based lookup table sub new_RE { require Amavis::Lookup::RE; Amavis::Lookup::RE->new(@_) } # shorthand: construct a query object for a DNSxL query on an IP address sub q_dns_a { require Amavis::Lookup::DNSxL; Amavis::Lookup::DNSxL->new(@_) } # dns zone, expect, resolver # shorthand: construct a query object for an SQL field sub q_sql_s { require Amavis::Lookup::SQLfield; Amavis::Lookup::SQLfield->new(undef, $_[0], 'S-') } # string sub q_sql_n { require Amavis::Lookup::SQLfield; Amavis::Lookup::SQLfield->new(undef, $_[0], 'N-') } # numeric sub q_sql_b { require Amavis::Lookup::SQLfield; Amavis::Lookup::SQLfield->new(undef, $_[0], 'B-') } # boolean # shorthand: construct a query object for an LDAP attribute sub q_ldap_s { require Amavis::Lookup::LDAPattr; Amavis::Lookup::LDAPattr->new(undef, $_[0], 'S-') } # string sub q_ldap_n { require Amavis::Lookup::LDAPattr; Amavis::Lookup::LDAPattr->new(undef, $_[0], 'N-') } # numeric sub q_ldap_b { require Amavis::Lookup::LDAPattr; Amavis::Lookup::LDAPattr->new(undef, $_[0], 'B-') } # boolean sub Opaque { require Amavis::Lookup::Opaque; Amavis::Lookup::Opaque->new(@_) } sub OpaqueRef { require Amavis::Lookup::OpaqueRef; Amavis::Lookup::OpaqueRef->new(@_) } # # Opaque provides a wrapper to arbitrary data structures, allowing them to be # treated as 'constant' pseudo-lookups, i.e. preventing arrays and hashes from # being interpreted as lookup lists/tables. In case of $forward_method this # allows for a listref of failover methods. Without the protection of Opaque # the listref would be interpreted by a lookup() as an acl lookup type instead # of a match-always data structure. The Opaque subroutine is not yet available # during a BEGIN phase, so this assignment must come after compiling the rest # of the code. # # This is the only case where both an array @*_maps as well as its default # element are members of a policy bank. Use lazy evaluation through a sub # to make this work as expected. # # @forward_method_maps = ( OpaqueRef(\$forward_method) ); @forward_method_maps = ( sub { Opaque(c('forward_method')) } ); # retain compatibility with old names use vars qw(%final_destiny_by_ccat %defang_by_ccat $sql_partition_tag $DO_SYSLOG $LOGFILE); *final_destiny_by_ccat = \%final_destiny_maps_by_ccat; *defang_by_ccat = \%defang_maps_by_ccat; *sql_partition_tag = \$partition_tag; *DO_SYSLOG = \$do_syslog; *LOGFILE = \$logfile; @virus_name_to_spam_score_maps = (new_RE( # the order matters, first match wins [ qr'^Structured\.(SSN|CreditCardNumber)\b' => 0.1 ], [ qr'^(Heuristics\.)?Phishing\.' => 0.1 ], [ qr'^(Email|HTML)\.Phishing\.(?!.*Sanesecurity)' => 0.1 ], [ qr'^Sanesecurity\.(Malware|Rogue|Trojan)\.' => undef ],# keep as infected [ qr'^Sanesecurity\.Foxhole\.Zip_exe' => 0.1 ], # F.P. [ qr'^Sanesecurity\.Foxhole\.Zip_bat' => 0.1 ], # F.P. [ qr'^Sanesecurity\.Foxhole\.Mail_gz' => 0.1 ], # F.P. [ qr'^Sanesecurity\.Foxhole\.Mail_ace' => 0.1 ], # F.P. [ qr'^Sanesecurity\.Foxhole\.Mail_tar' => 0 ], # F.P. [ qr'^Sanesecurity\.Foxhole\.' => undef ],# keep as infected [ qr'^Sanesecurity\.' => 0.1 ], [ qr'^Sanesecurity_PhishBar_' => 0 ], [ qr'^Sanesecurity.TestSig_' => 0 ], [ qr'^Email\.Spam\.Bounce(\.[^., ]*)*\.Sanesecurity\.' => 0 ], [ qr'^Email\.Spammail\b' => 0.1 ], [ qr'^MSRBL-(Images|SPAM)\b' => 0.1 ], [ qr'^VX\.Honeypot-SecuriteInfo\.com\.Joke' => 0.1 ], [ qr'^VX\.not-virus_(Hoax|Joke)\..*-SecuriteInfo\.com(\.|\z)' => 0.1 ], [ qr'^Email\.Spam.*-SecuriteInfo\.com(\.|\z)' => 0.1 ], [ qr'^Safebrowsing\.' => 0.1 ], [ qr'^winnow\.(phish|spam)\.' => 0.1 ], [ qr'^INetMsg\.SpamDomain' => 0.1 ], [ qr'^Doppelstern\.(Spam|Scam|Phishing|Junk|Lott|Loan)'=> 0.1 ], [ qr'^Bofhland\.Phishing' => 0.1 ], [ qr'^ScamNailer\.' => 0.1 ], [ qr'^HTML/Bankish' => 0.1 ], # F-Prot [ qr'^PORCUPINE_JUNK' => 0.1 ], [ qr'^PORCUPINE_PHISHING' => 0.1 ], [ qr'^Porcupine\.Junk' => 0.1 ], [ qr'^PhishTank\.Phishing\.' => 0.1 ], [ qr'-SecuriteInfo\.com(\.|\z)' => undef ], # keep as infected [ qr'^MBL_NA\.UNOFFICIAL' => 0.1 ], # false positives [ qr'^MBL_' => undef ], # keep as infected )); # Sanesecurity http://www.sanesecurity.co.uk/ # MSRBL- http://www.msrbl.com/site/contact # MBL http://www.malware.com.br/index.shtml # -SecuriteInfo.com http://clamav.securiteinfo.com/malwares.html # prepend a lookup table label object for logging purposes # sub label_default_maps() { for my $varname (qw( @disclaimer_options_bysender_maps @dkim_signature_options_bysender_maps @local_domains_maps @mynetworks_maps @ip_repu_ignore_maps @forward_method_maps @newvirus_admin_maps @banned_filename_maps @spam_quarantine_bysender_to_maps @spam_tag_level_maps @spam_tag2_level_maps @spam_tag3_level_maps @spam_kill_level_maps @spam_subject_tag_maps @spam_subject_tag2_maps @spam_subject_tag3_maps @spam_dsn_cutoff_level_maps @spam_dsn_cutoff_level_bysender_maps @spam_crediblefrom_dsn_cutoff_level_maps @spam_crediblefrom_dsn_cutoff_level_bysender_maps @spam_quarantine_cutoff_level_maps @spam_notifyadmin_cutoff_level_maps @whitelist_sender_maps @blacklist_sender_maps @score_sender_maps @author_to_policy_bank_maps @signer_reputation_maps @message_size_limit_maps @debug_sender_maps @debug_recipient_maps @bypass_virus_checks_maps @bypass_spam_checks_maps @bypass_banned_checks_maps @bypass_header_checks_maps @viruses_that_fake_sender_maps @virus_name_to_spam_score_maps @virus_name_to_policy_bank_maps @remove_existing_spam_headers_maps @sa_userconf_maps @sa_username_maps @keep_decoded_original_maps @map_full_type_to_short_type_maps @virus_lovers_maps @spam_lovers_maps @unchecked_lovers_maps @banned_files_lovers_maps @bad_header_lovers_maps @virus_quarantine_to_maps @banned_quarantine_to_maps @unchecked_quarantine_to_maps @spam_quarantine_to_maps @bad_header_quarantine_to_maps @clean_quarantine_to_maps @archive_quarantine_to_maps @virus_admin_maps @banned_admin_maps @spam_admin_maps @bad_header_admin_maps @spam_modifies_subj_maps @warnvirusrecip_maps @warnbannedrecip_maps @warnbadhrecip_maps @addr_extension_virus_maps @addr_extension_spam_maps @addr_extension_banned_maps @addr_extension_bad_header_maps )) { my $g = $varname; $g =~ s{\@}{Amavis::Conf::}; # qualified variable name my $label = $varname; $label=~s/^\@//; $label=~s/_maps$//; { no strict 'refs'; require Amavis::Lookup::Label; unshift(@$g, # NOTE: a symbolic reference Amavis::Lookup::Label->new($label)) if @$g; # no label if empty } } } # return a list of actually read&evaluated configuration files sub get_config_files_read() { @actual_config_files } # read and evaluate a configuration file, some sanity checking and housekeeping # sub read_config_file($$) { my($config_file,$is_optional) = @_; my(@stat_list) = stat($config_file); # symlinks-friendly my $errn = @stat_list ? 0 : 0+$!; if ($errn == ENOENT && $is_optional) { # don't complain if missing } else { my $owner_uid = $stat_list[4]; my $msg; if ($errn == ENOENT) { $msg = "does not exist" } elsif ($errn) { $msg = "is inaccessible: $!" } elsif (-d _) { $msg = "is a directory" } elsif (-S _ || -b _ || -c _) { $msg = "is not a regular file or pipe" } elsif (!$i_know_what_i_am_doing{no_conf_file_writable_check}) { if ($> && -o _) { $msg = "should not be owned by EUID $>"} elsif ($> && -w _) { $msg = "is writable by EUID $>, EGID $)" } elsif ($owner_uid) { $msg = "should be owned by root (uid 0)" } } if (defined $msg) { die "Config file \"$config_file\" $msg," } $read_config_files_depth++; push(@actual_config_files, $config_file); if ($read_config_files_depth >= 100) { print STDERR "read_config_files: recursion depth limit exceeded\n"; exit 1; # avoid unwinding deep recursion, abort right away } # avoid magic of searching @INC in do() and reporting unrelated errors $config_file = './'.$config_file if $config_file !~ m{^\.{0,2}/}; local($1,$2,$3,$4,$5,$6,$7,$8,$9); local $/ = $/; # protect us from a potential change in a config file $! = 0; if (defined(do $config_file)) {} elsif ($@ ne '') { die "Error in config file \"$config_file\": $@" } elsif ($! != 0) { die "Error reading config file \"$config_file\": $!" } $read_config_files_depth-- if $read_config_files_depth > 0; } 1; } sub include_config_files(@) { read_config_file($_,0) for @_; 1 } sub include_optional_config_files(@) { read_config_file($_,1) for @_; 1 } # supply remaining defaults after config files have already been read/evaluated # sub supply_after_defaults() { $daemon_chroot_dir = '' if !defined $daemon_chroot_dir || $daemon_chroot_dir eq '/'; unshift @daemon_groups, $daemon_group if defined $daemon_group; @daemon_groups = Amavis::Util::get_user_groups($daemon_user) if not @daemon_groups; # provide some sensible defaults for essential settings (post-defaults) $TEMPBASE = $MYHOME if !defined $TEMPBASE; $helpers_home = $MYHOME if !defined $helpers_home; $db_home = "$MYHOME/db" if !defined $db_home; @zmq_sockets = ( "ipc://$MYHOME/amavisd-zmq.sock" ) if !@zmq_sockets; $pid_file = "$MYHOME/amavisd.pid" if !defined $pid_file && $daemonize; # just keep $lock_file undefined by default, a temp file (File::Temp::tmpnam) # will be provided by Net::Server for 'flock' serialization on a socket accept() # $lock_file = "$MYHOME/amavisd.lock" if !defined $lock_file; local($1,$2); $X_HEADER_LINE = $myproduct_name . ' at ' . Amavis::Util::idn_to_ascii($mydomain) if !defined $X_HEADER_LINE; $X_HEADER_TAG = 'X-Virus-Scanned' if !defined $X_HEADER_TAG; if ($X_HEADER_TAG =~ /^[!-9;-\176]+\z/) { # implicitly add to %allowed_added_header_fields for compatibility, # unless the hash entry already exists my $allowed_hdrs = cr('allowed_added_header_fields'); $allowed_hdrs->{lc($X_HEADER_TAG)} = 1 if $allowed_hdrs && !exists($allowed_hdrs->{lc($X_HEADER_TAG)}); } $gunzip = "$gzip -d" if !defined $gunzip && $gzip ne ''; $bunzip2 = "$bzip2 -d" if !defined $bunzip2 && $bzip2 ne ''; $unlzop = "$lzop -d" if !defined $unlzop && $lzop ne ''; # substring "${myhostname}" will be expanded later, just before use my $pname = '"Content-filter at ${myhostname_utf8}"'; $hdrfrom_notify_sender = $pname . ' <postmaster@${myhostname_ascii}>' if !defined $hdrfrom_notify_sender; $hdrfrom_notify_recip = $mailfrom_notify_recip eq '' ? $hdrfrom_notify_sender : sprintf("%s <%s>", $pname, Amavis::Util::mail_addr_idn_to_ascii($mailfrom_notify_recip)) if !defined $hdrfrom_notify_recip; $hdrfrom_notify_admin = $mailfrom_notify_admin eq '' ? $hdrfrom_notify_sender : sprintf("%s <%s>", $pname, Amavis::Util::mail_addr_idn_to_ascii($mailfrom_notify_admin)) if !defined $hdrfrom_notify_admin; $hdrfrom_notify_spamadmin = $mailfrom_notify_spamadmin eq '' ? $hdrfrom_notify_sender : sprintf("%s <%s>", $pname, Amavis::Util::mail_addr_idn_to_ascii($mailfrom_notify_spamadmin)) if !defined $hdrfrom_notify_spamadmin; $hdrfrom_notify_release = $hdrfrom_notify_sender if !defined $hdrfrom_notify_release; $hdrfrom_notify_report = $hdrfrom_notify_sender if !defined $hdrfrom_notify_report; if ($final_banned_destiny == D_DISCARD && c('warnbannedsender') ) { $final_banned_destiny = D_BOUNCE } if ($final_bad_header_destiny == D_DISCARD && c('warnbadhsender') ) { $final_bad_header_destiny = D_BOUNCE } if (!%banned_rules) { # an associative array mapping a rule name # to a single 'banned names/types' lookup table %banned_rules = ('DEFAULT'=>$banned_filename_re); # backward compatible } 1; } 1;