~ubuntu-branches/ubuntu/utopic/spamassassin/utopic-updates

« back to all changes in this revision

Viewing changes to .pc/95_bug694504-spamdforkscaling-crash/spamd/spamd.raw

  • Committer: Package Import Robot
  • Author(s): Noah Meyerhans
  • Date: 2014-02-14 22:45:15 UTC
  • mfrom: (0.8.1) (0.6.2) (5.1.22 sid)
  • Revision ID: package-import@ubuntu.com-20140214224515-z1es2twos8xh7n2y
Tags: 3.4.0-1
* New upstream version! (Closes: 738963, 738872, 738867)
* Scrub the environment when switching to the debian-spamd user in
  postinst and cron.daily. (Closes: 738951)
* Enhancements to postinst to better manage ownership of
  /var/lib/spamassassin, via Iain Lane <iain.lane@canonical.com>
  (Closes: 738974)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/perl -w -T
2
 
# <@LICENSE>
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:
9
 
10
 
#     http://www.apache.org/licenses/LICENSE-2.0
11
 
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.
17
 
# </@LICENSE>
18
 
 
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
24
 
 
25
 
# added by jm for use inside the distro
26
 
# This is disabled during the "make install" process.
27
 
BEGIN {
28
 
  if ( -e '../blib/lib/Mail/SpamAssassin.pm' ) {    # REMOVEFORINST
29
 
    unshift ( @INC, '../blib/lib' );                # REMOVEFORINST
30
 
  } else {                                          # REMOVEFORINST
31
 
    unshift ( @INC, '../lib' );                     # REMOVEFORINST
32
 
  }                                                 # REMOVEFORINST
33
 
}
34
 
 
35
 
use strict;
36
 
use warnings;
37
 
use re 'taint';
38
 
 
39
 
# Big Ugly Hack; purpose: don't force requirement on IO::Socket::INET6
40
 
BEGIN {
41
 
  use Socket;
42
 
  eval {
43
 
    require IO::Socket::INET6;
44
 
    require Socket6;
45
 
  };
46
 
  if ($@) {    # IPv4 only
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);
52
 
    };
53
 
    *peer_info_from_socket = sub {
54
 
      my $sock = shift;
55
 
      my ($port, $in_addr) = Socket::sockaddr_in($sock->peername)
56
 
        or return;
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);
60
 
    };
61
 
  }
62
 
  else {    # IPv4+IPv6
63
 
    *new_io_socket_inetx = sub { IO::Socket::INET6->new(@_); };
64
 
    *ip_or_name_to_ip    = sub {
65
 
      my $addr = (
66
 
        Socket6::getaddrinfo(
67
 
          shift, '', Socket::AF_UNSPEC(), Socket::SOCK_STREAM()
68
 
        )
69
 
        )[3]
70
 
        or return undef;
71
 
      $addr = (Socket6::getnameinfo($addr, Socket6::NI_NUMERICHOST()))[0];
72
 
    };
73
 
    *peer_info_from_socket = sub {
74
 
      my $sock = shift;
75
 
      my $addr = $sock->peerhost or return;
76
 
      my $host =
77
 
        (Socket6::getnameinfo($sock->peername, Socket6::NI_NAMEREQD()))[0]
78
 
        || $addr;
79
 
      return ($sock->peerport(), $addr, $host);
80
 
    };
81
 
  }
82
 
}
83
 
 
84
 
use IO::Handle;
85
 
use IO::Pipe;
86
 
 
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;
95
 
 
96
 
use Getopt::Long;
97
 
use POSIX qw(:sys_wait_h);
98
 
use POSIX qw(setsid sigprocmask _exit);
99
 
use Errno;
100
 
 
101
 
use Cwd ();
102
 
use File::Spec 0.8;
103
 
use File::Path;
104
 
use Carp ();
105
 
use Time::HiRes qw(time);
106
 
 
107
 
use constant RUNNING_ON_MACOS => ($^O =~ /^darwin/oi);
108
 
 
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";
113
 
}
114
 
 
115
 
BEGIN {
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]);
121
 
  };
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;
125
 
  };
126
 
}
127
 
 
128
 
my %resphash = (
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
146
 
);
147
 
 
148
 
sub print_version {
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 ($@);
155
 
}
156
 
 
157
 
sub print_usage_and_exit {
158
 
  my ( $message, $respnam ) = (@_);
159
 
  $respnam ||= 'EX_USAGE';
160
 
 
161
 
  if ($respnam eq 'EX_OK' ) {
162
 
    print_version();
163
 
    print("\n");
164
 
  }
165
 
 
166
 
  require Pod::Usage;
167
 
  import Pod::Usage;
168
 
  pod2usage(
169
 
    -verbose => 0,
170
 
    -message => $message,
171
 
    -exitval => $resphash{$respnam},
172
 
  );
173
 
}
174
 
 
175
 
# defaults
176
 
my %opt = (
177
 
  'user-config'   => 1,
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
185
 
);
186
 
 
187
 
 
188
 
# bug 1725, 2192:
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.
192
 
 
193
 
# Testing for taintedness only works before detainting %ENV
194
 
Mail::SpamAssassin::Util::am_running_in_taint_mode();
195
 
 
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 );
200
 
 
201
 
# The zeroth argument will be replaced in daemonize().
202
 
my $ORIG_ARG0 = untaint_var($0);
203
 
 
204
 
# Getopt::Long clears all arguments it processed (untaint both @ARGVs here!)
205
 
my @ORIG_ARGV = untaint_var( \@ARGV );
206
 
 
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() );
210
 
 
211
 
prepare_for_sighup_restart();
212
 
 
213
 
# Parse the command line
214
 
Getopt::Long::Configure("bundling");
215
 
GetOptions(
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'},
273
 
 
274
 
  #
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.
278
 
  #
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.
282
 
  #
283
 
  
284
 
  # last in 2.3
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; },
287
 
 
288
 
  # last in 2.4
289
 
  'stop-at-threshold|S' => sub { warn "spamd: the -S option has been deprecated and is no longer supported, ignoring\n" },
290
 
 
291
 
) or print_usage_and_exit();
292
 
 
293
 
if ($opt{'help'}) {
294
 
  print_usage_and_exit(qq{For more details, use "man spamd".\n}, 'EX_OK');
295
 
}
296
 
if ($opt{'version'}) {
297
 
  print_version();
298
 
  exit($resphash{'EX_OK'});
299
 
}
300
 
 
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
304
 
}
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);
310
 
}
311
 
 
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';
317
 
}
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'});
322
 
 
323
 
# bug 2228: make the values of (almost) all parameters which accept file paths
324
 
# absolute, so they are still valid after daemonize()
325
 
foreach my $opt (
326
 
  qw(
327
 
  configpath
328
 
  siteconfigpath
329
 
  socketpath
330
 
  pidfile
331
 
  home_dir_for_helpers
332
 
  )
333
 
  )
334
 
{
335
 
  $opt{$opt} = Mail::SpamAssassin::Util::untaint_file_path(
336
 
    File::Spec->rel2abs( $opt{$opt} )    # rel2abs taints the new value!
337
 
  ) if ( $opt{$opt} );
338
 
}
339
 
 
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.
345
 
 
346
 
my $listen_unix = 1;
347
 
my $listen_inet = 1;
348
 
my $listen_ssl  = 0;
349
 
if (!defined $opt{'socketpath'}) {
350
 
  $listen_unix = 0;
351
 
} else {
352
 
  $listen_inet = 0;
353
 
}
354
 
 
355
 
if (
356
 
  ( @{ $opt{'allowed-ip'} } > 0 )
357
 
    or defined $opt{'auth-ident'}
358
 
    or defined $opt{'port'}
359
 
  )
360
 
{
361
 
  $listen_inet = 1;
362
 
}
363
 
 
364
 
if (
365
 
    defined $opt{'ssl'}
366
 
    or defined $opt{'ssl-port'}
367
 
    or defined $opt{'ssl-version'}
368
 
  )
369
 
{
370
 
  $listen_ssl = 1;
371
 
  if (!defined $opt{'ssl-port'}) {
372
 
    $listen_inet = 0;
373
 
  }
374
 
}
375
 
 
376
 
if (
377
 
  !$opt{'socketpath'}
378
 
  and ( $opt{'socketowner'}
379
 
    or $opt{'socketgroup'}
380
 
    or $opt{'socketmode'})
381
 
  )
382
 
{
383
 
  print_usage_and_exit("ERROR: --socketowner/group/mode requires --socketpath param");
384
 
}
385
 
 
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'} } );
392
 
  }
393
 
  else {
394
 
    set_allowed_ip('127.0.0.1'); #, '::1'); M::SA::NetSet needs fixing for IPv6
395
 
  }
396
 
}
397
 
 
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"
402
 
    if ($@);
403
 
 
404
 
  $opt{'ident-timeout'} = undef if $opt{'ident-timeout'} <= 0.0;
405
 
  import Net::Ident qw(ident_lookup);
406
 
}
407
 
 
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";
411
 
if ( $listen_ssl ) {
412
 
  eval { require IO::Socket::SSL };
413
 
  die "spamd: SSL encryption requested, but IO::Socket::SSL is unavailable ($@)\n"
414
 
    if ($@);
415
 
 
416
 
  if ( !-e $opt{'server-key'} ) {
417
 
    die "spamd: server key file $opt{'server-key'} does not exist\n";
418
 
  }
419
 
  if ( !-e $opt{'server-cert'} ) {
420
 
    die "spamd: server certificate file $opt{'server-cert'} does not exist\n";
421
 
  }
422
 
}
423
 
 
424
 
### Begin initialization of logging ########################
425
 
 
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';
434
 
 
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.
441
 
#
442
 
# In addition to values acceptable by Sys::Syslog::setlogsock(),
443
 
# a --syslog-socket=none is mapped to --syslog=stderr and $log_socket='file'.
444
 
#
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.
448
 
#
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.
455
 
#  But it works.
456
 
# )
457
 
 
458
 
my $log_socket = $opt{'syslog-socket'};
459
 
 
460
 
if (!defined $log_socket || $log_socket eq '') {
461
 
  $log_socket = am_running_on_windows() ? 'none' : 'unix';
462
 
} else {
463
 
  $log_socket = lc $log_socket;
464
 
}
465
 
 
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";
469
 
 
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';
474
 
}
475
 
# The generic log file was requested (--syslog=file).
476
 
elsif (lc($log_facility) eq 'file') {
477
 
  $log_socket = 'file';
478
 
}
479
 
# The casing is kept only if the facility specified a file.
480
 
else {
481
 
  $log_facility = lc($log_facility);
482
 
}
483
 
 
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';
488
 
}
489
 
# The socket 'none' (--syslog-socket=none) historically
490
 
# represents logging to STDERR.
491
 
elsif ($log_socket eq 'none') {
492
 
  $log_facility = 'stderr';
493
 
}
494
 
 
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';
499
 
}
500
 
 
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' .
508
 
 
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,
514
 
                                       ident => 'spamd',
515
 
                                       timestamp_fmt => $log_timestamp_fmt))
516
 
  {
517
 
    # syslog method failed 
518
 
    $log_facility = 'stderr';
519
 
  }
520
 
}
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))
526
 
  {
527
 
    # file method failed
528
 
    $log_facility = 'stderr';
529
 
  }
530
 
}
531
 
 
532
 
### End initialization of logging ##########################
533
 
 
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?
538
 
 
539
 
# support setuid() to user unless:
540
 
# run with -u
541
 
# we're not root
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 = (
546
 
        $opt{'username'} ||
547
 
        $> != 0 ||
548
 
        $opt{'vpopmail'} ||
549
 
        $opt{'virtual-config-dir'} ||
550
 
        ($opt{'sql-config'} && !$opt{'setuid-with-sql'}) ||
551
 
        ($opt{'ldap-config'} && !$opt{'setuid-with-ldap'})
552
 
      ) ? 0 : 1;
