summaryrefslogtreecommitdiffstats
path: root/sendmail/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'sendmail/src/main.c')
-rw-r--r--sendmail/src/main.c4554
1 files changed, 4554 insertions, 0 deletions
diff --git a/sendmail/src/main.c b/sendmail/src/main.c
new file mode 100644
index 0000000..8680add
--- /dev/null
+++ b/sendmail/src/main.c
@@ -0,0 +1,4554 @@
+/*
+ * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#define _DEFINE
+#include <sendmail.h>
+#include <sm/sendmail.h>
+#include <sm/xtrap.h>
+#include <sm/signal.h>
+
+#ifndef lint
+SM_UNUSED(static char copyright[]) =
+"@(#) Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.\n\
+ All rights reserved.\n\
+ Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\
+ Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* ! lint */
+
+SM_RCSID("@(#)$Id: main.c,v 8.963 2007/06/29 20:07:37 ca Exp $")
+
+
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+
+/* for getcfname() */
+#include <sendmail/pathnames.h>
+
+static SM_DEBUG_T
+DebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
+ "@(#)$Debug: no_persistent_restart - don't restart, log only $");
+
+static void dump_class __P((STAB *, int));
+static void obsolete __P((char **));
+static void testmodeline __P((char *, ENVELOPE *));
+static char *getextenv __P((const char *));
+static void sm_printoptions __P((char **));
+static SIGFUNC_DECL intindebug __P((int));
+static SIGFUNC_DECL sighup __P((int));
+static SIGFUNC_DECL sigpipe __P((int));
+static SIGFUNC_DECL sigterm __P((int));
+#ifdef SIGUSR1
+static SIGFUNC_DECL sigusr1 __P((int));
+#endif /* SIGUSR1 */
+
+/*
+** SENDMAIL -- Post mail to a set of destinations.
+**
+** This is the basic mail router. All user mail programs should
+** call this routine to actually deliver mail. Sendmail in
+** turn calls a bunch of mail servers that do the real work of
+** delivering the mail.
+**
+** Sendmail is driven by settings read in from /etc/mail/sendmail.cf
+** (read by readcf.c).
+**
+** Usage:
+** /usr/lib/sendmail [flags] addr ...
+**
+** See the associated documentation for details.
+**
+** Authors:
+** Eric Allman, UCB/INGRES (until 10/81).
+** Britton-Lee, Inc., purveyors of fine
+** database computers (11/81 - 10/88).
+** International Computer Science Institute
+** (11/88 - 9/89).
+** UCB/Mammoth Project (10/89 - 7/95).
+** InReference, Inc. (8/95 - 1/97).
+** Sendmail, Inc. (1/98 - present).
+** The support of my employers is gratefully acknowledged.
+** Few of them (Britton-Lee in particular) have had
+** anything to gain from my involvement in this project.
+**
+** Gregory Neil Shapiro,
+** Worcester Polytechnic Institute (until 3/98).
+** Sendmail, Inc. (3/98 - present).
+**
+** Claus Assmann,
+** Sendmail, Inc. (12/98 - present).
+*/
+
+char *FullName; /* sender's full name */
+ENVELOPE BlankEnvelope; /* a "blank" envelope */
+static ENVELOPE MainEnvelope; /* the envelope around the basic letter */
+ADDRESS NullAddress = /* a null address */
+ { "", "", NULL, "" };
+char *CommandLineArgs; /* command line args for pid file */
+bool Warn_Q_option = false; /* warn about Q option use */
+static int MissingFds = 0; /* bit map of fds missing on startup */
+char *Mbdb = "pw"; /* mailbox database defaults to /etc/passwd */
+
+#ifdef NGROUPS_MAX
+GIDSET_T InitialGidSet[NGROUPS_MAX];
+#endif /* NGROUPS_MAX */
+
+#define MAXCONFIGLEVEL 10 /* highest config version level known */
+
+#if SASL
+static sasl_callback_t srvcallbacks[] =
+{
+ { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
+ { SASL_CB_PROXY_POLICY, &proxy_policy, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+#endif /* SASL */
+
+unsigned int SubmitMode;
+int SyslogPrefixLen; /* estimated length of syslog prefix */
+#define PIDLEN 6 /* pid length for computing SyslogPrefixLen */
+#ifndef SL_FUDGE
+# define SL_FUDGE 10 /* fudge offset for SyslogPrefixLen */
+#endif /* ! SL_FUDGE */
+#define SLDLL 8 /* est. length of default syslog label */
+
+
+/* Some options are dangerous to allow users to use in non-submit mode */
+#define CHECK_AGAINST_OPMODE(cmd) \
+{ \
+ if (extraprivs && \
+ OpMode != MD_DELIVER && OpMode != MD_SMTP && \
+ OpMode != MD_ARPAFTP && \
+ OpMode != MD_VERIFY && OpMode != MD_TEST) \
+ { \
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
+ "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
+ (cmd)); \
+ break; \
+ } \
+ if (extraprivs && queuerun) \
+ { \
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \
+ "WARNING: Ignoring submission mode -%c option with -q\n", \
+ (cmd)); \
+ break; \
+ } \
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register char *p;
+ char **av;
+ extern char Version[];
+ char *ep, *from;
+ STAB *st;
+ register int i;
+ int j;
+ int dp;
+ int fill_errno;
+ int qgrp = NOQGRP; /* queue group to process */
+ bool safecf = true;
+ BITMAP256 *p_flags = NULL; /* daemon flags */
+ bool warn_C_flag = false;
+ bool auth = true; /* whether to set e_auth_param */
+ char warn_f_flag = '\0';
+ bool run_in_foreground = false; /* -bD mode */
+ bool queuerun = false, debug = false;
+ struct passwd *pw;
+ struct hostent *hp;
+ char *nullserver = NULL;
+ char *authinfo = NULL;
+ char *sysloglabel = NULL; /* label for syslog */
+ char *conffile = NULL; /* name of .cf file */
+ char *queuegroup = NULL; /* queue group to process */
+ char *quarantining = NULL; /* quarantine queue items? */
+ bool extraprivs;
+ bool forged, negate;
+ bool queuepersistent = false; /* queue runner process runs forever */
+ bool foregroundqueue = false; /* queue run in foreground */
+ bool save_val; /* to save some bool var. */
+ int cftype; /* which cf file to use? */
+ SM_FILE_T *smdebug;
+ static time_t starttime = 0; /* when was process started */
+ struct stat traf_st; /* for TrafficLog FIFO check */
+ char buf[MAXLINE];
+ char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
+ static char rnamebuf[MAXNAME]; /* holds RealUserName */
+ char *emptyenviron[1];
+#if STARTTLS
+ bool tls_ok;
+#endif /* STARTTLS */
+ QUEUE_CHAR *new;
+ ENVELOPE *e;
+ extern int DtableSize;
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+ extern char **environ;
+#if SASL
+ extern void sm_sasl_init __P((void));
+#endif /* SASL */
+
+#if USE_ENVIRON
+ envp = environ;
+#endif /* USE_ENVIRON */
+
+ /* turn off profiling */
+ SM_PROF(0);
+
+ /* install default exception handler */
+ sm_exc_newthread(fatal_error);
+
+ /* set the default in/out channel so errors reported to screen */
+ InChannel = smioin;
+ OutChannel = smioout;
+
+ /*
+ ** Check to see if we reentered.
+ ** This would normally happen if e_putheader or e_putbody
+ ** were NULL when invoked.
+ */
+
+ if (starttime != 0)
+ {
+ syserr("main: reentered!");
+ abort();
+ }
+ starttime = curtime();
+
+ /* avoid null pointer dereferences */
+ TermEscape.te_rv_on = TermEscape.te_under_on = TermEscape.te_normal = "";
+
+ RealUid = getuid();
+ RealGid = getgid();
+
+ /* Check if sendmail is running with extra privs */
+ extraprivs = (RealUid != 0 &&
+ (geteuid() != getuid() || getegid() != getgid()));
+
+ CurrentPid = getpid();
+
+ /* get whatever .cf file is right for the opmode */
+ cftype = SM_GET_RIGHT_CF;
+
+ /* in 4.4BSD, the table can be huge; impose a reasonable limit */
+ DtableSize = getdtsize();
+ if (DtableSize > 256)
+ DtableSize = 256;
+
+ /*
+ ** 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 */
+ errno = fill_errno = 0;
+ fill_fd(STDIN_FILENO, NULL);
+ if (errno != 0)
+ fill_errno = errno;
+ fill_fd(STDOUT_FILENO, NULL);
+ if (errno != 0)
+ fill_errno = errno;
+ fill_fd(STDERR_FILENO, NULL);
+ if (errno != 0)
+ fill_errno = errno;
+
+ sm_closefrom(STDERR_FILENO + 1, DtableSize);
+ errno = 0;
+ smdebug = NULL;
+
+#if LOG
+# ifndef SM_LOG_STR
+# define SM_LOG_STR "sendmail"
+# endif /* ! SM_LOG_STR */
+# ifdef LOG_MAIL
+ openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
+# else /* LOG_MAIL */
+ openlog(SM_LOG_STR, LOG_PID);
+# endif /* LOG_MAIL */
+#endif /* LOG */
+
+ /*
+ ** Seed the random number generator.
+ ** Used for queue file names, picking a queue directory, and
+ ** MX randomization.
+ */
+
+ seed_random();
+
+ /* do machine-dependent initializations */
+ init_md(argc, argv);
+
+
+ SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
+
+ /* reset status from syserr() calls for missing file descriptors */
+ Errors = 0;
+ ExitStat = EX_OK;
+
+ SubmitMode = SUBMIT_UNKNOWN;
+#if XDEBUG
+ checkfd012("after openlog");
+#endif /* XDEBUG */
+
+ tTsetup(tTdvect, sizeof(tTdvect), "0-99.1,*_trace_*.1");
+
+#ifdef NGROUPS_MAX
+ /* save initial group set for future checks */
+ i = getgroups(NGROUPS_MAX, InitialGidSet);
+ if (i <= 0)
+ {
+ InitialGidSet[0] = (GID_T) -1;
+ i = 0;
+ }
+ while (i < NGROUPS_MAX)
+ InitialGidSet[i++] = InitialGidSet[0];
+#endif /* NGROUPS_MAX */
+
+ /* drop group id privileges (RunAsUser not yet set) */
+ dp = drop_privileges(false);
+ setstat(dp);
+
+#ifdef SIGUSR1
+ /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
+ if (!extraprivs)
+ {
+ /* arrange to dump state on user-1 signal */
+ (void) sm_signal(SIGUSR1, sigusr1);
+ }
+ else
+ {
+ /* ignore user-1 signal */
+ (void) sm_signal(SIGUSR1, SIG_IGN);
+ }
+#endif /* SIGUSR1 */
+
+ /* initialize for setproctitle */
+ initsetproctitle(argc, argv, envp);
+
+ /* Handle any non-getoptable constructions. */
+ obsolete(argv);
+
+ /*
+ ** Do a quick prescan of the argument list.
+ */
+
+
+ /* find initial opMode */
+ OpMode = MD_DELIVER;
+ av = argv;
+ p = strrchr(*av, '/');
+ if (p++ == NULL)
+ p = *av;
+ if (strcmp(p, "newaliases") == 0)
+ OpMode = MD_INITALIAS;
+ else if (strcmp(p, "mailq") == 0)
+ OpMode = MD_PRINT;
+ else if (strcmp(p, "smtpd") == 0)
+ OpMode = MD_DAEMON;
+ else if (strcmp(p, "hoststat") == 0)
+ OpMode = MD_HOSTSTAT;
+ else if (strcmp(p, "purgestat") == 0)
+ OpMode = MD_PURGESTAT;
+
+#if defined(__osf__) || defined(_AIX3)
+# 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) */
+#if defined(sony_news)
+# 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) */
+#ifndef OPTIONS
+# define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
+#endif /* ! OPTIONS */
+
+ /* Set to 0 to allow -b; need to check optarg before using it! */
+ opterr = 0;
+ while ((j = getopt(argc, argv, OPTIONS)) != -1)
+ {
+ switch (j)
+ {
+ case 'b': /* operations mode */
+ j = (optarg == NULL) ? ' ' : *optarg;
+ switch (j)
+ {
+ case MD_DAEMON:
+ case MD_FGDAEMON:
+ case MD_SMTP:
+ case MD_INITALIAS:
+ case MD_DELIVER:
+ case MD_VERIFY:
+ case MD_TEST:
+ case MD_PRINT:
+ case MD_PRINTNQE:
+ case MD_HOSTSTAT:
+ case MD_PURGESTAT:
+ case MD_ARPAFTP:
+ OpMode = j;
+ break;
+
+ case MD_FREEZE:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Frozen configurations unsupported\n");
+ return EX_USAGE;
+
+ default:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Invalid operation mode %c\n",
+ j);
+ return EX_USAGE;
+ }
+ break;
+
+ case 'D':
+ if (debug)
+ {
+ errno = 0;
+ syserr("-D file must be before -d");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ dp = drop_privileges(true);
+ setstat(dp);
+ smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
+ optarg, SM_IO_APPEND, NULL);
+ if (smdebug == NULL)
+ {
+ syserr("cannot open %s", optarg);
+ ExitStat = EX_CANTCREAT;
+ break;
+ }
+ sm_debug_setfile(smdebug);
+ break;
+
+ case 'd':
+ debug = true;
+ tTflag(optarg);
+ (void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT,
+ (char *) NULL, SM_IO_NBF,
+ SM_IO_BUFSIZ);
+ break;
+
+ case 'G': /* relay (gateway) submission */
+ SubmitMode = SUBMIT_MTA;
+ break;
+
+ case 'L':
+ if (optarg == NULL)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "option requires an argument -- '%c'",
+ (char) j);
+ return EX_USAGE;
+ }
+ j = SM_MIN(strlen(optarg), 32) + 1;
+ sysloglabel = xalloc(j);
+ (void) sm_strlcpy(sysloglabel, optarg, j);
+ SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
+ SL_FUDGE + j;
+ break;
+
+ case 'Q':
+ case 'q':
+ /* just check if it is there */
+ queuerun = true;
+ break;
+ }
+ }
+ opterr = 1;
+
+ /* Don't leak queue information via debug flags */
+ if (extraprivs && queuerun && debug)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "WARNING: Can not use -d with -q. Disabling debugging.\n");
+ sm_debug_close();
+ sm_debug_setfile(NULL);
+ (void) memset(tTdvect, '\0', sizeof(tTdvect));
+ }
+
+#if LOG
+ if (sysloglabel != NULL)
+ {
+ /* Sanitize the string */
+ for (p = sysloglabel; *p != '\0'; p++)
+ {
+ if (!isascii(*p) || !isprint(*p) || *p == '%')
+ *p = '*';
+ }
+ closelog();
+# ifdef LOG_MAIL
+ openlog(sysloglabel, LOG_PID, LOG_MAIL);
+# else /* LOG_MAIL */
+ openlog(sysloglabel, LOG_PID);
+# endif /* LOG_MAIL */
+ }
+#endif /* LOG */
+
+ /* set up the blank envelope */
+ BlankEnvelope.e_puthdr = putheader;
+ BlankEnvelope.e_putbody = putbody;
+ BlankEnvelope.e_xfp = NULL;
+ STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
+ CurEnv = &BlankEnvelope;
+ STRUCTCOPY(NullAddress, MainEnvelope.e_from);
+
+ /*
+ ** Set default values for variables.
+ ** These cannot be in initialized data space.
+ */
+
+ setdefaults(&BlankEnvelope);
+ initmacros(&BlankEnvelope);
+
+ /* reset macro */
+ set_op_mode(OpMode);
+ if (OpMode == MD_DAEMON)
+ DaemonPid = CurrentPid; /* needed for finis() to work */
+
+ pw = sm_getpwuid(RealUid);
+ if (pw != NULL)
+ (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof(rnamebuf));
+ else
+ (void) sm_snprintf(rnamebuf, sizeof(rnamebuf), "Unknown UID %d",
+ (int) RealUid);
+
+ RealUserName = rnamebuf;
+
+ if (tTd(0, 101))
+ {
+ sm_dprintf("Version %s\n", Version);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ }
+
+ /*
+ ** if running non-set-user-ID binary as non-root, pretend
+ ** we are the RunAsUid
+ */
+
+ if (RealUid != 0 && geteuid() == RealUid)
+ {
+ if (tTd(47, 1))
+ sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
+ (int) RealUid);
+ RunAsUid = RealUid;
+ }
+ else if (geteuid() != 0)
+ RunAsUid = geteuid();
+
+ EffGid = getegid();
+ if (RealUid != 0 && EffGid == RealGid)
+ RunAsGid = RealGid;
+
+ if (tTd(47, 5))
+ {
+ sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
+ (int) geteuid(), (int) getuid(),
+ (int) getegid(), (int) getgid());
+ sm_dprintf("main: RunAsUser = %d:%d\n",
+ (int) RunAsUid, (int) RunAsGid);
+ }
+
+ /* save command line arguments */
+ j = 0;
+ for (av = argv; *av != NULL; )
+ j += strlen(*av++) + 1;
+ SaveArgv = (char **) xalloc(sizeof(char *) * (argc + 1));
+ CommandLineArgs = xalloc(j);
+ p = CommandLineArgs;
+ for (av = argv, i = 0; *av != NULL; )
+ {
+ int h;
+
+ SaveArgv[i++] = newstr(*av);
+ if (av != argv)
+ *p++ = ' ';
+ (void) sm_strlcpy(p, *av++, j);
+ h = strlen(p);
+ p += h;
+ j -= h + 1;
+ }
+ SaveArgv[i] = NULL;
+
+ if (tTd(0, 1))
+ {
+ extern char *CompileOptions[];
+
+ sm_dprintf("Version %s\n Compiled with:", Version);
+ sm_printoptions(CompileOptions);
+ }
+ if (tTd(0, 10))
+ {
+ extern char *OsCompileOptions[];
+
+ sm_dprintf(" OS Defines:");
+ sm_printoptions(OsCompileOptions);
+#ifdef _PATH_UNIX
+ sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
+#endif /* _PATH_UNIX */
+
+ sm_dprintf(" Conf file:\t%s (default for MSP)\n",
+ getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
+ conffile));
+ sm_dprintf(" Conf file:\t%s (default for MTA)\n",
+ getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
+ conffile));
+ sm_dprintf(" Pid file:\t%s (default)\n", PidFile);
+ }
+
+ if (tTd(0, 12))
+ {
+ extern char *SmCompileOptions[];
+
+ sm_dprintf(" libsm Defines:");
+ sm_printoptions(SmCompileOptions);
+ }
+
+ if (tTd(0, 13))
+ {
+ extern char *FFRCompileOptions[];
+
+ sm_dprintf(" FFR Defines:");
+ sm_printoptions(FFRCompileOptions);
+ }
+
+ /* clear sendmail's environment */
+ ExternalEnviron = environ;
+ emptyenviron[0] = NULL;
+ environ = emptyenviron;
+
+ /*
+ ** restore any original TZ setting until TimeZoneSpec has been
+ ** determined - or early log messages may get bogus time stamps
+ */
+
+ if ((p = getextenv("TZ")) != NULL)
+ {
+ char *tz;
+ int tzlen;
+
+ /* XXX check for reasonable length? */
+ tzlen = strlen(p) + 4;
+ tz = xalloc(tzlen);
+ (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
+
+ /* XXX check return code? */
+ (void) putenv(tz);
+ }
+
+ /* prime the child environment */
+ sm_setuserenv("AGENT", "sendmail");
+
+ (void) sm_signal(SIGPIPE, SIG_IGN);
+ OldUmask = umask(022);
+ FullName = getextenv("NAME");
+ if (FullName != NULL)
+ FullName = newstr(FullName);
+
+ /*
+ ** Initialize name server if it is going to be used.
+ */
+
+#if NAMED_BIND
+ if (!bitset(RES_INIT, _res.options))
+ (void) res_init();
+ if (tTd(8, 8))
+ _res.options |= RES_DEBUG;
+ else
+ _res.options &= ~RES_DEBUG;
+# ifdef RES_NOALIASES
+ _res.options |= RES_NOALIASES;
+# endif /* RES_NOALIASES */
+ TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
+ TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
+ TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
+ TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
+ TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
+ TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
+#endif /* NAMED_BIND */
+
+ errno = 0;
+ from = NULL;
+
+ /* initialize some macros, etc. */
+ init_vendor_macros(&BlankEnvelope);
+
+ /* version */
+ macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
+
+ /* hostname */
+ hp = myhostname(jbuf, sizeof(jbuf));
+ if (jbuf[0] != '\0')
+ {
+ struct utsname utsname;
+
+ if (tTd(0, 4))
+ sm_dprintf("Canonical name: %s\n", jbuf);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
+ setclass('w', jbuf);
+
+ p = strchr(jbuf, '.');
+ if (p != NULL && p[1] != '\0')
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]);
+
+ if (uname(&utsname) >= 0)
+ p = utsname.nodename;
+ else
+ {
+ if (tTd(0, 22))
+ sm_dprintf("uname failed (%s)\n",
+ sm_errstring(errno));
+ makelower(jbuf);
+ p = jbuf;
+ }
+ if (tTd(0, 4))
+ sm_dprintf(" UUCP nodename: %s\n", p);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
+ setclass('k', p);
+ setclass('w', p);
+ }
+ if (hp != NULL)
+ {
+ for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
+ {
+ if (tTd(0, 4))
+ sm_dprintf("\ta.k.a.: %s\n", *av);
+ setclass('w', *av);
+ }
+#if NETINET || NETINET6
+ for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
+ {
+# if NETINET6
+ char *addr;
+ char buf6[INET6_ADDRSTRLEN];
+ struct in6_addr ia6;
+# endif /* NETINET6 */
+# if NETINET
+ struct in_addr ia;
+# endif /* NETINET */
+ char ipbuf[103];
+
+ ipbuf[0] = '\0';
+ switch (hp->h_addrtype)
+ {
+# if NETINET
+ case AF_INET:
+ if (hp->h_length != INADDRSZ)
+ break;
+
+ memmove(&ia, hp->h_addr_list[i], INADDRSZ);
+ (void) sm_snprintf(ipbuf, sizeof(ipbuf),
+ "[%.100s]", inet_ntoa(ia));
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ if (hp->h_length != IN6ADDRSZ)
+ break;
+
+ memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
+ addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
+ if (addr != NULL)
+ (void) sm_snprintf(ipbuf, sizeof(ipbuf),
+ "[%.100s]", addr);
+ break;
+# endif /* NETINET6 */
+ }
+ if (ipbuf[0] == '\0')
+ break;
+
+ if (tTd(0, 4))
+ sm_dprintf("\ta.k.a.: %s\n", ipbuf);
+ setclass('w', ipbuf);
+ }
+#endif /* NETINET || NETINET6 */
+#if NETINET6
+ freehostent(hp);
+ hp = NULL;
+#endif /* NETINET6 */
+ }
+
+ /* current time */
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
+
+ /* current load average */
+ sm_getla();
+
+ QueueLimitRecipient = (QUEUE_CHAR *) NULL;
+ QueueLimitSender = (QUEUE_CHAR *) NULL;
+ QueueLimitId = (QUEUE_CHAR *) NULL;
+ QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
+
+ /*
+ ** Crack argv.
+ */
+
+ optind = 1;
+ while ((j = getopt(argc, argv, OPTIONS)) != -1)
+ {
+ switch (j)
+ {
+ case 'b': /* operations mode */
+ /* already done */
+ break;
+
+ case 'A': /* use Alternate sendmail/submit.cf */
+ cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
+ : SM_GET_SENDMAIL_CF;
+ break;
+
+ case 'B': /* body type */
+ CHECK_AGAINST_OPMODE(j);
+ BlankEnvelope.e_bodytype = newstr(optarg);
+ break;
+
+ case 'C': /* select configuration file (already done) */
+ if (RealUid != 0)
+ warn_C_flag = true;
+ conffile = newstr(optarg);
+ dp = drop_privileges(true);
+ setstat(dp);
+ safecf = false;
+ break;
+
+ case 'D':
+ case 'd': /* debugging */
+ /* already done */
+ break;
+
+ case 'f': /* from address */
+ case 'r': /* obsolete -f flag */
+ CHECK_AGAINST_OPMODE(j);
+ if (from != NULL)
+ {
+ usrerr("More than one \"from\" person");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ if (optarg[0] == '\0')
+ from = newstr("<>");
+ else
+ from = newstr(denlstring(optarg, true, true));
+ if (strcmp(RealUserName, from) != 0)
+ warn_f_flag = j;
+ break;
+
+ case 'F': /* set full name */
+ CHECK_AGAINST_OPMODE(j);
+ FullName = newstr(optarg);
+ break;
+
+ case 'G': /* relay (gateway) submission */
+ /* already set */
+ CHECK_AGAINST_OPMODE(j);
+ break;
+
+ case 'h': /* hop count */
+ CHECK_AGAINST_OPMODE(j);
+ BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
+ 10);
+ (void) sm_snprintf(buf, sizeof(buf), "%d",
+ BlankEnvelope.e_hopcount);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
+
+ if (*ep)
+ {
+ usrerr("Bad hop count (%s)", optarg);
+ ExitStat = EX_USAGE;
+ }
+ break;
+
+ case 'L': /* program label */
+ /* already set */
+ break;
+
+ case 'n': /* don't alias */
+ CHECK_AGAINST_OPMODE(j);
+ NoAlias = true;
+ break;
+
+ case 'N': /* delivery status notifications */
+ CHECK_AGAINST_OPMODE(j);
+ DefaultNotify |= QHASNOTIFY;
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{dsn_notify}"), optarg);
+ if (sm_strcasecmp(optarg, "never") == 0)
+ break;
+ for (p = optarg; p != NULL; optarg = p)
+ {
+ p = strchr(p, ',');
+ if (p != NULL)
+ *p++ = '\0';
+ if (sm_strcasecmp(optarg, "success") == 0)
+ DefaultNotify |= QPINGONSUCCESS;
+ else if (sm_strcasecmp(optarg, "failure") == 0)
+ DefaultNotify |= QPINGONFAILURE;
+ else if (sm_strcasecmp(optarg, "delay") == 0)
+ DefaultNotify |= QPINGONDELAY;
+ else
+ {
+ usrerr("Invalid -N argument");
+ ExitStat = EX_USAGE;
+ }
+ }
+ break;
+
+ case 'o': /* set option */
+ setoption(*optarg, optarg + 1, false, true,
+ &BlankEnvelope);
+ break;
+
+ case 'O': /* set option (long form) */
+ setoption(' ', optarg, false, true, &BlankEnvelope);
+ break;
+
+ case 'p': /* set protocol */
+ CHECK_AGAINST_OPMODE(j);
+ p = strchr(optarg, ':');
+ if (p != NULL)
+ {
+ *p++ = '\0';
+ if (*p != '\0')
+ {
+ i = strlen(p) + 1;
+ ep = sm_malloc_x(i);
+ cleanstrcpy(ep, p, i);
+ macdefine(&BlankEnvelope.e_macro,
+ A_HEAP, 's', ep);
+ }
+ }
+ if (*optarg != '\0')
+ {
+ i = strlen(optarg) + 1;
+ ep = sm_malloc_x(i);
+ cleanstrcpy(ep, optarg, i);
+ macdefine(&BlankEnvelope.e_macro, A_HEAP,
+ 'r', ep);
+ }
+ break;
+
+ case 'Q': /* change quarantining on queued items */
+ /* sanity check */
+ if (OpMode != MD_DELIVER &&
+ OpMode != MD_QUEUERUN)
+ {
+ usrerr("Can not use -Q with -b%c", OpMode);
+ ExitStat = EX_USAGE;
+ break;
+ }
+
+ if (OpMode == MD_DELIVER)
+ set_op_mode(MD_QUEUERUN);
+
+ FullName = NULL;
+
+ quarantining = newstr(optarg);
+ break;
+
+ case 'q': /* run queue files at intervals */
+ /* sanity check */
+ if (OpMode != MD_DELIVER &&
+ OpMode != MD_DAEMON &&
+ OpMode != MD_FGDAEMON &&
+ OpMode != MD_PRINT &&
+ OpMode != MD_PRINTNQE &&
+ OpMode != MD_QUEUERUN)
+ {
+ usrerr("Can not use -q with -b%c", OpMode);
+ ExitStat = EX_USAGE;
+ break;
+ }
+
+ /* don't override -bd, -bD or -bp */
+ if (OpMode == MD_DELIVER)
+ set_op_mode(MD_QUEUERUN);
+
+ FullName = NULL;
+ negate = optarg[0] == '!';
+ if (negate)
+ {
+ /* negate meaning of pattern match */
+ optarg++; /* skip '!' for next switch */
+ }
+
+ switch (optarg[0])
+ {
+ case 'G': /* Limit by queue group name */
+ if (negate)
+ {
+ usrerr("Can not use -q!G");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ if (queuegroup != NULL)
+ {
+ usrerr("Can not use multiple -qG options");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ queuegroup = newstr(&optarg[1]);
+ break;
+
+ case 'I': /* Limit by ID */
+ new = (QUEUE_CHAR *) xalloc(sizeof(*new));
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
+ new->queue_next = QueueLimitId;
+ QueueLimitId = new;
+ break;
+
+ case 'R': /* Limit by recipient */
+ new = (QUEUE_CHAR *) xalloc(sizeof(*new));
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
+ new->queue_next = QueueLimitRecipient;
+ QueueLimitRecipient = new;
+ break;
+
+ case 'S': /* Limit by sender */
+ new = (QUEUE_CHAR *) xalloc(sizeof(*new));
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
+ new->queue_next = QueueLimitSender;
+ QueueLimitSender = new;
+ break;
+
+ case 'f': /* foreground queue run */
+ foregroundqueue = true;
+ break;
+
+ case 'Q': /* Limit by quarantine message */
+ if (optarg[1] != '\0')
+ {
+ new = (QUEUE_CHAR *) xalloc(sizeof(*new));
+ new->queue_match = newstr(&optarg[1]);
+ new->queue_negate = negate;
+ new->queue_next = QueueLimitQuarantine;
+ QueueLimitQuarantine = new;
+ }
+ QueueMode = QM_QUARANTINE;
+ break;
+
+ case 'L': /* act on lost items */
+ QueueMode = QM_LOST;
+ break;
+
+ case 'p': /* Persistent queue */
+ queuepersistent = true;
+ if (QueueIntvl == 0)
+ QueueIntvl = 1;
+ if (optarg[1] == '\0')
+ break;
+ ++optarg;
+ /* FALLTHROUGH */
+
+ default:
+ i = Errors;
+ QueueIntvl = convtime(optarg, 'm');
+ if (QueueIntvl < 0)
+ {
+ usrerr("Invalid -q value");
+ ExitStat = EX_USAGE;
+ }
+
+ /* check for bad conversion */
+ if (i < Errors)
+ ExitStat = EX_USAGE;
+ break;
+ }
+ break;
+
+ case 'R': /* DSN RET: what to return */
+ CHECK_AGAINST_OPMODE(j);
+ if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
+ {
+ usrerr("Duplicate -R flag");
+ ExitStat = EX_USAGE;
+ break;
+ }
+ BlankEnvelope.e_flags |= EF_RET_PARAM;
+ if (sm_strcasecmp(optarg, "hdrs") == 0)
+ BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
+ else if (sm_strcasecmp(optarg, "full") != 0)
+ {
+ usrerr("Invalid -R value");
+ ExitStat = EX_USAGE;
+ }
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{dsn_ret}"), optarg);
+ break;
+
+ case 't': /* read recipients from message */
+ CHECK_AGAINST_OPMODE(j);
+ GrabTo = true;
+ break;
+
+ case 'V': /* DSN ENVID: set "original" envelope id */
+ CHECK_AGAINST_OPMODE(j);
+ if (!xtextok(optarg))
+ {
+ usrerr("Invalid syntax in -V flag");
+ ExitStat = EX_USAGE;
+ }
+ else
+ {
+ BlankEnvelope.e_envid = newstr(optarg);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{dsn_envid}"), optarg);
+ }
+ break;
+
+ case 'X': /* traffic log file */
+ dp = drop_privileges(true);
+ setstat(dp);
+ if (stat(optarg, &traf_st) == 0 &&
+ S_ISFIFO(traf_st.st_mode))
+ TrafficLogFile = sm_io_open(SmFtStdio,
+ SM_TIME_DEFAULT,
+ optarg,
+ SM_IO_WRONLY, NULL);
+ else
+ TrafficLogFile = sm_io_open(SmFtStdio,
+ SM_TIME_DEFAULT,
+ optarg,
+ SM_IO_APPEND, NULL);
+ if (TrafficLogFile == NULL)
+ {
+ syserr("cannot open %s", optarg);
+ ExitStat = EX_CANTCREAT;
+ break;
+ }
+ (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
+ NULL, SM_IO_LBF, 0);
+ break;
+
+ /* 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 */
+ setoption(j, "T", false, true, &BlankEnvelope);
+ break;
+
+ case 'e': /* error message disposition */
+ case 'M': /* define macro */
+ setoption(j, optarg, false, true, &BlankEnvelope);
+ break;
+
+ case 's': /* save From lines in headers */
+ setoption('f', "T", false, true, &BlankEnvelope);
+ break;
+
+#ifdef DBM
+ case 'I': /* initialize alias DBM file */
+ set_op_mode(MD_INITALIAS);
+ break;
+#endif /* DBM */
+
+#if defined(__osf__) || defined(_AIX3)
+ case 'x': /* random flag that OSF/1 & AIX mailx passes */
+ break;
+#endif /* defined(__osf__) || defined(_AIX3) */
+#if defined(sony_news)
+ case 'E':
+ case 'J': /* ignore flags for Japanese code conversion
+ implemented on Sony NEWS */
+ break;
+#endif /* defined(sony_news) */
+
+ default:
+ finis(true, true, EX_USAGE);
+ /* NOTREACHED */
+ break;
+ }
+ }
+
+ /* if we've had errors so far, exit now */
+ if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
+ ExitStat == EX_OSERR)
+ {
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+
+ if (bitset(SUBMIT_MTA, SubmitMode))
+ {
+ /* If set daemon_flags on command line, don't reset it */
+ if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"), "CC f");
+ }
+ else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
+ {
+ SubmitMode = SUBMIT_MSA;
+
+ /* If set daemon_flags on command line, don't reset it */
+ if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{daemon_flags}"), "c u");
+ }
+
+ /*
+ ** Do basic initialization.
+ ** Read system control file.
+ ** Extract special fields for local use.
+ */
+
+#if XDEBUG
+ checkfd012("before readcf");
+#endif /* XDEBUG */
+ vendor_pre_defaults(&BlankEnvelope);
+
+ readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
+ safecf, &BlankEnvelope);
+#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
+ ConfigFileRead = true;
+#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
+ vendor_post_defaults(&BlankEnvelope);
+
+ /* now we can complain about missing fds */
+ if (MissingFds != 0 && LogLevel > 8)
+ {
+ char mbuf[MAXLINE];
+
+ mbuf[0] = '\0';
+ if (bitset(1 << STDIN_FILENO, MissingFds))
+ (void) sm_strlcat(mbuf, ", stdin", sizeof(mbuf));
+ if (bitset(1 << STDOUT_FILENO, MissingFds))
+ (void) sm_strlcat(mbuf, ", stdout", sizeof(mbuf));
+ if (bitset(1 << STDERR_FILENO, MissingFds))
+ (void) sm_strlcat(mbuf, ", stderr", sizeof(mbuf));
+
+ /* Notice: fill_errno is from high above: fill_fd() */
+ sm_syslog(LOG_WARNING, NOQID,
+ "File descriptors missing on startup: %s; %s",
+ &mbuf[2], sm_errstring(fill_errno));
+ }
+
+ /* Remove the ability for a normal user to send signals */
+ if (RealUid != 0 && RealUid != geteuid())
+ {
+ uid_t new_uid = geteuid();
+
+#if HASSETREUID
+ /*
+ ** 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
+ ** root (euid has root).
+ */
+
+ if (new_uid == 0)
+ new_uid = DefUid;
+ if (tTd(47, 5))
+ sm_dprintf("Changing real uid to %d\n", (int) new_uid);
+ if (setreuid(new_uid, geteuid()) < 0)
+ {
+ syserr("main: setreuid(%d, %d) failed",
+ (int) new_uid, (int) geteuid());
+ finis(false, true, EX_OSERR);
+ /* NOTREACHED */
+ }
+ if (tTd(47, 10))
+ sm_dprintf("Now running as e/ruid %d:%d\n",
+ (int) geteuid(), (int) getuid());
+#else /* HASSETREUID */
+ /*
+ ** Have to change both effective and real so need to
+ ** change them both to effective to keep privs.
+ */
+
+ if (tTd(47, 5))
+ sm_dprintf("Changing uid to %d\n", (int) new_uid);
+ if (setuid(new_uid) < 0)
+ {
+ syserr("main: setuid(%d) failed", (int) new_uid);
+ finis(false, true, EX_OSERR);
+ /* NOTREACHED */
+ }
+ if (tTd(47, 10))
+ sm_dprintf("Now running as e/ruid %d:%d\n",
+ (int) geteuid(), (int) getuid());
+#endif /* HASSETREUID */
+ }
+
+#if NAMED_BIND
+ if (FallbackMX != NULL)
+ (void) getfallbackmxrr(FallbackMX);
+#endif /* NAMED_BIND */
+
+ if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n");
+ }
+
+ if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
+ {
+ usrerr("Mail submission program cannot be used as daemon");
+ finis(false, true, EX_USAGE);
+ }
+
+ if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
+ OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
+ OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
+ makeworkgroups();
+
+ /* set up the basic signal handlers */
+ if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void) sm_signal(SIGINT, intsig);
+ (void) sm_signal(SIGTERM, intsig);
+
+ /* Enforce use of local time (null string overrides this) */
+ if (TimeZoneSpec == NULL)
+ unsetenv("TZ");
+ else if (TimeZoneSpec[0] != '\0')
+ sm_setuserenv("TZ", TimeZoneSpec);
+ else
+ sm_setuserenv("TZ", NULL);
+ tzset();
+
+ /* initialize mailbox database */
+ i = sm_mbdb_initialize(Mbdb);
+ if (i != EX_OK)
+ {
+ usrerr("Can't initialize mailbox database \"%s\": %s",
+ Mbdb, sm_strexit(i));
+ ExitStat = i;
+ }
+
+ /* avoid denial-of-service attacks */
+ resetlimits();
+
+ if (OpMode == MD_TEST)
+ {
+ /* can't be done after readcf if RunAs* is used */
+ dp = drop_privileges(true);
+ if (dp != EX_OK)
+ {
+ finis(false, true, dp);
+ /* NOTREACHED */
+ }
+ }
+ else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
+ {
+ /* drop privileges -- daemon mode done after socket/bind */
+ dp = drop_privileges(false);
+ setstat(dp);
+ if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
+ {
+ usrerr("Mail submission program must have RunAsUser set to non root user");
+ finis(false, true, EX_CONFIG);
+ /* NOTREACHED */
+ }
+ }
+
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
+#endif /* NAMED_BIND */
+
+ /*
+ ** Find our real host name for future logging.
+ */
+
+ authinfo = getauthinfo(STDIN_FILENO, &forged);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
+
+ /* suppress error printing if errors mailed back or whatever */
+ if (BlankEnvelope.e_errormode != EM_PRINT)
+ HoldErrs = true;
+
+ /* set up the $=m class now, after .cf has a chance to redefine $m */
+ expand("\201m", jbuf, sizeof(jbuf), &BlankEnvelope);
+ if (jbuf[0] != '\0')
+ setclass('m', jbuf);
+
+ /* probe interfaces and locate any additional names */
+ if (DontProbeInterfaces != DPI_PROBENONE)
+ load_if_names();
+
+ if (tTd(0, 10))
+ {
+ char pidpath[MAXPATHLEN];
+
+ /* Now we know which .cf file we use */
+ sm_dprintf(" Conf file:\t%s (selected)\n",
+ getcfname(OpMode, SubmitMode, cftype, conffile));
+ expand(PidFile, pidpath, sizeof(pidpath), &BlankEnvelope);
+ sm_dprintf(" Pid file:\t%s (selected)\n", pidpath);
+ }
+
+ if (tTd(0, 1))
+ {
+ sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
+ sm_dprintf("\n (short domain name) $w = ");
+ xputs(sm_debug_file(), macvalue('w', &BlankEnvelope));
+ sm_dprintf("\n (canonical domain name) $j = ");
+ xputs(sm_debug_file(), macvalue('j', &BlankEnvelope));
+ sm_dprintf("\n (subdomain name) $m = ");
+ xputs(sm_debug_file(), macvalue('m', &BlankEnvelope));
+ sm_dprintf("\n (node name) $k = ");
+ xputs(sm_debug_file(), macvalue('k', &BlankEnvelope));
+ 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 */
+ if (warn_C_flag)
+ auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
+ RealUserName, conffile);
+ if (Warn_Q_option && !wordinclass(RealUserName, 't'))
+ auth_warning(&BlankEnvelope, "Processed from queue %s",
+ QueueDir);
+ if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
+ RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
+ sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
+ (int) RealUid);
+
+ /* check body type for legality */
+ i = check_bodytype(BlankEnvelope.e_bodytype);
+ if (i == BODYTYPE_ILLEGAL)
+ {
+ usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
+ BlankEnvelope.e_bodytype = NULL;
+ }
+ else if (i != BODYTYPE_NONE)
+ SevenBitInput = (i == BODYTYPE_7BIT);
+
+ /* tweak default DSN notifications */
+ if (DefaultNotify == 0)
+ DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
+
+ /* check for sane configuration level */
+ if (ConfigLevel > MAXCONFIGLEVEL)
+ {
+ syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
+ ConfigLevel, Version, MAXCONFIGLEVEL);
+ }
+
+ /* need MCI cache to have persistence */
+ if (HostStatDir != NULL && MaxMciCache == 0)
+ {
+ HostStatDir = NULL;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
+ }
+
+ /* need HostStatusDir in order to have SingleThreadDelivery */
+ if (SingleThreadDelivery && HostStatDir == NULL)
+ {
+ SingleThreadDelivery = false;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
+ }
+
+#if _FFR_MEMSTAT
+ j = sm_memstat_open();
+ if (j < 0 && (RefuseLowMem > 0 || QueueLowMem > 0) && LogLevel > 4)
+ {
+ sm_syslog(LOG_WARNING, NOQID,
+ "cannot get memory statistics, settings ignored, error=%d"
+ , j);
+ }
+#endif /* _FFR_MEMSTAT */
+
+ /* check for permissions */
+ if (RealUid != 0 &&
+ RealUid != TrustedUid)
+ {
+ char *action = NULL;
+
+ switch (OpMode)
+ {
+ case MD_QUEUERUN:
+ if (quarantining != NULL)
+ action = "quarantine jobs";
+ else
+ {
+ /* Normal users can do a single queue run */
+ if (QueueIntvl == 0)
+ break;
+ }
+
+ /* but not persistent queue runners */
+ if (action == NULL)
+ action = "start a queue runner daemon";
+ /* FALLTHROUGH */
+
+ case MD_PURGESTAT:
+ if (action == NULL)
+ action = "purge host status";
+ /* FALLTHROUGH */
+
+ case MD_DAEMON:
+ case MD_FGDAEMON:
+ if (action == NULL)
+ action = "run daemon";
+
+ if (tTd(65, 1))
+ sm_dprintf("Deny user %d attempt to %s\n",
+ (int) RealUid, action);
+
+ if (LogLevel > 1)
+ sm_syslog(LOG_ALERT, NOQID,
+ "user %d attempted to %s",
+ (int) RealUid, action);
+ HoldErrs = false;
+ usrerr("Permission denied (real uid not trusted)");
+ finis(false, true, EX_USAGE);
+ /* NOTREACHED */
+ break;
+
+ case MD_VERIFY:
+ if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
+ {
+ /*
+ ** If -bv and RestrictExpand,
+ ** drop privs to prevent normal
+ ** users from reading private
+ ** aliases/forwards/:include:s
+ */
+
+ if (tTd(65, 1))
+ sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
+ (int) RealUid);
+
+ dp = drop_privileges(true);
+
+ /* Fake address safety */
+ if (tTd(65, 1))
+ sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
+ setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
+
+ if (dp != EX_OK)
+ {
+ if (tTd(65, 1))
+ sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
+ (int) RealUid);
+ CurEnv->e_id = NULL;
+ finis(true, true, dp);
+ /* NOTREACHED */
+ }
+ }
+ break;
+
+ case MD_TEST:
+ case MD_PRINT:
+ case MD_PRINTNQE:
+ case MD_FREEZE:
+ case MD_HOSTSTAT:
+ /* Nothing special to check */
+ break;
+
+ case MD_INITALIAS:
+ if (!wordinclass(RealUserName, 't'))
+ {
+ if (tTd(65, 1))
+ sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
+ (int) RealUid);
+ if (LogLevel > 1)
+ sm_syslog(LOG_ALERT, NOQID,
+ "user %d attempted to rebuild the alias map",
+ (int) RealUid);
+ HoldErrs = false;
+ usrerr("Permission denied (real uid not trusted)");
+ finis(false, true, EX_USAGE);
+ /* NOTREACHED */
+ }
+ if (UseMSP)
+ {
+ HoldErrs = false;
+ usrerr("User %d cannot rebuild aliases in mail submission program",
+ (int) RealUid);
+ finis(false, true, EX_USAGE);
+ /* NOTREACHED */
+ }
+ /* FALLTHROUGH */
+
+ default:
+ if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
+ Verbose != 0)
+ {
+ /*
+ ** If -v and RestrictExpand, reset
+ ** Verbose to prevent normal users
+ ** from seeing the expansion of
+ ** aliases/forwards/:include:s
+ */
+
+ if (tTd(65, 1))
+ sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
+ (int) RealUid);
+ Verbose = 0;
+ }
+ break;
+ }
+ }
+
+ if (MeToo)
+ BlankEnvelope.e_flags |= EF_METOO;
+
+ switch (OpMode)
+ {
+ case MD_TEST:
+ /* don't have persistent host status in test mode */
+ HostStatDir = NULL;
+ if (Verbose == 0)
+ Verbose = 2;
+ BlankEnvelope.e_errormode = EM_PRINT;
+ HoldErrs = false;
+ break;
+
+ case MD_VERIFY:
+ BlankEnvelope.e_errormode = EM_PRINT;
+ HoldErrs = false;
+ /* arrange to exit cleanly on hangup signal */
+ if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
+ (void) sm_signal(SIGHUP, intsig);
+ if (geteuid() != 0)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Notice: -bv may give misleading output for non-privileged user\n");
+ break;
+
+ case MD_FGDAEMON:
+ run_in_foreground = true;
+ set_op_mode(MD_DAEMON);
+ /* FALLTHROUGH */
+
+ case MD_DAEMON:
+ vendor_daemon_setup(&BlankEnvelope);
+
+ /* remove things that don't make sense in daemon mode */
+ FullName = NULL;
+ GrabTo = false;
+
+ /* arrange to restart on hangup signal */
+ if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
+ sm_syslog(LOG_WARNING, NOQID,
+ "daemon invoked without full pathname; kill -1 won't work");
+ break;
+
+ case MD_INITALIAS:
+ Verbose = 2;
+ BlankEnvelope.e_errormode = EM_PRINT;
+ HoldErrs = false;
+ /* FALLTHROUGH */
+
+ default:
+ /* arrange to exit cleanly on hangup signal */
+ if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
+ (void) sm_signal(SIGHUP, intsig);
+ break;
+ }
+
+ /* special considerations for FullName */
+ if (FullName != NULL)
+ {
+ char *full = NULL;
+
+ /* full names can't have newlines */
+ if (strchr(FullName, '\n') != NULL)
+ {
+ full = newstr(denlstring(FullName, true, true));
+ FullName = full;
+ }
+
+ /* check for characters that may have to be quoted */
+ if (!rfc822_string(FullName))
+ {
+ /*
+ ** Quote a full name with special characters
+ ** as a comment so crackaddr() doesn't destroy
+ ** the name portion of the address.
+ */
+
+ FullName = addquotes(FullName, NULL);
+ if (full != NULL)
+ sm_free(full); /* XXX */
+ }
+ }
+
+ /* do heuristic mode adjustment */
+ if (Verbose)
+ {
+ /* turn off noconnect option */
+ setoption('c', "F", true, false, &BlankEnvelope);
+
+ /* turn on interactive delivery */
+ setoption('d', "", true, false, &BlankEnvelope);
+ }
+
+#ifdef VENDOR_CODE
+ /* check for vendor mismatch */
+ if (VendorCode != VENDOR_CODE)
+ {
+ message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
+ getvendor(VENDOR_CODE), getvendor(VendorCode));
+ }
+#endif /* VENDOR_CODE */
+
+ /* check for out of date configuration level */
+ if (ConfigLevel < MAXCONFIGLEVEL)
+ {
+ message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
+ Version, MAXCONFIGLEVEL, ConfigLevel);
+ }
+
+ if (ConfigLevel < 3)
+ UseErrorsTo = true;
+
+ /* set options that were previous macros */
+ if (SmtpGreeting == NULL)
+ {
+ if (ConfigLevel < 7 &&
+ (p = macvalue('e', &BlankEnvelope)) != NULL)
+ SmtpGreeting = newstr(p);
+ else
+ SmtpGreeting = "\201j Sendmail \201v ready at \201b";
+ }
+ if (UnixFromLine == NULL)
+ {
+ if (ConfigLevel < 7 &&
+ (p = macvalue('l', &BlankEnvelope)) != NULL)
+ UnixFromLine = newstr(p);
+ else
+ UnixFromLine = "From \201g \201d";
+ }
+ SmtpError[0] = '\0';
+
+ /* our name for SMTP codes */
+ expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
+ if (jbuf[0] == '\0')
+ PSTRSET(MyHostName, "localhost");
+ else
+ PSTRSET(MyHostName, jbuf);
+ if (strchr(MyHostName, '.') == NULL)
+ message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
+ MyHostName);
+
+ /* make certain that this name is part of the $=w class */
+ setclass('w', MyHostName);
+
+ /* fill in the structure of the *default* queue */
+ st = stab("mqueue", ST_QUEUE, ST_FIND);
+ if (st == NULL)
+ syserr("No default queue (mqueue) defined");
+ else
+ set_def_queueval(st->s_quegrp, true);
+
+ /* the indices of built-in mailers */
+ st = stab("local", ST_MAILER, ST_FIND);
+ if (st != NULL)
+ LocalMailer = st->s_mailer;
+ else if (OpMode != MD_TEST || !warn_C_flag)
+ syserr("No local mailer defined");
+
+ st = stab("prog", ST_MAILER, ST_FIND);
+ if (st == NULL)
+ syserr("No prog mailer defined");
+ else
+ {
+ ProgMailer = st->s_mailer;
+ clrbitn(M_MUSER, ProgMailer->m_flags);
+ }
+
+ st = stab("*file*", ST_MAILER, ST_FIND);
+ if (st == NULL)
+ syserr("No *file* mailer defined");
+ else
+ {
+ FileMailer = st->s_mailer;
+ clrbitn(M_MUSER, FileMailer->m_flags);
+ }
+
+ st = stab("*include*", ST_MAILER, ST_FIND);
+ if (st == NULL)
+ syserr("No *include* mailer defined");
+ else
+ InclMailer = st->s_mailer;
+
+ if (ConfigLevel < 6)
+ {
+ /* heuristic tweaking of local mailer for back compat */
+ if (LocalMailer != NULL)
+ {
+ setbitn(M_ALIASABLE, LocalMailer->m_flags);
+ setbitn(M_HASPWENT, LocalMailer->m_flags);
+ setbitn(M_TRYRULESET5, LocalMailer->m_flags);
+ setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
+ setbitn(M_CHECKPROG, LocalMailer->m_flags);
+ setbitn(M_CHECKFILE, LocalMailer->m_flags);
+ setbitn(M_CHECKUDB, LocalMailer->m_flags);
+ }
+ if (ProgMailer != NULL)
+ setbitn(M_RUNASRCPT, ProgMailer->m_flags);
+ if (FileMailer != NULL)
+ setbitn(M_RUNASRCPT, FileMailer->m_flags);
+ }
+ if (ConfigLevel < 7)
+ {
+ if (LocalMailer != NULL)
+ setbitn(M_VRFY250, LocalMailer->m_flags);
+ if (ProgMailer != NULL)
+ setbitn(M_VRFY250, ProgMailer->m_flags);
+ if (FileMailer != NULL)
+ setbitn(M_VRFY250, FileMailer->m_flags);
+ }
+
+ /* MIME Content-Types that cannot be transfer encoded */
+ setclass('n', "multipart/signed");
+
+ /* MIME message/xxx subtypes that can be treated as messages */
+ setclass('s', "rfc822");
+
+ /* MIME Content-Transfer-Encodings that can be encoded */
+ setclass('e', "7bit");
+ setclass('e', "8bit");
+ setclass('e', "binary");
+
+#ifdef USE_B_CLASS
+ /* MIME Content-Types that should be treated as binary */
+ setclass('b', "image");
+ setclass('b', "audio");
+ setclass('b', "video");
+ setclass('b', "application/octet-stream");
+#endif /* USE_B_CLASS */
+
+ /* MIME headers which have fields to check for overflow */
+ setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
+ setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
+
+ /* MIME headers to check for length overflow */
+ setclass(macid("{checkMIMETextHeaders}"), "content-description");
+
+ /* MIME headers to check for overflow and rebalance */
+ setclass(macid("{checkMIMEHeaders}"), "content-disposition");
+ setclass(macid("{checkMIMEHeaders}"), "content-id");
+ setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
+ setclass(macid("{checkMIMEHeaders}"), "content-type");
+ setclass(macid("{checkMIMEHeaders}"), "mime-version");
+
+ /* Macros to save in the queue file -- don't remove any */
+ setclass(macid("{persistentMacros}"), "r");
+ setclass(macid("{persistentMacros}"), "s");
+ setclass(macid("{persistentMacros}"), "_");
+ setclass(macid("{persistentMacros}"), "{if_addr}");
+ setclass(macid("{persistentMacros}"), "{daemon_flags}");
+
+ /* operate in queue directory */
+ if (QueueDir == NULL || *QueueDir == '\0')
+ {
+ if (OpMode != MD_TEST)
+ {
+ syserr("QueueDirectory (Q) option must be set");
+ ExitStat = EX_CONFIG;
+ }
+ }
+ else
+ {
+ if (OpMode != MD_TEST)
+ setup_queues(OpMode == MD_DAEMON);
+ }
+
+ /* check host status directory for validity */
+ if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
+ {
+ /* cannot use this value */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Warning: Cannot use HostStatusDirectory = %s: %s\n",
+ HostStatDir, sm_errstring(errno));
+ HostStatDir = NULL;
+ }
+
+ if (OpMode == MD_QUEUERUN &&
+ RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
+ {
+ struct stat stbuf;
+
+ /* check to see if we own the queue directory */
+ if (stat(".", &stbuf) < 0)
+ syserr("main: cannot stat %s", QueueDir);
+ if (stbuf.st_uid != RealUid)
+ {
+ /* nope, really a botch */
+ HoldErrs = false;
+ usrerr("You do not have permission to process the queue");
+ finis(false, true, EX_NOPERM);
+ /* NOTREACHED */
+ }
+ }
+
+#if MILTER
+ /* sanity checks on milter filters */
+ if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
+ {
+ milter_config(InputFilterList, InputFilters, MAXFILTERS);
+ setup_daemon_milters();
+ }
+#endif /* MILTER */
+
+ /* Convert queuegroup string to qgrp number */
+ if (queuegroup != NULL)
+ {
+ qgrp = name2qid(queuegroup);
+ if (qgrp == NOQGRP)
+ {
+ HoldErrs = false;
+ usrerr("Queue group %s unknown", queuegroup);
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+ }
+
+ /* if we've had errors so far, exit now */
+ if (ExitStat != EX_OK && OpMode != MD_TEST)
+ {
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+
+#if SASL
+ /* sendmail specific SASL initialization */
+ sm_sasl_init();
+#endif /* SASL */
+
+#if XDEBUG
+ checkfd012("before main() initmaps");
+#endif /* XDEBUG */
+
+ /*
+ ** Do operation-mode-dependent initialization.
+ */
+
+ switch (OpMode)
+ {
+ case MD_PRINT:
+ /* print the queue */
+ HoldErrs = false;
+ dropenvelope(&BlankEnvelope, true, false);
+ (void) sm_signal(SIGPIPE, sigpipe);
+ if (qgrp != NOQGRP)
+ {
+ int j;
+
+ /* Selecting a particular queue group to run */
+ for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
+ {
+ if (StopRequest)
+ stop_sendmail();
+ (void) print_single_queue(qgrp, j);
+ }
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ }
+ printqueue();
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ break;
+
+ case MD_PRINTNQE:
+ /* print number of entries in queue */
+ dropenvelope(&BlankEnvelope, true, false);
+ (void) sm_signal(SIGPIPE, sigpipe);
+ printnqe(smioout, NULL);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ break;
+
+ case MD_QUEUERUN:
+ /* only handle quarantining here */
+ if (quarantining == NULL)
+ break;
+
+ if (QueueMode != QM_QUARANTINE &&
+ QueueMode != QM_NORMAL)
+ {
+ HoldErrs = false;
+ usrerr("Can not use -Q with -q%c", QueueMode);
+ ExitStat = EX_USAGE;
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ }
+ quarantine_queue(quarantining, qgrp);
+ finis(false, true, EX_OK);
+ break;
+
+ case MD_HOSTSTAT:
+ (void) sm_signal(SIGPIPE, sigpipe);
+ (void) mci_traverse_persistent(mci_print_persistent, NULL);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ break;
+
+ case MD_PURGESTAT:
+ (void) mci_traverse_persistent(mci_purge_persistent, NULL);
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ break;
+
+ case MD_INITALIAS:
+ /* initialize maps */
+ initmaps();
+ finis(false, true, ExitStat);
+ /* NOTREACHED */
+ break;
+
+ case MD_SMTP:
+ case MD_DAEMON:
+ /* reset DSN parameters */
+ DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{dsn_notify}"), NULL);
+ BlankEnvelope.e_envid = NULL;
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{dsn_envid}"), NULL);
+ BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{dsn_ret}"), NULL);
+
+ /* don't open maps for daemon -- done below in child */
+ break;
+ }
+
+ if (tTd(0, 15))
+ {
+ /* print configuration table (or at least part of it) */
+ if (tTd(0, 90))
+ printrules();
+ for (i = 0; i < MAXMAILERS; i++)
+ {
+ if (Mailer[i] != NULL)
+ printmailer(sm_debug_file(), Mailer[i]);
+ }
+ }
+
+ /*
+ ** Switch to the main envelope.
+ */
+
+ CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
+ sm_rpool_new_x(NULL));
+ MainEnvelope.e_flags = BlankEnvelope.e_flags;
+
+ /*
+ ** If test mode, read addresses from stdin and process.
+ */
+
+ if (OpMode == MD_TEST)
+ {
+ if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
+ Verbose = 2;
+
+ if (Verbose)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Enter <ruleset> <address>\n");
+ }
+ macdefine(&(MainEnvelope.e_macro), A_PERM,
+ macid("{addr_type}"), "e r");
+ for (;;)
+ {
+ SM_TRY
+ {
+ (void) sm_signal(SIGINT, intindebug);
+ (void) sm_releasesignal(SIGINT);
+ if (Verbose == 2)
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "> ");
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+ if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
+ sizeof(buf)) == NULL)
+ testmodeline("/quit", &MainEnvelope);
+ p = strchr(buf, '\n');
+ if (p != NULL)
+ *p = '\0';
+ if (Verbose < 2)
+ (void) sm_io_fprintf(smioout,
+ SM_TIME_DEFAULT,
+ "> %s\n", buf);
+ testmodeline(buf, &MainEnvelope);
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** 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.
+ */
+
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\n");
+ sm_exc_print(exc, smioout);
+ }
+ SM_END_TRY
+ }
+ }
+
+#if STARTTLS
+ tls_ok = true;
+ if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER ||
+ OpMode == MD_ARPAFTP)
+ {
+ /* check whether STARTTLS is turned off for the client */
+ if (chkclientmodifiers(D_NOTLS))
+ tls_ok = false;
+ }
+ else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
+ OpMode == MD_SMTP)
+ {
+ /* check whether STARTTLS is turned off for the server */
+ if (chkdaemonmodifiers(D_NOTLS))
+ tls_ok = false;
+ }
+ else /* other modes don't need STARTTLS */
+ tls_ok = false;
+
+ if (tls_ok)
+ {
+ /* basic TLS initialization */
+ tls_ok = init_tls_library();
+ }
+
+ if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
+ {
+ /* disable TLS for client */
+ setclttls(false);
+ }
+#endif /* STARTTLS */
+
+ /*
+ ** If collecting stuff from the queue, go start doing that.
+ */
+
+ if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
+ {
+ pid_t pid = -1;
+
+#if STARTTLS
+ /* init TLS for client, ignore result for now */
+ (void) initclttls(tls_ok);
+#endif /* STARTTLS */
+
+ /*
+ ** 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
+ ** extra fork().
+ */
+
+ if (Verbose || foregroundqueue || (pid = fork()) <= 0)
+ {
+ /*
+ ** 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.
+ */
+
+ if (pid == 0)
+ {
+ /* Reset global flags */
+ RestartRequest = NULL;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+
+ /* disconnect from terminal */
+ disconnect(2, CurEnv);
+ }
+
+ CurrentPid = getpid();
+ if (qgrp != NOQGRP)
+ {
+ int rwgflags = RWG_NONE;
+
+ /*
+ ** To run a specific queue group mark it to
+ ** be run, select the work group it's in and
+ ** increment the work counter.
+ */
+
+ for (i = 0; i < NumQueue && Queue[i] != NULL;
+ i++)
+ Queue[i]->qg_nextrun = (time_t) -1;
+ Queue[qgrp]->qg_nextrun = 0;
+ if (Verbose)
+ rwgflags |= RWG_VERBOSE;
+ if (queuepersistent)
+ rwgflags |= RWG_PERSISTENT;
+ rwgflags |= RWG_FORCE;
+ (void) run_work_group(Queue[qgrp]->qg_wgrp,
+ rwgflags);
+ }
+ else
+ (void) runqueue(false, Verbose,
+ queuepersistent, true);
+
+ /* set the title to make it easier to find */
+ sm_setproctitle(true, CurEnv, "Queue control");
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ while (CurChildren > 0)
+ {
+ int status;
+ pid_t ret;
+
+ errno = 0;
+ while ((ret = sm_wait(&status)) <= 0)
+ {
+ if (errno == ECHILD)
+ {
+ /*
+ ** Oops... something got messed
+ ** up really bad. Waiting for
+ ** non-existent children
+ ** shouldn't happen. Let's get
+ ** out of here.
+ */
+
+ CurChildren = 0;
+ break;
+ }
+ continue;
+ }
+
+ /* something is really really wrong */
+ if (errno == ECHILD)
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "queue control process: lost all children: wait returned ECHILD");
+ break;
+ }
+
+ /* Only drop when a child gives status */
+ if (WIFSTOPPED(status))
+ continue;
+
+ proc_list_drop(ret, status, NULL);
+ }
+ }
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+
+# if SASL
+ if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
+ {
+ /* check whether AUTH is turned off for the server */
+ if (!chkdaemonmodifiers(D_NOAUTH) &&
+ (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
+ syserr("!sasl_server_init failed! [%s]",
+ sasl_errstring(i, NULL, NULL));
+ }
+# endif /* SASL */
+
+ if (OpMode == MD_SMTP)
+ {
+ proc_list_add(CurrentPid, "Sendmail SMTP Agent",
+ PROC_DAEMON, 0, -1, NULL);
+
+ /* clean up background delivery children */
+ (void) sm_signal(SIGCHLD, reapchild);
+ }
+
+ /*
+ ** 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
+ ** during startup.
+ */
+
+ if (OpMode == MD_DAEMON || QueueIntvl > 0)
+ {
+ char dtype[200];
+
+ /* avoid cleanup in finis(), DaemonPid will be set below */
+ DaemonPid = 0;
+ if (!run_in_foreground && !tTd(99, 100))
+ {
+ /* put us in background */
+ i = fork();
+ if (i < 0)
+ syserr("daemon: cannot fork");
+ if (i != 0)
+ {
+ finis(false, true, EX_OK);
+ /* NOTREACHED */
+ }
+
+ /*
+ ** Initialize exception stack and default exception
+ ** handler for child process.
+ */
+
+ /* Reset global flags */
+ RestartRequest = NULL;
+ RestartWorkGroup = false;
+ ShutdownRequest = NULL;
+ PendingSignal = 0;
+ CurrentPid = getpid();
+
+ sm_exc_newthread(fatal_error);
+
+ /* disconnect from our controlling tty */
+ disconnect(2, &MainEnvelope);
+ }
+
+ dtype[0] = '\0';
+ if (OpMode == MD_DAEMON)
+ {
+ (void) sm_strlcat(dtype, "+SMTP", sizeof(dtype));
+ DaemonPid = CurrentPid;
+ }
+ if (QueueIntvl > 0)
+ {
+ (void) sm_strlcat2(dtype,
+ queuepersistent
+ ? "+persistent-queueing@"
+ : "+queueing@",
+ pintvl(QueueIntvl, true),
+ sizeof(dtype));
+ }
+ if (tTd(0, 1))
+ (void) sm_strlcat(dtype, "+debugging", sizeof(dtype));
+
+ sm_syslog(LOG_INFO, NOQID,
+ "starting daemon (%s): %s", Version, dtype + 1);
+#if XLA
+ xla_create_file();
+#endif /* XLA */
+
+ /* save daemon type in a macro for possible PidFile use */
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{daemon_info}"), dtype + 1);
+
+ /* save queue interval in a macro for possible PidFile use */
+ macdefine(&MainEnvelope.e_macro, A_TEMP,
+ macid("{queue_interval}"), pintvl(QueueIntvl, true));
+
+ /* workaround: can't seem to release the signal in the parent */
+ (void) sm_signal(SIGHUP, sighup);
+ (void) sm_releasesignal(SIGHUP);
+ (void) sm_signal(SIGTERM, sigterm);
+
+ if (QueueIntvl > 0)
+ {
+#if _FFR_RUNPQG
+ if (qgrp != NOQGRP)
+ {
+ int rwgflags = RWG_NONE;
+
+ /*
+ ** To run a specific queue group mark it to
+ ** be run, select the work group it's in and
+ ** increment the work counter.
+ */
+
+ for (i = 0; i < NumQueue && Queue[i] != NULL;
+ i++)
+ Queue[i]->qg_nextrun = (time_t) -1;
+ Queue[qgrp]->qg_nextrun = 0;
+ if (Verbose)
+ rwgflags |= RWG_VERBOSE;
+ if (queuepersistent)
+ rwgflags |= RWG_PERSISTENT;
+ rwgflags |= RWG_FORCE;
+ (void) run_work_group(Queue[qgrp]->qg_wgrp,
+ rwgflags);
+ }
+ else
+#endif /* _FFR_RUNPQG */
+ (void) runqueue(true, false, queuepersistent,
+ true);
+
+ /*
+ ** 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
+ ** elsewhere.
+ */
+
+ if (OpMode != MD_DAEMON && queuepersistent)
+ {
+ /*
+ ** Write the pid to file
+ ** XXX Overwrites sendmail.pid
+ */
+
+ log_sendmail_pid(&MainEnvelope);
+
+ /* set the title to make it easier to find */
+ sm_setproctitle(true, CurEnv, "Queue control");
+ (void) sm_signal(SIGCHLD, SIG_DFL);
+ while (CurChildren > 0)
+ {
+ int status;
+ pid_t ret;
+ int group;
+
+ CHECK_RESTART;
+ errno = 0;
+ while ((ret = sm_wait(&status)) <= 0)
+ {
+ /*
+ ** Waiting for non-existent
+ ** children shouldn't happen.
+ ** Let's get out of here if
+ ** it occurs.
+ */
+
+ if (errno == ECHILD)
+ {
+ CurChildren = 0;
+ break;
+ }
+ continue;
+ }
+
+ /* something is really really wrong */
+ if (errno == ECHILD)
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "persistent queue runner control process: lost all children: wait returned ECHILD");
+ break;
+ }
+
+ if (WIFSTOPPED(status))
+ continue;
+
+ /* Probe only on a child status */
+ proc_list_drop(ret, status, &group);
+
+ if (WIFSIGNALED(status))
+ {
+ if (WCOREDUMP(status))
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "persistent queue runner=%d core dumped, signal=%d",
+ group, WTERMSIG(status));
+
+ /* don't restart this */
+ mark_work_group_restart(
+ group, -1);
+ continue;
+ }
+
+ sm_syslog(LOG_ERR, NOQID,
+ "persistent queue runner=%d died, pid=%ld, signal=%d",
+ group, (long) ret,
+ WTERMSIG(status));
+ }
+
+ /*
+ ** When debugging active, don't
+ ** restart the persistent queues.
+ ** But do log this as info.
+ */
+
+ if (sm_debug_active(&DebugNoPRestart,
+ 1))
+ {
+ sm_syslog(LOG_DEBUG, NOQID,
+ "persistent queue runner=%d, exited",
+ group);
+ mark_work_group_restart(group,
+ -1);
+ }
+ CHECK_RESTART;
+ }
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+
+ if (OpMode != MD_DAEMON)
+ {
+ char qtype[200];
+
+ /*
+ ** Write the pid to file
+ ** XXX Overwrites sendmail.pid
+ */
+
+ log_sendmail_pid(&MainEnvelope);
+
+ /* set the title to make it easier to find */
+ qtype[0] = '\0';
+ (void) sm_strlcpyn(qtype, sizeof(qtype), 4,
+ "Queue runner@",
+ pintvl(QueueIntvl, true),
+ " for ",
+ QueueDir);
+ sm_setproctitle(true, CurEnv, qtype);
+ for (;;)
+ {
+ (void) pause();
+
+ CHECK_RESTART;
+
+ if (doqueuerun())
+ (void) runqueue(true, false,
+ false, false);
+ }
+ }
+ }
+ dropenvelope(&MainEnvelope, true, false);
+
+#if STARTTLS
+ /* init TLS for server, ignore result for now */
+ (void) initsrvtls(tls_ok);
+#endif /* STARTTLS */
+
+ nextreq:
+ p_flags = getrequests(&MainEnvelope);
+
+ /* drop privileges */
+ (void) drop_privileges(false);
+
+ /*
+ ** Get authentication data
+ ** Set _ macro in BlankEnvelope before calling newenvelope().
+ */
+
+ authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
+ NULL), &forged);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
+
+ /* at this point we are in a child: reset state */
+ sm_rpool_free(MainEnvelope.e_rpool);
+ (void) newenvelope(&MainEnvelope, &MainEnvelope,
+ sm_rpool_new_x(NULL));
+ }
+
+ if (LogLevel > 9)
+ {
+ /* log connection information */
+ sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo);
+ }
+
+ /*
+ ** If running SMTP protocol, start collecting and executing
+ ** commands. This will never return.
+ */
+
+ if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
+ {
+ char pbuf[20];
+
+ /*
+ ** Save some macros for check_* rulesets.
+ */
+
+ if (forged)
+ {
+ char ipbuf[103];
+
+ (void) sm_snprintf(ipbuf, sizeof(ipbuf), "[%.100s]",
+ anynet_ntoa(&RealHostAddr));
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{client_name}"), ipbuf);
+ }
+ else
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{client_name}"), RealHostName);
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{client_ptr}"), RealHostName);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
+ sm_getla();
+
+ switch (RealHostAddr.sa.sa_family)
+ {
+#if NETINET
+ case AF_INET:
+ (void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
+ RealHostAddr.sin.sin_port);
+ break;
+#endif /* NETINET */
+#if NETINET6
+ case AF_INET6:
+ (void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
+ RealHostAddr.sin6.sin6_port);
+ break;
+#endif /* NETINET6 */
+ default:
+ (void) sm_snprintf(pbuf, sizeof(pbuf), "0");
+ break;
+ }
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{client_port}"), pbuf);
+
+ if (OpMode == MD_DAEMON)
+ {
+ ENVELOPE *saved_env;
+
+ /* validate the connection */
+ HoldErrs = true;
+ saved_env = CurEnv;
+ CurEnv = &BlankEnvelope;
+ nullserver = validate_connection(&RealHostAddr,
+ macvalue(macid("{client_name}"),
+ &BlankEnvelope),
+ &BlankEnvelope);
+ if (bitset(EF_DISCARD, BlankEnvelope.e_flags))
+ MainEnvelope.e_flags |= EF_DISCARD;
+ CurEnv = saved_env;
+ HoldErrs = false;
+ }
+ else if (p_flags == NULL)
+ {
+ p_flags = (BITMAP256 *) xalloc(sizeof(*p_flags));
+ clrbitmap(p_flags);
+ }
+#if STARTTLS
+ if (OpMode == MD_SMTP)
+ (void) initsrvtls(tls_ok);
+#endif /* STARTTLS */
+
+ /* turn off profiling */
+ SM_PROF(1);
+ smtp(nullserver, *p_flags, &MainEnvelope);
+
+ if (tTd(93, 100))
+ {
+ /* turn off profiling */
+ SM_PROF(0);
+ if (OpMode == MD_DAEMON)
+ goto nextreq;
+ }
+ }
+
+ sm_rpool_free(MainEnvelope.e_rpool);
+ clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
+ if (OpMode == MD_VERIFY)
+ {
+ set_delivery_mode(SM_VERIFY, &MainEnvelope);
+ PostMasterCopy = NULL;
+ }
+ else
+ {
+ /* interactive -- all errors are global */
+ MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
+ }
+
+ /*
+ ** Do basic system initialization and set the sender
+ */
+
+ initsys(&MainEnvelope);
+ macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
+ macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
+ setsender(from, &MainEnvelope, NULL, '\0', false);
+ if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
+ (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
+ strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
+ {
+ auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
+ RealUserName, from, warn_f_flag);
+#if SASL
+ auth = false;
+#endif /* SASL */
+ }
+ if (auth)
+ {
+ char *fv;
+
+ /* set the initial sender for AUTH= to $f@$j */
+ fv = macvalue('f', &MainEnvelope);
+ if (fv == NULL || *fv == '\0')
+ MainEnvelope.e_auth_param = NULL;
+ else
+ {
+ if (strchr(fv, '@') == NULL)
+ {
+ i = strlen(fv) + strlen(macvalue('j',
+ &MainEnvelope)) + 2;
+ p = sm_malloc_x(i);
+ (void) sm_strlcpyn(p, i, 3, fv, "@",
+ macvalue('j',
+ &MainEnvelope));
+ }
+ else
+ p = sm_strdup_x(fv);
+ MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
+ xtextify(p, "="));
+ sm_free(p); /* XXX */
+ }
+ }
+ if (macvalue('s', &MainEnvelope) == NULL)
+ macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
+
+ av = argv + optind;
+ if (*av == NULL && !GrabTo)
+ {
+ MainEnvelope.e_to = NULL;
+ MainEnvelope.e_flags |= EF_GLOBALERRS;
+ HoldErrs = false;
+ SuperSafe = SAFE_NO;
+ usrerr("Recipient names must be specified");
+
+ /* collect body for UUCP return */
+ if (OpMode != MD_VERIFY)
+ collect(InChannel, false, NULL, &MainEnvelope, true);
+ finis(true, true, EX_USAGE);
+ /* NOTREACHED */
+ }
+
+ /*
+ ** Scan argv and deliver the message to everyone.
+ */
+
+ save_val = LogUsrErrs;
+ LogUsrErrs = true;
+ sendtoargv(av, &MainEnvelope);
+ LogUsrErrs = save_val;
+
+ /* if we have had errors sofar, arrange a meaningful exit stat */
+ if (Errors > 0 && ExitStat == EX_OK)
+ ExitStat = EX_USAGE;
+
+#if _FFR_FIX_DASHT
+ /*
+ ** If using -t, force not sending to argv recipients, even
+ ** if they are mentioned in the headers.
+ */
+
+ if (GrabTo)
+ {
+ ADDRESS *q;
+
+ for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
+ q->q_state = QS_REMOVED;
+ }
+#endif /* _FFR_FIX_DASHT */
+
+ /*
+ ** Read the input mail.
+ */
+
+ MainEnvelope.e_to = NULL;
+ if (OpMode != MD_VERIFY || GrabTo)
+ {
+ int savederrors;
+ unsigned long savedflags;
+
+ /*
+ ** workaround for compiler warning on Irix:
+ ** do not initialize variable in the definition, but
+ ** later on:
+ ** warning(1548): transfer of control bypasses
+ ** initialization of:
+ ** variable "savederrors" (declared at line 2570)
+ ** variable "savedflags" (declared at line 2571)
+ ** goto giveup;
+ */
+
+ savederrors = Errors;
+ savedflags = MainEnvelope.e_flags & EF_FATALERRS;
+ MainEnvelope.e_flags |= EF_GLOBALERRS;
+ MainEnvelope.e_flags &= ~EF_FATALERRS;
+ Errors = 0;
+ buffer_errors();
+ collect(InChannel, false, NULL, &MainEnvelope, true);
+
+ /* header checks failed */
+ if (Errors > 0)
+ {
+ giveup:
+ if (!GrabTo)
+ {
+ /* Log who the mail would have gone to */
+ logundelrcpts(&MainEnvelope,
+ MainEnvelope.e_message,
+ 8, false);
+ }
+ flush_errors(true);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ return -1;
+ }
+
+ /* bail out if message too large */
+ if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
+ {
+ finis(true, true, ExitStat != EX_OK ? ExitStat
+ : EX_DATAERR);
+ /* NOTREACHED */
+ return -1;
+ }
+
+ /* set message size */
+ (void) sm_snprintf(buf, sizeof(buf), "%ld",
+ MainEnvelope.e_msgsize);
+ macdefine(&MainEnvelope.e_macro, A_TEMP,
+ macid("{msg_size}"), buf);
+
+ Errors = savederrors;
+ MainEnvelope.e_flags |= savedflags;
+ }
+ errno = 0;
+
+ if (tTd(1, 1))
+ sm_dprintf("From person = \"%s\"\n",
+ MainEnvelope.e_from.q_paddr);
+
+ /* Check if quarantining stats should be updated */
+ if (MainEnvelope.e_quarmsg != NULL)
+ markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
+
+ /*
+ ** Actually send everything.
+ ** If verifying, just ack.
+ */
+
+ if (Errors == 0)
+ {
+ if (!split_by_recipient(&MainEnvelope) &&
+ bitset(EF_FATALERRS, MainEnvelope.e_flags))
+ goto giveup;
+ }
+
+ /* make sure we deliver at least the first envelope */
+ i = FastSplit > 0 ? 0 : -1;
+ for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
+ {
+ ENVELOPE *next;
+
+ e->e_from.q_state = QS_SENDER;
+ if (tTd(1, 5))
+ {
+ sm_dprintf("main[%d]: QS_SENDER ", i);
+ printaddr(sm_debug_file(), &e->e_from, false);
+ }
+ e->e_to = NULL;
+ sm_getla();
+ GrabTo = false;
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+#endif /* NAMED_BIND */
+ next = e->e_sibling;
+ e->e_sibling = NULL;
+
+ /* after FastSplit envelopes: queue up */
+ sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
+ e->e_sibling = next;
+ }
+
+ /*
+ ** All done.
+ ** Don't send return error message if in VERIFY mode.
+ */
+
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ return ExitStat;
+}
+/*
+** STOP_SENDMAIL -- Stop the running program
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** exits.
+*/
+
+void
+stop_sendmail()
+{
+ /* reset uid for process accounting */
+ endpwent();
+ (void) setuid(RealUid);
+ exit(EX_OK);
+}
+/*
+** FINIS -- Clean up and exit.
+**
+** Parameters:
+** drop -- whether or not to drop CurEnv envelope
+** cleanup -- call exit() or _exit()?
+** exitstat -- exit status to use for exit() call
+**
+** Returns:
+** never
+**
+** Side Effects:
+** exits sendmail
+*/
+
+void
+finis(drop, cleanup, exitstat)
+ bool drop;
+ bool cleanup;
+ volatile int exitstat;
+{
+ char pidpath[MAXPATHLEN];
+ pid_t pid;
+
+ /* Still want to process new timeouts added below */
+ sm_clear_events();
+ (void) sm_releasesignal(SIGALRM);
+
+ if (tTd(2, 1))
+ {
+ sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
+ exitstat,
+ CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
+ printenvflags(CurEnv);
+ }
+ if (tTd(2, 9))
+ printopenfds(false);
+
+ SM_TRY
+ /*
+ ** Clean up. This might raise E:mta.quickabort
+ */
+
+ /* clean up temp files */
+ CurEnv->e_to = NULL;
+ if (drop)
+ {
+ if (CurEnv->e_id != NULL)
+ {
+ dropenvelope(CurEnv, true, false);
+ sm_rpool_free(CurEnv->e_rpool);
+ CurEnv->e_rpool = NULL;
+
+ /* these may have pointed to the rpool */
+ CurEnv->e_to = NULL;
+ CurEnv->e_message = NULL;
+ CurEnv->e_statmsg = NULL;
+ CurEnv->e_quarmsg = NULL;
+ CurEnv->e_bodytype = NULL;
+ CurEnv->e_id = NULL;
+ CurEnv->e_envid = NULL;
+ CurEnv->e_auth_param = NULL;
+ }
+ else
+ poststats(StatFile);
+ }
+
+ /* flush any cached connections */
+ mci_flush(true, NULL);
+
+ /* close maps belonging to this pid */
+ closemaps(false);
+
+#if USERDB
+ /* close UserDatabase */
+ _udbx_close();
+#endif /* USERDB */
+
+#if SASL
+ stop_sasl_client();
+#endif /* SASL */
+
+#if XLA
+ /* clean up extended load average stuff */
+ xla_all_end();
+#endif /* XLA */
+
+ SM_FINALLY
+ /*
+ ** And exit.
+ */
+
+ if (LogLevel > 78)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
+ (int) CurrentPid);
+ if (exitstat == EX_TEMPFAIL ||
+ CurEnv->e_errormode == EM_BERKNET)
+ exitstat = EX_OK;
+
+ /* XXX clean up queues and related data structures */
+ cleanup_queues();
+ pid = getpid();
+#if SM_CONF_SHM
+ cleanup_shm(DaemonPid == pid);
+#endif /* SM_CONF_SHM */
+
+ /* close locked pid file */
+ close_sendmail_pid();
+
+ if (DaemonPid == pid || PidFilePid == pid)
+ {
+ /* blow away the pid file */
+ expand(PidFile, pidpath, sizeof(pidpath), CurEnv);
+ (void) unlink(pidpath);
+ }
+
+ /* reset uid for process accounting */
+ endpwent();
+ sm_mbdb_terminate();
+#if _FFR_MEMSTAT
+ (void) sm_memstat_close();
+#endif /* _FFR_MEMSTAT */
+ (void) setuid(RealUid);
+#if SM_HEAP_CHECK
+ /* dump the heap, if we are checking for memory leaks */
+ if (sm_debug_active(&SmHeapCheck, 2))
+ sm_heap_report(smioout,
+ sm_debug_level(&SmHeapCheck) - 1);
+#endif /* SM_HEAP_CHECK */
+ if (sm_debug_active(&SmXtrapReport, 1))
+ sm_dprintf("xtrap count = %d\n", SmXtrapCount);
+ if (cleanup)
+ exit(exitstat);
+ else
+ _exit(exitstat);
+ SM_END_TRY
+}
+/*
+** INTINDEBUG -- signal handler for SIGINT in -bt mode
+**
+** Parameters:
+** sig -- incoming signal.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** 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
+** DOING.
+*/
+
+/* Type of an exception generated on SIGINT during address test mode. */
+static const SM_EXC_TYPE_T EtypeInterrupt =
+{
+ SmExcTypeMagic,
+ "S:mta.interrupt",
+ "",
+ sm_etype_printf,
+ "interrupt",
+};
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+intindebug(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, intindebug);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+ errno = save_errno;
+ sm_exc_raisenew_x(&EtypeInterrupt);
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** SIGTERM -- SIGTERM handler for the daemon
+**
+** Parameters:
+** sig -- signal number.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets ShutdownRequest which will hopefully trigger
+** the daemon to exit.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+sigterm(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, sigterm);
+ ShutdownRequest = "signal";
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** SIGHUP -- handle a SIGHUP signal
+**
+** Parameters:
+** sig -- incoming signal.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets RestartRequest which should cause the daemon
+** to restart.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+sighup(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, sighup);
+ RestartRequest = "signal";
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** SIGPIPE -- signal handler for SIGPIPE
+**
+** Parameters:
+** sig -- incoming signal.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Sets StopRequest which should cause the mailq/hoststatus
+** display to stop.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+*/
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+sigpipe(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, sigpipe);
+ StopRequest = true;
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+/*
+** INTSIG -- clean up on interrupt
+**
+** This just arranges to exit. It pessimizes in that it
+** may resend a message.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** 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
+** DOING.
+**
+** XXX: More work is needed for this signal handler.
+*/
+
+/* ARGSUSED */
+SIGFUNC_DECL
+intsig(sig)
+ int sig;
+{
+ bool drop = false;
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, intsig);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+ sm_allsignals(true);
+
+ if (sig != 0 && LogLevel > 79)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
+ FileName = NULL;
+
+ /* Clean-up on aborted stdin message submission */
+ if (CurEnv->e_id != NULL &&
+ (OpMode == MD_SMTP ||
+ OpMode == MD_DELIVER ||
+ OpMode == MD_ARPAFTP))
+ {
+ register ADDRESS *q;
+
+ /* don't return an error indication */
+ CurEnv->e_to = NULL;
+ CurEnv->e_flags &= ~EF_FATALERRS;
+ CurEnv->e_flags |= EF_CLRQUEUE;
+
+ /*
+ ** Spin through the addresses and
+ ** mark them dead to prevent bounces
+ */
+
+ for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
+ q->q_state = QS_DONTSEND;
+
+ drop = true;
+ }
+ else if (OpMode != MD_TEST)
+ {
+ unlockqueue(CurEnv);
+ }
+
+ finis(drop, false, EX_OK);
+ /* NOTREACHED */
+}
+/*
+** DISCONNECT -- remove our connection with any foreground process
+**
+** Parameters:
+** 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.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Trys to insure that we are immune to vagaries of
+** the controlling tty.
+*/
+
+void
+disconnect(droplev, e)
+ int droplev;
+ register ENVELOPE *e;
+{
+ int fd;
+
+ if (tTd(52, 1))
+ sm_dprintf("disconnect: In %d Out %d, e=%p\n",
+ sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
+ sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
+ if (tTd(52, 100))
+ {
+ sm_dprintf("don't\n");
+ return;
+ }
+ if (LogLevel > 93)
+ sm_syslog(LOG_DEBUG, e->e_id,
+ "disconnect level %d",
+ droplev);
+
+ /* be sure we don't get nasty signals */
+ (void) sm_signal(SIGINT, SIG_IGN);
+ (void) sm_signal(SIGQUIT, SIG_IGN);
+
+ /* we can't communicate with our caller, so.... */
+ HoldErrs = true;
+ CurEnv->e_errormode = EM_MAIL;
+ Verbose = 0;
+ DisConnected = true;
+
+ /* all input from /dev/null */
+ if (InChannel != smioin)
+ {
+ (void) sm_io_close(InChannel, SM_TIME_DEFAULT);
+ InChannel = smioin;
+ }
+ if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
+ SM_IO_RDONLY, NULL, smioin) == NULL)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: sm_io_reopen(\"%s\") failed: %s",
+ SM_PATH_DEVNULL, sm_errstring(errno));
+
+ /*
+ ** output to the transcript
+ ** We also compare the fd numbers here since OutChannel
+ ** might be a layer on top of smioout due to encryption
+ ** (see sfsasl.c).
+ */
+
+ if (OutChannel != smioout &&
+ sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
+ sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
+ {
+ (void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
+ OutChannel = smioout;
+
+#if 0
+ /*
+ ** Has smioout been closed? Reopen it.
+ ** This shouldn't happen anymore, the code is here
+ ** just as a reminder.
+ */
+
+ if (smioout->sm_magic == NULL &&
+ sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
+ SM_IO_WRONLY, NULL, smioout) == NULL)
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: sm_io_reopen(\"%s\") failed: %s",
+ SM_PATH_DEVNULL, sm_errstring(errno));
+#endif /* 0 */
+ }
+ if (droplev > 0)
+ {
+ fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
+ if (fd == -1)
+ {
+ sm_syslog(LOG_ERR, e->e_id,
+ "disconnect: open(\"%s\") failed: %s",
+ SM_PATH_DEVNULL, sm_errstring(errno));
+ }
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+ if (fd >= 0)
+ {
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) dup2(fd, STDERR_FILENO);
+ (void) close(fd);
+ }
+ }
+
+ /* drop our controlling TTY completely if possible */
+ if (droplev > 1)
+ {
+ (void) setsid();
+ errno = 0;
+ }
+
+#if XDEBUG
+ checkfd012("disconnect");
+#endif /* XDEBUG */
+
+ if (LogLevel > 71)
+ sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
+ (int) CurrentPid);
+
+ errno = 0;
+}
+
+static void
+obsolete(argv)
+ char *argv[];
+{
+ register char *ap;
+ register char *op;
+
+ while ((ap = *++argv) != NULL)
+ {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-' || ap[1] == '-')
+ return;
+
+ /* Don't allow users to use "-Q." or "-Q ." */
+ if ((ap[1] == 'Q' && ap[2] == '.') ||
+ (ap[1] == 'Q' && argv[1] != NULL &&
+ argv[1][0] == '.' && argv[1][1] == '\0'))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Can not use -Q.\n");
+ exit(EX_USAGE);
+ }
+
+ /* skip over options that do have a value */
+ op = strchr(OPTIONS, ap[1]);
+ if (op != NULL && *++op == ':' && ap[2] == '\0' &&
+ ap[1] != 'd' &&
+#if defined(sony_news)
+ ap[1] != 'E' && ap[1] != 'J' &&
+#endif /* defined(sony_news) */
+ argv[1] != NULL && argv[1][0] != '-')
+ {
+ argv++;
+ continue;
+ }
+
+ /* If -C doesn't have an argument, use sendmail.cf. */
+#define __DEFPATH "sendmail.cf"
+ if (ap[1] == 'C' && ap[2] == '\0')
+ {
+ *argv = xalloc(sizeof(__DEFPATH) + 2);
+ (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
+ "-C", __DEFPATH);
+ }
+
+ /* If -q doesn't have an argument, run it once. */
+ if (ap[1] == 'q' && ap[2] == '\0')
+ *argv = "-q0";
+
+ /* If -Q doesn't have an argument, disable quarantining */
+ if (ap[1] == 'Q' && ap[2] == '\0')
+ *argv = "-Q.";
+
+ /* if -d doesn't have an argument, use 0-99.1 */
+ if (ap[1] == 'd' && ap[2] == '\0')
+ *argv = "-d0-99.1";
+
+#if defined(sony_news)
+ /* if -E doesn't have an argument, use -EC */
+ if (ap[1] == 'E' && ap[2] == '\0')
+ *argv = "-EC";
+
+ /* if -J doesn't have an argument, use -JJ */
+ if (ap[1] == 'J' && ap[2] == '\0')
+ *argv = "-JJ";
+#endif /* defined(sony_news) */
+ }
+}
+/*
+** AUTH_WARNING -- specify authorization warning
+**
+** Parameters:
+** e -- the current envelope.
+** msg -- the text of the message.
+** args -- arguments to the message.
+**
+** Returns:
+** none.
+*/
+
+void
+#ifdef __STDC__
+auth_warning(register ENVELOPE *e, const char *msg, ...)
+#else /* __STDC__ */
+auth_warning(e, msg, va_alist)
+ register ENVELOPE *e;
+ const char *msg;
+ va_dcl
+#endif /* __STDC__ */
+{
+ char buf[MAXLINE];
+ SM_VA_LOCAL_DECL
+
+ if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
+ {
+ register char *p;
+ static char hostbuf[48];
+
+ if (hostbuf[0] == '\0')
+ {
+ struct hostent *hp;
+
+ hp = myhostname(hostbuf, sizeof(hostbuf));
+#if NETINET6
+ if (hp != NULL)
+ {
+ freehostent(hp);
+ hp = NULL;
+ }
+#endif /* NETINET6 */
+ }
+
+ (void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
+ p = &buf[strlen(buf)];
+ SM_VA_START(ap, msg);
+ (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
+ SM_VA_END(ap);
+ addheader("X-Authentication-Warning", buf, 0, e, true);
+ if (LogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Authentication-Warning: %.400s",
+ buf);
+ }
+}
+/*
+** GETEXTENV -- get from external environment
+**
+** Parameters:
+** envar -- the name of the variable to retrieve
+**
+** Returns:
+** The value, if any.
+*/
+
+static char *
+getextenv(envar)
+ const char *envar;
+{
+ char **envp;
+ int l;
+
+ l = strlen(envar);
+ for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
+ {
+ if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
+ return &(*envp)[l + 1];
+ }
+ return NULL;
+}
+/*
+** SM_SETUSERENV -- set an environment variable in the propagated environment
+**
+** Parameters:
+** 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.
+**
+** Returns:
+** none.
+*/
+
+void
+sm_setuserenv(envar, value)
+ const char *envar;
+ const char *value;
+{
+ int i, l;
+ char **evp = UserEnviron;
+ char *p;
+
+ if (value == NULL)
+ {
+ value = getextenv(envar);
+ if (value == NULL)
+ return;
+ }
+
+ /* XXX enforce reasonable size? */
+ i = strlen(envar) + 1;
+ l = strlen(value) + i + 1;
+ p = (char *) xalloc(l);
+ (void) sm_strlcpyn(p, l, 3, envar, "=", value);
+
+ while (*evp != NULL && strncmp(*evp, p, i) != 0)
+ evp++;
+ if (*evp != NULL)
+ {
+ *evp++ = p;
+ }
+ else if (evp < &UserEnviron[MAXUSERENVIRON])
+ {
+ *evp++ = p;
+ *evp = NULL;
+ }
+
+ /* make sure it is in our environment as well */
+ if (putenv(p) < 0)
+ syserr("sm_setuserenv: putenv(%s) failed", p);
+}
+/*
+** DUMPSTATE -- dump state
+**
+** For debugging.
+*/
+
+void
+dumpstate(when)
+ char *when;
+{
+ register char *j = macvalue('j', CurEnv);
+ int rs;
+ extern int NextMacroId;
+
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "--- dumping state on %s: $j = %s ---",
+ when,
+ j == NULL ? "<NULL>" : j);
+ if (j != NULL)
+ {
+ if (!wordinclass(j, 'w'))
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "*** $j not in $=w ***");
+ }
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
+ NextMacroId, MAXMACROID);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
+ printopenfds(true);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
+ mci_dump_all(smioout, true);
+ rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
+ if (rs > 0)
+ {
+ int status;
+ register char **pvp;
+ char *pv[MAXATOM + 1];
+
+ pv[0] = NULL;
+ status = REWRITE(pv, rs, CurEnv);
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
+ "--- ruleset debug_dumpstate returns stat %d, pv: ---",
+ status);
+ for (pvp = pv; *pvp != NULL; pvp++)
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
+ }
+ sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
+}
+
+#ifdef SIGUSR1
+/*
+** SIGUSR1 -- Signal a request to dump state.
+**
+** Parameters:
+** sig -- calling signal.
+**
+** Returns:
+** none.
+**
+** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
+** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
+** DOING.
+**
+** XXX: More work is needed for this signal handler.
+*/
+
+/* ARGSUSED */
+static SIGFUNC_DECL
+sigusr1(sig)
+ int sig;
+{
+ int save_errno = errno;
+
+ FIX_SYSV_SIGNAL(sig, sigusr1);
+ errno = save_errno;
+ CHECK_CRITICAL(sig);
+ dumpstate("user signal");
+# if SM_HEAP_CHECK
+ dumpstab();
+# endif /* SM_HEAP_CHECK */
+ errno = save_errno;
+ return SIGFUNC_RETURN;
+}
+#endif /* SIGUSR1 */
+
+/*
+** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
+**
+** Parameters:
+** to_real_uid -- if set, drop to the real uid instead
+** of the RunAsUser.
+**
+** Returns:
+** EX_OSERR if the setuid failed.
+** EX_OK otherwise.
+*/
+
+int
+drop_privileges(to_real_uid)
+ bool to_real_uid;
+{
+ int rval = EX_OK;
+ GIDSET_T emptygidset[1];
+
+ if (tTd(47, 1))
+ 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",
+ (int) to_real_uid,
+ (int) RealUid, (int) RealGid,
+ (int) getuid(), (int) getgid(),
+ (int) geteuid(), (int) getegid(),
+ (int) RunAsUid, (int) RunAsGid);
+
+ if (to_real_uid)
+ {
+ RunAsUserName = RealUserName;
+ RunAsUid = RealUid;
+ RunAsGid = RealGid;
+ EffGid = RunAsGid;
+ }
+
+ /* make sure no one can grab open descriptors for secret files */
+ endpwent();
+ sm_mbdb_terminate();
+
+ /* reset group permissions; these can be set later */
+ emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
+
+ /*
+ ** 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)
+ */
+
+ if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
+ {
+ syserr("drop_privileges: setgroups(1, %d) failed",
+ (int) emptygidset[0]);
+ rval = EX_OSERR;
+ }
+
+ /* reset primary group id */
+ if (to_real_uid)
+ {
+ /*
+ ** Drop gid to real gid.
+ ** 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
+ ** effective gid.
+ */
+
+#if HASSETEGID
+ if (setegid(RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setegid(%d) failed",
+ (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+#else /* HASSETEGID */
+# if HASSETREGID
+ if (setregid(RunAsGid, RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setregid(%d, %d) failed",
+ (int) RunAsGid, (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+# else /* HASSETREGID */
+# if HASSETRESGID
+ if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
+ {
+ syserr("drop_privileges: setresgid(%d, %d, %d) failed",
+ (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+# endif /* HASSETRESGID */
+# endif /* HASSETREGID */
+#endif /* HASSETEGID */
+ }
+ if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
+ {
+ if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
+ {
+ syserr("drop_privileges: setgid(%d) failed",
+ (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+ errno = 0;
+ if (rval == EX_OK && getegid() != RunAsGid)
+ {
+ syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
+ (int) getegid(), (int) RunAsGid);
+ rval = EX_OSERR;
+ }
+ }
+
+ /* fiddle with uid */
+ if (to_real_uid || RunAsUid != 0)
+ {
+ uid_t euid;
+
+ /*
+ ** 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 (setuid(RunAsUid) < 0 ||
+ geteuid() != RunAsUid ||
+ (getuid() != RunAsUid &&
+ (to_real_uid || geteuid() == 0 || getuid() == 0)))
+ {
+#if HASSETREUID
+ /*
+ ** if ruid != RunAsUid, euid == RunAsUid, then
+ ** try resetting just the real uid, then using
+ ** setuid() to drop the saved-uid as well.
+ */
+
+ if (geteuid() == RunAsUid)
+ {
+ if (setreuid(RunAsUid, -1) < 0)
+ {
+ syserr("drop_privileges: setreuid(%d, -1) failed",
+ (int) RunAsUid);
+ rval = EX_OSERR;
+ }
+ if (setuid(RunAsUid) < 0)
+ {
+ syserr("drop_privileges: second setuid(%d) attempt failed",
+ (int) RunAsUid);
+ rval = EX_OSERR;
+ }
+ }
+ else
+#endif /* HASSETREUID */
+ {
+ syserr("drop_privileges: setuid(%d) failed",
+ (int) RunAsUid);
+ rval = EX_OSERR;
+ }
+ }
+ euid = geteuid();
+ if (RunAsUid != 0 && setuid(0) == 0)
+ {
+ /*
+ ** 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)");
+ rval = EX_OSERR;
+ }
+ else if (RunAsUid != euid && setuid(euid) == 0)
+ {
+ /*
+ ** 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");
+ rval = EX_OSERR;
+ }
+ }
+
+ if ((to_real_uid || RunAsGid != 0) &&
+ rval == EX_OK && RunAsGid != EffGid &&
+ getuid() != 0 && geteuid() != 0)
+ {
+ errno = 0;
+ if (setgid(EffGid) == 0)
+ {
+ syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
+ (int) EffGid);
+ rval = EX_OSERR;
+ }
+ }
+
+ if (tTd(47, 5))
+ {
+ sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
+ (int) geteuid(), (int) getuid(),
+ (int) getegid(), (int) getgid());
+ sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
+ (int) RunAsUid, (int) RunAsGid);
+ if (tTd(47, 10))
+ sm_dprintf("drop_privileges: rval = %d\n", rval);
+ }
+ return rval;
+}
+/*
+** FILL_FD -- make sure a file descriptor has been properly allocated
+**
+** Used to make sure that stdin/out/err are allocated on startup
+**
+** Parameters:
+** fd -- the file descriptor to be filled.
+** where -- a string used for logging. If NULL, this is
+** being called on startup, and logging should
+** not be done.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** possibly changes MissingFds
+*/
+
+void
+fill_fd(fd, where)
+ int fd;
+ char *where;
+{
+ int i;
+ struct stat stbuf;
+
+ if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
+ return;
+
+ if (where != NULL)
+ syserr("fill_fd: %s: fd %d not open", where, fd);
+ else
+ MissingFds |= 1 << fd;
+ i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
+ if (i < 0)
+ {
+ syserr("!fill_fd: %s: cannot open %s",
+ where == NULL ? "startup" : where, SM_PATH_DEVNULL);
+ }
+ if (fd != i)
+ {
+ (void) dup2(i, fd);
+ (void) close(i);
+ }
+}
+/*
+** SM_PRINTOPTIONS -- print options
+**
+** Parameters:
+** options -- array of options.
+**
+** Returns:
+** none.
+*/
+
+static void
+sm_printoptions(options)
+ char **options;
+{
+ int ll;
+ char **av;
+
+ av = options;
+ ll = 7;
+ while (*av != NULL)
+ {
+ if (ll + strlen(*av) > 63)
+ {
+ sm_dprintf("\n");
+ ll = 0;
+ }
+ if (ll == 0)
+ sm_dprintf("\t\t");
+ else
+ sm_dprintf(" ");
+ sm_dprintf("%s", *av);
+ ll += strlen(*av++) + 1;
+ }
+ sm_dprintf("\n");
+}
+
+/*
+** TO8BIT -- convert \octal sequences in a test mode input line
+**
+** Parameters:
+** str -- the input line.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** replaces \0octal in str with octal value.
+*/
+
+static bool to8bit __P((char *));
+
+static bool
+to8bit(str)
+ char *str;
+{
+ int c, len;
+ char *out, *in;
+ bool changed;
+
+ if (str == NULL)
+ return false;
+ in = out = str;
+ changed = false;
+ len = 0;
+ while ((c = (*str++ & 0377)) != '\0')
+ {
+ int oct, nxtc;
+
+ ++len;
+ if (c == '\\' &&
+ (nxtc = (*str & 0377)) == '0')
+ {
+ oct = 0;
+ while ((nxtc = (*str & 0377)) != '\0' &&
+ isascii(nxtc) && isdigit(nxtc))
+ {
+ oct <<= 3;
+ oct += nxtc - '0';
+ ++str;
+ ++len;
+ }
+ changed = true;
+ c = oct;
+ }
+ *out++ = c;
+ }
+ *out++ = c;
+ if (changed)
+ {
+ char *q;
+
+ q = quote_internal_chars(in, in, &len);
+ if (q != in)
+ sm_strlcpy(in, q, len);
+ }
+ return changed;
+}
+
+/*
+** TESTMODELINE -- process a test mode input line
+**
+** Parameters:
+** line -- the input line.
+** e -- the current environment.
+** Syntax:
+** # a comment
+** .X process X as a configuration line
+** =X dump a configuration item (such as mailers)
+** $X dump a macro or class
+** /X try an activity
+** X normal process through rule set X
+*/
+
+static void
+testmodeline(line, e)
+ char *line;
+ ENVELOPE *e;
+{
+ register char *p;
+ char *q;
+ auto char *delimptr;
+ int mid;
+ int i, rs;
+ STAB *map;
+ char **s;
+ struct rewrite *rw;
+ ADDRESS a;
+ char *lbp;
+ auto int lbs;
+ static int tryflags = RF_COPYNONE;
+ char exbuf[MAXLINE];
+ char lbuf[MAXLINE];
+ extern unsigned char TokTypeNoC[];
+ bool eightbit;
+
+ /* skip leading spaces */
+ while (*line == ' ')
+ line++;
+
+ lbp = NULL;
+ eightbit = false;
+ switch (line[0])
+ {
+ case '#':
+ case '\0':
+ return;
+
+ case '?':
+ help("-bt", e);
+ return;
+
+ case '.': /* config-style settings */
+ switch (line[1])
+ {
+ case 'D':
+ mid = macid_parse(&line[2], &delimptr);
+ if (mid == 0)
+ return;
+ lbs = sizeof(lbuf);
+ lbp = translate_dollars(delimptr, lbuf, &lbs);
+ macdefine(&e->e_macro, A_TEMP, mid, lbp);
+ if (lbp != lbuf)
+ SM_FREE(lbp);
+ break;
+
+ case 'C':
+ if (line[2] == '\0') /* not to call syserr() */
+ return;
+
+ mid = macid_parse(&line[2], &delimptr);
+ if (mid == 0)
+ return;
+ lbs = sizeof(lbuf);
+ lbp = translate_dollars(delimptr, lbuf, &lbs);
+ expand(lbp, exbuf, sizeof(exbuf), e);
+ if (lbp != lbuf)
+ SM_FREE(lbp);
+ p = exbuf;
+ while (*p != '\0')
+ {
+ register char *wd;
+ char delim;
+
+ while (*p != '\0' && isascii(*p) && isspace(*p))
+ p++;
+ wd = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ delim = *p;
+ *p = '\0';
+ if (wd[0] != '\0')
+ setclass(mid, wd);
+ *p = delim;
+ }
+ break;
+
+ case '\0':
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: .[DC]macro value(s)\n");
+ break;
+
+ default:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \".\" command %s\n", line);
+ break;
+ }
+ return;
+
+ case '=': /* config-style settings */
+ switch (line[1])
+ {
+ case 'S': /* dump rule set */
+ rs = strtorwset(&line[2], NULL, ST_FIND);
+ if (rs < 0)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Undefined ruleset %s\n", &line[2]);
+ return;
+ }
+ rw = RewriteRules[rs];
+ if (rw == NULL)
+ return;
+ do
+ {
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ 'R');
+ s = rw->r_lhs;
+ while (*s != NULL)
+ {
+ xputs(smioout, *s++);
+ (void) sm_io_putc(smioout,
+ SM_TIME_DEFAULT, ' ');
+ }
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ '\t');
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ '\t');
+ s = rw->r_rhs;
+ while (*s != NULL)
+ {
+ xputs(smioout, *s++);
+ (void) sm_io_putc(smioout,
+ SM_TIME_DEFAULT, ' ');
+ }
+ (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
+ '\n');
+ } while ((rw = rw->r_next) != NULL);
+ break;
+
+ case 'M':
+ for (i = 0; i < MAXMAILERS; i++)
+ {
+ if (Mailer[i] != NULL)
+ printmailer(smioout, Mailer[i]);
+ }
+ break;
+
+ case '\0':
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: =Sruleset or =M\n");
+ break;
+
+ default:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \"=\" command %s\n", line);
+ break;
+ }
+ return;
+
+ case '-': /* set command-line-like opts */
+ switch (line[1])
+ {
+ case 'd':
+ tTflag(&line[2]);
+ break;
+
+ case '\0':
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: -d{debug arguments}\n");
+ break;
+
+ default:
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \"-\" command %s\n", line);
+ break;
+ }
+ return;
+
+ case '$':
+ if (line[1] == '=')
+ {
+ mid = macid(&line[2]);
+ if (mid != 0)
+ stabapply(dump_class, mid);
+ return;
+ }
+ mid = macid(&line[1]);
+ if (mid == 0)
+ return;
+ p = macvalue(mid, e);
+ if (p == NULL)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Undefined\n");
+ else
+ {
+ xputs(smioout, p);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\n");
+ }
+ return;
+
+ case '/': /* miscellaneous commands */
+ p = &line[strlen(line)];
+ while (--p >= line && isascii(*p) && isspace(*p))
+ *p = '\0';
+ p = strpbrk(line, " \t");
+ if (p != NULL)
+ {
+ while (isascii(*p) && isspace(*p))
+ *p++ = '\0';
+ }
+ else
+ p = "";
+ if (line[1] == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /[canon|map|mx|parse|try|tryflags]\n");
+ return;
+ }
+ if (sm_strcasecmp(&line[1], "quit") == 0)
+ {
+ CurEnv->e_id = NULL;
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+ if (sm_strcasecmp(&line[1], "mx") == 0)
+ {
+#if NAMED_BIND
+ /* look up MX records */
+ int nmx;
+ auto int rcode;
+ char *mxhosts[MAXMXHOSTS + 1];
+
+ if (*p == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /mx address\n");
+ return;
+ }
+ nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
+ NULL);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "getmxrr(%s) returns %d value(s):\n",
+ p, nmx);
+ for (i = 0; i < nmx; i++)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\t%s\n", mxhosts[i]);
+#else /* NAMED_BIND */
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No MX code compiled in\n");
+#endif /* NAMED_BIND */
+ }
+ else if (sm_strcasecmp(&line[1], "canon") == 0)
+ {
+ char host[MAXHOSTNAMELEN];
+
+ if (*p == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /canon address\n");
+ return;
+ }
+ else if (sm_strlcpy(host, p, sizeof(host)) >= sizeof(host))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Name too long\n");
+ return;
+ }
+ (void) getcanonname(host, sizeof(host), !HasWildcardMX,
+ NULL);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "getcanonname(%s) returns %s\n",
+ p, host);
+ }
+ else if (sm_strcasecmp(&line[1], "map") == 0)
+ {
+ auto int rcode = EX_OK;
+ char *av[2];
+
+ if (*p == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /map mapname key\n");
+ return;
+ }
+ for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
+ continue;
+ if (*q == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No key specified\n");
+ return;
+ }
+ *q++ = '\0';
+ map = stab(p, ST_MAP, ST_FIND);
+ if (map == NULL)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Map named \"%s\" not found\n", p);
+ return;
+ }
+ if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
+ !openmap(&(map->s_map)))
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Map named \"%s\" not open\n", p);
+ return;
+ }
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "map_lookup: %s (%s) ", p, q);
+ av[0] = q;
+ av[1] = NULL;
+ p = (*map->s_map.map_class->map_lookup)
+ (&map->s_map, q, av, &rcode);
+ if (p == NULL)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "no match (%d)\n",
+ rcode);
+ else
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "returns %s (%d)\n", p,
+ rcode);
+ }
+ else if (sm_strcasecmp(&line[1], "try") == 0)
+ {
+ MAILER *m;
+ STAB *st;
+ auto int rcode = EX_OK;
+
+ q = strpbrk(p, " \t");
+ if (q != NULL)
+ {
+ while (isascii(*q) && isspace(*q))
+ *q++ = '\0';
+ }
+ if (q == NULL || *q == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /try mailer address\n");
+ return;
+ }
+ st = stab(p, ST_MAILER, ST_FIND);
+ if (st == NULL)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown mailer %s\n", p);
+ return;
+ }
+ m = st->s_mailer;
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Trying %s %s address %s for mailer %s\n",
+ bitset(RF_HEADERADDR, tryflags) ? "header"
+ : "envelope",
+ bitset(RF_SENDERADDR, tryflags) ? "sender"
+ : "recipient", q, p);
+ p = remotename(q, m, tryflags, &rcode, CurEnv);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Rcode = %d, addr = %s\n",
+ rcode, p == NULL ? "<NULL>" : p);
+ e->e_to = NULL;
+ }
+ else if (sm_strcasecmp(&line[1], "tryflags") == 0)
+ {
+ if (*p == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
+ return;
+ }
+ for (; *p != '\0'; p++)
+ {
+ switch (*p)
+ {
+ case 'H':
+ case 'h':
+ tryflags |= RF_HEADERADDR;
+ break;
+
+ case 'E':
+ case 'e':
+ tryflags &= ~RF_HEADERADDR;
+ break;
+
+ case 'S':
+ case 's':
+ tryflags |= RF_SENDERADDR;
+ break;
+
+ case 'R':
+ case 'r':
+ tryflags &= ~RF_SENDERADDR;
+ break;
+ }
+ }
+ exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
+ exbuf[1] = ' ';
+ exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
+ exbuf[3] = '\0';
+ macdefine(&e->e_macro, A_TEMP,
+ macid("{addr_type}"), exbuf);
+ }
+ else if (sm_strcasecmp(&line[1], "parse") == 0)
+ {
+ if (*p == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Usage: /parse address\n");
+ return;
+ }
+ q = crackaddr(p, e);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Cracked address = ");
+ xputs(smioout, q);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "\nParsing %s %s address\n",
+ bitset(RF_HEADERADDR, tryflags) ?
+ "header" : "envelope",
+ bitset(RF_SENDERADDR, tryflags) ?
+ "sender" : "recipient");
+ if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
+ == NULL)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Cannot parse\n");
+ else if (a.q_host != NULL && a.q_host[0] != '\0')
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "mailer %s, host %s, user %s\n",
+ a.q_mailer->m_name,
+ a.q_host,
+ a.q_user);
+ else
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "mailer %s, user %s\n",
+ a.q_mailer->m_name,
+ a.q_user);
+ e->e_to = NULL;
+ }
+ else if (sm_strcasecmp(&line[1], "header") == 0)
+ {
+ unsigned long ul;
+
+ ul = chompheader(p, CHHDR_CHECK|CHHDR_USER, NULL, e);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "ul = %lu\n", ul);
+ }
+ else
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Unknown \"/\" command %s\n",
+ line);
+ }
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+ return;
+ }
+
+ for (p = line; isascii(*p) && isspace(*p); p++)
+ continue;
+ q = p;
+ while (*p != '\0' && !(isascii(*p) && isspace(*p)))
+ p++;
+ if (*p == '\0')
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "No address!\n");
+ return;
+ }
+ *p = '\0';
+ if (tTd(23, 101))
+ eightbit = to8bit(p + 1);
+ if (invalidaddr(p + 1, NULL, true))
+ return;
+ do
+ {
+ register char **pvp;
+ char pvpbuf[PSBUFSIZE];
+
+ pvp = prescan(++p, ',', pvpbuf, sizeof(pvpbuf), &delimptr,
+ ConfigLevel >= 9 ? TokTypeNoC : ExtTokenTab, false);
+ if (pvp == NULL)
+ continue;
+ p = q;
+ while (*p != '\0')
+ {
+ int status;
+
+ rs = strtorwset(p, NULL, ST_FIND);
+ if (rs < 0)
+ {
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Undefined ruleset %s\n",
+ p);
+ break;
+ }
+ status = REWRITE(pvp, rs, e);
+ if (status != EX_OK)
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "== Ruleset %s (%d) status %d\n",
+ p, rs, status);
+ else if (eightbit)
+ {
+ cataddr(pvp, NULL, exbuf, sizeof(exbuf), '\0',
+ true);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "cataddr: %s\n",
+ str2prt(exbuf));
+ }
+ while (*p != '\0' && *p++ != ',')
+ continue;
+ }
+ } while (*(p = delimptr) != '\0');
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
+}
+
+static void
+dump_class(s, id)
+ register STAB *s;
+ int id;
+{
+ if (s->s_symtype != ST_CLASS)
+ return;
+ if (bitnset(bitidx(id), s->s_class))
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "%s\n", s->s_name);
+}
+
+/*
+** 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.
+*/
+
+const SM_EXC_TYPE_T EtypeQuickAbort =
+{
+ SmExcTypeMagic,
+ "E:mta.quickabort",
+ "i",
+ sm_etype_printf,
+ "quick abort %0",
+};
OpenPOWER on IntegriCloud