File: //usr/share/perl5/vendor_perl/Amavis/Conf.pm
# 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.1'; $myversion_date = '20240304';
$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/^(?i: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;