553
 
 
554
 
dbg("spamd: will perform setuids? $setuid_to_user");
555
 
 
556
 
if ( $opt{'vpopmail'} ) {
557
 
  if ( !$opt{'username'} ) {
558
 
    die "spamd: cannot use --vpopmail without -u\n";
559
 
  }
560
 
}
561
 
 
562
 
if ( $opt{'virtual-config-dir'} ) {
563
 
  if ( !$opt{'username'} ) {
564
 
    die "spamd: cannot use --virtual-config-dir without -u\n";
565
 
  }
566
 
}
567
 
 
568
 
if ($opt{'sql-config'} && !$opt{'setuid-with-sql'}) {
569
 
  if ( !$opt{'username'} ) {
570
 
    die "spamd: cannot use --sql-config without -u\n";
571
 
  }
572
 
}
573
 
 
574
 
if ($opt{'ldap-config'} && !$opt{'setuid-with-ldap'}) {
575
 
  if ( !$opt{'username'} ) {
576
 
    die "spamd: cannot use --ldap-config without -u\n";
577
 
  }
578
 
}
579
 
 
580
 
# always copy the config, later code may disable
581
 
my $copy_config_p = 1;
582
 
 
583
 
my $current_user;
584
 
 
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
591
 
 
592
 
if ( defined $opt{'max-children'} ) {
593
 
  $childlimit = $opt{'max-children'};
594
 
 
595
 
  # Make sure that the values are at least 1
596
 
  $childlimit = undef if ( $childlimit < 1 );
597
 
}
598
 
 
599
 
if ( defined $opt{'max-conn-per-child'} ) {
600
 
  $clients_per_child = $opt{'max-conn-per-child'};
601
 
 
602
 
  # Make sure that the values are at least 1
603
 
  $clients_per_child = undef if ( $clients_per_child < 1 );
604
 
}
605
 
 
606
 
if (defined $opt{'timeout-tcp'}) {
607
 
  $timeout_tcp = $opt{'timeout-tcp'};
608
 
  $timeout_tcp = undef if ($timeout_tcp < 1);
609
 
}
610
 
 
611
 
if (defined $opt{'timeout-child'}) {
612
 
  $timeout_child = $opt{'timeout-child'};
613
 
  $timeout_child = undef if ($timeout_child < 1);
614
 
}
615
 
 
616
 
# Set some "sane" limits for defaults
617
 
$childlimit        ||= 5;
618
 
$clients_per_child ||= 200;
619
 
$timeout_child     ||= 300;
620
 
$timeout_tcp       ||= 30;
621
 
 
622
 
# ensure scaling parameters are logical
623
 
if ($opt{'min-children'} < 1) {
624
 
  $opt{'min-children'} = 1;
625
 
}
626
 
if ($opt{'min-spare'} < 0) {
627
 
  $opt{'min-spare'} = 0;
628
 
}
629
 
if ($opt{'min-spare'} > $childlimit) {
630
 
  $opt{'min-spare'} = $childlimit-1;
631
 
}
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;
636
 
}
637
 
 
638
 
my $dontcopy = 1;
639
 
if ( $opt{'create-prefs'} ) { $dontcopy = 0; }
640
 
 
641
 
my $orighome;
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] ) {
646
 
      $ENV{'HOME'} = $nh;
647
 
    }
648
 
    else {
649
 
      die "spamd: unable to determine home directory for user '"
650
 
        . $opt{'username'} . "'\n";
651
 
    }
652
 
  }
653
 
 
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
656
 
}
657
 
 
658
 
# Do whitelist later in tmp dir. Side effect: this will be done as -u user.
659
 
 
660
 
my ( $sslport, $sslversion, $inetport, $addr, $proto );
661
 
 
662
 
if ( $listen_inet || $listen_ssl ) {
663
 
  $proto = getprotobyname('tcp') or die "getprotobyname(tcp): $!";
664
 
 
665
 
  $addr = $opt{'listen-ip'};
666
 
  if (defined $addr) {
667
 
    if ($addr ne '') {
668
 
      $addr = ip_or_name_to_ip($addr);
669
 
      die "spamd: invalid address: $opt{'listen-ip'}\n" unless $addr;
670
 
    }
671
 
    else {
672
 
      $addr = '0.0.0.0';    # FIXME: this won't bind to IPv6 sockets
673
 
    }
674
 
  }
675
 
  else {
676
 
    $addr = '127.0.0.1';
677
 
  }
678
 
}
679
 
 
680
 
my $backchannel = Mail::SpamAssassin::SubProcBackChannel->new();
681
 
my $scaling;
682
 
if (!$opt{'round-robin'})
683
 
{
684
 
  my $max_children = $childlimit;
685
 
 
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'};
691
 
  }
692
 
 
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
700
 
      });
701
 
}
702
 
 
703
 
# ---------------------------------------------------------------------------
704
 
 
705
 
my $listeninfo = compose_listen_info_string();
706
 
 
707
 
sub compose_listen_info_string {
708
 
  my @listeninfo;
709
 
 
710
 
  if ( $listen_unix ) {
711
 
    push @listeninfo, "UNIX domain socket " . $opt{'socketpath'};
712
 
  }
713
 
 
714
 
  if ( $listen_ssl ) {
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;
719
 
    }
720
 
    $sslversion = $opt{'ssl-version'} || 'sslv3';
721
 
    if ($sslversion !~ /^(?:sslv3|tlsv1)$/) {
722
 
      die "spamd: invalid ssl-version: $opt{'ssl-version'}\n";
723
 
    }
724
 
 
725
 
    push @listeninfo, "SSL port $sslport/tcp";
726
 
    push @listeninfo, "SSL version $sslversion";
727
 
  }
728
 
 
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;
734
 
    }
735
 
 
736
 
    push @listeninfo, "port $inetport/tcp";
737
 
  }
738
 
 
739
 
  # just for reporting at startup
740
 
  $listeninfo = join ', ', @listeninfo;
741
 
}
742
 
 
743
 
# ---------------------------------------------------------------------------
744
 
# Server (listening) socket setup for the various supported types
745
 
 
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;
750
 
 
751
 
# abstract out the setup-retry code
752
 
if ( $listen_unix ) {
753
 
  server_sock_setup(sub { server_sock_setup_unix(); });
754
 
}
755
 
if ( $listen_ssl ) {
756
 
  server_sock_setup(sub { server_sock_setup_ssl(); });
757
 
}
758
 
if ( $listen_inet ) {
759
 
  server_sock_setup(sub { server_sock_setup_inet(); });
760
 
}
761
 
 
762
 
sub server_sock_setup {
763
 
  my $sub = shift;
764
 
 
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
768
 
  my $lastretry = 10;
769
 
  for my $retry (1 .. $lastretry) {
770
 
    if ($retry > 1) { sleep 1; }
771
 
 
772
 
    eval { $sub->(); };
773
 
    last unless ($@);       # success => break
774
 
 
775
 
    if ($retry == $lastretry) {
776
 
      die $@;               # this is fatal
777
 
    } else {
778
 
      warn "server socket setup failed, retry $retry: $@";
779
 
      # but retry
780
 
    }
781
 
  }
782
 
}
783
 
 
784
 
# ---------------------------------------------------------------------------
785
 
 
786
 
# Create the sockets
787
 
sub server_sock_setup_unix {
788
 
  my $path = $opt{'socketpath'};
789
 
 
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.
794
 
 
795
 
  if (-e $path) {
796
 
    unless (-S $path) {
797
 
      die "spamd: file $path exists but is no socket, exiting\n";
798
 
    }
799
 
  
800
 
    if ( new IO::Socket::UNIX( Peer => $path, Type => SOCK_STREAM ) ) {
801
 
      # we connected successfully: must alreadybe running
802
 
 
803
 
      undef $opt{'socketpath'};    # so exit handlers won't unlink it!
804
 
 
805
 
      die "spamd: already running on $path, exiting\n";
806
 
    }
807
 
    else {
808
 
      dbg("spamd: removing stale socket file $path");
809
 
      unlink $path;
810
 
    }
811
 
  }
812
 
  if (not -d (File::Spec->splitpath($path))[1]) {
813
 
    die "spamd: directory for $path does not exist, exiting\n";
814
 
  }
815
 
 
816
 
  my %socket = (
817
 
    Local  => $path,
818
 
    Type   => SOCK_STREAM,
819
 
    Listen => SOMAXCONN,
820
 
  );
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);
823
 
  
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";
829
 
    }
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";
835
 
    }
836
 
    else {
837
 
      die "spamd: could not find newly-created UNIX socket on $path: $!\n";
838
 
    }
839
 
  }
840
 
 
841
 
  my $mode = $opt{socketmode};
842
 
  if ($mode) {
843
 
    $mode = oct $mode;
844
 
  } else {
845
 
    $mode = 0666;        # default
846
 
  }
847
 
 
848
 
  my $owner = $opt{socketowner};
849
 
  my $group = $opt{socketgroup};
850
 
  if ($owner || $group) {
851
 
    my $uid = -1;
852
 
    my $gid = -1;
853
 
    if ($owner) {
854
 
      my ($login,$pass,$puid,$pgid) = getpwnam($owner)
855
 
                           or die "spamd: $owner not in passwd database\n";
856
 
      $uid = $puid;
857
 
    }
858
 
    if ($group) {
859
 
      my ($name,$pass,$ggid,$members) = getgrnam($group)
860
 
                           or die "spamd: $group not in group database\n";
861
 
      $gid = $ggid;
862
 
    }
863
 
    if (!chown $uid, $gid, $path) {
864
 
      die "spamd: could not chown $path to $uid/$gid: $!";
865
 
    }
866
 
  }
867
 
 
868
 
  if (!chmod $mode, $path) {    # make sure everybody can talk to it
869
 
    die "spamd: could not chmod $path to $mode: $!";
870
 
  }
871
 
}
872
 
 
873
 
sub server_sock_setup_ssl {
874
 
  my %socket = (
875
 
    LocalAddr       => $addr,
876
 
    LocalPort       => $sslport,
877
 
    Proto           => $proto,
878
 
    Type            => SOCK_STREAM,
879
 
    ReuseAddr       => 1,
880
 
    Listen          => SOMAXCONN,
881
 
    SSL_version     => $sslversion,
882
 
    SSL_verify_mode => 0x00,
883
 
    SSL_key_file    => $opt{'server-key'},
884
 
    SSL_cert_file   => $opt{'server-cert'}
885
 
  );
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";
889
 
}
890
 
 
891
 
sub server_sock_setup_inet {
892
 
  my %socket = (
893
 
    LocalAddr => $addr,
894
 
    LocalPort => $inetport,
895
 
    Proto     => $proto,
896
 
    Type      => SOCK_STREAM,
897
 
    ReuseAddr => 1,
898
 
    Listen    => SOMAXCONN
899
 
  );
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";
903
 
}
904
 
 
905
 
# ---------------------------------------------------------------------------
906
 
 
907
 
# for select() purposes: make a map of the server socket FDs
908
 
map_server_sockets();
909
 
 
910
 
if ( defined $opt{'pidfile'} ) {
911
 
  $opt{'pidfile'} =
912
 
    Mail::SpamAssassin::Util::untaint_file_path( $opt{'pidfile'} );
913
 
}
914
 
 
915
 
 
916
 
my $spamtest = Mail::SpamAssassin->new(
917
 
  {
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 ),
926
 
    require_rules        => 1,
927
 
    home_dir_for_helpers => (
928
 
      defined $opt{'home_dir_for_helpers'}
929
 
      ? $opt{'home_dir_for_helpers'}
930
 
      : $orighome
931
 
    ),
932
 
    PREFIX          => $PREFIX,
933
 
    DEF_RULES_DIR   => $DEF_RULES_DIR,
934
 
    LOCAL_RULES_DIR => $LOCAL_RULES_DIR,
935
 
    LOCAL_STATE_DIR => $LOCAL_STATE_DIR
936
 
  }
