main.c revision 1
2N/A * Copyright (c) 1998-2006, 2008, 2009, 2011 Sendmail, Inc. and its suppliers. 2N/A * All rights reserved. 2N/A * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 2N/A * Copyright (c) 1988, 1993 2N/A * The Regents of the University of California. All rights reserved. 2N/A * By using this file, you agree to the terms and conditions set 2N/A * forth in the LICENSE file which can be found at the top level of 2N/A * the sendmail distribution. 2N/A * Copyright 1996-2006 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A"@(#) Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.\n\ 2N/A@(#) All rights reserved.\n\ 2N/A@(#) Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ 2N/A@(#) Copyright (c) 1988, 1993\n\ 2N/A@(#) The Regents of the University of California. All rights reserved.\n\ 2N/A@(#) Copyright 1996-2006 Sun Microsystems, Inc. All rights reserved.\n\ 2N/A@(#) Use is subject to license terms.\n";
2N/A#
endif /* NETINET || NETINET6 */ 2N/A/* for getcfname() */ 2N/A "@(#)$Debug: no_persistent_restart - don't restart, log only $");
2N/A** SENDMAIL -- Post mail to a set of destinations. 2N/A** This is the basic mail router. All user mail programs should 2N/A** call this routine to actually deliver mail. Sendmail in 2N/A** turn calls a bunch of mail servers that do the real work of 2N/A** delivering the mail. 2N/A** See the associated documentation for details. 2N/A** Britton-Lee, Inc., purveyors of fine 2N/A** database computers (11/81 - 10/88). 2N/A** International Computer Science Institute 2N/A** InReference, Inc. (8/95 - 1/97). 2N/A** Sendmail, Inc. (1/98 - present). 2N/A** The support of my employers is gratefully acknowledged. 2N/A** Few of them (Britton-Lee in particular) have had 2N/A** anything to gain from my involvement in this project. 2N/A** Gregory Neil Shapiro, 2N/A** Worcester Polytechnic Institute (until 3/98). 2N/A** Sendmail, Inc. (3/98 - present). 2N/A** Sendmail, Inc. (12/98 - present). 2N/Astatic int MissingFds = 0;
/* bit map of fds missing on startup */ 2N/A#
endif /* NGROUPS_MAX */ 2N/A#
define PIDLEN 6 /* pid length for computing SyslogPrefixLen */ 2N/A#
define SL_FUDGE 10 /* fudge offset for SyslogPrefixLen */ 2N/A#
endif /* ! SL_FUDGE */ 2N/A#
define SLDLL 8 /* est. length of default syslog label */ 2N/A/* Some options are dangerous to allow users to use in non-submit mode */ "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
"WARNING: Ignoring submission mode -%c option with -q\n", \
bool auth =
true;
/* whether to set e_auth_param */ bool save_val;
/* to save some bool var. */ int cftype;
/* which cf file to use? */ /* install default exception handler */ /* set the default in/out channel so errors reported to screen */ ** Check to see if we reentered. ** This would normally happen if e_putheader or e_putbody ** were NULL when invoked. /* avoid null pointer dereferences */ /* Check if sendmail is running with extra privs */ /* get whatever .cf file is right for the opmode */ /* in 4.4BSD, the table can be huge; impose a reasonable limit */ ** Be sure we have enough file descriptors. ** But also be sure that 0, 1, & 2 are open. /* reset errno and fill_errno; the latter is used way down below */ #
endif /* ! SM_LOG_STR */ ** Seed the random number generator. ** Used for queue file names, picking a queue directory, and /* do machine-dependent initializations */ /* reset status from syserr() calls for missing file descriptors */ #
endif /* _FFR_LOCAL_DAEMON */ /* save initial group set for future checks */ /* drop group id privileges (RunAsUser not yet set) */ /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */ /* arrange to dump state on user-1 signal */ /* ignore user-1 signal */ /* initialize for setproctitle */ /* Handle any non-getoptable constructions. */ ** Do a quick prescan of the argument list. /* find initial opMode */ if (
strcmp(p,
"newaliases") == 0)
else if (
strcmp(p,
"mailq") == 0)
else if (
strcmp(p,
"smtpd") == 0)
else if (
strcmp(p,
"hoststat") == 0)
else if (
strcmp(p,
"purgestat") == 0)
#
define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:x"#
endif /* defined(__osf__) || defined(_AIX3) */#
define OPTIONS "A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"#
endif /* defined(sony_news) */#
define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:" /* Set to 0 to allow -b; need to check optarg before using it! */ case 'b':
/* operations mode */ #
endif /* _FFR_CHECKCONFIG */#
endif /* _FFR_LOCAL_DAEMON */ "Frozen configurations unsupported\n");
"Invalid operation mode %c\n",
syserr(
"-D file must be before -d");
case 'G':
/* relay (gateway) submission */ "option requires an argument -- '%c'",
/* just check if it is there */ /* Don't leak queue information via debug flags */ "WARNING: Can not use -d with -q. Disabling debugging.\n");
/* Sanitize the string */ /* set up the blank envelope */ ** Set default values for variables. ** These cannot be in initialized data space. ** if running non-set-user-ID binary as non-root, pretend sm_dprintf(
"Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
/* save command line arguments */ /* clear sendmail's environment */ ** restore any original TZ setting until TimeZoneSpec has been ** determined - or early log messages may get bogus time stamps /* XXX check for reasonable length? */ /* XXX check return code? */ /* prime the child environment */ ** Initialize name server if it is going to be used. #
endif /* RES_NOALIASES */ /* initialize some macros, etc. */ if (p !=
NULL && p[
1] !=
'\0')
#
endif /* NETINET || NETINET6 */ /* current load average */ case 'b':
/* operations mode */ case 'B':
/* body type */ case 'C':
/* select configuration file (already done) */ case 'd':
/* debugging */ case 'f':
/* from address */ case 'r':
/* obsolete -f flag */ usrerr(
"More than one \"from\" person");
case 'F':
/* set full name */ case 'G':
/* relay (gateway) submission */ case 'h':
/* hop count */ case 'L':
/* program label */ case 'n':
/* don't alias */ case 'N':
/* delivery status notifications */ usrerr(
"Invalid -N argument");
case 'o':
/* set option */ case 'O':
/* set option (long form) */ case 'p':
/* set protocol */ case 'Q':
/* change quarantining on queued items */ case 'q':
/* run queue files at intervals */ /* don't override -bd, -bD or -bp */ /* negate meaning of pattern match */ optarg++;
/* skip '!' for next switch */ case 'G':
/* Limit by queue group name */ usrerr(
"Can not use multiple -qG options");
case 'I':
/* Limit by ID */ case 'R':
/* Limit by recipient */ case 'S':
/* Limit by sender */ case 'f':
/* foreground queue run */ case 'Q':
/* Limit by quarantine message */ case 'L':
/* act on lost items */ case 'p':
/* Persistent queue */ /* check for bad conversion */ case 'R':
/* DSN RET: what to return */ case 't':
/* read recipients from message */ case 'V':
/* DSN ENVID: set "original" envelope id */ usrerr(
"Invalid syntax in -V flag");
case 'X':
/* traffic log file */ /* compatibility flags */ case 'c':
/* connect to non-local mailers */ case 'i':
/* don't let dot stop me */ case 'm':
/* send to me too */ case 'T':
/* set timeout interval */ case 'v':
/* give blow-by-blow description */ case 'e':
/* error message disposition */ case 'M':
/* define macro */ case 's':
/* save From lines in headers */ case 'I':
/* initialize alias DBM file */ case 'x':
/* random flag that OSF/1 & AIX mailx passes */ #
endif /* defined(__osf__) || defined(_AIX3) */ case 'J':
/* ignore flags for Japanese code conversion implemented on Sony NEWS */ #
endif /* defined(sony_news) */ /* if we've had errors so far, exit now */ /* If set daemon_flags on command line, don't reset it */ macid(
"{daemon_flags}"),
"CC f");
/* If set daemon_flags on command line, don't reset it */ macid(
"{daemon_flags}"),
"c u");
** Do basic initialization. ** Read system control file. ** Extract special fields for local use. #
endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */ /* now we can complain about missing fds */ /* Notice: fill_errno is from high above: fill_fd() */ "File descriptors missing on startup: %s; %s",
/* Remove the ability for a normal user to send signals */ ** Since we can differentiate between uid and euid, ** make the uid a different user so the real user ** can't send signals. However, it doesn't need to be syserr(
"main: setreuid(%d, %d) failed",
** Have to change both effective and real so need to ** change them both to effective to keep privs. "WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n");
usrerr(
"Mail submission program cannot be used as daemon");
/* set up the basic signal handlers */ /* Enforce use of local time (null string overrides this) */ /* initialize mailbox database */ usrerr(
"Can't initialize mailbox database \"%s\": %s",
/* avoid denial-of-service attacks */ /* can't be done after readcf if RunAs* is used */ /* drop privileges -- daemon mode done after socket/bind */ usrerr(
"Mail submission program must have RunAsUser set to non root user");
** Find our real host name for future logging. /* suppress error printing if errors mailed back or whatever */ /* set up the $=m class now, after .cf has a chance to redefine $m */ /* probe interfaces and locate any additional names */ /* Now we know which .cf file we use */ sm_dprintf(
"\n============ SYSTEM IDENTITY (after readcf) ============");
sm_dprintf(
"\n========================================================\n\n");
** Do more command line checking -- these are things that ** have to modify the results of reading the config file. /* process authorization warnings from command line */ /* check body type for legality */ /* tweak default DSN notifications */ /* check for sane configuration level */ syserr(
"Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
/* need MCI cache to have persistence */ "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
/* need HostStatusDir in order to have SingleThreadDelivery */ "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
"cannot get memory statistics, settings ignored, error=%d" #
endif /* _FFR_MEMSTAT */ /* check for permissions */ /* Normal users can do a single queue run */ /* but not persistent queue runners */ action =
"start a queue runner daemon";
"user %d attempted to %s",
usrerr(
"Permission denied (real uid not trusted)");
** If -bv and RestrictExpand, ** drop privs to prevent normal ** users from reading private sm_dprintf(
"Drop privs for user %d attempt to expand (RestrictExpand)\n",
/* Fake address safety */ sm_dprintf(
"Faking DontBlameSendmail=NonRootSafeAddr\n");
sm_dprintf(
"Failed to drop privs for user %d attempt to expand, exiting\n",
/* Nothing special to check */ sm_dprintf(
"Deny user %d attempt to rebuild the alias map\n",
"user %d attempted to rebuild the alias map",
usrerr(
"Permission denied (real uid not trusted)");
usrerr(
"User %d cannot rebuild aliases in mail submission program",
** If -v and RestrictExpand, reset ** Verbose to prevent normal users ** from seeing the expansion of sm_dprintf(
"Dropping verbosity for user %d (RestrictExpand)\n",
/* don't have persistent host status in test mode */ /* arrange to exit cleanly on hangup signal */ "Notice: -bv may give misleading output for non-privileged user\n");
/* remove things that don't make sense in daemon mode */ /* arrange to restart on hangup signal */ "daemon invoked without full pathname; kill -1 won't work");
/* arrange to exit cleanly on hangup signal */ /* special considerations for FullName */ /* full names can't have newlines */ /* check for characters that may have to be quoted */ ** Quote a full name with special characters ** as a comment so crackaddr() doesn't destroy ** the name portion of the address. /* do heuristic mode adjustment */ /* turn off noconnect option */ /* turn on interactive delivery */ /* check for vendor mismatch */ message(
"Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
/* check for out of date configuration level */ message(
"Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
/* set options that were previous macros */ /* our name for SMTP codes */ message(
"WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
/* make certain that this name is part of the $=w class */ /* fill in the structure of the *default* queue */ syserr(
"No default queue (mqueue) defined");
/* the indices of built-in mailers */ syserr(
"No local mailer defined");
syserr(
"No prog mailer defined");
syserr(
"No *file* mailer defined");
syserr(
"No *include* mailer defined");
/* heuristic tweaking of local mailer for back compat */ /* MIME Content-Types that cannot be transfer encoded */ /* MIME message/xxx subtypes that can be treated as messages */ /* MIME Content-Transfer-Encodings that can be encoded */ /* MIME Content-Types that should be treated as binary */ /* MIME headers which have fields to check for overflow */ setclass(
macid(
"{checkMIMEFieldHeaders}"),
"content-disposition");
/* MIME headers to check for length overflow */ setclass(
macid(
"{checkMIMETextHeaders}"),
"content-description");
/* MIME headers to check for overflow and rebalance */ setclass(
macid(
"{checkMIMEHeaders}"),
"content-transfer-encoding");
/* Macros to save in the queue file -- don't remove any */ /* operate in queue directory */ syserr(
"QueueDirectory (Q) option must be set");
/* check host status directory for validity */ /* cannot use this value */ "Warning: Cannot use HostStatusDirectory = %s: %s\n",
/* check to see if we own the queue directory */ /* nope, really a botch */ usrerr(
"You do not have permission to process the queue");
/* sanity checks on milter filters */ /* Convert queuegroup string to qgrp number */ /* if checking config or have had errors so far, exit now */ /* sendmail specific SASL initialization */ ** Do operation-mode-dependent initialization. /* Selecting a particular queue group to run */ /* print number of entries in queue */ /* only handle quarantining here */ /* reset DSN parameters */ /* don't open maps for daemon -- done below in child */ /* print configuration table (or at least part of it) */ ** Switch to the main envelope. ** If test mode, read addresses from stdin and process. "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
"Enter <ruleset> <address>\n");
macid(
"{addr_type}"),
"e r");
** 8.10 just prints \n on interrupt. ** I'm printing the exception here in case ** sendmail is extended to raise additional ** exceptions in this context. /* check whether STARTTLS is turned off for the client */ /* check whether STARTTLS is turned off */ else /* other modes don't need STARTTLS */ /* basic TLS initialization */ /* disable TLS for client */ ** If collecting stuff from the queue, go start doing that. /* init TLS for client, ignore result for now */ ** The parent process of the caller of runqueue() needs ** to stay around for a possible SIGTERM. The SIGTERM will ** tell this process that all of the queue runners children ** need to be sent SIGTERM as well. At the same time, we ** want to return control to the command line. So we do an ** If the fork() failed we should still try to do ** the queue run. If it succeeded then the child ** is going to start the run and wait for all ** of the children to finish. /* disconnect from terminal */ ** To run a specific queue group mark it to ** be run, select the work group it's in and ** increment the work counter. /* set the title to make it easier to find */ ** Oops... something got messed ** up really bad. Waiting for ** shouldn't happen. Let's get /* something is really really wrong */ "queue control process: lost all children: wait returned ECHILD");
/* Only drop when a child gives status */ /* check whether AUTH is turned off for the server */ syserr(
"!sasl_server_init failed! [%s]",
/* clean up background delivery children */ ** If a daemon, wait for a request. ** getrequests will always return in a child. ** If we should also be processing the queue, start ** doing it in background. ** We check for any errors that might have happened /* avoid cleanup in finis(), DaemonPid will be set below */ /* put us in background */ syserr(
"daemon: cannot fork");
** Initialize exception stack and default exception ** handler for child process. /* disconnect from our controlling tty */ ?
"+persistent-queueing@" /* save daemon type in a macro for possible PidFile use */ /* save queue interval in a macro for possible PidFile use */ /* workaround: can't seem to release the signal in the parent */ ** To run a specific queue group mark it to ** be run, select the work group it's in and ** increment the work counter. ** If queuepersistent but not in daemon mode then ** we're going to do the queue runner monitoring here. ** If in daemon mode then the monitoring will happen ** XXX Overwrites sendmail.pid /* set the title to make it easier to find */ ** Waiting for non-existent ** children shouldn't happen. ** Let's get out of here if /* something is really really wrong */ "persistent queue runner control process: lost all children: wait returned ECHILD");
/* Probe only on a child status */ "persistent queue runner=%d core dumped, signal=%d",
"persistent queue runner=%d died, pid=%ld, signal=%d",
** When debugging active, don't ** restart the persistent queues. ** But do log this as info. "persistent queue runner=%d, exited",
** XXX Overwrites sendmail.pid /* set the title to make it easier to find */ /* init TLS for server, ignore result for now */ ** Get authentication data ** Set _ macro in BlankEnvelope before calling newenvelope(). /* at this point we are in a child: reset state */ /* log connection information */ ** If running SMTP protocol, start collecting and executing ** commands. This will never return. ** Save some macros for check_* rulesets. /* validate the connection */ /* interactive -- all errors are global */ ** Do basic system initialization and set the sender /* set the initial sender for AUTH= to $f@$j */ usrerr(
"Recipient names must be specified");
/* collect body for UUCP return */ ** Scan argv and deliver the message to everyone. /* if we have had errors sofar, arrange a meaningful exit stat */ ** If using -t, force not sending to argv recipients, even ** if they are mentioned in the headers. #
endif /* _FFR_FIX_DASHT */ ** workaround for compiler warning on Irix: ** do not initialize variable in the definition, but ** warning(1548): transfer of control bypasses ** variable "savederrors" (declared at line 2570) ** variable "savedflags" (declared at line 2571) /* header checks failed */ /* Log who the mail would have gone to */ /* bail out if message too large */ /* Check if quarantining stats should be updated */ ** Actually send everything. ** If verifying, just ack. /* make sure we deliver at least the first envelope */ /* after FastSplit envelopes: queue up */ ** Don't send return error message if in VERIFY mode. ** STOP_SENDMAIL -- Stop the running program /* reset uid for process accounting */ ** FINIS -- Clean up and exit. ** drop -- whether or not to drop CurEnv envelope ** cleanup -- call exit() or _exit()? ** exitstat -- exit status to use for exit() call /* Still want to process new timeouts added below */ sm_dprintf(
"\n====finis: stat %d e_id=%s e_flags=",
** Clean up. This might raise E:mta.quickabort /* clean up temp files */ /* these may have pointed to the rpool */ /* flush any cached connections */ /* close maps belonging to this pid */ /* clean up extended load average stuff */ /* XXX clean up queues and related data structures */ /* close locked pid file */ /* blow away the pid file */ /* reset uid for process accounting */ #
endif /* _FFR_MEMSTAT */ /* dump the heap, if we are checking for memory leaks */ #
endif /* SM_HEAP_CHECK */** INTINDEBUG -- signal handler for SIGINT in -bt mode ** sig -- incoming signal. ** longjmps back to test mode loop. ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE /* Type of an exception generated on SIGINT during address test mode. */ ** SIGTERM -- SIGTERM handler for the daemon ** Sets ShutdownRequest which will hopefully trigger ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** SIGHUP -- handle a SIGHUP signal ** sig -- incoming signal. ** Sets RestartRequest which should cause the daemon ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** SIGPIPE -- signal handler for SIGPIPE ** sig -- incoming signal. ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** INTSIG -- clean up on interrupt ** This just arranges to exit. It pessimizes in that it ** sig -- incoming signal. ** Unlocks the current job. ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE /* Clean-up on aborted stdin message submission */ ** DISCONNECT -- remove our connection with any foreground process ** droplev -- how "deeply" we should drop the line. ** 0 -- ignore signals, mail back errors, make sure ** output goes to stdout. ** 1 -- also, make stdout go to /dev/null. ** 2 -- also, disconnect from controlling terminal ** (only for daemon mode). ** e -- the current envelope. ** Trys to insure that we are immune to vagaries of /* be sure we don't get nasty signals */ /* we can't communicate with our caller, so.... */ "disconnect: sm_io_reopen(\"%s\") failed: %s",
** output to the transcript ** We also compare the fd numbers here since OutChannel ** might be a layer on top of smioout due to encryption ** Has smioout been closed? Reopen it. ** This shouldn't happen anymore, the code is here "disconnect: sm_io_reopen(\"%s\") failed: %s",
"disconnect: open(\"%s\") failed: %s",
/* drop our controlling TTY completely if possible */ /* Return if "--" or not an option of any form. */ if (
ap[0] !=
'-' ||
ap[
1] ==
'-')
/* Don't allow users to use "-Q." or "-Q ." */ if ((
ap[
1] ==
'Q' &&
ap[
2] ==
'.') ||
argv[
1][0] ==
'.' &&
argv[
1][
1] ==
'\0'))
/* skip over options that do have a value */ if (
op !=
NULL && *++
op ==
':' &&
ap[
2] ==
'\0' &&
ap[
1] !=
'E' &&
ap[
1] !=
'J' &&
#
endif /* defined(sony_news) */ /* If -C doesn't have an argument, use sendmail.cf. */ if (
ap[
1] ==
'C' &&
ap[
2] ==
'\0')
/* If -q doesn't have an argument, run it once. */ if (
ap[
1] ==
'q' &&
ap[
2] ==
'\0')
/* If -Q doesn't have an argument, disable quarantining */ if (
ap[
1] ==
'Q' &&
ap[
2] ==
'\0')
/* if -d doesn't have an argument, use 0-99.1 */ if (
ap[
1] ==
'd' &&
ap[
2] ==
'\0')
/* if -E doesn't have an argument, use -EC */ if (
ap[
1] ==
'E' &&
ap[
2] ==
'\0')
/* if -J doesn't have an argument, use -JJ */ if (
ap[
1] ==
'J' &&
ap[
2] ==
'\0')
#
endif /* defined(sony_news) */** AUTH_WARNING -- specify authorization warning ** e -- the current envelope. ** msg -- the text of the message. ** args -- arguments to the message. "Authentication-Warning: %.400s",
** GETEXTENV -- get from external environment ** envar -- the name of the variable to retrieve ** SM_SETUSERENV -- set an environment variable in the propagated environment ** envar -- the name of the environment variable. ** value -- the value to which it should be set. If ** null, this is extracted from the incoming ** environment. If that is not set, the call ** to sm_setuserenv is ignored. /* XXX enforce reasonable size? */ /* make sure it is in our environment as well */ syserr(
"sm_setuserenv: putenv(%s) failed", p);
** DUMPSTATE -- dump state "--- dumping state on %s: $j = %s ---",
j ==
NULL ?
"<NULL>" : j);
"*** $j not in $=w ***");
"--- ruleset debug_dumpstate returns stat %d, pv: ---",
** SIGUSR1 -- Signal a request to dump state. ** sig -- calling signal. ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE ** XXX: More work is needed for this signal handler. #
endif /* SM_HEAP_CHECK */** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option ** to_real_uid -- if set, drop to the real uid instead ** EX_OSERR if the setuid failed. sm_dprintf(
"drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
/* make sure no one can grab open descriptors for secret files */ /* reset group permissions; these can be set later */ ** Notice: on some OS (Linux...) the setgroups() call causes ** a logfile entry if sendmail is not run by root. ** However, it is unclear (no POSIX standard) whether ** setgroups() can only succeed if executed by root. ** So for now we keep it as it is; if you want to change it, use ** if (geteuid() == 0 && setgroups(1, emptygidset) == -1) syserr(
"drop_privileges: setgroups(1, %d) failed",
/* reset primary group id */ ** On some OS we must reset the effective[/real[/saved]] gid, ** and then use setgid() to finally drop all group privileges. ** Later on we check whether we can get back the syserr(
"drop_privileges: setegid(%d) failed",
syserr(
"drop_privileges: setregid(%d, %d) failed",
syserr(
"drop_privileges: setresgid(%d, %d, %d) failed",
#
endif /* HASSETRESGID */#
endif /* HASSETREGID */ syserr(
"drop_privileges: setgid(%d) failed",
syserr(
"drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
** Try to setuid(RunAsUid). ** euid must be RunAsUid, ** ruid must be RunAsUid unless (e|r)uid wasn't 0 ** and we didn't have to drop privileges to the real uid. ** if ruid != RunAsUid, euid == RunAsUid, then ** try resetting just the real uid, then using ** setuid() to drop the saved-uid as well. syserr(
"drop_privileges: setreuid(%d, -1) failed",
syserr(
"drop_privileges: second setuid(%d) attempt failed",
syserr(
"drop_privileges: setuid(%d) failed",
** Believe it or not, the Linux capability model ** allows a non-root process to override setuid() ** on a process running as root and prevent that ** process from dropping privileges. syserr(
"drop_privileges: setuid(0) succeeded (when it should not)");
** Some operating systems will keep the saved-uid ** if a non-root effective-uid calls setuid(real-uid) ** making it possible to set it back again later. syserr(
"drop_privileges: Unable to drop non-root set-user-ID privileges");
syserr(
"drop_privileges: setgid(%d) succeeded (when it should not)",
sm_dprintf(
"drop_privileges: RunAsUser = %d:%d\n",
** FILL_FD -- make sure a file descriptor has been properly allocated ** Used to make sure that stdin/out/err are allocated on startup ** fd -- the file descriptor to be filled. ** where -- a string used for logging. If NULL, this is ** being called on startup, and logging should ** possibly changes MissingFds syserr(
"!fill_fd: %s: cannot open %s",
** SM_PRINTOPTIONS -- print options ** options -- array of options. ** TO8BIT -- convert \octal sequences in a test mode input line ** str -- the input line. ** replaces \0octal in str with octal value. while ((c = (*
str++ & 0
377)) !=
'\0')
while ((
nxtc = (*
str & 0
377)) !=
'\0' &&
** TESTMODELINE -- process a test mode input line ** line -- the input line. ** e -- the current environment. ** .X process X as a configuration line ** =X dump a configuration item (such as mailers) ** $X dump a macro or class ** X normal process through rule set X /* skip leading spaces */ case '.':
/* config-style settings */ if (
line[
2] ==
'\0')
/* not to call syserr() */ "Usage: .[DC]macro value(s)\n");
"Unknown \".\" command %s\n",
line);
case '=':
/* config-style settings */ case 'S':
/* dump rule set */ "Undefined ruleset %s\n", &
line[
2]);
"Usage: =Sruleset or =M\n");
"Unknown \"=\" command %s\n",
line);
case '-':
/* set command-line-like opts */ "Usage: -d{debug arguments}\n");
"Unknown \"-\" command %s\n",
line);
case '/':
/* miscellaneous commands */ "Usage: /[canon|map|mx|parse|try|tryflags]\n");
"getmxrr(%s) returns %d value(s):\n",
for (i = 0; i <
nmx; i++)
"No MX code compiled in\n");
"Usage: /canon address\n");
"getcanonname(%s) returns %s\n",
"Usage: /map mapname key\n");
"Map named \"%s\" not found\n", p);
"Map named \"%s\" not open\n", p);
"map_lookup: %s (%s) ", p, q);
if (q ==
NULL || *q ==
'\0')
"Usage: /try mailer address\n");
"Unknown mailer %s\n", p);
"Trying %s %s address %s for mailer %s\n",
"Rcode = %d, addr = %s\n",
"Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
"Usage: /parse address\n");
"\nParsing %s %s address\n",
"mailer %s, host %s, user %s\n",
"Unknown \"/\" command %s\n",
"Undefined ruleset %s\n",
"== Ruleset %s (%d) status %d\n",
while (*p !=
'\0' && *p++ !=
',')
** An exception type used to create QuickAbort exceptions. ** This is my first cut at converting QuickAbort from longjmp to exceptions. ** These exceptions have a single integer argument, which is the argument ** to longjmp in the original code (either 1 or 2). I don't know the ** significance of 1 vs 2: the calls to setjmp don't care.