main.c revision 64562
138032Speter/* 264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1438032Speter#ifndef lint 1538032Speterstatic char copyright[] = 1664562Sgshapiro"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\ 1764562Sgshapiro All rights reserved.\n\ 1838032Speter Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ 1938032Speter Copyright (c) 1988, 1993\n\ 2038032Speter The Regents of the University of California. All rights reserved.\n"; 2164562Sgshapiro#endif /* ! lint */ 2238032Speter 2338032Speter#ifndef lint 2464562Sgshapirostatic char id[] = "@(#)$Id: main.c,v 8.485.4.19 2000/06/29 01:31:02 gshapiro Exp $"; 2564562Sgshapiro#endif /* ! lint */ 2638032Speter 2738032Speter#define _DEFINE 2838032Speter 2964562Sgshapiro#include <sendmail.h> 3038032Speter 3164562Sgshapiro 3264562Sgshapiro#if NETINET || NETINET6 3364562Sgshapiro# include <arpa/inet.h> 3464562Sgshapiro#endif /* NETINET || NETINET6 */ 3564562Sgshapiro 3664562Sgshapirostatic void dump_class __P((STAB *, int)); 3764562Sgshapirostatic void obsolete __P((char **)); 3864562Sgshapirostatic void testmodeline __P((char *, ENVELOPE *)); 3964562Sgshapiro 4038032Speter/* 4138032Speter** SENDMAIL -- Post mail to a set of destinations. 4238032Speter** 4338032Speter** This is the basic mail router. All user mail programs should 4438032Speter** call this routine to actually deliver mail. Sendmail in 4538032Speter** turn calls a bunch of mail servers that do the real work of 4638032Speter** delivering the mail. 4738032Speter** 4864562Sgshapiro** Sendmail is driven by settings read in from /etc/mail/sendmail.cf 4938032Speter** (read by readcf.c). 5038032Speter** 5138032Speter** Usage: 5238032Speter** /usr/lib/sendmail [flags] addr ... 5338032Speter** 5438032Speter** See the associated documentation for details. 5538032Speter** 5638032Speter** Author: 5738032Speter** Eric Allman, UCB/INGRES (until 10/81). 5838032Speter** Britton-Lee, Inc., purveyors of fine 5938032Speter** database computers (11/81 - 10/88). 6038032Speter** International Computer Science Institute 6138032Speter** (11/88 - 9/89). 6238032Speter** UCB/Mammoth Project (10/89 - 7/95). 6338032Speter** InReference, Inc. (8/95 - 1/97). 6438032Speter** Sendmail, Inc. (1/98 - present). 6538032Speter** The support of the my employers is gratefully acknowledged. 6638032Speter** Few of them (Britton-Lee in particular) have had 6738032Speter** anything to gain from my involvement in this project. 6838032Speter*/ 6938032Speter 7038032Speter 7138032Speterint NextMailer; /* "free" index into Mailer struct */ 7238032Speterchar *FullName; /* sender's full name */ 7338032SpeterENVELOPE BlankEnvelope; /* a "blank" envelope */ 7464562Sgshapirostatic ENVELOPE MainEnvelope; /* the envelope around the basic letter */ 7538032SpeterADDRESS NullAddress = /* a null address */ 7638032Speter { "", "", NULL, "" }; 7738032Speterchar *CommandLineArgs; /* command line args for pid file */ 7838032Speterbool Warn_Q_option = FALSE; /* warn about Q option use */ 7938032Speterchar **SaveArgv; /* argument vector for re-execing */ 8064562Sgshapirostatic int MissingFds = 0; /* bit map of fds missing on startup */ 8138032Speter 8238032Speter#ifdef NGROUPS_MAX 8338032SpeterGIDSET_T InitialGidSet[NGROUPS_MAX]; 8464562Sgshapiro#endif /* NGROUPS_MAX */ 8538032Speter 8638032Speter#if DAEMON && !SMTP 8738032SpeterERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR 8838032Speter#endif /* DAEMON && !SMTP */ 8938032Speter#if SMTP && !QUEUE 9038032SpeterERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR 9164562Sgshapiro#endif /* SMTP && !QUEUE */ 9238032Speter 9364562Sgshapiro#define MAXCONFIGLEVEL 9 /* highest config version level known */ 9438032Speter 9564562Sgshapiro#if SASL 9664562Sgshapirostatic sasl_callback_t srvcallbacks[] = 9764562Sgshapiro{ 9864562Sgshapiro { SASL_CB_VERIFYFILE, &safesaslfile, NULL }, 9964562Sgshapiro { SASL_CB_PROXY_POLICY, &proxy_policy, NULL }, 10064562Sgshapiro { SASL_CB_LIST_END, NULL, NULL } 10164562Sgshapiro}; 10264562Sgshapiro#endif /* SASL */ 10364562Sgshapiro 10464562Sgshapiroint SubmitMode; 10564562Sgshapiro 10638032Speterint 10738032Spetermain(argc, argv, envp) 10838032Speter int argc; 10938032Speter char **argv; 11038032Speter char **envp; 11138032Speter{ 11238032Speter register char *p; 11338032Speter char **av; 11438032Speter extern char Version[]; 11538032Speter char *ep, *from; 11638032Speter STAB *st; 11738032Speter register int i; 11838032Speter int j; 11964562Sgshapiro int dp; 12038032Speter bool safecf = TRUE; 12164562Sgshapiro BITMAP256 *p_flags = NULL; /* daemon flags */ 12238032Speter bool warn_C_flag = FALSE; 12364562Sgshapiro bool auth = TRUE; /* whether to set e_auth_param */ 12438032Speter char warn_f_flag = '\0'; 12538032Speter bool run_in_foreground = FALSE; /* -bD mode */ 12638032Speter static bool reenter = FALSE; 12738032Speter struct passwd *pw; 12838032Speter struct hostent *hp; 12938032Speter char *nullserver = NULL; 13064562Sgshapiro char *authinfo = NULL; 13164562Sgshapiro char *sysloglabel = NULL; /* label for syslog */ 13238032Speter bool forged; 13364562Sgshapiro struct stat traf_st; /* for TrafficLog FIFO check */ 13438032Speter char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ 13538032Speter static char rnamebuf[MAXNAME]; /* holds RealUserName */ 13638032Speter char *emptyenviron[1]; 13738032Speter QUEUE_CHAR *new; 13838032Speter extern int DtableSize; 13938032Speter extern int optind; 14038032Speter extern int opterr; 14138032Speter extern char *optarg; 14238032Speter extern char **environ; 14338032Speter 14438032Speter /* 14538032Speter ** Check to see if we reentered. 14638032Speter ** This would normally happen if e_putheader or e_putbody 14738032Speter ** were NULL when invoked. 14838032Speter */ 14938032Speter 15038032Speter if (reenter) 15138032Speter { 15238032Speter syserr("main: reentered!"); 15338032Speter abort(); 15438032Speter } 15538032Speter reenter = TRUE; 15638032Speter 15738032Speter /* avoid null pointer dereferences */ 15838032Speter TermEscape.te_rv_on = TermEscape.te_rv_off = ""; 15938032Speter 16038032Speter /* do machine-dependent initializations */ 16138032Speter init_md(argc, argv); 16238032Speter 16364562Sgshapiro 16438032Speter /* in 4.4BSD, the table can be huge; impose a reasonable limit */ 16538032Speter DtableSize = getdtsize(); 16638032Speter if (DtableSize > 256) 16738032Speter DtableSize = 256; 16838032Speter 16938032Speter /* 17038032Speter ** Be sure we have enough file descriptors. 17138032Speter ** But also be sure that 0, 1, & 2 are open. 17238032Speter */ 17338032Speter 17438032Speter fill_fd(STDIN_FILENO, NULL); 17538032Speter fill_fd(STDOUT_FILENO, NULL); 17638032Speter fill_fd(STDERR_FILENO, NULL); 17738032Speter 17838032Speter i = DtableSize; 17938032Speter while (--i > 0) 18038032Speter { 18138032Speter if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) 18238032Speter (void) close(i); 18338032Speter } 18438032Speter errno = 0; 18538032Speter 18638032Speter#if LOG 18764562Sgshapiro# ifdef LOG_MAIL 18838032Speter openlog("sendmail", LOG_PID, LOG_MAIL); 18964562Sgshapiro# else /* LOG_MAIL */ 19038032Speter openlog("sendmail", LOG_PID); 19164562Sgshapiro# endif /* LOG_MAIL */ 19264562Sgshapiro#endif /* LOG */ 19338032Speter 19438032Speter if (MissingFds != 0) 19538032Speter { 19638032Speter char mbuf[MAXLINE]; 19738032Speter 19838032Speter mbuf[0] = '\0'; 19938032Speter if (bitset(1 << STDIN_FILENO, MissingFds)) 20064562Sgshapiro (void) strlcat(mbuf, ", stdin", sizeof mbuf); 20138032Speter if (bitset(1 << STDOUT_FILENO, MissingFds)) 20264562Sgshapiro (void) strlcat(mbuf, ", stdout", sizeof mbuf); 20338032Speter if (bitset(1 << STDERR_FILENO, MissingFds)) 20464562Sgshapiro (void) strlcat(mbuf, ", stderr", sizeof mbuf); 20538032Speter syserr("File descriptors missing on startup: %s", &mbuf[2]); 20638032Speter } 20738032Speter 20838032Speter /* reset status from syserr() calls for missing file descriptors */ 20938032Speter Errors = 0; 21038032Speter ExitStat = EX_OK; 21138032Speter 21264562Sgshapiro SubmitMode = SUBMIT_UNKNOWN; 21338032Speter#if XDEBUG 21438032Speter checkfd012("after openlog"); 21564562Sgshapiro#endif /* XDEBUG */ 21638032Speter 21764562Sgshapiro /* 21864562Sgshapiro ** Seed the random number generator. 21964562Sgshapiro ** Used for queue file names, picking a queue directory, and 22064562Sgshapiro ** MX randomization. 22164562Sgshapiro */ 22264562Sgshapiro 22364562Sgshapiro seed_random(); 22464562Sgshapiro 22538032Speter tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 22638032Speter 22738032Speter#ifdef NGROUPS_MAX 22838032Speter /* save initial group set for future checks */ 22938032Speter i = getgroups(NGROUPS_MAX, InitialGidSet); 23038032Speter if (i == 0) 23138032Speter InitialGidSet[0] = (GID_T) -1; 23238032Speter while (i < NGROUPS_MAX) 23338032Speter InitialGidSet[i++] = InitialGidSet[0]; 23464562Sgshapiro#endif /* NGROUPS_MAX */ 23538032Speter 23638032Speter /* drop group id privileges (RunAsUser not yet set) */ 23764562Sgshapiro dp = drop_privileges(FALSE); 23864562Sgshapiro setstat(dp); 23938032Speter 24064562Sgshapiro# ifdef SIGUSR1 24138032Speter /* arrange to dump state on user-1 signal */ 24264562Sgshapiro (void) setsignal(SIGUSR1, sigusr1); 24364562Sgshapiro# endif /* SIGUSR1 */ 24438032Speter 24538032Speter /* initialize for setproctitle */ 24638032Speter initsetproctitle(argc, argv, envp); 24738032Speter 24838032Speter /* Handle any non-getoptable constructions. */ 24938032Speter obsolete(argv); 25038032Speter 25138032Speter /* 25238032Speter ** Do a quick prescan of the argument list. 25338032Speter */ 25438032Speter 25564562Sgshapiro 25638032Speter#if defined(__osf__) || defined(_AIX3) 25764562Sgshapiro# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x" 25864562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */ 25938032Speter#if defined(sony_news) 26064562Sgshapiro# define OPTIONS "B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:" 26164562Sgshapiro#endif /* defined(sony_news) */ 26238032Speter#ifndef OPTIONS 26364562Sgshapiro# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:" 26464562Sgshapiro#endif /* ! OPTIONS */ 26538032Speter opterr = 0; 26638032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 26738032Speter { 26838032Speter switch (j) 26938032Speter { 27038032Speter case 'd': 27138032Speter /* hack attack -- see if should use ANSI mode */ 27238032Speter if (strcmp(optarg, "ANSI") == 0) 27338032Speter { 27438032Speter TermEscape.te_rv_on = "\033[7m"; 27538032Speter TermEscape.te_rv_off = "\033[0m"; 27638032Speter break; 27738032Speter } 27838032Speter tTflag(optarg); 27938032Speter setbuf(stdout, (char *) NULL); 28038032Speter break; 28164562Sgshapiro 28264562Sgshapiro case 'G': /* relay (gateway) submission */ 28364562Sgshapiro SubmitMode |= SUBMIT_MTA; 28464562Sgshapiro break; 28564562Sgshapiro 28664562Sgshapiro case 'L': 28764562Sgshapiro j = min(strlen(optarg), 24) + 1; 28864562Sgshapiro sysloglabel = xalloc(j); 28964562Sgshapiro (void) strlcpy(sysloglabel, optarg, j); 29064562Sgshapiro break; 29164562Sgshapiro 29264562Sgshapiro case 'U': /* initial (user) submission */ 29364562Sgshapiro SubmitMode |= SUBMIT_MSA; 29464562Sgshapiro break; 29538032Speter } 29638032Speter } 29738032Speter opterr = 1; 29838032Speter 29964562Sgshapiro if (sysloglabel != NULL) 30064562Sgshapiro { 30164562Sgshapiro#if LOG 30264562Sgshapiro closelog(); 30364562Sgshapiro# ifdef LOG_MAIL 30464562Sgshapiro openlog(sysloglabel, LOG_PID, LOG_MAIL); 30564562Sgshapiro# else /* LOG_MAIL */ 30664562Sgshapiro openlog(sysloglabel, LOG_PID); 30764562Sgshapiro# endif /* LOG_MAIL */ 30864562Sgshapiro#endif /* LOG */ 30964562Sgshapiro } 31064562Sgshapiro 31164562Sgshapiro 31238032Speter /* set up the blank envelope */ 31338032Speter BlankEnvelope.e_puthdr = putheader; 31438032Speter BlankEnvelope.e_putbody = putbody; 31538032Speter BlankEnvelope.e_xfp = NULL; 31638032Speter STRUCTCOPY(NullAddress, BlankEnvelope.e_from); 31738032Speter CurEnv = &BlankEnvelope; 31838032Speter STRUCTCOPY(NullAddress, MainEnvelope.e_from); 31938032Speter 32038032Speter /* 32138032Speter ** Set default values for variables. 32238032Speter ** These cannot be in initialized data space. 32338032Speter */ 32438032Speter 32538032Speter setdefaults(&BlankEnvelope); 32638032Speter 32738032Speter RealUid = getuid(); 32838032Speter RealGid = getgid(); 32938032Speter 33038032Speter pw = sm_getpwuid(RealUid); 33138032Speter if (pw != NULL) 33238032Speter (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); 33338032Speter else 33464562Sgshapiro (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", 33564562Sgshapiro (int) RealUid); 33664562Sgshapiro 33738032Speter RealUserName = rnamebuf; 33838032Speter 33938032Speter if (tTd(0, 101)) 34038032Speter { 34164562Sgshapiro dprintf("Version %s\n", Version); 34242575Speter finis(FALSE, EX_OK); 34338032Speter } 34438032Speter 34538032Speter /* 34638032Speter ** if running non-setuid binary as non-root, pretend 34738032Speter ** we are the RunAsUid 34838032Speter */ 34938032Speter if (RealUid != 0 && geteuid() == RealUid) 35038032Speter { 35138032Speter if (tTd(47, 1)) 35264562Sgshapiro dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n", 35338032Speter (int)RealUid); 35438032Speter RunAsUid = RealUid; 35538032Speter } 35638032Speter else if (geteuid() != 0) 35738032Speter RunAsUid = geteuid(); 35838032Speter 35938032Speter if (RealUid != 0 && getegid() == RealGid) 36038032Speter RunAsGid = RealGid; 36138032Speter 36238032Speter if (tTd(47, 5)) 36338032Speter { 36464562Sgshapiro dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n", 36564562Sgshapiro (int)geteuid(), (int)getuid(), 36664562Sgshapiro (int)getegid(), (int)getgid()); 36764562Sgshapiro dprintf("main: RunAsUser = %d:%d\n", 36864562Sgshapiro (int)RunAsUid, (int)RunAsGid); 36938032Speter } 37038032Speter 37138032Speter /* save command line arguments */ 37264562Sgshapiro j = 0; 37338032Speter for (av = argv; *av != NULL; ) 37464562Sgshapiro j += strlen(*av++) + 1; 37538032Speter SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); 37664562Sgshapiro CommandLineArgs = xalloc(j); 37738032Speter p = CommandLineArgs; 37838032Speter for (av = argv, i = 0; *av != NULL; ) 37938032Speter { 38064562Sgshapiro int h; 38164562Sgshapiro 38238032Speter SaveArgv[i++] = newstr(*av); 38338032Speter if (av != argv) 38438032Speter *p++ = ' '; 38564562Sgshapiro (void) strlcpy(p, *av++, j); 38664562Sgshapiro h = strlen(p); 38764562Sgshapiro p += h; 38864562Sgshapiro j -= h + 1; 38938032Speter } 39038032Speter SaveArgv[i] = NULL; 39138032Speter 39238032Speter if (tTd(0, 1)) 39338032Speter { 39438032Speter int ll; 39538032Speter extern char *CompileOptions[]; 39638032Speter 39764562Sgshapiro dprintf("Version %s\n Compiled with:", Version); 39838032Speter av = CompileOptions; 39938032Speter ll = 7; 40038032Speter while (*av != NULL) 40138032Speter { 40238032Speter if (ll + strlen(*av) > 63) 40338032Speter { 40464562Sgshapiro dprintf("\n"); 40538032Speter ll = 0; 40638032Speter } 40738032Speter if (ll == 0) 40864562Sgshapiro dprintf("\t\t"); 40938032Speter else 41064562Sgshapiro dprintf(" "); 41164562Sgshapiro dprintf("%s", *av); 41238032Speter ll += strlen(*av++) + 1; 41338032Speter } 41464562Sgshapiro dprintf("\n"); 41538032Speter } 41638032Speter if (tTd(0, 10)) 41738032Speter { 41838032Speter int ll; 41938032Speter extern char *OsCompileOptions[]; 42038032Speter 42164562Sgshapiro dprintf(" OS Defines:"); 42238032Speter av = OsCompileOptions; 42338032Speter ll = 7; 42438032Speter while (*av != NULL) 42538032Speter { 42638032Speter if (ll + strlen(*av) > 63) 42738032Speter { 42864562Sgshapiro dprintf("\n"); 42938032Speter ll = 0; 43038032Speter } 43138032Speter if (ll == 0) 43264562Sgshapiro dprintf("\t\t"); 43338032Speter else 43464562Sgshapiro dprintf(" "); 43564562Sgshapiro dprintf("%s", *av); 43638032Speter ll += strlen(*av++) + 1; 43738032Speter } 43864562Sgshapiro dprintf("\n"); 43938032Speter#ifdef _PATH_UNIX 44064562Sgshapiro dprintf("Kernel symbols:\t%s\n", _PATH_UNIX); 44164562Sgshapiro#endif /* _PATH_UNIX */ 44264562Sgshapiro dprintf(" Def Conf file:\t%s\n", getcfname()); 44364562Sgshapiro dprintf(" Def Pid file:\t%s\n", PidFile); 44438032Speter } 44538032Speter 44638032Speter InChannel = stdin; 44738032Speter OutChannel = stdout; 44838032Speter 44938032Speter /* clear sendmail's environment */ 45038032Speter ExternalEnviron = environ; 45138032Speter emptyenviron[0] = NULL; 45238032Speter environ = emptyenviron; 45338032Speter 45438032Speter /* 45542575Speter ** restore any original TZ setting until TimeZoneSpec has been 45642575Speter ** determined - or early log messages may get bogus time stamps 45738032Speter */ 45838032Speter if ((p = getextenv("TZ")) != NULL) 45938032Speter { 46038032Speter char *tz; 46138032Speter int tzlen; 46238032Speter 46338032Speter tzlen = strlen(p) + 4; 46438032Speter tz = xalloc(tzlen); 46564562Sgshapiro (void) snprintf(tz, tzlen, "TZ=%s", p); 46664562Sgshapiro (void) putenv(tz); 46738032Speter } 46838032Speter 46938032Speter /* prime the child environment */ 47038032Speter setuserenv("AGENT", "sendmail"); 47138032Speter 47238032Speter if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) 47338032Speter (void) setsignal(SIGINT, intsig); 47438032Speter (void) setsignal(SIGTERM, intsig); 47538032Speter (void) setsignal(SIGPIPE, SIG_IGN); 47638032Speter OldUmask = umask(022); 47738032Speter OpMode = MD_DELIVER; 47838032Speter FullName = getextenv("NAME"); 47938032Speter 48038032Speter /* 48138032Speter ** Initialize name server if it is going to be used. 48238032Speter */ 48338032Speter 48438032Speter#if NAMED_BIND 48538032Speter if (!bitset(RES_INIT, _res.options)) 48664562Sgshapiro (void) res_init(); 48764562Sgshapiro 48864562Sgshapiro /* 48964562Sgshapiro ** hack to avoid crashes when debugging for the resolver is 49064562Sgshapiro ** turned on and sfio is used 49164562Sgshapiro */ 49238032Speter if (tTd(8, 8)) 49364562Sgshapiro# if !SFIO || SFIO_STDIO_COMPAT 49438032Speter _res.options |= RES_DEBUG; 49564562Sgshapiro# else /* !SFIO || SFIO_STDIO_COMPAT */ 49664562Sgshapiro dprintf("RES_DEBUG not available due to SFIO\n"); 49764562Sgshapiro# endif /* !SFIO || SFIO_STDIO_COMPAT */ 49838032Speter else 49938032Speter _res.options &= ~RES_DEBUG; 50038032Speter# ifdef RES_NOALIASES 50138032Speter _res.options |= RES_NOALIASES; 50264562Sgshapiro# endif /* RES_NOALIASES */ 50364562Sgshapiro TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry; 50464562Sgshapiro TimeOuts.res_retry[RES_TO_FIRST] = _res.retry; 50564562Sgshapiro TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry; 50664562Sgshapiro TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans; 50764562Sgshapiro TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans; 50864562Sgshapiro TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans; 50964562Sgshapiro#endif /* NAMED_BIND */ 51038032Speter 51138032Speter errno = 0; 51238032Speter from = NULL; 51338032Speter 51438032Speter /* initialize some macros, etc. */ 51538032Speter initmacros(CurEnv); 51638032Speter init_vendor_macros(CurEnv); 51738032Speter 51838032Speter /* version */ 51938032Speter define('v', Version, CurEnv); 52038032Speter 52138032Speter /* hostname */ 52238032Speter hp = myhostname(jbuf, sizeof jbuf); 52338032Speter if (jbuf[0] != '\0') 52438032Speter { 52538032Speter struct utsname utsname; 52638032Speter 52738032Speter if (tTd(0, 4)) 52864562Sgshapiro dprintf("canonical name: %s\n", jbuf); 52938032Speter define('w', newstr(jbuf), CurEnv); /* must be new string */ 53038032Speter define('j', newstr(jbuf), CurEnv); 53138032Speter setclass('w', jbuf); 53238032Speter 53338032Speter p = strchr(jbuf, '.'); 53438032Speter if (p != NULL) 53538032Speter { 53638032Speter if (p[1] != '\0') 53738032Speter { 53838032Speter define('m', newstr(&p[1]), CurEnv); 53938032Speter } 54038032Speter while (p != NULL && strchr(&p[1], '.') != NULL) 54138032Speter { 54238032Speter *p = '\0'; 54338032Speter if (tTd(0, 4)) 54464562Sgshapiro dprintf("\ta.k.a.: %s\n", jbuf); 54538032Speter setclass('w', jbuf); 54638032Speter *p++ = '.'; 54738032Speter p = strchr(p, '.'); 54838032Speter } 54938032Speter } 55038032Speter 55138032Speter if (uname(&utsname) >= 0) 55238032Speter p = utsname.nodename; 55338032Speter else 55438032Speter { 55538032Speter if (tTd(0, 22)) 55664562Sgshapiro dprintf("uname failed (%s)\n", 55764562Sgshapiro errstring(errno)); 55838032Speter makelower(jbuf); 55938032Speter p = jbuf; 56038032Speter } 56138032Speter if (tTd(0, 4)) 56264562Sgshapiro dprintf(" UUCP nodename: %s\n", p); 56338032Speter p = newstr(p); 56438032Speter define('k', p, CurEnv); 56538032Speter setclass('k', p); 56638032Speter setclass('w', p); 56738032Speter } 56838032Speter if (hp != NULL) 56938032Speter { 57038032Speter for (av = hp->h_aliases; av != NULL && *av != NULL; av++) 57138032Speter { 57238032Speter if (tTd(0, 4)) 57364562Sgshapiro dprintf("\ta.k.a.: %s\n", *av); 57438032Speter setclass('w', *av); 57538032Speter } 57664562Sgshapiro#if NETINET || NETINET6 57764562Sgshapiro for (i = 0; hp->h_addr_list[i] != NULL; i++) 57838032Speter { 57964562Sgshapiro# if NETINET6 58064562Sgshapiro char *addr; 58164562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 58264562Sgshapiro struct in6_addr ia6; 58364562Sgshapiro# endif /* NETINET6 */ 58464562Sgshapiro# if NETINET 58564562Sgshapiro struct in_addr ia; 58664562Sgshapiro# endif /* NETINET */ 58764562Sgshapiro char ipbuf[103]; 58864562Sgshapiro 58964562Sgshapiro ipbuf[0] = '\0'; 59064562Sgshapiro switch (hp->h_addrtype) 59138032Speter { 59264562Sgshapiro# if NETINET 59364562Sgshapiro case AF_INET: 59464562Sgshapiro if (hp->h_length != INADDRSZ) 59564562Sgshapiro break; 59638032Speter 59764562Sgshapiro memmove(&ia, hp->h_addr_list[i], INADDRSZ); 59864562Sgshapiro (void) snprintf(ipbuf, sizeof ipbuf, 59964562Sgshapiro "[%.100s]", inet_ntoa(ia)); 60064562Sgshapiro break; 60164562Sgshapiro# endif /* NETINET */ 60264562Sgshapiro 60364562Sgshapiro# if NETINET6 60464562Sgshapiro case AF_INET6: 60564562Sgshapiro if (hp->h_length != IN6ADDRSZ) 60664562Sgshapiro break; 60764562Sgshapiro 60864562Sgshapiro memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ); 60964562Sgshapiro addr = anynet_ntop(&ia6, buf6, sizeof buf6); 61064562Sgshapiro if (addr != NULL) 61164562Sgshapiro (void) snprintf(ipbuf, sizeof ipbuf, 61264562Sgshapiro "[%.100s]", addr); 61364562Sgshapiro break; 61464562Sgshapiro# endif /* NETINET6 */ 61538032Speter } 61664562Sgshapiro if (ipbuf[0] == '\0') 61764562Sgshapiro break; 61864562Sgshapiro 61964562Sgshapiro if (tTd(0, 4)) 62064562Sgshapiro dprintf("\ta.k.a.: %s\n", ipbuf); 62164562Sgshapiro setclass('w', ipbuf); 62238032Speter } 62364562Sgshapiro#endif /* NETINET || NETINET6 */ 62438032Speter } 62538032Speter 62638032Speter /* current time */ 62738032Speter define('b', arpadate((char *) NULL), CurEnv); 62864562Sgshapiro /* current load average */ 62964562Sgshapiro CurrentLA = sm_getla(CurEnv); 63038032Speter 63138032Speter QueueLimitRecipient = (QUEUE_CHAR *) NULL; 63238032Speter QueueLimitSender = (QUEUE_CHAR *) NULL; 63338032Speter QueueLimitId = (QUEUE_CHAR *) NULL; 63438032Speter 63538032Speter /* 63642575Speter ** Crack argv. 63738032Speter */ 63838032Speter 63938032Speter av = argv; 64038032Speter p = strrchr(*av, '/'); 64138032Speter if (p++ == NULL) 64238032Speter p = *av; 64338032Speter if (strcmp(p, "newaliases") == 0) 64438032Speter OpMode = MD_INITALIAS; 64538032Speter else if (strcmp(p, "mailq") == 0) 64638032Speter OpMode = MD_PRINT; 64738032Speter else if (strcmp(p, "smtpd") == 0) 64838032Speter OpMode = MD_DAEMON; 64938032Speter else if (strcmp(p, "hoststat") == 0) 65038032Speter OpMode = MD_HOSTSTAT; 65138032Speter else if (strcmp(p, "purgestat") == 0) 65238032Speter OpMode = MD_PURGESTAT; 65338032Speter 65438032Speter optind = 1; 65538032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 65638032Speter { 65738032Speter switch (j) 65838032Speter { 65938032Speter case 'b': /* operations mode */ 66038032Speter switch (j = *optarg) 66138032Speter { 66238032Speter case MD_DAEMON: 66338032Speter case MD_FGDAEMON: 66464562Sgshapiro#if !DAEMON 66538032Speter usrerr("Daemon mode not implemented"); 66638032Speter ExitStat = EX_USAGE; 66738032Speter break; 66864562Sgshapiro#endif /* !DAEMON */ 66938032Speter case MD_SMTP: 67064562Sgshapiro#if !SMTP 67138032Speter usrerr("I don't speak SMTP"); 67238032Speter ExitStat = EX_USAGE; 67338032Speter break; 67464562Sgshapiro#endif /* !SMTP */ 67538032Speter 67638032Speter case MD_INITALIAS: 67738032Speter case MD_DELIVER: 67838032Speter case MD_VERIFY: 67938032Speter case MD_TEST: 68038032Speter case MD_PRINT: 68138032Speter case MD_HOSTSTAT: 68238032Speter case MD_PURGESTAT: 68338032Speter case MD_ARPAFTP: 68438032Speter OpMode = j; 68538032Speter break; 68638032Speter 68738032Speter case MD_FREEZE: 68838032Speter usrerr("Frozen configurations unsupported"); 68938032Speter ExitStat = EX_USAGE; 69038032Speter break; 69138032Speter 69238032Speter default: 69338032Speter usrerr("Invalid operation mode %c", j); 69438032Speter ExitStat = EX_USAGE; 69538032Speter break; 69638032Speter } 69738032Speter break; 69838032Speter 69938032Speter case 'B': /* body type */ 70038032Speter CurEnv->e_bodytype = optarg; 70138032Speter break; 70238032Speter 70338032Speter case 'C': /* select configuration file (already done) */ 70438032Speter if (RealUid != 0) 70538032Speter warn_C_flag = TRUE; 70638032Speter ConfFile = optarg; 70764562Sgshapiro dp = drop_privileges(TRUE); 70864562Sgshapiro setstat(dp); 70938032Speter safecf = FALSE; 71038032Speter break; 71138032Speter 71238032Speter case 'd': /* debugging -- already done */ 71338032Speter break; 71438032Speter 71538032Speter case 'f': /* from address */ 71638032Speter case 'r': /* obsolete -f flag */ 71738032Speter if (from != NULL) 71838032Speter { 71938032Speter usrerr("More than one \"from\" person"); 72038032Speter ExitStat = EX_USAGE; 72138032Speter break; 72238032Speter } 72338032Speter from = newstr(denlstring(optarg, TRUE, TRUE)); 72438032Speter if (strcmp(RealUserName, from) != 0) 72538032Speter warn_f_flag = j; 72638032Speter break; 72738032Speter 72838032Speter case 'F': /* set full name */ 72938032Speter FullName = newstr(optarg); 73038032Speter break; 73138032Speter 73264562Sgshapiro case 'G': /* relay (gateway) submission */ 73364562Sgshapiro /* already set */ 73464562Sgshapiro break; 73564562Sgshapiro 73638032Speter case 'h': /* hop count */ 73764562Sgshapiro CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10); 73838032Speter if (*ep) 73938032Speter { 74038032Speter usrerr("Bad hop count (%s)", optarg); 74138032Speter ExitStat = EX_USAGE; 74238032Speter } 74338032Speter break; 74464562Sgshapiro 74564562Sgshapiro case 'L': /* program label */ 74664562Sgshapiro /* already set */ 74764562Sgshapiro break; 74864562Sgshapiro 74938032Speter case 'n': /* don't alias */ 75038032Speter NoAlias = TRUE; 75138032Speter break; 75238032Speter 75338032Speter case 'N': /* delivery status notifications */ 75438032Speter DefaultNotify |= QHASNOTIFY; 75564562Sgshapiro define(macid("{dsn_notify}", NULL), 75664562Sgshapiro newstr(optarg), CurEnv); 75738032Speter if (strcasecmp(optarg, "never") == 0) 75838032Speter break; 75938032Speter for (p = optarg; p != NULL; optarg = p) 76038032Speter { 76138032Speter p = strchr(p, ','); 76238032Speter if (p != NULL) 76338032Speter *p++ = '\0'; 76438032Speter if (strcasecmp(optarg, "success") == 0) 76538032Speter DefaultNotify |= QPINGONSUCCESS; 76638032Speter else if (strcasecmp(optarg, "failure") == 0) 76738032Speter DefaultNotify |= QPINGONFAILURE; 76838032Speter else if (strcasecmp(optarg, "delay") == 0) 76938032Speter DefaultNotify |= QPINGONDELAY; 77038032Speter else 77138032Speter { 77238032Speter usrerr("Invalid -N argument"); 77338032Speter ExitStat = EX_USAGE; 77438032Speter } 77538032Speter } 77638032Speter break; 77738032Speter 77838032Speter case 'o': /* set option */ 77938032Speter setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); 78038032Speter break; 78138032Speter 78238032Speter case 'O': /* set option (long form) */ 78338032Speter setoption(' ', optarg, FALSE, TRUE, CurEnv); 78438032Speter break; 78538032Speter 78638032Speter case 'p': /* set protocol */ 78738032Speter p = strchr(optarg, ':'); 78838032Speter if (p != NULL) 78938032Speter { 79038032Speter *p++ = '\0'; 79138032Speter if (*p != '\0') 79238032Speter { 79338032Speter ep = xalloc(strlen(p) + 1); 79438032Speter cleanstrcpy(ep, p, MAXNAME); 79538032Speter define('s', ep, CurEnv); 79638032Speter } 79738032Speter } 79838032Speter if (*optarg != '\0') 79938032Speter { 80038032Speter ep = xalloc(strlen(optarg) + 1); 80138032Speter cleanstrcpy(ep, optarg, MAXNAME); 80238032Speter define('r', ep, CurEnv); 80338032Speter } 80438032Speter break; 80538032Speter 80638032Speter case 'q': /* run queue files at intervals */ 80764562Sgshapiro#if QUEUE 80864562Sgshapiro /* sanity check */ 80964562Sgshapiro if (OpMode != MD_DELIVER && 81064562Sgshapiro OpMode != MD_DAEMON && 81164562Sgshapiro OpMode != MD_FGDAEMON && 81264562Sgshapiro OpMode != MD_PRINT && 81364562Sgshapiro OpMode != MD_QUEUERUN) 81464562Sgshapiro { 81564562Sgshapiro usrerr("Can not use -q with -b%c", OpMode); 81664562Sgshapiro ExitStat = EX_USAGE; 81764562Sgshapiro break; 81864562Sgshapiro } 81964562Sgshapiro 82064562Sgshapiro /* don't override -bd, -bD or -bp */ 82164562Sgshapiro if (OpMode == MD_DELIVER) 82264562Sgshapiro OpMode = MD_QUEUERUN; 82364562Sgshapiro 82438032Speter FullName = NULL; 82564562Sgshapiro 82638032Speter switch (optarg[0]) 82738032Speter { 82838032Speter case 'I': 82964562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 83038032Speter new->queue_match = newstr(&optarg[1]); 83138032Speter new->queue_next = QueueLimitId; 83238032Speter QueueLimitId = new; 83338032Speter break; 83438032Speter 83538032Speter case 'R': 83664562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 83738032Speter new->queue_match = newstr(&optarg[1]); 83838032Speter new->queue_next = QueueLimitRecipient; 83938032Speter QueueLimitRecipient = new; 84038032Speter break; 84138032Speter 84238032Speter case 'S': 84364562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 84438032Speter new->queue_match = newstr(&optarg[1]); 84538032Speter new->queue_next = QueueLimitSender; 84638032Speter QueueLimitSender = new; 84738032Speter break; 84838032Speter 84938032Speter default: 85064562Sgshapiro i = Errors; 85138032Speter QueueIntvl = convtime(optarg, 'm'); 85264562Sgshapiro 85364562Sgshapiro /* check for bad conversion */ 85464562Sgshapiro if (i < Errors) 85564562Sgshapiro ExitStat = EX_USAGE; 85638032Speter break; 85738032Speter } 85864562Sgshapiro#else /* QUEUE */ 85938032Speter usrerr("I don't know about queues"); 86038032Speter ExitStat = EX_USAGE; 86164562Sgshapiro#endif /* QUEUE */ 86238032Speter break; 86338032Speter 86438032Speter case 'R': /* DSN RET: what to return */ 86538032Speter if (bitset(EF_RET_PARAM, CurEnv->e_flags)) 86638032Speter { 86738032Speter usrerr("Duplicate -R flag"); 86838032Speter ExitStat = EX_USAGE; 86938032Speter break; 87038032Speter } 87138032Speter CurEnv->e_flags |= EF_RET_PARAM; 87238032Speter if (strcasecmp(optarg, "hdrs") == 0) 87338032Speter CurEnv->e_flags |= EF_NO_BODY_RETN; 87438032Speter else if (strcasecmp(optarg, "full") != 0) 87538032Speter { 87638032Speter usrerr("Invalid -R value"); 87738032Speter ExitStat = EX_USAGE; 87838032Speter } 87964562Sgshapiro define(macid("{dsn_ret}", NULL), 88064562Sgshapiro newstr(optarg), CurEnv); 88138032Speter break; 88238032Speter 88338032Speter case 't': /* read recipients from message */ 88438032Speter GrabTo = TRUE; 88538032Speter break; 88638032Speter 88738032Speter case 'U': /* initial (user) submission */ 88864562Sgshapiro /* already set */ 88938032Speter break; 89038032Speter 89138032Speter case 'V': /* DSN ENVID: set "original" envelope id */ 89238032Speter if (!xtextok(optarg)) 89338032Speter { 89438032Speter usrerr("Invalid syntax in -V flag"); 89538032Speter ExitStat = EX_USAGE; 89638032Speter } 89738032Speter else 89864562Sgshapiro { 89938032Speter CurEnv->e_envid = newstr(optarg); 90064562Sgshapiro define(macid("{dsn_envid}", NULL), 90164562Sgshapiro newstr(optarg), CurEnv); 90264562Sgshapiro } 90338032Speter break; 90438032Speter 90538032Speter case 'X': /* traffic log file */ 90664562Sgshapiro dp = drop_privileges(TRUE); 90764562Sgshapiro setstat(dp); 90864562Sgshapiro if (stat(optarg, &traf_st) == 0 && 90964562Sgshapiro S_ISFIFO(traf_st.st_mode)) 91064562Sgshapiro TrafficLogFile = fopen(optarg, "w"); 91164562Sgshapiro else 91264562Sgshapiro TrafficLogFile = fopen(optarg, "a"); 91338032Speter if (TrafficLogFile == NULL) 91438032Speter { 91538032Speter syserr("cannot open %s", optarg); 91638032Speter ExitStat = EX_CANTCREAT; 91738032Speter break; 91838032Speter } 91964562Sgshapiro#if HASSETVBUF 92064562Sgshapiro (void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0); 92164562Sgshapiro#else /* HASSETVBUF */ 92264562Sgshapiro (void) setlinebuf(TrafficLogFile); 92364562Sgshapiro#endif /* HASSETVBUF */ 92438032Speter break; 92538032Speter 92638032Speter /* compatibility flags */ 92738032Speter case 'c': /* connect to non-local mailers */ 92838032Speter case 'i': /* don't let dot stop me */ 92938032Speter case 'm': /* send to me too */ 93038032Speter case 'T': /* set timeout interval */ 93138032Speter case 'v': /* give blow-by-blow description */ 93238032Speter setoption(j, "T", FALSE, TRUE, CurEnv); 93338032Speter break; 93438032Speter 93538032Speter case 'e': /* error message disposition */ 93638032Speter case 'M': /* define macro */ 93738032Speter setoption(j, optarg, FALSE, TRUE, CurEnv); 93838032Speter break; 93938032Speter 94038032Speter case 's': /* save From lines in headers */ 94138032Speter setoption('f', "T", FALSE, TRUE, CurEnv); 94238032Speter break; 94338032Speter 94464562Sgshapiro#ifdef DBM 94538032Speter case 'I': /* initialize alias DBM file */ 94638032Speter OpMode = MD_INITALIAS; 94738032Speter break; 94864562Sgshapiro#endif /* DBM */ 94938032Speter 95064562Sgshapiro#if defined(__osf__) || defined(_AIX3) 95138032Speter case 'x': /* random flag that OSF/1 & AIX mailx passes */ 95238032Speter break; 95364562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */ 95464562Sgshapiro#if defined(sony_news) 95538032Speter case 'E': 95638032Speter case 'J': /* ignore flags for Japanese code conversion 95764562Sgshapiro implemented on Sony NEWS */ 95838032Speter break; 95964562Sgshapiro#endif /* defined(sony_news) */ 96038032Speter 96138032Speter default: 96242575Speter finis(TRUE, EX_USAGE); 96338032Speter break; 96438032Speter } 96538032Speter } 96638032Speter av += optind; 96738032Speter 96864562Sgshapiro if (bitset(SUBMIT_MTA, SubmitMode) && 96964562Sgshapiro bitset(SUBMIT_MSA, SubmitMode)) 97064562Sgshapiro { 97164562Sgshapiro /* sanity check */ 97264562Sgshapiro errno = 0; /* reset to avoid bogus error messages */ 97364562Sgshapiro syserr("Cannot use both -G and -U together"); 97464562Sgshapiro } 97564562Sgshapiro else if (bitset(SUBMIT_MTA, SubmitMode)) 97664562Sgshapiro define(macid("{daemon_flags}", NULL), "CC f", CurEnv); 97764562Sgshapiro else if (bitset(SUBMIT_MSA, SubmitMode)) 97864562Sgshapiro { 97964562Sgshapiro define(macid("{daemon_flags}", NULL), "c u", CurEnv); 98064562Sgshapiro 98164562Sgshapiro /* check for wrong OpMode */ 98264562Sgshapiro if (OpMode != MD_DELIVER && OpMode != MD_SMTP) 98364562Sgshapiro { 98464562Sgshapiro errno = 0; /* reset to avoid bogus error msgs */ 98564562Sgshapiro syserr("Cannot use -U and -b%c", OpMode); 98664562Sgshapiro } 98764562Sgshapiro } 98864562Sgshapiro else 98964562Sgshapiro { 99064562Sgshapiro#if _FFR_DEFAULT_SUBMIT_TO_MSA 99164562Sgshapiro define(macid("{daemon_flags}", NULL), "c u", CurEnv); 99264562Sgshapiro#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */ 99364562Sgshapiro /* EMPTY */ 99464562Sgshapiro#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */ 99564562Sgshapiro } 99664562Sgshapiro 99738032Speter /* 99838032Speter ** Do basic initialization. 99938032Speter ** Read system control file. 100038032Speter ** Extract special fields for local use. 100138032Speter */ 100238032Speter 100338032Speter /* set up ${opMode} for use in config file */ 100438032Speter { 100538032Speter char mbuf[2]; 100638032Speter 100738032Speter mbuf[0] = OpMode; 100838032Speter mbuf[1] = '\0'; 100938032Speter define(MID_OPMODE, newstr(mbuf), CurEnv); 101038032Speter } 101138032Speter 101238032Speter#if XDEBUG 101338032Speter checkfd012("before readcf"); 101464562Sgshapiro#endif /* XDEBUG */ 101538032Speter vendor_pre_defaults(CurEnv); 101664562Sgshapiro 101738032Speter readcf(getcfname(), safecf, CurEnv); 101838032Speter ConfigFileRead = TRUE; 101938032Speter vendor_post_defaults(CurEnv); 102038032Speter 102138032Speter /* Enforce use of local time (null string overrides this) */ 102238032Speter if (TimeZoneSpec == NULL) 102338032Speter unsetenv("TZ"); 102438032Speter else if (TimeZoneSpec[0] != '\0') 102538032Speter setuserenv("TZ", TimeZoneSpec); 102638032Speter else 102738032Speter setuserenv("TZ", NULL); 102838032Speter tzset(); 102938032Speter 103038032Speter /* avoid denial-of-service attacks */ 103138032Speter resetlimits(); 103238032Speter 103338032Speter if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) 103438032Speter { 103538032Speter /* drop privileges -- daemon mode done after socket/bind */ 103664562Sgshapiro dp = drop_privileges(FALSE); 103764562Sgshapiro setstat(dp); 103838032Speter } 103938032Speter 104064562Sgshapiro#if NAMED_BIND 104164562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT]; 104264562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT]; 104364562Sgshapiro#endif /* NAMED_BIND */ 104464562Sgshapiro 104538032Speter /* 104638032Speter ** Find our real host name for future logging. 104738032Speter */ 104838032Speter 104964562Sgshapiro authinfo = getauthinfo(STDIN_FILENO, &forged); 105064562Sgshapiro define('_', authinfo, CurEnv); 105138032Speter 105238032Speter /* suppress error printing if errors mailed back or whatever */ 105338032Speter if (CurEnv->e_errormode != EM_PRINT) 105438032Speter HoldErrs = TRUE; 105538032Speter 105638032Speter /* set up the $=m class now, after .cf has a chance to redefine $m */ 105738032Speter expand("\201m", jbuf, sizeof jbuf, CurEnv); 105838032Speter setclass('m', jbuf); 105938032Speter 106038032Speter /* probe interfaces and locate any additional names */ 106138032Speter if (!DontProbeInterfaces) 106238032Speter load_if_names(); 106338032Speter 106438032Speter if (tTd(0, 1)) 106538032Speter { 106664562Sgshapiro dprintf("\n============ SYSTEM IDENTITY (after readcf) ============"); 106764562Sgshapiro dprintf("\n (short domain name) $w = "); 106838032Speter xputs(macvalue('w', CurEnv)); 106964562Sgshapiro dprintf("\n (canonical domain name) $j = "); 107038032Speter xputs(macvalue('j', CurEnv)); 107164562Sgshapiro dprintf("\n (subdomain name) $m = "); 107238032Speter xputs(macvalue('m', CurEnv)); 107364562Sgshapiro dprintf("\n (node name) $k = "); 107438032Speter xputs(macvalue('k', CurEnv)); 107564562Sgshapiro dprintf("\n========================================================\n\n"); 107638032Speter } 107738032Speter 107838032Speter /* 107938032Speter ** Do more command line checking -- these are things that 108038032Speter ** have to modify the results of reading the config file. 108138032Speter */ 108238032Speter 108338032Speter /* process authorization warnings from command line */ 108438032Speter if (warn_C_flag) 108538032Speter auth_warning(CurEnv, "Processed by %s with -C %s", 108638032Speter RealUserName, ConfFile); 108764562Sgshapiro if (Warn_Q_option && !wordinclass(RealUserName, 't')) 108838032Speter auth_warning(CurEnv, "Processed from queue %s", QueueDir); 108938032Speter 109038032Speter /* check body type for legality */ 109138032Speter if (CurEnv->e_bodytype == NULL) 109264562Sgshapiro /* EMPTY */ 109338032Speter /* nothing */ ; 109438032Speter else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) 109538032Speter SevenBitInput = TRUE; 109638032Speter else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) 109738032Speter SevenBitInput = FALSE; 109838032Speter else 109938032Speter { 110038032Speter usrerr("Illegal body type %s", CurEnv->e_bodytype); 110138032Speter CurEnv->e_bodytype = NULL; 110238032Speter } 110338032Speter 110438032Speter /* tweak default DSN notifications */ 110538032Speter if (DefaultNotify == 0) 110638032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 110738032Speter 110838032Speter /* be sure we don't pick up bogus HOSTALIASES environment variable */ 110964562Sgshapiro if (OpMode == MD_QUEUERUN && RealUid != 0) 111038032Speter (void) unsetenv("HOSTALIASES"); 111138032Speter 111238032Speter /* check for sane configuration level */ 111338032Speter if (ConfigLevel > MAXCONFIGLEVEL) 111438032Speter { 111538032Speter syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", 111638032Speter ConfigLevel, Version, MAXCONFIGLEVEL); 111738032Speter } 111838032Speter 111938032Speter /* need MCI cache to have persistence */ 112038032Speter if (HostStatDir != NULL && MaxMciCache == 0) 112138032Speter { 112238032Speter HostStatDir = NULL; 112338032Speter printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); 112438032Speter } 112538032Speter 112638032Speter /* need HostStatusDir in order to have SingleThreadDelivery */ 112738032Speter if (SingleThreadDelivery && HostStatDir == NULL) 112838032Speter { 112938032Speter SingleThreadDelivery = FALSE; 113038032Speter printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); 113138032Speter } 113238032Speter 113338032Speter /* check for permissions */ 113442575Speter if ((OpMode == MD_DAEMON || 113542575Speter OpMode == MD_FGDAEMON || 113642575Speter OpMode == MD_PURGESTAT) && 113742575Speter RealUid != 0 && 113842575Speter RealUid != TrustedUid) 113938032Speter { 114038032Speter if (LogLevel > 1) 114138032Speter sm_syslog(LOG_ALERT, NOQID, 114264562Sgshapiro "user %d attempted to %s", 114364562Sgshapiro RealUid, 114464562Sgshapiro OpMode != MD_PURGESTAT ? "run daemon" 114564562Sgshapiro : "purge host status"); 114638032Speter usrerr("Permission denied"); 114742575Speter finis(FALSE, EX_USAGE); 114838032Speter } 114953696Speter if (OpMode == MD_INITALIAS && 115053696Speter RealUid != 0 && 115153696Speter RealUid != TrustedUid && 115253696Speter !wordinclass(RealUserName, 't')) 115353696Speter { 115453696Speter if (LogLevel > 1) 115553696Speter sm_syslog(LOG_ALERT, NOQID, 115653696Speter "user %d attempted to rebuild the alias map", 115753696Speter RealUid); 115864562Sgshapiro usrerr("Permission denied"); 115964562Sgshapiro finis(FALSE, EX_USAGE); 116064562Sgshapiro } 116138032Speter 116238032Speter if (MeToo) 116338032Speter BlankEnvelope.e_flags |= EF_METOO; 116438032Speter 116538032Speter switch (OpMode) 116638032Speter { 116738032Speter case MD_TEST: 116838032Speter /* don't have persistent host status in test mode */ 116938032Speter HostStatDir = NULL; 117038032Speter if (Verbose == 0) 117138032Speter Verbose = 2; 117238032Speter CurEnv->e_errormode = EM_PRINT; 117338032Speter HoldErrs = FALSE; 117438032Speter break; 117538032Speter 117638032Speter case MD_VERIFY: 117738032Speter CurEnv->e_errormode = EM_PRINT; 117838032Speter HoldErrs = FALSE; 117938032Speter /* arrange to exit cleanly on hangup signal */ 118038032Speter if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 118164562Sgshapiro (void) setsignal(SIGHUP, intsig); 118238032Speter break; 118338032Speter 118438032Speter case MD_FGDAEMON: 118538032Speter run_in_foreground = TRUE; 118638032Speter OpMode = MD_DAEMON; 118764562Sgshapiro /* FALLTHROUGH */ 118838032Speter 118938032Speter case MD_DAEMON: 119038032Speter vendor_daemon_setup(CurEnv); 119138032Speter 119238032Speter /* remove things that don't make sense in daemon mode */ 119338032Speter FullName = NULL; 119438032Speter GrabTo = FALSE; 119538032Speter 119638032Speter /* arrange to restart on hangup signal */ 119738032Speter if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') 119838032Speter sm_syslog(LOG_WARNING, NOQID, 119964562Sgshapiro "daemon invoked without full pathname; kill -1 won't work"); 120064562Sgshapiro (void) setsignal(SIGHUP, sighup); 120138032Speter 120238032Speter /* workaround: can't seem to release the signal in the parent */ 120364562Sgshapiro (void) releasesignal(SIGHUP); 120438032Speter break; 120538032Speter 120638032Speter case MD_INITALIAS: 120738032Speter Verbose = 2; 120838032Speter CurEnv->e_errormode = EM_PRINT; 120938032Speter HoldErrs = FALSE; 121064562Sgshapiro /* FALLTHROUGH */ 121138032Speter 121238032Speter default: 121338032Speter /* arrange to exit cleanly on hangup signal */ 121438032Speter if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 121564562Sgshapiro (void) setsignal(SIGHUP, intsig); 121638032Speter break; 121738032Speter } 121838032Speter 121938032Speter /* special considerations for FullName */ 122038032Speter if (FullName != NULL) 122138032Speter { 122238032Speter char *full = NULL; 122338032Speter 122438032Speter /* full names can't have newlines */ 122564562Sgshapiro if (strchr(FullName, '\n') != NULL) 122638032Speter { 122738032Speter FullName = full = newstr(denlstring(FullName, TRUE, TRUE)); 122838032Speter } 122938032Speter /* check for characters that may have to be quoted */ 123038032Speter if (!rfc822_string(FullName)) 123138032Speter { 123238032Speter /* 123338032Speter ** Quote a full name with special characters 123438032Speter ** as a comment so crackaddr() doesn't destroy 123538032Speter ** the name portion of the address. 123638032Speter */ 123738032Speter FullName = addquotes(FullName); 123838032Speter if (full != NULL) 123938032Speter free(full); 124038032Speter } 124138032Speter } 124238032Speter 124338032Speter /* do heuristic mode adjustment */ 124438032Speter if (Verbose) 124538032Speter { 124638032Speter /* turn off noconnect option */ 124738032Speter setoption('c', "F", TRUE, FALSE, CurEnv); 124838032Speter 124938032Speter /* turn on interactive delivery */ 125038032Speter setoption('d', "", TRUE, FALSE, CurEnv); 125138032Speter } 125238032Speter 125342575Speter#ifdef VENDOR_CODE 125442575Speter /* check for vendor mismatch */ 125542575Speter if (VendorCode != VENDOR_CODE) 125642575Speter { 125742575Speter message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s", 125842575Speter getvendor(VENDOR_CODE), getvendor(VendorCode)); 125942575Speter } 126064562Sgshapiro#endif /* VENDOR_CODE */ 126164562Sgshapiro 126238032Speter /* check for out of date configuration level */ 126338032Speter if (ConfigLevel < MAXCONFIGLEVEL) 126438032Speter { 126538032Speter message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d", 126638032Speter Version, MAXCONFIGLEVEL, ConfigLevel); 126738032Speter } 126838032Speter 126938032Speter if (ConfigLevel < 3) 127038032Speter UseErrorsTo = TRUE; 127138032Speter 127238032Speter /* set options that were previous macros */ 127338032Speter if (SmtpGreeting == NULL) 127438032Speter { 127538032Speter if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL) 127638032Speter SmtpGreeting = newstr(p); 127738032Speter else 127838032Speter SmtpGreeting = "\201j Sendmail \201v ready at \201b"; 127938032Speter } 128038032Speter if (UnixFromLine == NULL) 128138032Speter { 128238032Speter if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) 128338032Speter UnixFromLine = newstr(p); 128438032Speter else 128538032Speter UnixFromLine = "From \201g \201d"; 128638032Speter } 128764562Sgshapiro SmtpError[0] = '\0'; 128838032Speter 128938032Speter /* our name for SMTP codes */ 129038032Speter expand("\201j", jbuf, sizeof jbuf, CurEnv); 129138032Speter MyHostName = jbuf; 129238032Speter if (strchr(jbuf, '.') == NULL) 129338032Speter message("WARNING: local host name (%s) is not qualified; fix $j in config file", 129438032Speter jbuf); 129538032Speter 129638032Speter /* make certain that this name is part of the $=w class */ 129738032Speter setclass('w', MyHostName); 129838032Speter 129938032Speter /* the indices of built-in mailers */ 130038032Speter st = stab("local", ST_MAILER, ST_FIND); 130138032Speter if (st != NULL) 130238032Speter LocalMailer = st->s_mailer; 130338032Speter else if (OpMode != MD_TEST || !warn_C_flag) 130438032Speter syserr("No local mailer defined"); 130538032Speter 130638032Speter st = stab("prog", ST_MAILER, ST_FIND); 130738032Speter if (st == NULL) 130838032Speter syserr("No prog mailer defined"); 130938032Speter else 131038032Speter { 131138032Speter ProgMailer = st->s_mailer; 131238032Speter clrbitn(M_MUSER, ProgMailer->m_flags); 131338032Speter } 131438032Speter 131538032Speter st = stab("*file*", ST_MAILER, ST_FIND); 131638032Speter if (st == NULL) 131738032Speter syserr("No *file* mailer defined"); 131838032Speter else 131938032Speter { 132038032Speter FileMailer = st->s_mailer; 132138032Speter clrbitn(M_MUSER, FileMailer->m_flags); 132238032Speter } 132338032Speter 132438032Speter st = stab("*include*", ST_MAILER, ST_FIND); 132538032Speter if (st == NULL) 132638032Speter syserr("No *include* mailer defined"); 132738032Speter else 132838032Speter InclMailer = st->s_mailer; 132938032Speter 133038032Speter if (ConfigLevel < 6) 133138032Speter { 133238032Speter /* heuristic tweaking of local mailer for back compat */ 133338032Speter if (LocalMailer != NULL) 133438032Speter { 133538032Speter setbitn(M_ALIASABLE, LocalMailer->m_flags); 133638032Speter setbitn(M_HASPWENT, LocalMailer->m_flags); 133738032Speter setbitn(M_TRYRULESET5, LocalMailer->m_flags); 133838032Speter setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); 133938032Speter setbitn(M_CHECKPROG, LocalMailer->m_flags); 134038032Speter setbitn(M_CHECKFILE, LocalMailer->m_flags); 134138032Speter setbitn(M_CHECKUDB, LocalMailer->m_flags); 134238032Speter } 134338032Speter if (ProgMailer != NULL) 134438032Speter setbitn(M_RUNASRCPT, ProgMailer->m_flags); 134538032Speter if (FileMailer != NULL) 134638032Speter setbitn(M_RUNASRCPT, FileMailer->m_flags); 134738032Speter } 134838032Speter if (ConfigLevel < 7) 134938032Speter { 135038032Speter if (LocalMailer != NULL) 135138032Speter setbitn(M_VRFY250, LocalMailer->m_flags); 135238032Speter if (ProgMailer != NULL) 135338032Speter setbitn(M_VRFY250, ProgMailer->m_flags); 135438032Speter if (FileMailer != NULL) 135538032Speter setbitn(M_VRFY250, FileMailer->m_flags); 135638032Speter } 135738032Speter 135838032Speter /* MIME Content-Types that cannot be transfer encoded */ 135938032Speter setclass('n', "multipart/signed"); 136038032Speter 136138032Speter /* MIME message/xxx subtypes that can be treated as messages */ 136238032Speter setclass('s', "rfc822"); 136338032Speter 136438032Speter /* MIME Content-Transfer-Encodings that can be encoded */ 136538032Speter setclass('e', "7bit"); 136638032Speter setclass('e', "8bit"); 136738032Speter setclass('e', "binary"); 136838032Speter 136938032Speter#ifdef USE_B_CLASS 137038032Speter /* MIME Content-Types that should be treated as binary */ 137138032Speter setclass('b', "image"); 137238032Speter setclass('b', "audio"); 137338032Speter setclass('b', "video"); 137438032Speter setclass('b', "application/octet-stream"); 137564562Sgshapiro#endif /* USE_B_CLASS */ 137638032Speter 137742575Speter /* MIME headers which have fields to check for overflow */ 137842575Speter setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition"); 137942575Speter setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type"); 138042575Speter 138142575Speter /* MIME headers to check for length overflow */ 138242575Speter setclass(macid("{checkMIMETextHeaders}", NULL), "content-description"); 138342575Speter 138442575Speter /* MIME headers to check for overflow and rebalance */ 138542575Speter setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition"); 138642575Speter setclass(macid("{checkMIMEHeaders}", NULL), "content-id"); 138742575Speter setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding"); 138842575Speter setclass(macid("{checkMIMEHeaders}", NULL), "content-type"); 138942575Speter setclass(macid("{checkMIMEHeaders}", NULL), "mime-version"); 139042575Speter 139164562Sgshapiro /* Macros to save in the qf file -- don't remove any */ 139264562Sgshapiro setclass(macid("{persistentMacros}", NULL), "r"); 139364562Sgshapiro setclass(macid("{persistentMacros}", NULL), "s"); 139464562Sgshapiro setclass(macid("{persistentMacros}", NULL), "_"); 139564562Sgshapiro setclass(macid("{persistentMacros}", NULL), "{if_addr}"); 139664562Sgshapiro setclass(macid("{persistentMacros}", NULL), "{daemon_flags}"); 139764562Sgshapiro setclass(macid("{persistentMacros}", NULL), "{client_flags}"); 139864562Sgshapiro 139938032Speter /* operate in queue directory */ 140038032Speter if (QueueDir == NULL) 140138032Speter { 140238032Speter if (OpMode != MD_TEST) 140338032Speter { 140438032Speter syserr("QueueDirectory (Q) option must be set"); 140538032Speter ExitStat = EX_CONFIG; 140638032Speter } 140738032Speter } 140838032Speter else 140938032Speter { 141064562Sgshapiro /* 141164562Sgshapiro ** If multiple queues wildcarded, use one for 141264562Sgshapiro ** the daemon's home. Note that this preconditions 141364562Sgshapiro ** a wildcarded QueueDir to a real pathname. 141464562Sgshapiro */ 141564562Sgshapiro 141664562Sgshapiro if (OpMode != MD_TEST) 141764562Sgshapiro multiqueue_cache(); 141838032Speter } 141938032Speter 142038032Speter /* check host status directory for validity */ 142138032Speter if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) 142238032Speter { 142338032Speter /* cannot use this value */ 142438032Speter if (tTd(0, 2)) 142564562Sgshapiro dprintf("Cannot use HostStatusDirectory = %s: %s\n", 142638032Speter HostStatDir, errstring(errno)); 142738032Speter HostStatDir = NULL; 142838032Speter } 142938032Speter 143064562Sgshapiro#if QUEUE 143164562Sgshapiro if (OpMode == MD_QUEUERUN && RealUid != 0 && 143264562Sgshapiro bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) 143338032Speter { 143438032Speter struct stat stbuf; 143538032Speter 143638032Speter /* check to see if we own the queue directory */ 143738032Speter if (stat(".", &stbuf) < 0) 143838032Speter syserr("main: cannot stat %s", QueueDir); 143938032Speter if (stbuf.st_uid != RealUid) 144038032Speter { 144138032Speter /* nope, really a botch */ 144238032Speter usrerr("You do not have permission to process the queue"); 144342575Speter finis(FALSE, EX_NOPERM); 144438032Speter } 144538032Speter } 144664562Sgshapiro#endif /* QUEUE */ 144738032Speter 144864562Sgshapiro#if _FFR_MILTER 144964562Sgshapiro /* sanity checks on milter filters */ 145064562Sgshapiro if (OpMode == MD_DAEMON || OpMode == MD_SMTP) 145164562Sgshapiro milter_parse_list(InputFilterList, InputFilters, MAXFILTERS); 145264562Sgshapiro#endif /* _FFR_MILTER */ 145364562Sgshapiro 145438032Speter /* if we've had errors so far, exit now */ 145538032Speter if (ExitStat != EX_OK && OpMode != MD_TEST) 145642575Speter finis(FALSE, ExitStat); 145738032Speter 145838032Speter#if XDEBUG 145938032Speter checkfd012("before main() initmaps"); 146064562Sgshapiro#endif /* XDEBUG */ 146138032Speter 146238032Speter /* 146338032Speter ** Do operation-mode-dependent initialization. 146438032Speter */ 146538032Speter 146638032Speter switch (OpMode) 146738032Speter { 146838032Speter case MD_PRINT: 146938032Speter /* print the queue */ 147038032Speter#if QUEUE 147138032Speter dropenvelope(CurEnv, TRUE); 147264562Sgshapiro (void) setsignal(SIGPIPE, quiesce); 147338032Speter printqueue(); 147442575Speter finis(FALSE, EX_OK); 147538032Speter#else /* QUEUE */ 147638032Speter usrerr("No queue to print"); 147764562Sgshapiro finis(FALSE, EX_UNAVAILABLE); 147838032Speter#endif /* QUEUE */ 147942575Speter break; 148038032Speter 148138032Speter case MD_HOSTSTAT: 148264562Sgshapiro (void) setsignal(SIGPIPE, quiesce); 148364562Sgshapiro (void) mci_traverse_persistent(mci_print_persistent, NULL); 148442575Speter finis(FALSE, EX_OK); 148564562Sgshapiro break; 148638032Speter 148738032Speter case MD_PURGESTAT: 148864562Sgshapiro (void) mci_traverse_persistent(mci_purge_persistent, NULL); 148942575Speter finis(FALSE, EX_OK); 149064562Sgshapiro break; 149138032Speter 149238032Speter case MD_INITALIAS: 149342575Speter /* initialize maps */ 149464562Sgshapiro initmaps(); 149542575Speter finis(FALSE, ExitStat); 149642575Speter break; 149738032Speter 149838032Speter case MD_SMTP: 149938032Speter case MD_DAEMON: 150038032Speter /* reset DSN parameters */ 150138032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 150264562Sgshapiro define(macid("{dsn_notify}", NULL), NULL, CurEnv); 150338032Speter CurEnv->e_envid = NULL; 150464562Sgshapiro define(macid("{dsn_envid}", NULL), NULL, CurEnv); 150538032Speter CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); 150664562Sgshapiro define(macid("{dsn_ret}", NULL), NULL, CurEnv); 150738032Speter 150842575Speter /* don't open maps for daemon -- done below in child */ 150938032Speter break; 151038032Speter } 151138032Speter 151238032Speter if (tTd(0, 15)) 151338032Speter { 151438032Speter /* print configuration table (or at least part of it) */ 151538032Speter if (tTd(0, 90)) 151638032Speter printrules(); 151738032Speter for (i = 0; i < MAXMAILERS; i++) 151838032Speter { 151938032Speter if (Mailer[i] != NULL) 152038032Speter printmailer(Mailer[i]); 152138032Speter } 152238032Speter } 152338032Speter 152438032Speter /* 152538032Speter ** Switch to the main envelope. 152638032Speter */ 152738032Speter 152838032Speter CurEnv = newenvelope(&MainEnvelope, CurEnv); 152938032Speter MainEnvelope.e_flags = BlankEnvelope.e_flags; 153038032Speter 153138032Speter /* 153238032Speter ** If test mode, read addresses from stdin and process. 153338032Speter */ 153438032Speter 153538032Speter if (OpMode == MD_TEST) 153638032Speter { 153738032Speter char buf[MAXLINE]; 153838032Speter 153938032Speter if (isatty(fileno(stdin))) 154038032Speter Verbose = 2; 154138032Speter 154238032Speter if (Verbose) 154338032Speter { 154438032Speter printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); 154538032Speter printf("Enter <ruleset> <address>\n"); 154638032Speter } 154738032Speter if (setjmp(TopFrame) > 0) 154838032Speter printf("\n"); 154938032Speter (void) setsignal(SIGINT, intindebug); 155038032Speter for (;;) 155138032Speter { 155238032Speter if (Verbose == 2) 155338032Speter printf("> "); 155438032Speter (void) fflush(stdout); 155538032Speter if (fgets(buf, sizeof buf, stdin) == NULL) 155664562Sgshapiro testmodeline("/quit", CurEnv); 155738032Speter p = strchr(buf, '\n'); 155838032Speter if (p != NULL) 155938032Speter *p = '\0'; 156038032Speter if (Verbose < 2) 156138032Speter printf("> %s\n", buf); 156238032Speter testmodeline(buf, CurEnv); 156338032Speter } 156438032Speter } 156538032Speter 156664562Sgshapiro#if SMTP 156764562Sgshapiro# if STARTTLS 156838032Speter /* 156964562Sgshapiro ** basic TLS initialization 157064562Sgshapiro ** ignore result for now 157164562Sgshapiro */ 157264562Sgshapiro SSL_library_init(); 157364562Sgshapiro SSL_load_error_strings(); 157464562Sgshapiro# if 0 157564562Sgshapiro /* this is currently a macro for SSL_library_init */ 157664562Sgshapiro SSLeay_add_ssl_algorithms(); 157764562Sgshapiro# endif /* 0 */ 157864562Sgshapiro 157964562Sgshapiro /* initialize PRNG */ 158064562Sgshapiro tls_rand_init(RandFile, 7); 158164562Sgshapiro 158264562Sgshapiro# endif /* STARTTLS */ 158364562Sgshapiro#endif /* SMTP */ 158464562Sgshapiro 158564562Sgshapiro#if QUEUE 158664562Sgshapiro /* 158738032Speter ** If collecting stuff from the queue, go start doing that. 158838032Speter */ 158938032Speter 159064562Sgshapiro if (OpMode == MD_QUEUERUN && QueueIntvl == 0) 159138032Speter { 159264562Sgshapiro# if SMTP 159364562Sgshapiro# if STARTTLS 159464562Sgshapiro { 159564562Sgshapiro /* init TLS for client, ignore result for now */ 159664562Sgshapiro (void) initclttls(); 159764562Sgshapiro } 159864562Sgshapiro# endif /* STARTTLS */ 159964562Sgshapiro# endif /* SMTP */ 160038032Speter (void) runqueue(FALSE, Verbose); 160142575Speter finis(TRUE, ExitStat); 160238032Speter } 160364562Sgshapiro#endif /* QUEUE */ 160438032Speter 160538032Speter /* 160638032Speter ** If a daemon, wait for a request. 160738032Speter ** getrequests will always return in a child. 160838032Speter ** If we should also be processing the queue, start 160938032Speter ** doing it in background. 161038032Speter ** We check for any errors that might have happened 161138032Speter ** during startup. 161238032Speter */ 161338032Speter 161438032Speter if (OpMode == MD_DAEMON || QueueIntvl != 0) 161538032Speter { 161638032Speter char dtype[200]; 161738032Speter 161838032Speter if (!run_in_foreground && !tTd(99, 100)) 161938032Speter { 162038032Speter /* put us in background */ 162138032Speter i = fork(); 162238032Speter if (i < 0) 162338032Speter syserr("daemon: cannot fork"); 162438032Speter if (i != 0) 162542575Speter finis(FALSE, EX_OK); 162638032Speter 162738032Speter /* disconnect from our controlling tty */ 162838032Speter disconnect(2, CurEnv); 162938032Speter } 163038032Speter 163138032Speter dtype[0] = '\0'; 163238032Speter if (OpMode == MD_DAEMON) 163364562Sgshapiro (void) strlcat(dtype, "+SMTP", sizeof dtype); 163438032Speter if (QueueIntvl != 0) 163538032Speter { 163664562Sgshapiro (void) strlcat(dtype, "+queueing@", sizeof dtype); 163764562Sgshapiro (void) strlcat(dtype, pintvl(QueueIntvl, TRUE), 163864562Sgshapiro sizeof dtype); 163938032Speter } 164038032Speter if (tTd(0, 1)) 164164562Sgshapiro (void) strlcat(dtype, "+debugging", sizeof dtype); 164238032Speter 164338032Speter sm_syslog(LOG_INFO, NOQID, 164464562Sgshapiro "starting daemon (%s): %s", Version, dtype + 1); 164538032Speter#ifdef XLA 164638032Speter xla_create_file(); 164764562Sgshapiro#endif /* XLA */ 164838032Speter 164964562Sgshapiro /* save daemon type in a macro for possible PidFile use */ 165064562Sgshapiro define(macid("{daemon_info}", NULL), 165164562Sgshapiro newstr(dtype + 1), &BlankEnvelope); 165264562Sgshapiro 165364562Sgshapiro /* save queue interval in a macro for possible PidFile use */ 165464562Sgshapiro define(macid("{queue_interval}", NULL), 165564562Sgshapiro newstr(pintvl(QueueIntvl, TRUE)), CurEnv); 165664562Sgshapiro 165764562Sgshapiro#if QUEUE 165864562Sgshapiro if (QueueIntvl != 0) 165938032Speter { 166038032Speter (void) runqueue(TRUE, FALSE); 166138032Speter if (OpMode != MD_DAEMON) 166238032Speter { 166364562Sgshapiro /* write the pid to file */ 166464562Sgshapiro log_sendmail_pid(CurEnv); 166538032Speter for (;;) 166638032Speter { 166764562Sgshapiro (void) pause(); 166838032Speter if (DoQueueRun) 166938032Speter (void) runqueue(TRUE, FALSE); 167038032Speter } 167138032Speter } 167238032Speter } 167364562Sgshapiro#endif /* QUEUE */ 167438032Speter dropenvelope(CurEnv, TRUE); 167538032Speter 167638032Speter#if DAEMON 167764562Sgshapiro# if STARTTLS 167864562Sgshapiro /* init TLS for server, ignore result for now */ 167964562Sgshapiro (void) initsrvtls(); 168064562Sgshapiro# endif /* STARTTLS */ 168164562Sgshapiro p_flags = getrequests(CurEnv); 168238032Speter 168338032Speter /* drop privileges */ 168438032Speter (void) drop_privileges(FALSE); 168538032Speter 168638032Speter /* at this point we are in a child: reset state */ 168738032Speter (void) newenvelope(CurEnv, CurEnv); 168838032Speter 168938032Speter /* 169038032Speter ** Get authentication data 169138032Speter */ 169238032Speter 169364562Sgshapiro authinfo = getauthinfo(fileno(InChannel), &forged); 169464562Sgshapiro define('_', authinfo, &BlankEnvelope); 169538032Speter#endif /* DAEMON */ 169638032Speter } 169738032Speter 169864562Sgshapiro if (LogLevel > 9) 169964562Sgshapiro { 170064562Sgshapiro /* log connection information */ 170164562Sgshapiro sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo); 170264562Sgshapiro } 170364562Sgshapiro 170464562Sgshapiro#if SMTP 170538032Speter /* 170638032Speter ** If running SMTP protocol, start collecting and executing 170738032Speter ** commands. This will never return. 170838032Speter */ 170938032Speter 171038032Speter if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 171138032Speter { 171238032Speter char pbuf[20]; 171338032Speter 171438032Speter /* 171538032Speter ** Save some macros for check_* rulesets. 171638032Speter */ 171738032Speter 171838032Speter if (forged) 171938032Speter { 172038032Speter char ipbuf[103]; 172138032Speter 172264562Sgshapiro (void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]", 172364562Sgshapiro anynet_ntoa(&RealHostAddr)); 172438032Speter define(macid("{client_name}", NULL), 172538032Speter newstr(ipbuf), &BlankEnvelope); 172664562Sgshapiro define(macid("{client_resolve}", NULL), 172764562Sgshapiro "FORGED", &BlankEnvelope); 172838032Speter } 172938032Speter else 173064562Sgshapiro define(macid("{client_name}", NULL), RealHostName, 173164562Sgshapiro &BlankEnvelope); 173238032Speter define(macid("{client_addr}", NULL), 173338032Speter newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); 173464562Sgshapiro (void)sm_getla(&BlankEnvelope); 173538032Speter 173664562Sgshapiro switch(RealHostAddr.sa.sa_family) 173764562Sgshapiro { 173864562Sgshapiro# if NETINET 173964562Sgshapiro case AF_INET: 174064562Sgshapiro (void) snprintf(pbuf, sizeof pbuf, "%d", 174164562Sgshapiro RealHostAddr.sin.sin_port); 174264562Sgshapiro break; 174364562Sgshapiro# endif /* NETINET */ 174464562Sgshapiro# if NETINET6 174564562Sgshapiro case AF_INET6: 174664562Sgshapiro (void) snprintf(pbuf, sizeof pbuf, "%d", 174764562Sgshapiro RealHostAddr.sin6.sin6_port); 174864562Sgshapiro break; 174964562Sgshapiro# endif /* NETINET6 */ 175064562Sgshapiro default: 175164562Sgshapiro (void) snprintf(pbuf, sizeof pbuf, "0"); 175264562Sgshapiro break; 175364562Sgshapiro } 175464562Sgshapiro define(macid("{client_port}", NULL), 175564562Sgshapiro newstr(pbuf), &BlankEnvelope); 175642575Speter 175764562Sgshapiro#if SASL 175864562Sgshapiro /* give a syserr or just disable AUTH ? */ 175964562Sgshapiro if (sasl_server_init(srvcallbacks, "Sendmail") != SASL_OK) 176064562Sgshapiro syserr("!sasl_server_init failed!"); 176164562Sgshapiro#endif /* SASL */ 176264562Sgshapiro 176338032Speter if (OpMode == MD_DAEMON) 176438032Speter { 176538032Speter /* validate the connection */ 176638032Speter HoldErrs = TRUE; 176738032Speter nullserver = validate_connection(&RealHostAddr, 176838032Speter RealHostName, CurEnv); 176938032Speter HoldErrs = FALSE; 177038032Speter } 177164562Sgshapiro else if (p_flags == NULL) 177264562Sgshapiro { 177364562Sgshapiro p_flags = (BITMAP256 *) xalloc(sizeof *p_flags); 177464562Sgshapiro clrbitmap(p_flags); 177564562Sgshapiro } 177664562Sgshapiro# if STARTTLS 177764562Sgshapiro if (OpMode == MD_SMTP) 177864562Sgshapiro (void) initsrvtls(); 177964562Sgshapiro# endif /* STARTTLS */ 178064562Sgshapiro smtp(nullserver, *p_flags, CurEnv); 178138032Speter } 178264562Sgshapiro#endif /* SMTP */ 178338032Speter 178438032Speter clearenvelope(CurEnv, FALSE); 178538032Speter if (OpMode == MD_VERIFY) 178638032Speter { 178764562Sgshapiro set_delivery_mode(SM_VERIFY, CurEnv); 178838032Speter PostMasterCopy = NULL; 178938032Speter } 179038032Speter else 179138032Speter { 179238032Speter /* interactive -- all errors are global */ 179338032Speter CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; 179438032Speter } 179538032Speter 179638032Speter /* 179738032Speter ** Do basic system initialization and set the sender 179838032Speter */ 179938032Speter 180038032Speter initsys(CurEnv); 180164562Sgshapiro define(macid("{ntries}", NULL), "0", CurEnv); 180264562Sgshapiro setsender(from, CurEnv, NULL, '\0', FALSE); 180364562Sgshapiro if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') && 180464562Sgshapiro (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) || 180564562Sgshapiro strcmp(CurEnv->e_from.q_user, RealUserName) != 0)) 180664562Sgshapiro { 180738032Speter auth_warning(CurEnv, "%s set sender to %s using -%c", 180838032Speter RealUserName, from, warn_f_flag); 180964562Sgshapiro#if SASL 181064562Sgshapiro auth = FALSE; 181164562Sgshapiro#endif /* SASL */ 181264562Sgshapiro } 181364562Sgshapiro if (auth) 181464562Sgshapiro { 181564562Sgshapiro char *fv; 181664562Sgshapiro 181764562Sgshapiro /* set the initial sender for AUTH= to $f@$j */ 181864562Sgshapiro fv = macvalue('f', CurEnv); 181964562Sgshapiro if (fv == NULL || *fv == '\0') 182064562Sgshapiro CurEnv->e_auth_param = NULL; 182164562Sgshapiro else 182264562Sgshapiro { 182364562Sgshapiro if (strchr(fv, '@') == NULL) 182464562Sgshapiro { 182564562Sgshapiro i = strlen(fv) + strlen(macvalue('j', CurEnv)) 182664562Sgshapiro + 2; 182764562Sgshapiro p = xalloc(i); 182864562Sgshapiro (void) snprintf(p, i, "%s@%s", fv, 182964562Sgshapiro macvalue('j', CurEnv)); 183064562Sgshapiro } 183164562Sgshapiro else 183264562Sgshapiro p = newstr(fv); 183364562Sgshapiro CurEnv->e_auth_param = newstr(xtextify(p, NULL)); 183464562Sgshapiro } 183564562Sgshapiro } 183638032Speter if (macvalue('s', CurEnv) == NULL) 183738032Speter define('s', RealHostName, CurEnv); 183838032Speter 183938032Speter if (*av == NULL && !GrabTo) 184038032Speter { 184164562Sgshapiro CurEnv->e_to = NULL; 184238032Speter CurEnv->e_flags |= EF_GLOBALERRS; 184364562Sgshapiro HoldErrs = FALSE; 184438032Speter usrerr("Recipient names must be specified"); 184538032Speter 184638032Speter /* collect body for UUCP return */ 184738032Speter if (OpMode != MD_VERIFY) 184838032Speter collect(InChannel, FALSE, NULL, CurEnv); 184964562Sgshapiro finis(TRUE, EX_USAGE); 185038032Speter } 185138032Speter 185238032Speter /* 185338032Speter ** Scan argv and deliver the message to everyone. 185438032Speter */ 185538032Speter 185638032Speter sendtoargv(av, CurEnv); 185738032Speter 185838032Speter /* if we have had errors sofar, arrange a meaningful exit stat */ 185938032Speter if (Errors > 0 && ExitStat == EX_OK) 186038032Speter ExitStat = EX_USAGE; 186138032Speter 186238032Speter#if _FFR_FIX_DASHT 186338032Speter /* 186438032Speter ** If using -t, force not sending to argv recipients, even 186538032Speter ** if they are mentioned in the headers. 186638032Speter */ 186738032Speter 186838032Speter if (GrabTo) 186938032Speter { 187038032Speter ADDRESS *q; 187164562Sgshapiro 187238032Speter for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 187364562Sgshapiro q->q_state = QS_REMOVED; 187438032Speter } 187564562Sgshapiro#endif /* _FFR_FIX_DASHT */ 187638032Speter 187738032Speter /* 187838032Speter ** Read the input mail. 187938032Speter */ 188038032Speter 188138032Speter CurEnv->e_to = NULL; 188238032Speter if (OpMode != MD_VERIFY || GrabTo) 188338032Speter { 188464562Sgshapiro int savederrors = Errors; 188538032Speter long savedflags = CurEnv->e_flags & EF_FATALERRS; 188638032Speter 188738032Speter CurEnv->e_flags |= EF_GLOBALERRS; 188838032Speter CurEnv->e_flags &= ~EF_FATALERRS; 188964562Sgshapiro Errors = 0; 189064562Sgshapiro buffer_errors(); 189138032Speter collect(InChannel, FALSE, NULL, CurEnv); 189238032Speter 189364562Sgshapiro /* header checks failed */ 189464562Sgshapiro if (Errors > 0) 189564562Sgshapiro { 189664562Sgshapiro /* Log who the mail would have gone to */ 189764562Sgshapiro if (LogLevel > 8 && CurEnv->e_message != NULL && 189864562Sgshapiro !GrabTo) 189964562Sgshapiro { 190064562Sgshapiro ADDRESS *a; 190164562Sgshapiro 190264562Sgshapiro for (a = CurEnv->e_sendqueue; 190364562Sgshapiro a != NULL; 190464562Sgshapiro a = a->q_next) 190564562Sgshapiro { 190664562Sgshapiro if (!QS_IS_UNDELIVERED(a->q_state)) 190764562Sgshapiro continue; 190864562Sgshapiro 190964562Sgshapiro CurEnv->e_to = a->q_paddr; 191064562Sgshapiro logdelivery(NULL, NULL, NULL, 191164562Sgshapiro CurEnv->e_message, 191264562Sgshapiro NULL, (time_t) 0, CurEnv); 191364562Sgshapiro } 191464562Sgshapiro CurEnv->e_to = NULL; 191564562Sgshapiro } 191664562Sgshapiro flush_errors(TRUE); 191764562Sgshapiro finis(TRUE, ExitStat); 191864562Sgshapiro /* NOTREACHED */ 191964562Sgshapiro return -1; 192064562Sgshapiro } 192164562Sgshapiro 192238032Speter /* bail out if message too large */ 192338032Speter if (bitset(EF_CLRQUEUE, CurEnv->e_flags)) 192438032Speter { 192564562Sgshapiro finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR); 192664562Sgshapiro /* NOTREACHED */ 192738032Speter return -1; 192838032Speter } 192964562Sgshapiro Errors = savederrors; 193038032Speter CurEnv->e_flags |= savedflags; 193138032Speter } 193238032Speter errno = 0; 193338032Speter 193438032Speter if (tTd(1, 1)) 193564562Sgshapiro dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); 193638032Speter 193738032Speter /* 193838032Speter ** Actually send everything. 193938032Speter ** If verifying, just ack. 194038032Speter */ 194138032Speter 194264562Sgshapiro CurEnv->e_from.q_state = QS_SENDER; 194338032Speter if (tTd(1, 5)) 194438032Speter { 194564562Sgshapiro dprintf("main: QS_SENDER "); 194638032Speter printaddr(&CurEnv->e_from, FALSE); 194738032Speter } 194838032Speter CurEnv->e_to = NULL; 194964562Sgshapiro CurrentLA = sm_getla(CurEnv); 195038032Speter GrabTo = FALSE; 195164562Sgshapiro#if NAMED_BIND 195264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 195364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 195464562Sgshapiro#endif /* NAMED_BIND */ 195538032Speter sendall(CurEnv, SM_DEFAULT); 195638032Speter 195738032Speter /* 195838032Speter ** All done. 195938032Speter ** Don't send return error message if in VERIFY mode. 196038032Speter */ 196138032Speter 196242575Speter finis(TRUE, ExitStat); 196364562Sgshapiro /* NOTREACHED */ 196464562Sgshapiro return ExitStat; 196538032Speter} 196638032Speter 196742575Speter/* ARGSUSED */ 196842575SpeterSIGFUNC_DECL 196942575Speterquiesce(sig) 197042575Speter int sig; 197142575Speter{ 197264562Sgshapiro clear_events(); 197342575Speter finis(FALSE, EX_OK); 197442575Speter} 197538032Speter 197638032Speter/* ARGSUSED */ 197738032SpeterSIGFUNC_DECL 197838032Speterintindebug(sig) 197938032Speter int sig; 198038032Speter{ 198138032Speter longjmp(TopFrame, 1); 198238032Speter return SIGFUNC_RETURN; 198338032Speter} 198438032Speter/* 198538032Speter** FINIS -- Clean up and exit. 198638032Speter** 198738032Speter** Parameters: 198842575Speter** drop -- whether or not to drop CurEnv envelope 198942575Speter** exitstat -- exit status to use for exit() call 199038032Speter** 199138032Speter** Returns: 199238032Speter** never 199338032Speter** 199438032Speter** Side Effects: 199538032Speter** exits sendmail 199638032Speter*/ 199738032Speter 199838032Spetervoid 199942575Speterfinis(drop, exitstat) 200042575Speter bool drop; 200142575Speter volatile int exitstat; 200238032Speter{ 200342575Speter 200438032Speter if (tTd(2, 1)) 200538032Speter { 200664562Sgshapiro dprintf("\n====finis: stat %d e_id=%s e_flags=", 200742575Speter exitstat, 200838032Speter CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); 200938032Speter printenvflags(CurEnv); 201038032Speter } 201138032Speter if (tTd(2, 9)) 201238032Speter printopenfds(FALSE); 201338032Speter 201438032Speter /* if we fail in finis(), just exit */ 201538032Speter if (setjmp(TopFrame) != 0) 201638032Speter { 201738032Speter /* failed -- just give it up */ 201838032Speter goto forceexit; 201938032Speter } 202038032Speter 202138032Speter /* clean up temp files */ 202238032Speter CurEnv->e_to = NULL; 202364562Sgshapiro if (drop) 202464562Sgshapiro { 202564562Sgshapiro if (CurEnv->e_id != NULL) 202664562Sgshapiro dropenvelope(CurEnv, TRUE); 202764562Sgshapiro else 202864562Sgshapiro poststats(StatFile); 202964562Sgshapiro } 203038032Speter 203138032Speter /* flush any cached connections */ 203238032Speter mci_flush(TRUE, NULL); 203338032Speter 203442575Speter /* close maps belonging to this pid */ 203542575Speter closemaps(); 203642575Speter 203764562Sgshapiro#if USERDB 203842575Speter /* close UserDatabase */ 203942575Speter _udbx_close(); 204064562Sgshapiro#endif /* USERDB */ 204142575Speter 204264562Sgshapiro#ifdef XLA 204338032Speter /* clean up extended load average stuff */ 204438032Speter xla_all_end(); 204564562Sgshapiro#endif /* XLA */ 204638032Speter 204738032Speter /* and exit */ 204838032Speter forceexit: 204938032Speter if (LogLevel > 78) 205038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 205164562Sgshapiro "finis, pid=%d", 205264562Sgshapiro getpid()); 205342575Speter if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) 205442575Speter exitstat = EX_OK; 205538032Speter 205664562Sgshapiro sync_queue_time(); 205764562Sgshapiro 205838032Speter /* reset uid for process accounting */ 205938032Speter endpwent(); 206064562Sgshapiro (void) setuid(RealUid); 206142575Speter exit(exitstat); 206238032Speter} 206338032Speter/* 206438032Speter** INTSIG -- clean up on interrupt 206538032Speter** 206664562Sgshapiro** This just arranges to exit. It pessimizes in that it 206738032Speter** may resend a message. 206838032Speter** 206938032Speter** Parameters: 207038032Speter** none. 207138032Speter** 207238032Speter** Returns: 207338032Speter** none. 207438032Speter** 207538032Speter** Side Effects: 207638032Speter** Unlocks the current job. 207738032Speter*/ 207838032Speter 207938032Speter/* ARGSUSED */ 208038032SpeterSIGFUNC_DECL 208138032Speterintsig(sig) 208238032Speter int sig; 208338032Speter{ 208464562Sgshapiro bool drop = FALSE; 208564562Sgshapiro 208664562Sgshapiro clear_events(); 208764562Sgshapiro if (sig != 0 && LogLevel > 79) 208838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); 208938032Speter FileName = NULL; 209042575Speter closecontrolsocket(TRUE); 209138032Speter#ifdef XLA 209238032Speter xla_all_end(); 209364562Sgshapiro#endif /* XLA */ 209464562Sgshapiro 209564562Sgshapiro /* Clean-up on aborted stdin message submission */ 209664562Sgshapiro if (CurEnv->e_id != NULL && 209764562Sgshapiro (OpMode == MD_SMTP || 209864562Sgshapiro OpMode == MD_DELIVER || 209964562Sgshapiro OpMode == MD_ARPAFTP)) 210064562Sgshapiro { 210164562Sgshapiro register ADDRESS *q; 210264562Sgshapiro 210364562Sgshapiro /* don't return an error indication */ 210464562Sgshapiro CurEnv->e_to = NULL; 210564562Sgshapiro CurEnv->e_flags &= ~EF_FATALERRS; 210664562Sgshapiro CurEnv->e_flags |= EF_CLRQUEUE; 210764562Sgshapiro 210864562Sgshapiro /* 210964562Sgshapiro ** Spin through the addresses and 211064562Sgshapiro ** mark them dead to prevent bounces 211164562Sgshapiro */ 211264562Sgshapiro 211364562Sgshapiro for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 211464562Sgshapiro q->q_state = QS_DONTSEND; 211564562Sgshapiro 211664562Sgshapiro /* and don't try to deliver the partial message either */ 211764562Sgshapiro if (InChild) 211864562Sgshapiro ExitStat = EX_QUIT; 211964562Sgshapiro 212064562Sgshapiro drop = TRUE; 212164562Sgshapiro } 212264562Sgshapiro else 212364562Sgshapiro unlockqueue(CurEnv); 212464562Sgshapiro 212564562Sgshapiro finis(drop, EX_OK); 212638032Speter} 212738032Speter/* 212838032Speter** INITMACROS -- initialize the macro system 212938032Speter** 213038032Speter** This just involves defining some macros that are actually 213138032Speter** used internally as metasymbols to be themselves. 213238032Speter** 213338032Speter** Parameters: 213438032Speter** none. 213538032Speter** 213638032Speter** Returns: 213738032Speter** none. 213838032Speter** 213938032Speter** Side Effects: 214038032Speter** initializes several macros to be themselves. 214138032Speter*/ 214238032Speter 214338032Speterstruct metamac MetaMacros[] = 214438032Speter{ 214538032Speter /* LHS pattern matching characters */ 214638032Speter { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, 214738032Speter { '=', MATCHCLASS }, { '~', MATCHNCLASS }, 214838032Speter 214938032Speter /* these are RHS metasymbols */ 215038032Speter { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, 215138032Speter { '>', CALLSUBR }, 215238032Speter 215338032Speter /* the conditional operations */ 215438032Speter { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, 215538032Speter 215638032Speter /* the hostname lookup characters */ 215738032Speter { '[', HOSTBEGIN }, { ']', HOSTEND }, 215838032Speter { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, 215938032Speter 216038032Speter /* miscellaneous control characters */ 216138032Speter { '&', MACRODEXPAND }, 216238032Speter 216338032Speter { '\0' } 216438032Speter}; 216538032Speter 216638032Speter#define MACBINDING(name, mid) \ 216738032Speter stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ 216838032Speter MacroName[mid] = name; 216938032Speter 217038032Spetervoid 217138032Speterinitmacros(e) 217238032Speter register ENVELOPE *e; 217338032Speter{ 217438032Speter register struct metamac *m; 217538032Speter register int c; 217638032Speter char buf[5]; 217738032Speter extern char *MacroName[256]; 217838032Speter 217938032Speter for (m = MetaMacros; m->metaname != '\0'; m++) 218038032Speter { 218138032Speter buf[0] = m->metaval; 218238032Speter buf[1] = '\0'; 218338032Speter define(m->metaname, newstr(buf), e); 218438032Speter } 218538032Speter buf[0] = MATCHREPL; 218638032Speter buf[2] = '\0'; 218738032Speter for (c = '0'; c <= '9'; c++) 218838032Speter { 218938032Speter buf[1] = c; 219038032Speter define(c, newstr(buf), e); 219138032Speter } 219238032Speter 219338032Speter /* set defaults for some macros sendmail will use later */ 219438032Speter define('n', "MAILER-DAEMON", e); 219538032Speter 219638032Speter /* set up external names for some internal macros */ 219738032Speter MACBINDING("opMode", MID_OPMODE); 219838032Speter /*XXX should probably add equivalents for all short macros here XXX*/ 219938032Speter} 220038032Speter/* 220138032Speter** DISCONNECT -- remove our connection with any foreground process 220238032Speter** 220338032Speter** Parameters: 220438032Speter** droplev -- how "deeply" we should drop the line. 220538032Speter** 0 -- ignore signals, mail back errors, make sure 220638032Speter** output goes to stdout. 220764562Sgshapiro** 1 -- also, make stdout go to /dev/null. 220838032Speter** 2 -- also, disconnect from controlling terminal 220938032Speter** (only for daemon mode). 221038032Speter** e -- the current envelope. 221138032Speter** 221238032Speter** Returns: 221338032Speter** none 221438032Speter** 221538032Speter** Side Effects: 221638032Speter** Trys to insure that we are immune to vagaries of 221738032Speter** the controlling tty. 221838032Speter*/ 221938032Speter 222038032Spetervoid 222138032Speterdisconnect(droplev, e) 222238032Speter int droplev; 222338032Speter register ENVELOPE *e; 222438032Speter{ 222538032Speter int fd; 222638032Speter 222738032Speter if (tTd(52, 1)) 222864562Sgshapiro dprintf("disconnect: In %d Out %d, e=%lx\n", 222938032Speter fileno(InChannel), fileno(OutChannel), (u_long) e); 223038032Speter if (tTd(52, 100)) 223138032Speter { 223264562Sgshapiro dprintf("don't\n"); 223338032Speter return; 223438032Speter } 223538032Speter if (LogLevel > 93) 223638032Speter sm_syslog(LOG_DEBUG, e->e_id, 223764562Sgshapiro "disconnect level %d", 223864562Sgshapiro droplev); 223938032Speter 224038032Speter /* be sure we don't get nasty signals */ 224138032Speter (void) setsignal(SIGINT, SIG_IGN); 224238032Speter (void) setsignal(SIGQUIT, SIG_IGN); 224338032Speter 224438032Speter /* we can't communicate with our caller, so.... */ 224538032Speter HoldErrs = TRUE; 224638032Speter CurEnv->e_errormode = EM_MAIL; 224738032Speter Verbose = 0; 224838032Speter DisConnected = TRUE; 224938032Speter 225038032Speter /* all input from /dev/null */ 225138032Speter if (InChannel != stdin) 225238032Speter { 225338032Speter (void) fclose(InChannel); 225438032Speter InChannel = stdin; 225538032Speter } 225638032Speter if (freopen("/dev/null", "r", stdin) == NULL) 225738032Speter sm_syslog(LOG_ERR, e->e_id, 225838032Speter "disconnect: freopen(\"/dev/null\") failed: %s", 225938032Speter errstring(errno)); 226038032Speter 226138032Speter /* output to the transcript */ 226238032Speter if (OutChannel != stdout) 226338032Speter { 226438032Speter (void) fclose(OutChannel); 226538032Speter OutChannel = stdout; 226638032Speter } 226738032Speter if (droplev > 0) 226838032Speter { 226964562Sgshapiro fd = open("/dev/null", O_WRONLY, 0666); 227064562Sgshapiro if (fd == -1) 227164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 227264562Sgshapiro "disconnect: open(\"/dev/null\") failed: %s", 227364562Sgshapiro errstring(errno)); 227438032Speter (void) fflush(stdout); 227564562Sgshapiro (void) dup2(fd, STDOUT_FILENO); 227664562Sgshapiro (void) dup2(fd, STDERR_FILENO); 227764562Sgshapiro (void) close(fd); 227838032Speter } 227938032Speter 228038032Speter /* drop our controlling TTY completely if possible */ 228138032Speter if (droplev > 1) 228238032Speter { 228338032Speter (void) setsid(); 228438032Speter errno = 0; 228538032Speter } 228638032Speter 228738032Speter#if XDEBUG 228838032Speter checkfd012("disconnect"); 228964562Sgshapiro#endif /* XDEBUG */ 229038032Speter 229138032Speter if (LogLevel > 71) 229238032Speter sm_syslog(LOG_DEBUG, e->e_id, 229364562Sgshapiro "in background, pid=%d", 229464562Sgshapiro getpid()); 229538032Speter 229638032Speter errno = 0; 229738032Speter} 229838032Speter 229938032Speterstatic void 230038032Speterobsolete(argv) 230138032Speter char *argv[]; 230238032Speter{ 230338032Speter register char *ap; 230438032Speter register char *op; 230538032Speter 230638032Speter while ((ap = *++argv) != NULL) 230738032Speter { 230838032Speter /* Return if "--" or not an option of any form. */ 230938032Speter if (ap[0] != '-' || ap[1] == '-') 231038032Speter return; 231138032Speter 231238032Speter /* skip over options that do have a value */ 231338032Speter op = strchr(OPTIONS, ap[1]); 231438032Speter if (op != NULL && *++op == ':' && ap[2] == '\0' && 231538032Speter ap[1] != 'd' && 231638032Speter#if defined(sony_news) 231738032Speter ap[1] != 'E' && ap[1] != 'J' && 231864562Sgshapiro#endif /* defined(sony_news) */ 231938032Speter argv[1] != NULL && argv[1][0] != '-') 232038032Speter { 232138032Speter argv++; 232238032Speter continue; 232338032Speter } 232438032Speter 232538032Speter /* If -C doesn't have an argument, use sendmail.cf. */ 232638032Speter#define __DEFPATH "sendmail.cf" 232738032Speter if (ap[1] == 'C' && ap[2] == '\0') 232838032Speter { 232938032Speter *argv = xalloc(sizeof(__DEFPATH) + 2); 233064562Sgshapiro (void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s", 233164562Sgshapiro __DEFPATH); 233238032Speter } 233338032Speter 233438032Speter /* If -q doesn't have an argument, run it once. */ 233538032Speter if (ap[1] == 'q' && ap[2] == '\0') 233638032Speter *argv = "-q0"; 233738032Speter 233838032Speter /* if -d doesn't have an argument, use 0-99.1 */ 233938032Speter if (ap[1] == 'd' && ap[2] == '\0') 234038032Speter *argv = "-d0-99.1"; 234138032Speter 234264562Sgshapiro#if defined(sony_news) 234338032Speter /* if -E doesn't have an argument, use -EC */ 234438032Speter if (ap[1] == 'E' && ap[2] == '\0') 234538032Speter *argv = "-EC"; 234638032Speter 234738032Speter /* if -J doesn't have an argument, use -JJ */ 234838032Speter if (ap[1] == 'J' && ap[2] == '\0') 234938032Speter *argv = "-JJ"; 235064562Sgshapiro#endif /* defined(sony_news) */ 235138032Speter } 235238032Speter} 235338032Speter/* 235438032Speter** AUTH_WARNING -- specify authorization warning 235538032Speter** 235638032Speter** Parameters: 235738032Speter** e -- the current envelope. 235838032Speter** msg -- the text of the message. 235938032Speter** args -- arguments to the message. 236038032Speter** 236138032Speter** Returns: 236238032Speter** none. 236338032Speter*/ 236438032Speter 236538032Spetervoid 236638032Speter#ifdef __STDC__ 236738032Speterauth_warning(register ENVELOPE *e, const char *msg, ...) 236864562Sgshapiro#else /* __STDC__ */ 236938032Speterauth_warning(e, msg, va_alist) 237038032Speter register ENVELOPE *e; 237138032Speter const char *msg; 237238032Speter va_dcl 237364562Sgshapiro#endif /* __STDC__ */ 237438032Speter{ 237538032Speter char buf[MAXLINE]; 237638032Speter VA_LOCAL_DECL 237738032Speter 237838032Speter if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) 237938032Speter { 238038032Speter register char *p; 238138032Speter static char hostbuf[48]; 238238032Speter 238338032Speter if (hostbuf[0] == '\0') 238438032Speter (void) myhostname(hostbuf, sizeof hostbuf); 238538032Speter 238638032Speter (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); 238738032Speter p = &buf[strlen(buf)]; 238838032Speter VA_START(msg); 238938032Speter vsnprintf(p, SPACELEFT(buf, p), msg, ap); 239038032Speter VA_END; 239164562Sgshapiro addheader("X-Authentication-Warning", buf, 0, &e->e_header); 239238032Speter if (LogLevel > 3) 239338032Speter sm_syslog(LOG_INFO, e->e_id, 239464562Sgshapiro "Authentication-Warning: %.400s", 239564562Sgshapiro buf); 239638032Speter } 239738032Speter} 239838032Speter/* 239938032Speter** GETEXTENV -- get from external environment 240038032Speter** 240138032Speter** Parameters: 240238032Speter** envar -- the name of the variable to retrieve 240338032Speter** 240438032Speter** Returns: 240538032Speter** The value, if any. 240638032Speter*/ 240738032Speter 240838032Speterchar * 240938032Spetergetextenv(envar) 241038032Speter const char *envar; 241138032Speter{ 241238032Speter char **envp; 241338032Speter int l; 241438032Speter 241538032Speter l = strlen(envar); 241638032Speter for (envp = ExternalEnviron; *envp != NULL; envp++) 241738032Speter { 241838032Speter if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') 241938032Speter return &(*envp)[l + 1]; 242038032Speter } 242138032Speter return NULL; 242238032Speter} 242338032Speter/* 242438032Speter** SETUSERENV -- set an environment in the propogated environment 242538032Speter** 242638032Speter** Parameters: 242738032Speter** envar -- the name of the environment variable. 242838032Speter** value -- the value to which it should be set. If 242938032Speter** null, this is extracted from the incoming 243038032Speter** environment. If that is not set, the call 243138032Speter** to setuserenv is ignored. 243238032Speter** 243338032Speter** Returns: 243438032Speter** none. 243538032Speter*/ 243638032Speter 243738032Spetervoid 243838032Spetersetuserenv(envar, value) 243938032Speter const char *envar; 244038032Speter const char *value; 244138032Speter{ 244264562Sgshapiro int i, l; 244338032Speter char **evp = UserEnviron; 244438032Speter char *p; 244538032Speter 244638032Speter if (value == NULL) 244738032Speter { 244838032Speter value = getextenv(envar); 244938032Speter if (value == NULL) 245038032Speter return; 245138032Speter } 245238032Speter 245364562Sgshapiro i = strlen(envar) + 1; 245464562Sgshapiro l = strlen(value) + i + 1; 245564562Sgshapiro p = (char *) xalloc(l); 245664562Sgshapiro (void) snprintf(p, l, "%s=%s", envar, value); 245738032Speter 245838032Speter while (*evp != NULL && strncmp(*evp, p, i) != 0) 245938032Speter evp++; 246038032Speter if (*evp != NULL) 246138032Speter { 246238032Speter *evp++ = p; 246338032Speter } 246438032Speter else if (evp < &UserEnviron[MAXUSERENVIRON]) 246538032Speter { 246638032Speter *evp++ = p; 246738032Speter *evp = NULL; 246838032Speter } 246938032Speter 247038032Speter /* make sure it is in our environment as well */ 247138032Speter if (putenv(p) < 0) 247238032Speter syserr("setuserenv: putenv(%s) failed", p); 247338032Speter} 247438032Speter/* 247538032Speter** DUMPSTATE -- dump state 247638032Speter** 247738032Speter** For debugging. 247838032Speter*/ 247938032Speter 248038032Spetervoid 248138032Speterdumpstate(when) 248238032Speter char *when; 248338032Speter{ 248438032Speter register char *j = macvalue('j', CurEnv); 248538032Speter int rs; 248664562Sgshapiro extern int NextMacroId; 248738032Speter 248838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 248964562Sgshapiro "--- dumping state on %s: $j = %s ---", 249064562Sgshapiro when, 249164562Sgshapiro j == NULL ? "<NULL>" : j); 249238032Speter if (j != NULL) 249338032Speter { 249438032Speter if (!wordinclass(j, 'w')) 249538032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 249664562Sgshapiro "*** $j not in $=w ***"); 249738032Speter } 249838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); 249964562Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n", 250064562Sgshapiro NextMacroId, MAXMACROID); 250138032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); 250238032Speter printopenfds(TRUE); 250338032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); 250438032Speter mci_dump_all(TRUE); 250538032Speter rs = strtorwset("debug_dumpstate", NULL, ST_FIND); 250638032Speter if (rs > 0) 250738032Speter { 250864562Sgshapiro int status; 250938032Speter register char **pvp; 251038032Speter char *pv[MAXATOM + 1]; 251138032Speter 251238032Speter pv[0] = NULL; 251364562Sgshapiro status = rewrite(pv, rs, 0, CurEnv); 251438032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 251564562Sgshapiro "--- ruleset debug_dumpstate returns stat %d, pv: ---", 251664562Sgshapiro status); 251738032Speter for (pvp = pv; *pvp != NULL; pvp++) 251838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); 251938032Speter } 252038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); 252138032Speter} 252238032Speter 252338032Speter 252438032Speter/* ARGSUSED */ 252538032SpeterSIGFUNC_DECL 252638032Spetersigusr1(sig) 252738032Speter int sig; 252838032Speter{ 252938032Speter dumpstate("user signal"); 253038032Speter return SIGFUNC_RETURN; 253138032Speter} 253238032Speter 253338032Speter 253438032Speter/* ARGSUSED */ 253538032SpeterSIGFUNC_DECL 253638032Spetersighup(sig) 253738032Speter int sig; 253838032Speter{ 253964562Sgshapiro int i; 254064562Sgshapiro extern int DtableSize; 254164562Sgshapiro 254264562Sgshapiro clear_events(); 254364562Sgshapiro (void) alarm(0); 254438032Speter if (SaveArgv[0][0] != '/') 254538032Speter { 254638032Speter if (LogLevel > 3) 254764562Sgshapiro sm_syslog(LOG_INFO, NOQID, 254864562Sgshapiro "could not restart: need full path"); 254942575Speter finis(FALSE, EX_OSFILE); 255038032Speter } 255138032Speter if (LogLevel > 3) 255264562Sgshapiro sm_syslog(LOG_INFO, NOQID, "restarting %s %s", 255364562Sgshapiro sig == 0 ? "due to control command" : "on signal", 255464562Sgshapiro SaveArgv[0]); 255564562Sgshapiro 255664562Sgshapiro /* Control socket restart? */ 255764562Sgshapiro if (sig != 0) 255864562Sgshapiro (void) releasesignal(SIGHUP); 255964562Sgshapiro 256042575Speter closecontrolsocket(TRUE); 256138032Speter if (drop_privileges(TRUE) != EX_OK) 256238032Speter { 256338032Speter if (LogLevel > 0) 256464562Sgshapiro sm_syslog(LOG_ALERT, NOQID, 256564562Sgshapiro "could not set[ug]id(%d, %d): %m", 256664562Sgshapiro RunAsUid, RunAsGid); 256742575Speter finis(FALSE, EX_OSERR); 256838032Speter } 256964562Sgshapiro 257064562Sgshapiro /* arrange for all the files to be closed */ 257164562Sgshapiro for (i = 3; i < DtableSize; i++) 257264562Sgshapiro { 257364562Sgshapiro register int j; 257464562Sgshapiro 257564562Sgshapiro if ((j = fcntl(i, F_GETFD, 0)) != -1) 257664562Sgshapiro (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); 257764562Sgshapiro } 257864562Sgshapiro 257964562Sgshapiro (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); 258038032Speter if (LogLevel > 0) 258164562Sgshapiro sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", 258264562Sgshapiro SaveArgv[0]); 258342575Speter finis(FALSE, EX_OSFILE); 258438032Speter} 258538032Speter/* 258638032Speter** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option 258738032Speter** 258838032Speter** Parameters: 258938032Speter** to_real_uid -- if set, drop to the real uid instead 259038032Speter** of the RunAsUser. 259138032Speter** 259238032Speter** Returns: 259338032Speter** EX_OSERR if the setuid failed. 259438032Speter** EX_OK otherwise. 259538032Speter*/ 259638032Speter 259738032Speterint 259838032Speterdrop_privileges(to_real_uid) 259938032Speter bool to_real_uid; 260038032Speter{ 260138032Speter int rval = EX_OK; 260238032Speter GIDSET_T emptygidset[1]; 260338032Speter 260438032Speter if (tTd(47, 1)) 260564562Sgshapiro dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n", 260664562Sgshapiro (int)to_real_uid, (int)RealUid, 260764562Sgshapiro (int)RealGid, (int)RunAsUid, (int)RunAsGid); 260838032Speter 260938032Speter if (to_real_uid) 261038032Speter { 261138032Speter RunAsUserName = RealUserName; 261238032Speter RunAsUid = RealUid; 261338032Speter RunAsGid = RealGid; 261438032Speter } 261538032Speter 261638032Speter /* make sure no one can grab open descriptors for secret files */ 261738032Speter endpwent(); 261838032Speter 261938032Speter /* reset group permissions; these can be set later */ 262038032Speter emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); 262138032Speter if (setgroups(1, emptygidset) == -1 && geteuid() == 0) 262264562Sgshapiro { 262364562Sgshapiro syserr("drop_privileges: setgroups(1, %d) failed", 262464562Sgshapiro (int)emptygidset[0]); 262538032Speter rval = EX_OSERR; 262664562Sgshapiro } 262738032Speter 262838032Speter /* reset primary group and user id */ 262938032Speter if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0) 263064562Sgshapiro { 263164562Sgshapiro syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid); 263238032Speter rval = EX_OSERR; 263364562Sgshapiro } 263464562Sgshapiro if (to_real_uid || RunAsUid != 0) 263564562Sgshapiro { 263664562Sgshapiro uid_t euid = geteuid(); 263764562Sgshapiro 263864562Sgshapiro if (setuid(RunAsUid) < 0) 263964562Sgshapiro { 264064562Sgshapiro syserr("drop_privileges: setuid(%d) failed", 264164562Sgshapiro (int)RunAsUid); 264264562Sgshapiro rval = EX_OSERR; 264364562Sgshapiro } 264464562Sgshapiro else if (RunAsUid != 0 && setuid(0) == 0) 264564562Sgshapiro { 264664562Sgshapiro /* 264764562Sgshapiro ** Believe it or not, the Linux capability model 264864562Sgshapiro ** allows a non-root process to override setuid() 264964562Sgshapiro ** on a process running as root and prevent that 265064562Sgshapiro ** process from dropping privileges. 265164562Sgshapiro */ 265264562Sgshapiro 265364562Sgshapiro syserr("drop_privileges: setuid(0) succeeded (when it should not)"); 265464562Sgshapiro rval = EX_OSERR; 265564562Sgshapiro } 265664562Sgshapiro else if (RunAsUid != euid && setuid(euid) == 0) 265764562Sgshapiro { 265864562Sgshapiro /* 265964562Sgshapiro ** Some operating systems will keep the saved-uid 266064562Sgshapiro ** if a non-root effective-uid calls setuid(real-uid) 266164562Sgshapiro ** making it possible to set it back again later. 266264562Sgshapiro */ 266364562Sgshapiro 266464562Sgshapiro syserr("drop_privileges: Unable to drop non-root set-user-id privileges"); 266564562Sgshapiro rval = EX_OSERR; 266664562Sgshapiro } 266764562Sgshapiro } 266838032Speter if (tTd(47, 5)) 266938032Speter { 267064562Sgshapiro dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", 267164562Sgshapiro (int)geteuid(), (int)getuid(), 267264562Sgshapiro (int)getegid(), (int)getgid()); 267364562Sgshapiro dprintf("drop_privileges: RunAsUser = %d:%d\n", 267464562Sgshapiro (int)RunAsUid, (int)RunAsGid); 267564562Sgshapiro if (tTd(47, 10)) 267664562Sgshapiro dprintf("drop_privileges: rval = %d\n", rval); 267738032Speter } 267838032Speter return rval; 267938032Speter} 268038032Speter/* 268138032Speter** FILL_FD -- make sure a file descriptor has been properly allocated 268238032Speter** 268338032Speter** Used to make sure that stdin/out/err are allocated on startup 268438032Speter** 268538032Speter** Parameters: 268638032Speter** fd -- the file descriptor to be filled. 268738032Speter** where -- a string used for logging. If NULL, this is 268838032Speter** being called on startup, and logging should 268938032Speter** not be done. 269038032Speter** 269138032Speter** Returns: 269238032Speter** none 269338032Speter*/ 269438032Speter 269538032Spetervoid 269638032Speterfill_fd(fd, where) 269738032Speter int fd; 269838032Speter char *where; 269938032Speter{ 270038032Speter int i; 270138032Speter struct stat stbuf; 270238032Speter 270338032Speter if (fstat(fd, &stbuf) >= 0 || errno != EBADF) 270438032Speter return; 270538032Speter 270638032Speter if (where != NULL) 270738032Speter syserr("fill_fd: %s: fd %d not open", where, fd); 270838032Speter else 270938032Speter MissingFds |= 1 << fd; 271038032Speter i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666); 271138032Speter if (i < 0) 271238032Speter { 271338032Speter syserr("!fill_fd: %s: cannot open /dev/null", 271438032Speter where == NULL ? "startup" : where); 271538032Speter } 271638032Speter if (fd != i) 271738032Speter { 271838032Speter (void) dup2(i, fd); 271938032Speter (void) close(i); 272038032Speter } 272138032Speter} 272238032Speter/* 272338032Speter** TESTMODELINE -- process a test mode input line 272438032Speter** 272538032Speter** Parameters: 272638032Speter** line -- the input line. 272738032Speter** e -- the current environment. 272838032Speter** Syntax: 272938032Speter** # a comment 273038032Speter** .X process X as a configuration line 273138032Speter** =X dump a configuration item (such as mailers) 273238032Speter** $X dump a macro or class 273338032Speter** /X try an activity 273438032Speter** X normal process through rule set X 273538032Speter*/ 273638032Speter 273764562Sgshapirostatic void 273838032Spetertestmodeline(line, e) 273938032Speter char *line; 274038032Speter ENVELOPE *e; 274138032Speter{ 274238032Speter register char *p; 274338032Speter char *q; 274438032Speter auto char *delimptr; 274538032Speter int mid; 274638032Speter int i, rs; 274738032Speter STAB *map; 274838032Speter char **s; 274938032Speter struct rewrite *rw; 275038032Speter ADDRESS a; 275138032Speter static int tryflags = RF_COPYNONE; 275238032Speter char exbuf[MAXLINE]; 275364562Sgshapiro extern u_char TokTypeNoC[]; 275438032Speter 275564562Sgshapiro#if _FFR_ADDR_TYPE 275664562Sgshapiro define(macid("{addr_type}", NULL), "e r", e); 275764562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 275838032Speter switch (line[0]) 275938032Speter { 276038032Speter case '#': 276164562Sgshapiro case '\0': 276238032Speter return; 276338032Speter 276438032Speter case '?': 276564562Sgshapiro help("-bt", e); 276638032Speter return; 276738032Speter 276838032Speter case '.': /* config-style settings */ 276938032Speter switch (line[1]) 277038032Speter { 277138032Speter case 'D': 277238032Speter mid = macid(&line[2], &delimptr); 277338032Speter if (mid == '\0') 277438032Speter return; 277538032Speter translate_dollars(delimptr); 277638032Speter define(mid, newstr(delimptr), e); 277738032Speter break; 277838032Speter 277938032Speter case 'C': 278038032Speter if (line[2] == '\0') /* not to call syserr() */ 278138032Speter return; 278238032Speter 278338032Speter mid = macid(&line[2], &delimptr); 278438032Speter if (mid == '\0') 278538032Speter return; 278638032Speter translate_dollars(delimptr); 278738032Speter expand(delimptr, exbuf, sizeof exbuf, e); 278838032Speter p = exbuf; 278938032Speter while (*p != '\0') 279038032Speter { 279138032Speter register char *wd; 279238032Speter char delim; 279338032Speter 279438032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 279538032Speter p++; 279638032Speter wd = p; 279738032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 279838032Speter p++; 279938032Speter delim = *p; 280038032Speter *p = '\0'; 280138032Speter if (wd[0] != '\0') 280238032Speter setclass(mid, wd); 280338032Speter *p = delim; 280438032Speter } 280538032Speter break; 280638032Speter 280738032Speter case '\0': 280838032Speter printf("Usage: .[DC]macro value(s)\n"); 280938032Speter break; 281038032Speter 281138032Speter default: 281238032Speter printf("Unknown \".\" command %s\n", line); 281338032Speter break; 281438032Speter } 281538032Speter return; 281638032Speter 281738032Speter case '=': /* config-style settings */ 281838032Speter switch (line[1]) 281938032Speter { 282038032Speter case 'S': /* dump rule set */ 282138032Speter rs = strtorwset(&line[2], NULL, ST_FIND); 282238032Speter if (rs < 0) 282338032Speter { 282438032Speter printf("Undefined ruleset %s\n", &line[2]); 282538032Speter return; 282638032Speter } 282738032Speter rw = RewriteRules[rs]; 282838032Speter if (rw == NULL) 282938032Speter return; 283038032Speter do 283138032Speter { 283264562Sgshapiro (void) putchar('R'); 283338032Speter s = rw->r_lhs; 283438032Speter while (*s != NULL) 283538032Speter { 283638032Speter xputs(*s++); 283764562Sgshapiro (void) putchar(' '); 283838032Speter } 283964562Sgshapiro (void) putchar('\t'); 284064562Sgshapiro (void) putchar('\t'); 284138032Speter s = rw->r_rhs; 284238032Speter while (*s != NULL) 284338032Speter { 284438032Speter xputs(*s++); 284564562Sgshapiro (void) putchar(' '); 284638032Speter } 284764562Sgshapiro (void) putchar('\n'); 284838032Speter } while ((rw = rw->r_next) != NULL); 284938032Speter break; 285038032Speter 285138032Speter case 'M': 285238032Speter for (i = 0; i < MAXMAILERS; i++) 285338032Speter { 285438032Speter if (Mailer[i] != NULL) 285538032Speter printmailer(Mailer[i]); 285638032Speter } 285738032Speter break; 285838032Speter 285938032Speter case '\0': 286038032Speter printf("Usage: =Sruleset or =M\n"); 286138032Speter break; 286238032Speter 286338032Speter default: 286438032Speter printf("Unknown \"=\" command %s\n", line); 286538032Speter break; 286638032Speter } 286738032Speter return; 286838032Speter 286938032Speter case '-': /* set command-line-like opts */ 287038032Speter switch (line[1]) 287138032Speter { 287238032Speter case 'd': 287338032Speter tTflag(&line[2]); 287438032Speter break; 287538032Speter 287638032Speter case '\0': 287738032Speter printf("Usage: -d{debug arguments}\n"); 287838032Speter break; 287938032Speter 288038032Speter default: 288138032Speter printf("Unknown \"-\" command %s\n", line); 288238032Speter break; 288338032Speter } 288438032Speter return; 288538032Speter 288638032Speter case '$': 288738032Speter if (line[1] == '=') 288838032Speter { 288938032Speter mid = macid(&line[2], NULL); 289038032Speter if (mid != '\0') 289138032Speter stabapply(dump_class, mid); 289238032Speter return; 289338032Speter } 289438032Speter mid = macid(&line[1], NULL); 289538032Speter if (mid == '\0') 289638032Speter return; 289738032Speter p = macvalue(mid, e); 289838032Speter if (p == NULL) 289938032Speter printf("Undefined\n"); 290038032Speter else 290138032Speter { 290238032Speter xputs(p); 290338032Speter printf("\n"); 290438032Speter } 290538032Speter return; 290638032Speter 290738032Speter case '/': /* miscellaneous commands */ 290838032Speter p = &line[strlen(line)]; 290938032Speter while (--p >= line && isascii(*p) && isspace(*p)) 291038032Speter *p = '\0'; 291138032Speter p = strpbrk(line, " \t"); 291238032Speter if (p != NULL) 291338032Speter { 291438032Speter while (isascii(*p) && isspace(*p)) 291538032Speter *p++ = '\0'; 291638032Speter } 291738032Speter else 291838032Speter p = ""; 291938032Speter if (line[1] == '\0') 292038032Speter { 292138032Speter printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); 292238032Speter return; 292338032Speter } 292464562Sgshapiro if (strcasecmp(&line[1], "quit") == 0) 292564562Sgshapiro { 292664562Sgshapiro CurEnv->e_id = NULL; 292764562Sgshapiro finis(TRUE, ExitStat); 292864562Sgshapiro } 292938032Speter if (strcasecmp(&line[1], "mx") == 0) 293038032Speter { 293138032Speter#if NAMED_BIND 293238032Speter /* look up MX records */ 293338032Speter int nmx; 293438032Speter auto int rcode; 293538032Speter char *mxhosts[MAXMXHOSTS + 1]; 293638032Speter 293738032Speter if (*p == '\0') 293838032Speter { 293938032Speter printf("Usage: /mx address\n"); 294038032Speter return; 294138032Speter } 294264562Sgshapiro nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode); 294338032Speter printf("getmxrr(%s) returns %d value(s):\n", p, nmx); 294438032Speter for (i = 0; i < nmx; i++) 294538032Speter printf("\t%s\n", mxhosts[i]); 294664562Sgshapiro#else /* NAMED_BIND */ 294738032Speter printf("No MX code compiled in\n"); 294864562Sgshapiro#endif /* NAMED_BIND */ 294938032Speter } 295038032Speter else if (strcasecmp(&line[1], "canon") == 0) 295138032Speter { 295238032Speter char host[MAXHOSTNAMELEN]; 295338032Speter 295438032Speter if (*p == '\0') 295538032Speter { 295638032Speter printf("Usage: /canon address\n"); 295738032Speter return; 295838032Speter } 295964562Sgshapiro else if (strlcpy(host, p, sizeof host) >= sizeof host) 296038032Speter { 296138032Speter printf("Name too long\n"); 296238032Speter return; 296338032Speter } 296464562Sgshapiro (void) getcanonname(host, sizeof host, HasWildcardMX); 296538032Speter printf("getcanonname(%s) returns %s\n", p, host); 296638032Speter } 296738032Speter else if (strcasecmp(&line[1], "map") == 0) 296838032Speter { 296938032Speter auto int rcode = EX_OK; 297038032Speter char *av[2]; 297138032Speter 297238032Speter if (*p == '\0') 297338032Speter { 297438032Speter printf("Usage: /map mapname key\n"); 297538032Speter return; 297638032Speter } 297738032Speter for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) 297838032Speter continue; 297938032Speter if (*q == '\0') 298038032Speter { 298138032Speter printf("No key specified\n"); 298238032Speter return; 298338032Speter } 298438032Speter *q++ = '\0'; 298538032Speter map = stab(p, ST_MAP, ST_FIND); 298638032Speter if (map == NULL) 298738032Speter { 298838032Speter printf("Map named \"%s\" not found\n", p); 298938032Speter return; 299038032Speter } 299164562Sgshapiro if (!bitset(MF_OPEN, map->s_map.map_mflags) && 299264562Sgshapiro !openmap(&(map->s_map))) 299338032Speter { 299438032Speter printf("Map named \"%s\" not open\n", p); 299538032Speter return; 299638032Speter } 299738032Speter printf("map_lookup: %s (%s) ", p, q); 299838032Speter av[0] = q; 299938032Speter av[1] = NULL; 300038032Speter p = (*map->s_map.map_class->map_lookup) 300138032Speter (&map->s_map, q, av, &rcode); 300238032Speter if (p == NULL) 300338032Speter printf("no match (%d)\n", rcode); 300438032Speter else 300538032Speter printf("returns %s (%d)\n", p, rcode); 300638032Speter } 300738032Speter else if (strcasecmp(&line[1], "try") == 0) 300838032Speter { 300938032Speter MAILER *m; 301064562Sgshapiro STAB *st; 301138032Speter auto int rcode = EX_OK; 301238032Speter 301338032Speter q = strpbrk(p, " \t"); 301438032Speter if (q != NULL) 301538032Speter { 301638032Speter while (isascii(*q) && isspace(*q)) 301738032Speter *q++ = '\0'; 301838032Speter } 301938032Speter if (q == NULL || *q == '\0') 302038032Speter { 302138032Speter printf("Usage: /try mailer address\n"); 302238032Speter return; 302338032Speter } 302464562Sgshapiro st = stab(p, ST_MAILER, ST_FIND); 302564562Sgshapiro if (st == NULL) 302638032Speter { 302738032Speter printf("Unknown mailer %s\n", p); 302838032Speter return; 302938032Speter } 303064562Sgshapiro m = st->s_mailer; 303138032Speter printf("Trying %s %s address %s for mailer %s\n", 303238032Speter bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", 303338032Speter bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", 303438032Speter q, p); 303538032Speter p = remotename(q, m, tryflags, &rcode, CurEnv); 303638032Speter printf("Rcode = %d, addr = %s\n", 303738032Speter rcode, p == NULL ? "<NULL>" : p); 303838032Speter e->e_to = NULL; 303938032Speter } 304038032Speter else if (strcasecmp(&line[1], "tryflags") == 0) 304138032Speter { 304238032Speter if (*p == '\0') 304338032Speter { 304438032Speter printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); 304538032Speter return; 304638032Speter } 304738032Speter for (; *p != '\0'; p++) 304838032Speter { 304938032Speter switch (*p) 305038032Speter { 305138032Speter case 'H': 305238032Speter case 'h': 305338032Speter tryflags |= RF_HEADERADDR; 305438032Speter break; 305538032Speter 305638032Speter case 'E': 305738032Speter case 'e': 305838032Speter tryflags &= ~RF_HEADERADDR; 305938032Speter break; 306038032Speter 306138032Speter case 'S': 306238032Speter case 's': 306338032Speter tryflags |= RF_SENDERADDR; 306438032Speter break; 306538032Speter 306638032Speter case 'R': 306738032Speter case 'r': 306838032Speter tryflags &= ~RF_SENDERADDR; 306938032Speter break; 307038032Speter } 307138032Speter } 307264562Sgshapiro#if _FFR_ADDR_TYPE 307364562Sgshapiro exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e'; 307464562Sgshapiro exbuf[1] = ' '; 307564562Sgshapiro exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r'; 307664562Sgshapiro exbuf[3] = '\0'; 307764562Sgshapiro define(macid("{addr_type}", NULL), newstr(exbuf), e); 307864562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 307938032Speter } 308038032Speter else if (strcasecmp(&line[1], "parse") == 0) 308138032Speter { 308238032Speter if (*p == '\0') 308338032Speter { 308438032Speter printf("Usage: /parse address\n"); 308538032Speter return; 308638032Speter } 308738032Speter q = crackaddr(p); 308838032Speter printf("Cracked address = "); 308938032Speter xputs(q); 309038032Speter printf("\nParsing %s %s address\n", 309138032Speter bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", 309238032Speter bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); 309338032Speter if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) 309438032Speter printf("Cannot parse\n"); 309538032Speter else if (a.q_host != NULL && a.q_host[0] != '\0') 309638032Speter printf("mailer %s, host %s, user %s\n", 309738032Speter a.q_mailer->m_name, a.q_host, a.q_user); 309838032Speter else 309938032Speter printf("mailer %s, user %s\n", 310038032Speter a.q_mailer->m_name, a.q_user); 310138032Speter e->e_to = NULL; 310238032Speter } 310338032Speter else 310438032Speter { 310538032Speter printf("Unknown \"/\" command %s\n", line); 310638032Speter } 310738032Speter return; 310838032Speter } 310938032Speter 311038032Speter for (p = line; isascii(*p) && isspace(*p); p++) 311138032Speter continue; 311238032Speter q = p; 311338032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 311438032Speter p++; 311538032Speter if (*p == '\0') 311638032Speter { 311738032Speter printf("No address!\n"); 311838032Speter return; 311938032Speter } 312038032Speter *p = '\0'; 312138032Speter if (invalidaddr(p + 1, NULL)) 312238032Speter return; 312338032Speter do 312438032Speter { 312538032Speter register char **pvp; 312638032Speter char pvpbuf[PSBUFSIZE]; 312738032Speter 312838032Speter pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, 312964562Sgshapiro &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL); 313038032Speter if (pvp == NULL) 313138032Speter continue; 313238032Speter p = q; 313338032Speter while (*p != '\0') 313438032Speter { 313564562Sgshapiro int status; 313638032Speter 313738032Speter rs = strtorwset(p, NULL, ST_FIND); 313838032Speter if (rs < 0) 313938032Speter { 314038032Speter printf("Undefined ruleset %s\n", p); 314138032Speter break; 314238032Speter } 314364562Sgshapiro status = rewrite(pvp, rs, 0, e); 314464562Sgshapiro if (status != EX_OK) 314538032Speter printf("== Ruleset %s (%d) status %d\n", 314664562Sgshapiro p, rs, status); 314738032Speter while (*p != '\0' && *p++ != ',') 314838032Speter continue; 314938032Speter } 315038032Speter } while (*(p = delimptr) != '\0'); 315138032Speter} 315238032Speter 315364562Sgshapirostatic void 315438032Speterdump_class(s, id) 315538032Speter register STAB *s; 315638032Speter int id; 315738032Speter{ 315838032Speter if (s->s_type != ST_CLASS) 315938032Speter return; 316038032Speter if (bitnset(id & 0xff, s->s_class)) 316138032Speter printf("%s\n", s->s_name); 316238032Speter} 3163