937
 
);
938
 
 
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
943
 
  $copy_config_p = 0;
944
 
}
945
 
 
946
 
## DAEMONIZE! ##
947
 
 
948
 
my $originalparent = $$;
949
 
$opt{'daemonize'} and daemonize();
950
 
 
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.
954
 
my $got_sighup;
955
 
setup_parent_sig_handlers();
956
 
 
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();
960
 
 
961
 
# this must be after preload_modules_with_tmp_homedir(), for bug 5606
962
 
$spamtest->init_learner({
963
 
  opportunistic_expire_check_only => 1,
964
 
});
965
 
 
966
 
# bayes DBs may still be tied() at this point, so untie them and such.
967
 
$spamtest->finish_learner();
968
 
 
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.
972
 
my %conf_backup;
973
 
my %msa_backup;
974
 
 
975
 
if ($copy_config_p) {
976
 
  foreach( 'username', 'user_dir', 'userstate_dir', 'learn_to_journal' ) {
977
 
    $msa_backup{$_} = $spamtest->{$_} if (exists $spamtest->{$_});
978
 
  }
979
 
 
980
 
  $spamtest->copy_config(undef, \%conf_backup) ||
981
 
    die "spamd: error returned from copy_config\n";
982
 
}
983
 
 
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();
987
 
 
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() . ")" );
993
 
 
994
 
my $remote_port;
995
 
 
996
 
# Make the pidfile ...
997
 
if (defined $opt{'pidfile'}) {
998
 
  if (open PIDF, ">$opt{'pidfile'}") {
999
 
    print PIDF "$$\n";
1000
 
    close PIDF;
1001
 
  }
1002
 
  else {
1003
 
    warn "spamd: cannot write to PID file: $!\n";
1004
 
  }
1005
 
}
1006
 
 
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'});
1011
 
 
1012
 
# Fork off our children.
1013
 
for ( 1 .. $childlimit ) {
1014
 
  spawn();
1015
 
}
1016
 
 
1017
 
if ($scaling) {
1018
 
  $scaling->set_server_fh($server_inet, $server_unix, $server_ssl);
1019
 
}
1020
 
 
1021
 
while (1) {
1022
 
  if (!$scaling) {
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
1026
 
    sleep 10;
1027
 
  } else {
1028
 
    $scaling->main_server_poll($opt{'server-scale-period'});
1029
 
  }
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
1033
 
 
1034
 
  do_sighup_restart()  if defined $got_sighup;
1035
 
 
1036
 
  for (my $i = keys %children; $i < $childlimit; $i++) {
1037
 
    spawn();
1038
 
  }
1039
 
}
1040
 
 
1041
 
# Kicks off a kid ...
1042
 
sub spawn {
1043
 
  my $pid;
1044
 
 
1045
 
  $backchannel->setup_backchannel_parent_pre_fork();
1046
 
 
1047
 
  # block signal for fork
1048
 
  my $sigset;
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";
1053
 
  }    
1054
 
 
1055
 
  $pid = fork();
1056
 
  die "spamd: fork: $!" unless defined $pid;
1057
 
 
1058
 
  if ($pid) {
1059
 
    ## PARENT
1060
 
 
1061
 
    $children{$pid} = 1;
1062
 
    info("spamd: server successfully spawned child process, pid $pid");
1063
 
    $backchannel->setup_backchannel_parent_post_fork($pid);
1064
 
    if ($scaling) {
1065
 
      $scaling->add_child($pid);
1066
 
    }
1067
 
    if (!am_running_on_windows()) {
1068
 
      sigprocmask( POSIX::SIG_UNBLOCK(), $sigset )
1069
 
        or die "spamd: cannot unblock SIGINT/SIGCHLD for fork: $!\n";
1070
 
    }
1071
 
    #Changing to return the process id to improve communications for bug 6304
1072
 
    return $pid;
1073
 
  }
1074
 
  else {
1075
 
    ## CHILD
1076
 
 
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";
1083
 
    }
1084
 
 
1085
 
    $spamtest->call_plugins("spamd_child_init");
1086
 
 
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";
1092
 
      }
1093
 
 
1094
 
      if ( $opt{'groupname'} ) {
1095
 
        $ugid = getgrnam( $opt{'groupname'} ) || $ugid;
1096
 
      }
1097
 
 
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
1104
 
 
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";
1108
 
      }
1109
 
    }
1110
 
 
1111
 
    # set process name where supported
1112
 
    # this will help make it clear via process listing which is child/parent
1113
 
    $0 = 'spamd child';
1114
 
 
1115
 
    $backchannel->setup_backchannel_child_post_fork();
1116
 
    if ($scaling) {     # only do this once, for efficiency; $$ is a syscall
1117
 
      $scaling->set_my_pid($$);
1118
 
    }
1119
 
 
1120
 
    # handle $clients_per_child connections, then die in "old" age...
1121
 
    my $orders;
1122
 
    for ( my $i = 0 ; $i < $clients_per_child ; $i++ ) {
1123
 
      if ($scaling) {
1124
 
        $scaling->update_child_status_idle();
1125
 
        $orders = $scaling->wait_for_orders(); # and sleep...
1126
 
 
1127
 
        if ($orders != PFORDER_ACCEPT) {
1128
 
          info("spamd: unknown order: $orders");
1129
 
        }
1130
 
      }
1131
 
 
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(); };
1135
 
 
1136
 
      if (!defined ($evalret)) {
1137
 
        warn("spamd: error: $@ $!, continuing");
1138
 
        if ($client) { $client->close(); }  # avoid fd leaks
1139
 
      }
1140
 
      elsif ($evalret == -1) {
1141
 
        # serious error; used for accept() failure
1142
 
        die("spamd: respawning server");
1143
 
      }
1144
 
 
1145
 
      $spamtest->call_plugins("spamd_child_post_connection_close");
1146
 
 
1147
 
      # if we changed UID during processing, change back!
1148
 
      if ($setuid_to_user && ($> != $<) && ($> != ($< - 2**32))) {
1149
 
        $) = "$( $(";    # change eGID
1150
 
        $> = $<;         # change eUID
1151
 
 
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");
1156
 
        }
1157
 
      }
1158
 
 
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
1163
 
        # in case.
1164
 
        # bug 4699: this is the alarm that often ends up with an empty $@
1165
 
 
1166
 
        my $timer = Mail::SpamAssassin::Timeout->new({ secs => 20 });
1167
 
        my $err = $timer->run(sub {     
1168
 
 
1169
 
          while(my($k,$v) = each %msa_backup) {
1170
 
            $spamtest->{$k} = $v;
1171
 
          }
1172
 
 
1173
 
          # if we changed user, we would have also loaded up new configs
1174
 
          # (potentially), so let's restore back the saved version we
1175
 
          # had before.
1176
 
          $spamtest->copy_config(\%conf_backup, undef) ||
1177
 
            die "spamd: error returned from copy_config\n";
1178
 
        });
1179
 
 
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
1184
 
        }
1185
 
      }
1186
 
      undef $current_user;
1187
 
 
1188
 
      dbg("timing: " . $spamtest->timer_report()) if would_log('dbg', 'timing');
1189
 
    }
1190
 
 
1191
 
    # If the child lives to get here, it will die ...  Muhaha.
1192
 
    exit;
1193
 
  }
1194
 
}
1195
 
 
1196
 
sub accept_from_any_server_socket {
1197
 
 
1198
 
  my $fdvec = $server_select_mask;
1199
 
 
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;
1208
 
  }
1209
 
 
1210
 
  if ($fd_inet && vec $fdvec, $fd_inet, 1) {
1211
 
    $client = $server_inet->accept;
1212
 
  }
1213
 
  elsif ($fd_unix && vec $fdvec, $fd_unix, 1) {
1214
 
    $client = $server_unix->accept;
1215
 
  }
1216
 
  elsif ($fd_ssl && vec $fdvec, $fd_ssl, 1) {
1217
 
    $client = $server_ssl->accept;
1218
 
  }
1219
 
  else {
1220
 
    die "accept_a_conn: no fds ready by vec: $fdvec";
1221
 
  }
1222
 
  return $client;
1223
 
}
1224
 
 
1225
 
sub accept_a_conn {
1226
 
  $client = accept_from_any_server_socket();
1227
 
 
1228
 
  if ($scaling) {
1229
 
    $scaling->update_child_status_busy();
1230
 
  }
1231
 
 
1232
 
  # Bah!
1233
 
  if ( !$client  || !defined $client->connected() ) {
1234
 
 
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 ) {
1238
 
      return 0;
1239
 
    }
1240
 
    elsif ( $! == 0 && $listen_ssl ) {
1241
 
      warn("spamd: SSL failure: " . &IO::Socket::SSL::errstr());
1242
 
      return 0;
1243
 
    }
1244
 
    else {
1245
 
      warn("spamd: accept failed: $!");
1246
 
      return -1;
1247
 
    }
1248
 
  }
1249
 
 
1250
 
  $client->autoflush(1);
1251
 
 
1252
 
  # keep track of start time
1253
 
  $spamtest->timer_reset;
1254
 
  my $start = time;
1255
 
 
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'});
1262
 
  }
1263
 
  else {
1264
 
    ($remote_port, $remote_hostaddr, $remote_hostname) =
1265
 
      peer_info_from_socket($client)
1266
 
      or die 'failed to obtain port and ip from socket';
1267
 
 
1268
 
    my $msg = "connection from ${remote_hostname} [${remote_hostaddr}] at port ${remote_port}";
1269
 
    if (ip_is_allowed($remote_hostaddr)) {
1270
 
      info("spamd: $msg");
1271
 
    }
1272
 
    else {
1273
 
      warn("spamd: unauthorized $msg");
1274
 
      $client->close;
1275
 
      return 0;
1276
 
    }
1277
 
  }
1278
 
 
1279
 
  local ($_);
1280
 
  eval {
1281
 
    Mail::SpamAssassin::Util::trap_sigalrm_fully(sub {
1282
 
                          die "tcp timeout";
1283
 
                        });
1284
 
    alarm $timeout_tcp if ($timeout_tcp);
1285
 
    # send the request to the child process
1286
 
    $_ = $client->getline;
1287
 
  };
1288
 
  alarm 0;
1289
 
 
1290
 
  if ($@) {
1291
 
    if ($@ =~ /tcp timeout/) {
1292
 
      service_timeout("($timeout_tcp second socket timeout reading input from client)");
1293
 
    } else {
1294
 
      warn "spamd: $@";
1295
 
    }
1296
 
    $client->close;
1297
 
    return 0;
1298
 
  }
1299
 
 
1300
 
  if ( !defined $_ ) {
1301
 
    protocol_error("(closed before headers)");
1302
 
    $client->close;
1303
 
    return 0;
1304
 
  }
1305
 
 
1306
 
  s/\r?\n//;
1307
 
 
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.
1312
 
 
1313
 
  if (/(PROCESS|CHECK|SYMBOLS|REPORT|HEADERS|REPORT_IFSPAM) SPAMC\/(.*)/) {
1314
 
    my $method = $1;
1315
 
    my $version = $2;
1316
 
    eval {
1317
 
      Mail::SpamAssassin::Util::trap_sigalrm_fully(sub {
1318
 
                          die "child processing timeout";
1319
 
                        });
1320
 
      alarm $timeout_child if ($timeout_child);
1321
 
      check($method, $version, $start, $remote_hostname, $remote_hostaddr);
1322
 
    };
1323
 
    alarm 0;
1324
 
 
1325
 
    if ($@) {
1326
 
      if ($@ =~ /child processing timeout/) {
1327
 
        service_timeout("($timeout_child second timeout while trying to $method)");
1328
 
      } else {
1329
 
        warn "spamd: $@";
1330
 
      }
1331
 
      $client->close();
1332
 
      return 0;
1333
 
    }
1334
 
  }
