]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/main.c
Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for details
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / main.c
1 /*
2  * Copyright (c) 1998-2006, 2008, 2009, 2011 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13
14 #define _DEFINE
15 #include <sendmail.h>
16 #include <sm/sendmail.h>
17 #include <sm/xtrap.h>
18 #include <sm/signal.h>
19 #include <tls.h>
20
21 #ifndef lint
22 SM_UNUSED(static char copyright[]) =
23 "@(#) Copyright (c) 1998-2013 Proofpoint, Inc. and its suppliers.\n\
24         All rights reserved.\n\
25      Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
26      Copyright (c) 1988, 1993\n\
27         The Regents of the University of California.  All rights reserved.\n";
28 #endif /* ! lint */
29
30 SM_RCSID("@(#)$Id: main.c,v 8.988 2013-11-23 02:52:37 gshapiro Exp $")
31
32
33 #if NETINET || NETINET6
34 # include <arpa/inet.h>
35 # if DANE
36 #  include "sm_resolve.h"
37 # endif
38 #endif
39
40 /* for getcfname() */
41 #include <sendmail/pathnames.h>
42 #include <ratectrl.h>
43
44 static SM_DEBUG_T
45 DebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
46         "@(#)$Debug: no_persistent_restart - don't restart, log only $");
47
48 static void     dump_class __P((STAB *, int));
49 static void     obsolete __P((char **));
50 static void     testmodeline __P((char *, ENVELOPE *));
51 static char     *getextenv __P((const char *));
52 static void     sm_printoptions __P((char **));
53 static SIGFUNC_DECL     intindebug __P((int));
54 static SIGFUNC_DECL     sighup __P((int));
55 static SIGFUNC_DECL     sigpipe __P((int));
56 static SIGFUNC_DECL     sigterm __P((int));
57 #ifdef SIGUSR1
58 static SIGFUNC_DECL     sigusr1 __P((int));
59 #endif
60
61 /*
62 **  SENDMAIL -- Post mail to a set of destinations.
63 **
64 **      This is the basic mail router.  All user mail programs should
65 **      call this routine to actually deliver mail.  Sendmail in
66 **      turn calls a bunch of mail servers that do the real work of
67 **      delivering the mail.
68 **
69 **      Sendmail is driven by settings read in from /etc/mail/sendmail.cf
70 **      (read by readcf.c).
71 **
72 **      Usage:
73 **              /usr/lib/sendmail [flags] addr ...
74 **
75 **              See the associated documentation for details.
76 **
77 **      Authors:
78 **              Eric Allman, UCB/INGRES (until 10/81).
79 **                           Britton-Lee, Inc., purveyors of fine
80 **                              database computers (11/81 - 10/88).
81 **                           International Computer Science Institute
82 **                              (11/88 - 9/89).
83 **                           UCB/Mammoth Project (10/89 - 7/95).
84 **                           InReference, Inc. (8/95 - 1/97).
85 **                           Sendmail, Inc. (1/98 - 9/13).
86 **              The support of my employers is gratefully acknowledged.
87 **                      Few of them (Britton-Lee in particular) have had
88 **                      anything to gain from my involvement in this project.
89 **
90 **              Gregory Neil Shapiro,
91 **                      Worcester Polytechnic Institute (until 3/98).
92 **                      Sendmail, Inc. (3/98 - 10/13).
93 **                      Proofpoint, Inc. (10/13 - present).
94 **
95 **              Claus Assmann,
96 **                      Sendmail, Inc. (12/98 - 10/13).
97 **                      Proofpoint, Inc. (10/13 - present).
98 */
99
100 char            *FullName;      /* sender's full name */
101 ENVELOPE        BlankEnvelope;  /* a "blank" envelope */
102 static ENVELOPE MainEnvelope;   /* the envelope around the basic letter */
103 ADDRESS         NullAddress =   /* a null address */
104                 { "", "", NULL, "" };
105 char            *CommandLineArgs;       /* command line args for pid file */
106 bool            Warn_Q_option = false;  /* warn about Q option use */
107 static int      MissingFds = 0; /* bit map of fds missing on startup */
108 char            *Mbdb = "pw";   /* mailbox database defaults to /etc/passwd */
109
110 #ifdef NGROUPS_MAX
111 GIDSET_T        InitialGidSet[NGROUPS_MAX];
112 #endif
113
114 #define MAXCONFIGLEVEL  10      /* highest config version level known */
115
116 #if SASL
117 static sasl_callback_t srvcallbacks[] =
118 {
119         {       SASL_CB_VERIFYFILE,     (sasl_callback_ft)&safesaslfile,        NULL    },
120         {       SASL_CB_PROXY_POLICY,   (sasl_callback_ft)&proxy_policy,        NULL    },
121         {       SASL_CB_LIST_END,       NULL,           NULL    }
122 };
123 #endif /* SASL */
124
125 unsigned int    SubmitMode;
126 int             SyslogPrefixLen; /* estimated length of syslog prefix */
127 #define PIDLEN          6       /* pid length for computing SyslogPrefixLen */
128 #ifndef SL_FUDGE
129 # define SL_FUDGE       10      /* fudge offset for SyslogPrefixLen */
130 #endif
131 #define SLDLL           8       /* est. length of default syslog label */
132
133
134 /* Some options are dangerous to allow users to use in non-submit mode */
135 #define CHECK_AGAINST_OPMODE(cmd)                                       \
136 {                                                                       \
137         if (extraprivs &&                                               \
138             OpMode != MD_DELIVER && OpMode != MD_SMTP &&                \
139             OpMode != MD_ARPAFTP && OpMode != MD_CHECKCONFIG &&         \
140             OpMode != MD_VERIFY && OpMode != MD_TEST)                   \
141         {                                                               \
142                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,          \
143                                      "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
144                        (cmd));                                          \
145                 break;                                                  \
146         }                                                               \
147         if (extraprivs && queuerun)                                     \
148         {                                                               \
149                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,          \
150                                      "WARNING: Ignoring submission mode -%c option with -q\n", \
151                        (cmd));                                          \
152                 break;                                                  \
153         }                                                               \
154 }
155
156 int
157 main(argc, argv, envp)
158         int argc;
159         char **argv;
160         char **envp;
161 {
162         register char *p;
163         char **av;
164         extern char Version[];
165         char *ep, *from;
166         STAB *st;
167         register int i;
168         int j;
169         int dp;
170         int fill_errno;
171         int qgrp = NOQGRP;              /* queue group to process */
172         bool safecf = true;
173         BITMAP256 *p_flags = NULL;      /* daemon flags */
174         bool warn_C_flag = false;
175         bool auth = true;               /* whether to set e_auth_param */
176         char warn_f_flag = '\0';
177         bool run_in_foreground = false; /* -bD mode */
178         bool queuerun = false, debug = false;
179         struct passwd *pw;
180         struct hostent *hp;
181         char *nullserver = NULL;
182         char *authinfo = NULL;
183         char *sysloglabel = NULL;       /* label for syslog */
184         char *conffile = NULL;          /* name of .cf file */
185         char *queuegroup = NULL;        /* queue group to process */
186         char *quarantining = NULL;      /* quarantine queue items? */
187         bool extraprivs;
188         bool forged, negate;
189         bool queuepersistent = false;   /* queue runner process runs forever */
190         bool foregroundqueue = false;   /* queue run in foreground */
191         bool save_val;                  /* to save some bool var. */
192         int cftype;                     /* which cf file to use? */
193         SM_FILE_T *smdebug;
194         static time_t starttime = 0;    /* when was process started */
195         struct stat traf_st;            /* for TrafficLog FIFO check */
196         char buf[MAXLINE];
197         char jbuf[MAXHOSTNAMELEN];      /* holds MyHostName */
198         static char rnamebuf[MAXNAME];  /* holds RealUserName */
199         char *emptyenviron[1];
200 #if STARTTLS
201         bool tls_ok;
202 #endif
203         QUEUE_CHAR *new;
204         ENVELOPE *e;
205         extern int DtableSize;
206         extern int optind;
207         extern int opterr;
208         extern char *optarg;
209         extern char **environ;
210 #if SASL
211         extern void sm_sasl_init __P((void));
212 #endif
213
214 #if USE_ENVIRON
215         envp = environ;
216 #endif
217
218         /* turn off profiling */
219         SM_PROF(0);
220
221         /* install default exception handler */
222         sm_exc_newthread(fatal_error);
223
224         /* set the default in/out channel so errors reported to screen */
225         InChannel = smioin;
226         OutChannel = smioout;
227
228         /*
229         **  Check to see if we reentered.
230         **      This would normally happen if e_putheader or e_putbody
231         **      were NULL when invoked.
232         */
233
234         if (starttime != 0)
235         {
236                 syserr("main: reentered!");
237                 abort();
238         }
239         starttime = curtime();
240
241         /* avoid null pointer dereferences */
242         TermEscape.te_rv_on = TermEscape.te_under_on = TermEscape.te_normal = "";
243
244         RealUid = getuid();
245         RealGid = getgid();
246
247         /* Check if sendmail is running with extra privs */
248         extraprivs = (RealUid != 0 &&
249                       (geteuid() != getuid() || getegid() != getgid()));
250
251         CurrentPid = getpid();
252
253         /* get whatever .cf file is right for the opmode */
254         cftype = SM_GET_RIGHT_CF;
255
256         /* in 4.4BSD, the table can be huge; impose a reasonable limit */
257         DtableSize = getdtsize();
258         if (DtableSize > 256)
259                 DtableSize = 256;
260
261         /*
262         **  Be sure we have enough file descriptors.
263         **      But also be sure that 0, 1, & 2 are open.
264         */
265
266         /* reset errno and fill_errno; the latter is used way down below */
267         errno = fill_errno = 0;
268         fill_fd(STDIN_FILENO, NULL);
269         if (errno != 0)
270                 fill_errno = errno;
271         fill_fd(STDOUT_FILENO, NULL);
272         if (errno != 0)
273                 fill_errno = errno;
274         fill_fd(STDERR_FILENO, NULL);
275         if (errno != 0)
276                 fill_errno = errno;
277
278         sm_closefrom(STDERR_FILENO + 1, DtableSize);
279         errno = 0;
280         smdebug = NULL;
281
282 #if LOG
283 # ifndef SM_LOG_STR
284 #  define SM_LOG_STR    "sendmail"
285 # endif
286 # ifdef LOG_MAIL
287         openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
288 # else
289         openlog(SM_LOG_STR, LOG_PID);
290 # endif
291 #endif /* LOG */
292
293         /*
294         **  Seed the random number generator.
295         **  Used for queue file names, picking a queue directory, and
296         **  MX randomization.
297         */
298
299         seed_random();
300
301         /* do machine-dependent initializations */
302         init_md(argc, argv);
303
304
305         SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
306
307         /* reset status from syserr() calls for missing file descriptors */
308         Errors = 0;
309         ExitStat = EX_OK;
310
311         SubmitMode = SUBMIT_UNKNOWN;
312 #if _FFR_LOCAL_DAEMON
313         LocalDaemon = false;
314 # if NETINET6
315         V6LoopbackAddrFound = false;
316 # endif
317 #endif
318 #if XDEBUG
319         checkfd012("after openlog");
320 #endif
321
322         tTsetup(tTdvect, sizeof(tTdvect), "0-99.1,*_trace_*.1");
323
324 #ifdef NGROUPS_MAX
325         /* save initial group set for future checks */
326         i = getgroups(NGROUPS_MAX, InitialGidSet);
327         if (i <= 0)
328         {
329                 InitialGidSet[0] = (GID_T) -1;
330                 i = 0;
331         }
332         while (i < NGROUPS_MAX)
333                 InitialGidSet[i++] = InitialGidSet[0];
334 #endif /* NGROUPS_MAX */
335
336         /* drop group id privileges (RunAsUser not yet set) */
337         dp = drop_privileges(false);
338         setstat(dp);
339
340 #ifdef SIGUSR1
341         /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
342         if (!extraprivs)
343         {
344                 /* arrange to dump state on user-1 signal */
345                 (void) sm_signal(SIGUSR1, sigusr1);
346         }
347         else
348         {
349                 /* ignore user-1 signal */
350                 (void) sm_signal(SIGUSR1, SIG_IGN);
351         }
352 #endif /* SIGUSR1 */
353
354         /* initialize for setproctitle */
355         initsetproctitle(argc, argv, envp);
356
357         /* Handle any non-getoptable constructions. */
358         obsolete(argv);
359
360         /*
361         **  Do a quick prescan of the argument list.
362         */
363
364
365         /* find initial opMode */
366         OpMode = MD_DELIVER;
367         av = argv;
368         p = strrchr(*av, '/');
369         if (p++ == NULL)
370                 p = *av;
371         if (strcmp(p, "newaliases") == 0)
372                 OpMode = MD_INITALIAS;
373         else if (strcmp(p, "mailq") == 0)
374                 OpMode = MD_PRINT;
375         else if (strcmp(p, "smtpd") == 0)
376                 OpMode = MD_DAEMON;
377         else if (strcmp(p, "hoststat") == 0)
378                 OpMode = MD_HOSTSTAT;
379         else if (strcmp(p, "purgestat") == 0)
380                 OpMode = MD_PURGESTAT;
381
382 #if defined(__osf__) || defined(_AIX3)
383 # 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"
384 #endif /* defined(__osf__) || defined(_AIX3) */
385 #if defined(sony_news)
386 # 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:"
387 #endif /* defined(sony_news) */
388 #ifndef OPTIONS
389 # define OPTIONS        "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
390 #endif
391
392         /* Set to 0 to allow -b; need to check optarg before using it! */
393         opterr = 0;
394         while ((j = getopt(argc, argv, OPTIONS)) != -1)
395         {
396                 switch (j)
397                 {
398                   case 'b':     /* operations mode */
399                         j = (optarg == NULL) ? ' ' : *optarg;
400                         switch (j)
401                         {
402                           case MD_DAEMON:
403                           case MD_FGDAEMON:
404                           case MD_SMTP:
405                           case MD_INITALIAS:
406                           case MD_DELIVER:
407                           case MD_VERIFY:
408                           case MD_TEST:
409                           case MD_PRINT:
410                           case MD_PRINTNQE:
411                           case MD_HOSTSTAT:
412                           case MD_PURGESTAT:
413                           case MD_ARPAFTP:
414                           case MD_CHECKCONFIG:
415                                 OpMode = j;
416                                 break;
417
418 #if _FFR_LOCAL_DAEMON
419                           case MD_LOCAL:
420                                 OpMode = MD_DAEMON;
421                                 LocalDaemon = true;
422                                 break;
423 #endif /* _FFR_LOCAL_DAEMON */
424
425                           case MD_FREEZE:
426                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
427                                                      "Frozen configurations unsupported\n");
428                                 return EX_USAGE;
429
430                           default:
431                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
432                                                      "Invalid operation mode %c\n",
433                                                      j);
434                                 return EX_USAGE;
435                         }
436                         break;
437
438                   case 'D':
439                         if (debug)
440                         {
441                                 errno = 0;
442                                 syserr("-D file must be before -d");
443                                 ExitStat = EX_USAGE;
444                                 break;
445                         }
446                         dp = drop_privileges(true);
447                         setstat(dp);
448                         smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
449                                             optarg, SM_IO_APPEND, NULL);
450                         if (smdebug == NULL)
451                         {
452                                 syserr("cannot open %s", optarg);
453                                 ExitStat = EX_CANTCREAT;
454                                 break;
455                         }
456                         sm_debug_setfile(smdebug);
457                         break;
458
459                   case 'd':
460                         debug = true;
461                         tTflag(optarg);
462                         (void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT,
463                                              (char *) NULL, SM_IO_NBF,
464                                              SM_IO_BUFSIZ);
465                         break;
466
467                   case 'G':     /* relay (gateway) submission */
468                         SubmitMode = SUBMIT_MTA;
469                         break;
470
471                   case 'L':
472                         if (optarg == NULL)
473                         {
474                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
475                                                      "option requires an argument -- '%c'",
476                                                      (char) j);
477                                 return EX_USAGE;
478                         }
479                         j = SM_MIN(strlen(optarg), 32) + 1;
480                         sysloglabel = xalloc(j);
481                         (void) sm_strlcpy(sysloglabel, optarg, j);
482                         SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
483                                           SL_FUDGE + j;
484                         break;
485
486                   case 'Q':
487                   case 'q':
488                         /* just check if it is there */
489                         queuerun = true;
490                         break;
491                 }
492         }
493         opterr = 1;
494
495         /* Don't leak queue information via debug flags */
496         if (extraprivs && queuerun && debug)
497         {
498                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
499                                      "WARNING: Can not use -d with -q.  Disabling debugging.\n");
500                 sm_debug_close();
501                 sm_debug_setfile(NULL);
502                 (void) memset(tTdvect, '\0', sizeof(tTdvect));
503         }
504
505 #if LOG
506         if (sysloglabel != NULL)
507         {
508                 /* Sanitize the string */
509                 for (p = sysloglabel; *p != '\0'; p++)
510                 {
511                         if (!isascii(*p) || !isprint(*p) || *p == '%')
512                                 *p = '*';
513                 }
514                 closelog();
515 # ifdef LOG_MAIL
516                 openlog(sysloglabel, LOG_PID, LOG_MAIL);
517 # else
518                 openlog(sysloglabel, LOG_PID);
519 # endif
520         }
521 #endif /* LOG */
522
523         /* set up the blank envelope */
524         BlankEnvelope.e_puthdr = putheader;
525         BlankEnvelope.e_putbody = putbody;
526         BlankEnvelope.e_xfp = NULL;
527         STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
528         CurEnv = &BlankEnvelope;
529         STRUCTCOPY(NullAddress, MainEnvelope.e_from);
530
531         /*
532         **  Set default values for variables.
533         **      These cannot be in initialized data space.
534         */
535
536         setdefaults(&BlankEnvelope);
537         initmacros(&BlankEnvelope);
538
539         /* reset macro */
540         set_op_mode(OpMode);
541         if (OpMode == MD_DAEMON)
542                 DaemonPid = CurrentPid; /* needed for finis() to work */
543
544         pw = sm_getpwuid(RealUid);
545         if (pw != NULL)
546                 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof(rnamebuf));
547         else
548                 (void) sm_snprintf(rnamebuf, sizeof(rnamebuf), "Unknown UID %d",
549                                    (int) RealUid);
550
551         RealUserName = rnamebuf;
552
553         if (tTd(0, 101))
554         {
555                 sm_dprintf("Version %s\n", Version);
556                 finis(false, true, EX_OK);
557                 /* NOTREACHED */
558         }
559
560         /*
561         **  if running non-set-user-ID binary as non-root, pretend
562         **  we are the RunAsUid
563         */
564
565         if (RealUid != 0 && geteuid() == RealUid)
566         {
567                 if (tTd(47, 1))
568                         sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
569                                    (int) RealUid);
570                 RunAsUid = RealUid;
571         }
572         else if (geteuid() != 0)
573                 RunAsUid = geteuid();
574
575         EffGid = getegid();
576         if (RealUid != 0 && EffGid == RealGid)
577                 RunAsGid = RealGid;
578
579         if (tTd(47, 5))
580         {
581                 sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
582                            (int) geteuid(), (int) getuid(),
583                            (int) getegid(), (int) getgid());
584                 sm_dprintf("main: RunAsUser = %d:%d\n",
585                            (int) RunAsUid, (int) RunAsGid);
586         }
587
588         /* save command line arguments */
589         j = 0;
590         for (av = argv; *av != NULL; )
591                 j += strlen(*av++) + 1;
592         SaveArgv = (char **) xalloc(sizeof(char *) * (argc + 1));
593         CommandLineArgs = xalloc(j);
594         p = CommandLineArgs;
595         for (av = argv, i = 0; *av != NULL; )
596         {
597                 int h;
598
599                 SaveArgv[i++] = newstr(*av);
600                 if (av != argv)
601                         *p++ = ' ';
602                 (void) sm_strlcpy(p, *av++, j);
603                 h = strlen(p);
604                 p += h;
605                 j -= h + 1;
606         }
607         SaveArgv[i] = NULL;
608
609         if (tTd(0, 1))
610         {
611                 extern char *CompileOptions[];
612
613                 sm_dprintf("Version %s\n Compiled with:", Version);
614                 sm_printoptions(CompileOptions);
615         }
616         if (tTd(0, 10))
617         {
618                 extern char *OsCompileOptions[];
619
620                 sm_dprintf("    OS Defines:");
621                 sm_printoptions(OsCompileOptions);
622 #ifdef _PATH_UNIX
623                 sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
624 #endif
625
626                 sm_dprintf("     Conf file:\t%s (default for MSP)\n",
627                            getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
628                                      conffile));
629                 sm_dprintf("     Conf file:\t%s (default for MTA)\n",
630                            getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
631                                      conffile));
632                 sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
633         }
634
635         if (tTd(0, 12))
636         {
637                 extern char *SmCompileOptions[];
638
639                 sm_dprintf(" libsm Defines:");
640                 sm_printoptions(SmCompileOptions);
641         }
642
643         if (tTd(0, 13))
644         {
645                 extern char *FFRCompileOptions[];
646
647                 sm_dprintf("   FFR Defines:");
648                 sm_printoptions(FFRCompileOptions);
649         }
650
651 #if STARTTLS
652         if (tTd(0, 14))
653         {
654                 /* exit(EX_CONFIG) if different? */
655                 sm_dprintf("       OpenSSL: compiled 0x%08x\n",
656                            (uint) OPENSSL_VERSION_NUMBER);
657                 sm_dprintf("       OpenSSL: linked   0x%08x\n",
658                            (uint) TLS_version_num());
659         }
660 #endif /* STARTTLS */
661
662         /* clear sendmail's environment */
663         ExternalEnviron = environ;
664         emptyenviron[0] = NULL;
665         environ = emptyenviron;
666
667         /*
668         **  restore any original TZ setting until TimeZoneSpec has been
669         **  determined - or early log messages may get bogus time stamps
670         */
671
672         if ((p = getextenv("TZ")) != NULL)
673         {
674                 char *tz;
675                 int tzlen;
676
677                 /* XXX check for reasonable length? */
678                 tzlen = strlen(p) + 4;
679                 tz = xalloc(tzlen);
680                 (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
681
682                 /* XXX check return code? */
683                 (void) putenv(tz);
684         }
685
686         /* prime the child environment */
687         sm_setuserenv("AGENT", "sendmail");
688
689         (void) sm_signal(SIGPIPE, SIG_IGN);
690         OldUmask = umask(022);
691         FullName = getextenv("NAME");
692         if (FullName != NULL)
693                 FullName = newstr(FullName);
694
695         /*
696         **  Initialize name server if it is going to be used.
697         */
698
699 #if NAMED_BIND
700         if (!bitset(RES_INIT, _res.options))
701                 (void) res_init();
702         if (tTd(8, 8))
703                 _res.options |= RES_DEBUG;
704         else
705                 _res.options &= ~RES_DEBUG;
706 # ifdef RES_NOALIASES
707         _res.options |= RES_NOALIASES;
708 # endif
709         TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
710         TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
711         TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
712         TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
713         TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
714         TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
715 #endif /* NAMED_BIND */
716
717         errno = 0;
718         from = NULL;
719
720         /* initialize some macros, etc. */
721         init_vendor_macros(&BlankEnvelope);
722
723         /* version */
724         macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
725
726         /* hostname */
727         hp = myhostname(jbuf, sizeof(jbuf));
728         if (jbuf[0] != '\0')
729         {
730                 struct utsname utsname;
731
732                 if (tTd(0, 4))
733                         sm_dprintf("Canonical name: %s\n", jbuf);
734                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
735                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
736                 setclass('w', jbuf);
737
738                 p = strchr(jbuf, '.');
739                 if (p != NULL && p[1] != '\0')
740                         macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]);
741
742                 if (uname(&utsname) >= 0)
743                         p = utsname.nodename;
744                 else
745                 {
746                         if (tTd(0, 22))
747                                 sm_dprintf("uname failed (%s)\n",
748                                            sm_errstring(errno));
749                         makelower(jbuf);
750                         p = jbuf;
751                 }
752                 if (tTd(0, 4))
753                         sm_dprintf(" UUCP nodename: %s\n", p);
754                 macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
755                 setclass('k', p);
756                 setclass('w', p);
757         }
758         if (hp != NULL)
759         {
760                 for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
761                 {
762                         if (tTd(0, 4))
763                                 sm_dprintf("\ta.k.a.: %s\n", *av);
764                         setclass('w', *av);
765                 }
766 #if NETINET || NETINET6
767                 for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
768                 {
769 # if NETINET6
770                         char *addr;
771                         char buf6[INET6_ADDRSTRLEN];
772                         struct in6_addr ia6;
773 # endif /* NETINET6 */
774 # if NETINET
775                         struct in_addr ia;
776 # endif
777                         char ipbuf[103];
778
779                         ipbuf[0] = '\0';
780                         switch (hp->h_addrtype)
781                         {
782 # if NETINET
783                           case AF_INET:
784                                 if (hp->h_length != INADDRSZ)
785                                         break;
786
787                                 memmove(&ia, hp->h_addr_list[i], INADDRSZ);
788                                 (void) sm_snprintf(ipbuf, sizeof(ipbuf),
789                                                    "[%.100s]", inet_ntoa(ia));
790                                 break;
791 # endif /* NETINET */
792
793 # if NETINET6
794                           case AF_INET6:
795                                 if (hp->h_length != IN6ADDRSZ)
796                                         break;
797
798                                 memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
799                                 addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
800                                 if (addr != NULL)
801                                         (void) sm_snprintf(ipbuf, sizeof(ipbuf),
802                                                            "[%.100s]", addr);
803                                 break;
804 # endif /* NETINET6 */
805                         }
806                         if (ipbuf[0] == '\0')
807                                 break;
808
809                         if (tTd(0, 4))
810                                 sm_dprintf("\ta.k.a.: %s\n", ipbuf);
811                         setclass('w', ipbuf);
812                 }
813 #endif /* NETINET || NETINET6 */
814 #if NETINET6
815                 freehostent(hp);
816                 hp = NULL;
817 #endif
818         }
819
820         /* current time */
821         macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
822
823         /* current load average */
824         sm_getla();
825
826         QueueLimitRecipient = (QUEUE_CHAR *) NULL;
827         QueueLimitSender = (QUEUE_CHAR *) NULL;
828         QueueLimitId = (QUEUE_CHAR *) NULL;
829         QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
830
831         /*
832         **  Crack argv.
833         */
834
835         optind = 1;
836         while ((j = getopt(argc, argv, OPTIONS)) != -1)
837         {
838                 switch (j)
839                 {
840                   case 'b':     /* operations mode */
841                         /* already done */
842                         break;
843
844                   case 'A':     /* use Alternate sendmail/submit.cf */
845                         cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
846                                                   : SM_GET_SENDMAIL_CF;
847                         break;
848
849                   case 'B':     /* body type */
850                         CHECK_AGAINST_OPMODE(j);
851                         BlankEnvelope.e_bodytype = newstr(optarg);
852                         break;
853
854                   case 'C':     /* select configuration file (already done) */
855                         if (RealUid != 0)
856                                 warn_C_flag = true;
857                         conffile = newstr(optarg);
858                         dp = drop_privileges(true);
859                         setstat(dp);
860                         safecf = false;
861                         break;
862
863                   case 'D':
864                   case 'd':     /* debugging */
865                         /* already done */
866                         break;
867
868                   case 'f':     /* from address */
869                   case 'r':     /* obsolete -f flag */
870                         CHECK_AGAINST_OPMODE(j);
871                         if (from != NULL)
872                         {
873                                 usrerr("More than one \"from\" person");
874                                 ExitStat = EX_USAGE;
875                                 break;
876                         }
877                         if (optarg[0] == '\0')
878                                 from = newstr("<>");
879                         else
880                                 from = newstr(denlstring(optarg, true, true));
881                         if (strcmp(RealUserName, from) != 0)
882                                 warn_f_flag = j;
883                         break;
884
885                   case 'F':     /* set full name */
886                         CHECK_AGAINST_OPMODE(j);
887                         FullName = newstr(optarg);
888                         break;
889
890                   case 'G':     /* relay (gateway) submission */
891                         /* already set */
892                         CHECK_AGAINST_OPMODE(j);
893                         break;
894
895                   case 'h':     /* hop count */
896                         CHECK_AGAINST_OPMODE(j);
897                         BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
898                                                                   10);
899                         (void) sm_snprintf(buf, sizeof(buf), "%d",
900                                            BlankEnvelope.e_hopcount);
901                         macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
902
903                         if (*ep)
904                         {
905                                 usrerr("Bad hop count (%s)", optarg);
906                                 ExitStat = EX_USAGE;
907                         }
908                         break;
909
910                   case 'L':     /* program label */
911                         /* already set */
912                         break;
913
914                   case 'n':     /* don't alias */
915                         CHECK_AGAINST_OPMODE(j);
916                         NoAlias = true;
917                         break;
918
919                   case 'N':     /* delivery status notifications */
920                         CHECK_AGAINST_OPMODE(j);
921                         DefaultNotify |= QHASNOTIFY;
922                         macdefine(&BlankEnvelope.e_macro, A_TEMP,
923                                 macid("{dsn_notify}"), optarg);
924                         if (sm_strcasecmp(optarg, "never") == 0)
925                                 break;
926                         for (p = optarg; p != NULL; optarg = p)
927                         {
928                                 p = strchr(p, ',');
929                                 if (p != NULL)
930                                         *p++ = '\0';
931                                 if (sm_strcasecmp(optarg, "success") == 0)
932                                         DefaultNotify |= QPINGONSUCCESS;
933                                 else if (sm_strcasecmp(optarg, "failure") == 0)
934                                         DefaultNotify |= QPINGONFAILURE;
935                                 else if (sm_strcasecmp(optarg, "delay") == 0)
936                                         DefaultNotify |= QPINGONDELAY;
937                                 else
938                                 {
939                                         usrerr("Invalid -N argument");
940                                         ExitStat = EX_USAGE;
941                                 }
942                         }
943                         break;
944
945                   case 'o':     /* set option */
946                         setoption(*optarg, optarg + 1, false, true,
947                                   &BlankEnvelope);
948                         break;
949
950                   case 'O':     /* set option (long form) */
951                         setoption(' ', optarg, false, true, &BlankEnvelope);
952                         break;
953
954                   case 'p':     /* set protocol */
955                         CHECK_AGAINST_OPMODE(j);
956                         p = strchr(optarg, ':');
957                         if (p != NULL)
958                         {
959                                 *p++ = '\0';
960                                 if (*p != '\0')
961                                 {
962                                         i = strlen(p) + 1;
963                                         ep = sm_malloc_x(i);
964                                         cleanstrcpy(ep, p, i);
965                                         macdefine(&BlankEnvelope.e_macro,
966                                                   A_HEAP, 's', ep);
967                                 }
968                         }
969                         if (*optarg != '\0')
970                         {
971                                 i = strlen(optarg) + 1;
972                                 ep = sm_malloc_x(i);
973                                 cleanstrcpy(ep, optarg, i);
974                                 macdefine(&BlankEnvelope.e_macro, A_HEAP,
975                                           'r', ep);
976                         }
977                         break;
978
979                   case 'Q':     /* change quarantining on queued items */
980                         /* sanity check */
981                         if (OpMode != MD_DELIVER &&
982                             OpMode != MD_QUEUERUN)
983                         {
984                                 usrerr("Can not use -Q with -b%c", OpMode);
985                                 ExitStat = EX_USAGE;
986                                 break;
987                         }
988
989                         if (OpMode == MD_DELIVER)
990                                 set_op_mode(MD_QUEUERUN);
991
992                         FullName = NULL;
993
994                         quarantining = newstr(optarg);
995                         break;
996
997                   case 'q':     /* run queue files at intervals */
998                         /* sanity check */
999                         if (OpMode != MD_DELIVER &&
1000                             OpMode != MD_DAEMON &&
1001                             OpMode != MD_FGDAEMON &&
1002                             OpMode != MD_PRINT &&
1003                             OpMode != MD_PRINTNQE &&
1004                             OpMode != MD_QUEUERUN)
1005                         {
1006                                 usrerr("Can not use -q with -b%c", OpMode);
1007                                 ExitStat = EX_USAGE;
1008                                 break;
1009                         }
1010
1011                         /* don't override -bd, -bD or -bp */
1012                         if (OpMode == MD_DELIVER)
1013                                 set_op_mode(MD_QUEUERUN);
1014
1015                         FullName = NULL;
1016                         negate = optarg[0] == '!';
1017                         if (negate)
1018                         {
1019                                 /* negate meaning of pattern match */
1020                                 optarg++; /* skip '!' for next switch */
1021                         }
1022
1023                         switch (optarg[0])
1024                         {
1025                           case 'G': /* Limit by queue group name */
1026                                 if (negate)
1027                                 {
1028                                         usrerr("Can not use -q!G");
1029                                         ExitStat = EX_USAGE;
1030                                         break;
1031                                 }
1032                                 if (queuegroup != NULL)
1033                                 {
1034                                         usrerr("Can not use multiple -qG options");
1035                                         ExitStat = EX_USAGE;
1036                                         break;
1037                                 }
1038                                 queuegroup = newstr(&optarg[1]);
1039                                 break;
1040
1041                           case 'I': /* Limit by ID */
1042                                 new = (QUEUE_CHAR *) xalloc(sizeof(*new));
1043                                 new->queue_match = newstr(&optarg[1]);
1044                                 new->queue_negate = negate;
1045                                 new->queue_next = QueueLimitId;
1046                                 QueueLimitId = new;
1047                                 break;
1048
1049                           case 'R': /* Limit by recipient */
1050                                 new = (QUEUE_CHAR *) xalloc(sizeof(*new));
1051                                 new->queue_match = newstr(&optarg[1]);
1052                                 new->queue_negate = negate;
1053                                 new->queue_next = QueueLimitRecipient;
1054                                 QueueLimitRecipient = new;
1055                                 break;
1056
1057                           case 'S': /* Limit by sender */
1058                                 new = (QUEUE_CHAR *) xalloc(sizeof(*new));
1059                                 new->queue_match = newstr(&optarg[1]);
1060                                 new->queue_negate = negate;
1061                                 new->queue_next = QueueLimitSender;
1062                                 QueueLimitSender = new;
1063                                 break;
1064
1065                           case 'f': /* foreground queue run */
1066                                 foregroundqueue  = true;
1067                                 break;
1068
1069                           case 'Q': /* Limit by quarantine message */
1070                                 if (optarg[1] != '\0')
1071                                 {
1072                                         new = (QUEUE_CHAR *) xalloc(sizeof(*new));
1073                                         new->queue_match = newstr(&optarg[1]);
1074                                         new->queue_negate = negate;
1075                                         new->queue_next = QueueLimitQuarantine;
1076                                         QueueLimitQuarantine = new;
1077                                 }
1078                                 QueueMode = QM_QUARANTINE;
1079                                 break;
1080
1081                           case 'L': /* act on lost items */
1082                                 QueueMode = QM_LOST;
1083                                 break;
1084
1085                           case 'p': /* Persistent queue */
1086                                 queuepersistent = true;
1087                                 if (QueueIntvl == 0)
1088                                         QueueIntvl = 1;
1089                                 if (optarg[1] == '\0')
1090                                         break;
1091                                 ++optarg;
1092                                 /* FALLTHROUGH */
1093
1094                           default:
1095                                 i = Errors;
1096                                 QueueIntvl = convtime(optarg, 'm');
1097                                 if (QueueIntvl < 0)
1098                                 {
1099                                         usrerr("Invalid -q value");
1100                                         ExitStat = EX_USAGE;
1101                                 }
1102
1103                                 /* check for bad conversion */
1104                                 if (i < Errors)
1105                                         ExitStat = EX_USAGE;
1106                                 break;
1107                         }
1108                         break;
1109
1110                   case 'R':     /* DSN RET: what to return */
1111                         CHECK_AGAINST_OPMODE(j);
1112                         if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
1113                         {
1114                                 usrerr("Duplicate -R flag");
1115                                 ExitStat = EX_USAGE;
1116                                 break;
1117                         }
1118                         BlankEnvelope.e_flags |= EF_RET_PARAM;
1119                         if (sm_strcasecmp(optarg, "hdrs") == 0)
1120                                 BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
1121                         else if (sm_strcasecmp(optarg, "full") != 0)
1122                         {
1123                                 usrerr("Invalid -R value");
1124                                 ExitStat = EX_USAGE;
1125                         }
1126                         macdefine(&BlankEnvelope.e_macro, A_TEMP,
1127                                   macid("{dsn_ret}"), optarg);
1128                         break;
1129
1130                   case 't':     /* read recipients from message */
1131                         CHECK_AGAINST_OPMODE(j);
1132                         GrabTo = true;
1133                         break;
1134
1135                   case 'V':     /* DSN ENVID: set "original" envelope id */
1136                         CHECK_AGAINST_OPMODE(j);
1137                         if (!xtextok(optarg))
1138                         {
1139                                 usrerr("Invalid syntax in -V flag");
1140                                 ExitStat = EX_USAGE;
1141                         }
1142                         else
1143                         {
1144                                 BlankEnvelope.e_envid = newstr(optarg);
1145                                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
1146                                           macid("{dsn_envid}"), optarg);
1147                         }
1148                         break;
1149
1150                   case 'X':     /* traffic log file */
1151                         dp = drop_privileges(true);
1152                         setstat(dp);
1153                         if (stat(optarg, &traf_st) == 0 &&
1154                             S_ISFIFO(traf_st.st_mode))
1155                                 TrafficLogFile = sm_io_open(SmFtStdio,
1156                                                             SM_TIME_DEFAULT,
1157                                                             optarg,
1158                                                             SM_IO_WRONLY, NULL);
1159                         else
1160                                 TrafficLogFile = sm_io_open(SmFtStdio,
1161                                                             SM_TIME_DEFAULT,
1162                                                             optarg,
1163                                                             SM_IO_APPEND, NULL);
1164                         if (TrafficLogFile == NULL)
1165                         {
1166                                 syserr("cannot open %s", optarg);
1167                                 ExitStat = EX_CANTCREAT;
1168                                 break;
1169                         }
1170                         (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
1171                                              NULL, SM_IO_LBF, 0);
1172                         break;
1173
1174                         /* compatibility flags */
1175                   case 'c':     /* connect to non-local mailers */
1176                   case 'i':     /* don't let dot stop me */
1177                   case 'm':     /* send to me too */
1178                   case 'T':     /* set timeout interval */
1179                   case 'v':     /* give blow-by-blow description */
1180                         setoption(j, "T", false, true, &BlankEnvelope);
1181                         break;
1182
1183                   case 'e':     /* error message disposition */
1184                   case 'M':     /* define macro */
1185                         setoption(j, optarg, false, true, &BlankEnvelope);
1186                         break;
1187
1188                   case 's':     /* save From lines in headers */
1189                         setoption('f', "T", false, true, &BlankEnvelope);
1190                         break;
1191
1192 #ifdef DBM
1193                   case 'I':     /* initialize alias DBM file */
1194                         set_op_mode(MD_INITALIAS);
1195                         break;
1196 #endif /* DBM */
1197
1198 #if defined(__osf__) || defined(_AIX3)
1199                   case 'x':     /* random flag that OSF/1 & AIX mailx passes */
1200                         break;
1201 #endif
1202 #if defined(sony_news)
1203                   case 'E':
1204                   case 'J':     /* ignore flags for Japanese code conversion
1205                                    implemented on Sony NEWS */
1206                         break;
1207 #endif /* defined(sony_news) */
1208
1209                   default:
1210                         finis(true, true, EX_USAGE);
1211                         /* NOTREACHED */
1212                         break;
1213                 }
1214         }
1215
1216         /* if we've had errors so far, exit now */
1217         if ((ExitStat != EX_OK && OpMode != MD_TEST && OpMode != MD_CHECKCONFIG) ||
1218             ExitStat == EX_OSERR)
1219         {
1220                 finis(false, true, ExitStat);
1221                 /* NOTREACHED */
1222         }
1223
1224         if (bitset(SUBMIT_MTA, SubmitMode))
1225         {
1226                 /* If set daemon_flags on command line, don't reset it */
1227                 if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
1228                         macdefine(&BlankEnvelope.e_macro, A_PERM,
1229                                   macid("{daemon_flags}"), "CC f");
1230         }
1231         else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
1232         {
1233                 SubmitMode = SUBMIT_MSA;
1234
1235                 /* If set daemon_flags on command line, don't reset it */
1236                 if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
1237                         macdefine(&BlankEnvelope.e_macro, A_PERM,
1238                                   macid("{daemon_flags}"), "c u");
1239         }
1240
1241         /*
1242         **  Do basic initialization.
1243         **      Read system control file.
1244         **      Extract special fields for local use.
1245         */
1246
1247 #if XDEBUG
1248         checkfd012("before readcf");
1249 #endif
1250         vendor_pre_defaults(&BlankEnvelope);
1251
1252         readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
1253                          safecf, &BlankEnvelope);
1254 #if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
1255         ConfigFileRead = true;
1256 #endif
1257         vendor_post_defaults(&BlankEnvelope);
1258
1259         /* now we can complain about missing fds */
1260         if (MissingFds != 0 && LogLevel > 8)
1261         {
1262                 char mbuf[MAXLINE];
1263
1264                 mbuf[0] = '\0';
1265                 if (bitset(1 << STDIN_FILENO, MissingFds))
1266                         (void) sm_strlcat(mbuf, ", stdin", sizeof(mbuf));
1267                 if (bitset(1 << STDOUT_FILENO, MissingFds))
1268                         (void) sm_strlcat(mbuf, ", stdout", sizeof(mbuf));
1269                 if (bitset(1 << STDERR_FILENO, MissingFds))
1270                         (void) sm_strlcat(mbuf, ", stderr", sizeof(mbuf));
1271
1272                 /* Notice: fill_errno is from high above: fill_fd() */
1273                 sm_syslog(LOG_WARNING, NOQID,
1274                           "File descriptors missing on startup: %s; %s",
1275                           &mbuf[2], sm_errstring(fill_errno));
1276         }
1277
1278         /* Remove the ability for a normal user to send signals */
1279         if (RealUid != 0 && RealUid != geteuid())
1280         {
1281                 uid_t new_uid = geteuid();
1282
1283 #if HASSETREUID
1284                 /*
1285                 **  Since we can differentiate between uid and euid,
1286                 **  make the uid a different user so the real user
1287                 **  can't send signals.  However, it doesn't need to be
1288                 **  root (euid has root).
1289                 */
1290
1291                 if (new_uid == 0)
1292                         new_uid = DefUid;
1293                 if (tTd(47, 5))
1294                         sm_dprintf("Changing real uid to %d\n", (int) new_uid);
1295                 if (setreuid(new_uid, geteuid()) < 0)
1296                 {
1297                         syserr("main: setreuid(%d, %d) failed",
1298                                (int) new_uid, (int) geteuid());
1299                         finis(false, true, EX_OSERR);
1300                         /* NOTREACHED */
1301                 }
1302                 if (tTd(47, 10))
1303                         sm_dprintf("Now running as e/ruid %d:%d\n",
1304                                    (int) geteuid(), (int) getuid());
1305 #else /* HASSETREUID */
1306                 /*
1307                 **  Have to change both effective and real so need to
1308                 **  change them both to effective to keep privs.
1309                 */
1310
1311                 if (tTd(47, 5))
1312                         sm_dprintf("Changing uid to %d\n", (int) new_uid);
1313                 if (setuid(new_uid) < 0)
1314                 {
1315                         syserr("main: setuid(%d) failed", (int) new_uid);
1316                         finis(false, true, EX_OSERR);
1317                         /* NOTREACHED */
1318                 }
1319                 if (tTd(47, 10))
1320                         sm_dprintf("Now running as e/ruid %d:%d\n",
1321                                    (int) geteuid(), (int) getuid());
1322 #endif /* HASSETREUID */
1323         }
1324
1325 #if NAMED_BIND
1326         if (FallbackMX != NULL)
1327                 (void) getfallbackmxrr(FallbackMX);
1328 #endif
1329
1330         if (SuperSafe == SAFE_INTERACTIVE && !SM_IS_INTERACTIVE(CurEnv->e_sendmode))
1331         {
1332                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1333                                      "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
1334         }
1335
1336         if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
1337         {
1338                 usrerr("Mail submission program cannot be used as daemon");
1339                 finis(false, true, EX_USAGE);
1340         }
1341
1342         if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
1343             OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
1344             OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
1345                 makeworkgroups();
1346
1347         /* set up the basic signal handlers */
1348         if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
1349                 (void) sm_signal(SIGINT, intsig);
1350         (void) sm_signal(SIGTERM, intsig);
1351
1352         /* Enforce use of local time (null string overrides this) */
1353         if (TimeZoneSpec == NULL)
1354                 unsetenv("TZ");
1355         else if (TimeZoneSpec[0] != '\0')
1356                 sm_setuserenv("TZ", TimeZoneSpec);
1357         else
1358                 sm_setuserenv("TZ", NULL);
1359         tzset();
1360
1361         /* initialize mailbox database */
1362         i = sm_mbdb_initialize(Mbdb);
1363         if (i != EX_OK)
1364         {
1365                 usrerr("Can't initialize mailbox database \"%s\": %s",
1366                        Mbdb, sm_strexit(i));
1367                 ExitStat = i;
1368         }
1369
1370         /* avoid denial-of-service attacks */
1371         resetlimits();
1372
1373         if (OpMode == MD_TEST)
1374         {
1375                 /* can't be done after readcf if RunAs* is used */
1376                 dp = drop_privileges(true);
1377                 if (dp != EX_OK)
1378                 {
1379                         finis(false, true, dp);
1380                         /* NOTREACHED */
1381                 }
1382         }
1383         else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
1384         {
1385                 /* drop privileges -- daemon mode done after socket/bind */
1386                 dp = drop_privileges(false);
1387                 setstat(dp);
1388                 if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
1389                 {
1390                         usrerr("Mail submission program must have RunAsUser set to non root user");
1391                         finis(false, true, EX_CONFIG);
1392                         /* NOTREACHED */
1393                 }
1394         }
1395
1396 #if NAMED_BIND
1397         _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
1398         _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
1399 #endif
1400
1401         /*
1402         **  Find our real host name for future logging.
1403         */
1404
1405         authinfo = getauthinfo(STDIN_FILENO, &forged);
1406         macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
1407
1408         /* suppress error printing if errors mailed back or whatever */
1409         if (BlankEnvelope.e_errormode != EM_PRINT)
1410                 HoldErrs = true;
1411
1412         /* set up the $=m class now, after .cf has a chance to redefine $m */
1413         expand("\201m", jbuf, sizeof(jbuf), &BlankEnvelope);
1414         if (jbuf[0] != '\0')
1415                 setclass('m', jbuf);
1416
1417         /* probe interfaces and locate any additional names */
1418         if (DontProbeInterfaces != DPI_PROBENONE)
1419                 load_if_names();
1420
1421         if (tTd(0, 10))
1422         {
1423                 char pidpath[MAXPATHLEN];
1424
1425                 /* Now we know which .cf file we use */
1426                 sm_dprintf("     Conf file:\t%s (selected)\n",
1427                            getcfname(OpMode, SubmitMode, cftype, conffile));
1428                 expand(PidFile, pidpath, sizeof(pidpath), &BlankEnvelope);
1429                 sm_dprintf("      Pid file:\t%s (selected)\n", pidpath);
1430         }
1431
1432         if (tTd(0, 1))
1433         {
1434                 sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
1435                 sm_dprintf("\n      (short domain name) $w = ");
1436                 xputs(sm_debug_file(), macvalue('w', &BlankEnvelope));
1437                 sm_dprintf("\n  (canonical domain name) $j = ");
1438                 xputs(sm_debug_file(), macvalue('j', &BlankEnvelope));
1439                 sm_dprintf("\n         (subdomain name) $m = ");
1440                 xputs(sm_debug_file(), macvalue('m', &BlankEnvelope));
1441                 sm_dprintf("\n              (node name) $k = ");
1442                 xputs(sm_debug_file(), macvalue('k', &BlankEnvelope));
1443                 sm_dprintf("\n========================================================\n\n");
1444         }
1445
1446         /*
1447         **  Do more command line checking -- these are things that
1448         **  have to modify the results of reading the config file.
1449         */
1450
1451         /* process authorization warnings from command line */
1452         if (warn_C_flag)
1453                 auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
1454                              RealUserName, conffile);
1455         if (Warn_Q_option && !wordinclass(RealUserName, 't'))
1456                 auth_warning(&BlankEnvelope, "Processed from queue %s",
1457                              QueueDir);
1458         if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
1459             RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
1460                 sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
1461                           (int) RealUid);
1462
1463         /* check body type for legality */
1464         i = check_bodytype(BlankEnvelope.e_bodytype);
1465         if (i == BODYTYPE_ILLEGAL)
1466         {
1467                 usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
1468                 BlankEnvelope.e_bodytype = NULL;
1469         }
1470         else if (i != BODYTYPE_NONE)
1471                 SevenBitInput = (i == BODYTYPE_7BIT);
1472
1473         /* tweak default DSN notifications */
1474         if (DefaultNotify == 0)
1475                 DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1476
1477         /* check for sane configuration level */
1478         if (ConfigLevel > MAXCONFIGLEVEL)
1479         {
1480                 syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
1481                        ConfigLevel, Version, MAXCONFIGLEVEL);
1482         }
1483
1484         /* need MCI cache to have persistence */
1485         if (HostStatDir != NULL && MaxMciCache == 0)
1486         {
1487                 HostStatDir = NULL;
1488                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1489                                      "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
1490         }
1491
1492         /* need HostStatusDir in order to have SingleThreadDelivery */
1493         if (SingleThreadDelivery && HostStatDir == NULL)
1494         {
1495                 SingleThreadDelivery = false;
1496                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1497                                      "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
1498         }
1499
1500 #if _FFR_MEMSTAT
1501         j = sm_memstat_open();
1502         if (j < 0 && (RefuseLowMem > 0 || QueueLowMem > 0) && LogLevel > 4)
1503         {
1504                 sm_syslog(LOG_WARNING, NOQID,
1505                           "cannot get memory statistics, settings ignored, error=%d"
1506                           , j);
1507         }
1508 #endif /* _FFR_MEMSTAT */
1509
1510         /* check for permissions */
1511         if (RealUid != 0 &&
1512             RealUid != TrustedUid)
1513         {
1514                 char *action = NULL;
1515
1516                 switch (OpMode)
1517                 {
1518                   case MD_QUEUERUN:
1519                         if (quarantining != NULL)
1520                                 action = "quarantine jobs";
1521                         else
1522                         {
1523                                 /* Normal users can do a single queue run */
1524                                 if (QueueIntvl == 0)
1525                                         break;
1526                         }
1527
1528                         /* but not persistent queue runners */
1529                         if (action == NULL)
1530                                 action = "start a queue runner daemon";
1531                         /* FALLTHROUGH */
1532
1533                   case MD_PURGESTAT:
1534                         if (action == NULL)
1535                                 action = "purge host status";
1536                         /* FALLTHROUGH */
1537
1538                   case MD_DAEMON:
1539                   case MD_FGDAEMON:
1540                         if (action == NULL)
1541                                 action = "run daemon";
1542
1543                         if (tTd(65, 1))
1544                                 sm_dprintf("Deny user %d attempt to %s\n",
1545                                            (int) RealUid, action);
1546
1547                         if (LogLevel > 1)
1548                                 sm_syslog(LOG_ALERT, NOQID,
1549                                           "user %d attempted to %s",
1550                                           (int) RealUid, action);
1551                         HoldErrs = false;
1552                         usrerr("Permission denied (real uid not trusted)");
1553                         finis(false, true, EX_USAGE);
1554                         /* NOTREACHED */
1555                         break;
1556
1557                   case MD_VERIFY:
1558                         if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
1559                         {
1560                                 /*
1561                                 **  If -bv and RestrictExpand,
1562                                 **  drop privs to prevent normal
1563                                 **  users from reading private
1564                                 **  aliases/forwards/:include:s
1565                                 */
1566
1567                                 if (tTd(65, 1))
1568                                         sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
1569                                                    (int) RealUid);
1570
1571                                 dp = drop_privileges(true);
1572
1573                                 /* Fake address safety */
1574                                 if (tTd(65, 1))
1575                                         sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
1576                                 setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
1577
1578                                 if (dp != EX_OK)
1579                                 {
1580                                         if (tTd(65, 1))
1581                                                 sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
1582                                                            (int) RealUid);
1583                                         CurEnv->e_id = NULL;
1584                                         finis(true, true, dp);
1585                                         /* NOTREACHED */
1586                                 }
1587                         }
1588                         break;
1589
1590                   case MD_TEST:
1591                   case MD_CHECKCONFIG:
1592                   case MD_PRINT:
1593                   case MD_PRINTNQE:
1594                   case MD_FREEZE:
1595                   case MD_HOSTSTAT:
1596                         /* Nothing special to check */
1597                         break;
1598
1599                   case MD_INITALIAS:
1600                         if (!wordinclass(RealUserName, 't'))
1601                         {
1602                                 if (tTd(65, 1))
1603                                         sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
1604                                                    (int) RealUid);
1605                                 if (LogLevel > 1)
1606                                         sm_syslog(LOG_ALERT, NOQID,
1607                                                   "user %d attempted to rebuild the alias map",
1608                                                   (int) RealUid);
1609                                 HoldErrs = false;
1610                                 usrerr("Permission denied (real uid not trusted)");
1611                                 finis(false, true, EX_USAGE);
1612                                 /* NOTREACHED */
1613                         }
1614                         if (UseMSP)
1615                         {
1616                                 HoldErrs = false;
1617                                 usrerr("User %d cannot rebuild aliases in mail submission program",
1618                                        (int) RealUid);
1619                                 finis(false, true, EX_USAGE);
1620                                 /* NOTREACHED */
1621                         }
1622                         /* FALLTHROUGH */
1623
1624                   default:
1625                         if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
1626                             Verbose != 0)
1627                         {
1628                                 /*
1629                                 **  If -v and RestrictExpand, reset
1630                                 **  Verbose to prevent normal users
1631                                 **  from seeing the expansion of
1632                                 **  aliases/forwards/:include:s
1633                                 */
1634
1635                                 if (tTd(65, 1))
1636                                         sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
1637                                                    (int) RealUid);
1638                                 Verbose = 0;
1639                         }
1640                         break;
1641                 }
1642         }
1643
1644         if (MeToo)
1645                 BlankEnvelope.e_flags |= EF_METOO;
1646
1647         switch (OpMode)
1648         {
1649           case MD_TEST:
1650                 /* don't have persistent host status in test mode */
1651                 HostStatDir = NULL;
1652                 /* FALLTHROUGH */
1653
1654           case MD_CHECKCONFIG:
1655                 if (Verbose == 0)
1656                         Verbose = 2;
1657                 BlankEnvelope.e_errormode = EM_PRINT;
1658                 HoldErrs = false;
1659                 break;
1660
1661           case MD_VERIFY:
1662                 BlankEnvelope.e_errormode = EM_PRINT;
1663                 HoldErrs = false;
1664                 /* arrange to exit cleanly on hangup signal */
1665                 if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1666                         (void) sm_signal(SIGHUP, intsig);
1667                 if (geteuid() != 0)
1668                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1669                                              "Notice: -bv may give misleading output for non-privileged user\n");
1670                 break;
1671
1672           case MD_FGDAEMON:
1673                 run_in_foreground = true;
1674                 set_op_mode(MD_DAEMON);
1675                 /* FALLTHROUGH */
1676
1677           case MD_DAEMON:
1678                 vendor_daemon_setup(&BlankEnvelope);
1679
1680                 /* remove things that don't make sense in daemon mode */
1681                 FullName = NULL;
1682                 GrabTo = false;
1683
1684                 /* arrange to restart on hangup signal */
1685                 if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1686                         sm_syslog(LOG_WARNING, NOQID,
1687                                   "daemon invoked without full pathname; kill -1 won't work");
1688                 break;
1689
1690           case MD_INITALIAS:
1691                 Verbose = 2;
1692                 BlankEnvelope.e_errormode = EM_PRINT;
1693                 HoldErrs = false;
1694                 /* FALLTHROUGH */
1695
1696           default:
1697                 /* arrange to exit cleanly on hangup signal */
1698                 if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1699                         (void) sm_signal(SIGHUP, intsig);
1700                 break;
1701         }
1702
1703         /* special considerations for FullName */
1704         if (FullName != NULL)
1705         {
1706                 char *full = NULL;
1707
1708                 /* full names can't have newlines */
1709                 if (strchr(FullName, '\n') != NULL)
1710                 {
1711                         full = newstr(denlstring(FullName, true, true));
1712                         FullName = full;
1713                 }
1714
1715                 /* check for characters that may have to be quoted */
1716                 if (!rfc822_string(FullName))
1717                 {
1718                         /*
1719                         **  Quote a full name with special characters
1720                         **  as a comment so crackaddr() doesn't destroy
1721                         **  the name portion of the address.
1722                         */
1723
1724                         FullName = addquotes(FullName, NULL);
1725                         if (full != NULL)
1726                                 sm_free(full);  /* XXX */
1727                 }
1728         }
1729
1730         /* do heuristic mode adjustment */
1731         if (Verbose)
1732         {
1733                 /* turn off noconnect option */
1734                 setoption('c', "F", true, false, &BlankEnvelope);
1735
1736                 /* turn on interactive delivery */
1737                 setoption('d', "", true, false, &BlankEnvelope);
1738         }
1739
1740 #ifdef VENDOR_CODE
1741         /* check for vendor mismatch */
1742         if (VendorCode != VENDOR_CODE)
1743         {
1744                 message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1745                         getvendor(VENDOR_CODE), getvendor(VendorCode));
1746         }
1747 #endif /* VENDOR_CODE */
1748
1749         /* check for out of date configuration level */
1750         if (ConfigLevel < MAXCONFIGLEVEL)
1751         {
1752                 message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1753                         Version, MAXCONFIGLEVEL, ConfigLevel);
1754         }
1755
1756         if (ConfigLevel < 3)
1757                 UseErrorsTo = true;
1758
1759         /* set options that were previous macros */
1760         if (SmtpGreeting == NULL)
1761         {
1762                 if (ConfigLevel < 7 &&
1763                     (p = macvalue('e', &BlankEnvelope)) != NULL)
1764                         SmtpGreeting = newstr(p);
1765                 else
1766                         SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1767         }
1768         if (UnixFromLine == NULL)
1769         {
1770                 if (ConfigLevel < 7 &&
1771                     (p = macvalue('l', &BlankEnvelope)) != NULL)
1772                         UnixFromLine = newstr(p);
1773                 else
1774                         UnixFromLine = "From \201g  \201d";
1775         }
1776         SmtpError[0] = '\0';
1777
1778         /* our name for SMTP codes */
1779         expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
1780         if (jbuf[0] == '\0')
1781                 PSTRSET(MyHostName, "localhost");
1782         else
1783                 PSTRSET(MyHostName, jbuf);
1784         if (strchr(MyHostName, '.') == NULL)
1785                 message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
1786                         MyHostName);
1787
1788         /* make certain that this name is part of the $=w class */
1789         setclass('w', MyHostName);
1790
1791         /* fill in the structure of the *default* queue */
1792         st = stab("mqueue", ST_QUEUE, ST_FIND);
1793         if (st == NULL)
1794                 syserr("No default queue (mqueue) defined");
1795         else
1796                 set_def_queueval(st->s_quegrp, true);
1797
1798         /* the indices of built-in mailers */
1799         st = stab("local", ST_MAILER, ST_FIND);
1800         if (st != NULL)
1801                 LocalMailer = st->s_mailer;
1802         else if (OpMode != MD_TEST || !warn_C_flag)
1803                 syserr("No local mailer defined");
1804
1805         st = stab("prog", ST_MAILER, ST_FIND);
1806         if (st == NULL)
1807                 syserr("No prog mailer defined");
1808         else
1809         {
1810                 ProgMailer = st->s_mailer;
1811                 clrbitn(M_MUSER, ProgMailer->m_flags);
1812         }
1813
1814         st = stab("*file*", ST_MAILER, ST_FIND);
1815         if (st == NULL)
1816                 syserr("No *file* mailer defined");
1817         else
1818         {
1819                 FileMailer = st->s_mailer;
1820                 clrbitn(M_MUSER, FileMailer->m_flags);
1821         }
1822
1823         st = stab("*include*", ST_MAILER, ST_FIND);
1824         if (st == NULL)
1825                 syserr("No *include* mailer defined");
1826         else
1827                 InclMailer = st->s_mailer;
1828
1829         if (ConfigLevel < 6)
1830         {
1831                 /* heuristic tweaking of local mailer for back compat */
1832                 if (LocalMailer != NULL)
1833                 {
1834                         setbitn(M_ALIASABLE, LocalMailer->m_flags);
1835                         setbitn(M_HASPWENT, LocalMailer->m_flags);
1836                         setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1837                         setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1838                         setbitn(M_CHECKPROG, LocalMailer->m_flags);
1839                         setbitn(M_CHECKFILE, LocalMailer->m_flags);
1840                         setbitn(M_CHECKUDB, LocalMailer->m_flags);
1841                 }
1842                 if (ProgMailer != NULL)
1843                         setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1844                 if (FileMailer != NULL)
1845                         setbitn(M_RUNASRCPT, FileMailer->m_flags);
1846         }
1847         if (ConfigLevel < 7)
1848         {
1849                 if (LocalMailer != NULL)
1850                         setbitn(M_VRFY250, LocalMailer->m_flags);
1851                 if (ProgMailer != NULL)
1852                         setbitn(M_VRFY250, ProgMailer->m_flags);
1853                 if (FileMailer != NULL)
1854                         setbitn(M_VRFY250, FileMailer->m_flags);
1855         }
1856
1857         /* MIME Content-Types that cannot be transfer encoded */
1858         setclass('n', "multipart/signed");
1859
1860         /* MIME message/xxx subtypes that can be treated as messages */
1861         setclass('s', "rfc822");
1862 #if _FFR_EAI
1863         setclass('s', "global");
1864 #endif
1865
1866         /* MIME Content-Transfer-Encodings that can be encoded */
1867         setclass('e', "7bit");
1868         setclass('e', "8bit");
1869         setclass('e', "binary");
1870
1871 #ifdef USE_B_CLASS
1872         /* MIME Content-Types that should be treated as binary */
1873         setclass('b', "image");
1874         setclass('b', "audio");
1875         setclass('b', "video");
1876         setclass('b', "application/octet-stream");
1877 #endif /* USE_B_CLASS */
1878
1879         /* MIME headers which have fields to check for overflow */
1880         setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
1881         setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
1882
1883         /* MIME headers to check for length overflow */
1884         setclass(macid("{checkMIMETextHeaders}"), "content-description");
1885
1886         /* MIME headers to check for overflow and rebalance */
1887         setclass(macid("{checkMIMEHeaders}"), "content-disposition");
1888         setclass(macid("{checkMIMEHeaders}"), "content-id");
1889         setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
1890         setclass(macid("{checkMIMEHeaders}"), "content-type");
1891         setclass(macid("{checkMIMEHeaders}"), "mime-version");
1892
1893         /* Macros to save in the queue file -- don't remove any */
1894         setclass(macid("{persistentMacros}"), "r");
1895         setclass(macid("{persistentMacros}"), "s");
1896         setclass(macid("{persistentMacros}"), "_");
1897         setclass(macid("{persistentMacros}"), "{if_addr}");
1898         setclass(macid("{persistentMacros}"), "{daemon_flags}");
1899
1900         /* operate in queue directory */
1901         if (QueueDir == NULL || *QueueDir == '\0')
1902         {
1903                 if (OpMode != MD_TEST)
1904                 {
1905                         syserr("QueueDirectory (Q) option must be set");
1906                         ExitStat = EX_CONFIG;
1907                 }
1908         }
1909         else
1910         {
1911                 if (OpMode != MD_TEST)
1912                         setup_queues(OpMode == MD_DAEMON);
1913         }
1914
1915         /* check host status directory for validity */
1916         if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
1917         {
1918                 /* cannot use this value */
1919                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1920                                      "Warning: Cannot use HostStatusDirectory = %s: %s\n",
1921                                      HostStatDir, sm_errstring(errno));
1922                 HostStatDir = NULL;
1923         }
1924
1925         if (OpMode == MD_QUEUERUN &&
1926             RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1927         {
1928                 struct stat stbuf;
1929
1930                 /* check to see if we own the queue directory */
1931                 if (stat(".", &stbuf) < 0)
1932                         syserr("main: cannot stat %s", QueueDir);
1933                 if (stbuf.st_uid != RealUid)
1934                 {
1935                         /* nope, really a botch */
1936                         HoldErrs = false;
1937                         usrerr("You do not have permission to process the queue");
1938                         finis(false, true, EX_NOPERM);
1939                         /* NOTREACHED */
1940                 }
1941         }
1942
1943 #if MILTER
1944         /* sanity checks on milter filters */
1945         if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
1946         {
1947                 milter_config(InputFilterList, InputFilters, MAXFILTERS);
1948                 setup_daemon_milters();
1949         }
1950 #endif /* MILTER */
1951
1952         /* Convert queuegroup string to qgrp number */
1953         if (queuegroup != NULL)
1954         {
1955                 qgrp = name2qid(queuegroup);
1956                 if (qgrp == NOQGRP)
1957                 {
1958                         HoldErrs = false;
1959                         usrerr("Queue group %s unknown", queuegroup);
1960                         finis(false, true, ExitStat);
1961                         /* NOTREACHED */
1962                 }
1963         }
1964
1965         /* if checking config or have had errors so far, exit now */
1966         if (OpMode == MD_CHECKCONFIG || (ExitStat != EX_OK && OpMode != MD_TEST))
1967         {
1968                 finis(false, true, ExitStat);
1969                 /* NOTREACHED */
1970         }
1971
1972 #if SASL
1973         /* sendmail specific SASL initialization */
1974         sm_sasl_init();
1975 #endif
1976
1977 #if XDEBUG
1978         checkfd012("before main() initmaps");
1979 #endif
1980
1981         /*
1982         **  Do operation-mode-dependent initialization.
1983         */
1984
1985         switch (OpMode)
1986         {
1987           case MD_PRINT:
1988                 /* print the queue */
1989                 HoldErrs = false;
1990                 (void) dropenvelope(&BlankEnvelope, true, false);
1991                 (void) sm_signal(SIGPIPE, sigpipe);
1992                 if (qgrp != NOQGRP)
1993                 {
1994                         /* Selecting a particular queue group to run */
1995                         for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
1996                         {
1997                                 if (StopRequest)
1998                                         stop_sendmail();
1999                                 (void) print_single_queue(qgrp, j);
2000                         }
2001                         finis(false, true, EX_OK);
2002                         /* NOTREACHED */
2003                 }
2004                 printqueue();
2005                 finis(false, true, EX_OK);
2006                 /* NOTREACHED */
2007                 break;
2008
2009           case MD_PRINTNQE:
2010                 /* print number of entries in queue */
2011                 (void) dropenvelope(&BlankEnvelope, true, false);
2012                 (void) sm_signal(SIGPIPE, sigpipe);
2013                 printnqe(smioout, NULL);
2014                 finis(false, true, EX_OK);
2015                 /* NOTREACHED */
2016                 break;
2017
2018           case MD_QUEUERUN:
2019                 /* only handle quarantining here */
2020                 if (quarantining == NULL)
2021                         break;
2022
2023                 if (QueueMode != QM_QUARANTINE &&
2024                     QueueMode != QM_NORMAL)
2025                 {
2026                         HoldErrs = false;
2027                         usrerr("Can not use -Q with -q%c", QueueMode);
2028                         ExitStat = EX_USAGE;
2029                         finis(false, true, ExitStat);
2030                         /* NOTREACHED */
2031                 }
2032                 quarantine_queue(quarantining, qgrp);
2033                 finis(false, true, EX_OK);
2034                 break;
2035
2036           case MD_HOSTSTAT:
2037                 (void) sm_signal(SIGPIPE, sigpipe);
2038                 (void) mci_traverse_persistent(mci_print_persistent, NULL);
2039                 finis(false, true, EX_OK);
2040                 /* NOTREACHED */
2041                 break;
2042
2043           case MD_PURGESTAT:
2044                 (void) mci_traverse_persistent(mci_purge_persistent, NULL);
2045                 finis(false, true, EX_OK);
2046                 /* NOTREACHED */
2047                 break;
2048
2049           case MD_INITALIAS:
2050                 /* initialize maps */
2051                 initmaps();
2052                 finis(false, true, ExitStat);
2053                 /* NOTREACHED */
2054                 break;
2055
2056           case MD_SMTP:
2057           case MD_DAEMON:
2058                 /* reset DSN parameters */
2059                 DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
2060                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2061                           macid("{dsn_notify}"), NULL);
2062                 BlankEnvelope.e_envid = NULL;
2063                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2064                           macid("{dsn_envid}"), NULL);
2065                 BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
2066                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2067                           macid("{dsn_ret}"), NULL);
2068
2069                 /* don't open maps for daemon -- done below in child */
2070                 break;
2071         }
2072
2073         if (tTd(0, 15))
2074         {
2075                 /* print configuration table (or at least part of it) */
2076                 if (tTd(0, 90))
2077                         printrules();
2078                 for (i = 0; i < MAXMAILERS; i++)
2079                 {
2080                         if (Mailer[i] != NULL)
2081                                 printmailer(sm_debug_file(), Mailer[i]);
2082                 }
2083         }
2084
2085         /*
2086         **  Switch to the main envelope.
2087         */
2088
2089         CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
2090                              sm_rpool_new_x(NULL));
2091         MainEnvelope.e_flags = BlankEnvelope.e_flags;
2092
2093         /*
2094         **  If test mode, read addresses from stdin and process.
2095         */
2096
2097         if (OpMode == MD_TEST)
2098         {
2099                 if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
2100                         Verbose = 2;
2101
2102                 if (Verbose)
2103                 {
2104                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2105                                      "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
2106                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2107                                      "Enter <ruleset> <address>\n");
2108                 }
2109                 macdefine(&(MainEnvelope.e_macro), A_PERM,
2110                           macid("{addr_type}"), "e r");
2111                 for (;;)
2112                 {
2113                         SM_TRY
2114                         {
2115                                 (void) sm_signal(SIGINT, intindebug);
2116                                 (void) sm_releasesignal(SIGINT);
2117                                 if (Verbose == 2)
2118                                         (void) sm_io_fprintf(smioout,
2119                                                              SM_TIME_DEFAULT,
2120                                                              "> ");
2121                                 (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
2122                                 if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
2123                                                 sizeof(buf)) < 0)
2124                                         testmodeline("/quit", &MainEnvelope);
2125                                 p = strchr(buf, '\n');
2126                                 if (p != NULL)
2127                                         *p = '\0';
2128                                 if (Verbose < 2)
2129                                         (void) sm_io_fprintf(smioout,
2130                                                              SM_TIME_DEFAULT,
2131                                                              "> %s\n", buf);
2132                                 testmodeline(buf, &MainEnvelope);
2133                         }
2134                         SM_EXCEPT(exc, "[!F]*")
2135                         {
2136                                 /*
2137                                 **  8.10 just prints \n on interrupt.
2138                                 **  I'm printing the exception here in case
2139                                 **  sendmail is extended to raise additional
2140                                 **  exceptions in this context.
2141                                 */
2142
2143                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2144                                                      "\n");
2145                                 sm_exc_print(exc, smioout);
2146                         }
2147                         SM_END_TRY
2148                 }
2149         }
2150
2151 #if STARTTLS
2152         tls_ok = true;
2153         if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER ||
2154             OpMode == MD_ARPAFTP)
2155         {
2156                 /* check whether STARTTLS is turned off for the client */
2157                 if (chkclientmodifiers(D_NOTLS))
2158                         tls_ok = false;
2159         }
2160         else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
2161                  OpMode == MD_SMTP)
2162         {
2163                 /* check whether STARTTLS is turned off */
2164                 if (chkdaemonmodifiers(D_NOTLS) && chkclientmodifiers(D_NOTLS))
2165                         tls_ok = false;
2166         }
2167         else    /* other modes don't need STARTTLS */
2168                 tls_ok = false;
2169
2170         if (tls_ok)
2171         {
2172                 /* basic TLS initialization */
2173                 j = init_tls_library(FipsMode);
2174                 if (j < 0)
2175                 {
2176                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2177                                      "ERROR: TLS failed to initialize\n");
2178                         exit(EX_USAGE);
2179                 }
2180                 if (j > 0)
2181                         tls_ok = false;
2182         }
2183
2184         if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
2185         {
2186                 /* disable TLS for client */
2187                 setclttls(false);
2188         }
2189 #endif /* STARTTLS */
2190
2191         /*
2192         **  If collecting stuff from the queue, go start doing that.
2193         */
2194
2195         if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
2196         {
2197                 pid_t pid = -1;
2198
2199 #if STARTTLS
2200                 /* init TLS for client, ignore result for now */
2201                 (void) initclttls(tls_ok);
2202 #endif
2203
2204                 /*
2205                 **  The parent process of the caller of runqueue() needs
2206                 **  to stay around for a possible SIGTERM. The SIGTERM will
2207                 **  tell this process that all of the queue runners children
2208                 **  need to be sent SIGTERM as well. At the same time, we
2209                 **  want to return control to the command line. So we do an
2210                 **  extra fork().
2211                 */
2212
2213                 if (Verbose || foregroundqueue || (pid = fork()) <= 0)
2214                 {
2215                         /*
2216                         **  If the fork() failed we should still try to do
2217                         **  the queue run. If it succeeded then the child
2218                         **  is going to start the run and wait for all
2219                         **  of the children to finish.
2220                         */
2221
2222                         if (pid == 0)
2223                         {
2224                                 /* Reset global flags */
2225                                 RestartRequest = NULL;
2226                                 ShutdownRequest = NULL;
2227                                 PendingSignal = 0;
2228
2229                                 /* disconnect from terminal */
2230                                 disconnect(2, CurEnv);
2231                         }
2232
2233                         CurrentPid = getpid();
2234                         if (qgrp != NOQGRP)
2235                         {
2236                                 int rwgflags = RWG_NONE;
2237
2238                                 /*
2239                                 **  To run a specific queue group mark it to
2240                                 **  be run, select the work group it's in and
2241                                 **  increment the work counter.
2242                                 */
2243
2244                                 for (i = 0; i < NumQueue && Queue[i] != NULL;
2245                                      i++)
2246                                         Queue[i]->qg_nextrun = (time_t) -1;
2247                                 Queue[qgrp]->qg_nextrun = 0;
2248                                 if (Verbose)
2249                                         rwgflags |= RWG_VERBOSE;
2250                                 if (queuepersistent)
2251                                         rwgflags |= RWG_PERSISTENT;
2252                                 rwgflags |= RWG_FORCE;
2253                                 (void) run_work_group(Queue[qgrp]->qg_wgrp,
2254                                                       rwgflags);
2255                         }
2256                         else
2257                                 (void) runqueue(false, Verbose,
2258                                                 queuepersistent, true);
2259
2260                         /* set the title to make it easier to find */
2261                         sm_setproctitle(true, CurEnv, "Queue control");
2262                         (void) sm_signal(SIGCHLD, SIG_DFL);
2263                         while (CurChildren > 0)
2264                         {
2265                                 int status;
2266                                 pid_t ret;
2267
2268                                 errno = 0;
2269                                 while ((ret = sm_wait(&status)) <= 0)
2270                                 {
2271                                         if (errno == ECHILD)
2272                                         {
2273                                                 /*
2274                                                 **  Oops... something got messed
2275                                                 **  up really bad. Waiting for
2276                                                 **  non-existent children
2277                                                 **  shouldn't happen. Let's get
2278                                                 **  out of here.
2279                                                 */
2280
2281                                                 CurChildren = 0;
2282                                                 break;
2283                                         }
2284                                         continue;
2285                                 }
2286
2287                                 /* something is really really wrong */
2288                                 if (errno == ECHILD)
2289                                 {
2290                                         sm_syslog(LOG_ERR, NOQID,
2291                                                   "queue control process: lost all children: wait returned ECHILD");
2292                                         break;
2293                                 }
2294
2295                                 /* Only drop when a child gives status */
2296                                 if (WIFSTOPPED(status))
2297                                         continue;
2298
2299                                 proc_list_drop(ret, status, NULL);
2300                         }
2301                 }
2302                 finis(true, true, ExitStat);
2303                 /* NOTREACHED */
2304         }
2305
2306 #if SASL
2307         if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
2308         {
2309                 /* check whether AUTH is turned off for the server */
2310                 if (!chkdaemonmodifiers(D_NOAUTH) &&
2311                     (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
2312                         syserr("!sasl_server_init failed! [%s]",
2313                                 sasl_errstring(i, NULL, NULL));
2314         }
2315 #endif /* SASL */
2316
2317         if (OpMode == MD_SMTP)
2318         {
2319                 proc_list_add(CurrentPid, "Sendmail SMTP Agent",
2320                               PROC_DAEMON, 0, -1, NULL);
2321
2322                 /* clean up background delivery children */
2323                 (void) sm_signal(SIGCHLD, reapchild);
2324         }
2325
2326         /*
2327         **  If a daemon, wait for a request.
2328         **      getrequests will always return in a child.
2329         **      If we should also be processing the queue, start
2330         **              doing it in background.
2331         **      We check for any errors that might have happened
2332         **              during startup.
2333         */
2334
2335         if (OpMode == MD_DAEMON || QueueIntvl > 0)
2336         {
2337                 char dtype[200];
2338
2339                 /* avoid cleanup in finis(), DaemonPid will be set below */
2340                 DaemonPid = 0;
2341                 if (!run_in_foreground && !tTd(99, 100))
2342                 {
2343                         /* put us in background */
2344                         i = fork();
2345                         if (i < 0)
2346                                 syserr("daemon: cannot fork");
2347                         if (i != 0)
2348                         {
2349                                 finis(false, true, EX_OK);
2350                                 /* NOTREACHED */
2351                         }
2352
2353                         /*
2354                         **  Initialize exception stack and default exception
2355                         **  handler for child process.
2356                         */
2357
2358                         /* Reset global flags */
2359                         RestartRequest = NULL;
2360                         RestartWorkGroup = false;
2361                         ShutdownRequest = NULL;
2362                         PendingSignal = 0;
2363                         CurrentPid = getpid();
2364
2365                         sm_exc_newthread(fatal_error);
2366
2367                         /* disconnect from our controlling tty */
2368                         disconnect(2, &MainEnvelope);
2369                 }
2370
2371                 dtype[0] = '\0';
2372                 if (OpMode == MD_DAEMON)
2373                 {
2374                         (void) sm_strlcat(dtype, "+SMTP", sizeof(dtype));
2375                         DaemonPid = CurrentPid;
2376                 }
2377                 if (QueueIntvl > 0)
2378                 {
2379                         (void) sm_strlcat2(dtype,
2380                                            queuepersistent
2381                                            ? "+persistent-queueing@"
2382                                            : "+queueing@",
2383                                            pintvl(QueueIntvl, true),
2384                                            sizeof(dtype));
2385                 }
2386                 if (tTd(0, 1))
2387                         (void) sm_strlcat(dtype, "+debugging", sizeof(dtype));
2388
2389                 sm_syslog(LOG_INFO, NOQID,
2390                           "starting daemon (%s): %s", Version, dtype + 1);
2391 #if XLA
2392                 xla_create_file();
2393 #endif
2394
2395                 /* save daemon type in a macro for possible PidFile use */
2396                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
2397                         macid("{daemon_info}"), dtype + 1);
2398
2399                 /* save queue interval in a macro for possible PidFile use */
2400                 macdefine(&MainEnvelope.e_macro, A_TEMP,
2401                         macid("{queue_interval}"), pintvl(QueueIntvl, true));
2402
2403                 /* workaround: can't seem to release the signal in the parent */
2404                 (void) sm_signal(SIGHUP, sighup);
2405                 (void) sm_releasesignal(SIGHUP);
2406                 (void) sm_signal(SIGTERM, sigterm);
2407
2408                 if (QueueIntvl > 0)
2409                 {
2410 #if _FFR_RUNPQG
2411                         if (qgrp != NOQGRP)
2412                         {
2413                                 int rwgflags = RWG_NONE;
2414
2415                                 /*
2416                                 **  To run a specific queue group mark it to
2417                                 **  be run, select the work group it's in and
2418                                 **  increment the work counter.
2419                                 */
2420
2421                                 for (i = 0; i < NumQueue && Queue[i] != NULL;
2422                                      i++)
2423                                         Queue[i]->qg_nextrun = (time_t) -1;
2424                                 Queue[qgrp]->qg_nextrun = 0;
2425                                 if (Verbose)
2426                                         rwgflags |= RWG_VERBOSE;
2427                                 if (queuepersistent)
2428                                         rwgflags |= RWG_PERSISTENT;
2429                                 rwgflags |= RWG_FORCE;
2430                                 (void) run_work_group(Queue[qgrp]->qg_wgrp,
2431                                                       rwgflags);
2432                         }
2433                         else
2434 #endif /* _FFR_RUNPQG */
2435                                 (void) runqueue(true, false, queuepersistent,
2436                                                 true);
2437
2438                         /*
2439                         **  If queuepersistent but not in daemon mode then
2440                         **  we're going to do the queue runner monitoring here.
2441                         **  If in daemon mode then the monitoring will happen
2442                         **  elsewhere.
2443                         */
2444
2445                         if (OpMode != MD_DAEMON && queuepersistent)
2446                         {
2447                                 /*
2448                                 **  Write the pid to file
2449                                 **  XXX Overwrites sendmail.pid
2450                                 */
2451
2452                                 log_sendmail_pid(&MainEnvelope);
2453
2454                                 /* set the title to make it easier to find */
2455                                 sm_setproctitle(true, CurEnv, "Queue control");
2456                                 (void) sm_signal(SIGCHLD, SIG_DFL);
2457                                 while (CurChildren > 0)
2458                                 {
2459                                         int status;
2460                                         pid_t ret;
2461                                         int group;
2462
2463                                         CHECK_RESTART;
2464                                         errno = 0;
2465                                         while ((ret = sm_wait(&status)) <= 0)
2466                                         {
2467                                                 /*
2468                                                 **  Waiting for non-existent
2469                                                 **  children shouldn't happen.
2470                                                 **  Let's get out of here if
2471                                                 **  it occurs.
2472                                                 */
2473
2474                                                 if (errno == ECHILD)
2475                                                 {
2476                                                         CurChildren = 0;
2477                                                         break;
2478                                                 }
2479                                                 continue;
2480                                         }
2481
2482                                         /* something is really really wrong */
2483                                         if (errno == ECHILD)
2484                                         {
2485                                                 sm_syslog(LOG_ERR, NOQID,
2486                                                           "persistent queue runner control process: lost all children: wait returned ECHILD");
2487                                                 break;
2488                                         }
2489
2490                                         if (WIFSTOPPED(status))
2491                                                 continue;
2492
2493                                         /* Probe only on a child status */
2494                                         proc_list_drop(ret, status, &group);
2495
2496                                         if (WIFSIGNALED(status))
2497                                         {
2498                                                 if (WCOREDUMP(status))
2499                                                 {
2500                                                         sm_syslog(LOG_ERR, NOQID,
2501                                                                   "persistent queue runner=%d core dumped, signal=%d",
2502                                                                   group, WTERMSIG(status));
2503
2504                                                         /* don't restart this */
2505                                                         mark_work_group_restart(
2506                                                                 group, -1);
2507                                                         continue;
2508                                                 }
2509
2510                                                 sm_syslog(LOG_ERR, NOQID,
2511                                                           "persistent queue runner=%d died, pid=%ld, signal=%d",
2512                                                           group, (long) ret,
2513                                                           WTERMSIG(status));
2514                                         }
2515
2516                                         /*
2517                                         **  When debugging active, don't
2518                                         **  restart the persistent queues.
2519                                         **  But do log this as info.
2520                                         */
2521
2522                                         if (sm_debug_active(&DebugNoPRestart,
2523                                                             1))
2524                                         {
2525                                                 sm_syslog(LOG_DEBUG, NOQID,
2526                                                           "persistent queue runner=%d, exited",
2527                                                           group);
2528                                                 mark_work_group_restart(group,
2529                                                                         -1);
2530                                         }
2531                                         CHECK_RESTART;
2532                                 }
2533                                 finis(true, true, ExitStat);
2534                                 /* NOTREACHED */
2535                         }
2536
2537                         if (OpMode != MD_DAEMON)
2538                         {
2539                                 char qtype[200];
2540
2541                                 /*
2542                                 **  Write the pid to file
2543                                 **  XXX Overwrites sendmail.pid
2544                                 */
2545
2546                                 log_sendmail_pid(&MainEnvelope);
2547
2548                                 /* set the title to make it easier to find */
2549                                 qtype[0] = '\0';
2550                                 (void) sm_strlcpyn(qtype, sizeof(qtype), 4,
2551                                                    "Queue runner@",
2552                                                    pintvl(QueueIntvl, true),
2553                                                    " for ",
2554                                                    QueueDir);
2555                                 sm_setproctitle(true, CurEnv, qtype);
2556                                 for (;;)
2557                                 {
2558                                         (void) pause();
2559
2560                                         CHECK_RESTART;
2561
2562                                         if (doqueuerun())
2563                                                 (void) runqueue(true, false,
2564                                                                 false, false);
2565                                 }
2566                         }
2567                 }
2568                 (void) dropenvelope(&MainEnvelope, true, false);
2569
2570 #if STARTTLS
2571                 /* init TLS for server, ignore result for now */
2572                 (void) initsrvtls(tls_ok);
2573 #endif
2574
2575         nextreq:
2576                 p_flags = getrequests(&MainEnvelope);
2577
2578                 /* drop privileges */
2579                 (void) drop_privileges(false);
2580
2581                 /*
2582                 **  Get authentication data
2583                 **  Set _ macro in BlankEnvelope before calling newenvelope().
2584                 */
2585
2586 #if _FFR_XCNCT
2587                 if (bitnset(D_XCNCT, *p_flags) || bitnset(D_XCNCT_M, *p_flags))
2588                 {
2589                         /* copied from getauthinfo() */
2590                         if (RealHostName == NULL)
2591                         {
2592                                 RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
2593                                 if (strlen(RealHostName) > MAXNAME)
2594                                         RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
2595                         }
2596                         snprintf(buf, sizeof(buf), "%s [%s]",
2597                                 RealHostName, anynet_ntoa(&RealHostAddr));
2598
2599                         forged = bitnset(D_XCNCT_M, *p_flags);
2600                         if (forged)
2601                         {
2602                                 (void) sm_strlcat(buf, " (may be forged)",
2603                                                 sizeof(buf));
2604                                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2605                                           macid("{client_resolve}"), "FORGED");
2606                         }
2607
2608                         /* HACK! variable used only two times right below */
2609                         authinfo = buf;
2610                         if (tTd(75, 9))
2611                                 sm_syslog(LOG_INFO, NOQID,
2612                                         "main: where=not_calling_getauthinfo, RealHostAddr=%s",
2613                                         anynet_ntoa(&RealHostAddr));
2614                 }
2615                 else
2616                 /* WARNING: "non-braced" else */
2617 #endif /* _FFR_XCNCT */
2618                 authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
2619                                                      NULL), &forged);
2620                 macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
2621                 if (tTd(75, 9))
2622                         sm_syslog(LOG_INFO, NOQID,
2623                                 "main: where=after_getauthinfo, RealHostAddr=%s",
2624                                 anynet_ntoa(&RealHostAddr));
2625
2626                 /* at this point we are in a child: reset state */
2627                 sm_rpool_free(MainEnvelope.e_rpool);
2628                 (void) newenvelope(&MainEnvelope, &MainEnvelope,
2629                                    sm_rpool_new_x(NULL));
2630         }
2631
2632         if (LogLevel > 9)
2633         {
2634                 p = authinfo;
2635                 if (NULL == p)
2636                 {
2637                         if (NULL != RealHostName)
2638                                 p = RealHostName;
2639                         else
2640                                 p = anynet_ntoa(&RealHostAddr);
2641                         if (NULL == p)
2642                                 p = "unknown";
2643                 }
2644
2645                 /* log connection information */
2646                 sm_syslog(LOG_INFO, NULL, "connect from %s", p);
2647         }
2648
2649         /*
2650         **  If running SMTP protocol, start collecting and executing
2651         **  commands.  This will never return.
2652         */
2653
2654         if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
2655         {
2656                 char pbuf[20];
2657
2658                 /*
2659                 **  Save some macros for check_* rulesets.
2660                 */
2661
2662                 if (forged)
2663                 {
2664                         char ipbuf[103];
2665
2666                         (void) sm_snprintf(ipbuf, sizeof(ipbuf), "[%.100s]",
2667                                            anynet_ntoa(&RealHostAddr));
2668                         macdefine(&BlankEnvelope.e_macro, A_TEMP,
2669                                   macid("{client_name}"), ipbuf);
2670                 }
2671                 else
2672                         macdefine(&BlankEnvelope.e_macro, A_PERM,
2673                                   macid("{client_name}"), RealHostName);
2674                 macdefine(&BlankEnvelope.e_macro, A_PERM,
2675                           macid("{client_ptr}"), RealHostName);
2676                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
2677                           macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
2678                 sm_getla();
2679
2680                 switch (RealHostAddr.sa.sa_family)
2681                 {
2682 #if NETINET
2683                   case AF_INET:
2684                         (void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
2685                                            ntohs(RealHostAddr.sin.sin_port));
2686                         break;
2687 #endif /* NETINET */
2688 #if NETINET6
2689                   case AF_INET6:
2690                         (void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
2691                                            ntohs(RealHostAddr.sin6.sin6_port));
2692                         break;
2693 #endif /* NETINET6 */
2694                   default:
2695                         (void) sm_snprintf(pbuf, sizeof(pbuf), "0");
2696                         break;
2697                 }
2698                 macdefine(&BlankEnvelope.e_macro, A_TEMP,
2699                         macid("{client_port}"), pbuf);
2700
2701                 if (OpMode == MD_DAEMON)
2702                 {
2703                         ENVELOPE *saved_env;
2704
2705                         /* validate the connection */
2706                         HoldErrs = true;
2707                         saved_env = CurEnv;
2708                         CurEnv = &BlankEnvelope;
2709                         nullserver = validate_connection(&RealHostAddr,
2710                                                 macvalue(macid("{client_name}"),
2711                                                         &BlankEnvelope),
2712                                                 &BlankEnvelope);
2713                         if (bitset(EF_DISCARD, BlankEnvelope.e_flags))
2714                                 MainEnvelope.e_flags |= EF_DISCARD;
2715                         CurEnv = saved_env;
2716                         HoldErrs = false;
2717                 }
2718                 else if (p_flags == NULL)
2719                 {
2720                         p_flags = (BITMAP256 *) xalloc(sizeof(*p_flags));
2721                         clrbitmap(p_flags);
2722                 }
2723 #if STARTTLS
2724                 if (OpMode == MD_SMTP)
2725                         (void) initsrvtls(tls_ok);
2726 #endif
2727
2728                 /* turn off profiling */
2729                 SM_PROF(1);
2730                 smtp(nullserver, *p_flags, &MainEnvelope);
2731
2732                 if (tTd(93, 100))
2733                 {
2734                         /* turn off profiling */
2735                         SM_PROF(0);
2736                         if (OpMode == MD_DAEMON)
2737                                 goto nextreq;
2738                 }
2739         }
2740
2741         sm_rpool_free(MainEnvelope.e_rpool);
2742         clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
2743         if (OpMode == MD_VERIFY)
2744         {
2745                 set_delivery_mode(SM_VERIFY, &MainEnvelope);
2746                 PostMasterCopy = NULL;
2747         }
2748         else
2749         {
2750                 /* interactive -- all errors are global */
2751                 MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
2752         }
2753
2754         /*
2755         **  Do basic system initialization and set the sender
2756         */
2757
2758         initsys(&MainEnvelope);
2759         macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
2760         macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
2761         setsender(from, &MainEnvelope, NULL, '\0', false);
2762         if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
2763             (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
2764              strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
2765         {
2766                 auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
2767                              RealUserName, from, warn_f_flag);
2768 #if SASL
2769                 auth = false;
2770 #endif
2771         }
2772         if (auth)
2773         {
2774                 char *fv;
2775
2776                 /* set the initial sender for AUTH= to $f@$j */
2777                 fv = macvalue('f', &MainEnvelope);
2778                 if (fv == NULL || *fv == '\0')
2779                         MainEnvelope.e_auth_param = NULL;
2780                 else
2781                 {
2782                         if (strchr(fv, '@') == NULL)
2783                         {
2784                                 i = strlen(fv) + strlen(macvalue('j',
2785                                                         &MainEnvelope)) + 2;
2786                                 p = sm_malloc_x(i);
2787                                 (void) sm_strlcpyn(p, i, 3, fv, "@",
2788                                                    macvalue('j',
2789                                                             &MainEnvelope));
2790                         }
2791                         else
2792                                 p = sm_strdup_x(fv);
2793                         MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
2794                                                                       xtextify(p, "="));
2795                         sm_free(p);  /* XXX */
2796                 }
2797         }
2798         if (macvalue('s', &MainEnvelope) == NULL)
2799                 macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
2800
2801         av = argv + optind;
2802         if (*av == NULL && !GrabTo)
2803         {
2804                 MainEnvelope.e_to = NULL;
2805                 MainEnvelope.e_flags |= EF_GLOBALERRS;
2806                 HoldErrs = false;
2807                 SuperSafe = SAFE_NO;
2808                 usrerr("Recipient names must be specified");
2809
2810                 /* collect body for UUCP return */
2811                 if (OpMode != MD_VERIFY)
2812                         collect(InChannel, false, NULL, &MainEnvelope, true);
2813                 finis(true, true, EX_USAGE);
2814                 /* NOTREACHED */
2815         }
2816
2817         /*
2818         **  Scan argv and deliver the message to everyone.
2819         */
2820
2821         save_val = LogUsrErrs;
2822         LogUsrErrs = true;
2823         sendtoargv(av, &MainEnvelope);
2824         LogUsrErrs = save_val;
2825
2826         /* if we have had errors sofar, arrange a meaningful exit stat */
2827         if (Errors > 0 && ExitStat == EX_OK)
2828                 ExitStat = EX_USAGE;
2829
2830 #if _FFR_FIX_DASHT
2831         /*
2832         **  If using -t, force not sending to argv recipients, even
2833         **  if they are mentioned in the headers.
2834         */
2835
2836         if (GrabTo)
2837         {
2838                 ADDRESS *q;
2839
2840                 for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
2841                         q->q_state = QS_REMOVED;
2842         }
2843 #endif /* _FFR_FIX_DASHT */
2844
2845         /*
2846         **  Read the input mail.
2847         */
2848
2849         MainEnvelope.e_to = NULL;
2850         if (OpMode != MD_VERIFY || GrabTo)
2851         {
2852                 int savederrors;
2853                 unsigned long savedflags;
2854
2855                 /*
2856                 **  workaround for compiler warning on Irix:
2857                 **  do not initialize variable in the definition, but
2858                 **  later on:
2859                 **  warning(1548): transfer of control bypasses
2860                 **  initialization of:
2861                 **  variable "savederrors" (declared at line 2570)
2862                 **  variable "savedflags" (declared at line 2571)
2863                 **  goto giveup;
2864                 */
2865
2866                 savederrors = Errors;
2867                 savedflags = MainEnvelope.e_flags & EF_FATALERRS;
2868                 MainEnvelope.e_flags |= EF_GLOBALERRS;
2869                 MainEnvelope.e_flags &= ~EF_FATALERRS;
2870                 Errors = 0;
2871                 buffer_errors();
2872                 collect(InChannel, false, NULL, &MainEnvelope, true);
2873
2874                 /* header checks failed */
2875                 if (Errors > 0)
2876                 {
2877   giveup:
2878                         if (!GrabTo)
2879                         {
2880                                 /* Log who the mail would have gone to */
2881                                 logundelrcpts(&MainEnvelope,
2882                                               MainEnvelope.e_message,
2883                                               8, false);
2884                         }
2885                         flush_errors(true);
2886                         finis(true, true, ExitStat);
2887                         /* NOTREACHED */
2888                         return -1;
2889                 }
2890
2891                 /* bail out if message too large */
2892                 if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
2893                 {
2894                         finis(true, true, ExitStat != EX_OK ? ExitStat
2895                                                             : EX_DATAERR);
2896                         /* NOTREACHED */
2897                         return -1;
2898                 }
2899
2900                 /* set message size */
2901                 (void) sm_snprintf(buf, sizeof(buf), "%ld",
2902                                    PRT_NONNEGL(MainEnvelope.e_msgsize));
2903                 macdefine(&MainEnvelope.e_macro, A_TEMP,
2904                           macid("{msg_size}"), buf);
2905
2906                 Errors = savederrors;
2907                 MainEnvelope.e_flags |= savedflags;
2908         }
2909         errno = 0;
2910
2911         if (tTd(1, 1))
2912                 sm_dprintf("From person = \"%s\"\n",
2913                            MainEnvelope.e_from.q_paddr);
2914
2915         /* Check if quarantining stats should be updated */
2916         if (MainEnvelope.e_quarmsg != NULL)
2917                 markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
2918
2919         /*
2920         **  Actually send everything.
2921         **      If verifying, just ack.
2922         */
2923
2924         if (Errors == 0)
2925         {
2926                 if (!split_by_recipient(&MainEnvelope) &&
2927                     bitset(EF_FATALERRS, MainEnvelope.e_flags))
2928                         goto giveup;
2929         }
2930
2931         /* make sure we deliver at least the first envelope */
2932         i = FastSplit > 0 ? 0 : -1;
2933         for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
2934         {
2935                 ENVELOPE *next;
2936
2937                 e->e_from.q_state = QS_SENDER;
2938                 if (tTd(1, 5))
2939                 {
2940                         sm_dprintf("main[%d]: QS_SENDER ", i);
2941                         printaddr(sm_debug_file(), &e->e_from, false);
2942                 }
2943                 e->e_to = NULL;
2944                 sm_getla();
2945                 GrabTo = false;
2946 #if NAMED_BIND
2947                 _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
2948                 _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
2949 #endif
2950                 next = e->e_sibling;
2951                 e->e_sibling = NULL;
2952
2953                 /* after FastSplit envelopes: queue up */
2954                 sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
2955                 e->e_sibling = next;
2956         }
2957
2958         /*
2959         **  All done.
2960         **      Don't send return error message if in VERIFY mode.
2961         */
2962
2963         finis(true, true, ExitStat);
2964         /* NOTREACHED */
2965         return ExitStat;
2966 }
2967 /*
2968 **  STOP_SENDMAIL -- Stop the running program
2969 **
2970 **      Parameters:
2971 **              none.
2972 **
2973 **      Returns:
2974 **              none.
2975 **
2976 **      Side Effects:
2977 **              exits.
2978 */
2979
2980 void
2981 stop_sendmail()
2982 {
2983         /* reset uid for process accounting */
2984         endpwent();
2985         (void) setuid(RealUid);
2986         exit(EX_OK);
2987 }
2988 /*
2989 **  FINIS -- Clean up and exit.
2990 **
2991 **      Parameters:
2992 **              drop -- whether or not to drop CurEnv envelope
2993 **              cleanup -- call exit() or _exit()?
2994 **              exitstat -- exit status to use for exit() call
2995 **
2996 **      Returns:
2997 **              never
2998 **
2999 **      Side Effects:
3000 **              exits sendmail
3001 */
3002
3003 void
3004 finis(drop, cleanup, exitstat)
3005         bool drop;
3006         bool cleanup;
3007         volatile int exitstat;
3008 {
3009         char pidpath[MAXPATHLEN];
3010         pid_t pid;
3011
3012         /* Still want to process new timeouts added below */
3013         sm_clear_events();
3014         (void) sm_releasesignal(SIGALRM);
3015
3016 #if RATECTL_DEBUG || _FFR_OCC
3017         /* do this only in "main" process */
3018         if (DaemonPid == getpid())
3019         {
3020                 SM_FILE_T *fp;
3021
3022                 fp = sm_debug_file();
3023                 if (fp != NULL)
3024                         dump_ch(fp);
3025         }
3026 #endif
3027         if (tTd(2, 1))
3028         {
3029                 sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
3030                            exitstat,
3031                            CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
3032                 printenvflags(CurEnv);
3033         }
3034         if (tTd(2, 9))
3035                 printopenfds(false);
3036
3037         SM_TRY
3038                 /*
3039                 **  Clean up.  This might raise E:mta.quickabort
3040                 */
3041
3042                 /* clean up temp files */
3043                 CurEnv->e_to = NULL;
3044                 if (drop)
3045                 {
3046                         if (CurEnv->e_id != NULL)
3047                         {
3048                                 int r;
3049
3050                                 r = dropenvelope(CurEnv, true, false);
3051                                 if (exitstat == EX_OK)
3052                                         exitstat = r;
3053                                 sm_rpool_free(CurEnv->e_rpool);
3054                                 CurEnv->e_rpool = NULL;
3055
3056                                 /* these may have pointed to the rpool */
3057                                 CurEnv->e_to = NULL;
3058                                 CurEnv->e_message = NULL;
3059                                 CurEnv->e_statmsg = NULL;
3060                                 CurEnv->e_quarmsg = NULL;
3061                                 CurEnv->e_bodytype = NULL;
3062                                 CurEnv->e_id = NULL;
3063                                 CurEnv->e_envid = NULL;
3064                                 CurEnv->e_auth_param = NULL;
3065                         }
3066                         else
3067                                 poststats(StatFile);
3068                 }
3069
3070                 /* flush any cached connections */
3071                 mci_flush(true, NULL);
3072
3073                 /* close maps belonging to this pid */
3074                 closemaps(false);
3075
3076 #if USERDB
3077                 /* close UserDatabase */
3078                 _udbx_close();
3079 #endif
3080
3081 #if SASL
3082                 stop_sasl_client();
3083 #endif
3084
3085 #if XLA
3086                 /* clean up extended load average stuff */
3087                 xla_all_end();
3088 #endif
3089
3090         SM_FINALLY
3091                 /*
3092                 **  And exit.
3093                 */
3094
3095                 if (LogLevel > 78)
3096                         sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
3097                                   (int) CurrentPid);
3098                 if (exitstat == EX_TEMPFAIL ||
3099                     CurEnv->e_errormode == EM_BERKNET)
3100                         exitstat = EX_OK;
3101
3102                 /* XXX clean up queues and related data structures */
3103                 cleanup_queues();
3104                 pid = getpid();
3105 #if SM_CONF_SHM
3106                 cleanup_shm(DaemonPid == pid);
3107 #endif
3108
3109                 /* close locked pid file */
3110                 close_sendmail_pid();
3111
3112                 if (DaemonPid == pid || PidFilePid == pid)
3113                 {
3114                         /* blow away the pid file */
3115                         expand(PidFile, pidpath, sizeof(pidpath), CurEnv);
3116                         (void) unlink(pidpath);
3117                 }
3118
3119                 /* reset uid for process accounting */
3120                 endpwent();
3121                 sm_mbdb_terminate();
3122 #if _FFR_MEMSTAT
3123                 (void) sm_memstat_close();
3124 #endif
3125                 (void) setuid(RealUid);
3126 #if SM_HEAP_CHECK
3127                 /* dump the heap, if we are checking for memory leaks */
3128                 if (sm_debug_active(&SmHeapCheck, 2))
3129                         sm_heap_report(smioout,
3130                                        sm_debug_level(&SmHeapCheck) - 1);
3131 #endif
3132                 if (sm_debug_active(&SmXtrapReport, 1))
3133                         sm_dprintf("xtrap count = %d\n", SmXtrapCount);
3134                 if (cleanup)
3135                         exit(exitstat);
3136                 else
3137                         _exit(exitstat);
3138         SM_END_TRY
3139 }
3140 /*
3141 **  INTINDEBUG -- signal handler for SIGINT in -bt mode
3142 **
3143 **      Parameters:
3144 **              sig -- incoming signal.
3145 **
3146 **      Returns:
3147 **              none.
3148 **
3149 **      Side Effects:
3150 **              longjmps back to test mode loop.
3151 **
3152 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3153 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3154 **              DOING.
3155 */
3156
3157 /* Type of an exception generated on SIGINT during address test mode.  */
3158 static const SM_EXC_TYPE_T EtypeInterrupt =
3159 {
3160         SmExcTypeMagic,
3161         "S:mta.interrupt",
3162         "",
3163         sm_etype_printf,
3164         "interrupt",
3165 };
3166
3167 /* ARGSUSED */
3168 static SIGFUNC_DECL
3169 intindebug(sig)
3170         int sig;
3171 {
3172         int save_errno = errno;
3173
3174         FIX_SYSV_SIGNAL(sig, intindebug);
3175         errno = save_errno;
3176         CHECK_CRITICAL(sig);
3177         errno = save_errno;
3178         sm_exc_raisenew_x(&EtypeInterrupt);
3179         errno = save_errno;
3180         return SIGFUNC_RETURN;
3181 }
3182 /*
3183 **  SIGTERM -- SIGTERM handler for the daemon
3184 **
3185 **      Parameters:
3186 **              sig -- signal number.
3187 **
3188 **      Returns:
3189 **              none.
3190 **
3191 **      Side Effects:
3192 **              Sets ShutdownRequest which will hopefully trigger
3193 **              the daemon to exit.
3194 **
3195 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3196 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3197 **              DOING.
3198 */
3199
3200 /* ARGSUSED */
3201 static SIGFUNC_DECL
3202 sigterm(sig)
3203         int sig;
3204 {
3205         int save_errno = errno;
3206
3207         FIX_SYSV_SIGNAL(sig, sigterm);
3208         ShutdownRequest = "signal";
3209         errno = save_errno;
3210         return SIGFUNC_RETURN;
3211 }
3212 /*
3213 **  SIGHUP -- handle a SIGHUP signal
3214 **
3215 **      Parameters:
3216 **              sig -- incoming signal.
3217 **
3218 **      Returns:
3219 **              none.
3220 **
3221 **      Side Effects:
3222 **              Sets RestartRequest which should cause the daemon
3223 **              to restart.
3224 **
3225 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3226 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3227 **              DOING.
3228 */
3229
3230 /* ARGSUSED */
3231 static SIGFUNC_DECL
3232 sighup(sig)
3233         int sig;
3234 {
3235         int save_errno = errno;
3236
3237         FIX_SYSV_SIGNAL(sig, sighup);
3238         RestartRequest = "signal";
3239         errno = save_errno;
3240         return SIGFUNC_RETURN;
3241 }
3242 /*
3243 **  SIGPIPE -- signal handler for SIGPIPE
3244 **
3245 **      Parameters:
3246 **              sig -- incoming signal.
3247 **
3248 **      Returns:
3249 **              none.
3250 **
3251 **      Side Effects:
3252 **              Sets StopRequest which should cause the mailq/hoststatus
3253 **              display to stop.
3254 **
3255 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3256 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3257 **              DOING.
3258 */
3259
3260 /* ARGSUSED */
3261 static SIGFUNC_DECL
3262 sigpipe(sig)
3263         int sig;
3264 {
3265         int save_errno = errno;
3266
3267         FIX_SYSV_SIGNAL(sig, sigpipe);
3268         StopRequest = true;
3269         errno = save_errno;
3270         return SIGFUNC_RETURN;
3271 }
3272 /*
3273 **  INTSIG -- clean up on interrupt
3274 **
3275 **      This just arranges to exit.  It pessimizes in that it
3276 **      may resend a message.
3277 **
3278 **      Parameters:
3279 **              sig -- incoming signal.
3280 **
3281 **      Returns:
3282 **              none.
3283 **
3284 **      Side Effects:
3285 **              Unlocks the current job.
3286 **
3287 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3288 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3289 **              DOING.
3290 */
3291
3292 /* ARGSUSED */
3293 SIGFUNC_DECL
3294 intsig(sig)
3295         int sig;
3296 {
3297         bool drop = false;
3298         int save_errno = errno;
3299
3300         FIX_SYSV_SIGNAL(sig, intsig);
3301         errno = save_errno;
3302         CHECK_CRITICAL(sig);
3303         sm_allsignals(true);
3304         IntSig = true;
3305
3306         FileName = NULL;
3307
3308         /* Clean-up on aborted stdin message submission */
3309         if  (OpMode == MD_SMTP ||
3310              OpMode == MD_DELIVER ||
3311              OpMode == MD_ARPAFTP)
3312         {
3313                 if (CurEnv->e_id != NULL)
3314                 {
3315                         char *fn;
3316
3317                         fn = queuename(CurEnv, DATAFL_LETTER);
3318                         if (fn != NULL)
3319                                 (void) unlink(fn);
3320                         fn = queuename(CurEnv, ANYQFL_LETTER);
3321                         if (fn != NULL)
3322                                 (void) unlink(fn);
3323                 }
3324                 _exit(EX_OK);
3325                 /* NOTREACHED */
3326         }
3327
3328         if (sig != 0 && LogLevel > 79)
3329                 sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
3330         if (OpMode != MD_TEST)
3331                 unlockqueue(CurEnv);
3332
3333         finis(drop, false, EX_OK);
3334         /* NOTREACHED */
3335 }
3336 /*
3337 **  DISCONNECT -- remove our connection with any foreground process
3338 **
3339 **      Parameters:
3340 **              droplev -- how "deeply" we should drop the line.
3341 **                      0 -- ignore signals, mail back errors, make sure
3342 **                           output goes to stdout.
3343 **                      1 -- also, make stdout go to /dev/null.
3344 **                      2 -- also, disconnect from controlling terminal
3345 **                           (only for daemon mode).
3346 **              e -- the current envelope.
3347 **
3348 **      Returns:
3349 **              none
3350 **
3351 **      Side Effects:
3352 **              Trys to insure that we are immune to vagaries of
3353 **              the controlling tty.
3354 */
3355
3356 void
3357 disconnect(droplev, e)
3358         int droplev;
3359         register ENVELOPE *e;
3360 {
3361         int fd;
3362
3363         if (tTd(52, 1))
3364                 sm_dprintf("disconnect: In %d Out %d, e=%p\n",
3365                            sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
3366                            sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL),
3367                            (void *)e);
3368         if (tTd(52, 100))
3369         {
3370                 sm_dprintf("don't\n");
3371                 return;
3372         }
3373         if (LogLevel > 93)
3374                 sm_syslog(LOG_DEBUG, e->e_id,
3375                           "disconnect level %d",
3376                           droplev);
3377
3378         /* be sure we don't get nasty signals */
3379         (void) sm_signal(SIGINT, SIG_IGN);
3380         (void) sm_signal(SIGQUIT, SIG_IGN);
3381
3382         /* we can't communicate with our caller, so.... */
3383         HoldErrs = true;
3384         CurEnv->e_errormode = EM_MAIL;
3385         Verbose = 0;
3386         DisConnected = true;
3387
3388         /* all input from /dev/null */
3389         if (InChannel != smioin)
3390         {
3391                 (void) sm_io_close(InChannel, SM_TIME_DEFAULT);
3392                 InChannel = smioin;
3393         }
3394         if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
3395                          SM_IO_RDONLY, NULL, smioin) == NULL)
3396                 sm_syslog(LOG_ERR, e->e_id,
3397                           "disconnect: sm_io_reopen(\"%s\") failed: %s",
3398                           SM_PATH_DEVNULL, sm_errstring(errno));
3399
3400         /*
3401         **  output to the transcript
3402         **      We also compare the fd numbers here since OutChannel
3403         **      might be a layer on top of smioout due to encryption
3404         **      (see sfsasl.c).
3405         */
3406
3407         if (OutChannel != smioout &&
3408             sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
3409             sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
3410         {
3411                 (void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
3412                 OutChannel = smioout;
3413
3414 #if 0
3415                 /*
3416                 **  Has smioout been closed? Reopen it.
3417                 **      This shouldn't happen anymore, the code is here
3418                 **      just as a reminder.
3419                 */
3420
3421                 if (smioout->sm_magic == NULL &&
3422                     sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
3423                                  SM_IO_WRONLY, NULL, smioout) == NULL)
3424                         sm_syslog(LOG_ERR, e->e_id,
3425                                   "disconnect: sm_io_reopen(\"%s\") failed: %s",
3426                                   SM_PATH_DEVNULL, sm_errstring(errno));
3427 #endif /* 0 */
3428         }
3429         if (droplev > 0)
3430         {
3431                 fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
3432                 if (fd == -1)
3433                 {
3434                         sm_syslog(LOG_ERR, e->e_id,
3435                                   "disconnect: open(\"%s\") failed: %s",
3436                                   SM_PATH_DEVNULL, sm_errstring(errno));
3437                 }
3438                 (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
3439                 if (fd >= 0)
3440                 {
3441                         (void) dup2(fd, STDOUT_FILENO);
3442                         (void) dup2(fd, STDERR_FILENO);
3443                         (void) close(fd);
3444                 }
3445         }
3446
3447         /* drop our controlling TTY completely if possible */
3448         if (droplev > 1)
3449         {
3450                 (void) setsid();
3451                 errno = 0;
3452         }
3453
3454 #if XDEBUG
3455         checkfd012("disconnect");
3456 #endif
3457
3458         if (LogLevel > 71)
3459                 sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
3460                           (int) CurrentPid);
3461
3462         errno = 0;
3463 }
3464
3465 static void
3466 obsolete(argv)
3467         char *argv[];
3468 {
3469         register char *ap;
3470         register char *op;
3471
3472         while ((ap = *++argv) != NULL)
3473         {
3474                 /* Return if "--" or not an option of any form. */
3475                 if (ap[0] != '-' || ap[1] == '-')
3476                         return;
3477
3478                 /* Don't allow users to use "-Q." or "-Q ." */
3479                 if ((ap[1] == 'Q' && ap[2] == '.') ||
3480                     (ap[1] == 'Q' && argv[1] != NULL &&
3481                      argv[1][0] == '.' && argv[1][1] == '\0'))
3482                 {
3483                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3484                                              "Can not use -Q.\n");
3485                         exit(EX_USAGE);
3486                 }
3487
3488                 /* skip over options that do have a value */
3489                 op = strchr(OPTIONS, ap[1]);
3490                 if (op != NULL && *++op == ':' && ap[2] == '\0' &&
3491                     ap[1] != 'd' &&
3492 #if defined(sony_news)
3493                     ap[1] != 'E' && ap[1] != 'J' &&
3494 #endif
3495                     argv[1] != NULL && argv[1][0] != '-')
3496                 {
3497                         argv++;
3498                         continue;
3499                 }
3500
3501                 /* If -C doesn't have an argument, use sendmail.cf. */
3502 #define __DEFPATH       "sendmail.cf"
3503                 if (ap[1] == 'C' && ap[2] == '\0')
3504                 {
3505                         *argv = xalloc(sizeof(__DEFPATH) + 2);
3506                         (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
3507                                            "-C", __DEFPATH);
3508                 }
3509
3510                 /* If -q doesn't have an argument, run it once. */
3511                 if (ap[1] == 'q' && ap[2] == '\0')
3512                         *argv = "-q0";
3513
3514                 /* If -Q doesn't have an argument, disable quarantining */
3515                 if (ap[1] == 'Q' && ap[2] == '\0')
3516                         *argv = "-Q.";
3517
3518                 /* if -d doesn't have an argument, use 0-99.1 */
3519                 if (ap[1] == 'd' && ap[2] == '\0')
3520                         *argv = "-d0-99.1";
3521
3522 #if defined(sony_news)
3523                 /* if -E doesn't have an argument, use -EC */
3524                 if (ap[1] == 'E' && ap[2] == '\0')
3525                         *argv = "-EC";
3526
3527                 /* if -J doesn't have an argument, use -JJ */
3528                 if (ap[1] == 'J' && ap[2] == '\0')
3529                         *argv = "-JJ";
3530 #endif /* defined(sony_news) */
3531         }
3532 }
3533 /*
3534 **  AUTH_WARNING -- specify authorization warning
3535 **
3536 **      Parameters:
3537 **              e -- the current envelope.
3538 **              msg -- the text of the message.
3539 **              args -- arguments to the message.
3540 **
3541 **      Returns:
3542 **              none.
3543 */
3544
3545 void
3546 #ifdef __STDC__
3547 auth_warning(register ENVELOPE *e, const char *msg, ...)
3548 #else /* __STDC__ */
3549 auth_warning(e, msg, va_alist)
3550         register ENVELOPE *e;
3551         const char *msg;
3552         va_dcl
3553 #endif /* __STDC__ */
3554 {
3555         char buf[MAXLINE];
3556         SM_VA_LOCAL_DECL
3557
3558         if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
3559         {
3560                 register char *p;
3561                 static char hostbuf[48];
3562
3563                 if (hostbuf[0] == '\0')
3564                 {
3565                         struct hostent *hp;
3566
3567                         hp = myhostname(hostbuf, sizeof(hostbuf));
3568 #if NETINET6
3569                         if (hp != NULL)
3570                         {
3571                                 freehostent(hp);
3572                                 hp = NULL;
3573                         }
3574 #endif /* NETINET6 */
3575                 }
3576
3577                 (void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
3578                 p = &buf[strlen(buf)];
3579                 SM_VA_START(ap, msg);
3580                 (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
3581                 SM_VA_END(ap);
3582                 addheader("X-Authentication-Warning", buf, 0, e, true);
3583                 if (LogLevel > 3)
3584                         sm_syslog(LOG_INFO, e->e_id,
3585                                   "Authentication-Warning: %.400s",
3586                                   buf);
3587         }
3588 }
3589 /*
3590 **  GETEXTENV -- get from external environment
3591 **
3592 **      Parameters:
3593 **              envar -- the name of the variable to retrieve
3594 **
3595 **      Returns:
3596 **              The value, if any.
3597 */
3598
3599 static char *
3600 getextenv(envar)
3601         const char *envar;
3602 {
3603         char **envp;
3604         int l;
3605
3606         l = strlen(envar);
3607         for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
3608         {
3609                 if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
3610                         return &(*envp)[l + 1];
3611         }
3612         return NULL;
3613 }
3614 /*
3615 **  SM_SETUSERENV -- set an environment variable in the propagated environment
3616 **
3617 **      Parameters:
3618 **              envar -- the name of the environment variable.
3619 **              value -- the value to which it should be set.  If
3620 **                      null, this is extracted from the incoming
3621 **                      environment.  If that is not set, the call
3622 **                      to sm_setuserenv is ignored.
3623 **
3624 **      Returns:
3625 **              none.
3626 */
3627
3628 void
3629 sm_setuserenv(envar, value)
3630         const char *envar;
3631         const char *value;
3632 {
3633         int i, l;
3634         char **evp = UserEnviron;
3635         char *p;
3636
3637         if (value == NULL)
3638         {
3639                 value = getextenv(envar);
3640                 if (value == NULL)
3641                         return;
3642         }
3643
3644         /* XXX enforce reasonable size? */
3645         i = strlen(envar) + 1;
3646         l = strlen(value) + i + 1;
3647         p = (char *) xalloc(l);
3648         (void) sm_strlcpyn(p, l, 3, envar, "=", value);
3649
3650         while (*evp != NULL && strncmp(*evp, p, i) != 0)
3651                 evp++;
3652         if (*evp != NULL)
3653         {
3654                 *evp++ = p;
3655         }
3656         else if (evp < &UserEnviron[MAXUSERENVIRON])
3657         {
3658                 *evp++ = p;
3659                 *evp = NULL;
3660         }
3661
3662         /* make sure it is in our environment as well */
3663         if (putenv(p) < 0)
3664                 syserr("sm_setuserenv: putenv(%s) failed", p);
3665 }
3666 /*
3667 **  DUMPSTATE -- dump state
3668 **
3669 **      For debugging.
3670 */
3671
3672 void
3673 dumpstate(when)
3674         char *when;
3675 {
3676         register char *j = macvalue('j', CurEnv);
3677         int rs;
3678         extern int NextMacroId;
3679
3680         sm_syslog(LOG_DEBUG, CurEnv->e_id,
3681                   "--- dumping state on %s: $j = %s ---",
3682                   when,
3683                   j == NULL ? "<NULL>" : j);
3684         if (j != NULL)
3685         {
3686                 if (!wordinclass(j, 'w'))
3687                         sm_syslog(LOG_DEBUG, CurEnv->e_id,
3688                                   "*** $j not in $=w ***");
3689         }
3690         sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
3691         sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
3692                   NextMacroId, MAXMACROID);
3693         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
3694         printopenfds(true);
3695         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
3696         mci_dump_all(smioout, true);
3697         rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
3698         if (rs > 0)
3699         {
3700                 int status;
3701                 register char **pvp;
3702                 char *pv[MAXATOM + 1];
3703
3704                 pv[0] = NULL;
3705                 status = REWRITE(pv, rs, CurEnv);
3706                 sm_syslog(LOG_DEBUG, CurEnv->e_id,
3707                           "--- ruleset debug_dumpstate returns stat %d, pv: ---",
3708                           status);
3709                 for (pvp = pv; *pvp != NULL; pvp++)
3710                         sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
3711         }
3712         sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
3713 }
3714
3715 #ifdef SIGUSR1
3716 /*
3717 **  SIGUSR1 -- Signal a request to dump state.
3718 **
3719 **      Parameters:
3720 **              sig -- calling signal.
3721 **
3722 **      Returns:
3723 **              none.
3724 **
3725 **      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3726 **              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3727 **              DOING.
3728 **
3729 **              XXX: More work is needed for this signal handler.
3730 */
3731
3732 /* ARGSUSED */
3733 static SIGFUNC_DECL
3734 sigusr1(sig)
3735         int sig;
3736 {
3737         int save_errno = errno;
3738
3739         FIX_SYSV_SIGNAL(sig, sigusr1);
3740         errno = save_errno;
3741         CHECK_CRITICAL(sig);
3742         dumpstate("user signal");
3743 # if SM_HEAP_CHECK
3744         dumpstab();
3745 # endif
3746         errno = save_errno;
3747         return SIGFUNC_RETURN;
3748 }
3749 #endif /* SIGUSR1 */
3750
3751 /*
3752 **  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
3753 **
3754 **      Parameters:
3755 **              to_real_uid -- if set, drop to the real uid instead
3756 **                      of the RunAsUser.
3757 **
3758 **      Returns:
3759 **              EX_OSERR if the setuid failed.
3760 **              EX_OK otherwise.
3761 */
3762
3763 int
3764 drop_privileges(to_real_uid)
3765         bool to_real_uid;
3766 {
3767         int rval = EX_OK;
3768         GIDSET_T emptygidset[1];
3769
3770         if (tTd(47, 1))
3771                 sm_dprintf("drop_privileges(%d): Real[UG]id=%ld:%ld, get[ug]id=%ld:%ld, gete[ug]id=%ld:%ld, RunAs[UG]id=%ld:%ld\n",
3772                            (int) to_real_uid,
3773                            (long) RealUid, (long) RealGid,
3774                            (long) getuid(), (long) getgid(),
3775                            (long) geteuid(), (long) getegid(),
3776                            (long) RunAsUid, (long) RunAsGid);
3777
3778         if (to_real_uid)
3779         {
3780                 RunAsUserName = RealUserName;
3781                 RunAsUid = RealUid;
3782                 RunAsGid = RealGid;
3783                 EffGid = RunAsGid;
3784         }
3785
3786         /* make sure no one can grab open descriptors for secret files */
3787         endpwent();
3788         sm_mbdb_terminate();
3789
3790         /* reset group permissions; these can be set later */
3791         emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
3792
3793         /*
3794         **  Notice:  on some OS (Linux...) the setgroups() call causes
3795         **      a logfile entry if sendmail is not run by root.
3796         **      However, it is unclear (no POSIX standard) whether
3797         **      setgroups() can only succeed if executed by root.
3798         **      So for now we keep it as it is; if you want to change it, use
3799         **  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
3800         */
3801
3802         if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
3803         {
3804                 syserr("drop_privileges: setgroups(1, %d) failed",
3805                        (int) emptygidset[0]);
3806                 rval = EX_OSERR;
3807         }
3808
3809         /* reset primary group id */
3810         if (to_real_uid)
3811         {
3812                 /*
3813                 **  Drop gid to real gid.
3814                 **  On some OS we must reset the effective[/real[/saved]] gid,
3815                 **  and then use setgid() to finally drop all group privileges.
3816                 **  Later on we check whether we can get back the
3817                 **  effective gid.
3818                 */
3819
3820 #if HASSETEGID
3821                 if (setegid(RunAsGid) < 0)
3822                 {
3823                         syserr("drop_privileges: setegid(%d) failed",
3824                                (int) RunAsGid);
3825                         rval = EX_OSERR;
3826                 }
3827 #else /* HASSETEGID */
3828 # if HASSETREGID
3829                 if (setregid(RunAsGid, RunAsGid) < 0)
3830                 {
3831                         syserr("drop_privileges: setregid(%d, %d) failed",
3832                                (int) RunAsGid, (int) RunAsGid);
3833                         rval = EX_OSERR;
3834                 }
3835 # else /* HASSETREGID */
3836 #  if HASSETRESGID
3837                 if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
3838                 {
3839                         syserr("drop_privileges: setresgid(%d, %d, %d) failed",
3840                                (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
3841                         rval = EX_OSERR;
3842                 }
3843 #  endif /* HASSETRESGID */
3844 # endif /* HASSETREGID */
3845 #endif /* HASSETEGID */
3846         }
3847         if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
3848         {
3849                 if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
3850                 {
3851                         syserr("drop_privileges: setgid(%ld) failed",
3852                                (long) RunAsGid);
3853                         rval = EX_OSERR;
3854                 }
3855                 errno = 0;
3856                 if (rval == EX_OK && getegid() != RunAsGid)
3857                 {
3858                         syserr("drop_privileges: Unable to set effective gid=%ld to RunAsGid=%ld",
3859                                (long) getegid(), (long) RunAsGid);
3860                         rval = EX_OSERR;
3861                 }
3862         }
3863
3864         /* fiddle with uid */
3865         if (to_real_uid || RunAsUid != 0)
3866         {
3867                 uid_t euid;
3868
3869                 /*
3870                 **  Try to setuid(RunAsUid).
3871                 **  euid must be RunAsUid,
3872                 **  ruid must be RunAsUid unless (e|r)uid wasn't 0
3873                 **      and we didn't have to drop privileges to the real uid.
3874                 */
3875
3876                 if (setuid(RunAsUid) < 0 ||
3877                     geteuid() != RunAsUid ||
3878                     (getuid() != RunAsUid &&
3879                      (to_real_uid || geteuid() == 0 || getuid() == 0)))
3880                 {
3881 #if HASSETREUID
3882                         /*
3883                         **  if ruid != RunAsUid, euid == RunAsUid, then
3884                         **  try resetting just the real uid, then using
3885                         **  setuid() to drop the saved-uid as well.
3886                         */
3887
3888                         if (geteuid() == RunAsUid)
3889                         {
3890                                 if (setreuid(RunAsUid, -1) < 0)
3891                                 {
3892                                         syserr("drop_privileges: setreuid(%d, -1) failed",
3893                                                (int) RunAsUid);
3894                                         rval = EX_OSERR;
3895                                 }
3896                                 if (setuid(RunAsUid) < 0)
3897                                 {
3898                                         syserr("drop_privileges: second setuid(%d) attempt failed",
3899                                                (int) RunAsUid);
3900                                         rval = EX_OSERR;
3901                                 }
3902                         }
3903                         else
3904 #endif /* HASSETREUID */
3905                         {
3906                                 syserr("drop_privileges: setuid(%d) failed",
3907                                        (int) RunAsUid);
3908                                 rval = EX_OSERR;
3909                         }
3910                 }
3911                 euid = geteuid();
3912                 if (RunAsUid != 0 && setuid(0) == 0)
3913                 {
3914                         /*
3915                         **  Believe it or not, the Linux capability model
3916                         **  allows a non-root process to override setuid()
3917                         **  on a process running as root and prevent that
3918                         **  process from dropping privileges.
3919                         */
3920
3921                         syserr("drop_privileges: setuid(0) succeeded (when it should not)");
3922                         rval = EX_OSERR;
3923                 }
3924                 else if (RunAsUid != euid && setuid(euid) == 0)
3925                 {
3926                         /*
3927                         **  Some operating systems will keep the saved-uid
3928                         **  if a non-root effective-uid calls setuid(real-uid)
3929                         **  making it possible to set it back again later.
3930                         */
3931
3932                         syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
3933                         rval = EX_OSERR;
3934                 }
3935         }
3936
3937         if ((to_real_uid || RunAsGid != 0) &&
3938             rval == EX_OK && RunAsGid != EffGid &&
3939             getuid() != 0 && geteuid() != 0)
3940         {
3941                 errno = 0;
3942                 if (setgid(EffGid) == 0)
3943                 {
3944                         syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
3945                                (int) EffGid);
3946                         rval = EX_OSERR;
3947                 }
3948         }
3949
3950         if (tTd(47, 5))
3951         {
3952                 sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
3953                            (int) geteuid(), (int) getuid(),
3954                            (int) getegid(), (int) getgid());
3955                 sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
3956                            (int) RunAsUid, (int) RunAsGid);
3957                 if (tTd(47, 10))
3958                         sm_dprintf("drop_privileges: rval = %d\n", rval);
3959         }
3960         return rval;
3961 }
3962 /*
3963 **  FILL_FD -- make sure a file descriptor has been properly allocated
3964 **
3965 **      Used to make sure that stdin/out/err are allocated on startup
3966 **
3967 **      Parameters:
3968 **              fd -- the file descriptor to be filled.
3969 **              where -- a string used for logging.  If NULL, this is
3970 **                      being called on startup, and logging should
3971 **                      not be done.
3972 **
3973 **      Returns:
3974 **              none
3975 **
3976 **      Side Effects:
3977 **              possibly changes MissingFds
3978 */
3979
3980 void
3981 fill_fd(fd, where)
3982         int fd;
3983         char *where;
3984 {
3985         int i;
3986         struct stat stbuf;
3987
3988         if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
3989                 return;
3990
3991         if (where != NULL)
3992                 syserr("fill_fd: %s: fd %d not open", where, fd);
3993         else
3994                 MissingFds |= 1 << fd;
3995         i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
3996         if (i < 0)
3997         {
3998                 syserr("!fill_fd: %s: cannot open %s",
3999                        where == NULL ? "startup" : where, SM_PATH_DEVNULL);
4000         }
4001         if (fd != i)
4002         {
4003                 (void) dup2(i, fd);
4004                 (void) close(i);
4005         }
4006 }
4007 /*
4008 **  SM_PRINTOPTIONS -- print options
4009 **
4010 **      Parameters:
4011 **              options -- array of options.
4012 **
4013 **      Returns:
4014 **              none.
4015 */
4016
4017 static void
4018 sm_printoptions(options)
4019         char **options;
4020 {
4021         int ll;
4022         char **av;
4023
4024         av = options;
4025         ll = 7;
4026         while (*av != NULL)
4027         {
4028                 if (ll + strlen(*av) > 63)
4029                 {
4030                         sm_dprintf("\n");
4031                         ll = 0;
4032                 }
4033                 if (ll == 0)
4034                         sm_dprintf("\t\t");
4035                 else
4036                         sm_dprintf(" ");
4037                 sm_dprintf("%s", *av);
4038                 ll += strlen(*av++) + 1;
4039         }
4040         sm_dprintf("\n");
4041 }
4042
4043 /*
4044 **  TO8BIT -- convert \octal sequences in a test mode input line
4045 **
4046 **      Parameters:
4047 **              str -- the input line.
4048 **
4049 **      Returns:
4050 **              none.
4051 **
4052 **      Side Effects:
4053 **              replaces \0octal in str with octal value.
4054 */
4055
4056 static bool to8bit __P((char *));
4057
4058 static bool
4059 to8bit(str)
4060         char *str;
4061 {
4062         int c, len;
4063         char *out, *in;
4064         bool changed;
4065
4066         if (str == NULL)
4067                 return false;
4068         in = out = str;
4069         changed = false;
4070         len = 0;
4071         while ((c = (*str++ & 0377)) != '\0')
4072         {
4073                 int oct, nxtc;
4074
4075                 ++len;
4076                 if (c == '\\' &&
4077                     (nxtc = (*str & 0377)) == '0')
4078                 {
4079                         oct = 0;
4080                         while ((nxtc = (*str & 0377)) != '\0' &&
4081                                 isascii(nxtc) && isdigit(nxtc))
4082                         {
4083                                 oct <<= 3;
4084                                 oct += nxtc - '0';
4085                                 ++str;
4086                                 ++len;
4087                         }
4088                         changed = true;
4089                         c = oct;
4090                 }
4091                 *out++ = c;
4092         }
4093         *out++ = c;
4094         if (changed)
4095         {
4096                 char *q;
4097
4098                 q = quote_internal_chars(in, in, &len);
4099                 if (q != in)
4100                         sm_strlcpy(in, q, len);
4101         }
4102         return changed;
4103 }
4104
4105 /*
4106 **  TESTMODELINE -- process a test mode input line
4107 **
4108 **      Parameters:
4109 **              line -- the input line.
4110 **              e -- the current environment.
4111 **      Syntax:
4112 **              #  a comment
4113 **              .X process X as a configuration line
4114 **              =X dump a configuration item (such as mailers)
4115 **              $X dump a macro or class
4116 **              /X try an activity
4117 **              X  normal process through rule set X
4118 */
4119
4120 static void
4121 testmodeline(line, e)
4122         char *line;
4123         ENVELOPE *e;
4124 {
4125         register char *p;
4126         char *q;
4127         auto char *delimptr;
4128         int mid;
4129         int i, rs;
4130         STAB *map;
4131         char **s;
4132         struct rewrite *rw;
4133         ADDRESS a;
4134         char *lbp;
4135         auto int lbs;
4136         static int tryflags = RF_COPYNONE;
4137         char exbuf[MAXLINE];
4138         char lbuf[MAXLINE];
4139         extern unsigned char TokTypeNoC[];
4140         bool eightbit;
4141
4142         /* skip leading spaces */
4143         while (*line == ' ')
4144                 line++;
4145
4146         lbp = NULL;
4147         eightbit = false;
4148         switch (line[0])
4149         {
4150           case '#':
4151           case '\0':
4152                 return;
4153
4154           case '?':
4155                 help("-bt", e);
4156                 return;
4157
4158           case '.':             /* config-style settings */
4159                 switch (line[1])
4160                 {
4161                   case 'D':
4162                         mid = macid_parse(&line[2], &delimptr);
4163                         if (mid == 0)
4164                                 return;
4165                         lbs = sizeof(lbuf);
4166                         lbp = translate_dollars(delimptr, lbuf, &lbs);
4167                         macdefine(&e->e_macro, A_TEMP, mid, lbp);
4168                         if (lbp != lbuf)
4169                                 SM_FREE(lbp);
4170                         break;
4171
4172                   case 'C':
4173                         if (line[2] == '\0')    /* not to call syserr() */
4174                                 return;
4175
4176                         mid = macid_parse(&line[2], &delimptr);
4177                         if (mid == 0)
4178                                 return;
4179                         lbs = sizeof(lbuf);
4180                         lbp = translate_dollars(delimptr, lbuf, &lbs);
4181                         expand(lbp, exbuf, sizeof(exbuf), e);
4182                         if (lbp != lbuf)
4183                                 SM_FREE(lbp);
4184                         p = exbuf;
4185                         while (*p != '\0')
4186                         {
4187                                 register char *wd;
4188                                 char delim;
4189
4190                                 while (*p != '\0' && SM_ISSPACE(*p))
4191                                         p++;
4192                                 wd = p;
4193                                 while (*p != '\0' && !(SM_ISSPACE(*p)))
4194                                         p++;
4195                                 delim = *p;
4196                                 *p = '\0';
4197                                 if (wd[0] != '\0')
4198                                         setclass(mid, wd);
4199                                 *p = delim;
4200                         }
4201                         break;
4202
4203                   case '\0':
4204                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4205                                              "Usage: .[DC]macro value(s)\n");
4206                         break;
4207
4208                   default:
4209                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4210                                              "Unknown \".\" command %s\n", line);
4211                         break;
4212                 }
4213                 return;
4214
4215           case '=':             /* config-style settings */
4216                 switch (line[1])
4217                 {
4218                   case 'S':             /* dump rule set */
4219                         rs = strtorwset(&line[2], NULL, ST_FIND);
4220                         if (rs < 0)
4221                         {
4222                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4223                                                      "Undefined ruleset %s\n", &line[2]);
4224                                 return;
4225                         }
4226                         rw = RewriteRules[rs];
4227                         if (rw == NULL)
4228                                 return;
4229                         do
4230                         {
4231                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
4232                                                   'R');
4233                                 s = rw->r_lhs;
4234                                 while (*s != NULL)
4235                                 {
4236                                         xputs(smioout, *s++);
4237                                         (void) sm_io_putc(smioout,
4238                                                           SM_TIME_DEFAULT, ' ');
4239                                 }
4240                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
4241                                                   '\t');
4242                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
4243                                                   '\t');
4244                                 s = rw->r_rhs;
4245                                 while (*s != NULL)
4246                                 {
4247                                         xputs(smioout, *s++);
4248                                         (void) sm_io_putc(smioout,
4249                                                           SM_TIME_DEFAULT, ' ');
4250                                 }
4251                                 (void) sm_io_putc(smioout, SM_TIME_DEFAULT,
4252                                                   '\n');
4253                         } while ((rw = rw->r_next) != NULL);
4254                         break;
4255
4256                   case 'M':
4257                         for (i = 0; i < MAXMAILERS; i++)
4258                         {
4259                                 if (Mailer[i] != NULL)
4260                                         printmailer(smioout, Mailer[i]);
4261                         }
4262                         break;
4263
4264                   case '\0':
4265                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4266                                              "Usage: =Sruleset or =M\n");
4267                         break;
4268
4269                   default:
4270                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4271                                              "Unknown \"=\" command %s\n", line);
4272                         break;
4273                 }
4274                 return;
4275
4276           case '-':             /* set command-line-like opts */
4277                 switch (line[1])
4278                 {
4279                   case 'd':
4280                         tTflag(&line[2]);
4281                         break;
4282
4283                   case '\0':
4284                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4285                                              "Usage: -d{debug arguments}\n");
4286                         break;
4287
4288                   default:
4289                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4290                                              "Unknown \"-\" command %s\n", line);
4291                         break;
4292                 }
4293                 return;
4294
4295           case '$':
4296                 if (line[1] == '=')
4297                 {
4298                         mid = macid(&line[2]);
4299                         if (mid != 0)
4300                                 stabapply(dump_class, mid);
4301                         return;
4302                 }
4303                 mid = macid(&line[1]);
4304                 if (mid == 0)
4305                         return;
4306                 p = macvalue(mid, e);
4307                 if (p == NULL)
4308                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4309                                              "Undefined\n");
4310                 else
4311                 {
4312                         xputs(smioout, p);
4313                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4314                                              "\n");
4315                 }
4316                 return;
4317
4318           case '/':             /* miscellaneous commands */
4319                 p = &line[strlen(line)];
4320                 while (--p >= line && SM_ISSPACE(*p))
4321                         *p = '\0';
4322                 p = strpbrk(line, " \t");
4323                 if (p != NULL)
4324                 {
4325                         while (SM_ISSPACE(*p))
4326                                 *p++ = '\0';
4327                 }
4328                 else
4329                         p = "";
4330                 if (line[1] == '\0')
4331                 {
4332                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4333                                              "Usage: /[canon|map|mx|parse|try|tryflags]\n");
4334                         return;
4335                 }
4336                 if (sm_strcasecmp(&line[1], "quit") == 0)
4337                 {
4338                         CurEnv->e_id = NULL;
4339                         finis(true, true, ExitStat);
4340                         /* NOTREACHED */
4341                 }
4342                 if (sm_strcasecmp(&line[1], "mx") == 0)
4343                 {
4344 #if NAMED_BIND
4345                         /* look up MX records */
4346                         int nmx;
4347                         auto int rcode;
4348                         char *mxhosts[MAXMXHOSTS + 1];
4349
4350                         if (*p == '\0')
4351                         {
4352                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4353                                                      "Usage: /mx address\n");
4354                                 return;
4355                         }
4356                         nmx = getmxrr(p, mxhosts, NULL, TRYFALLBACK, &rcode,
4357                                       NULL, -1);
4358                         if (nmx == NULLMX)
4359                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4360                                                      "getmxrr(%s) returns null MX (See RFC7505)\n",
4361                                                      p);
4362                         else
4363                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4364                                                      "getmxrr(%s) returns %d value(s):\n",
4365                                                      p, nmx);
4366                         for (i = 0; i < nmx; i++)
4367                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4368                                                      "\t%s\n", mxhosts[i]);
4369 #else /* NAMED_BIND */
4370                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4371                                              "No MX code compiled in\n");
4372 #endif /* NAMED_BIND */
4373                 }
4374                 else if (sm_strcasecmp(&line[1], "canon") == 0)
4375                 {
4376                         char host[MAXHOSTNAMELEN];
4377
4378                         if (*p == '\0')
4379                         {
4380                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4381                                                      "Usage: /canon address\n");
4382                                 return;
4383                         }
4384                         else if (sm_strlcpy(host, p, sizeof(host)) >= sizeof(host))
4385                         {
4386                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4387                                                      "Name too long\n");
4388                                 return;
4389                         }
4390                         (void) getcanonname(host, sizeof(host), !HasWildcardMX,
4391                                             NULL);
4392                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4393                                              "getcanonname(%s) returns %s\n",
4394                                              p, host);
4395                 }
4396                 else if (sm_strcasecmp(&line[1], "map") == 0)
4397                 {
4398                         auto int rcode = EX_OK;
4399                         char *av[2];
4400
4401                         if (*p == '\0')
4402                         {
4403                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4404                                                      "Usage: /map mapname key\n");
4405                                 return;
4406                         }
4407                         for (q = p; *q != '\0' && !(SM_ISSPACE(*q)); q++)
4408                                 continue;
4409                         if (*q == '\0')
4410                         {
4411                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4412                                                      "No key specified\n");
4413                                 return;
4414                         }
4415                         *q++ = '\0';
4416                         map = stab(p, ST_MAP, ST_FIND);
4417                         if (map == NULL)
4418                         {
4419                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4420                                                      "Map named \"%s\" not found\n", p);
4421                                 return;
4422                         }
4423                         if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
4424                             !openmap(&(map->s_map)))
4425                         {
4426                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4427                                                      "Map named \"%s\" not open\n", p);
4428                                 return;
4429                         }
4430                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4431                                              "map_lookup: %s (%s) ", p, q);
4432                         av[0] = q;
4433                         av[1] = NULL;
4434                         p = (*map->s_map.map_class->map_lookup)
4435                                         (&map->s_map, q, av, &rcode);
4436                         if (p == NULL)
4437                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4438                                                      "no match (%d)\n",
4439                                                      rcode);
4440                         else
4441                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4442                                                      "returns %s (%d)\n", p,
4443                                                      rcode);
4444                 }
4445                 else if (sm_strcasecmp(&line[1], "try") == 0)
4446                 {
4447                         MAILER *m;
4448                         STAB *st;
4449                         auto int rcode = EX_OK;
4450
4451                         q = strpbrk(p, " \t");
4452                         if (q != NULL)
4453                         {
4454                                 while (SM_ISSPACE(*q))
4455                                         *q++ = '\0';
4456                         }
4457                         if (q == NULL || *q == '\0')
4458                         {
4459                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4460                                                      "Usage: /try mailer address\n");
4461                                 return;
4462                         }
4463                         st = stab(p, ST_MAILER, ST_FIND);
4464                         if (st == NULL)
4465                         {
4466                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4467                                                      "Unknown mailer %s\n", p);
4468                                 return;
4469                         }
4470                         m = st->s_mailer;
4471                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4472                                              "Trying %s %s address %s for mailer %s\n",
4473                                      bitset(RF_HEADERADDR, tryflags) ? "header"
4474                                                         : "envelope",
4475                                      bitset(RF_SENDERADDR, tryflags) ? "sender"
4476                                                         : "recipient", q, p);
4477                         p = remotename(q, m, tryflags, &rcode, CurEnv);
4478                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4479                                              "Rcode = %d, addr = %s\n",
4480                                              rcode, p == NULL ? "<NULL>" : p);
4481                         e->e_to = NULL;
4482                 }
4483                 else if (sm_strcasecmp(&line[1], "tryflags") == 0)
4484                 {
4485                         if (*p == '\0')
4486                         {
4487                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4488                                                      "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
4489                                 return;
4490                         }
4491                         for (; *p != '\0'; p++)
4492                         {
4493                                 switch (*p)
4494                                 {
4495                                   case 'H':
4496                                   case 'h':
4497                                         tryflags |= RF_HEADERADDR;
4498                                         break;
4499
4500                                   case 'E':
4501                                   case 'e':
4502                                         tryflags &= ~RF_HEADERADDR;
4503                                         break;
4504
4505                                   case 'S':
4506                                   case 's':
4507                                         tryflags |= RF_SENDERADDR;
4508                                         break;
4509
4510                                   case 'R':
4511                                   case 'r':
4512                                         tryflags &= ~RF_SENDERADDR;
4513                                         break;
4514                                 }
4515                         }
4516                         exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
4517                         exbuf[1] = ' ';
4518                         exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
4519                         exbuf[3] = '\0';
4520                         macdefine(&e->e_macro, A_TEMP,
4521                                 macid("{addr_type}"), exbuf);
4522                 }
4523                 else if (sm_strcasecmp(&line[1], "parse") == 0)
4524                 {
4525                         if (*p == '\0')
4526                         {
4527                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4528                                                      "Usage: /parse address\n");
4529                                 return;
4530                         }
4531                         q = crackaddr(p, e);
4532                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4533                                              "Cracked address = ");
4534                         xputs(smioout, q);
4535                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4536                                              "\nParsing %s %s address\n",
4537                                              bitset(RF_HEADERADDR, tryflags) ?
4538                                                         "header" : "envelope",
4539                                              bitset(RF_SENDERADDR, tryflags) ?
4540                                                         "sender" : "recipient");
4541                         if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
4542                             == NULL)
4543                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4544                                                      "Cannot parse\n");
4545                         else if (a.q_host != NULL && a.q_host[0] != '\0')
4546                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4547                                                      "mailer %s, host %s, user %s\n",
4548                                                      a.q_mailer->m_name,
4549                                                      a.q_host,
4550                                                      a.q_user);
4551                         else
4552                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4553                                                      "mailer %s, user %s\n",
4554                                                      a.q_mailer->m_name,
4555                                                      a.q_user);
4556                         e->e_to = NULL;
4557                 }
4558                 else if (sm_strcasecmp(&line[1], "header") == 0)
4559                 {
4560                         unsigned long ul;
4561
4562                         ul = chompheader(p, CHHDR_CHECK|CHHDR_USER, NULL, e);
4563                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4564                                              "ul = %lu\n", ul);
4565                 }
4566 #if NETINET || NETINET6
4567                 else if (sm_strcasecmp(&line[1], "gethostbyname") == 0)
4568                 {
4569                         int family = AF_INET;
4570
4571                         q = strpbrk(p, " \t");
4572                         if (q != NULL)
4573                         {
4574                                 while (SM_ISSPACE(*q))
4575                                         *q++ = '\0';
4576 # if NETINET6
4577                                 if (*q != '\0' && (strcmp(q, "inet6") == 0 ||
4578                                                    strcmp(q, "AAAA") == 0))
4579                                         family = AF_INET6;
4580 # endif /* NETINET6 */
4581                         }
4582                         (void) sm_gethostbyname(p, family);
4583                 }
4584 #endif /* NETINET || NETINET6 */
4585 #if DANE
4586                 else if (sm_strcasecmp(&line[1], "dnslookup") == 0)
4587                 {
4588                         DNS_REPLY_T *r;
4589                         int rr_type, family;
4590                         unsigned int flags;
4591
4592                         rr_type = T_A;
4593                         family = AF_INET;
4594                         flags = RR_AS_TEXT;
4595                         q = strpbrk(p, " \t");
4596                         if (q != NULL)
4597                         {
4598                                 char *pflags;
4599
4600                                 while (SM_ISSPACE(*q))
4601                                         *q++ = '\0';
4602                                 pflags = strpbrk(q, " \t");
4603                                 if (pflags != NULL)
4604                                 {
4605                                         while (SM_ISSPACE(*pflags))
4606                                                 *pflags++ = '\0';
4607                                 }
4608                                 rr_type = dns_string_to_type(q);
4609                                 if (rr_type == T_A)
4610                                         family = AF_INET;
4611 # if NETINET6
4612                                 if (rr_type == T_AAAA)
4613                                         family = AF_INET6;
4614 # endif
4615                                 while (pflags != NULL && *pflags != '\0' &&
4616                                         !SM_ISSPACE(*pflags))
4617                                 {
4618                                         if (*pflags == 'c')
4619                                                 flags |= RR_NO_CNAME;
4620                                         else if (*pflags == 'o')
4621                                                 flags |= RR_ONLY_CNAME;
4622                                         else if (*pflags == 'T')
4623                                                 flags &= ~RR_AS_TEXT;
4624                                         ++pflags;
4625                                 }
4626                         }
4627                         r = dns_lookup_int(p, C_IN, rr_type,
4628                                         0, 0, 0, flags, NULL, NULL);
4629                         if (r != NULL && family >= 0)
4630                         {
4631                                 (void) dns2he(r, family);
4632                                 dns_free_data(r);
4633                                 r = NULL;
4634                         }
4635                 }
4636 #endif /* DANE */
4637                 else
4638                 {
4639                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4640                                              "Unknown \"/\" command %s\n",
4641                                              line);
4642                 }
4643                 (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
4644                 return;
4645         }
4646
4647         for (p = line; SM_ISSPACE(*p); p++)
4648                 continue;
4649         q = p;
4650         while (*p != '\0' && !(SM_ISSPACE(*p)))
4651                 p++;
4652         if (*p == '\0')
4653         {
4654                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4655                                      "No address!\n");
4656                 return;
4657         }
4658         *p = '\0';
4659         if (tTd(23, 101))
4660                 eightbit = to8bit(p + 1);
4661         if (invalidaddr(p + 1, NULL, true))
4662                 return;
4663         do
4664         {
4665                 register char **pvp;
4666                 char pvpbuf[PSBUFSIZE];
4667
4668                 pvp = prescan(++p, ',', pvpbuf, sizeof(pvpbuf), &delimptr,
4669                               ConfigLevel >= 9 ? TokTypeNoC : ExtTokenTab, false);
4670                 if (pvp == NULL)
4671                         continue;
4672                 p = q;
4673                 while (*p != '\0')
4674                 {
4675                         int status;
4676
4677                         rs = strtorwset(p, NULL, ST_FIND);
4678                         if (rs < 0)
4679                         {
4680                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4681                                                      "Undefined ruleset %s\n",
4682                                                      p);
4683                                 break;
4684                         }
4685                         status = REWRITE(pvp, rs, e);
4686                         if (status != EX_OK)
4687                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4688                                                      "== Ruleset %s (%d) status %d\n",
4689                                                      p, rs, status);
4690                         else if (eightbit)
4691                         {
4692                                 cataddr(pvp, NULL, exbuf, sizeof(exbuf), '\0',
4693                                         true);
4694                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4695                                                      "cataddr: %s\n",
4696                                                      str2prt(exbuf));
4697                         }
4698                         while (*p != '\0' && *p++ != ',')
4699                                 continue;
4700                 }
4701         } while (*(p = delimptr) != '\0');
4702         (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
4703 }
4704
4705 static void
4706 dump_class(s, id)
4707         register STAB *s;
4708         int id;
4709 {
4710         if (s->s_symtype != ST_CLASS)
4711                 return;
4712         if (bitnset(bitidx(id), s->s_class))
4713                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4714                                      "%s\n", s->s_name);
4715 }
4716
4717 /*
4718 **  An exception type used to create QuickAbort exceptions.
4719 **  This is my first cut at converting QuickAbort from longjmp to exceptions.
4720 **  These exceptions have a single integer argument, which is the argument
4721 **  to longjmp in the original code (either 1 or 2).  I don't know the
4722 **  significance of 1 vs 2: the calls to setjmp don't care.
4723 */
4724
4725 const SM_EXC_TYPE_T EtypeQuickAbort =
4726 {
4727         SmExcTypeMagic,
4728         "E:mta.quickabort",
4729         "i",
4730         sm_etype_printf,
4731         "quick abort %0",
4732 };