source: trunk/third/sendmail/src/main.c @ 12554

Revision 12554, 59.2 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char copyright[] =
15"@(#) Copyright (c) 1998 Sendmail, Inc.  All rights reserved.\n\
16     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
17     Copyright (c) 1988, 1993\n\
18        The Regents of the University of California.  All rights reserved.\n";
19#endif /* not lint */
20
21#ifndef lint
22static char sccsid[] = "@(#)main.c      8.322 (Berkeley) 12/18/1998";
23#endif /* not lint */
24
25#define _DEFINE
26
27#include "sendmail.h"
28#include <arpa/inet.h>
29#include <grp.h>
30#if NAMED_BIND
31#include <resolv.h>
32#endif
33
34/*
35**  SENDMAIL -- Post mail to a set of destinations.
36**
37**      This is the basic mail router.  All user mail programs should
38**      call this routine to actually deliver mail.  Sendmail in
39**      turn calls a bunch of mail servers that do the real work of
40**      delivering the mail.
41**
42**      Sendmail is driven by settings read in from /etc/sendmail.cf
43**      (read by readcf.c).
44**
45**      Usage:
46**              /usr/lib/sendmail [flags] addr ...
47**
48**              See the associated documentation for details.
49**
50**      Author:
51**              Eric Allman, UCB/INGRES (until 10/81).
52**                           Britton-Lee, Inc., purveyors of fine
53**                              database computers (11/81 - 10/88).
54**                           International Computer Science Institute
55**                              (11/88 - 9/89).
56**                           UCB/Mammoth Project (10/89 - 7/95).
57**                           InReference, Inc. (8/95 - 1/97).
58**                           Sendmail, Inc. (1/98 - present).
59**              The support of the my employers is gratefully acknowledged.
60**                      Few of them (Britton-Lee in particular) have had
61**                      anything to gain from my involvement in this project.
62*/
63
64
65int             NextMailer;     /* "free" index into Mailer struct */
66char            *FullName;      /* sender's full name */
67ENVELOPE        BlankEnvelope;  /* a "blank" envelope */
68ENVELOPE        MainEnvelope;   /* the envelope around the basic letter */
69ADDRESS         NullAddress =   /* a null address */
70                { "", "", NULL, "" };
71char            *CommandLineArgs;       /* command line args for pid file */
72bool            Warn_Q_option = FALSE;  /* warn about Q option use */
73char            **SaveArgv;     /* argument vector for re-execing */
74int             MissingFds = 0; /* bit map of fds missing on startup */
75
76#ifdef NGROUPS_MAX
77GIDSET_T        InitialGidSet[NGROUPS_MAX];
78#endif
79
80static void     obsolete __P((char **));
81extern void     printmailer __P((MAILER *));
82extern void     tTflag __P((char *));
83
84#if DAEMON && !SMTP
85ERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
86#endif /* DAEMON && !SMTP */
87#if SMTP && !QUEUE
88ERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
89#endif /* DAEMON && !SMTP */
90
91#define MAXCONFIGLEVEL  8       /* highest config version level known */
92
93int
94main(argc, argv, envp)
95        int argc;
96        char **argv;
97        char **envp;
98{
99        register char *p;
100        char **av;
101        extern char Version[];
102        char *ep, *from;
103        STAB *st;
104        register int i;
105        int j;
106        bool queuemode = FALSE;         /* process queue requests */
107        bool safecf = TRUE;
108        bool warn_C_flag = FALSE;
109        char warn_f_flag = '\0';
110        bool run_in_foreground = FALSE; /* -bD mode */
111        static bool reenter = FALSE;
112        struct passwd *pw;
113        struct hostent *hp;
114        char *nullserver = NULL;
115        bool forged;
116        char jbuf[MAXHOSTNAMELEN];      /* holds MyHostName */
117        static char rnamebuf[MAXNAME];  /* holds RealUserName */
118        char *emptyenviron[1];
119        QUEUE_CHAR *new;
120        extern int DtableSize;
121        extern int optind;
122        extern int opterr;
123        extern char *optarg;
124        extern char **environ;
125        extern time_t convtime __P((char *, char));
126        extern SIGFUNC_DECL intsig __P((int));
127        extern struct hostent *myhostname __P((char *, int));
128        extern char *getauthinfo __P((int, bool *));
129        extern char *getcfname __P((void));
130        extern SIGFUNC_DECL sigusr1 __P((int));
131        extern SIGFUNC_DECL sighup __P((int));
132        extern SIGFUNC_DECL quiesce __P((int));
133        extern void initmacros __P((ENVELOPE *));
134        extern void init_md __P((int, char **));
135        extern int getdtsize __P((void));
136        extern void tTsetup __P((u_char *, int, char *));
137        extern void setdefaults __P((ENVELOPE *));
138        extern void initsetproctitle __P((int, char **, char **));
139        extern void init_vendor_macros __P((ENVELOPE *));
140        extern void load_if_names __P((void));
141        extern void vendor_pre_defaults __P((ENVELOPE *));
142        extern void vendor_post_defaults __P((ENVELOPE *));
143        extern void readcf __P((char *, bool, ENVELOPE *));
144        extern void printqueue __P((void));
145        extern void sendtoargv __P((char **, ENVELOPE *));
146        extern void resetlimits __P((void));
147#ifndef HASUNSETENV
148        extern void unsetenv __P((char *));
149#endif 
150
151        /*
152        **  Check to see if we reentered.
153        **      This would normally happen if e_putheader or e_putbody
154        **      were NULL when invoked.
155        */
156
157        if (reenter)
158        {
159                syserr("main: reentered!");
160                abort();
161        }
162        reenter = TRUE;
163
164        /* avoid null pointer dereferences */
165        TermEscape.te_rv_on = TermEscape.te_rv_off = "";
166
167        /* do machine-dependent initializations */
168        init_md(argc, argv);
169
170        /* in 4.4BSD, the table can be huge; impose a reasonable limit */
171        DtableSize = getdtsize();
172        if (DtableSize > 256)
173                DtableSize = 256;
174
175        /*
176        **  Be sure we have enough file descriptors.
177        **      But also be sure that 0, 1, & 2 are open.
178        */
179
180        fill_fd(STDIN_FILENO, NULL);
181        fill_fd(STDOUT_FILENO, NULL);
182        fill_fd(STDERR_FILENO, NULL);
183
184        i = DtableSize;
185        while (--i > 0)
186        {
187                if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
188                        (void) close(i);
189        }
190        errno = 0;
191
192#if LOG
193# ifdef LOG_MAIL
194        openlog("sendmail", LOG_PID, LOG_MAIL);
195# else
196        openlog("sendmail", LOG_PID);
197# endif
198#endif
199
200        if (MissingFds != 0)
201        {
202                char mbuf[MAXLINE];
203
204                mbuf[0] = '\0';
205                if (bitset(1 << STDIN_FILENO, MissingFds))
206                        strcat(mbuf, ", stdin");
207                if (bitset(1 << STDOUT_FILENO, MissingFds))
208                        strcat(mbuf, ", stdout");
209                if (bitset(1 << STDERR_FILENO, MissingFds))
210                        strcat(mbuf, ", stderr");
211                syserr("File descriptors missing on startup: %s", &mbuf[2]);
212        }
213
214        /* reset status from syserr() calls for missing file descriptors */
215        Errors = 0;
216        ExitStat = EX_OK;
217
218#if XDEBUG
219        checkfd012("after openlog");
220#endif
221
222        tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
223
224#ifdef NGROUPS_MAX
225        /* save initial group set for future checks */
226        i = getgroups(NGROUPS_MAX, InitialGidSet);
227        if (i == 0)
228                InitialGidSet[0] = (GID_T) -1;
229        while (i < NGROUPS_MAX)
230                InitialGidSet[i++] = InitialGidSet[0];
231#endif
232
233        /* drop group id privileges (RunAsUser not yet set) */
234        (void) drop_privileges(FALSE);
235
236#ifdef SIGUSR1
237        /* arrange to dump state on user-1 signal */
238        setsignal(SIGUSR1, sigusr1);
239#endif
240
241        /* initialize for setproctitle */
242        initsetproctitle(argc, argv, envp);
243
244        /* Handle any non-getoptable constructions. */
245        obsolete(argv);
246
247        /*
248        **  Do a quick prescan of the argument list.
249        */
250
251#if defined(__osf__) || defined(_AIX3)
252# define OPTIONS        "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
253#endif
254#if defined(sony_news)
255# define OPTIONS        "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
256#endif
257#ifndef OPTIONS
258# define OPTIONS        "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
259#endif
260        opterr = 0;
261        while ((j = getopt(argc, argv, OPTIONS)) != -1)
262        {
263                switch (j)
264                {
265                  case 'd':
266                        /* hack attack -- see if should use ANSI mode */
267                        if (strcmp(optarg, "ANSI") == 0)
268                        {
269                                TermEscape.te_rv_on = "\033[7m";
270                                TermEscape.te_rv_off = "\033[0m";
271                                break;
272                        }
273                        tTflag(optarg);
274                        setbuf(stdout, (char *) NULL);
275                        break;
276                }
277        }
278        opterr = 1;
279
280        /* set up the blank envelope */
281        BlankEnvelope.e_puthdr = putheader;
282        BlankEnvelope.e_putbody = putbody;
283        BlankEnvelope.e_xfp = NULL;
284        STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
285        CurEnv = &BlankEnvelope;
286        STRUCTCOPY(NullAddress, MainEnvelope.e_from);
287
288        /*
289        **  Set default values for variables.
290        **      These cannot be in initialized data space.
291        */
292
293        setdefaults(&BlankEnvelope);
294
295        RealUid = getuid();
296        RealGid = getgid();
297
298        pw = sm_getpwuid(RealUid);
299        if (pw != NULL)
300                (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
301        else
302                (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
303        RealUserName = rnamebuf;
304
305        if (tTd(0, 101))
306        {
307                printf("Version %s\n", Version);
308                finis(FALSE, EX_OK);
309        }
310
311        /*
312        **  if running non-setuid binary as non-root, pretend
313        **  we are the RunAsUid
314        */
315        if (RealUid != 0 && geteuid() == RealUid)
316        {
317                if (tTd(47, 1))
318                        printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
319                                (int)RealUid);
320                RunAsUid = RealUid;
321        }
322        else if (geteuid() != 0)
323                RunAsUid = geteuid();
324
325        if (RealUid != 0 && getegid() == RealGid)
326                RunAsGid = RealGid;
327
328        if (tTd(47, 5))
329        {
330                printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
331                        (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
332                printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
333        }
334
335        /* save command line arguments */
336        i = 0;
337        for (av = argv; *av != NULL; )
338                i += strlen(*av++) + 1;
339        SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
340        CommandLineArgs = xalloc(i);
341        p = CommandLineArgs;
342        for (av = argv, i = 0; *av != NULL; )
343        {
344                SaveArgv[i++] = newstr(*av);
345                if (av != argv)
346                        *p++ = ' ';
347                strcpy(p, *av++);
348                p += strlen(p);
349        }
350        SaveArgv[i] = NULL;
351
352        if (tTd(0, 1))
353        {
354                int ll;
355                extern char *CompileOptions[];
356
357                printf("Version %s\n Compiled with:", Version);
358                av = CompileOptions;
359                ll = 7;
360                while (*av != NULL)
361                {
362                        if (ll + strlen(*av) > 63)
363                        {
364                                putchar('\n');
365                                ll = 0;
366                        }
367                        if (ll == 0)
368                        {
369                                putchar('\t');
370                                putchar('\t');
371                        }
372                        else
373                                putchar(' ');
374                        printf("%s", *av);
375                        ll += strlen(*av++) + 1;
376                }
377                putchar('\n');
378        }
379        if (tTd(0, 10))
380        {
381                int ll;
382                extern char *OsCompileOptions[];
383
384                printf("    OS Defines:");
385                av = OsCompileOptions;
386                ll = 7;
387                while (*av != NULL)
388                {
389                        if (ll + strlen(*av) > 63)
390                        {
391                                putchar('\n');
392                                ll = 0;
393                        }
394                        if (ll == 0)
395                        {
396                                putchar('\t');
397                                putchar('\t');
398                        }
399                        else
400                                putchar(' ');
401                        printf("%s", *av);
402                        ll += strlen(*av++) + 1;
403                }
404                putchar('\n');
405#ifdef _PATH_UNIX
406                printf("Kernel symbols:\t%s\n", _PATH_UNIX);
407#endif
408                printf(" Def Conf file:\t%s\n", getcfname());
409                printf("      Pid file:\t%s\n", PidFile);
410        }
411
412        InChannel = stdin;
413        OutChannel = stdout;
414
415        /* clear sendmail's environment */
416        ExternalEnviron = environ;
417        emptyenviron[0] = NULL;
418        environ = emptyenviron;
419
420        /*
421        **  restore any original TZ setting until TimeZoneSpec has been
422        **  determined - or early log messages may get bogus time stamps
423        */
424        if ((p = getextenv("TZ")) != NULL)
425        {
426                char *tz;
427                int tzlen;
428
429                tzlen = strlen(p) + 4;
430                tz = xalloc(tzlen);
431                snprintf(tz, tzlen, "TZ=%s", p);
432                putenv(tz);
433        }
434
435        /* prime the child environment */
436        setuserenv("AGENT", "sendmail");
437
438        if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
439                (void) setsignal(SIGINT, intsig);
440        (void) setsignal(SIGTERM, intsig);
441        (void) setsignal(SIGPIPE, SIG_IGN);
442        OldUmask = umask(022);
443        OpMode = MD_DELIVER;
444        FullName = getextenv("NAME");
445
446        /*
447        **  Initialize name server if it is going to be used.
448        */
449
450#if NAMED_BIND
451        if (!bitset(RES_INIT, _res.options))
452                res_init();
453        if (tTd(8, 8))
454                _res.options |= RES_DEBUG;
455        else
456                _res.options &= ~RES_DEBUG;
457# ifdef RES_NOALIASES
458        _res.options |= RES_NOALIASES;
459# endif
460#endif
461
462        errno = 0;
463        from = NULL;
464
465        /* initialize some macros, etc. */
466        initmacros(CurEnv);
467        init_vendor_macros(CurEnv);
468
469        /* version */
470        define('v', Version, CurEnv);
471
472        /* hostname */
473        hp = myhostname(jbuf, sizeof jbuf);
474        if (jbuf[0] != '\0')
475        {
476                struct  utsname utsname;
477
478                if (tTd(0, 4))
479                        printf("canonical name: %s\n", jbuf);
480                define('w', newstr(jbuf), CurEnv);      /* must be new string */
481                define('j', newstr(jbuf), CurEnv);
482                setclass('w', jbuf);
483
484                p = strchr(jbuf, '.');
485                if (p != NULL)
486                {
487                        if (p[1] != '\0')
488                        {
489                                define('m', newstr(&p[1]), CurEnv);
490                        }
491                        while (p != NULL && strchr(&p[1], '.') != NULL)
492                        {
493                                *p = '\0';
494                                if (tTd(0, 4))
495                                        printf("\ta.k.a.: %s\n", jbuf);
496                                setclass('w', jbuf);
497                                *p++ = '.';
498                                p = strchr(p, '.');
499                        }
500                }
501
502                if (uname(&utsname) >= 0)
503                        p = utsname.nodename;
504                else
505                {
506                        if (tTd(0, 22))
507                                printf("uname failed (%s)\n", errstring(errno));
508                        makelower(jbuf);
509                        p = jbuf;
510                }
511                if (tTd(0, 4))
512                        printf(" UUCP nodename: %s\n", p);
513                p = newstr(p);
514                define('k', p, CurEnv);
515                setclass('k', p);
516                setclass('w', p);
517        }
518        if (hp != NULL)
519        {
520                for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
521                {
522                        if (tTd(0, 4))
523                                printf("\ta.k.a.: %s\n", *av);
524                        setclass('w', *av);
525                }
526#if NETINET
527                if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
528                {
529                        for (i = 0; hp->h_addr_list[i] != NULL; i++)
530                        {
531                                char ipbuf[103];
532
533                                snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
534                                        inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
535                                if (tTd(0, 4))
536                                        printf("\ta.k.a.: %s\n", ipbuf);
537                                setclass('w', ipbuf);
538                        }
539                }
540#endif
541        }
542
543        /* current time */
544        define('b', arpadate((char *) NULL), CurEnv);
545
546        QueueLimitRecipient = (QUEUE_CHAR *) NULL;
547        QueueLimitSender = (QUEUE_CHAR *) NULL;
548        QueueLimitId = (QUEUE_CHAR *) NULL;
549
550        /*
551        **  Crack argv.
552        */
553
554        av = argv;
555        p = strrchr(*av, '/');
556        if (p++ == NULL)
557                p = *av;
558        if (strcmp(p, "newaliases") == 0)
559                OpMode = MD_INITALIAS;
560        else if (strcmp(p, "mailq") == 0)
561                OpMode = MD_PRINT;
562        else if (strcmp(p, "smtpd") == 0)
563                OpMode = MD_DAEMON;
564        else if (strcmp(p, "hoststat") == 0)
565                OpMode = MD_HOSTSTAT;
566        else if (strcmp(p, "purgestat") == 0)
567                OpMode = MD_PURGESTAT;
568
569        optind = 1;
570        while ((j = getopt(argc, argv, OPTIONS)) != -1)
571        {
572                switch (j)
573                {
574                  case 'b':     /* operations mode */
575                        switch (j = *optarg)
576                        {
577                          case MD_DAEMON:
578                          case MD_FGDAEMON:
579# if !DAEMON
580                                usrerr("Daemon mode not implemented");
581                                ExitStat = EX_USAGE;
582                                break;
583# endif /* DAEMON */
584                          case MD_SMTP:
585# if !SMTP
586                                usrerr("I don't speak SMTP");
587                                ExitStat = EX_USAGE;
588                                break;
589# endif /* SMTP */
590
591                          case MD_INITALIAS:
592                          case MD_DELIVER:
593                          case MD_VERIFY:
594                          case MD_TEST:
595                          case MD_PRINT:
596                          case MD_HOSTSTAT:
597                          case MD_PURGESTAT:
598                          case MD_ARPAFTP:
599                                OpMode = j;
600                                break;
601
602                          case MD_FREEZE:
603                                usrerr("Frozen configurations unsupported");
604                                ExitStat = EX_USAGE;
605                                break;
606
607                          default:
608                                usrerr("Invalid operation mode %c", j);
609                                ExitStat = EX_USAGE;
610                                break;
611                        }
612                        break;
613
614                  case 'B':     /* body type */
615                        CurEnv->e_bodytype = optarg;
616                        break;
617
618                  case 'C':     /* select configuration file (already done) */
619                        if (RealUid != 0)
620                                warn_C_flag = TRUE;
621                        ConfFile = optarg;
622                        (void) drop_privileges(TRUE);
623                        safecf = FALSE;
624                        break;
625
626                  case 'd':     /* debugging -- already done */
627                        break;
628
629                  case 'f':     /* from address */
630                  case 'r':     /* obsolete -f flag */
631                        if (from != NULL)
632                        {
633                                usrerr("More than one \"from\" person");
634                                ExitStat = EX_USAGE;
635                                break;
636                        }
637                        from = newstr(denlstring(optarg, TRUE, TRUE));
638                        if (strcmp(RealUserName, from) != 0)
639                                warn_f_flag = j;
640                        break;
641
642                  case 'F':     /* set full name */
643                        FullName = newstr(optarg);
644                        break;
645
646                  case 'h':     /* hop count */
647                        CurEnv->e_hopcount = strtol(optarg, &ep, 10);
648                        if (*ep)
649                        {
650                                usrerr("Bad hop count (%s)", optarg);
651                                ExitStat = EX_USAGE;
652                        }
653                        break;
654               
655                  case 'n':     /* don't alias */
656                        NoAlias = TRUE;
657                        break;
658
659                  case 'N':     /* delivery status notifications */
660                        DefaultNotify |= QHASNOTIFY;
661                        if (strcasecmp(optarg, "never") == 0)
662                                break;
663                        for (p = optarg; p != NULL; optarg = p)
664                        {
665                                p = strchr(p, ',');
666                                if (p != NULL)
667                                        *p++ = '\0';
668                                if (strcasecmp(optarg, "success") == 0)
669                                        DefaultNotify |= QPINGONSUCCESS;
670                                else if (strcasecmp(optarg, "failure") == 0)
671                                        DefaultNotify |= QPINGONFAILURE;
672                                else if (strcasecmp(optarg, "delay") == 0)
673                                        DefaultNotify |= QPINGONDELAY;
674                                else
675                                {
676                                        usrerr("Invalid -N argument");
677                                        ExitStat = EX_USAGE;
678                                }
679                        }
680                        break;
681
682                  case 'o':     /* set option */
683                        setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
684                        break;
685
686                  case 'O':     /* set option (long form) */
687                        setoption(' ', optarg, FALSE, TRUE, CurEnv);
688                        break;
689
690                  case 'p':     /* set protocol */
691                        p = strchr(optarg, ':');
692                        if (p != NULL)
693                        {
694                                *p++ = '\0';
695                                if (*p != '\0')
696                                {
697                                        ep = xalloc(strlen(p) + 1);
698                                        cleanstrcpy(ep, p, MAXNAME);
699                                        define('s', ep, CurEnv);
700                                }
701                        }
702                        if (*optarg != '\0')
703                        {
704                                ep = xalloc(strlen(optarg) + 1);
705                                cleanstrcpy(ep, optarg, MAXNAME);
706                                define('r', ep, CurEnv);
707                        }
708                        break;
709
710                  case 'q':     /* run queue files at intervals */
711# if QUEUE
712                        FullName = NULL;
713                        queuemode = TRUE;
714                        switch (optarg[0])
715                        {
716                          case 'I':
717                                if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
718                                        syserr("!Out of memory!!");
719                                new->queue_match = newstr(&optarg[1]);
720                                new->queue_next = QueueLimitId;
721                                QueueLimitId = new;
722                                break;
723
724                          case 'R':
725                                if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
726                                        syserr("!Out of memory!!");
727                                new->queue_match = newstr(&optarg[1]);
728                                new->queue_next = QueueLimitRecipient;
729                                QueueLimitRecipient = new;
730                                break;
731
732                          case 'S':
733                                if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
734                                        syserr("!Out of memory!!");
735                                new->queue_match = newstr(&optarg[1]);
736                                new->queue_next = QueueLimitSender;
737                                QueueLimitSender = new;
738                                break;
739
740                          default:
741                                QueueIntvl = convtime(optarg, 'm');
742                                break;
743                        }
744# else /* QUEUE */
745                        usrerr("I don't know about queues");
746                        ExitStat = EX_USAGE;
747# endif /* QUEUE */
748                        break;
749
750                  case 'R':     /* DSN RET: what to return */
751                        if (bitset(EF_RET_PARAM, CurEnv->e_flags))
752                        {
753                                usrerr("Duplicate -R flag");
754                                ExitStat = EX_USAGE;
755                                break;
756                        }
757                        CurEnv->e_flags |= EF_RET_PARAM;
758                        if (strcasecmp(optarg, "hdrs") == 0)
759                                CurEnv->e_flags |= EF_NO_BODY_RETN;
760                        else if (strcasecmp(optarg, "full") != 0)
761                        {
762                                usrerr("Invalid -R value");
763                                ExitStat = EX_USAGE;
764                        }
765                        break;
766
767                  case 't':     /* read recipients from message */
768                        GrabTo = TRUE;
769                        break;
770
771                  case 'U':     /* initial (user) submission */
772                        UserSubmission = TRUE;
773                        break;
774
775                  case 'V':     /* DSN ENVID: set "original" envelope id */
776                        if (!xtextok(optarg))
777                        {
778                                usrerr("Invalid syntax in -V flag");
779                                ExitStat = EX_USAGE;
780                        }
781                        else
782                                CurEnv->e_envid = newstr(optarg);
783                        break;
784
785                  case 'X':     /* traffic log file */
786                        (void) drop_privileges(TRUE);
787                        TrafficLogFile = fopen(optarg, "a");
788                        if (TrafficLogFile == NULL)
789                        {
790                                syserr("cannot open %s", optarg);
791                                ExitStat = EX_CANTCREAT;
792                                break;
793                        }
794#ifdef HASSETVBUF
795                        setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
796#else
797                        setlinebuf(TrafficLogFile);
798#endif
799                        break;
800
801                        /* compatibility flags */
802                  case 'c':     /* connect to non-local mailers */
803                  case 'i':     /* don't let dot stop me */
804                  case 'm':     /* send to me too */
805                  case 'T':     /* set timeout interval */
806                  case 'v':     /* give blow-by-blow description */
807                        setoption(j, "T", FALSE, TRUE, CurEnv);
808                        break;
809
810                  case 'e':     /* error message disposition */
811                  case 'M':     /* define macro */
812                        setoption(j, optarg, FALSE, TRUE, CurEnv);
813                        break;
814
815                  case 's':     /* save From lines in headers */
816                        setoption('f', "T", FALSE, TRUE, CurEnv);
817                        break;
818
819# ifdef DBM
820                  case 'I':     /* initialize alias DBM file */
821                        OpMode = MD_INITALIAS;
822                        break;
823# endif /* DBM */
824
825# if defined(__osf__) || defined(_AIX3)
826                  case 'x':     /* random flag that OSF/1 & AIX mailx passes */
827                        break;
828# endif
829# if defined(sony_news)
830                  case 'E':
831                  case 'J':     /* ignore flags for Japanese code conversion
832                                   impremented on Sony NEWS */
833                        break;
834# endif
835
836                  default:
837                        finis(TRUE, EX_USAGE);
838                        break;
839                }
840        }
841        av += optind;
842
843        /*
844        **  Do basic initialization.
845        **      Read system control file.
846        **      Extract special fields for local use.
847        */
848
849        /* set up ${opMode} for use in config file */
850        {
851                char mbuf[2];
852
853                mbuf[0] = OpMode;
854                mbuf[1] = '\0';
855                define(MID_OPMODE, newstr(mbuf), CurEnv);
856        }
857
858#if XDEBUG
859        checkfd012("before readcf");
860#endif
861        vendor_pre_defaults(CurEnv);
862        readcf(getcfname(), safecf, CurEnv);
863        ConfigFileRead = TRUE;
864        vendor_post_defaults(CurEnv);
865
866        /* Enforce use of local time (null string overrides this) */
867        if (TimeZoneSpec == NULL)
868                unsetenv("TZ");
869        else if (TimeZoneSpec[0] != '\0')
870                setuserenv("TZ", TimeZoneSpec);
871        else
872                setuserenv("TZ", NULL);
873        tzset();
874
875        /* avoid denial-of-service attacks */
876        resetlimits();
877
878        if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
879        {
880                /* drop privileges -- daemon mode done after socket/bind */
881                (void) drop_privileges(FALSE);
882        }
883
884        /*
885        **  Find our real host name for future logging.
886        */
887
888        p = getauthinfo(STDIN_FILENO, &forged);
889        define('_', p, CurEnv);
890
891        /* suppress error printing if errors mailed back or whatever */
892        if (CurEnv->e_errormode != EM_PRINT)
893                HoldErrs = TRUE;
894
895        /* set up the $=m class now, after .cf has a chance to redefine $m */
896        expand("\201m", jbuf, sizeof jbuf, CurEnv);
897        setclass('m', jbuf);
898
899        /* probe interfaces and locate any additional names */
900        if (!DontProbeInterfaces)
901                load_if_names();
902
903        if (tTd(0, 1))
904        {
905                printf("\n============ SYSTEM IDENTITY (after readcf) ============");
906                printf("\n      (short domain name) $w = ");
907                xputs(macvalue('w', CurEnv));
908                printf("\n  (canonical domain name) $j = ");
909                xputs(macvalue('j', CurEnv));
910                printf("\n         (subdomain name) $m = ");
911                xputs(macvalue('m', CurEnv));
912                printf("\n              (node name) $k = ");
913                xputs(macvalue('k', CurEnv));
914                printf("\n========================================================\n\n");
915        }
916
917        /*
918        **  Do more command line checking -- these are things that
919        **  have to modify the results of reading the config file.
920        */
921
922        /* process authorization warnings from command line */
923        if (warn_C_flag)
924                auth_warning(CurEnv, "Processed by %s with -C %s",
925                        RealUserName, ConfFile);
926        if (Warn_Q_option)
927                auth_warning(CurEnv, "Processed from queue %s", QueueDir);
928
929        /* check body type for legality */
930        if (CurEnv->e_bodytype == NULL)
931                /* nothing */ ;
932        else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
933                SevenBitInput = TRUE;
934        else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
935                SevenBitInput = FALSE;
936        else
937        {
938                usrerr("Illegal body type %s", CurEnv->e_bodytype);
939                CurEnv->e_bodytype = NULL;
940        }
941
942        /* tweak default DSN notifications */
943        if (DefaultNotify == 0)
944                DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
945
946        /* be sure we don't pick up bogus HOSTALIASES environment variable */
947        if (queuemode && RealUid != 0)
948                (void) unsetenv("HOSTALIASES");
949
950        /* check for sane configuration level */
951        if (ConfigLevel > MAXCONFIGLEVEL)
952        {
953                syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
954                        ConfigLevel, Version, MAXCONFIGLEVEL);
955        }
956
957        /* need MCI cache to have persistence */
958        if (HostStatDir != NULL && MaxMciCache == 0)
959        {
960                HostStatDir = NULL;
961                printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
962        }
963
964        /* need HostStatusDir in order to have SingleThreadDelivery */
965        if (SingleThreadDelivery && HostStatDir == NULL)
966        {
967                SingleThreadDelivery = FALSE;
968                printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
969        }
970
971        /* check for permissions */
972        if ((OpMode == MD_DAEMON ||
973             OpMode == MD_FGDAEMON ||
974             OpMode == MD_PURGESTAT) &&
975            RealUid != 0 &&
976            RealUid != TrustedUid)
977        {
978                if (LogLevel > 1)
979                        sm_syslog(LOG_ALERT, NOQID,
980                                "user %d attempted to %s",
981                                RealUid,
982                                OpMode != MD_PURGESTAT ? "run daemon"
983                                                       : "purge host status");
984                usrerr("Permission denied");
985                finis(FALSE, EX_USAGE);
986        }
987
988        if (MeToo)
989                BlankEnvelope.e_flags |= EF_METOO;
990
991        switch (OpMode)
992        {
993          case MD_TEST:
994                /* don't have persistent host status in test mode */
995                HostStatDir = NULL;
996                if (Verbose == 0)
997                        Verbose = 2;
998                CurEnv->e_errormode = EM_PRINT;
999                HoldErrs = FALSE;
1000                break;
1001
1002          case MD_VERIFY:
1003                CurEnv->e_errormode = EM_PRINT;
1004                HoldErrs = FALSE;
1005                /* arrange to exit cleanly on hangup signal */
1006                if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1007                        setsignal(SIGHUP, intsig);
1008                break;
1009
1010          case MD_FGDAEMON:
1011                run_in_foreground = TRUE;
1012                OpMode = MD_DAEMON;
1013                /* fall through ... */
1014
1015          case MD_DAEMON:
1016                vendor_daemon_setup(CurEnv);
1017
1018                /* remove things that don't make sense in daemon mode */
1019                FullName = NULL;
1020                GrabTo = FALSE;
1021
1022                /* arrange to restart on hangup signal */
1023                if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1024                        sm_syslog(LOG_WARNING, NOQID,
1025                                "daemon invoked without full pathname; kill -1 won't work");
1026                setsignal(SIGHUP, sighup);
1027
1028                /* workaround: can't seem to release the signal in the parent */
1029                releasesignal(SIGHUP);
1030                break;
1031
1032          case MD_INITALIAS:
1033                Verbose = 2;
1034                CurEnv->e_errormode = EM_PRINT;
1035                HoldErrs = FALSE;
1036                /* fall through... */
1037
1038          case MD_PRINT:
1039                /* to handle sendmail -bp -qSfoobar properly */
1040                queuemode = FALSE;
1041                /* fall through... */
1042
1043          default:
1044                /* arrange to exit cleanly on hangup signal */
1045                if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1046                        setsignal(SIGHUP, intsig);
1047                break;
1048        }
1049
1050        /* special considerations for FullName */
1051        if (FullName != NULL)
1052        {
1053                char *full = NULL;
1054                extern bool rfc822_string __P((char *));
1055
1056                /* full names can't have newlines */
1057                if (strchr(FullName, '\n') != NULL)
1058                {
1059                        FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
1060                }
1061                /* check for characters that may have to be quoted */
1062                if (!rfc822_string(FullName))
1063                {
1064                        extern char *addquotes __P((char *));
1065
1066                        /*
1067                        **  Quote a full name with special characters
1068                        **  as a comment so crackaddr() doesn't destroy
1069                        **  the name portion of the address.
1070                        */
1071                        FullName = addquotes(FullName);
1072                        if (full != NULL)
1073                                free(full);
1074                }
1075        }
1076
1077        /* do heuristic mode adjustment */
1078        if (Verbose)
1079        {
1080                /* turn off noconnect option */
1081                setoption('c', "F", TRUE, FALSE, CurEnv);
1082
1083                /* turn on interactive delivery */
1084                setoption('d', "", TRUE, FALSE, CurEnv);
1085        }
1086
1087#ifdef VENDOR_CODE
1088        /* check for vendor mismatch */
1089        if (VendorCode != VENDOR_CODE)
1090        {
1091                extern char *getvendor __P((int));
1092
1093                message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1094                        getvendor(VENDOR_CODE), getvendor(VendorCode));
1095        }
1096#endif
1097       
1098        /* check for out of date configuration level */
1099        if (ConfigLevel < MAXCONFIGLEVEL)
1100        {
1101                message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1102                        Version, MAXCONFIGLEVEL, ConfigLevel);
1103        }
1104
1105        if (ConfigLevel < 3)
1106        {
1107                UseErrorsTo = TRUE;
1108        }
1109
1110        /* set options that were previous macros */
1111        if (SmtpGreeting == NULL)
1112        {
1113                if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1114                        SmtpGreeting = newstr(p);
1115                else
1116                        SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1117        }
1118        if (UnixFromLine == NULL)
1119        {
1120                if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1121                        UnixFromLine = newstr(p);
1122                else
1123                        UnixFromLine = "From \201g  \201d";
1124        }
1125
1126        /* our name for SMTP codes */
1127        expand("\201j", jbuf, sizeof jbuf, CurEnv);
1128        MyHostName = jbuf;
1129        if (strchr(jbuf, '.') == NULL)
1130                message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1131                        jbuf);
1132
1133        /* make certain that this name is part of the $=w class */
1134        setclass('w', MyHostName);
1135
1136        /* the indices of built-in mailers */
1137        st = stab("local", ST_MAILER, ST_FIND);
1138        if (st != NULL)
1139                LocalMailer = st->s_mailer;
1140        else if (OpMode != MD_TEST || !warn_C_flag)
1141                syserr("No local mailer defined");
1142
1143        st = stab("prog", ST_MAILER, ST_FIND);
1144        if (st == NULL)
1145                syserr("No prog mailer defined");
1146        else
1147        {
1148                ProgMailer = st->s_mailer;
1149                clrbitn(M_MUSER, ProgMailer->m_flags);
1150        }
1151
1152        st = stab("*file*", ST_MAILER, ST_FIND);
1153        if (st == NULL)
1154                syserr("No *file* mailer defined");
1155        else
1156        {
1157                FileMailer = st->s_mailer;
1158                clrbitn(M_MUSER, FileMailer->m_flags);
1159        }
1160
1161        st = stab("*include*", ST_MAILER, ST_FIND);
1162        if (st == NULL)
1163                syserr("No *include* mailer defined");
1164        else
1165                InclMailer = st->s_mailer;
1166
1167        if (ConfigLevel < 6)
1168        {
1169                /* heuristic tweaking of local mailer for back compat */
1170                if (LocalMailer != NULL)
1171                {
1172                        setbitn(M_ALIASABLE, LocalMailer->m_flags);
1173                        setbitn(M_HASPWENT, LocalMailer->m_flags);
1174                        setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1175                        setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1176                        setbitn(M_CHECKPROG, LocalMailer->m_flags);
1177                        setbitn(M_CHECKFILE, LocalMailer->m_flags);
1178                        setbitn(M_CHECKUDB, LocalMailer->m_flags);
1179                }
1180                if (ProgMailer != NULL)
1181                        setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1182                if (FileMailer != NULL)
1183                        setbitn(M_RUNASRCPT, FileMailer->m_flags);
1184        }
1185        if (ConfigLevel < 7)
1186        {
1187                if (LocalMailer != NULL)
1188                        setbitn(M_VRFY250, LocalMailer->m_flags);
1189                if (ProgMailer != NULL)
1190                        setbitn(M_VRFY250, ProgMailer->m_flags);
1191                if (FileMailer != NULL)
1192                        setbitn(M_VRFY250, FileMailer->m_flags);
1193        }
1194
1195        /* MIME Content-Types that cannot be transfer encoded */
1196        setclass('n', "multipart/signed");
1197
1198        /* MIME message/xxx subtypes that can be treated as messages */
1199        setclass('s', "rfc822");
1200
1201        /* MIME Content-Transfer-Encodings that can be encoded */
1202        setclass('e', "7bit");
1203        setclass('e', "8bit");
1204        setclass('e', "binary");
1205
1206#ifdef USE_B_CLASS
1207        /* MIME Content-Types that should be treated as binary */
1208        setclass('b', "image");
1209        setclass('b', "audio");
1210        setclass('b', "video");
1211        setclass('b', "application/octet-stream");
1212#endif
1213
1214#if _FFR_MAX_MIME_HEADER_LENGTH
1215        /* MIME headers which have fields to check for overflow */
1216        setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
1217        setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
1218
1219        /* MIME headers to check for length overflow */
1220        setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
1221
1222        /* MIME headers to check for overflow and rebalance */
1223        setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
1224        setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
1225        setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
1226        setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
1227        setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
1228#endif
1229
1230        /* operate in queue directory */
1231        if (QueueDir == NULL)
1232        {
1233                if (OpMode != MD_TEST)
1234                {
1235                        syserr("QueueDirectory (Q) option must be set");
1236                        ExitStat = EX_CONFIG;
1237                }
1238        }
1239        else
1240        {
1241                /* test path to get warning messages */
1242                (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
1243                if (OpMode != MD_TEST && chdir(QueueDir) < 0)
1244                {
1245                        syserr("cannot chdir(%s)", QueueDir);
1246                        ExitStat = EX_CONFIG;
1247                }
1248        }
1249
1250        /* check host status directory for validity */
1251        if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1252        {
1253                /* cannot use this value */
1254                if (tTd(0, 2))
1255                        printf("Cannot use HostStatusDirectory = %s: %s\n",
1256                                HostStatDir, errstring(errno));
1257                HostStatDir = NULL;
1258        }
1259
1260# if QUEUE
1261        if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1262        {
1263                struct stat stbuf;
1264
1265                /* check to see if we own the queue directory */
1266                if (stat(".", &stbuf) < 0)
1267                        syserr("main: cannot stat %s", QueueDir);
1268                if (stbuf.st_uid != RealUid)
1269                {
1270                        /* nope, really a botch */
1271                        usrerr("You do not have permission to process the queue");
1272                        finis(FALSE, EX_NOPERM);
1273                }
1274        }
1275# endif /* QUEUE */
1276
1277        /* if we've had errors so far, exit now */
1278        if (ExitStat != EX_OK && OpMode != MD_TEST)
1279                finis(FALSE, ExitStat);
1280
1281#if XDEBUG
1282        checkfd012("before main() initmaps");
1283#endif
1284
1285        /*
1286        **  Do operation-mode-dependent initialization.
1287        */
1288
1289        switch (OpMode)
1290        {
1291          case MD_PRINT:
1292                /* print the queue */
1293#if QUEUE
1294                dropenvelope(CurEnv, TRUE);
1295                signal(SIGPIPE, quiesce);
1296                printqueue();
1297                finis(FALSE, EX_OK);
1298#else /* QUEUE */
1299                usrerr("No queue to print");
1300                finis(FALSE, ExitStat);
1301#endif /* QUEUE */
1302                break;
1303
1304          case MD_HOSTSTAT:
1305                signal(SIGPIPE, quiesce);
1306                mci_traverse_persistent(mci_print_persistent, NULL);
1307                finis(FALSE, EX_OK);
1308                break;
1309
1310          case MD_PURGESTAT:
1311                mci_traverse_persistent(mci_purge_persistent, NULL);
1312                finis(FALSE, EX_OK);
1313                break;
1314
1315          case MD_INITALIAS:
1316                /* initialize maps */
1317                initmaps(TRUE, CurEnv);
1318                finis(FALSE, ExitStat);
1319                break;
1320
1321          case MD_SMTP:
1322          case MD_DAEMON:
1323                /* reset DSN parameters */
1324                DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1325                CurEnv->e_envid = NULL;
1326                CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1327
1328                /* don't open maps for daemon -- done below in child */
1329                break;
1330
1331          default:
1332                /* open the maps */
1333                initmaps(FALSE, CurEnv);
1334                break;
1335        }
1336
1337        if (tTd(0, 15))
1338        {
1339                extern void printrules __P((void));
1340
1341                /* print configuration table (or at least part of it) */
1342                if (tTd(0, 90))
1343                        printrules();
1344                for (i = 0; i < MAXMAILERS; i++)
1345                {
1346                        if (Mailer[i] != NULL)
1347                                printmailer(Mailer[i]);
1348                }
1349        }
1350
1351        /*
1352        **  Switch to the main envelope.
1353        */
1354
1355        CurEnv = newenvelope(&MainEnvelope, CurEnv);
1356        MainEnvelope.e_flags = BlankEnvelope.e_flags;
1357
1358        /*
1359        **  If test mode, read addresses from stdin and process.
1360        */
1361
1362        if (OpMode == MD_TEST)
1363        {
1364                char buf[MAXLINE];
1365                SIGFUNC_DECL intindebug __P((int));
1366
1367                if (isatty(fileno(stdin)))
1368                        Verbose = 2;
1369
1370                if (Verbose)
1371                {
1372                        printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1373                        printf("Enter <ruleset> <address>\n");
1374                }
1375                if (setjmp(TopFrame) > 0)
1376                        printf("\n");
1377                (void) setsignal(SIGINT, intindebug);
1378                for (;;)
1379                {
1380                        extern void testmodeline __P((char *, ENVELOPE *));
1381
1382                        if (Verbose == 2)
1383                                printf("> ");
1384                        (void) fflush(stdout);
1385                        if (fgets(buf, sizeof buf, stdin) == NULL)
1386                                finis(TRUE, ExitStat);
1387                        p = strchr(buf, '\n');
1388                        if (p != NULL)
1389                                *p = '\0';
1390                        if (Verbose < 2)
1391                                printf("> %s\n", buf);
1392                        testmodeline(buf, CurEnv);
1393                }
1394        }
1395
1396# if QUEUE
1397        /*
1398        **  If collecting stuff from the queue, go start doing that.
1399        */
1400
1401        if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
1402        {
1403                (void) runqueue(FALSE, Verbose);
1404                finis(TRUE, ExitStat);
1405        }
1406# endif /* QUEUE */
1407
1408        /*
1409        **  If a daemon, wait for a request.
1410        **      getrequests will always return in a child.
1411        **      If we should also be processing the queue, start
1412        **              doing it in background.
1413        **      We check for any errors that might have happened
1414        **              during startup.
1415        */
1416
1417        if (OpMode == MD_DAEMON || QueueIntvl != 0)
1418        {
1419                char dtype[200];
1420                extern void getrequests __P((ENVELOPE *));
1421
1422                if (!run_in_foreground && !tTd(99, 100))
1423                {
1424                        /* put us in background */
1425                        i = fork();
1426                        if (i < 0)
1427                                syserr("daemon: cannot fork");
1428                        if (i != 0)
1429                                finis(FALSE, EX_OK);
1430
1431                        /* disconnect from our controlling tty */
1432                        disconnect(2, CurEnv);
1433                }
1434
1435                dtype[0] = '\0';
1436                if (OpMode == MD_DAEMON)
1437                        strcat(dtype, "+SMTP");
1438                if (QueueIntvl != 0)
1439                {
1440                        strcat(dtype, "+queueing@");
1441                        strcat(dtype, pintvl(QueueIntvl, TRUE));
1442                }
1443                if (tTd(0, 1))
1444                        strcat(dtype, "+debugging");
1445
1446                sm_syslog(LOG_INFO, NOQID,
1447                        "starting daemon (%s): %s", Version, dtype + 1);
1448#ifdef XLA
1449                xla_create_file();
1450#endif
1451
1452# if QUEUE
1453                if (queuemode)
1454                {
1455                        (void) runqueue(TRUE, FALSE);
1456                        if (OpMode != MD_DAEMON)
1457                        {
1458                                for (;;)
1459                                {
1460                                        pause();
1461                                        if (DoQueueRun)
1462                                                (void) runqueue(TRUE, FALSE);
1463                                }
1464                        }
1465                }
1466# endif /* QUEUE */
1467                dropenvelope(CurEnv, TRUE);
1468
1469#if DAEMON
1470                getrequests(CurEnv);
1471
1472                /* drop privileges */
1473                (void) drop_privileges(FALSE);
1474
1475                /* at this point we are in a child: reset state */
1476                (void) newenvelope(CurEnv, CurEnv);
1477
1478                /*
1479                **  Get authentication data
1480                */
1481
1482                p = getauthinfo(fileno(InChannel), &forged);
1483                define('_', p, &BlankEnvelope);
1484#endif /* DAEMON */
1485        }
1486
1487# if SMTP
1488        /*
1489        **  If running SMTP protocol, start collecting and executing
1490        **  commands.  This will never return.
1491        */
1492
1493        if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1494        {
1495                char pbuf[20];
1496                extern void smtp __P((char *, ENVELOPE *));
1497
1498                /*
1499                **  Save some macros for check_* rulesets.
1500                */
1501
1502                if (forged)
1503                {
1504                        char ipbuf[103];
1505
1506                        snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1507                                 inet_ntoa(RealHostAddr.sin.sin_addr));
1508
1509                        define(macid("{client_name}", NULL),
1510                               newstr(ipbuf), &BlankEnvelope);
1511                }
1512                else
1513                        define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
1514                define(macid("{client_addr}", NULL),
1515                       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1516                if (RealHostAddr.sa.sa_family == AF_INET)
1517                        snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
1518                else
1519                        snprintf(pbuf, sizeof pbuf, "0");
1520                define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
1521
1522                /* initialize maps now for check_relay ruleset */
1523                initmaps(FALSE, CurEnv);
1524
1525                if (OpMode == MD_DAEMON)
1526                {
1527                        /* validate the connection */
1528                        HoldErrs = TRUE;
1529                        nullserver = validate_connection(&RealHostAddr,
1530                                                         RealHostName, CurEnv);
1531                        HoldErrs = FALSE;
1532                }
1533                smtp(nullserver, CurEnv);
1534        }
1535# endif /* SMTP */
1536
1537        clearenvelope(CurEnv, FALSE);
1538        if (OpMode == MD_VERIFY)
1539        {
1540                CurEnv->e_sendmode = SM_VERIFY;
1541                PostMasterCopy = NULL;
1542        }
1543        else
1544        {
1545                /* interactive -- all errors are global */
1546                CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1547        }
1548
1549        /*
1550        **  Do basic system initialization and set the sender
1551        */
1552
1553        initsys(CurEnv);
1554        if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
1555                auth_warning(CurEnv, "%s set sender to %s using -%c",
1556                        RealUserName, from, warn_f_flag);
1557        setsender(from, CurEnv, NULL, '\0', FALSE);
1558        if (macvalue('s', CurEnv) == NULL)
1559                define('s', RealHostName, CurEnv);
1560
1561        if (*av == NULL && !GrabTo)
1562        {
1563                CurEnv->e_flags |= EF_GLOBALERRS;
1564                usrerr("Recipient names must be specified");
1565
1566                /* collect body for UUCP return */
1567                if (OpMode != MD_VERIFY)
1568                        collect(InChannel, FALSE, NULL, CurEnv);
1569                finis(TRUE, ExitStat);
1570        }
1571
1572        /*
1573        **  Scan argv and deliver the message to everyone.
1574        */
1575
1576        sendtoargv(av, CurEnv);
1577
1578        /* if we have had errors sofar, arrange a meaningful exit stat */
1579        if (Errors > 0 && ExitStat == EX_OK)
1580                ExitStat = EX_USAGE;
1581
1582#if _FFR_FIX_DASHT
1583        /*
1584        **  If using -t, force not sending to argv recipients, even
1585        **  if they are mentioned in the headers.
1586        */
1587
1588        if (GrabTo)
1589        {
1590                ADDRESS *q;
1591               
1592                for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1593                        q->q_flags |= QDONTSEND;
1594        }
1595#endif
1596
1597        /*
1598        **  Read the input mail.
1599        */
1600
1601        CurEnv->e_to = NULL;
1602        if (OpMode != MD_VERIFY || GrabTo)
1603        {
1604                long savedflags = CurEnv->e_flags & EF_FATALERRS;
1605
1606                CurEnv->e_flags |= EF_GLOBALERRS;
1607                CurEnv->e_flags &= ~EF_FATALERRS;
1608                collect(InChannel, FALSE, NULL, CurEnv);
1609
1610                /* bail out if message too large */
1611                if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
1612                {
1613                        finis(TRUE, ExitStat);
1614                        /*NOTREACHED*/
1615                        return -1;
1616                }
1617                CurEnv->e_flags |= savedflags;
1618        }
1619        errno = 0;
1620
1621        if (tTd(1, 1))
1622                printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1623
1624        /*
1625        **  Actually send everything.
1626        **      If verifying, just ack.
1627        */
1628
1629        CurEnv->e_from.q_flags |= QDONTSEND;
1630        if (tTd(1, 5))
1631        {
1632                printf("main: QDONTSEND ");
1633                printaddr(&CurEnv->e_from, FALSE);
1634        }
1635        CurEnv->e_to = NULL;
1636        CurrentLA = getla();
1637        GrabTo = FALSE;
1638        sendall(CurEnv, SM_DEFAULT);
1639
1640        /*
1641        **  All done.
1642        **      Don't send return error message if in VERIFY mode.
1643        */
1644
1645        finis(TRUE, ExitStat);
1646        /*NOTREACHED*/
1647        return -1;
1648}
1649
1650/* ARGSUSED */
1651SIGFUNC_DECL
1652quiesce(sig)
1653        int sig;
1654{
1655        finis(FALSE, EX_OK);
1656}
1657
1658/* ARGSUSED */
1659SIGFUNC_DECL
1660intindebug(sig)
1661        int sig;
1662{
1663        longjmp(TopFrame, 1);
1664        return SIGFUNC_RETURN;
1665}
1666
1667
1668/*
1669**  FINIS -- Clean up and exit.
1670**
1671**      Parameters:
1672**              drop -- whether or not to drop CurEnv envelope
1673**              exitstat -- exit status to use for exit() call
1674**
1675**      Returns:
1676**              never
1677**
1678**      Side Effects:
1679**              exits sendmail
1680*/
1681
1682void
1683finis(drop, exitstat)
1684        bool drop;
1685        volatile int exitstat;
1686{
1687        extern void closemaps __P((void));
1688#ifdef USERDB
1689        extern void _udbx_close __P((void));
1690#endif
1691
1692        if (tTd(2, 1))
1693        {
1694                extern void printenvflags __P((ENVELOPE *));
1695
1696                printf("\n====finis: stat %d e_id=%s e_flags=",
1697                        exitstat,
1698                        CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1699                printenvflags(CurEnv);
1700        }
1701        if (tTd(2, 9))
1702                printopenfds(FALSE);
1703
1704        /* if we fail in finis(), just exit */
1705        if (setjmp(TopFrame) != 0)
1706        {
1707                /* failed -- just give it up */
1708                goto forceexit;
1709        }
1710
1711        /* clean up temp files */
1712        CurEnv->e_to = NULL;
1713        if (drop && CurEnv->e_id != NULL)
1714                dropenvelope(CurEnv, TRUE);
1715
1716        /* flush any cached connections */
1717        mci_flush(TRUE, NULL);
1718
1719        /* close maps belonging to this pid */
1720        closemaps();
1721
1722#ifdef USERDB
1723        /* close UserDatabase */
1724        _udbx_close();
1725#endif
1726
1727# ifdef XLA
1728        /* clean up extended load average stuff */
1729        xla_all_end();
1730# endif
1731
1732        /* and exit */
1733  forceexit:
1734        if (LogLevel > 78)
1735                sm_syslog(LOG_DEBUG, CurEnv->e_id,
1736                        "finis, pid=%d",
1737                        getpid());
1738        if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
1739                exitstat = EX_OK;
1740
1741        /* reset uid for process accounting */
1742        endpwent();
1743        setuid(RealUid);
1744
1745        exit(exitstat);
1746}
1747/*
1748**  INTSIG -- clean up on interrupt
1749**
1750**      This just arranges to exit.  It pessimises in that it
1751**      may resend a message.
1752**
1753**      Parameters:
1754**              none.
1755**
1756**      Returns:
1757**              none.
1758**
1759**      Side Effects:
1760**              Unlocks the current job.
1761*/
1762
1763/* ARGSUSED */
1764SIGFUNC_DECL
1765intsig(sig)
1766        int sig;
1767{
1768        if (LogLevel > 79)
1769                sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
1770        FileName = NULL;
1771        unlockqueue(CurEnv);
1772        closecontrolsocket(TRUE);
1773#ifdef XLA
1774        xla_all_end();
1775#endif
1776        finis(FALSE, EX_OK);
1777}
1778/*
1779**  INITMACROS -- initialize the macro system
1780**
1781**      This just involves defining some macros that are actually
1782**      used internally as metasymbols to be themselves.
1783**
1784**      Parameters:
1785**              none.
1786**
1787**      Returns:
1788**              none.
1789**
1790**      Side Effects:
1791**              initializes several macros to be themselves.
1792*/
1793
1794struct metamac  MetaMacros[] =
1795{
1796        /* LHS pattern matching characters */
1797        { '*', MATCHZANY },     { '+', MATCHANY },      { '-', MATCHONE },
1798        { '=', MATCHCLASS },    { '~', MATCHNCLASS },
1799
1800        /* these are RHS metasymbols */
1801        { '#', CANONNET },      { '@', CANONHOST },     { ':', CANONUSER },
1802        { '>', CALLSUBR },
1803
1804        /* the conditional operations */
1805        { '?', CONDIF },        { '|', CONDELSE },      { '.', CONDFI },
1806
1807        /* the hostname lookup characters */
1808        { '[', HOSTBEGIN },     { ']', HOSTEND },
1809        { '(', LOOKUPBEGIN },   { ')', LOOKUPEND },
1810
1811        /* miscellaneous control characters */
1812        { '&', MACRODEXPAND },
1813
1814        { '\0' }
1815};
1816
1817#define MACBINDING(name, mid) \
1818                stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
1819                MacroName[mid] = name;
1820
1821void
1822initmacros(e)
1823        register ENVELOPE *e;
1824{
1825        register struct metamac *m;
1826        register int c;
1827        char buf[5];
1828        extern char *MacroName[256];
1829
1830        for (m = MetaMacros; m->metaname != '\0'; m++)
1831        {
1832                buf[0] = m->metaval;
1833                buf[1] = '\0';
1834                define(m->metaname, newstr(buf), e);
1835        }
1836        buf[0] = MATCHREPL;
1837        buf[2] = '\0';
1838        for (c = '0'; c <= '9'; c++)
1839        {
1840                buf[1] = c;
1841                define(c, newstr(buf), e);
1842        }
1843
1844        /* set defaults for some macros sendmail will use later */
1845        define('n', "MAILER-DAEMON", e);
1846
1847        /* set up external names for some internal macros */
1848        MACBINDING("opMode", MID_OPMODE);
1849        /*XXX should probably add equivalents for all short macros here XXX*/
1850}
1851/*
1852**  DISCONNECT -- remove our connection with any foreground process
1853**
1854**      Parameters:
1855**              droplev -- how "deeply" we should drop the line.
1856**                      0 -- ignore signals, mail back errors, make sure
1857**                           output goes to stdout.
1858**                      1 -- also, make stdout go to transcript.
1859**                      2 -- also, disconnect from controlling terminal
1860**                           (only for daemon mode).
1861**              e -- the current envelope.
1862**
1863**      Returns:
1864**              none
1865**
1866**      Side Effects:
1867**              Trys to insure that we are immune to vagaries of
1868**              the controlling tty.
1869*/
1870
1871void
1872disconnect(droplev, e)
1873        int droplev;
1874        register ENVELOPE *e;
1875{
1876        int fd;
1877
1878        if (tTd(52, 1))
1879                printf("disconnect: In %d Out %d, e=%lx\n",
1880                        fileno(InChannel), fileno(OutChannel), (u_long) e);
1881        if (tTd(52, 100))
1882        {
1883                printf("don't\n");
1884                return;
1885        }
1886        if (LogLevel > 93)
1887                sm_syslog(LOG_DEBUG, e->e_id,
1888                        "disconnect level %d",
1889                        droplev);
1890
1891        /* be sure we don't get nasty signals */
1892        (void) setsignal(SIGINT, SIG_IGN);
1893        (void) setsignal(SIGQUIT, SIG_IGN);
1894
1895        /* we can't communicate with our caller, so.... */
1896        HoldErrs = TRUE;
1897        CurEnv->e_errormode = EM_MAIL;
1898        Verbose = 0;
1899        DisConnected = TRUE;
1900
1901        /* all input from /dev/null */
1902        if (InChannel != stdin)
1903        {
1904                (void) fclose(InChannel);
1905                InChannel = stdin;
1906        }
1907        if (freopen("/dev/null", "r", stdin) == NULL)
1908                sm_syslog(LOG_ERR, e->e_id,
1909                          "disconnect: freopen(\"/dev/null\") failed: %s",
1910                          errstring(errno));
1911
1912        /* output to the transcript */
1913        if (OutChannel != stdout)
1914        {
1915                (void) fclose(OutChannel);
1916                OutChannel = stdout;
1917        }
1918        if (droplev > 0)
1919        {
1920                if (e->e_xfp == NULL)
1921                {
1922                        fd = open("/dev/null", O_WRONLY, 0666);
1923                        if (fd == -1)
1924                                sm_syslog(LOG_ERR, e->e_id,
1925                                          "disconnect: open(\"/dev/null\") failed: %s",
1926                                          errstring(errno));
1927                }
1928                else
1929                {
1930                        fd = fileno(e->e_xfp);
1931                        if (fd == -1)
1932                                sm_syslog(LOG_ERR, e->e_id,
1933                                          "disconnect: fileno(e->e_xfp) failed: %s",
1934                                          errstring(errno));
1935                }
1936                (void) fflush(stdout);
1937                dup2(fd, STDOUT_FILENO);
1938                dup2(fd, STDERR_FILENO);
1939                if (e->e_xfp == NULL)
1940                        close(fd);
1941        }
1942
1943        /* drop our controlling TTY completely if possible */
1944        if (droplev > 1)
1945        {
1946                (void) setsid();
1947                errno = 0;
1948        }
1949
1950#if XDEBUG
1951        checkfd012("disconnect");
1952#endif
1953
1954        if (LogLevel > 71)
1955                sm_syslog(LOG_DEBUG, e->e_id,
1956                        "in background, pid=%d",
1957                        getpid());
1958
1959        errno = 0;
1960}
1961
1962static void
1963obsolete(argv)
1964        char *argv[];
1965{
1966        register char *ap;
1967        register char *op;
1968
1969        while ((ap = *++argv) != NULL)
1970        {
1971                /* Return if "--" or not an option of any form. */
1972                if (ap[0] != '-' || ap[1] == '-')
1973                        return;
1974
1975                /* skip over options that do have a value */
1976                op = strchr(OPTIONS, ap[1]);
1977                if (op != NULL && *++op == ':' && ap[2] == '\0' &&
1978                    ap[1] != 'd' &&
1979#if defined(sony_news)
1980                    ap[1] != 'E' && ap[1] != 'J' &&
1981#endif
1982                    argv[1] != NULL && argv[1][0] != '-')
1983                {
1984                        argv++;
1985                        continue;
1986                }
1987
1988                /* If -C doesn't have an argument, use sendmail.cf. */
1989#define __DEFPATH       "sendmail.cf"
1990                if (ap[1] == 'C' && ap[2] == '\0')
1991                {
1992                        *argv = xalloc(sizeof(__DEFPATH) + 2);
1993                        argv[0][0] = '-';
1994                        argv[0][1] = 'C';
1995                        (void)strcpy(&argv[0][2], __DEFPATH);
1996                }
1997
1998                /* If -q doesn't have an argument, run it once. */
1999                if (ap[1] == 'q' && ap[2] == '\0')
2000                        *argv = "-q0";
2001
2002                /* if -d doesn't have an argument, use 0-99.1 */
2003                if (ap[1] == 'd' && ap[2] == '\0')
2004                        *argv = "-d0-99.1";
2005
2006# if defined(sony_news)
2007                /* if -E doesn't have an argument, use -EC */
2008                if (ap[1] == 'E' && ap[2] == '\0')
2009                        *argv = "-EC";
2010
2011                /* if -J doesn't have an argument, use -JJ */
2012                if (ap[1] == 'J' && ap[2] == '\0')
2013                        *argv = "-JJ";
2014# endif
2015        }
2016}
2017/*
2018**  AUTH_WARNING -- specify authorization warning
2019**
2020**      Parameters:
2021**              e -- the current envelope.
2022**              msg -- the text of the message.
2023**              args -- arguments to the message.
2024**
2025**      Returns:
2026**              none.
2027*/
2028
2029void
2030#ifdef __STDC__
2031auth_warning(register ENVELOPE *e, const char *msg, ...)
2032#else
2033auth_warning(e, msg, va_alist)
2034        register ENVELOPE *e;
2035        const char *msg;
2036        va_dcl
2037#endif
2038{
2039        char buf[MAXLINE];
2040        VA_LOCAL_DECL
2041
2042        if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
2043        {
2044                register char *p;
2045                static char hostbuf[48];
2046                extern struct hostent *myhostname __P((char *, int));
2047
2048                if (hostbuf[0] == '\0')
2049                        (void) myhostname(hostbuf, sizeof hostbuf);
2050
2051                (void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2052                p = &buf[strlen(buf)];
2053                VA_START(msg);
2054                vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2055                VA_END;
2056                addheader("X-Authentication-Warning", buf, &e->e_header);
2057                if (LogLevel > 3)
2058                        sm_syslog(LOG_INFO, e->e_id,
2059                                "Authentication-Warning: %.400s",
2060                                buf);
2061        }
2062}
2063/*
2064**  GETEXTENV -- get from external environment
2065**
2066**      Parameters:
2067**              envar -- the name of the variable to retrieve
2068**
2069**      Returns:
2070**              The value, if any.
2071*/
2072
2073char *
2074getextenv(envar)
2075        const char *envar;
2076{
2077        char **envp;
2078        int l;
2079
2080        l = strlen(envar);
2081        for (envp = ExternalEnviron; *envp != NULL; envp++)
2082        {
2083                if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2084                        return &(*envp)[l + 1];
2085        }
2086        return NULL;
2087}
2088/*
2089**  SETUSERENV -- set an environment in the propogated environment
2090**
2091**      Parameters:
2092**              envar -- the name of the environment variable.
2093**              value -- the value to which it should be set.  If
2094**                      null, this is extracted from the incoming
2095**                      environment.  If that is not set, the call
2096**                      to setuserenv is ignored.
2097**
2098**      Returns:
2099**              none.
2100*/
2101
2102void
2103setuserenv(envar, value)
2104        const char *envar;
2105        const char *value;
2106{
2107        int i;
2108        char **evp = UserEnviron;
2109        char *p;
2110
2111        if (value == NULL)
2112        {
2113                value = getextenv(envar);
2114                if (value == NULL)
2115                        return;
2116        }
2117
2118        i = strlen(envar);
2119        p = (char *) xalloc(strlen(value) + i + 2);
2120        strcpy(p, envar);
2121        p[i++] = '=';
2122        strcpy(&p[i], value);
2123
2124        while (*evp != NULL && strncmp(*evp, p, i) != 0)
2125                evp++;
2126        if (*evp != NULL)
2127        {
2128                *evp++ = p;
2129        }
2130        else if (evp < &UserEnviron[MAXUSERENVIRON])
2131        {
2132                *evp++ = p;
2133                *evp = NULL;
2134        }
2135
2136        /* make sure it is in our environment as well */
2137        if (putenv(p) < 0)
2138                syserr("setuserenv: putenv(%s) failed", p);
2139}
2140/*
2141**  DUMPSTATE -- dump state
2142**
2143**      For debugging.
2144*/
2145
2146void
2147dumpstate(when)
2148        char *when;
2149{
2150        register char *j = macvalue('j', CurEnv);
2151        int rs;
2152
2153        sm_syslog(LOG_DEBUG, CurEnv->e_id,
2154                "--- dumping state on %s: $j = %s ---",
2155                when,
2156                j == NULL ? "<NULL>" : j);
2157        if (j != NULL)
2158        {
2159                if (!wordinclass(j, 'w'))
2160                        sm_syslog(LOG_DEBUG, CurEnv->e_id,
2161                                "*** $j not in $=w ***");
2162        }
2163        sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2164        sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2165        printopenfds(TRUE);
2166        sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2167        mci_dump_all(TRUE);
2168        rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2169        if (rs > 0)
2170        {
2171                int stat;
2172                register char **pvp;
2173                char *pv[MAXATOM + 1];
2174
2175                pv[0] = NULL;
2176                stat = rewrite(pv, rs, 0, CurEnv);
2177                sm_syslog(LOG_DEBUG, CurEnv->e_id,
2178                       "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2179                       stat);
2180                for (pvp = pv; *pvp != NULL; pvp++)
2181                        sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2182        }
2183        sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2184}
2185
2186
2187/* ARGSUSED */
2188SIGFUNC_DECL
2189sigusr1(sig)
2190        int sig;
2191{
2192        dumpstate("user signal");
2193        return SIGFUNC_RETURN;
2194}
2195
2196
2197/* ARGSUSED */
2198SIGFUNC_DECL
2199sighup(sig)
2200        int sig;
2201{
2202        if (SaveArgv[0][0] != '/')
2203        {
2204                if (LogLevel > 3)
2205                        sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
2206                finis(FALSE, EX_OSFILE);
2207        }
2208        if (LogLevel > 3)
2209                sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
2210        alarm(0);
2211        releasesignal(SIGHUP);
2212        closecontrolsocket(TRUE);
2213        if (drop_privileges(TRUE) != EX_OK)
2214        {
2215                if (LogLevel > 0)
2216                        sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
2217                                RunAsUid, RunAsGid);
2218                finis(FALSE, EX_OSERR);
2219        }
2220        execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
2221        if (LogLevel > 0)
2222                sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
2223        finis(FALSE, EX_OSFILE);
2224}
2225/*
2226**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2227**
2228**      Parameters:
2229**              to_real_uid -- if set, drop to the real uid instead
2230**                      of the RunAsUser.
2231**
2232**      Returns:
2233**              EX_OSERR if the setuid failed.
2234**              EX_OK otherwise.
2235*/
2236
2237int
2238drop_privileges(to_real_uid)
2239        bool to_real_uid;
2240{
2241        int rval = EX_OK;
2242        GIDSET_T emptygidset[1];
2243
2244        if (tTd(47, 1))
2245                printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2246                        (int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
2247
2248        if (to_real_uid)
2249        {
2250                RunAsUserName = RealUserName;
2251                RunAsUid = RealUid;
2252                RunAsGid = RealGid;
2253        }
2254
2255        /* make sure no one can grab open descriptors for secret files */
2256        endpwent();
2257
2258        /* reset group permissions; these can be set later */
2259        emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2260        if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2261                rval = EX_OSERR;
2262
2263        /* reset primary group and user id */
2264        if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2265                rval = EX_OSERR;
2266        if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
2267                rval = EX_OSERR;
2268        if (tTd(47, 5))
2269        {
2270                printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2271                        (int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
2272                printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
2273        }
2274        return rval;
2275}
2276/*
2277**  FILL_FD -- make sure a file descriptor has been properly allocated
2278**
2279**      Used to make sure that stdin/out/err are allocated on startup
2280**
2281**      Parameters:
2282**              fd -- the file descriptor to be filled.
2283**              where -- a string used for logging.  If NULL, this is
2284**                      being called on startup, and logging should
2285**                      not be done.
2286**
2287**      Returns:
2288**              none
2289*/
2290
2291void
2292fill_fd(fd, where)
2293        int fd;
2294        char *where;
2295{
2296        int i;
2297        struct stat stbuf;
2298
2299        if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2300                return;
2301
2302        if (where != NULL)
2303                syserr("fill_fd: %s: fd %d not open", where, fd);
2304        else
2305                MissingFds |= 1 << fd;
2306        i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2307        if (i < 0)
2308        {
2309                syserr("!fill_fd: %s: cannot open /dev/null",
2310                        where == NULL ? "startup" : where);
2311        }
2312        if (fd != i)
2313        {
2314                (void) dup2(i, fd);
2315                (void) close(i);
2316        }
2317}
2318/*
2319**  TESTMODELINE -- process a test mode input line
2320**
2321**      Parameters:
2322**              line -- the input line.
2323**              e -- the current environment.
2324**      Syntax:
2325**              #  a comment
2326**              .X process X as a configuration line
2327**              =X dump a configuration item (such as mailers)
2328**              $X dump a macro or class
2329**              /X try an activity
2330**              X  normal process through rule set X
2331*/
2332
2333void
2334testmodeline(line, e)
2335        char *line;
2336        ENVELOPE *e;
2337{
2338        register char *p;
2339        char *q;
2340        auto char *delimptr;
2341        int mid;
2342        int i, rs;
2343        STAB *map;
2344        char **s;
2345        struct rewrite *rw;
2346        ADDRESS a;
2347        static int tryflags = RF_COPYNONE;
2348        char exbuf[MAXLINE];
2349        extern bool invalidaddr __P((char *, char *));
2350        extern char *crackaddr __P((char *));
2351        extern void dump_class __P((STAB *, int));
2352        extern void translate_dollars __P((char *));
2353        extern void help __P((char *));
2354
2355        switch (line[0])
2356        {
2357          case '#':
2358          case 0:
2359                return;
2360
2361          case '?':
2362                help("-bt");
2363                return;
2364
2365          case '.':             /* config-style settings */
2366                switch (line[1])
2367                {
2368                  case 'D':
2369                        mid = macid(&line[2], &delimptr);
2370                        if (mid == '\0')
2371                                return;
2372                        translate_dollars(delimptr);
2373                        define(mid, newstr(delimptr), e);
2374                        break;
2375
2376                  case 'C':
2377                        if (line[2] == '\0')    /* not to call syserr() */
2378                                return;
2379
2380                        mid = macid(&line[2], &delimptr);
2381                        if (mid == '\0')
2382                                return;
2383                        translate_dollars(delimptr);
2384                        expand(delimptr, exbuf, sizeof exbuf, e);
2385                        p = exbuf;
2386                        while (*p != '\0')
2387                        {
2388                                register char *wd;
2389                                char delim;
2390
2391                                while (*p != '\0' && isascii(*p) && isspace(*p))
2392                                        p++;
2393                                wd = p;
2394                                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2395                                        p++;
2396                                delim = *p;
2397                                *p = '\0';
2398                                if (wd[0] != '\0')
2399                                        setclass(mid, wd);
2400                                *p = delim;
2401                        }
2402                        break;
2403
2404                  case '\0':
2405                        printf("Usage: .[DC]macro value(s)\n");
2406                        break;
2407
2408                  default:
2409                        printf("Unknown \".\" command %s\n", line);
2410                        break;
2411                }
2412                return;
2413
2414          case '=':             /* config-style settings */
2415                switch (line[1])
2416                {
2417                  case 'S':             /* dump rule set */
2418                        rs = strtorwset(&line[2], NULL, ST_FIND);
2419                        if (rs < 0)
2420                        {
2421                                printf("Undefined ruleset %s\n", &line[2]);
2422                                return;
2423                        }
2424                        rw = RewriteRules[rs];
2425                        if (rw == NULL)
2426                                return;
2427                        do
2428                        {
2429                                putchar('R');
2430                                s = rw->r_lhs;
2431                                while (*s != NULL)
2432                                {
2433                                        xputs(*s++);
2434                                        putchar(' ');
2435                                }
2436                                putchar('\t');
2437                                putchar('\t');
2438                                s = rw->r_rhs;
2439                                while (*s != NULL)
2440                                {
2441                                        xputs(*s++);
2442                                        putchar(' ');
2443                                }
2444                                putchar('\n');
2445                        } while ((rw = rw->r_next) != NULL);
2446                        break;
2447
2448                  case 'M':
2449                        for (i = 0; i < MAXMAILERS; i++)
2450                        {
2451                                if (Mailer[i] != NULL)
2452                                        printmailer(Mailer[i]);
2453                        }
2454                        break;
2455
2456                  case '\0':
2457                        printf("Usage: =Sruleset or =M\n");
2458                        break;
2459
2460                  default:
2461                        printf("Unknown \"=\" command %s\n", line);
2462                        break;
2463                }
2464                return;
2465
2466          case '-':             /* set command-line-like opts */
2467                switch (line[1])
2468                {
2469                  case 'd':
2470                        tTflag(&line[2]);
2471                        break;
2472
2473                  case '\0':
2474                        printf("Usage: -d{debug arguments}\n");
2475                        break;
2476
2477                  default:
2478                        printf("Unknown \"-\" command %s\n", line);
2479                        break;
2480                }
2481                return;
2482
2483          case '$':
2484                if (line[1] == '=')
2485                {
2486                        mid = macid(&line[2], NULL);
2487                        if (mid != '\0')
2488                                stabapply(dump_class, mid);
2489                        return;
2490                }
2491                mid = macid(&line[1], NULL);
2492                if (mid == '\0')
2493                        return;
2494                p = macvalue(mid, e);
2495                if (p == NULL)
2496                        printf("Undefined\n");
2497                else
2498                {
2499                        xputs(p);
2500                        printf("\n");
2501                }
2502                return;
2503
2504          case '/':             /* miscellaneous commands */
2505                p = &line[strlen(line)];
2506                while (--p >= line && isascii(*p) && isspace(*p))
2507                        *p = '\0';
2508                p = strpbrk(line, " \t");
2509                if (p != NULL)
2510                {
2511                        while (isascii(*p) && isspace(*p))
2512                                *p++ = '\0';
2513                }
2514                else
2515                        p = "";
2516                if (line[1] == '\0')
2517                {
2518                        printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
2519                        return;
2520                }
2521                if (strcasecmp(&line[1], "mx") == 0)
2522                {
2523#if NAMED_BIND
2524                        /* look up MX records */
2525                        int nmx;
2526                        auto int rcode;
2527                        char *mxhosts[MAXMXHOSTS + 1];
2528
2529                        if (*p == '\0')
2530                        {
2531                                printf("Usage: /mx address\n");
2532                                return;
2533                        }
2534                        nmx = getmxrr(p, mxhosts, FALSE, &rcode);
2535                        printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
2536                        for (i = 0; i < nmx; i++)
2537                                printf("\t%s\n", mxhosts[i]);
2538#else
2539                        printf("No MX code compiled in\n");
2540#endif
2541                }
2542                else if (strcasecmp(&line[1], "canon") == 0)
2543                {
2544                        char host[MAXHOSTNAMELEN];
2545
2546                        if (*p == '\0')
2547                        {
2548                                printf("Usage: /canon address\n");
2549                                return;
2550                        }
2551                        else if (strlen(p) >= sizeof host)
2552                        {
2553                                printf("Name too long\n");
2554                                return;
2555                        }
2556                        strcpy(host, p);
2557                        (void) getcanonname(host, sizeof(host), HasWildcardMX);
2558                        printf("getcanonname(%s) returns %s\n", p, host);
2559                }
2560                else if (strcasecmp(&line[1], "map") == 0)
2561                {
2562                        auto int rcode = EX_OK;
2563                        char *av[2];
2564
2565                        if (*p == '\0')
2566                        {
2567                                printf("Usage: /map mapname key\n");
2568                                return;
2569                        }
2570                        for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
2571                                continue;
2572                        if (*q == '\0')
2573                        {
2574                                printf("No key specified\n");
2575                                return;
2576                        }
2577                        *q++ = '\0';
2578                        map = stab(p, ST_MAP, ST_FIND);
2579                        if (map == NULL)
2580                        {
2581                                printf("Map named \"%s\" not found\n", p);
2582                                return;
2583                        }
2584                        if (!bitset(MF_OPEN, map->s_map.map_mflags))
2585                        {
2586                                printf("Map named \"%s\" not open\n", p);
2587                                return;
2588                        }
2589                        printf("map_lookup: %s (%s) ", p, q);
2590                        av[0] = q;
2591                        av[1] = NULL;
2592                        p = (*map->s_map.map_class->map_lookup)
2593                                        (&map->s_map, q, av, &rcode);
2594                        if (p == NULL)
2595                                printf("no match (%d)\n", rcode);
2596                        else
2597                                printf("returns %s (%d)\n", p, rcode);
2598                }
2599                else if (strcasecmp(&line[1], "try") == 0)
2600                {
2601                        MAILER *m;
2602                        STAB *s;
2603                        auto int rcode = EX_OK;
2604
2605                        q = strpbrk(p, " \t");
2606                        if (q != NULL)
2607                        {
2608                                while (isascii(*q) && isspace(*q))
2609                                        *q++ = '\0';
2610                        }
2611                        if (q == NULL || *q == '\0')
2612                        {
2613                                printf("Usage: /try mailer address\n");
2614                                return;
2615                        }
2616                        s = stab(p, ST_MAILER, ST_FIND);
2617                        if (s == NULL)
2618                        {
2619                                printf("Unknown mailer %s\n", p);
2620                                return;
2621                        }
2622                        m = s->s_mailer;
2623                        printf("Trying %s %s address %s for mailer %s\n",
2624                                bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2625                                bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
2626                                q, p);
2627                        p = remotename(q, m, tryflags, &rcode, CurEnv);
2628                        printf("Rcode = %d, addr = %s\n",
2629                                rcode, p == NULL ? "<NULL>" : p);
2630                        e->e_to = NULL;
2631                }
2632                else if (strcasecmp(&line[1], "tryflags") == 0)
2633                {
2634                        if (*p == '\0')
2635                        {
2636                                printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
2637                                return;
2638                        }
2639                        for (; *p != '\0'; p++)
2640                        {
2641                                switch (*p)
2642                                {
2643                                  case 'H':
2644                                  case 'h':
2645                                        tryflags |= RF_HEADERADDR;
2646                                        break;
2647
2648                                  case 'E':
2649                                  case 'e':
2650                                        tryflags &= ~RF_HEADERADDR;
2651                                        break;
2652
2653                                  case 'S':
2654                                  case 's':
2655                                        tryflags |= RF_SENDERADDR;
2656                                        break;
2657
2658                                  case 'R':
2659                                  case 'r':
2660                                        tryflags &= ~RF_SENDERADDR;
2661                                        break;
2662                                }
2663                        }
2664                }
2665                else if (strcasecmp(&line[1], "parse") == 0)
2666                {
2667                        if (*p == '\0')
2668                        {
2669                                printf("Usage: /parse address\n");
2670                                return;
2671                        }
2672                        q = crackaddr(p);
2673                        printf("Cracked address = ");
2674                        xputs(q);
2675                        printf("\nParsing %s %s address\n",
2676                                bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2677                                bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
2678                        if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
2679                                printf("Cannot parse\n");
2680                        else if (a.q_host != NULL && a.q_host[0] != '\0')
2681                                printf("mailer %s, host %s, user %s\n",
2682                                        a.q_mailer->m_name, a.q_host, a.q_user);
2683                        else
2684                                printf("mailer %s, user %s\n",
2685                                        a.q_mailer->m_name, a.q_user);
2686                        e->e_to = NULL;
2687                }
2688                else
2689                {
2690                        printf("Unknown \"/\" command %s\n", line);
2691                }
2692                return;
2693        }
2694
2695        for (p = line; isascii(*p) && isspace(*p); p++)
2696                continue;
2697        q = p;
2698        while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2699                p++;
2700        if (*p == '\0')
2701        {
2702                printf("No address!\n");
2703                return;
2704        }
2705        *p = '\0';
2706        if (invalidaddr(p + 1, NULL))
2707                return;
2708        do
2709        {
2710                register char **pvp;
2711                char pvpbuf[PSBUFSIZE];
2712
2713                pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
2714                              &delimptr, NULL);
2715                if (pvp == NULL)
2716                        continue;
2717                p = q;
2718                while (*p != '\0')
2719                {
2720                        int stat;
2721
2722                        rs = strtorwset(p, NULL, ST_FIND);
2723                        if (rs < 0)
2724                        {
2725                                printf("Undefined ruleset %s\n", p);
2726                                break;
2727                        }
2728                        stat = rewrite(pvp, rs, 0, e);
2729                        if (stat != EX_OK)
2730                                printf("== Ruleset %s (%d) status %d\n",
2731                                        p, rs, stat);
2732                        while (*p != '\0' && *p++ != ',')
2733                                continue;
2734                }
2735        } while (*(p = delimptr) != '\0');
2736}
2737
2738
2739void
2740dump_class(s, id)
2741        register STAB *s;
2742        int id;
2743{
2744        if (s->s_type != ST_CLASS)
2745                return;
2746        if (bitnset(id & 0xff, s->s_class))
2747                printf("%s\n", s->s_name);
2748}
Note: See TracBrowser for help on using the repository browser.