1335
 
 
1336
 
  elsif (/(TELL) SPAMC\/(.*)/) {
1337
 
    my $method = $1;
1338
 
    my $version = $2;
1339
 
    eval {
1340
 
      Mail::SpamAssassin::Util::trap_sigalrm_fully(sub {
1341
 
                                                     die "child processing timeout";
1342
 
                                                   });
1343
 
      alarm $timeout_child if ($timeout_child);
1344
 
      dotell($method, $version, $start, $remote_hostname, $remote_hostaddr);
1345
 
    };
1346
 
    alarm 0;
1347
 
 
1348
 
    if ($@) {
1349
 
      if ($@ =~ /child processing timeout/) {
1350
 
        service_timeout("($timeout_child second timeout while trying to $method)");
1351
 
      } else {
1352
 
        warn "spamd: $@";
1353
 
      }
1354
 
      $client->close();
1355
 
      return 0;
1356
 
    }
1357
 
  }
1358
 
 
1359
 
  # Looks like a client is just seeing if we're alive or changed its mind
1360
 
 
1361
 
  elsif (/(SKIP|PING) SPAMC\/(.*)/) {
1362
 
    my $method = $1;
1363
 
    my $version = $2;
1364
 
 
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
1368
 
      # in that case.
1369
 
      info("spamd: skipped large message in %3.1f seconds", time - $start);
1370
 
    }
1371
 
    doskip_or_ping($method, $version,
1372
 
                   $start, $remote_hostname, $remote_hostaddr);
1373
 
  }
1374
 
 
1375
 
  # If it was none of the above, then we don't know what it was.
1376
 
 
1377
 
  else {
1378
 
    protocol_error($_);
1379
 
  }
1380
 
 
1381
 
  # Close out our connection to the client ...
1382
 
  $client->close();
1383
 
  return 1;
1384
 
}
1385
 
 
1386
 
sub handle_setuid_to_user {
1387
 
  if ($spamtest->{paranoid}) {
1388
 
    die("spamd: in paranoid mode, still running as root: closing connection");
1389
 
  }
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");
1393
 
 
1394
 
    my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $dir, $etc) =
1395
 
        getpwnam('nobody');
1396
 
  
1397
 
    $) = "$gid $gid";                   # eGID
1398
 
    $> = $uid;                          # eUID
1399
 
    if (!defined($uid) || ($> != $uid and $> != ($uid - 2**32))) {
1400
 
      die("spamd: setuid to nobody failed");
1401
 
  }
1402
 
 
1403
 
  $spamtest->signal_user_changed(
1404
 
    {
1405
 
      username => $name,
1406
 
      user_dir => $dir
1407
 
    }
1408
 
  );
1409
 
  }
1410
 
}
1411
 
 
1412
 
sub parse_body {
1413
 
  my ($client, $expected_length, $compress_zlib, $start_time) = @_;
1414
 
 
1415
 
  my @msglines;
1416
 
  my $actual_length;
1417
 
 
1418
 
  if ($compress_zlib && !defined($expected_length)) {
1419
 
    service_unavailable_error("Compress requires Content-length header");
1420
 
    return;
1421
 
  }
1422
 
 
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;
1427
 
  }
1428
 
  else {
1429
 
    @msglines = ();
1430
 
    $actual_length = 0;
1431
 
    while (defined($_ = $client->getline())) {
1432
 
      $actual_length += length($_);
1433
 
      push(@msglines, $_);    
1434
 
      last if (defined $expected_length && $actual_length >= $expected_length);
1435
 
    }
1436
 
  }
1437
 
  
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 } );
1443
 
 
1444
 
  return ($mail, $actual_length);
1445
 
}
1446
 
 
1447
 
sub zlib_inflate_read {
1448
 
  my ($client, $expected_length, $msglinesref) = @_;
1449
 
  my $out;
1450
 
  my $actual_length;
1451
 
 
1452
 
  eval {
1453
 
    require Compress::Zlib;
1454
 
    my ($zlib, $status) = Compress::Zlib::inflateInit();
1455
 
    if (!$zlib) { die "inflateInit failed: $status"; }
1456
 
 
1457
 
    my $red = 0;
1458
 
    my $buf;
1459
 
 
1460
 
    # TODO: inflate in smaller buffers instead of at EOF
1461
 
    while (1) {
1462
 
      my $numbytes = $client->read($buf, (1024 * 64) + $red, $red);
1463
 
      if (!defined $numbytes) {
1464
 
        die "read of zlib data failed: $!";
1465
 
        return -1;
1466
 
      }
1467
 
      last if $numbytes == 0;
1468
 
      $red += $numbytes;
1469
 
    }
1470
 
 
1471
 
    if ($red > $expected_length) {
1472
 
      warn "hmm, zlib read $red > expected_length $expected_length";
1473
 
      substr ($buf, $expected_length) = '';
1474
 
    }
1475
 
 
1476
 
    ($out, $status) = $zlib->inflate($buf);
1477
 
    if ($status != Compress::Zlib::Z_STREAM_END()) {
1478
 
      die "failed to find end of zlib stream";
1479
 
    }
1480
 
  };
1481
 
 
1482
 
  if ($@) { 
1483
 
    service_unavailable_error("zlib: $@");
1484
 
    return -1;
1485
 
  }
1486
 
 
1487
 
  $actual_length = length($out);
1488
 
 
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;
1493
 
}
1494
 
 
1495
 
sub parse_msgids {
1496
 
  my ($mail) = @_;
1497
 
 
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)) {
1502
 
    if ( $$id ) {
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)
1511
 
    }
1512
 
  }
1513
 
  return ($msgid, $rmsgid);
1514
 
}
1515
 
 
1516
 
sub check {
1517
 
  my ( $method, $version, $start_time, $remote_hostname, $remote_hostaddr ) = @_;
1518
 
  local ($_);
1519
 
  my $expected_length;
1520
 
  my $compress_zlib;
1521
 
 
1522
 
  # used to ensure we don't accidentally fork (bug 4370)
1523
 
  my $starting_self_pid = $$;
1524
 
 
1525
 
  # Protocol version 1.0 and greater may have "User:" and
1526
 
  # "Content-length:" headers.  But they're not required.
1527
 
 
1528
 
  if ( $version > 1.0 ) {
1529
 
    my $hdrs = {};
1530
 
 
1531
 
    return 0 unless (parse_headers($hdrs, $client));
1532
 
 
1533
 
    $expected_length = $hdrs->{expected_length};
1534
 
    $compress_zlib = $hdrs->{compress_zlib};
1535
 
  }
1536
 
 
1537
 
  return 0 unless do_user_handling();
1538
 
  if ($> == 0 && !am_running_on_windows()) {
1539
 
        die "spamd: still running as root! dying";
1540
 
  }
1541
 
 
1542
 
  my $resp = "EX_OK";
1543
 
 
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
1548
 
 
1549
 
  if ($compress_zlib) {
1550
 
    $expected_length = $actual_length;  # previously it was the gzipped length
1551
 
  }
1552
 
 
1553
 
  # attempt to fetch the message ids
1554
 
  my ($msgid, $rmsgid) = parse_msgids($mail);
1555
 
 
1556
 
  $msgid        ||= "(unknown)";
1557
 
  $current_user ||= "(unknown)";
1558
 
  $current_msgid = $msgid;      # for the SIGUSR2 backtrace
1559
 
  info("spamd: " . ($method eq 'PROCESS' ? "processing" : "checking")
1560
 
       . " message $msgid"
1561
 
       . ( $rmsgid ? " aka $rmsgid" : "" )
1562
 
       . " for ${current_user}:$>");
1563
 
 
1564
 
  # Check length if we're supposed to.
1565
 
  if (defined $expected_length && $actual_length != $expected_length) {
1566
 
    protocol_error(
1567
 
      "(Content-Length mismatch: Expected $expected_length bytes, got $actual_length bytes)"
1568
 
    );
1569
 
    $mail->finish();
1570
 
    return 0;
1571
 
  }
1572
 
 
1573
 
  # Go ahead and check the message
1574
 
  $spamtest->init(1);
1575
 
  my $status = Mail::SpamAssassin::PerMsgStatus->new($spamtest, $mail);
1576
 
  $status->check();
1577
 
 
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 );
1580
 
 
1581
 
  my $response_spam_status = "";
1582
 
  my $was_it_spam;
1583
 
  if ( $status->is_spam ) {
1584
 
    $response_spam_status = $method eq "REPORT_IFSPAM" ? "Yes" : "True";
1585
 
    $was_it_spam = 'identified spam';
1586
 
  }
1587
 
  else {
1588
 
    $response_spam_status = $method eq "REPORT_IFSPAM" ? "No" : "False";
1589
 
    $was_it_spam = 'clean message';
1590
 
  }
1591
 
 
1592
 
  my $spamhdr = "Spam: $response_spam_status ; $msg_score / $msg_threshold";
1593
 
 
1594
 
  if ( $method eq 'PROCESS' || $method eq 'HEADERS' ) {
1595
 
 
1596
 
    $status->set_tag('REMOTEHOSTNAME', $remote_hostname);
1597
 
    $status->set_tag('REMOTEHOSTADDR', $remote_hostaddr);
1598
 
 
1599
 
    # Build the message to send back and measure it
1600
 
    my $msg_resp        = $status->rewrite_mail();
1601
 
 
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;
1605
 
    }
1606
 
 
1607
 
    my $msg_resp_length = length($msg_resp);
1608
 
 
1609
 
    if ( $version >= 1.3 )    # Spamc protocol 1.3 means multi hdrs are OK
1610
 
    {
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" .
1613
 
        $msg_resp );
1614
 
    }
1615
 
    elsif (
1616
 
      $version >= 1.2 )    # Spamc protocol 1.2 means it accepts content-length
1617
 
    {
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 );
1620
 
    }
1621
 
    else                   # Earlier than 1.2 didn't accept content-length
1622
 
    {
1623
 
      syswrite_full_buffer( $client, "SPAMD/1.0 $resphash{$resp} $resp\r\n" . $msg_resp );
1624
 
    }
1625
 
  }
1626
 
  else                     # $method eq 'CHECK' et al
1627
 
  {
1628
 
    syswrite_full_buffer( $client, "SPAMD/1.1 $resphash{$resp} $resp\r\n" );
1629
 
 
1630
 
    if ( $method eq "CHECK" ) {
1631
 
      syswrite( $client, "$spamhdr\r\n\r\n" );
1632
 
    }
1633
 
    else {
1634
 
      my $msg_resp = '';
1635
 
 
1636
 
      if ( $method eq "REPORT"
1637
 
        or ( $method eq "REPORT_IFSPAM" and $status->is_spam ) )
1638
 
      {
1639
 
        $msg_resp = $status->get_report;
1640
 
      }
1641
 
      elsif ( $method eq "REPORT_IFSPAM" ) {
1642
 
 
1643
 
        # message is ham, $msg_resp remains empty
1644
 
      }
1645
 
      elsif ( $method eq "SYMBOLS" ) {
1646
 
        $msg_resp = $status->get_names_of_tests_hit;
1647
 
        $msg_resp .= "\r\n" if ( $version < 1.3 );
1648
 
      }
1649
 
      else {
1650
 
        die "spamd: unknown method $method";
1651
 
      }
1652
 
 
1653
 
      if ( $version >= 1.3 )    # Spamc protocol > 1.2 means multi hdrs are OK
1654
 
      {
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 );
1659
 
      }
1660
 
      else {
1661
 
        syswrite_full_buffer( $client, $spamhdr . "\r\n\r\n" . $msg_resp );
1662
 
      }
1663
 
    }
1664
 
  }
1665
 
 
1666
 
  my $scantime = sprintf( "%.1f", time - $start_time );
1667
 
 
1668
 
  info("spamd: $was_it_spam ($msg_score/$msg_threshold) for $current_user:$> in"
1669
 
       . " $scantime seconds, $actual_length bytes." );
1670
 
 
1671
 
  # add a summary "result:" line, based on mass-check format
