3
# Licensed to the Apache Software Foundation (ASF) under one or more
4
# contributor license agreements. See the NOTICE file distributed with
5
# this work for additional information regarding copyright ownership.
6
# The ASF licenses this file to you under the Apache License, Version 2.0
7
# (the "License"); you may not use this file except in compliance with
8
# the License. You may obtain a copy of the License at:
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS,
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
# See the License for the specific language governing permissions and
16
# limitations under the License.
19
my $PREFIX = '@@PREFIX@@'; # substituted at 'make' time
20
my $DEF_RULES_DIR = '@@DEF_RULES_DIR@@'; # substituted at 'make' time
21
my $LOCAL_RULES_DIR = '@@LOCAL_RULES_DIR@@'; # substituted at 'make' time
22
my $LOCAL_STATE_DIR = '@@LOCAL_STATE_DIR@@'; # substituted at 'make' time
23
use lib '@@INSTALLSITELIB@@'; # substituted at 'make' time
25
# added by jm for use inside the distro
26
# This is disabled during the "make install" process.
28
if ( -e '../blib/lib/Mail/SpamAssassin.pm' ) { # REMOVEFORINST
29
unshift ( @INC, '../blib/lib' ); # REMOVEFORINST
30
} else { # REMOVEFORINST
31
unshift ( @INC, '../lib' ); # REMOVEFORINST
39
# Big Ugly Hack; purpose: don't force requirement on IO::Socket::INET6
43
require IO::Socket::INET6;
47
require IO::Socket::INET;
48
*new_io_socket_inetx = sub { IO::Socket::INET->new(@_); };
49
*ip_or_name_to_ip = sub {
50
my $in_addr = (gethostbyname(shift))[4] or return undef;
51
Socket::inet_ntoa($in_addr);
53
*peer_info_from_socket = sub {
55
my ($port, $in_addr) = Socket::sockaddr_in($sock->peername)
57
my $addr = Socket::inet_ntoa($in_addr) or return;
58
my $host = gethostbyaddr($in_addr, Socket::AF_INET()) || $addr;
59
return ($port, $addr, $host);
63
*new_io_socket_inetx = sub { IO::Socket::INET6->new(@_); };
64
*ip_or_name_to_ip = sub {
67
shift, '', Socket::AF_UNSPEC(), Socket::SOCK_STREAM()
71
$addr = (Socket6::getnameinfo($addr, Socket6::NI_NUMERICHOST()))[0];
73
*peer_info_from_socket = sub {
75
my $addr = $sock->peerhost or return;
77
(Socket6::getnameinfo($sock->peername, Socket6::NI_NAMEREQD()))[0]
79
return ($sock->peerport(), $addr, $host);
87
use Mail::SpamAssassin;
88
use Mail::SpamAssassin::NetSet;
89
use Mail::SpamAssassin::SubProcBackChannel;
90
use Mail::SpamAssassin::SpamdForkScaling qw(:pfstates);
91
use Mail::SpamAssassin::Logger qw(:DEFAULT log_message);
92
use Mail::SpamAssassin::Util qw(untaint_var exit_status_str
93
am_running_on_windows);
94
use Mail::SpamAssassin::Timeout;
97
use POSIX qw(:sys_wait_h);
98
use POSIX qw(setsid sigprocmask _exit);
105
use Time::HiRes qw(time);
107
use constant RUNNING_ON_MACOS => ($^O =~ /^darwin/oi);
109
# Check to make sure the script version and the module version matches.
110
# If not, die here! Also, deal with unchanged VERSION macro.
111
if ($Mail::SpamAssassin::VERSION ne '@@VERSION@@' && '@@VERSION@@' ne "\@\@VERSION\@\@") {
112
die 'spamd: spamd script is v@@VERSION@@, but using modules v'.$Mail::SpamAssassin::VERSION."\n";
116
# redirect __WARN__ and __DIE__
117
# do not trap warnings here based on eval scope; evals are very
118
# common throughout. die()s can be trapped though.
119
$SIG{__WARN__} = sub {
120
log_message("warn", $_[0]);
122
$SIG{__DIE__} = sub {
123
# see http://use.perl.org/comments.pl?mode=flat&sid=33872 for $^S
124
log_message("error", $_[0]) unless $^S;
129
EX_OK => 0, # no problems
130
EX_USAGE => 64, # command line usage error
131
EX_DATAERR => 65, # data format error
132
EX_NOINPUT => 66, # cannot open input
133
EX_NOUSER => 67, # addressee unknown
134
EX_NOHOST => 68, # host name unknown
135
EX_UNAVAILABLE => 69, # service unavailable
136
EX_SOFTWARE => 70, # internal software error
137
EX_OSERR => 71, # system error (e.g., can't fork)
138
EX_OSFILE => 72, # critical OS file missing
139
EX_CANTCREAT => 73, # can't create (user) output file
140
EX_IOERR => 74, # input/output error
141
EX_TEMPFAIL => 75, # temp failure; user is invited to retry
142
EX_PROTOCOL => 76, # remote error in protocol
143
EX_NOPERM => 77, # permission denied
144
EX_CONFIG => 78, # configuration error
145
EX_TIMEOUT => 79, # read timeout
149
printf("%s version %s\n", "SpamAssassin Server", Mail::SpamAssassin::Version());
150
printf(" running on Perl %s\n", join(".", map { $_||=0; $_*1 } ($] =~ /(\d)\.(\d{3})(\d{3})?/)));
151
eval { require IO::Socket::SSL; };
152
printf(" with SSL support (%s %s)\n", "IO::Socket::SSL", $IO::Socket::SSL::VERSION) unless ($@);
153
eval { require Compress::Zlib; };
154
printf(" with zlib support (%s %s)\n", "Compress::Zlib", $Compress::Zlib::VERSION) unless ($@);
157
sub print_usage_and_exit {
158
my ( $message, $respnam ) = (@_);
159
$respnam ||= 'EX_USAGE';
161
if ($respnam eq 'EX_OK' ) {
170
-message => $message,
171
-exitval => $resphash{$respnam},
178
'ident-timeout' => 5.0,
179
# scaling settings; some of these aren't actually settable via cmdline
180
'server-scale-period' => 2, # how often to scale the # of kids, secs
181
'min-children' => 1, # min kids to have running
182
'min-spare' => 1, # min kids that must be spare
183
'max-spare' => 2, # max kids that should be spare
184
'cf' => [], # extra config lines
189
# Untaint all command-line options and ENV vars, since spamd is launched
190
# as a daemon from a known-safe environment. Also store away some of the
191
# vars we need for a SIGHUP later on.
193
# Testing for taintedness only works before detainting %ENV
194
Mail::SpamAssassin::Util::am_running_in_taint_mode();
196
# First clean PATH and untaint the environment -- need to do this before
197
# Cwd::cwd(), else it will croak.
198
Mail::SpamAssassin::Util::clean_path_in_taint_mode();
199
untaint_var( \%ENV );
201
# The zeroth argument will be replaced in daemonize().
202
my $ORIG_ARG0 = untaint_var($0);
204
# Getopt::Long clears all arguments it processed (untaint both @ARGVs here!)
205
my @ORIG_ARGV = untaint_var( \@ARGV );
207
# daemonize() switches to the root later on and we need to come back here
208
# somehow -- untaint the dir to be on the safe side.
209
my $ORIG_CWD = untaint_var( Cwd::cwd() );
211
prepare_for_sighup_restart();
213
# Parse the command line
214
Getopt::Long::Configure("bundling");
216
'allow-tell' => \$opt{'tell'},
217
'allowed-ips|A=s' => \@{ $opt{'allowed-ip'} },
218
'auth-ident' => \$opt{'auth-ident'},
219
'configpath|C=s' => \$opt{'configpath'},
220
'c' => \$opt{'create-prefs'},
221
'create-prefs!' => \$opt{'create-prefs'},
222
'daemonize!' => \$opt{'daemonize'},
223
'debug|D:s' => \$opt{'debug'},
224
'd' => \$opt{'daemonize'},
225
'groupname|g=s' => \$opt{'groupname'},
226
'helper-home-dir|H:s' => \$opt{'home_dir_for_helpers'},
227
'help|h' => \$opt{'help'},
228
'ident-timeout=f' => \$opt{'ident-timeout'},
229
'ipv4only|ipv4-only|ipv4' => \$opt{'force_ipv4'},
230
'ldap-config!' => \$opt{'ldap-config'},
231
'listen-ip|ip-address|i:s' => \$opt{'listen-ip'},
232
'local!' => \$opt{'local'},
233
'L' => \$opt{'local'},
234
'l' => \$opt{'tell'},
235
'round-robin!' => \$opt{'round-robin'},
236
'min-children=i' => \$opt{'min-children'},
237
'max-children|m=i' => \$opt{'max-children'},
238
'min-spare=i' => \$opt{'min-spare'},
239
'max-spare=i' => \$opt{'max-spare'},
240
'max-conn-per-child=i' => \$opt{'max-conn-per-child'},
241
'nouser-config|x' => sub { $opt{'user-config'} = 0 },
242
'paranoid!' => \$opt{'paranoid'},
243
'P' => \$opt{'paranoid'},
244
'pidfile|r=s' => \$opt{'pidfile'},
245
'port|p=s' => \$opt{'port'},
246
'Q' => \$opt{'setuid-with-sql'},
247
'q' => \$opt{'sql-config'},
248
'server-cert=s' => \$opt{'server-cert'},
249
'server-key=s' => \$opt{'server-key'},
250
'setuid-with-ldap' => \$opt{'setuid-with-ldap'},
251
'setuid-with-sql' => \$opt{'setuid-with-sql'},
252
'siteconfigpath=s' => \$opt{'siteconfigpath'},
253
'cf=s' => \@{$opt{'cf'}},
254
'socketgroup=s' => \$opt{'socketgroup'},
255
'socketmode=s' => \$opt{'socketmode'},
256
'socketowner=s' => \$opt{'socketowner'},
257
'socketpath=s' => \$opt{'socketpath'},
258
'sql-config!' => \$opt{'sql-config'},
259
'ssl' => \$opt{'ssl'},
260
'ssl-port=s' => \$opt{'ssl-port'},
261
'ssl-version=s' => \$opt{'ssl-version'},
262
'syslog-socket=s' => \$opt{'syslog-socket'},
263
'syslog|s=s' => \$opt{'syslog'},
264
'log-timestamp-fmt:s' => \$opt{'log-timestamp-fmt'},
265
'timeout-tcp|T=i' => \$opt{'timeout-tcp'},
266
'timeout-child|t=i' => \$opt{'timeout-child'},
267
'user-config' => \$opt{'user-config'},
268
'username|u=s' => \$opt{'username'},
269
'version|V' => \$opt{'version'},
270
'virtual-config-dir=s' => \$opt{'virtual-config-dir'},
271
'v' => \$opt{'vpopmail'},
272
'vpopmail!' => \$opt{'vpopmail'},
275
# NOTE: These are old options. We should ignore (but warn about)
276
# the ones that are now defaults. Everything else gets a die (see note2)
277
# so the user doesn't get us doing something they didn't expect.
279
# NOTE2: 'die' doesn't actually stop the process, GetOptions() catches
280
# it, then passes the error on, so we'll end up doing a Usage statement.
281
# You can avoid that by doing an explicit exit in the sub.
285
'F:i' => sub { warn "spamd: the -F option has been removed from spamd, please remove from your commandline and re-run\n"; exit 2; },
286
'add-from!' => sub { warn "spamd: the --add-from option has been removed from spamd, please remove from your commandline and re-run\n"; exit 2; },
289
'stop-at-threshold|S' => sub { warn "spamd: the -S option has been deprecated and is no longer supported, ignoring\n" },
291
) or print_usage_and_exit();
294
print_usage_and_exit(qq{For more details, use "man spamd".\n}, 'EX_OK');
296
if ($opt{'version'}) {
298
exit($resphash{'EX_OK'});
301
my $log_timestamp_fmt = $opt{'log-timestamp-fmt'};
302
if (defined $log_timestamp_fmt && lc($log_timestamp_fmt) eq 'default') {
303
undef $log_timestamp_fmt; # undefined implies per-logger's default
305
if (defined $log_timestamp_fmt) {
306
# a nondefault timestamp format was specified, need to reopen stderr logger
307
Mail::SpamAssassin::Logger::remove('stderr');
308
Mail::SpamAssassin::Logger::add(method => 'stderr',
309
timestamp_fmt => $log_timestamp_fmt);
312
# Enable debugging, if any areas were specified. We do this already here,
313
# accessing some non-public API so we can use the convenient dbg() routine.
314
# Don't do this at home (aka any 3rd party tools), kids!
315
if (defined $opt{'debug'}) {
316
$opt{'debug'} ||= 'all';
318
# always turn on at least info-level debugging for spamd
319
$opt{'debug'} ||= 'info';
320
# turn on debugging facilities as soon as possible
321
Mail::SpamAssassin::Logger::add_facilities($opt{'debug'});
323
# bug 2228: make the values of (almost) all parameters which accept file paths
324
# absolute, so they are still valid after daemonize()
335
$opt{$opt} = Mail::SpamAssassin::Util::untaint_file_path(
336
File::Spec->rel2abs( $opt{$opt} ) # rel2abs taints the new value!
340
# sanity checking on parameters: if --socketpath is used, and --port or
341
# --ssl-port is NOT specified, it means that we're using UNIX domain sockets,
342
# none of the IP params are allowed. The code would probably work ok if we
343
# didn't check it, but it's better if we detect the error and report it lest
344
# the admin find surprises.
349
if (!defined $opt{'socketpath'}) {
356
( @{ $opt{'allowed-ip'} } > 0 )
357
or defined $opt{'auth-ident'}
358
or defined $opt{'port'}
366
or defined $opt{'ssl-port'}
367
or defined $opt{'ssl-version'}
371
if (!defined $opt{'ssl-port'}) {
378
and ( $opt{'socketowner'}
379
or $opt{'socketgroup'}
380
or $opt{'socketmode'})
383
print_usage_and_exit("ERROR: --socketowner/group/mode requires --socketpath param");
386
# These can be changed on command line with -A flag
387
# but only if we're not using UNIX domain sockets
388
my $allowed_nets = Mail::SpamAssassin::NetSet->new();
389
if ( not defined $opt{'socketpath'} ) {
390
if ( @{ $opt{'allowed-ip'} } ) {
391
set_allowed_ip( grep length, map { split /,/ } @{ $opt{'allowed-ip'} } );
394
set_allowed_ip('127.0.0.1'); #, '::1'); M::SA::NetSet needs fixing for IPv6
398
# ident-based spamc user authentication
399
if ( $opt{'auth-ident'} ) {
400
eval { require Net::Ident };
401
die "spamd: ident-based authentication requested, but Net::Ident is unavailable ($@)\n"
404
$opt{'ident-timeout'} = undef if $opt{'ident-timeout'} <= 0.0;
405
import Net::Ident qw(ident_lookup);
408
# Check for server certs
409
$opt{'server-key'} ||= "$LOCAL_RULES_DIR/certs/server-key.pem";
410
$opt{'server-cert'} ||= "$LOCAL_RULES_DIR/certs/server-cert.pem";
412
eval { require IO::Socket::SSL };
413
die "spamd: SSL encryption requested, but IO::Socket::SSL is unavailable ($@)\n"
416
if ( !-e $opt{'server-key'} ) {
417
die "spamd: server key file $opt{'server-key'} does not exist\n";
419
if ( !-e $opt{'server-cert'} ) {
420
die "spamd: server certificate file $opt{'server-cert'} does not exist\n";
424
### Begin initialization of logging ########################
426
# The syslog facility can be changed on the command line with the
427
# --syslog flag. Special cases are:
428
# * A log facility of 'stderr' will log to STDERR
429
# * " " " " 'null' disables all logging
430
# * " " " " 'file' logs to the file "spamd.log"
431
# * Any facility containing non-word characters is interpreted as the name
432
# of a specific logfile
433
my $log_facility = $opt{'syslog'} || 'mail';
435
# The --syslog-socket option specifies one of the possible socket types or
436
# logging mechanisms as accepted by the Sys::Syslog::setlogsock() subroutine.
437
# Depending on a version of Sys::Syslog and on the underlying operating system,
438
# one of the following values (or their subset) can be used: native, eventlog,
439
# tcp, udp, inet, unix, stream, pipe, console. The value 'eventlog' is
440
# specific to Win32 events logger and requires a perl module Win32::EventLog.
442
# In addition to values acceptable by Sys::Syslog::setlogsock(),
443
# a --syslog-socket=none is mapped to --syslog=stderr and $log_socket='file'.
445
# A value 'file' in variable $log_socket implies logging to any file handler
446
# (either a specific log file or STDERR), A value 'none' in $log_socket
447
# represents no logging, equivalent to --syslog=null.
449
# (old text: The socket to log over can be changed on the command line with
450
# the --syslog-socket flag. Logging to any file handler (either a specific log
451
# file or STDERR) is internally represented by a socket 'file', no logging
452
# at all is 'none'. The latter is different from --syslog-socket=none which
453
# gets mapped to --syslog=stderr and such --syslog-socket=file. An internal
454
# socket of 'none' means as much as --syslog=null. Sounds complicated? It is.
458
my $log_socket = $opt{'syslog-socket'};
460
if (!defined $log_socket || $log_socket eq '') {
461
$log_socket = am_running_on_windows() ? 'none' : 'unix';
463
$log_socket = lc $log_socket;
466
# This is the default log file; it can be changed on the command line
467
# via a --syslog flag containing non-word characters.
468
my $log_file = "spamd.log";
470
# A specific log file was given (--syslog=/path/to/file).
471
if ($log_facility =~ /[^a-z0-9]/) {
472
$log_file = $log_facility;
473
$log_socket = 'file';
475
# The generic log file was requested (--syslog=file).
476
elsif (lc($log_facility) eq 'file') {
477
$log_socket = 'file';
479
# The casing is kept only if the facility specified a file.
481
$log_facility = lc($log_facility);
484
# Either above or at the command line the socket was set
485
# to 'file' (--syslog-socket=file).
486
if ($log_socket eq 'file') {
487
$log_facility = 'file';
489
# The socket 'none' (--syslog-socket=none) historically
490
# represents logging to STDERR.
491
elsif ($log_socket eq 'none') {
492
$log_facility = 'stderr';
495
# Either above or at the command line the facility was set
496
# to 'stderr' (--syslog=stderr).
497
if ($log_facility eq 'stderr') {
498
$log_socket = 'file';
501
# The --log-timestamp-fmt option can provide a POSIX strftime(3) format for
502
# timestamps included in each logged message. Each logger (stderr, file,
503
# syslog) has its own default value for a timestamp format, which applies when
504
# --log-timestamp-fmt option is not given, or with --log-timestamp-fmt=default
505
# Timestamps can be turned off by specifying an empty string with this
506
# option, e.g. --log-timestamp-fmt='' or just --log-timestamp-fmt=
507
# Typical use: --log-timestamp-fmt='%a %b %e %H:%M:%S %Y' .
509
# Logging via syslog is requested.
510
if ($log_socket ne 'file' && $log_facility ne 'null') {
511
if (!Mail::SpamAssassin::Logger::add(method => 'syslog',
512
socket => $log_socket,
513
facility => $log_facility,
515
timestamp_fmt => $log_timestamp_fmt))
517
# syslog method failed
518
$log_facility = 'stderr';
521
# Otherwise, the user wants to log to some file.
522
elsif ($log_facility eq 'file') {
523
if (!Mail::SpamAssassin::Logger::add(method => 'file',
524
filename => $log_file,
525
timestamp_fmt => $log_timestamp_fmt))
528
$log_facility = 'stderr';
532
### End initialization of logging ##########################
534
# REIMPLEMENT: if $log_socket is none, fall back to log_facility 'stderr'.
535
# If log_fac is stderr and defined $opt{'debug'}, set log_fac to 'null' to
536
# avoid duplicating log messages.
537
# TVD: isn't this already done up above?
539
# support setuid() to user unless:
542
# doing --vpopmail or --virtual-config-dir
543
# using --sql-config or --ldap-config
544
# (unless we're also using --setuid-with-sql or --setuid-with-ldap)
545
my $setuid_to_user = (
549
$opt{'virtual-config-dir'} ||
550
($opt{'sql-config'} && !$opt{'setuid-with-sql'}) ||
551
($opt{'ldap-config'} && !$opt{'setuid-with-ldap'})
554
dbg("spamd: will perform setuids? $setuid_to_user");
556
if ( $opt{'vpopmail'} ) {
557
if ( !$opt{'username'} ) {
558
die "spamd: cannot use --vpopmail without -u\n";
562
if ( $opt{'virtual-config-dir'} ) {
563
if ( !$opt{'username'} ) {
564
die "spamd: cannot use --virtual-config-dir without -u\n";
568
if ($opt{'sql-config'} && !$opt{'setuid-with-sql'}) {
569
if ( !$opt{'username'} ) {
570
die "spamd: cannot use --sql-config without -u\n";
574
if ($opt{'ldap-config'} && !$opt{'setuid-with-ldap'}) {
575
if ( !$opt{'username'} ) {
576
die "spamd: cannot use --ldap-config without -u\n";
580
# always copy the config, later code may disable
581
my $copy_config_p = 1;
585
my $client; # used for the client connection ...
586
my $childlimit; # max number of kids allowed
587
my $timeout_tcp; # socket timeout (connect->headers), 0=no timeout
588
my $timeout_child; # processing timeout (headers->finish), 0=no timeout
589
my $clients_per_child; # number of clients each child should process
590
my %children; # current children
592
if ( defined $opt{'max-children'} ) {
593
$childlimit = $opt{'max-children'};
595
# Make sure that the values are at least 1
596
$childlimit = undef if ( $childlimit < 1 );
599
if ( defined $opt{'max-conn-per-child'} ) {
600
$clients_per_child = $opt{'max-conn-per-child'};
602
# Make sure that the values are at least 1
603
$clients_per_child = undef if ( $clients_per_child < 1 );
606
if (defined $opt{'timeout-tcp'}) {
607
$timeout_tcp = $opt{'timeout-tcp'};
608
$timeout_tcp = undef if ($timeout_tcp < 1);
611
if (defined $opt{'timeout-child'}) {
612
$timeout_child = $opt{'timeout-child'};
613
$timeout_child = undef if ($timeout_child < 1);
616
# Set some "sane" limits for defaults
618
$clients_per_child ||= 200;
619
$timeout_child ||= 300;
622
# ensure scaling parameters are logical
623
if ($opt{'min-children'} < 1) {
624
$opt{'min-children'} = 1;
626
if ($opt{'min-spare'} < 0) {
627
$opt{'min-spare'} = 0;
629
if ($opt{'min-spare'} > $childlimit) {
630
$opt{'min-spare'} = $childlimit-1;
632
if ($opt{'max-spare'} < $opt{'min-spare'}) {
633
# emulate Apache behaviour:
634
# http://httpd.apache.org/docs-2.0/mod/prefork.html#maxspareservers
635
$opt{'max-spare'} = $opt{'min-spare'}+1;
639
if ( $opt{'create-prefs'} ) { $dontcopy = 0; }
642
if ( defined $ENV{'HOME'} ) {
643
if ( defined $opt{'username'} )
644
{ # spamd is going to run as another user, so reset $HOME
645
if ( my $nh = ( getpwnam( $opt{'username'} ) )[7] ) {
649
die "spamd: unable to determine home directory for user '"
650
. $opt{'username'} . "'\n";
654
$orighome = $ENV{'HOME'}; # keep a copy for use by Razor, Pyzor etc.
655
delete $ENV{'HOME'}; # we do not want to use this when running spamd
658
# Do whitelist later in tmp dir. Side effect: this will be done as -u user.
660
my ( $sslport, $sslversion, $inetport, $addr, $proto );
662
if ( $listen_inet || $listen_ssl ) {
663
$proto = getprotobyname('tcp') or die "getprotobyname(tcp): $!";
665
$addr = $opt{'listen-ip'};
668
$addr = ip_or_name_to_ip($addr);
669
die "spamd: invalid address: $opt{'listen-ip'}\n" unless $addr;
672
$addr = '0.0.0.0'; # FIXME: this won't bind to IPv6 sockets
680
my $backchannel = Mail::SpamAssassin::SubProcBackChannel->new();
682
if (!$opt{'round-robin'})
684
my $max_children = $childlimit;
686
# change $childlimit to avoid churn when we startup and create loads
687
# of spare servers; when we're using scaling, it's not as important
688
# as it was with the old algorithm.
689
if ($childlimit > $opt{'max-spare'}) {
690
$childlimit = $opt{'max-spare'};
693
$scaling = Mail::SpamAssassin::SpamdForkScaling->new({
694
backchannel => $backchannel,
695
min_children => $opt{'min-children'},
696
max_children => $max_children,
697
min_idle => $opt{'min-spare'},
698
max_idle => $opt{'max-spare'},
699
cur_children_ref => \$childlimit
703
# ---------------------------------------------------------------------------
705
my $listeninfo = compose_listen_info_string();
707
sub compose_listen_info_string {
710
if ( $listen_unix ) {
711
push @listeninfo, "UNIX domain socket " . $opt{'socketpath'};
715
$sslport = $opt{'ssl-port'} || $opt{'port'} || 783;
716
if ($sslport !~ /^(\d+)$/ ) {
717
$sslport = ( getservbyname($sslport, 'tcp') )[2];
718
die "spamd: invalid ssl-port: $opt{'port'}\n" unless $sslport;
720
$sslversion = $opt{'ssl-version'} || 'sslv3';
721
if ($sslversion !~ /^(?:sslv3|tlsv1)$/) {
722
die "spamd: invalid ssl-version: $opt{'ssl-version'}\n";
725
push @listeninfo, "SSL port $sslport/tcp";
726
push @listeninfo, "SSL version $sslversion";
729
if ( $listen_inet ) {
730
$inetport = $opt{'port'} || 783;
731
if ($inetport !~ /^(\d+)$/ ) {
732
$inetport = ( getservbyname($inetport, 'tcp') )[2];
733
die "spamd: invalid port: $opt{'port'}\n" unless $inetport;
736
push @listeninfo, "port $inetport/tcp";
739
# just for reporting at startup
740
$listeninfo = join ', ', @listeninfo;
743
# ---------------------------------------------------------------------------
744
# Server (listening) socket setup for the various supported types
746
my ( $server_inet, $server_unix, $server_ssl );
747
my ( $fd_inet, $fd_unix, $fd_ssl );
748
my $have_multiple_server_socks;
749
my $server_select_mask;
751
# abstract out the setup-retry code
752
if ( $listen_unix ) {
753
server_sock_setup(sub { server_sock_setup_unix(); });
756
server_sock_setup(sub { server_sock_setup_ssl(); });
758
if ( $listen_inet ) {
759
server_sock_setup(sub { server_sock_setup_inet(); });
762
sub server_sock_setup {
765
# retry 3 times to bind to the listening socket; 3 seconds delay,
766
# max, but should allow a little time for any existing shutting-down
767
# server to complete shutdown
769
for my $retry (1 .. $lastretry) {
770
if ($retry > 1) { sleep 1; }
773
last unless ($@); # success => break
775
if ($retry == $lastretry) {
776
die $@; # this is fatal
778
warn "server socket setup failed, retry $retry: $@";
784
# ---------------------------------------------------------------------------
787
sub server_sock_setup_unix {
788
my $path = $opt{'socketpath'};
790
# see if the socket is in use: if we connect to the current socket, it
791
# means that spamd is already running, so we have to bail on our own.
792
# Yes, there is a window here: best we can do for now. There is almost
793
# certainly a better way, but we don't know it. Yet.
797
die "spamd: file $path exists but is no socket, exiting\n";
800
if ( new IO::Socket::UNIX( Peer => $path, Type => SOCK_STREAM ) ) {
801
# we connected successfully: must alreadybe running
803
undef $opt{'socketpath'}; # so exit handlers won't unlink it!
805
die "spamd: already running on $path, exiting\n";
808
dbg("spamd: removing stale socket file $path");
812
if (not -d (File::Spec->splitpath($path))[1]) {
813
die "spamd: directory for $path does not exist, exiting\n";
821
dbg("spamd: creating UNIX socket:\n" . join("\n", map { " $_: " . (defined $socket{$_} ? $socket{$_} : "(undef)") } sort keys %socket));
822
$server_unix = IO::Socket::UNIX->new(%socket);
824
# sanity check! cf. bug 3490
825
if (not $server_unix or not -S $path) {
826
unless ($server_unix) {
827
dbg "spamd: socket path might have been truncated due to system limits\n";
828
die "spamd: could not create UNIX socket on $path: $!\n";
830
my $hostpath = $server_unix->hostpath();
831
if ($hostpath ne $path) {
832
warn "spamd: socket path was truncated at position " . length($hostpath) . "\n";
833
warn "spamd: leaving stale socket at $hostpath\n" if -S $hostpath;
834
die "spamd: path length for UNIX socket on $path exceeds system limit, exiting\n";
837
die "spamd: could not find newly-created UNIX socket on $path: $!\n";
841
my $mode = $opt{socketmode};
845
$mode = 0666; # default
848
my $owner = $opt{socketowner};
849
my $group = $opt{socketgroup};
850
if ($owner || $group) {
854
my ($login,$pass,$puid,$pgid) = getpwnam($owner)
855
or die "spamd: $owner not in passwd database\n";
859
my ($name,$pass,$ggid,$members) = getgrnam($group)
860
or die "spamd: $group not in group database\n";
863
if (!chown $uid, $gid, $path) {
864
die "spamd: could not chown $path to $uid/$gid: $!";
868
if (!chmod $mode, $path) { # make sure everybody can talk to it
869
die "spamd: could not chmod $path to $mode: $!";
873
sub server_sock_setup_ssl {
876
LocalPort => $sslport,
881
SSL_version => $sslversion,
882
SSL_verify_mode => 0x00,
883
SSL_key_file => $opt{'server-key'},
884
SSL_cert_file => $opt{'server-cert'}
886
dbg("spamd: creating SSL socket:\n" . join("\n", map { " $_: " . (defined $socket{$_} ? $socket{$_} : "(undef)") } sort keys %socket));
887
$server_ssl = new IO::Socket::SSL(%socket)
888
|| die "spamd: could not create SSL socket on $addr:$sslport: $!\n";
891
sub server_sock_setup_inet {
894
LocalPort => $inetport,
900
dbg("spamd: creating INET socket:\n" . join("\n", map { " $_: " . (defined $socket{$_} ? $socket{$_} : "(undef)") } sort keys %socket));
901
$server_inet = new_io_socket_inetx(%socket)
902
|| die "spamd: could not create INET socket on $addr:$inetport: $!\n";
905
# ---------------------------------------------------------------------------
907
# for select() purposes: make a map of the server socket FDs
908
map_server_sockets();
910
if ( defined $opt{'pidfile'} ) {
912
Mail::SpamAssassin::Util::untaint_file_path( $opt{'pidfile'} );
916
my $spamtest = Mail::SpamAssassin->new(
918
dont_copy_prefs => $dontcopy,
919
rules_filename => ( $opt{'configpath'} || 0 ),
920
site_rules_filename => ( $opt{'siteconfigpath'} || 0 ),
921
post_config_text => join("\n", @{$opt{'cf'}})."\n",
922
force_ipv4 => ( $opt{'force_ipv4'} || 0 ),
923
local_tests_only => ( $opt{'local'} || 0 ),
924
debug => ( $opt{'debug'} || 0 ),
925
paranoid => ( $opt{'paranoid'} || 0 ),
927
home_dir_for_helpers => (
928
defined $opt{'home_dir_for_helpers'}
929
? $opt{'home_dir_for_helpers'}
933
DEF_RULES_DIR => $DEF_RULES_DIR,
934
LOCAL_RULES_DIR => $LOCAL_RULES_DIR,
935
LOCAL_STATE_DIR => $LOCAL_STATE_DIR
939
# if $clients_per_child == 1, there's no point in copying configs around
940
unless ($clients_per_child > 1) {
941
# unset $copy_config_p so we don't bother trying to copy things back
942
# after closing the connection
948
my $originalparent = $$;
949
$opt{'daemonize'} and daemonize();
951
# bug 3443: setup signal handlers before the kids since we may have to
952
# kill them... make sure this happens before setting up the pidfile to
953
# avoid a race condition.
955
setup_parent_sig_handlers();
957
# should be done post-daemonize such that any files created by this
958
# process are written with the right ownership and everything.
959
preload_modules_with_tmp_homedir();
961
# this must be after preload_modules_with_tmp_homedir(), for bug 5606
962
$spamtest->init_learner({
963
opportunistic_expire_check_only => 1,
966
# bayes DBs may still be tied() at this point, so untie them and such.
967
$spamtest->finish_learner();
969
# If we're going to be switching users in check(), let's backup the
970
# fresh configuration now for later restoring ... MUST be placed after
971
# the M::SA creation.
975
if ($copy_config_p) {
976
foreach( 'username', 'user_dir', 'userstate_dir', 'learn_to_journal' ) {
977
$msa_backup{$_} = $spamtest->{$_} if (exists $spamtest->{$_});
980
$spamtest->copy_config(undef, \%conf_backup) ||
981
die "spamd: error returned from copy_config\n";
984
# bonus: SIGUSR2 to dump a stack trace. this is never reset
985
my $current_msgid = "(none)";
986
$SIG{USR2} = \&backtrace_handler if !am_running_on_windows();
988
# log server started, but processes watching the log to wait for connect
989
# should wait until they see the pid, after signal handlers are in place
990
# FIXME: two calls are one too much
991
info("spamd: server started on $listeninfo (running version "
992
. Mail::SpamAssassin::Version() . ")" );
996
# Make the pidfile ...
997
if (defined $opt{'pidfile'}) {
998
if (open PIDF, ">$opt{'pidfile'}") {
1003
warn "spamd: cannot write to PID file: $!\n";
1007
# now allow waiting processes to connect, if they're watching the log.
1008
# The test suite does this!
1009
info("spamd: server pid: $$\n");
1010
kill("USR1",$originalparent) if ($opt{'daemonize'});
1012
# Fork off our children.
1013
for ( 1 .. $childlimit ) {
1018
$scaling->set_server_fh($server_inet, $server_unix, $server_ssl);
1023
# wait for a signal (ie: child's death)
1024
# bug 4190: use a time-limited sleep, and call child_handler() even
1025
# if haven't received a SIGCHLD, due to inherent race condition
1028
$scaling->main_server_poll($opt{'server-scale-period'});
1030
# bug 6377: on win32 the parent never receives SIGCHLD
1031
# child_handler() if !$scaling || am_running_on_windows();
1032
child_handler(); # it doesn't hurt to call child_handler unconditionally
1034
do_sighup_restart() if defined $got_sighup;
1036
for (my $i = keys %children; $i < $childlimit; $i++) {
1041
# Kicks off a kid ...
1045
$backchannel->setup_backchannel_parent_pre_fork();
1047
# block signal for fork
1049
if (!am_running_on_windows()) {
1050
$sigset = POSIX::SigSet->new( POSIX::SIGINT(), POSIX::SIGCHLD() );
1051
sigprocmask( POSIX::SIG_BLOCK(), $sigset )
1052
or die "spamd: cannot block SIGINT/SIGCHLD for fork: $!\n";
1056
die "spamd: fork: $!" unless defined $pid;
1061
$children{$pid} = 1;
1062
info("spamd: server successfully spawned child process, pid $pid");
1063
$backchannel->setup_backchannel_parent_post_fork($pid);
1065
$scaling->add_child($pid);
1067
if (!am_running_on_windows()) {
1068
sigprocmask( POSIX::SIG_UNBLOCK(), $sigset )
1069
or die "spamd: cannot unblock SIGINT/SIGCHLD for fork: $!\n";
1071
#Changing to return the process id to improve communications for bug 6304
1077
# Reset signal handling to default settings, and unblock.
1078
# These lines must be as soon as possible after the fork (bug 4304)
1079
setup_child_sig_handlers();
1080
if (!am_running_on_windows()) {
1081
sigprocmask( POSIX::SIG_UNBLOCK(), $sigset )
1082
or die "spamd: cannot unblock SIGINT/SIGCHLD for fork: $!\n";
1085
$spamtest->call_plugins("spamd_child_init");
1087
# support non-root use
1088
if ( $opt{'username'} ) {
1089
my ( $uuid, $ugid ) = ( getpwnam( $opt{'username'} ) )[ 2, 3 ];
1090
if ( !defined $uuid || $uuid == 0 ) {
1091
die "spamd: cannot run as nonexistent user or root with -u option\n";
1094
if ( $opt{'groupname'} ) {
1095
$ugid = getgrnam( $opt{'groupname'} ) || $ugid;
1098
# bug 5518: assignments to $) and $( don't always work on all platforms
1099
# bug 3900: assignments to $> and $< problems with BSD perl bug
1100
# use the POSIX functions to hide the platform specific workarounds
1101
POSIX::setgid($ugid); # set effective and real gid
1102
POSIX::setuid($uuid); # set effective and real UID
1103
$< = $uuid; $> = $uuid; # bug 5574
1105
# keep the sanity check to catch problems like bug 3900 just in case
1106
if ( $> != $uuid and $> != ( $uuid - 2**32 ) ) {
1107
die "spamd: setuid to uid $uuid failed (> = $>, < = $<)\n";
1111
# set process name where supported
1112
# this will help make it clear via process listing which is child/parent
1115
$backchannel->setup_backchannel_child_post_fork();
1116
if ($scaling) { # only do this once, for efficiency; $$ is a syscall
1117
$scaling->set_my_pid($$);
1120
# handle $clients_per_child connections, then die in "old" age...
1122
for ( my $i = 0 ; $i < $clients_per_child ; $i++ ) {
1124
$scaling->update_child_status_idle();
1125
$orders = $scaling->wait_for_orders(); # and sleep...
1127
if ($orders != PFORDER_ACCEPT) {
1128
info("spamd: unknown order: $orders");
1132
# use a large eval scope to catch die()s and ensure they
1133
# don't kill the server.
1134
my $evalret = eval { accept_a_conn(); };
1136
if (!defined ($evalret)) {
1137
warn("spamd: error: $@ $!, continuing");
1138
if ($client) { $client->close(); } # avoid fd leaks
1140
elsif ($evalret == -1) {
1141
# serious error; used for accept() failure
1142
die("spamd: respawning server");
1145
$spamtest->call_plugins("spamd_child_post_connection_close");
1147
# if we changed UID during processing, change back!
1148
if ($setuid_to_user && ($> != $<) && ($> != ($< - 2**32))) {
1149
$) = "$( $("; # change eGID
1150
$> = $<; # change eUID
1152
# check again; ensure the change happened
1153
if ($> != $< && ($> != ( $< - 2**32))) {
1154
# make it fatal to avoid security breaches
1155
die("spamd: return setuid failed");
1159
if ($copy_config_p) {
1160
# use a timeout! There are bugs in Storable on certain platforms
1161
# that can cause spamd to hang -- see bug 3828 comment 154.
1162
# we don't use Storable any more, but leave this in -- just
1164
# bug 4699: this is the alarm that often ends up with an empty $@
1166
my $timer = Mail::SpamAssassin::Timeout->new({ secs => 20 });
1167
my $err = $timer->run(sub {
1169
while(my($k,$v) = each %msa_backup) {
1170
$spamtest->{$k} = $v;
1173
# if we changed user, we would have also loaded up new configs
1174
# (potentially), so let's restore back the saved version we
1176
$spamtest->copy_config(\%conf_backup, undef) ||
1177
die "spamd: error returned from copy_config\n";
1180
if ($timer->timed_out()) {
1181
warn("spamd: copy_config timeout, respawning child process after ".
1182
($i+1)." messages");
1183
exit; # so that the master spamd can respawn
1186
undef $current_user;
1188
dbg("timing: " . $spamtest->timer_report()) if would_log('dbg', 'timing');
1191
# If the child lives to get here, it will die ... Muhaha.
1196
sub accept_from_any_server_socket {
1198
my $fdvec = $server_select_mask;
1200
if ($have_multiple_server_socks) {
1201
# determine which of our server FDs is ready using select().
1202
# We only need to do this if we have more than one server
1203
# socket supported, since otherwise there can only be one socket
1204
# with a client waiting.
1205
# (TODO: we could extend the prefork protocol to pass this data)
1206
my $nfound = select($fdvec, undef, undef, 0.1);
1207
die "oops? accept_a_conn: no fds ready" unless $nfound;
1210
if ($fd_inet && vec $fdvec, $fd_inet, 1) {
1211
$client = $server_inet->accept;
1213
elsif ($fd_unix && vec $fdvec, $fd_unix, 1) {
1214
$client = $server_unix->accept;
1216
elsif ($fd_ssl && vec $fdvec, $fd_ssl, 1) {
1217
$client = $server_ssl->accept;
1220
die "accept_a_conn: no fds ready by vec: $fdvec";
1226
$client = accept_from_any_server_socket();
1229
$scaling->update_child_status_busy();
1233
if ( !$client || !defined $client->connected() ) {
1235
# this can happen when interrupted by SIGCHLD on Solaris,
1236
# perl 5.8.0, and some other platforms with -m.
1237
if ( $! == &Errno::EINTR ) {
1240
elsif ( $! == 0 && $listen_ssl ) {
1241
warn("spamd: SSL failure: " . &IO::Socket::SSL::errstr());
1245
warn("spamd: accept failed: $!");
1250
$client->autoflush(1);
1252
# keep track of start time
1253
$spamtest->timer_reset;
1256
my ($remote_hostname, $remote_hostaddr);
1257
if ($opt{'socketpath'}) {
1258
$remote_hostname = 'localhost';
1259
$remote_hostaddr = '127.0.0.1';
1260
$remote_port = $opt{'socketpath'};
1261
info("spamd: got connection over " . $opt{'socketpath'});
1264
($remote_port, $remote_hostaddr, $remote_hostname) =
1265
peer_info_from_socket($client)
1266
or die 'failed to obtain port and ip from socket';
1268
my $msg = "connection from ${remote_hostname} [${remote_hostaddr}] at port ${remote_port}";
1269
if (ip_is_allowed($remote_hostaddr)) {
1270
info("spamd: $msg");
1273
warn("spamd: unauthorized $msg");
1281
Mail::SpamAssassin::Util::trap_sigalrm_fully(sub {
1284
alarm $timeout_tcp if ($timeout_tcp);
1285
# send the request to the child process
1286
$_ = $client->getline;
1291
if ($@ =~ /tcp timeout/) {
1292
service_timeout("($timeout_tcp second socket timeout reading input from client)");
1300
if ( !defined $_ ) {
1301
protocol_error("(closed before headers)");
1308
# It might be a CHECK message, meaning that we should just check
1309
# if it's spam or not, then return the appropriate response.
1310
# If we get the PROCESS command, the client is going to send a
1311
# message that we need to filter.
1313
if (/(PROCESS|CHECK|SYMBOLS|REPORT|HEADERS|REPORT_IFSPAM) SPAMC\/(.*)/) {
1317
Mail::SpamAssassin::Util::trap_sigalrm_fully(sub {
1318
die "child processing timeout";
1320
alarm $timeout_child if ($timeout_child);
1321
check($method, $version, $start, $remote_hostname, $remote_hostaddr);
1326
if ($@ =~ /child processing timeout/) {
1327
service_timeout("($timeout_child second timeout while trying to $method)");
1336
elsif (/(TELL) SPAMC\/(.*)/) {
1340
Mail::SpamAssassin::Util::trap_sigalrm_fully(sub {
1341
die "child processing timeout";
1343
alarm $timeout_child if ($timeout_child);
1344
dotell($method, $version, $start, $remote_hostname, $remote_hostaddr);
1349
if ($@ =~ /child processing timeout/) {
1350
service_timeout("($timeout_child second timeout while trying to $method)");
1359
# Looks like a client is just seeing if we're alive or changed its mind
1361
elsif (/(SKIP|PING) SPAMC\/(.*)/) {
1365
if ($method eq 'SKIP') {
1366
# It may be a SKIP message, meaning that the client (spamc)
1367
# thinks it is too big to check. So we don't do any real work
1369
info("spamd: skipped large message in %3.1f seconds", time - $start);
1371
doskip_or_ping($method, $version,
1372
$start, $remote_hostname, $remote_hostaddr);
1375
# If it was none of the above, then we don't know what it was.
1381
# Close out our connection to the client ...
1386
sub handle_setuid_to_user {
1387
if ($spamtest->{paranoid}) {
1388
die("spamd: in paranoid mode, still running as root: closing connection");
1390
if (!am_running_on_windows()) {
1391
warn("spamd: still running as root: user not specified with -u, "
1392
. "not found, or set to root, falling back to nobody\n");
1394
my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $dir, $etc) =
1397
$) = "$gid $gid"; # eGID
1399
if (!defined($uid) || ($> != $uid and $> != ($uid - 2**32))) {
1400
die("spamd: setuid to nobody failed");
1403
$spamtest->signal_user_changed(
1413
my ($client, $expected_length, $compress_zlib, $start_time) = @_;
1418
if ($compress_zlib && !defined($expected_length)) {
1419
service_unavailable_error("Compress requires Content-length header");
1423
if ($compress_zlib) {
1424
$actual_length = zlib_inflate_read($client, $expected_length, \@msglines);
1425
if ($actual_length < 0) { return; }
1426
$expected_length = $actual_length;
1431
while (defined($_ = $client->getline())) {
1432
$actual_length += length($_);
1433
push(@msglines, $_);
1434
last if (defined $expected_length && $actual_length >= $expected_length);
1438
# Now parse *only* the message headers; the MIME tree won't be generated
1439
# yet, it will be done on demand later on.
1440
my $mail = $spamtest->parse(\@msglines, 0,
1441
!$timeout_child || !$start_time ? ()
1442
: { master_deadline => $start_time + $timeout_child } );
1444
return ($mail, $actual_length);
1447
sub zlib_inflate_read {
1448
my ($client, $expected_length, $msglinesref) = @_;
1453
require Compress::Zlib;
1454
my ($zlib, $status) = Compress::Zlib::inflateInit();
1455
if (!$zlib) { die "inflateInit failed: $status"; }
1460
# TODO: inflate in smaller buffers instead of at EOF
1462
my $numbytes = $client->read($buf, (1024 * 64) + $red, $red);
1463
if (!defined $numbytes) {
1464
die "read of zlib data failed: $!";
1467
last if $numbytes == 0;
1471
if ($red > $expected_length) {
1472
warn "hmm, zlib read $red > expected_length $expected_length";
1473
substr ($buf, $expected_length) = '';
1476
($out, $status) = $zlib->inflate($buf);
1477
if ($status != Compress::Zlib::Z_STREAM_END()) {
1478
die "failed to find end of zlib stream";
1483
service_unavailable_error("zlib: $@");
1487
$actual_length = length($out);
1489
# TODO: split during inflate, too
1490
# note that this preserves line endings
1491
@{$msglinesref} = map { my $s=$_; $s=~s/$/\n/gs; $s } split(/\n/, $out);
1492
return $actual_length;
1498
# Extract the Message-Id(s) for logging purposes.
1499
my $msgid = $mail->get_pristine_header("Message-Id");
1500
my $rmsgid = $mail->get_pristine_header("Resent-Message-Id");
1501
foreach my $id ((\$msgid, \$rmsgid)) {
1503
while ( $$id =~ s/\([^\(\)]*\)// )
1504
{ } # remove comments and
1505
$$id =~ s/^\s+|\s+$//g; # leading and trailing spaces
1506
$$id =~ s/\s+/ /g; # collapse whitespaces
1507
$$id =~ s/^.*?<(.*?)>.*$/$1/; # keep only the id itself
1508
$$id =~ s/[^\x21-\x7e]/?/g; # replace all weird chars
1509
$$id =~ s/[<>]/?/g; # plus all dangling angle brackets
1510
$$id =~ s/^(.+)$/<$1>/; # re-bracket the id (if not empty)
1513
return ($msgid, $rmsgid);
1517
my ( $method, $version, $start_time, $remote_hostname, $remote_hostaddr ) = @_;
1519
my $expected_length;
1522
# used to ensure we don't accidentally fork (bug 4370)
1523
my $starting_self_pid = $$;
1525
# Protocol version 1.0 and greater may have "User:" and
1526
# "Content-length:" headers. But they're not required.
1528
if ( $version > 1.0 ) {
1531
return 0 unless (parse_headers($hdrs, $client));
1533
$expected_length = $hdrs->{expected_length};
1534
$compress_zlib = $hdrs->{compress_zlib};
1537
return 0 unless do_user_handling();
1538
if ($> == 0 && !am_running_on_windows()) {
1539
die "spamd: still running as root! dying";
1544
# generate mail object from input
1545
my ($mail, $actual_length) = parse_body($client, $expected_length,
1546
$compress_zlib, $start_time);
1547
return 0 unless defined($mail); # error
1549
if ($compress_zlib) {
1550
$expected_length = $actual_length; # previously it was the gzipped length
1553
# attempt to fetch the message ids
1554
my ($msgid, $rmsgid) = parse_msgids($mail);
1556
$msgid ||= "(unknown)";
1557
$current_user ||= "(unknown)";
1558
$current_msgid = $msgid; # for the SIGUSR2 backtrace
1559
info("spamd: " . ($method eq 'PROCESS' ? "processing" : "checking")
1561
. ( $rmsgid ? " aka $rmsgid" : "" )
1562
. " for ${current_user}:$>");
1564
# Check length if we're supposed to.
1565
if (defined $expected_length && $actual_length != $expected_length) {
1567
"(Content-Length mismatch: Expected $expected_length bytes, got $actual_length bytes)"
1573
# Go ahead and check the message
1575
my $status = Mail::SpamAssassin::PerMsgStatus->new($spamtest, $mail);
1578
my $msg_score = &Mail::SpamAssassin::Util::get_tag_value_for_score($status->get_score, $status->get_required_score, $status->is_spam);
1579
my $msg_threshold = sprintf( "%2.1f", $status->get_required_score );
1581
my $response_spam_status = "";
1583
if ( $status->is_spam ) {
1584
$response_spam_status = $method eq "REPORT_IFSPAM" ? "Yes" : "True";
1585
$was_it_spam = 'identified spam';
1588
$response_spam_status = $method eq "REPORT_IFSPAM" ? "No" : "False";
1589
$was_it_spam = 'clean message';
1592
my $spamhdr = "Spam: $response_spam_status ; $msg_score / $msg_threshold";
1594
if ( $method eq 'PROCESS' || $method eq 'HEADERS' ) {
1596
$status->set_tag('REMOTEHOSTNAME', $remote_hostname);
1597
$status->set_tag('REMOTEHOSTADDR', $remote_hostaddr);
1599
# Build the message to send back and measure it
1600
my $msg_resp = $status->rewrite_mail();
1602
if ($method eq 'HEADERS') {
1603
# just the headers; delete everything after first \015\012\015\012
1604
$msg_resp =~ s/(\015?\012\015?\012).*$/$1/gs;
1607
my $msg_resp_length = length($msg_resp);
1609
if ( $version >= 1.3 ) # Spamc protocol 1.3 means multi hdrs are OK
1611
syswrite_full_buffer( $client, "SPAMD/1.1 $resphash{$resp} $resp\r\n" .
1612
"Content-length: $msg_resp_length\r\n" . $spamhdr . "\r\n\r\n" .
1616
$version >= 1.2 ) # Spamc protocol 1.2 means it accepts content-length
1618
syswrite_full_buffer( $client, "SPAMD/1.1 $resphash{$resp} $resp\r\n" .
1619
"Content-length: $msg_resp_length\r\n\r\n" . $msg_resp );
1621
else # Earlier than 1.2 didn't accept content-length
1623
syswrite_full_buffer( $client, "SPAMD/1.0 $resphash{$resp} $resp\r\n" . $msg_resp );
1626
else # $method eq 'CHECK' et al
1628
syswrite_full_buffer( $client, "SPAMD/1.1 $resphash{$resp} $resp\r\n" );
1630
if ( $method eq "CHECK" ) {
1631
syswrite( $client, "$spamhdr\r\n\r\n" );
1636
if ( $method eq "REPORT"
1637
or ( $method eq "REPORT_IFSPAM" and $status->is_spam ) )
1639
$msg_resp = $status->get_report;
1641
elsif ( $method eq "REPORT_IFSPAM" ) {
1643
# message is ham, $msg_resp remains empty
1645
elsif ( $method eq "SYMBOLS" ) {
1646
$msg_resp = $status->get_names_of_tests_hit;
1647
$msg_resp .= "\r\n" if ( $version < 1.3 );
1650
die "spamd: unknown method $method";
1653
if ( $version >= 1.3 ) # Spamc protocol > 1.2 means multi hdrs are OK
1655
my $msg_resp_length = length($msg_resp);
1656
syswrite_full_buffer( $client,
1657
"Content-length: $msg_resp_length\r\n" .
1658
$spamhdr . "\r\n\r\n" . $msg_resp );
1661
syswrite_full_buffer( $client, $spamhdr . "\r\n\r\n" . $msg_resp );
1666
my $scantime = sprintf( "%.1f", time - $start_time );
1668
info("spamd: $was_it_spam ($msg_score/$msg_threshold) for $current_user:$> in"
1669
. " $scantime seconds, $actual_length bytes." );
1671
# add a summary "result:" line, based on mass-check format
1673
push(@extra, "scantime=".$scantime, "size=$actual_length",
1674
"user=".$current_user, "uid=".$>,
1675
"required_score=".$msg_threshold,
1676
"rhost=".$remote_hostname, "raddr=".$remote_hostaddr,
1677
"rport=".$remote_port);
1680
my $safe = $msgid; $safe =~ s/[\x00-\x20\s,]/_/gs; push(@extra, "mid=$safe");
1683
my $safe = $rmsgid; $safe =~ s/[\x00-\x20\s,]/_/gs; push(@extra, "rmid=$safe");
1685
if (defined $status->{bayes_score}) {
1686
push(@extra, "bayes=".sprintf("%06f", $status->{bayes_score}));
1688
push(@extra, "autolearn=".$status->get_autolearn_status());
1689
push(@extra, $status->get_spamd_result_log_items());
1691
my $yorn = $status->is_spam() ? 'Y' : '.';
1692
my $score = $status->get_score();
1693
my $tests = join(",", sort(grep(length,$status->get_names_of_tests_hit())));
1695
my $log = sprintf("spamd: result: %s %2d - %s %s", $yorn, $score,
1696
$tests, join(",", @extra));
1699
# bug 3808: log scan results to any listening plugins, too
1700
$spamtest->call_plugins("log_scan_result", { result => $log });
1702
# bug 3466: handle the bayes expiry bits after the results were returned to
1703
# the client. keeps clients from timing out. if bayes_expiry_due is set,
1704
# then the opportunistic check has already checked. go ahead and do another
1706
if ($status->{'bayes_expiry_due'}) {
1707
dbg("spamd: bayes expiry was marked as due, running post-check");
1708
$spamtest->rebuild_learner_caches();
1709
$spamtest->finish_learner();
1712
$status->finish(); # added by jm to allow GC'ing
1715
# ensure we didn't accidentally fork (bug 4370)
1716
if ($starting_self_pid != $$) {
1717
eval { warn("spamd: accidental fork: $$ != $starting_self_pid"); };
1718
POSIX::_exit(1); # avoid END and dtor processing
1725
my ($method, $version, $start_time, $remote_hostname, $remote_hostaddr) = @_;
1730
return 0 unless (parse_headers($hdrs, $client));
1732
my $expected_length = $hdrs->{expected_length};
1733
my $compress_zlib = $hdrs->{compress_zlib};
1735
return 0 unless do_user_handling();
1736
if ($> == 0 && !am_running_on_windows()) {
1737
die "spamd: still running as root! dying";
1741
service_unavailable_error("TELL commands are not enabled, set the --allow-tell switch.");
1745
if ($hdrs->{set_local} && $hdrs->{remove_local}) {
1746
protocol_error("Unable to set local and remove local in the same operation.");
1750
if ($hdrs->{set_remote} && $hdrs->{remove_remote}) {
1751
protocol_error("Unable to set remote and remove remote in the same operation.");
1755
if ($opt{'sql-config'} && !defined($current_user)) {
1756
unless (handle_user_sql('nobody')) {
1757
service_unavailable_error("Error fetching user preferences via SQL");
1762
if ($opt{'ldap-config'} && !defined($current_user)) {
1763
handle_user_ldap('nobody');
1768
# generate mail object from input
1769
my($mail, $actual_length) =
1770
parse_body($client, $expected_length, $compress_zlib, $start_time);
1772
return 0 unless defined($mail); # error
1774
if ($compress_zlib) {
1775
$expected_length = $actual_length; # previously it was the gzipped length
1778
if ( $mail->get_header("X-Spam-Checker-Version") ) {
1779
my $new_mail = $spamtest->parse($spamtest->remove_spamassassin_markup($mail), 1);
1784
# attempt to fetch the message ids
1785
my ($msgid, $rmsgid) = parse_msgids($mail);
1787
$msgid ||= "(unknown)";
1788
$current_user ||= "(unknown)";
1790
# Check length if we're supposed to.
1791
if (defined $expected_length && $actual_length != $expected_length) {
1792
protocol_error("(Content-Length mismatch: Expected $expected_length bytes, got $actual_length bytes)");
1800
if ($hdrs->{set_local}) {
1801
my $status = $spamtest->learn($mail, undef, ($hdrs->{message_class} eq 'spam' ? 1 : 0), 0);
1803
push(@did_set, 'local') if ($status->did_learn());
1807
if ($hdrs->{remove_local}) {
1808
my $status = $spamtest->learn($mail, undef, undef, 1);
1810
push(@did_remove, 'local') if ($status->did_learn());
1814
if ($hdrs->{set_remote}) {
1815
require Mail::SpamAssassin::Reporter;
1816
my $msgrpt = Mail::SpamAssassin::Reporter->new($spamtest, $mail);
1818
push(@did_set, 'remote') if ($msgrpt->report());
1821
if ($hdrs->{remove_remote}) {
1822
require Mail::SpamAssassin::Reporter;
1823
my $msgrpt = Mail::SpamAssassin::Reporter->new($spamtest, $mail);
1825
push(@did_remove, 'remote') if ($msgrpt->revoke());
1831
if (scalar(@did_set)) {
1832
$hdr .= "DidSet: " . join(',', @did_set) . "\r\n";
1833
$info_str .= " Setting " . join(',', @did_set) . " ";
1836
if (scalar(@did_remove)) {
1837
$hdr .= "DidRemove: " . join(',', @did_remove) . "\r\n";
1838
$info_str .= " Removing " . join(',', @did_remove) . " ";
1842
$info_str = " Did nothing ";
1845
print $client "SPAMD/1.1 $resphash{$resp} $resp\r\n",
1848
my $scantime = sprintf( "%.1f", time - $start_time );
1850
info("spamd: Tell:${info_str}for $current_user:$> in"
1851
. " $scantime seconds, $actual_length bytes");
1857
sub doskip_or_ping {
1858
my ($method, $version, $start_time, $remote_hostname, $remote_hostaddr) = @_;
1860
if ( $version >= 1.5 ) {
1861
# Spamc protocol 1.5 means client is expected to send a protocol header
1862
# (usually just a null header), followed by an empty line
1866
return 0 unless (parse_headers($hdrs, $client));
1869
if ($method eq 'PING') {
1870
print $client "SPAMD/1.5 $resphash{EX_OK} PONG\r\n";
1876
###########################################################################
1878
sub do_user_handling {
1879
if ($setuid_to_user && $> == 0) {
1880
handle_setuid_to_user();
1883
if ( $opt{'sql-config'} && !defined($current_user) ) {
1884
unless ( handle_user_sql('nobody') ) {
1885
service_unavailable_error("Error fetching user preferences via SQL");
1890
if ( $opt{'ldap-config'} && !defined($current_user) ) {
1891
handle_user_ldap('nobody');
1894
dbg ("spamd: running as uid $>");
1898
# generalised header parser.
1900
my ($hdrs, $client) = @_;
1902
my $got_user_header;
1905
for my $hcount ( 0 .. 255 ) {
1906
my $line = $client->getline;
1908
unless (defined $line) {
1909
protocol_error("(EOF during headers)");
1914
if (!length $line) { # end of headers
1915
if (!$got_user_header && $opt{'auth-ident'}) {
1916
service_unavailable_error('User header required');
1922
my ($header, $value) = split (/:\s*/, $line, 2);
1923
unless (defined $value) {
1924
protocol_error("(header not in 'Name: value' format)");
1928
if ($header eq 'Content-length') {
1929
return 0 unless got_clen_header($hdrs, $header, $value);
1931
elsif ($header eq 'User') {
1932
return 0 unless got_user_header($hdrs, $header, $value);
1935
elsif ($header eq 'Message-class') {
1936
return 0 unless got_message_class_header($hdrs, $header, $value);
1938
elsif ($header eq 'Set') {
1939
return 0 unless got_set_header($hdrs, $header, $value);
1941
elsif ($header eq 'Remove') {
1942
return 0 unless got_remove_header($hdrs, $header, $value);
1944
elsif ($header eq 'Compress') {
1945
return 0 unless &got_compress_header($hdrs, $header, $value);
1949
# avoid too-many-headers DOS attack
1950
protocol_error("(too many headers)");
1954
# We'll run handle user unless we've been told not
1955
# to process per-user config files. Otherwise
1956
# we'll check and see if we need to try SQL
1957
# lookups. If $opt{'user-config'} is true, we need to try
1958
# their config file and then do the SQL lookup.
1959
# If $opt{'user-config'} IS NOT true, we skip the conf file and
1960
# only need to do the SQL lookup if $opt{'sql-config'} IS
1961
# true. (I got that wrong the first time.)
1963
sub got_user_header {
1964
my ( $client, $header, $value ) = @_;
1966
if ( $value !~ /^([\x20-\xFF]*)$/ ) {
1967
protocol_error("(User header contains control chars)");
1972
if ($opt{'auth-ident'} && !auth_ident($current_user)) {
1976
if ( !$opt{'user-config'} ) {
1977
if ( $opt{'sql-config'} ) {
1978
unless ( handle_user_sql($current_user) ) {
1979
service_unavailable_error("Error fetching user preferences via SQL");
1983
elsif ( $opt{'ldap-config'} ) {
1984
handle_user_ldap($current_user);
1986
elsif ( $opt{'virtual-config-dir'} ) {
1987
handle_virtual_config_dir($current_user);
1989
elsif ( $opt{'setuid-with-sql'} ) {
1990
unless ( handle_user_setuid_with_sql($current_user) ) {
1991
service_unavailable_error("Error fetching user preferences via SQL");
1994
$setuid_to_user = 1; #to benefit from any paranoia.
1996
elsif ( $opt{'setuid-with-ldap'} ) {
1997
handle_user_setuid_with_ldap($current_user);
1998
$setuid_to_user = 1; # as above
2001
handle_user_setuid_basic($current_user);
2005
handle_user_setuid_basic($current_user);
2006
if ( $opt{'sql-config'} ) {
2007
unless ( handle_user_sql($current_user) ) {
2008
service_unavailable_error("Error fetching user preferences via SQL");
2016
sub got_clen_header {
2017
my ( $hdrs, $header, $value ) = @_;
2018
if ( $value !~ /^(\d*)$/ ) {
2019
protocol_error("(Content-Length contains non-numeric bytes)");
2022
$hdrs->{expected_length} = $1;
2026
sub got_message_class_header {
2027
my ($hdrs, $header, $value) = @_;
2029
unless (lc($value) ne 'spam' || lc($value) ne 'ham') {
2030
protocol_error("(Message-class header contains invalid class)");
2033
$hdrs->{message_class} = $value;
2038
sub got_set_header {
2039
my ($hdrs, $header, $value) = @_;
2041
$hdrs->{set_local} = 0;
2042
$hdrs->{set_remote} = 0;
2044
if ($value =~ /local/i) {
2045
$hdrs->{set_local} = 1;
2048
if ($value =~ /remote/i) {
2049
$hdrs->{set_remote} = 1;
2055
sub got_remove_header {
2056
my ($hdrs, $header, $value) = @_;
2058
$hdrs->{remove_local} = 0;
2059
$hdrs->{remove_remote} = 0;
2061
if ($value =~ /local/i) {
2062
$hdrs->{remove_local} = 1;
2065
if ($value =~ /remote/i) {
2066
$hdrs->{remove_remote} = 1;
2072
sub got_compress_header {
2073
my ($hdrs, $header, $value) = @_;
2075
if ($value =~ /zlib/i) {
2076
eval { require Compress::Zlib; };
2078
protocol_error("(compression not supported, Compress::Zlib not installed: $@)");
2081
$hdrs->{compress_zlib} = 1;
2084
protocol_error("(compression type not supported)");
2091
sub protocol_error {
2093
my $resp = "EX_PROTOCOL";
2094
syswrite($client, "SPAMD/1.0 $resphash{$resp} Bad header line: $err\r\n");
2095
warn("spamd: bad protocol: header error: $err\n");
2098
sub service_unavailable_error {
2100
my $resp = "EX_UNAVAILABLE";
2102
"SPAMD/1.0 $resphash{$resp} Service Unavailable: $err\r\n");
2103
warn("spamd: service unavailable: $err\n");
2106
sub service_timeout {
2108
my $resp = "EX_TIMEOUT";
2109
print $client "SPAMD/1.0 $resphash{$resp} Timeout: $err\r\n";
2110
warn("spamd: timeout: $err\n");
2113
###########################################################################
2116
my $username = shift;
2117
my $ident_username = ident_lookup( $client, $opt{'ident-timeout'} );
2118
my $dn = $ident_username || 'NONE'; # display name
2119
dbg("ident: ident_username = $dn, spamc_username = $username\n");
2120
if ( !defined($ident_username) || $username ne $ident_username ) {
2121
info("spamd: ident username ($dn) does not match "
2122
. "spamc username ($username)" );
2128
sub handle_user_setuid_basic {
2129
my $username = shift;
2131
# If $opt{'username'} in use, then look up userinfo for that uid;
2132
# otherwise use what was passed via $username
2134
my $suidto = $username;
2135
if ( $opt{'username'} ) {
2136
$suidto = $opt{'username'};
2138
my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $suiddir, $etc) =
2139
am_running_on_windows() ? ('nobody') : getpwnam($suidto);
2141
if (!defined $uid) {
2142
my $errmsg = "spamd: handle_user unable to find user: '$suidto'\n";
2143
die $errmsg if $spamtest->{'paranoid'};
2144
# if we are given a username, but can't look it up, maybe name
2145
# services are down? let's break out here to allow them to get
2146
# 'defaults' when we are not running paranoid
2151
if ($setuid_to_user) {
2152
$) = "$gid $gid"; # change eGID
2153
$> = $uid; # change eUID
2154
if ( !defined($uid) || ( $> != $uid and $> != ( $uid - 2**32 ) ) ) {
2155
# make it fatal to avoid security breaches
2156
die("spamd: fatal error: setuid to $suidto failed");
2159
info("spamd: setuid to $suidto succeeded");
2165
# if $opt{'user-config'} is in use, read user prefs from the remote
2166
# username's home dir (if it exists): bug 5611
2167
if ( $opt{'user-config'} ) {
2168
my $prefsfrom = $username; # the one passed, NOT $opt{username}
2170
if ($prefsfrom eq $suidto) {
2171
$userdir = $suiddir; # reuse the already-looked-up info, tainted
2172
} elsif ( $opt{'vpopmail'} ) {
2174
# If vpopmail config enabled then set $userdir to virtual homedir
2176
my $username_untainted;
2177
$username_untainted =
2178
untaint_var($username) if $username =~ /^[-:,.=+A-Za-z0-9_\@~]+\z/;
2179
my $vpopdir = $suiddir; # This should work with common vpopmail setups
2180
$userdir = `$vpopdir/bin/vuserinfo -d \Q$username_untainted\E`;
2184
$userdir = handle_user_vpopmail($username_untainted,$vpopdir);
2187
$userdir = (getpwnam($prefsfrom))[7];
2190
# we *still* die if this can't be found
2191
if (!defined $userdir) {
2192
my $errmsg = "spamd: handle_user unable to find user: '$prefsfrom'\n";
2193
die $errmsg if $spamtest->{'paranoid'};
2194
# if we are given a username, but can't look it up, maybe name
2195
# services are down? let's break out here to allow them to get
2196
# 'defaults' when we are not running paranoid
2202
# call this anyway, regardless of --user-config, so that
2203
# signal_user_changed() is called
2204
handle_user_set_user_prefs(untaint_var($userdir), $username);
2207
sub handle_user_vpopmail {
2209
# If vuserinfo failed $username could be an alias
2210
# As the alias could be an alias itself we'll try to resolve it recursively
2211
# Because we're mistrusting vpopmail we'll set off an alarm
2213
my $username = shift;
2214
my $vpopdir = shift;
2216
my $vpoptimeout = 5;
2217
my $vptimer = Mail::SpamAssassin::Timeout->new({ secs => $vpoptimeout });
2220
my $vpopusername = $username;
2221
my @aliases = split(/\n/, `$vpopdir/bin/valias \Q$vpopusername\E`);
2223
my $vpopusername_tainted = shift(@aliases);
2225
if ($vpopusername_tainted =~ /-> &?(.+)$/) {
2226
$vpopusername = untaint_var($1);
2227
if ($vpopusername =~ s{^(/.+)/Maildir/$}{$1}) {
2228
# this is the path to a real mailbox
2229
$userdir = $vpopusername;
2230
} elsif ($vpopusername !~ /^[#| \t]/ &&
2231
$vpopusername =~ /^[^@ \t]+\@[^@ \t]+\s*$/) {
2232
# this is a forward to another e-mail address
2233
$vpopusername =~ s{^.+ -> (.+)}{$1};
2234
$vpopusername_tainted = `$vpopdir/bin/vuserinfo -d \Q$vpopusername\E`;
2235
if ($? == 0 && $vpopusername_tainted ne '') {
2236
$userdir = untaint_var($vpopusername_tainted);
2239
split(/\n/, `$vpopdir/bin/valias \Q$vpopusername\E`));
2242
last if defined $userdir;
2247
if ($vptimer->timed_out()) {
2248
dbg("spamd: timed out resolving vpopmail user/alias '%s'", $username);
2250
} elsif (!defined($userdir)) {
2251
dbg("spamd: failed to resolve vpopmail user/alias '%s'", $username);
2258
sub handle_user_set_user_prefs {
2259
my ($dir, $username) = @_;
2261
# don't do this if we weren't passed a directory
2263
my $cf_file = $dir . "/.spamassassin/user_prefs";
2264
create_default_cf_if_needed( $cf_file, $username, $dir );
2265
$spamtest->read_scoreonly_config($cf_file);
2268
# signal_user_changed will ignore undef user_dirs, so this is ok
2269
$spamtest->signal_user_changed(
2271
username => $username,
2279
# Handle user configs without the necessity of having individual users or a
2280
# SQL/LDAP database.
2281
sub handle_virtual_config_dir {
2282
my ($username) = @_;
2284
my $dir = $opt{'virtual-config-dir'};
2288
if ( defined $dir ) {
2289
my $safename = $username;
2290
$safename =~ s/[^-A-Za-z0-9\+_\.\,\@\=]/_/gs;
2293
if ( $safename =~ /^(.*)\@(.*)$/ ) { $localpart = $1; $domain = $2; }
2295
$dir =~ s/\%u/${safename}/g;
2296
$dir =~ s/\%l/${localpart}/g;
2297
$dir =~ s/\%d/${domain}/g;
2298
$dir =~ s/\%\%/\%/g;
2301
$prefsfile = $dir . '/user_prefs';
2303
# Log that the default configuration is being used for a user.
2304
info("spamd: using default config for $username: $prefsfile");
2307
if ( -f $prefsfile ) {
2309
# Found a config, load it.
2310
$spamtest->read_scoreonly_config($prefsfile);
2313
# assume that $userdir will be a writable directory we can
2314
# use for Bayes dbs etc.
2315
$spamtest->signal_user_changed(
2317
username => $username,
2318
userstate_dir => $userdir,
2319
user_dir => $userdir
2325
sub handle_user_sql {
2326
my ($username) = @_;
2328
unless ( $spamtest->load_scoreonly_sql($username) ) {
2331
$spamtest->signal_user_changed(
2333
username => $username,
2340
sub handle_user_ldap {
2341
my $username = shift;
2342
dbg("ldap: entering handle_user_ldap($username)");
2343
$spamtest->load_scoreonly_ldap($username);
2344
$spamtest->signal_user_changed(
2346
username => $username,
2353
sub handle_user_setuid_with_sql {
2354
my $username = shift;
2356
# Bug 6313: interestingly, if $username is not tainted than $pwd, $gcos and
2357
# $etc end up tainted but other fields not; if $username _is_ tainted,
2358
# getpwnam does not complain, but all returned fields are tainted (which
2359
# makes sense, but is worth remembering)
2361
my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $dir, $etc) =
2362
getpwnam(untaint_var($username));
2364
if (!$spamtest->{'paranoid'} && !defined($uid)) {
2365
# if we are given a username, but can't look it up, maybe name
2366
# services are down? let's break out here to allow them to get
2367
# 'defaults' when we are not running paranoid
2368
info("spamd: handle_user unable to find user: $username\n");
2372
if ($setuid_to_user) {
2373
$) = "$gid $gid"; # change eGID
2374
$> = $uid; # change eUID
2375
if (!defined($uid) || ($> != $uid and $> != ($uid - 2**32))) {
2376
# make it fatal to avoid security breaches
2377
die("spamd: fatal error: setuid to $username failed");
2380
info("spamd: setuid to $username succeeded, reading scores from SQL");
2384
my $spam_conf_dir = $dir . '/.spamassassin'; # needed for Bayes, etc.
2385
if ( ($opt{'user-config'} || defined $opt{'home_dir_for_helpers'})
2386
&& ! -d $spam_conf_dir ) {
2387
if (mkdir $spam_conf_dir, 0700) {
2388
info("spamd: created $spam_conf_dir for $username");
2391
info("spamd: failed to create $spam_conf_dir for $username");
2395
unless ($spamtest->load_scoreonly_sql($username)) {
2399
$spamtest->signal_user_changed( { username => $username } );
2403
sub handle_user_setuid_with_ldap {
2404
my $username = shift;
2405
my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $dir, $etc) =
2406
getpwnam($username);
2408
if (!$spamtest->{'paranoid'} && !defined($uid)) {
2409
# if we are given a username, but can't look it up, maybe name
2410
# services are down? let's break out here to allow them to get
2411
# 'defaults' when we are not running paranoid
2412
info("spamd: handle_user unable to find user: $username\n");
2416
if ($setuid_to_user) {
2417
$) = "$gid $gid"; # change eGID
2418
$> = $uid; # change eUID
2419
if (!defined($uid) || ($> != $uid and $> != ($uid - 2**32))) {
2420
# make it fatal to avoid security breaches
2421
die("spamd: fatal error: setuid to $username failed");
2424
info("spamd: setuid to $username succeeded, reading scores from LDAP");
2428
my $spam_conf_dir = $dir . '/.spamassassin'; # needed for Bayes, etc.
2429
if (! -d $spam_conf_dir) {
2430
if (mkdir $spam_conf_dir, 0700) {
2431
info("spamd: created $spam_conf_dir for $username");
2434
info("spamd: failed to create $spam_conf_dir for $username");
2438
$spamtest->load_scoreonly_ldap($username);
2440
$spamtest->signal_user_changed( { username => $username } );
2444
sub create_default_cf_if_needed {
2445
my ( $cf_file, $username, $userdir ) = @_;
2447
# Parse user scores, creating default .cf if needed:
2448
if ( !-r $cf_file && !$spamtest->{'dont_copy_prefs'} ) {
2449
info("spamd: creating default_prefs: $cf_file");
2451
# If vpopmail config enabled then pass virtual homedir onto
2452
# create_default_prefs via $userdir
2453
$spamtest->create_default_prefs( $cf_file, $username, $userdir );
2455
if (! -r $cf_file) {
2456
info("spamd: failed to create readable default_prefs: $cf_file");
2461
# sig handlers: parent process
2462
sub setup_parent_sig_handlers {
2463
$SIG{HUP} = \&restart_handler;
2464
$SIG{CHLD} = \&child_handler;
2465
$SIG{INT} = \&kill_handler;
2466
$SIG{TERM} = \&kill_handler;
2467
$SIG{PIPE} = 'IGNORE';
2470
# sig handlers: child processes
2471
sub setup_child_sig_handlers {
2472
# note: all the signals changed in setup_parent_sig_handlers() must
2473
# be reset to appropriate values here!
2475
if (am_running_on_windows()) {
2476
# on win32 the parent never receives SIGCHLD
2477
$h = sub { my($sig) = @_;
2478
info("spamd: child got SIG$sig, exiting");
2483
$SIG{$_} = $h foreach qw(HUP INT TERM CHLD);
2484
$SIG{PIPE} = 'IGNORE';
2489
info("spamd: server killed by SIG$sig, shutting down");
2490
$server_inet and $server_inet->close;
2491
$server_unix and $server_unix->close;
2492
$server_ssl and $server_ssl->close;
2494
if (defined($opt{'pidfile'})) {
2495
unlink($opt{'pidfile'}) || warn "spamd: cannot unlink $opt{'pidfile'}: $!\n";
2498
# the UNIX domain socket
2499
if (defined($opt{'socketpath'})) {
2500
unlink($opt{'socketpath'}) || warn "spamd: cannot unlink $opt{'socketpath'}: $!\n";
2503
$SIG{CHLD} = 'DEFAULT'; # we're going to kill our children
2505
$scaling->set_exiting_flag(); # don't start new ones
2507
my $killsig = am_running_on_windows() ? 'KILL' : 'INT';
2508
foreach my $pid (keys %children) {
2509
kill($killsig, $pid)
2510
or info("spamd: cannot send SIG$killsig to child process [$pid]: $!");
2515
# takes care of dead children
2519
# do NOT call syslog here unless the child's pid is in our list of known
2520
# children. This is due to syslog-ng brokenness -- bugs 3625, 4237.
2522
# clean up any children which have exited
2524
# waitpid returns a pid of the deceased process, or -1 if there is no
2525
# such child process. On some systems, a value of 0 indicates that there
2526
# are processes still running. Note that Windows uses negative pids for
2527
# child processes - bug 6376, bug 6356.
2529
my $pid = waitpid(-1, WNOHANG);
2530
last if !$pid || $pid == -1;
2531
my $child_stat = $?;
2533
if (!defined $children{$pid}) {
2534
# ignore this child; we didn't realise we'd forked it. bug 4237
2538
# remove them from our child listing
2539
delete $children{$pid};
2542
$scaling->child_exited($pid);
2544
my $sock = $backchannel->get_socket_for_child($pid);
2545
if ($sock) { $sock->close(); }
2548
unless ($Mail::SpamAssassin::Logger::LOG_SA{INHIBIT_LOGGING_IN_SIGCHLD_HANDLER}) {
2549
info("spamd: handled cleanup of child pid [%s]%s: %s",
2550
$pid, (defined $sig ? " due to SIG$sig" : ""),
2551
exit_status_str($child_stat,0));
2555
$SIG{CHLD} = \&child_handler; # reset as necessary, should be at end
2558
sub restart_handler {
2560
info("spamd: server hit by SIG$sig, restarting");
2562
$SIG{CHLD} = 'DEFAULT'; # we're going to kill our children
2564
$scaling->set_exiting_flag(); # don't start new ones
2567
foreach (keys %children) {
2569
my $pid = waitpid($_, 0);
2570
my $child_stat = $pid > 0 ? $? : undef;
2572
$scaling->child_exited($pid);
2574
info("spamd: child [%s] killed successfully: %s",
2575
$pid, exit_status_str($child_stat,0));
2579
unless ( !$server_inet || $server_inet->eof ) {
2580
$server_inet->shutdown(2);
2581
$server_inet->close;
2582
info("spamd: server INET socket closed");
2585
unless ( !$server_unix || $server_unix->eof ) {
2586
$server_unix->shutdown(2);
2587
$server_unix->close;
2588
if (defined($opt{'socketpath'})) {
2589
unlink($opt{'socketpath'}) || warn "spamd: cannot unlink $opt{'socketpath'}: $!\n";
2591
info("spamd: server UNIX socket closed");
2594
unless ( !$server_ssl || $server_ssl->eof ) {
2595
$server_ssl->shutdown(2);
2597
info("spamd: server SSL socket closed");
2602
sub backtrace_handler {
2603
Carp::cluck("spamd: caught SIGUSR2 - dumping backtrace. ".
2604
"most recent message: $current_msgid\n");
2607
my $serverstarted = 0;
2614
# Pretty command line in ps
2615
$0 = join (' ', $ORIG_ARG0, @ORIG_ARGV) unless would_log("dbg");
2617
# be a nice daemon and chdir to the root so we don't block any
2619
chdir '/' or die "spamd: cannot chdir to /: $!\n";
2621
# Redirect in and out to the bit bucket
2622
open STDIN, "</dev/null" or die "spamd: cannot read from /dev/null: $!\n";
2623
open STDOUT, ">/dev/null" or die "spamd: cannot write to /dev/null: $!\n";
2625
# Remove the stderr logger
2626
Mail::SpamAssassin::Logger::remove('stderr');
2629
$SIG{USR1} = \&serverstarted;
2630
defined( my $pid = fork ) or die "spamd: cannot fork: $!\n";
2633
# Bug 6191, Bug 6258: takes almost two minutes on a slow machine
2634
# for a forked child process to report back, bump limit to 180 seconds
2635
for (my $retry=180, my $waited=0;
2636
$retry > 0 && !$serverstarted && $waited != $pid;
2639
warn("waitpid failed: $waited $!") if $waited;
2641
$waited = waitpid($pid, WNOHANG);
2642
$child_stat = $? if $waited > 0;
2644
die sprintf("child process [%s] exited or timed out ".
2645
"without signaling production of a PID file: %s",
2646
$pid, exit_status_str($child_stat,0)) unless $serverstarted;
2650
setsid or die "spamd: cannot start new session: $!\n";
2652
# Now we can redirect the errors, too.
2653
open STDERR, '>&STDOUT' or die "spamd: cannot duplicate stdout: $!\n";
2655
dbg("spamd: successfully daemonized");
2658
sub set_allowed_ip {
2660
$allowed_nets->add_cidr($_) or die "spamd: aborting due to add_cidr error\n";
2665
$allowed_nets->contains_ip(@_);
2668
sub preload_modules_with_tmp_homedir {
2670
# set $ENV{HOME} in /tmp while we compile and preload everything.
2671
# File::Spec->tmpdir uses TMPDIR, TMP, TEMP, C:/temp, /tmp etc.
2672
my $tmpdir = File::Spec->tmpdir();
2674
die "spamd: cannot find writable tmp dir, set TMP or TMPDIR in environment";
2677
# If TMPDIR isn't set, File::Spec->tmpdir() will set it to undefined.
2678
# that then breaks other things ...
2679
delete $ENV{'TMPDIR'} if ( !defined $ENV{'TMPDIR'} );
2681
my $tmphome = File::Spec->catdir( $tmpdir, "spamd-$$-init" );
2682
$tmphome = Mail::SpamAssassin::Util::untaint_file_path($tmphome);
2684
my $tmpsadir = File::Spec->catdir( $tmphome, ".spamassassin" );
2686
dbg("spamd: Preloading modules with HOME=$tmphome");
2688
# bug 5379: spamd won't start if the temp preloading dir exists;
2689
# be sure to remove it just in case
2691
rmdir( $tmpsadir ) or die "spamd: $tmpsadir not empty: $!";
2694
rmdir( $tmphome ) or die "spamd: $tmphome not empty: $!";
2696
mkdir( $tmphome, 0700 ) or die "spamd: cannot create $tmphome: $!";
2697
mkdir( $tmpsadir, 0700 ) or die "spamd: cannot create $tmpsadir: $!";
2698
$ENV{HOME} = $tmphome;
2700
$spamtest->compile_now(0,1); # ensure all modules etc. are loaded
2701
$/ = "\n"; # argh, Razor resets this! Bad Razor!
2703
# now clean up the stuff we just created, and make us taint-safe
2706
# bug 2015, bug 2223: rmpath() is not taint safe, so we've got to implement
2707
# our own poor man's rmpath. If it fails, we report only the first error.
2709
foreach my $d ( ( $tmpsadir, $tmphome ) ) {
2710
opendir( TMPDIR, $d ) or $err ||= "open $d: $!";
2712
foreach my $f ( File::Spec->no_upwards( readdir(TMPDIR) ) ) {
2714
Mail::SpamAssassin::Util::untaint_file_path(
2715
File::Spec->catfile( $d, $f ) );
2716
unlink($f) or $err ||= "remove $f: $!";
2718
closedir(TMPDIR) or $err ||= "close $d: $!";
2720
rmdir($d) or $err ||= "remove $d: $!";
2723
# If the dir still exists, log a warning.
2724
if ( -d $tmphome ) {
2725
$err ||= "do something: $!";
2726
warn "spamd: failed to remove $tmphome: could not $err\n";
2730
# Keep calling syswrite until the entire buffer is written out
2731
# Retry if EAGAIN/EWOULDBLOCK or when partial buffer is written
2732
# Limit the number of retries to keep the execution time bounded
2733
sub syswrite_full_buffer {
2734
my ($sock, $buf, $numretries) = @_;
2735
$numretries ||= 10; # default 10 retries
2736
my $length = length($buf);
2740
while (($try < $numretries) && ($length > $written)) {
2741
my $nbytes = syswrite($sock, $buf, $length - $written, $written);
2742
if (!defined $nbytes) {
2743
unless ((exists &Errno::EAGAIN && $! == &Errno::EAGAIN)
2744
|| (exists &Errno::EWOULDBLOCK && $! == &Errno::EWOULDBLOCK))
2746
# an error that wasn't non-blocking I/O-related. that's serious
2749
# errcode says to try again
2754
return $written; # return early if no error but nothing was written
2757
$written += $nbytes;
2762
return $written; # it's complete, we can return
2765
sub map_server_sockets {
2766
$fd_inet = $server_inet ? $server_inet->fileno : undef;
2767
$fd_unix = $server_unix ? $server_unix->fileno : undef;
2768
$fd_ssl = $server_ssl ? $server_ssl->fileno : undef;
2770
$server_select_mask = '';
2771
$server_inet and vec($server_select_mask, $fd_inet, 1) = 1;
2772
$server_unix and vec($server_select_mask, $fd_unix, 1) = 1;
2773
$server_ssl and vec($server_select_mask, $fd_ssl, 1) = 1;
2775
my $back_selector = $server_select_mask;
2776
$backchannel->set_selector(\$back_selector);
2778
# and set a boolean indicating whether or not we have > 1 server socket
2779
$have_multiple_server_socks =
2780
((defined $fd_inet ? 1 : 0) +
2781
(defined $fd_unix ? 1 : 0) +
2782
(defined $fd_ssl ? 1 : 0)) > 1;
2785
# do this in advance, since we want to minimize work when SIGHUP
2787
my $perl_from_hashbang_line;
2788
sub prepare_for_sighup_restart {
2789
# it'd be great if we could introspect the interpreter to figure this
2790
# out, but bizarrely it seems unavailable.
2791
if (open (IN, "<$ORIG_ARG0")) {
2794
if ($l && $l =~ /^#!\s*(\S+)\s*.*?$/) {
2795
$perl_from_hashbang_line = $1;
2800
sub do_sighup_restart {
2801
if (defined($opt{'pidfile'})) {
2802
unlink($opt{'pidfile'}) || warn "spamd: cannot unlink $opt{'pidfile'}: $!\n";
2805
# leave Client fds active, and do not kill children; they can still
2806
# service clients until they exit. But restart the listener anyway.
2807
# And close the logfile, so the new instance can reopen it.
2808
Mail::SpamAssassin::Logger::close_log();
2810
or die "spamd: restart failed: chdir failed: ${ORIG_CWD}: $!\n";
2812
# ensure we re-run spamd using the right perl interpreter, and
2813
# with the right switches (taint mode and warnings) (bug 5255)
2814
my $perl = untaint_var($^X);
2815
my @execs = ( $perl, "-T", "-w", $ORIG_ARG0, @ORIG_ARGV );
2817
if ($perl eq $perl_from_hashbang_line) {
2818
# we're using the same perl as the script uses on the #! line;
2819
# we can safely just exec the script
2820
@execs = ( $ORIG_ARG0, @ORIG_ARGV );
2823
warn "spamd: restarting using '" . join (' ', @execs) . "'\n";
2826
# should not get past that...
2827
die "spamd: restart failed: exec failed: " . join (' ', @execs) . ": $!\n";
2834
spamd - daemonized version of spamassassin
2842
-l, --allow-tell Allow learning/reporting
2843
-c, --create-prefs Create user preferences files
2844
-C path, --configpath=path Path for default config files
2845
--siteconfigpath=path Path for site configs
2846
--cf='config line' Additional line of configuration
2847
-d, --daemonize Daemonize
2848
-h, --help Print usage message
2849
-i [ipaddr], --listen-ip=ipaddr Listen on the IP ipaddr
2850
--ipv4only, --ipv4-only, --ipv4 Disable attempted use of ipv6 for DNS
2851
-p port, --port=port Listen on specified port
2852
-m num, --max-children=num Allow maximum num children
2853
--min-children=num Allow minimum num children
2854
--min-spare=num Lower limit for number of spare children
2855
--max-spare=num Upper limit for number of spare children
2856
--max-conn-per-child=num Maximum connections accepted by child
2857
before it is respawned
2858
--round-robin Use traditional prefork algorithm
2859
--timeout-tcp=secs Connection timeout for client headers
2860
--timeout-child=secs Connection timeout for message checks
2861
-q, --sql-config Enable SQL config (needs -x)
2862
-Q, --setuid-with-sql Enable SQL config (needs -x,
2864
--ldap-config Enable LDAP config (needs -x)
2865
--setuid-with-ldap Enable LDAP config (needs -x,
2867
--virtual-config-dir=dir Enable pattern based Virtual configs
2869
-r pidfile, --pidfile Write the process id to pidfile
2870
-s facility, --syslog=facility Specify the syslog facility
2871
--syslog-socket=type How to connect to syslogd
2872
--log-timestamp-fmt=fmt strftime(3) format for timestamps, may be
2873
empty to disable timestamps, or 'default'
2874
-u username, --username=username Run as username
2875
-g groupname, --groupname=groupname Run as groupname
2876
-v, --vpopmail Enable vpopmail config
2877
-x, --nouser-config Disable user config files
2878
--auth-ident Use ident to authenticate spamc user
2879
--ident-timeout=timeout Timeout for ident connections
2880
-A host,..., --allowed-ips=..,.. Limit ip addresses which can connect
2881
-D, --debug[=areas] Print debugging messages (for areas)
2882
-L, --local Use local tests only (no DNS)
2883
-P, --paranoid Die upon user errors
2884
-H [dir], --helper-home-dir[=dir] Specify a different HOME directory
2885
--ssl Run an SSL server
2886
--ssl-port port Listen on port for SSL connections
2887
--ssl-version sslversion Specify SSL protocol version to use
2888
--server-key keyfile Specify an SSL keyfile
2889
--server-cert certfile Specify an SSL certificate
2890
--socketpath=path Listen on given UNIX domain socket
2891
--socketowner=name Set UNIX domain socket file's owner
2892
--socketgroup=name Set UNIX domain socket file's group
2893
--socketmode=mode Set UNIX domain socket file's mode
2894
-V, --version Print version and exit
2898
The purpose of this program is to provide a daemonized version of the
2899
spamassassin executable. The goal is improving throughput performance for
2900
automated mail checking.
2902
This is intended to be used alongside C<spamc>, a fast, low-overhead C client
2905
See the README file in the C<spamd> directory of the SpamAssassin distribution
2908
Note: Although C<spamd> will check per-user config files for every message, any
2909
changes to the system-wide config files will require either restarting spamd
2910
or forcing it to reload itself via B<SIGHUP> for the changes to take effect.
2912
Note: If C<spamd> receives a B<SIGHUP>, it internally reloads itself, which
2913
means that it will change its pid and might not restart at all if its
2914
environment changed (ie. if it can't change back into its own directory). If
2915
you plan to use B<SIGHUP>, you should always start C<spamd> with the B<-r>
2916
switch to know its current pid.
2920
Options of the long form can be shortened as long as they remain
2921
unambiguous. (i.e. B<--dae> can be used instead of B<--daemonize>)
2922
Also, boolean options (like B<--user-config>) can be negated by
2923
adding I<no> (B<--nouser-config>), however, this is usually unnecessary.
2927
=item B<-l>, B<--allow-tell>
2929
Allow learning and forgetting (to a local Bayes database), reporting
2930
and revoking (to a remote database) by spamd. The client issues a TELL
2931
command to tell what type of message is being processed and whether
2932
local (learn/forget) or remote (report/revoke) databases should be
2935
Note that spamd always trusts the username passed in (unless
2936
B<--auth-ident> is used) so clients could maliciously learn messages
2937
for other users. (This is not ususally a concern with an SQL Bayes
2938
store as users will typically have read-write access directly to the
2939
database, and can also use C<sa-learn> with the B<-u> option to
2940
achieve the same result.)
2942
=item B<-c>, B<--create-prefs>
2944
Create user preferences files if they don't exist (default: don't).
2946
=item B<-C> I<path>, B<--configpath>=I<path>
2948
Use the specified path for locating the distributed configuration files.
2949
Ignore the default directories (usually C</usr/share/spamassassin> or similar).
2951
=item B<--siteconfigpath>=I<path>
2953
Use the specified path for locating site-specific configuration files. Ignore
2954
the default directories (usually C</etc/spamassassin> or similar).
2956
=item B<--cf='config line'>
2958
Add additional lines of configuration directly from the command-line, parsed
2959
after the configuration files are read. Multiple B<--cf> arguments can be
2960
used, and each will be considered a separate line of configuration.
2962
=item B<-d>, B<--daemonize>
2964
Detach from starting process and run in background (daemonize).
2966
=item B<-h>, B<--help>
2968
Print a brief help message, then exit without further action.
2970
=item B<-V>, B<--version>
2972
Print version information, then exit without further action.
2974
=item B<-i> [I<ipaddress>], B<--listen-ip>[=I<ipaddress>], B<--ip-address>[=I<ipaddress>]
2976
Tells spamd to listen on the specified IP address (defaults to 127.0.0.1). If
2977
you specify no IP address after the switch, spamd will listen on all interfaces.
2978
(This is equal to the address 0.0.0.0). You can also use a valid hostname which
2979
will make spamd listen on the first address that name resolves to.
2981
=item B<-p> I<port>, B<--port>=I<port>
2983
Optionally specifies the port number for the server to listen on (default: 783).
2985
If the B<--ssl> switch is used, and B<--ssl-port> is not supplied, then this
2986
port will be used to accept SSL connections instead of unencrypted connections.
2987
If the B<--ssl> switch is used, and B<--ssl-port> is set, then unencrypted
2988
connections will be accepted on the B<--port> at the same time as encrypted
2989
connections are accepted at B<--ssl-port>.
2991
=item B<-q>, B<--sql-config>
2993
Turn on SQL lookups even when per-user config files have been disabled
2994
with B<-x>. this is useful for spamd hosts which don't have user's
2995
home directories but do want to load user preferences from an SQL
2998
If your spamc client does not support sending the C<User:> header,
2999
like C<exiscan>, then the SQL username used will always be B<nobody>.
3001
This inhibits the setuid() behavior, so the C<-u> option is
3002
required. If you want the setuid() behaviour, use C<-Q> or
3003
C<--setuid-with-sql> instead.
3005
=item B<--ldap-config>
3007
Turn on LDAP lookups. This is completely analog to C<--sql-config>,
3008
only it is using an LDAP server.
3010
Like C<--sql-config>, this disables the setuid behavior, and requires
3011
C<-u>. If you want it, use C<--setuid-with-ldap> instead.
3013
=item B<-Q>, B<--setuid-with-sql>
3015
Turn on SQL lookups even when per-user config files have been disabled
3016
with B<-x> and also setuid to the user. This is useful for spamd hosts
3017
which want to load user preferences from an SQL database but also wish to
3018
support the use of B<-H> (Helper home directories.)
3020
=item B<--setuid-with-ldap>
3022
Turn on LDAP lookups even when per-user config files have been disabled
3023
with B<-x> and also setuid to the user. This is again completely analog
3024
to C<--setuid-with-sql>, only it is using an LDAP server.
3026
=item B<--virtual-config-dir>=I<pattern>
3028
This option specifies where per-user preferences can be found for virtual
3029
users, for the B<-x> switch. The I<pattern> is used as a base pattern for the
3030
directory name. Any of the following escapes can be used:
3034
=item %u -- replaced with the full name of the current user, as sent by spamc.
3036
=item %l -- replaced with the 'local part' of the current username. In other
3037
words, if the username is an email address, this is the part before the C<@>
3040
=item %d -- replaced with the 'domain' of the current username. In other
3041
words, if the username is an email address, this is the part after the C<@>
3044
=item %% -- replaced with a single percent sign (%).
3048
So for example, if C</vhome/users/%u/spamassassin> is specified, and spamc
3049
sends a virtual username of C<jm@example.com>, the directory
3050
C</vhome/users/jm@example.com/spamassassin> will be used.
3052
The set of characters allowed in the virtual username for this path are
3055
A-Z a-z 0-9 - + _ . , @ =
3057
All others will be replaced by underscores (C<_>).
3059
This path must be a writable directory. It will be created if it does not
3060
already exist. If a file called B<user_prefs> exists in this directory (note:
3061
B<not> in a C<.spamassassin> subdirectory!), it will be loaded as the user's
3062
preferences. The Bayes databases for that user will be stored in this directory.
3064
Note that this B<requires> that B<-x> is used, and cannot be combined with
3065
SQL- or LDAP-based configuration.
3067
The pattern B<must> expand to an absolute directory when spamd is running
3070
Currently, use of this without B<-u> is not supported. This inhibits setuid.
3072
=item B<-r> I<pidfile>, B<--pidfile>=I<pidfile>
3074
Write the process ID of the spamd parent to the file specified by I<pidfile>.
3075
The file will be unlinked when the parent exits. Note that when running
3076
with the B<-u> option, the file must be writable by that user.
3078
=item B<-v>, B<--vpopmail>
3080
Enable vpopmail config. If specified with with B<-u> set to the vpopmail user,
3081
this allows spamd to lookup/create user_prefs in the vpopmail user's own
3082
maildir. This option is useful for vpopmail virtual users who do not have an
3083
entry in the system /etc/passwd file.
3085
Currently, use of this without B<-u> is not supported. This inhibits setuid.
3087
=item B<-s> I<facility>, B<--syslog>=I<facility>
3089
Specify the syslog facility to use (default: mail). If C<stderr> is specified,
3090
output will be written to stderr. (This is useful if you're running C<spamd>
3091
under the C<daemontools> package.) With a I<facility> of C<file>, all output
3092
goes to spamd.log. I<facility> is interpreted as a file name to log to if it
3093
contains any characters except a-z and 0-9. C<null> disables logging completely
3097
spamd -s mail # use syslog, facility mail (default)
3098
spamd -s ./mail # log to file ./mail
3099
spamd -s stderr 2>/dev/null # log to stderr, throw messages away
3100
spamd -s null # the same as above
3101
spamd -s file # log to file ./spamd.log
3102
spamd -s /var/log/spamd.log # log to file /var/log/spamd.log
3104
If logging to a file is enabled and that log file is rotated, the spamd server
3105
must be restarted with a SIGHUP. (If the log file is just truncated, this is
3106
not needed but still recommended.)
3108
Note that logging to a file does not use locking, so you cannot intermix
3109
logging from spamd and other processes into the same file. If you want
3110
to mix logging like this, use syslog instead.
3112
If you use syslog logging, it is essential to send a SIGHUP to the spamd daemon
3113
when you restart the syslogd daemon. (This is due to a shortcoming in Perl's
3114
syslog handling, where the disappearance of the connection to the syslogd is
3115
considered a fatal error.)
3117
=item B<--syslog-socket>=I<type>
3119
Specify how spamd should send messages to syslogd. The I<type> can be any
3120
of the socket types or logging mechanisms as accepted by the subroutine
3121
Sys::Syslog::setlogsock(). Depending on a version of Sys::Syslog and on the
3122
underlying operating system, one of the following values (or their subset) can
3123
be used: C<native>, C<eventlog>, C<tcp>, C<udp>, C<inet>, C<unix>, C<stream>,
3124
C<pipe>, or C<console>. The value C<eventlog> is specific to Win32 events
3125
logger and requires a perl module Win32::EventLog to be installed.
3126
For more information please consult the Sys::Syslog documentation.
3128
A historical setting --syslog-socket=none is mapped to --syslog=stderr.
3130
A default for Windows platforms is C<none>, otherwise the default is
3131
to try C<unix> first, falling back to C<inet> if perl detects errors
3132
in its C<unix> support.
3134
Some platforms, or versions of perl, are shipped with old or dysfunctional
3135
versions of the B<Sys::Syslog> module which do not support some socket types,
3136
so you may need to set this option explicitly. If you get error messages
3137
regarding B<__PATH_LOG> or similar spamd, try changing this setting.
3139
The socket types C<file> is used internally and should not be specified.
3140
Use the C<-s> switch instead.
3142
=item B<--log-timestamp-fmt>=I<format>
3144
The --log-timestamp-fmt option can provide a POSIX strftime(3) format for
3145
timestamps included in each logged message. Each logger (stderr, file,
3146
syslog) has its own default value for a timestamp format, which applies when
3147
--log-timestamp-fmt option is not given, or with --log-timestamp-fmt=default .
3148
Timestamps can be turned off by specifying an empty string with this
3149
option, e.g. --log-timestamp-fmt='' or just --log-timestamp-fmt= .
3150
Typical use: --log-timestamp-fmt='%a %b %e %H:%M:%S %Y' (provides
3151
localized weekday and month names in the ctime(3) style),
3152
or '%a, %e %b %Y %H:%M:%S %z (%Z)' for a RFC 2822 format,
3153
or maybe '%Y-%m-%d %H:%M:%S%z' for an ISO 8601 (EN 28601) format,
3154
or just '%Y%m%dT%H%M%S' .
3156
=item B<-u> I<username>, B<--username>=I<username>
3158
Run as the named user. If this option is not set, the default behaviour
3159
is to setuid() to the user running C<spamc>, if C<spamd> is running
3162
Note: "--username=root" is not a valid option. If specified, C<spamd> will
3163
exit with a fatal error on startup.
3165
=item B<-g> I<groupname>, B<--groupname>=I<groupname>
3167
Run as the named group if --username is being used. If this option is
3168
not set when --username is used then the primary group for the user
3169
given to --username is used.
3171
=item B<-x>, B<--nouser-config>, B<--user-config>
3173
Turn off (on) reading of per-user configuration files (user_prefs) from the
3174
user's home directory. The default behaviour is to read per-user
3175
configuration from the user's home directory (B<--user-config>).
3177
This option does not disable or otherwise influence the SQL, LDAP or
3178
Virtual Config Dir settings.
3180
=item B<--auth-ident>
3182
Verify the username provided by spamc using ident. This is only
3183
useful if connections are only allowed from trusted hosts (because an
3184
identd that lies is trivial to create) and if spamc REALLY SHOULD be
3185
running as the user it represents. Connections are terminated
3186
immediately if authentication fails. In this case, spamc will pass
3187
the mail through unchecked. Failure to connect to an ident server,
3188
and response timeouts are considered authentication failures. This
3189
requires that Net::Ident be installed.
3191
=item B<--ident-timeout>=I<timeout>
3193
Wait at most I<timeout> seconds for a response to ident queries.
3194
Authentication that takes long that I<timeout> seconds will fail, and
3195
mail will not be processed. Setting this to 0.0 or less results in no
3196
timeout, which is STRONGLY discouraged. The default is 5 seconds.
3198
=item B<-A> I<host,...>, B<--allowed-ips>=I<host,...>
3200
Specify a list of authorized hosts or networks which can connect to this spamd
3201
instance. Single IP addresses can be given, ranges of IP addresses in
3202
address/masklength CIDR format, or ranges of IP addresses by listing 3 or less
3203
octets with a trailing dot. Hostnames are not supported, only IP addresses.
3204
This option can be specified multiple times, or can take a list of addresses
3205
separated by commas. Examples:
3207
B<-A 10.11.12.13> -- only allow connections from C<10.11.12.13>.
3209
B<-A 10.11.12.13,10.11.12.14> -- only allow connections from C<10.11.12.13> and
3212
B<-A 10.200.300.0/24> -- allow connections from any machine in the range
3215
B<-A 10.> -- allow connections from any machine in the range C<10.*.*.*>.
3217
By default, connections are only accepted from localhost [127.0.0.1].
3219
=item B<-D> [I<area,...>], B<--debug> [I<area,...>]
3221
Produce debugging output. If no areas are listed, all debugging information is
3222
printed. Diagnostic output can also be enabled for each area individually;
3223
I<area> is the area of the code to instrument. For example, to produce
3224
diagnostic output on bayes, learn, and dns, use:
3226
spamassassin -D bayes,learn,dns
3228
Higher priority informational messages that are suitable for logging in normal
3229
circumstances are available with an area of "info".
3231
For more information about which areas (also known as channels) are available,
3232
please see the documentation at:
3234
C<http://wiki.apache.org/spamassassin/DebugChannels>
3236
=item B< --ipv4only>, B<--ipv4-only>, B<--ipv4>
3238
Do not use IPv6 for DNS tests. Use if the existing tests
3239
for IPv6 availability produce incorrect results or crashes.
3241
=item B<-L>, B<--local>
3243
Perform only local tests on all mail. In other words, skip DNS and other
3244
network tests. Works the same as the C<-L> flag to C<spamassassin(1)>.
3246
=item B<-P>, B<--paranoid>
3248
Die on user errors (for the user passed from spamc) instead of falling back to
3249
user I<nobody> and using the default configuration.
3251
=item B<-m> I<number> , B<--max-children>=I<number>
3253
This option specifies the maximum number of children to spawn.
3254
Spamd will spawn that number of children, then sleep in the background
3255
until a child dies, wherein it will go and spawn a new child.
3257
Incoming connections can still occur if all of the children are busy,
3258
however those connections will be queued waiting for a free child.
3259
The minimum value is C<1>, the default value is C<5>.
3261
Please note that there is a OS specific maximum of connections that can be
3262
queued (Try C<perl -MSocket -e'print SOMAXCONN'> to find this maximum).
3264
Note that if you run too many servers for the amount of free RAM available, you
3265
run the danger of hurting performance by causing a high swap load as server
3266
processes are swapped in and out continually.
3268
=item B<--min-children>=I<number>
3270
The minimum number of children that will be kept running. The minimum value is
3271
C<1>, the default value is C<1>. If you have lots of free RAM, you may want to
3274
=item B<--min-spare>=I<number>
3276
The lower limit for the number of spare children allowed to run. A
3277
spare, or idle, child is one that is not handling a scan request. If
3278
there are too few spare children available, a new server will be started
3279
every second or so. The default value is C<1>.
3281
=item B<--max-spare>=I<number>
3283
The upper limit for the number of spare children allowed to run. If there
3284
are too many spare children, one will be killed every second or so until
3285
the number of idle children is in the desired range. The default value
3288
=item B<--max-conn-per-child>=I<number>
3290
This option specifies the maximum number of connections each child
3291
should process before dying and letting the master spamd process spawn
3292
a new child. The minimum value is C<1>, the default value is C<200>.
3294
=item B<--round-robin>
3296
By default, C<spamd> will attempt to keep a small number of "hot" child
3297
processes as busy as possible, and keep any others as idle as possible, using
3298
something similar to the Apache httpd server scaling algorithm. This is
3299
accomplished by the master process coordinating the activities of the children.
3300
This switch will disable this scaling algorithm, and the behaviour seen in
3301
the 3.0.x versions will be used instead, where all processes receive an
3302
equal load and no scaling takes place.
3304
=item B<--timeout-tcp>=I<number>
3306
This option specifies the number of seconds to wait for headers from a
3307
client (spamc) before closing the connection. The minimum value is C<1>,
3308
the default value is C<30>, and a value of C<0> will disable socket
3309
timeouts completely.
3311
=item B<--timeout-child>=I<number>
3313
This option specifies the number of seconds to wait for a spamd child to
3314
process or check a message. The minimum value is C<1>, the default
3315
value is C<300>, and a value of C<0> will disable child timeouts completely.
3317
=item B<-H> I<directory>, B<--helper-home-dir>=I<directory>
3319
Specify that external programs such as Razor, DCC, and Pyzor should have
3320
a HOME environment variable set to a specific directory. The default
3321
is to use the HOME environment variable setting from the shell running
3322
spamd. By specifying no argument, spamd will use the spamc caller's
3323
home directory instead.
3327
Accept only SSL connections on the associated port.
3328
The B<IO::Socket::SSL> perl module must be installed.
3330
If the B<--ssl> switch is used, and B<--ssl-port> is not supplied, then
3331
B<--port> port will be used to accept SSL connections instead of unencrypted
3332
connections. If the B<--ssl> switch is used, and B<--ssl-port> is set, then
3333
unencrypted connections will be accepted on the B<--port>, at the same time as
3334
encrypted connections are accepted at B<--ssl-port>.
3336
=item B<--ssl-port>=I<port>
3338
Optionally specifies the port number for the server to listen on for
3339
SSL connections (default: whatever --port uses). See B<--ssl> for
3342
=item B<--ssl-version>=I<sslversion>
3344
Specify the SSL protocol version to use, one of B<sslv3> or B<tlsv1>.
3345
The default, B<sslv3>, is the most flexible, accepting a SSLv3 or
3346
higher hello handshake, then negotiating use of SSLv3 or TLSv1
3347
protocol if the client can accept it. Specifying B<--ssl-version>
3350
=item B<--server-key> I<keyfile>
3352
Specify the SSL key file to use for SSL connections.
3354
=item B<--server-cert> I<certfile>
3356
Specify the SSL certificate file to use for SSL connections.
3358
=item B<--socketpath> I<pathname>
3360
Listen on UNIX domain path I<pathname> instead of a TCP socket.
3362
Warning: the Perl support on BSD platforms for UNIX domain sockets seems to
3363
have a bug regarding paths of over 100 bytes or so (SpamAssassin bug 4380). If
3364
you see a 'could not find newly-created UNIX socket' error message, and the
3365
path appears truncated, this may be the cause. Try using a shorter path
3368
By default, use of B<--socketpath> will inhibit SSL connections and unencrypted
3369
TCP connections. To enable them, specify B<--port> and/or B<--ssl-port>
3372
=item B<--socketowner> I<name>
3374
Set UNIX domain socket to be owned by the user named I<name>. Note
3375
that this requires that spamd be started as C<root>, and if C<-u>
3376
is used, that user should have write permissions to unlink the file
3377
later, for when the C<spamd> server is killed.
3379
=item B<--socketgroup> I<name>
3381
Set UNIX domain socket to be owned by the group named I<name>. See
3382
C<--socketowner> for notes on ownership and permissions.
3384
=item B<--socketmode> I<mode>
3386
Set UNIX domain socket to use the octal mode I<mode>. Note that if C<-u> is
3387
used, that user should have write permissions to unlink the file later, for
3388
when the C<spamd> server is killed.
3396
Mail::SpamAssassin::Conf(3)
3397
Mail::SpamAssassin(3)
3399
=head1 PREREQUISITES
3401
C<Mail::SpamAssassin>
3405
The SpamAssassin(tm) Project (http://spamassassin.apache.org/)
3409
SpamAssassin is distributed under the Apache License, Version 2.0, as
3410
described in the file C<LICENSE> included with the distribution.