1672
 
  my @extra;
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);
1678
 
 
1679
 
  {
1680
 
    my $safe = $msgid; $safe =~ s/[\x00-\x20\s,]/_/gs; push(@extra, "mid=$safe");
1681
 
  }
1682
 
  if ($rmsgid) {
1683
 
    my $safe = $rmsgid; $safe =~ s/[\x00-\x20\s,]/_/gs; push(@extra, "rmid=$safe");
1684
 
  }
1685
 
  if (defined $status->{bayes_score}) {
1686
 
    push(@extra, "bayes=".sprintf("%06f", $status->{bayes_score}));
1687
 
  }
1688
 
  push(@extra, "autolearn=".$status->get_autolearn_status());
1689
 
  push(@extra, $status->get_spamd_result_log_items());
1690
 
 
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())));
1694
 
 
1695
 
  my $log = sprintf("spamd: result: %s %2d - %s %s", $yorn, $score,
1696
 
               $tests, join(",", @extra));
1697
 
  info($log);
1698
 
 
1699
 
  # bug 3808: log scan results to any listening plugins, too
1700
 
  $spamtest->call_plugins("log_scan_result", { result => $log });
1701
 
 
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
1705
 
  # sync/expire run.
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();
1710
 
  }
1711
 
 
1712
 
  $status->finish();    # added by jm to allow GC'ing
1713
 
  $mail->finish();
1714
 
 
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
1719
 
  }
1720
 
 
1721
 
  return 1;
1722
 
}
1723
 
 
1724
 
sub dotell {
1725
 
  my ($method, $version, $start_time, $remote_hostname, $remote_hostaddr) = @_;
1726
 
  local ($_);
1727
 
 
1728
 
  my $hdrs = {};
1729
 
 
1730
 
  return 0 unless (parse_headers($hdrs, $client));
1731
 
 
1732
 
  my $expected_length = $hdrs->{expected_length};
1733
 
  my $compress_zlib = $hdrs->{compress_zlib};
1734
 
 
1735
 
  return 0 unless do_user_handling();
1736
 
  if ($> == 0 && !am_running_on_windows()) {
1737
 
        die "spamd: still running as root! dying";
1738
 
  }
1739
 
 
1740
 
  if (!$opt{tell}) {
1741
 
    service_unavailable_error("TELL commands are not enabled, set the --allow-tell switch.");
1742
 
    return 0;
1743
 
  }
1744
 
 
1745
 
  if ($hdrs->{set_local} && $hdrs->{remove_local}) {
1746
 
    protocol_error("Unable to set local and remove local in the same operation.");
1747
 
    return 0;
1748
 
  }
1749
 
 
1750
 
  if ($hdrs->{set_remote} && $hdrs->{remove_remote}) {
1751
 
    protocol_error("Unable to set remote and remove remote in the same operation.");
1752
 
    return 0;
1753
 
  }
1754
 
 
1755
 
  if ($opt{'sql-config'} && !defined($current_user)) {
1756
 
    unless (handle_user_sql('nobody')) {
1757
 
      service_unavailable_error("Error fetching user preferences via SQL");
1758
 
      return 0;
1759
 
    }
1760
 
  }
1761
 
 
1762
 
  if ($opt{'ldap-config'} && !defined($current_user)) {
1763
 
    handle_user_ldap('nobody');
1764
 
  }
1765
 
 
1766
 
  my $resp = "EX_OK";
1767
 
 
1768
 
  # generate mail object from input
1769
 
  my($mail, $actual_length) =
1770
 
    parse_body($client, $expected_length, $compress_zlib, $start_time);
1771
 
 
1772
 
  return 0 unless defined($mail);       # error
1773
 
 
1774
 
  if ($compress_zlib) {
1775
 
    $expected_length = $actual_length;  # previously it was the gzipped length
1776
 
  }
1777
 
 
1778
 
  if ( $mail->get_header("X-Spam-Checker-Version") ) {
1779
 
    my $new_mail = $spamtest->parse($spamtest->remove_spamassassin_markup($mail), 1);
1780
 
    $mail->finish();
1781
 
    $mail = $new_mail;
1782
 
  }
1783
 
 
1784
 
  # attempt to fetch the message ids
1785
 
  my ($msgid, $rmsgid) = parse_msgids($mail);
1786
 
 
1787
 
  $msgid        ||= "(unknown)";
1788
 
  $current_user ||= "(unknown)";
1789
 
 
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)");
1793
 
    $mail->finish();
1794
 
    return 0;
1795
 
  }
1796
 
 
1797
 
  my @did_set;
1798
 
  my @did_remove;
1799
 
 
1800
 
  if ($hdrs->{set_local}) {
1801
 
    my $status = $spamtest->learn($mail, undef, ($hdrs->{message_class} eq 'spam' ? 1 : 0), 0);
1802
 
 
1803
 
    push(@did_set, 'local') if ($status->did_learn());
1804
 
    $status->finish();
1805
 
  }
1806
 
 
1807
 
  if ($hdrs->{remove_local}) {
1808
 
    my $status = $spamtest->learn($mail, undef, undef, 1);
1809
 
 
1810
 
    push(@did_remove, 'local') if ($status->did_learn());
1811
 
    $status->finish();
1812
 
  }
1813
 
 
1814
 
  if ($hdrs->{set_remote}) {
1815
 
    require Mail::SpamAssassin::Reporter;
1816
 
    my $msgrpt = Mail::SpamAssassin::Reporter->new($spamtest, $mail);
1817
 
 
1818
 
    push(@did_set, 'remote') if ($msgrpt->report());
1819
 
  }
1820
 
 
1821
 
  if ($hdrs->{remove_remote}) {
1822
 
    require Mail::SpamAssassin::Reporter;
1823
 
    my $msgrpt = Mail::SpamAssassin::Reporter->new($spamtest, $mail);
1824
 
 
1825
 
    push(@did_remove, 'remote') if ($msgrpt->revoke());
1826
 
  }
1827
 
 
1828
 
  my $hdr = "";
1829
 
  my $info_str;
1830
 
 
1831
 
  if (scalar(@did_set)) {
1832
 
    $hdr .= "DidSet: " . join(',', @did_set) . "\r\n";
1833
 
    $info_str .= " Setting " . join(',', @did_set) . " ";
1834
 
  }
1835
 
 
1836
 
  if (scalar(@did_remove)) {
1837
 
    $hdr .= "DidRemove: " . join(',', @did_remove) . "\r\n";
1838
 
    $info_str .= " Removing " . join(',', @did_remove) . " ";
1839
 
  }
1840
 
 
1841
 
  if (!$info_str) {
1842
 
    $info_str = " Did nothing ";
1843
 
  }
1844
 
 
1845
 
  print $client "SPAMD/1.1 $resphash{$resp} $resp\r\n",
1846
 
    $hdr . "\r\n\r\n";
1847
 
 
1848
 
  my $scantime = sprintf( "%.1f", time - $start_time );
1849
 
 
1850
 
  info("spamd: Tell:${info_str}for $current_user:$> in"
1851
 
       . " $scantime seconds, $actual_length bytes");
1852
 
 
1853
 
  $mail->finish();
1854
 
  return 1;
1855
 
}
1856
 
 
1857
 
sub doskip_or_ping {
1858
 
  my ($method, $version, $start_time, $remote_hostname, $remote_hostaddr) = @_;
1859
 
 
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
1863
 
    # Fixes Bug 6187.
1864
 
 
1865
 
    my $hdrs = {};
1866
 
    return 0 unless (parse_headers($hdrs, $client));
1867
 
  }
1868
 
 
1869
 
  if ($method eq 'PING') {
1870
 
    print $client "SPAMD/1.5 $resphash{EX_OK} PONG\r\n";
1871
 
  }
1872
 
 
1873
 
  return 1;
1874
 
}
1875
 
 
1876
 
###########################################################################
1877
 
 
1878
 
sub do_user_handling {
1879
 
  if ($setuid_to_user && $> == 0) {
1880
 
    handle_setuid_to_user();
1881
 
  }
1882
 
 
1883
 
  if ( $opt{'sql-config'} && !defined($current_user) ) {
1884
 
    unless ( handle_user_sql('nobody') ) {
1885
 
      service_unavailable_error("Error fetching user preferences via SQL");
1886
 
      return 0;
1887
 
    }
1888
 
  }
1889
 
 
1890
 
  if ( $opt{'ldap-config'} && !defined($current_user) ) {
1891
 
    handle_user_ldap('nobody');
1892
 
  }
1893
 
 
1894
 
  dbg ("spamd: running as uid $>");
1895
 
  return 1;
1896
 
}
1897
 
 
1898
 
# generalised header parser.  
1899
 
sub parse_headers {
1900
 
  my ($hdrs, $client) = @_;
1901
 
 
1902
 
  my $got_user_header;
1903
 
 
1904
 
  # max 255 headers
1905
 
  for my $hcount ( 0 .. 255 ) {
1906
 
    my $line = $client->getline;
1907
 
 
1908
 
    unless (defined $line) {
1909
 
      protocol_error("(EOF during headers)");
1910
 
      return 0;
1911
 
    }
1912
 
    $line =~ s/\r\n$//;
1913
 
 
1914
 
    if (!length $line) {    # end of headers
1915
 
      if (!$got_user_header && $opt{'auth-ident'}) {
1916
 
        service_unavailable_error('User header required');
1917
 
        return 0;
1918
 
      }
1919
 
      return 1;
1920
 
    }
1921
 
 
1922
 
    my ($header, $value) = split (/:\s*/, $line, 2);
1923
 
    unless (defined $value) {
1924
 
      protocol_error("(header not in 'Name: value' format)");
1925
 
      return 0;
1926
 
    }
1927
 
 
1928
 
    if ($header eq 'Content-length') {
1929
 
      return 0 unless got_clen_header($hdrs, $header, $value);
1930
 
    }
1931
 
    elsif ($header eq 'User') {
1932
 
      return 0 unless got_user_header($hdrs, $header, $value);
1933
 
      $got_user_header++;
1934
 
    }
1935
 
    elsif ($header eq 'Message-class') {
1936
 
      return 0 unless got_message_class_header($hdrs, $header, $value);
1937
 
    }
1938
 
    elsif ($header eq 'Set') {
1939
 
      return 0 unless got_set_header($hdrs, $header, $value);
1940
 
    }
1941
 
    elsif ($header eq 'Remove') {
1942
 
      return 0 unless got_remove_header($hdrs, $header, $value);
1943
 
    }
1944
 
    elsif ($header eq 'Compress') {
1945
 
      return 0 unless &got_compress_header($hdrs, $header, $value);
1946
 
    }
1947
 
  }
1948
 
 
1949
 
  # avoid too-many-headers DOS attack
1950
 
  protocol_error("(too many headers)");
1951
 
  return 0;
1952
 
}
1953
 
 
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.)
1962
 
#
1963
 
sub got_user_header {
1964
 
  my ( $client, $header, $value ) = @_;
1965
 
 
1966
 
  if ( $value !~ /^([\x20-\xFF]*)$/ ) {
1967
 
    protocol_error("(User header contains control chars)");
1968
 
    return 0;
1969
 
  }
1970
 
 
1971
 
  $current_user = $1;
1972
 
  if ($opt{'auth-ident'} && !auth_ident($current_user)) {
1973
 
    return 0;
1974
 
  }
1975
 
 
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");
1980
 
        return 0;
1981
 
      }
1982
 
    }
1983
 
    elsif ( $opt{'ldap-config'} ) {
1984
 
      handle_user_ldap($current_user);
1985
 
    }
1986
 
    elsif ( $opt{'virtual-config-dir'} ) {
1987
 
      handle_virtual_config_dir($current_user);
1988
 
    }
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");
1992
 
        return 0;
1993
 
      }
1994
 
      $setuid_to_user = 1;    #to benefit from any paranoia.
1995
 
    }
1996
 
    elsif ( $opt{'setuid-with-ldap'} ) {
1997
 
      handle_user_setuid_with_ldap($current_user);
1998
 
      $setuid_to_user = 1;    # as above
1999
 
    }
2000
 
    else {
2001
 
      handle_user_setuid_basic($current_user);
2002
 
    }
2003
 
  }
2004
 
  else {
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");
2009
 
        return 0;
2010
 
      }
2011
 
    }
2012
 
  }
2013
 
  return 1;
2014
 
}
2015
 
 
2016
 
sub got_clen_header {
2017
 
  my ( $hdrs, $header, $value ) = @_;
2018
 
  if ( $value !~ /^(\d*)$/ ) {
2019
 
    protocol_error("(Content-Length contains non-numeric bytes)");
2020
 
    return 0;
2021
 
  }
2022
 
  $hdrs->{expected_length} = $1;
2023
 
  return 1;
2024
 
}
2025
 
 
2026
 
sub got_message_class_header {
2027
 
  my ($hdrs, $header, $value) = @_;
2028
 
 
2029
 
  unless (lc($value) ne 'spam' || lc($value) ne 'ham') {
2030
 
    protocol_error("(Message-class header contains invalid class)");
2031
 
    return 0;
2032
 
  }
2033
 
  $hdrs->{message_class} = $value;
2034
 
 
2035
 
  return 1;
2036
 
}
2037
 
 
2038
 
sub got_set_header {
2039
 
  my ($hdrs, $header, $value) = @_;
2040
 
 
2041
 
  $hdrs->{set_local} = 0;
2042
 
  $hdrs->{set_remote} = 0;
2043
 
 
2044
 
  if ($value =~ /local/i) {
2045
 
    $hdrs->{set_local} = 1;
2046
 
  }
2047
 
 
2048
 
  if ($value =~ /remote/i) {
2049
 
    $hdrs->{set_remote} = 1;
2050
 
  }
2051
 
 
2052
 
  return 1;
2053
 
}
2054
 
 
2055
 
sub got_remove_header {
2056
 
  my ($hdrs, $header, $value) = @_;
2057
 
 
2058
 
  $hdrs->{remove_local} = 0;
2059
 
  $hdrs->{remove_remote} = 0;
2060
 
 
2061
 
  if ($value =~ /local/i) {
2062
 
    $hdrs->{remove_local} = 1;
2063
 
  }
2064
 
 
2065
 
  if ($value =~ /remote/i) {
2066
 
    $hdrs->{remove_remote} = 1;
2067
 
  }
2068
 
 
2069
 
  return 1;
2070
 
}
2071
 
 
2072
 
sub got_compress_header {
2073
 
  my ($hdrs, $header, $value) = @_;
2074
 
 
2075
 
  if ($value =~ /zlib/i) {
2076
 
    eval { require Compress::Zlib; };
2077
 
    if ($@) {
2078
 
      protocol_error("(compression not supported, Compress::Zlib not installed: $@)");
2079
 
      return 0;
2080
 
    }
2081
 
    $hdrs->{compress_zlib} = 1;
2082
 
  }
2083
 
  else {
2084
 
    protocol_error("(compression type not supported)");
2085
 
    return 0;
2086
 
  }
2087
 
 
2088
 
  return 1;
2089
 
}
2090
 
 
2091
 
sub protocol_error {
2092
 
  my ($err) = @_;
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");
2096
 
}
2097
 
 
2098
 
sub service_unavailable_error {
2099
 
  my ($err) = @_;
2100
 
  my $resp = "EX_UNAVAILABLE";
2101
 
  syswrite($client,
2102
 
           "SPAMD/1.0 $resphash{$resp} Service Unavailable: $err\r\n");
2103
 
  warn("spamd: service unavailable: $err\n");
2104
 
}
2105
 
 
2106
 
sub service_timeout {
2107
 
  my ($err) = @_;
2108
 
  my $resp = "EX_TIMEOUT";
2109
 
  print $client "SPAMD/1.0 $resphash{$resp} Timeout: $err\r\n";
2110
 
  warn("spamd: timeout: $err\n");
2111
 
}
2112
 
 
2113
 
###########################################################################
2114
 
 
2115
 
sub auth_ident {
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)" );
2123
 
    return 0;
2124
 
  }
2125
 
  return 1;
2126
 
}
2127
 
 
2128
 
sub handle_user_setuid_basic {
2129
 
  my $username = shift;
2130
 
 
2131
 
  # If $opt{'username'} in use, then look up userinfo for that uid;
2132
 
  # otherwise use what was passed via $username
2133
 
  #
2134
 
  my $suidto = $username;
2135
 
  if ( $opt{'username'} ) {
2136
 
    $suidto = $opt{'username'};
2137
 
  }
2138
 
  my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $suiddir, $etc) =
2139
 
      am_running_on_windows() ? ('nobody') : getpwnam($suidto);
2140
 
 
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
2147
 
      info($errmsg);
2148
 
      return 0;
2149
 
  }
2150
 
 
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");
2157
 
    }
2158
 
    else {
2159
 
      info("spamd: setuid to $suidto succeeded");
2160
 
    }
2161
 
  }
2162
 
 
2163
 
  my $userdir;
2164
 
 
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}
2169
 
 
2170
 
    if ($prefsfrom eq $suidto) {
2171
 
      $userdir = $suiddir;  # reuse the already-looked-up info, tainted
2172
 
    } elsif ( $opt{'vpopmail'} ) {
2173
 
      #
2174
 
      # If vpopmail config enabled then set $userdir to virtual homedir
2175
 
      #
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`;
2181
 
      if ($? == 0) {
2182
 
        chomp($userdir);
2183
 
      } else {
2184
 
        $userdir = handle_user_vpopmail($username_untainted,$vpopdir);
2185
 
      }
2186
 
    } else {
2187
 
      $userdir = (getpwnam($prefsfrom))[7];
2188
 
    }
2189
 
 
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
2197
 
        info($errmsg);
2198
 
        return 0;
2199
 
    }
2200
 
  }
2201
 
 
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);
2205
 
}
2206
 
 
2207
 
sub handle_user_vpopmail {
2208
 
  #
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
2212
 
  #
2213
 
  my $username = shift;
2214
 
  my $vpopdir = shift;
2215
 
  my $userdir;
2216
 
  my $vpoptimeout = 5;
2217
 
  my $vptimer = Mail::SpamAssassin::Timeout->new({ secs => $vpoptimeout });
2218
 
 
2219
 
  $vptimer->run(sub {
2220
 
    my $vpopusername = $username;
2221
 
    my @aliases = split(/\n/, `$vpopdir/bin/valias \Q$vpopusername\E`);
2222
 
    while (@aliases) {
2223
 
      my $vpopusername_tainted = shift(@aliases);
2224
 
      local $1;
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);
2237
 
          } else {
2238
 
            unshift(@aliases,
2239
 
                    split(/\n/, `$vpopdir/bin/valias \Q$vpopusername\E`));
2240
 
          }
2241
 
        }
2242
 
        last if defined $userdir;
2243
 
      }
2244
 
    }
2245
 
  });
2246
 
 
2247
 
  if ($vptimer->timed_out()) {
2248
 
    dbg("spamd: timed out resolving vpopmail user/alias '%s'", $username);
2249
 
    undef $userdir;
2250
 
  } elsif (!defined($userdir)) {
2251
 
    dbg("spamd: failed to resolve vpopmail user/alias '%s'", $username);
2252
 
  } else {
2253
 
    chomp($userdir);
2254
 
  }
2255
 
  return $userdir;
2256
 
}
2257
 
 
2258
 
sub handle_user_set_user_prefs {
2259
 
  my ($dir, $username) = @_;
2260
 
 
2261
 
  # don't do this if we weren't passed a directory
2262
 
  if ($dir) {
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);
2266
 
  }
2267
 
 
2268
 
  # signal_user_changed will ignore undef user_dirs, so this is ok
2269
 
  $spamtest->signal_user_changed(
2270
 
    {
2271
 
      username => $username,
2272
 
      user_dir => $dir
2273
 
    }
2274
 
  );
2275
 
 
2276
 
  return 1;
2277
 
}
2278
 
 
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) = @_;
2283
 
 
2284
 
  my $dir = $opt{'virtual-config-dir'};
2285
 
  my $userdir;
2286
 
  my $prefsfile;
2287
 
 
2288
 
  if ( defined $dir ) {
2289
 
    my $safename = $username;
2290
 
    $safename =~ s/[^-A-Za-z0-9\+_\.\,\@\=]/_/gs;
2291
 
    my $localpart = '';
2292
 
    my $domain    = '';
2293
 
    if ( $safename =~ /^(.*)\@(.*)$/ ) { $localpart = $1; $domain = $2; }
2294
 
 
2295
 
    $dir =~ s/\%u/${safename}/g;
2296
 
    $dir =~ s/\%l/${localpart}/g;
2297
 
    $dir =~ s/\%d/${domain}/g;
2298
 
    $dir =~ s/\%\%/\%/g;
2299
 
 
2300
 
    $userdir   = $dir;
2301
 
    $prefsfile = $dir . '/user_prefs';
2302
 
 
2303
 
    # Log that the default configuration is being used for a user.
2304
 
    info("spamd: using default config for $username: $prefsfile");
2305
 
  }
2306
 
 
2307
 
  if ( -f $prefsfile ) {
2308
 
 
2309
 
    # Found a config, load it.
2310
 
    $spamtest->read_scoreonly_config($prefsfile);
2311
 
  }
2312
 
 
2313
 
  # assume that $userdir will be a writable directory we can
2314
 
  # use for Bayes dbs etc.
2315
 
  $spamtest->signal_user_changed(
2316
 
    {
2317
 
      username => $username,
2318
 
      userstate_dir => $userdir,
2319
 
      user_dir => $userdir
2320
 
    }
2321
 
  );
2322
 
  return 1;
2323
 
}
2324
 
 
2325
 
sub handle_user_sql {
2326
 
  my ($username) = @_;
2327
 
 
2328
 
  unless ( $spamtest->load_scoreonly_sql($username) ) {
2329
 
    return 0;
2330
 
  }
2331
 
  $spamtest->signal_user_changed(
2332
 
    {
2333
 
      username => $username,
2334
 
      user_dir => undef
2335
 
    }
2336
 
  );
2337
 
  return 1;
2338
 
}
2339
 
 
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(
2345
 
    {
2346
 
      username => $username,
2347
 
      user_dir => undef
2348
 
    }
2349
 
  );
2350
 
  return 1;
2351
 
}
2352
 
 
2353
 
sub handle_user_setuid_with_sql {
2354
 
  my $username = shift;
2355
 
 
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)
2360
 
  #
2361
 
  my ($name, $pwd, $uid, $gid, $quota, $comment, $gcos, $dir, $etc) =
2362
 
      getpwnam(untaint_var($username));
2363
 
 
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");
2369
 
    return 0;
2370
 
  }
2371
 
 
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");
2378
 
    }
2379
 
    else {
2380
 
      info("spamd: setuid to $username succeeded, reading scores from SQL");
2381
 
    }
2382
 
  }
2383
 
 
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");
2389
 
    }
2390
 
    else {
2391
 
      info("spamd: failed to create $spam_conf_dir for $username");
2392
 
    }
2393
 
  }
2394
 
 
2395
 
  unless ($spamtest->load_scoreonly_sql($username)) {
2396
 
    return 0;
2397
 
  }
2398
 
 
2399
 
  $spamtest->signal_user_changed( { username => $username } );
2400
 
  return 1;
2401
 
}
2402
 
 
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);
2407
 
 
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");
2413
 
    return 0;
2414
 
  }
2415
 
 
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");
2422
 
    }
2423
 
    else {
2424
 
      info("spamd: setuid to $username succeeded, reading scores from LDAP");
2425
 
    }
2426
 
  }
2427
 
 
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");
2432
 
    }
2433
 
    else {
2434
 
      info("spamd: failed to create $spam_conf_dir for $username");
2435
 
    }
2436
 
  }
2437
 
 
2438
 
  $spamtest->load_scoreonly_ldap($username);
2439
 
 
2440
 
  $spamtest->signal_user_changed( { username => $username } );
2441
 
  return 1;
2442
 
}
2443
 
 
2444
 
sub create_default_cf_if_needed {
2445
 
  my ( $cf_file, $username, $userdir ) = @_;
2446
 
 
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");
2450
 
 
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 );
2454
 
 
2455
 
    if (! -r $cf_file) {
2456
 
      info("spamd: failed to create readable default_prefs: $cf_file");
2457
 
    }
2458
 
  }
2459
 
}
2460
 
 
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';
2468
 
}
2469
 
 
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!
2474
 
  my $h = 'DEFAULT';
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");
2479
 
               kill QUIT => 0;
2480
 
               exit 0;
2481
 
             };
2482
 
  }
2483
 
  $SIG{$_} = $h  foreach qw(HUP INT TERM CHLD);
2484
 
  $SIG{PIPE} = 'IGNORE';
2485
 
}
2486
 
 
2487
 
sub kill_handler {
2488
 
  my ($sig) = @_;
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;
2493
 
 
2494
 
  if (defined($opt{'pidfile'})) {
2495
 
    unlink($opt{'pidfile'}) || warn "spamd: cannot unlink $opt{'pidfile'}: $!\n";
2496
 
  }
2497
 
 
2498
 
  # the UNIX domain socket
2499
 
  if (defined($opt{'socketpath'})) {
2500
 
    unlink($opt{'socketpath'}) || warn "spamd: cannot unlink $opt{'socketpath'}: $!\n";
2501
 
  }
2502
 
 
2503
 
  $SIG{CHLD} = 'DEFAULT';    # we're going to kill our children
2504
 
  if ($scaling) {
2505
 
    $scaling->set_exiting_flag(); # don't start new ones
2506
 
  }
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]: $!");
2511
 
  }
2512
 
  exit 0;
2513
 
}
2514
 
 
2515
 
# takes care of dead children
2516
 
sub child_handler {
2517
 
  my ($sig) = @_;
2518
 
 
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.
2521
 
 
2522
 
  # clean up any children which have exited
2523
 
  for (;;) {
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.
2528
 
    #
2529
 
    my $pid = waitpid(-1, WNOHANG);
2530
 
    last if !$pid || $pid == -1;
2531
 
    my $child_stat = $?;
2532
 
 
2533
 
    if (!defined $children{$pid}) {
2534
 
      # ignore this child; we didn't realise we'd forked it. bug 4237
2535
 
      next;
2536
 
    }
2537
 
 
2538
 
    # remove them from our child listing
2539
 
    delete $children{$pid};
2540
 
 
2541
 
    if ($scaling) {
2542
 
      $scaling->child_exited($pid);
2543
 
    } else {
2544
 
      my $sock = $backchannel->get_socket_for_child($pid);
2545
 
      if ($sock) { $sock->close(); }
2546
 
    }
2547
 
 
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));
2552
 
    }
2553
 
  }
2554
 
 
2555
 
  $SIG{CHLD} = \&child_handler;    # reset as necessary, should be at end
2556
 
}
2557
 
 
2558
 
sub restart_handler {
2559
 
  my ($sig) = @_;
2560
 
  info("spamd: server hit by SIG$sig, restarting");
2561
 
 
2562
 
  $SIG{CHLD} = 'DEFAULT';    # we're going to kill our children
2563
 
  if ($scaling) {
2564
 
    $scaling->set_exiting_flag(); # don't start new ones
2565
 
  }
2566
 
 
2567
 
  foreach (keys %children) {
2568
 
    kill 'INT' => $_;
2569
 
    my $pid = waitpid($_, 0);
2570
 
    my $child_stat = $pid > 0 ? $? : undef;
2571
 
    if ($scaling) {
2572
 
      $scaling->child_exited($pid);
2573
 
    }
2574
 
    info("spamd: child [%s] killed successfully: %s",
2575
 
         $pid, exit_status_str($child_stat,0));
2576
 
  }
2577
 
  %children = ();
2578
 
 
2579
 
  unless ( !$server_inet || $server_inet->eof ) {
2580
 
    $server_inet->shutdown(2);
2581
 
    $server_inet->close;
2582
 
    info("spamd: server INET socket closed");
2583
 
  }
2584
 
 
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";
2590
 
    }
2591
 
    info("spamd: server UNIX socket closed");
2592
 
  }
2593
 
 
2594
 
  unless ( !$server_ssl || $server_ssl->eof ) {
2595
 
    $server_ssl->shutdown(2);
2596
 
    $server_ssl->close;
2597
 
    info("spamd: server SSL socket closed");
2598
 
  }
2599
 
  $got_sighup = 1;
2600
 
}
2601
 
 
2602
 
sub backtrace_handler {
2603
 
  Carp::cluck("spamd: caught SIGUSR2 - dumping backtrace. ".
2604
 
        "most recent message: $current_msgid\n");
2605
 
}
2606
 
 
2607
 
my  $serverstarted = 0;
2608
 
 
2609
 
sub serverstarted {
2610
 
  $serverstarted = 1;
2611
 
}
2612
 
 
2613
 
sub daemonize {
2614
 
  # Pretty command line in ps
2615
 
  $0 = join (' ', $ORIG_ARG0, @ORIG_ARGV) unless would_log("dbg");
2616
 
 
2617
 
  # be a nice daemon and chdir to the root so we don't block any
2618
 
  # unmount attempts
2619
 
  chdir '/' or die "spamd: cannot chdir to /: $!\n";
2620
 
 
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";
2624
 
 
2625
 
  # Remove the stderr logger
2626
 
  Mail::SpamAssassin::Logger::remove('stderr');
2627
 
 
2628
 
  # Here we go...
2629
 
  $SIG{USR1} = \&serverstarted;
2630
 
  defined( my $pid = fork ) or die "spamd: cannot fork: $!\n";
2631
 
  if ($pid) {
2632
 
    my $child_stat;
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;
2637
 
         $retry--)
2638
 
    {
2639
 
      warn("waitpid failed: $waited $!")  if $waited;
2640
 
      sleep 1;
2641
 
      $waited = waitpid($pid, WNOHANG);
2642
 
      $child_stat = $?  if $waited > 0;
2643
 
    }
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;
2647
 
    exit;
2648
 
  }
2649
 
  delete  $SIG{USR1};
2650
 
  setsid or die "spamd: cannot start new session: $!\n";
2651
 
 
2652
 
  # Now we can redirect the errors, too.
2653
 
  open STDERR, '>&STDOUT' or die "spamd: cannot duplicate stdout: $!\n";
2654
 
 
2655
 
  dbg("spamd: successfully daemonized");
2656
 
}
2657
 
 
2658
 
sub set_allowed_ip {
2659
 
  foreach (@_) {
2660
 
    $allowed_nets->add_cidr($_) or die "spamd: aborting due to add_cidr error\n";
2661
 
  }
2662
 
}
2663
 
 
2664
 
sub ip_is_allowed {
2665
 
  $allowed_nets->contains_ip(@_);
2666
 
}
2667
 
 
2668
 
sub preload_modules_with_tmp_homedir {
2669
 
 
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();
2673
 
  if ( !$tmpdir ) {
2674
 
    die "spamd: cannot find writable tmp dir, set TMP or TMPDIR in environment";
2675
 
  }
2676
 
 
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'} );
2680
 
 
2681
 
  my $tmphome = File::Spec->catdir( $tmpdir, "spamd-$$-init" );
2682
 
  $tmphome = Mail::SpamAssassin::Util::untaint_file_path($tmphome);
2683
 
 
2684
 
  my $tmpsadir = File::Spec->catdir( $tmphome, ".spamassassin" );
2685
 
 
2686
 
  dbg("spamd: Preloading modules with HOME=$tmphome");
2687
 
 
2688
 
  # bug 5379: spamd won't start if the temp preloading dir exists;
2689
 
  # be sure to remove it just in case 
2690
 
  if (-d $tmpsadir) {
2691
 
    rmdir( $tmpsadir ) or die "spamd: $tmpsadir not empty: $!";
2692
 
  }
2693
 
  if (-d $tmphome) {
2694
 
    rmdir( $tmphome ) or die "spamd: $tmphome not empty: $!";
2695
 
  }
2696
 
  mkdir( $tmphome,  0700 ) or die "spamd: cannot create $tmphome: $!";
2697
 
  mkdir( $tmpsadir, 0700 ) or die "spamd: cannot create $tmpsadir: $!";
2698
 
  $ENV{HOME} = $tmphome;
2699
 
 
2700
 
  $spamtest->compile_now(0,1);  # ensure all modules etc. are loaded
2701
 
  $/ = "\n";                    # argh, Razor resets this!  Bad Razor!
2702
 
 
2703
 
  # now clean up the stuff we just created, and make us taint-safe
2704
 
  delete $ENV{HOME};
2705
 
 
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.
2708
 
  my $err;
2709
 
  foreach my $d ( ( $tmpsadir, $tmphome ) ) {
2710
 
    opendir( TMPDIR, $d ) or $err ||= "open $d: $!";
2711
 
    unless ($err) {
2712
 
      foreach my $f ( File::Spec->no_upwards( readdir(TMPDIR) ) ) {
2713
 
        $f =
2714
 
          Mail::SpamAssassin::Util::untaint_file_path(
2715
 
          File::Spec->catfile( $d, $f ) );
2716
 
        unlink($f) or $err ||= "remove $f: $!";
2717
 
      }
2718
 
      closedir(TMPDIR) or $err ||= "close $d: $!";
2719
 
    }
2720
 
    rmdir($d) or $err ||= "remove $d: $!";
2721
 
  }
2722
 
 
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";
2727
 
  }
2728
 
}
2729
 
 
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);
2737
 
  my $written = 0;
2738
 
  my $try = 0;
2739
 
 
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))
2745
 
          {
2746
 
              # an error that wasn't non-blocking I/O-related.  that's serious
2747
 
              return undef;
2748
 
          }
2749
 
          # errcode says to try again
2750
 
      }
2751
 
      else {
2752
 
 
2753
 
          if ($nbytes == 0) {
2754
 
              return $written;  # return early if no error but nothing was written
2755
 
          }
2756
 
 
2757
 
          $written += $nbytes;
2758
 
      }
2759
 
      $try++;
2760
 
  }
2761
 
 
2762
 
  return $written;      # it's complete, we can return
2763
 
}
2764
 
 
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;
2769
 
 
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;
2774
 
 
2775
 
  my $back_selector = $server_select_mask;
2776
 
  $backchannel->set_selector(\$back_selector);
2777
 
 
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;
2783
 
}
2784
 
 
2785
 
# do this in advance, since we want to minimize work when SIGHUP
2786
 
# is received
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")) {
2792
 
    my $l = <IN>;
2793
 
    close IN;
2794
 
    if ($l && $l =~ /^#!\s*(\S+)\s*.*?$/) {
2795
 
      $perl_from_hashbang_line = $1;
2796
 
    }
2797
 
  }
2798
 
}
2799
 
 
2800
 
sub do_sighup_restart {
2801
 
  if (defined($opt{'pidfile'})) {
2802
 
    unlink($opt{'pidfile'}) || warn "spamd: cannot unlink $opt{'pidfile'}: $!\n";
2803
 
  }
2804
 
 
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();
2809
 
  chdir($ORIG_CWD)
2810
 
    or die "spamd: restart failed: chdir failed: ${ORIG_CWD}: $!\n";
2811
 
 
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 );
2816
 
 
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 );
2821
 
  }
2822
 
 
2823
 
  warn "spamd: restarting using '" . join (' ', @execs) . "'\n";
2824
 
  exec @execs;
2825
 
 
2826
 
  # should not get past that...
2827
 
  die "spamd: restart failed: exec failed: " . join (' ', @execs) . ": $!\n";
2828
 
}
2829
 
 
2830
 
__DATA__
2831
 
 
2832
 
=head1 NAME
2833
 
 
2834
 
spamd - daemonized version of spamassassin
2835
 
 
2836
 
=head1 SYNOPSIS
2837
 
 
2838
 
spamd [options]
2839
 
 
2840
 
Options:
2841
 
 
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,
2863
 
                                   enables use of -H)
2864
 
 --ldap-config                     Enable LDAP config (needs -x)
2865
 
 --setuid-with-ldap                Enable LDAP config (needs -x,
2866
 
                                   enables use of -H)
2867
 
 --virtual-config-dir=dir          Enable pattern based Virtual configs
2868
 
                                   (needs -x)
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
2895
 
 
2896
 
=head1 DESCRIPTION
2897
 
 
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.
2901
 
 
2902
 
This is intended to be used alongside C<spamc>, a fast, low-overhead C client
2903
 
program.
2904
 
 
2905
 
See the README file in the C<spamd> directory of the SpamAssassin distribution
2906
 
for more details.
2907
 
 
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.
2911
 
 
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.
2917
 
 
2918
 
=head1 OPTIONS
2919
 
 
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.
2924
 
 
2925
 
=over 4
2926
 
 
2927
 
=item B<-l>, B<--allow-tell>
2928
 
 
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
2933
 
updated.
2934
 
 
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.)
2941
 
 
2942
 
=item B<-c>, B<--create-prefs>
2943
 
 
2944
 
Create user preferences files if they don't exist (default: don't).
2945
 
 
2946
 
=item B<-C> I<path>, B<--configpath>=I<path>
2947
 
 
2948
 
Use the specified path for locating the distributed configuration files.
2949
 
Ignore the default directories (usually C</usr/share/spamassassin> or similar).
2950
 
 
2951
 
=item B<--siteconfigpath>=I<path>
2952
 
 
2953
 
Use the specified path for locating site-specific configuration files.  Ignore
2954
 
the default directories (usually C</etc/spamassassin> or similar).
2955
 
 
2956
 
=item B<--cf='config line'>
2957
 
 
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.
2961
 
 
2962
 
=item B<-d>, B<--daemonize>
2963
 
 
2964
 
Detach from starting process and run in background (daemonize).
2965
 
 
2966
 
=item B<-h>, B<--help>
2967
 
 
2968
 
Print a brief help message, then exit without further action.
2969
 
 
2970
 
=item B<-V>, B<--version>
2971
 
 
2972
 
Print version information, then exit without further action.
2973
 
 
2974
 
=item B<-i> [I<ipaddress>], B<--listen-ip>[=I<ipaddress>], B<--ip-address>[=I<ipaddress>]
2975
 
 
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.
2980
 
 
2981
 
=item B<-p> I<port>, B<--port>=I<port>
2982
 
 
2983
 
Optionally specifies the port number for the server to listen on (default: 783).
2984
 
 
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>.
2990
 
 
2991
 
=item B<-q>, B<--sql-config>
2992
 
 
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
2996
 
database.
2997
 
 
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>.
3000
 
 
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.
3004
 
 
3005
 
=item B<--ldap-config>
3006
 
 
3007
 
Turn on LDAP lookups. This is completely analog to C<--sql-config>,
3008
 
only it is using an LDAP server.
3009
 
 
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.
3012
 
 
3013
 
=item B<-Q>, B<--setuid-with-sql>
3014
 
 
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.)
3019
 
 
3020
 
=item B<--setuid-with-ldap>
3021
 
 
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.
3025
 
 
3026
 
=item B<--virtual-config-dir>=I<pattern>
3027
 
 
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:
3031
 
 
3032
 
=over 4
3033
 
 
3034
 
=item %u -- replaced with the full name of the current user, as sent by spamc.
3035
 
 
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<@>
3038
 
sign.
3039
 
 
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<@>
3042
 
sign.
3043
 
 
3044
 
=item %% -- replaced with a single percent sign (%).
3045
 
 
3046
 
=back
3047
 
 
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.
3051
 
 
3052
 
The set of characters allowed in the virtual username for this path are
3053
 
restricted to:
3054
 
 
3055
 
        A-Z a-z 0-9 - + _ . , @ =
3056
 
 
3057
 
All others will be replaced by underscores (C<_>).
3058
 
 
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.
3063
 
 
3064
 
Note that this B<requires> that B<-x> is used, and cannot be combined with
3065
 
SQL- or LDAP-based configuration.
3066
 
 
3067
 
The pattern B<must> expand to an absolute directory when spamd is running
3068
 
daemonized (B<-d>).
3069
 
 
3070
 
Currently, use of this without B<-u> is not supported. This inhibits setuid.
3071
 
 
3072
 
=item B<-r> I<pidfile>, B<--pidfile>=I<pidfile>
3073
 
 
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.
3077
 
 
3078
 
=item B<-v>, B<--vpopmail>
3079
 
 
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.
3084
 
 
3085
 
Currently, use of this without B<-u> is not supported. This inhibits setuid.
3086
 
 
3087
 
=item B<-s> I<facility>, B<--syslog>=I<facility>
3088
 
 
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
3094
 
(used internally).
3095
 
 
3096
 
Examples:
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
3103
 
 
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.)
3107
 
 
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.
3111
 
 
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.)
3116
 
 
3117
 
=item B<--syslog-socket>=I<type>
3118
 
 
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.
3127
 
 
3128
 
A historical setting --syslog-socket=none is mapped to --syslog=stderr.
3129
 
 
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.
3133
 
 
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.
3138
 
 
3139
 
The socket types C<file> is used internally and should not be specified.
3140
 
Use the C<-s> switch instead.
3141
 
 
3142
 
=item B<--log-timestamp-fmt>=I<format>
3143
 
 
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' .
3155
 
 
3156
 
=item B<-u> I<username>, B<--username>=I<username>
3157
 
 
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
3160
 
as root.
3161
 
 
3162
 
Note: "--username=root" is not a valid option.  If specified, C<spamd> will
3163
 
exit with a fatal error on startup.
3164
 
 
3165
 
=item B<-g> I<groupname>, B<--groupname>=I<groupname>
3166
 
 
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.
3170
 
 
3171
 
=item B<-x>, B<--nouser-config>, B<--user-config>
3172
 
 
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>).
3176
 
 
3177
 
This option does not disable or otherwise influence the SQL, LDAP or
3178
 
Virtual Config Dir settings.
3179
 
 
3180
 
=item B<--auth-ident>
3181
 
 
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.
3190
 
 
3191
 
=item B<--ident-timeout>=I<timeout>
3192
 
 
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.
3197
 
 
3198
 
=item B<-A> I<host,...>, B<--allowed-ips>=I<host,...>
3199
 
 
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:
3206
 
 
3207
 
B<-A 10.11.12.13> -- only allow connections from C<10.11.12.13>.
3208
 
 
3209
 
B<-A 10.11.12.13,10.11.12.14> -- only allow connections from C<10.11.12.13> and
3210
 
C<10.11.12.14>.
3211
 
 
3212
 
B<-A 10.200.300.0/24> -- allow connections from any machine in the range
3213
 
C<10.200.300.*>.
3214
 
 
3215
 
B<-A 10.> -- allow connections from any machine in the range C<10.*.*.*>.
3216
 
 
3217
 
By default, connections are only accepted from localhost [127.0.0.1].
3218
 
 
3219
 
=item B<-D> [I<area,...>], B<--debug> [I<area,...>]
3220
 
 
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:
3225
 
 
3226
 
        spamassassin -D bayes,learn,dns
3227
 
 
3228
 
Higher priority informational messages that are suitable for logging in normal
3229
 
circumstances are available with an area of "info".
3230
 
 
3231
 
For more information about which areas (also known as channels) are available,
3232
 
please see the documentation at:
3233
 
 
3234
 
        C<http://wiki.apache.org/spamassassin/DebugChannels>
3235
 
 
3236
 
=item B< --ipv4only>, B<--ipv4-only>, B<--ipv4>
3237
 
 
3238
 
Do not use IPv6 for DNS tests. Use if the existing tests
3239
 
for IPv6 availability produce incorrect results or crashes.
3240
 
 
3241
 
=item B<-L>, B<--local>
3242
 
 
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)>.
3245
 
 
3246
 
=item B<-P>, B<--paranoid>
3247
 
 
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.
3250
 
 
3251
 
=item B<-m> I<number> , B<--max-children>=I<number>
3252
 
 
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.
3256
 
 
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>.
3260
 
 
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).
3263
 
 
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.
3267
 
 
3268
 
=item B<--min-children>=I<number>
3269
 
 
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
3272
 
increase this.
3273
 
 
3274
 
=item B<--min-spare>=I<number>
3275
 
 
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>.
3280
 
 
3281
 
=item B<--max-spare>=I<number>
3282
 
 
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
3286
 
is C<2>.
3287
 
 
3288
 
=item B<--max-conn-per-child>=I<number>
3289
 
 
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>.
3293
 
 
3294
 
=item B<--round-robin>
3295
 
 
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.
3303
 
 
3304
 
=item B<--timeout-tcp>=I<number>
3305
 
 
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.
3310
 
 
3311
 
=item B<--timeout-child>=I<number>
3312
 
 
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.
3316
 
 
3317
 
=item B<-H> I<directory>, B<--helper-home-dir>=I<directory>
3318
 
 
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.
3324
 
 
3325
 
=item B<--ssl>
3326
 
 
3327
 
Accept only SSL connections on the associated port.
3328
 
The B<IO::Socket::SSL> perl module must be installed.
3329
 
 
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>.
3335
 
 
3336
 
=item B<--ssl-port>=I<port>
3337
 
 
3338
 
Optionally specifies the port number for the server to listen on for
3339
 
SSL connections (default: whatever --port uses).  See B<--ssl> for
3340
 
more details.
3341
 
 
3342
 
=item B<--ssl-version>=I<sslversion>
3343
 
 
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>
3348
 
implies B<--ssl>.
3349
 
 
3350
 
=item B<--server-key> I<keyfile>
3351
 
 
3352
 
Specify the SSL key file to use for SSL connections.
3353
 
 
3354
 
=item B<--server-cert> I<certfile>
3355
 
 
3356
 
Specify the SSL certificate file to use for SSL connections.
3357
 
 
3358
 
=item B<--socketpath> I<pathname>
3359
 
 
3360
 
Listen on UNIX domain path I<pathname> instead of a TCP socket.
3361
 
 
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
3366
 
to the socket.
3367
 
 
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>
3370
 
explicitly.
3371
 
 
3372
 
=item B<--socketowner> I<name>
3373
 
 
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.
3378
 
 
3379
 
=item B<--socketgroup> I<name>
3380
 
 
3381
 
Set UNIX domain socket to be owned by the group named I<name>.  See
3382
 
C<--socketowner> for notes on ownership and permissions.
3383
 
 
3384
 
=item B<--socketmode> I<mode>
3385
 
 
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.
3389
 
 
3390
 
=back
3391
 
 
3392
 
=head1 SEE ALSO
3393
 
 
3394
 
spamc(1)
3395
 
spamassassin(1)
3396
 
Mail::SpamAssassin::Conf(3)
3397
 
Mail::SpamAssassin(3)
3398
 
 
3399
 
=head1 PREREQUISITES
3400
 
 
3401
 
C<Mail::SpamAssassin>
3402
 
 
3403
 
=head1 AUTHORS
3404
 
 
3405
 
The SpamAssassin(tm) Project (http://spamassassin.apache.org/)
3406
 
 
3407
 
=head1 LICENSE
3408
 
 
3409
 
SpamAssassin is distributed under the Apache License, Version 2.0, as
3410
 
described in the file C<LICENSE> included with the distribution.
3411
 
 
3412
 
=cut