main.c revision 132943
138032Speter/* 2132943Sgshapiro * Copyright (c) 1998-2004 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 1490792Sgshapiro#define _DEFINE 1590792Sgshapiro#include <sendmail.h> 1690792Sgshapiro#include <sm/xtrap.h> 1790792Sgshapiro#include <sm/signal.h> 1890792Sgshapiro 1938032Speter#ifndef lint 2090792SgshapiroSM_UNUSED(static char copyright[]) = 21132943Sgshapiro"@(#) Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.\n\ 2264562Sgshapiro All rights reserved.\n\ 2338032Speter Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ 2438032Speter Copyright (c) 1988, 1993\n\ 2538032Speter The Regents of the University of California. All rights reserved.\n"; 2664562Sgshapiro#endif /* ! lint */ 2738032Speter 28132943SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.939 2004/06/17 16:39:21 ca Exp $") 2938032Speter 3038032Speter 3164562Sgshapiro#if NETINET || NETINET6 3264562Sgshapiro# include <arpa/inet.h> 3364562Sgshapiro#endif /* NETINET || NETINET6 */ 3464562Sgshapiro 3590792Sgshapiro/* for getcfname() */ 3690792Sgshapiro#include <sendmail/pathnames.h> 3790792Sgshapiro 3890792Sgshapirostatic SM_DEBUG_T 3990792SgshapiroDebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart", 4090792Sgshapiro "@(#)$Debug: no_persistent_restart - don't restart, log only $"); 4190792Sgshapiro 4290792Sgshapirostatic void dump_class __P((STAB *, int)); 4390792Sgshapirostatic void obsolete __P((char **)); 4490792Sgshapirostatic void testmodeline __P((char *, ENVELOPE *)); 4590792Sgshapirostatic char *getextenv __P((const char *)); 4690792Sgshapirostatic void sm_printoptions __P((char **)); 4777349Sgshapirostatic SIGFUNC_DECL intindebug __P((int)); 4890792Sgshapirostatic SIGFUNC_DECL sighup __P((int)); 4990792Sgshapirostatic SIGFUNC_DECL sigpipe __P((int)); 5090792Sgshapirostatic SIGFUNC_DECL sigterm __P((int)); 5180785Sgshapiro#ifdef SIGUSR1 5277349Sgshapirostatic SIGFUNC_DECL sigusr1 __P((int)); 5390792Sgshapiro#endif /* SIGUSR1 */ 5464562Sgshapiro 5538032Speter/* 5638032Speter** SENDMAIL -- Post mail to a set of destinations. 5738032Speter** 5838032Speter** This is the basic mail router. All user mail programs should 5938032Speter** call this routine to actually deliver mail. Sendmail in 6038032Speter** turn calls a bunch of mail servers that do the real work of 6138032Speter** delivering the mail. 6238032Speter** 6364562Sgshapiro** Sendmail is driven by settings read in from /etc/mail/sendmail.cf 6438032Speter** (read by readcf.c). 6538032Speter** 6638032Speter** Usage: 6738032Speter** /usr/lib/sendmail [flags] addr ... 6838032Speter** 6938032Speter** See the associated documentation for details. 7038032Speter** 7190792Sgshapiro** Authors: 7238032Speter** Eric Allman, UCB/INGRES (until 10/81). 7338032Speter** Britton-Lee, Inc., purveyors of fine 7438032Speter** database computers (11/81 - 10/88). 7538032Speter** International Computer Science Institute 7638032Speter** (11/88 - 9/89). 7738032Speter** UCB/Mammoth Project (10/89 - 7/95). 7838032Speter** InReference, Inc. (8/95 - 1/97). 7938032Speter** Sendmail, Inc. (1/98 - present). 80111823Sgshapiro** The support of my employers is gratefully acknowledged. 8138032Speter** Few of them (Britton-Lee in particular) have had 8238032Speter** anything to gain from my involvement in this project. 8390792Sgshapiro** 8490792Sgshapiro** Gregory Neil Shapiro, 8590792Sgshapiro** Worcester Polytechnic Institute (until 3/98). 8690792Sgshapiro** Sendmail, Inc. (3/98 - present). 8790792Sgshapiro** 8890792Sgshapiro** Claus Assmann, 8990792Sgshapiro** Sendmail, Inc. (12/98 - present). 9038032Speter*/ 9138032Speter 9238032Speterchar *FullName; /* sender's full name */ 9338032SpeterENVELOPE BlankEnvelope; /* a "blank" envelope */ 9464562Sgshapirostatic ENVELOPE MainEnvelope; /* the envelope around the basic letter */ 9538032SpeterADDRESS NullAddress = /* a null address */ 9638032Speter { "", "", NULL, "" }; 9738032Speterchar *CommandLineArgs; /* command line args for pid file */ 9890792Sgshapirobool Warn_Q_option = false; /* warn about Q option use */ 9964562Sgshapirostatic int MissingFds = 0; /* bit map of fds missing on startup */ 10090792Sgshapirochar *Mbdb = "pw"; /* mailbox database defaults to /etc/passwd */ 10138032Speter 10238032Speter#ifdef NGROUPS_MAX 10338032SpeterGIDSET_T InitialGidSet[NGROUPS_MAX]; 10464562Sgshapiro#endif /* NGROUPS_MAX */ 10538032Speter 10690792Sgshapiro#define MAXCONFIGLEVEL 10 /* highest config version level known */ 10738032Speter 10864562Sgshapiro#if SASL 10964562Sgshapirostatic sasl_callback_t srvcallbacks[] = 11064562Sgshapiro{ 11164562Sgshapiro { SASL_CB_VERIFYFILE, &safesaslfile, NULL }, 11264562Sgshapiro { SASL_CB_PROXY_POLICY, &proxy_policy, NULL }, 11364562Sgshapiro { SASL_CB_LIST_END, NULL, NULL } 11464562Sgshapiro}; 11564562Sgshapiro#endif /* SASL */ 11664562Sgshapiro 11790792Sgshapirounsigned int SubmitMode; 11890792Sgshapiroint SyslogPrefixLen; /* estimated length of syslog prefix */ 11990792Sgshapiro#define PIDLEN 6 /* pid length for computing SyslogPrefixLen */ 12090792Sgshapiro#ifndef SL_FUDGE 12190792Sgshapiro# define SL_FUDGE 10 /* fudge offset for SyslogPrefixLen */ 12290792Sgshapiro#endif /* ! SL_FUDGE */ 12390792Sgshapiro#define SLDLL 8 /* est. length of default syslog label */ 12464562Sgshapiro 12590792Sgshapiro 12690792Sgshapiro/* Some options are dangerous to allow users to use in non-submit mode */ 12790792Sgshapiro#define CHECK_AGAINST_OPMODE(cmd) \ 12890792Sgshapiro{ \ 12990792Sgshapiro if (extraprivs && \ 13090792Sgshapiro OpMode != MD_DELIVER && OpMode != MD_SMTP && \ 131112810Sgshapiro OpMode != MD_ARPAFTP && \ 13290792Sgshapiro OpMode != MD_VERIFY && OpMode != MD_TEST) \ 13390792Sgshapiro { \ 13490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \ 13590792Sgshapiro "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \ 13690792Sgshapiro (cmd)); \ 13790792Sgshapiro break; \ 13890792Sgshapiro } \ 13990792Sgshapiro if (extraprivs && queuerun) \ 14090792Sgshapiro { \ 14190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \ 14290792Sgshapiro "WARNING: Ignoring submission mode -%c option with -q\n", \ 14390792Sgshapiro (cmd)); \ 14490792Sgshapiro break; \ 14590792Sgshapiro } \ 14690792Sgshapiro} 14790792Sgshapiro 14838032Speterint 14938032Spetermain(argc, argv, envp) 15038032Speter int argc; 15138032Speter char **argv; 15238032Speter char **envp; 15338032Speter{ 15438032Speter register char *p; 15538032Speter char **av; 15638032Speter extern char Version[]; 15738032Speter char *ep, *from; 15838032Speter STAB *st; 15938032Speter register int i; 16038032Speter int j; 16164562Sgshapiro int dp; 16290792Sgshapiro int fill_errno; 16390792Sgshapiro int qgrp = NOQGRP; /* queue group to process */ 16490792Sgshapiro bool safecf = true; 16564562Sgshapiro BITMAP256 *p_flags = NULL; /* daemon flags */ 16690792Sgshapiro bool warn_C_flag = false; 16790792Sgshapiro bool auth = true; /* whether to set e_auth_param */ 16838032Speter char warn_f_flag = '\0'; 16990792Sgshapiro bool run_in_foreground = false; /* -bD mode */ 17090792Sgshapiro bool queuerun = false, debug = false; 17138032Speter struct passwd *pw; 17238032Speter struct hostent *hp; 17338032Speter char *nullserver = NULL; 17464562Sgshapiro char *authinfo = NULL; 17564562Sgshapiro char *sysloglabel = NULL; /* label for syslog */ 17690792Sgshapiro char *conffile = NULL; /* name of .cf file */ 17790792Sgshapiro char *queuegroup = NULL; /* queue group to process */ 17890792Sgshapiro char *quarantining = NULL; /* quarantine queue items? */ 17990792Sgshapiro bool extraprivs; 18090792Sgshapiro bool forged, negate; 18190792Sgshapiro bool queuepersistent = false; /* queue runner process runs forever */ 18290792Sgshapiro bool foregroundqueue = false; /* queue run in foreground */ 18390792Sgshapiro bool save_val; /* to save some bool var. */ 18490792Sgshapiro int cftype; /* which cf file to use? */ 185132943Sgshapiro SM_FILE_T *smdebug; 18690792Sgshapiro static time_t starttime = 0; /* when was process started */ 18764562Sgshapiro struct stat traf_st; /* for TrafficLog FIFO check */ 18890792Sgshapiro char buf[MAXLINE]; 18938032Speter char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ 19038032Speter static char rnamebuf[MAXNAME]; /* holds RealUserName */ 19138032Speter char *emptyenviron[1]; 19290792Sgshapiro#if STARTTLS 19366494Sgshapiro bool tls_ok; 19490792Sgshapiro#endif /* STARTTLS */ 19538032Speter QUEUE_CHAR *new; 19690792Sgshapiro ENVELOPE *e; 19738032Speter extern int DtableSize; 19838032Speter extern int optind; 19938032Speter extern int opterr; 20038032Speter extern char *optarg; 20138032Speter extern char **environ; 20290792Sgshapiro#if SASL 20390792Sgshapiro extern void sm_sasl_init __P((void)); 20490792Sgshapiro#endif /* SASL */ 20538032Speter 20690792Sgshapiro#if USE_ENVIRON 20790792Sgshapiro envp = environ; 20890792Sgshapiro#endif /* USE_ENVIRON */ 20990792Sgshapiro 21090792Sgshapiro /* turn off profiling */ 21190792Sgshapiro SM_PROF(0); 21290792Sgshapiro 21390792Sgshapiro /* install default exception handler */ 21490792Sgshapiro sm_exc_newthread(fatal_error); 21590792Sgshapiro 216110560Sgshapiro /* set the default in/out channel so errors reported to screen */ 217110560Sgshapiro InChannel = smioin; 218110560Sgshapiro OutChannel = smioout; 219110560Sgshapiro 22038032Speter /* 22138032Speter ** Check to see if we reentered. 22238032Speter ** This would normally happen if e_putheader or e_putbody 22338032Speter ** were NULL when invoked. 22438032Speter */ 22538032Speter 22690792Sgshapiro if (starttime != 0) 22738032Speter { 22838032Speter syserr("main: reentered!"); 22938032Speter abort(); 23038032Speter } 23190792Sgshapiro starttime = curtime(); 23238032Speter 23338032Speter /* avoid null pointer dereferences */ 23438032Speter TermEscape.te_rv_on = TermEscape.te_rv_off = ""; 23538032Speter 23690792Sgshapiro RealUid = getuid(); 23790792Sgshapiro RealGid = getgid(); 23877349Sgshapiro 23990792Sgshapiro /* Check if sendmail is running with extra privs */ 24090792Sgshapiro extraprivs = (RealUid != 0 && 24190792Sgshapiro (geteuid() != getuid() || getegid() != getgid())); 24277349Sgshapiro 24390792Sgshapiro CurrentPid = getpid(); 24438032Speter 24590792Sgshapiro /* get whatever .cf file is right for the opmode */ 24690792Sgshapiro cftype = SM_GET_RIGHT_CF; 24764562Sgshapiro 24838032Speter /* in 4.4BSD, the table can be huge; impose a reasonable limit */ 24938032Speter DtableSize = getdtsize(); 25038032Speter if (DtableSize > 256) 25138032Speter DtableSize = 256; 25238032Speter 25338032Speter /* 25438032Speter ** Be sure we have enough file descriptors. 25538032Speter ** But also be sure that 0, 1, & 2 are open. 25638032Speter */ 25738032Speter 25890792Sgshapiro /* reset errno and fill_errno; the latter is used way down below */ 25990792Sgshapiro errno = fill_errno = 0; 26038032Speter fill_fd(STDIN_FILENO, NULL); 26190792Sgshapiro if (errno != 0) 26290792Sgshapiro fill_errno = errno; 26338032Speter fill_fd(STDOUT_FILENO, NULL); 26490792Sgshapiro if (errno != 0) 26590792Sgshapiro fill_errno = errno; 26638032Speter fill_fd(STDERR_FILENO, NULL); 26790792Sgshapiro if (errno != 0) 26890792Sgshapiro fill_errno = errno; 26938032Speter 270132943Sgshapiro sm_closefrom(STDERR_FILENO + 1, DtableSize); 27138032Speter errno = 0; 272132943Sgshapiro smdebug = NULL; 27338032Speter 27438032Speter#if LOG 27590792Sgshapiro# ifndef SM_LOG_STR 27690792Sgshapiro# define SM_LOG_STR "sendmail" 27790792Sgshapiro# endif /* ! SM_LOG_STR */ 27864562Sgshapiro# ifdef LOG_MAIL 27990792Sgshapiro openlog(SM_LOG_STR, LOG_PID, LOG_MAIL); 28064562Sgshapiro# else /* LOG_MAIL */ 28190792Sgshapiro openlog(SM_LOG_STR, LOG_PID); 28264562Sgshapiro# endif /* LOG_MAIL */ 28364562Sgshapiro#endif /* LOG */ 28438032Speter 28590792Sgshapiro /* 28690792Sgshapiro ** Seed the random number generator. 28790792Sgshapiro ** Used for queue file names, picking a queue directory, and 28890792Sgshapiro ** MX randomization. 28990792Sgshapiro */ 29038032Speter 29190792Sgshapiro seed_random(); 29238032Speter 29390792Sgshapiro /* do machine-dependent initializations */ 29490792Sgshapiro init_md(argc, argv); 29590792Sgshapiro 29690792Sgshapiro 29790792Sgshapiro SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL; 29890792Sgshapiro 29938032Speter /* reset status from syserr() calls for missing file descriptors */ 30038032Speter Errors = 0; 30138032Speter ExitStat = EX_OK; 30238032Speter 30364562Sgshapiro SubmitMode = SUBMIT_UNKNOWN; 30438032Speter#if XDEBUG 30538032Speter checkfd012("after openlog"); 30664562Sgshapiro#endif /* XDEBUG */ 30738032Speter 30890792Sgshapiro tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1"); 30938032Speter 31038032Speter#ifdef NGROUPS_MAX 31138032Speter /* save initial group set for future checks */ 31238032Speter i = getgroups(NGROUPS_MAX, InitialGidSet); 31390792Sgshapiro if (i <= 0) 31490792Sgshapiro { 31538032Speter InitialGidSet[0] = (GID_T) -1; 31690792Sgshapiro i = 0; 31790792Sgshapiro } 31838032Speter while (i < NGROUPS_MAX) 31938032Speter InitialGidSet[i++] = InitialGidSet[0]; 32064562Sgshapiro#endif /* NGROUPS_MAX */ 32138032Speter 32238032Speter /* drop group id privileges (RunAsUser not yet set) */ 32390792Sgshapiro dp = drop_privileges(false); 32464562Sgshapiro setstat(dp); 32538032Speter 32690792Sgshapiro#ifdef SIGUSR1 32777349Sgshapiro /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */ 32894334Sgshapiro if (!extraprivs) 32977349Sgshapiro { 33077349Sgshapiro /* arrange to dump state on user-1 signal */ 33190792Sgshapiro (void) sm_signal(SIGUSR1, sigusr1); 33277349Sgshapiro } 33394334Sgshapiro else 33494334Sgshapiro { 33594334Sgshapiro /* ignore user-1 signal */ 33694334Sgshapiro (void) sm_signal(SIGUSR1, SIG_IGN); 33794334Sgshapiro } 33890792Sgshapiro#endif /* SIGUSR1 */ 33938032Speter 34038032Speter /* initialize for setproctitle */ 34138032Speter initsetproctitle(argc, argv, envp); 34238032Speter 34338032Speter /* Handle any non-getoptable constructions. */ 34438032Speter obsolete(argv); 34538032Speter 34638032Speter /* 34738032Speter ** Do a quick prescan of the argument list. 34838032Speter */ 34938032Speter 35064562Sgshapiro 35190792Sgshapiro /* find initial opMode */ 35290792Sgshapiro OpMode = MD_DELIVER; 35390792Sgshapiro av = argv; 35490792Sgshapiro p = strrchr(*av, '/'); 35590792Sgshapiro if (p++ == NULL) 35690792Sgshapiro p = *av; 35790792Sgshapiro if (strcmp(p, "newaliases") == 0) 35890792Sgshapiro OpMode = MD_INITALIAS; 35990792Sgshapiro else if (strcmp(p, "mailq") == 0) 36090792Sgshapiro OpMode = MD_PRINT; 36190792Sgshapiro else if (strcmp(p, "smtpd") == 0) 36290792Sgshapiro OpMode = MD_DAEMON; 36390792Sgshapiro else if (strcmp(p, "hoststat") == 0) 36490792Sgshapiro OpMode = MD_HOSTSTAT; 36590792Sgshapiro else if (strcmp(p, "purgestat") == 0) 36690792Sgshapiro OpMode = MD_PURGESTAT; 36790792Sgshapiro 368132943Sgshapiro#if defined(__osf__) || defined(_AIX3) 369132943Sgshapiro# define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:x" 370132943Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */ 371132943Sgshapiro#if defined(sony_news) 372132943Sgshapiro# define OPTIONS "A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:" 373132943Sgshapiro#endif /* defined(sony_news) */ 374132943Sgshapiro#ifndef OPTIONS 375132943Sgshapiro# define OPTIONS "A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:" 376132943Sgshapiro#endif /* ! OPTIONS */ 37790792Sgshapiro 378112810Sgshapiro /* Set to 0 to allow -b; need to check optarg before using it! */ 37938032Speter opterr = 0; 38038032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 38138032Speter { 38238032Speter switch (j) 38338032Speter { 38490792Sgshapiro case 'b': /* operations mode */ 38594334Sgshapiro j = (optarg == NULL) ? ' ' : *optarg; 38694334Sgshapiro switch (j) 38738032Speter { 38890792Sgshapiro case MD_DAEMON: 38990792Sgshapiro case MD_FGDAEMON: 39090792Sgshapiro case MD_SMTP: 39190792Sgshapiro case MD_INITALIAS: 39290792Sgshapiro case MD_DELIVER: 39390792Sgshapiro case MD_VERIFY: 39490792Sgshapiro case MD_TEST: 39590792Sgshapiro case MD_PRINT: 39690792Sgshapiro case MD_PRINTNQE: 39790792Sgshapiro case MD_HOSTSTAT: 39890792Sgshapiro case MD_PURGESTAT: 39990792Sgshapiro case MD_ARPAFTP: 40090792Sgshapiro OpMode = j; 40138032Speter break; 40290792Sgshapiro 40390792Sgshapiro case MD_FREEZE: 40490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 40590792Sgshapiro "Frozen configurations unsupported\n"); 40690792Sgshapiro return EX_USAGE; 40790792Sgshapiro 40890792Sgshapiro default: 40990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 41090792Sgshapiro "Invalid operation mode %c\n", 41190792Sgshapiro j); 41290792Sgshapiro return EX_USAGE; 41338032Speter } 41490792Sgshapiro break; 41590792Sgshapiro 416132943Sgshapiro case 'D': 417132943Sgshapiro if (debug) 418132943Sgshapiro { 419132943Sgshapiro errno = 0; 420132943Sgshapiro syserr("-D file must be before -d"); 421132943Sgshapiro ExitStat = EX_USAGE; 422132943Sgshapiro break; 423132943Sgshapiro } 424132943Sgshapiro dp = drop_privileges(true); 425132943Sgshapiro setstat(dp); 426132943Sgshapiro smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, 427132943Sgshapiro optarg, SM_IO_APPEND, NULL); 428132943Sgshapiro if (smdebug == NULL) 429132943Sgshapiro { 430132943Sgshapiro syserr("cannot open %s", optarg); 431132943Sgshapiro ExitStat = EX_CANTCREAT; 432132943Sgshapiro break; 433132943Sgshapiro } 434132943Sgshapiro sm_debug_setfile(smdebug); 435132943Sgshapiro break; 436132943Sgshapiro 43790792Sgshapiro case 'd': 43890792Sgshapiro debug = true; 43938032Speter tTflag(optarg); 440132943Sgshapiro (void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT, 44190792Sgshapiro (char *) NULL, SM_IO_NBF, 44290792Sgshapiro SM_IO_BUFSIZ); 44338032Speter break; 44464562Sgshapiro 44564562Sgshapiro case 'G': /* relay (gateway) submission */ 44690792Sgshapiro SubmitMode = SUBMIT_MTA; 44764562Sgshapiro break; 44864562Sgshapiro 44964562Sgshapiro case 'L': 450112810Sgshapiro if (optarg == NULL) 451112810Sgshapiro { 452112810Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 453112810Sgshapiro "option requires an argument -- '%c'", 454112810Sgshapiro (char) j); 455112810Sgshapiro return EX_USAGE; 456112810Sgshapiro } 457132943Sgshapiro j = SM_MIN(strlen(optarg), 32) + 1; 45864562Sgshapiro sysloglabel = xalloc(j); 45990792Sgshapiro (void) sm_strlcpy(sysloglabel, optarg, j); 46090792Sgshapiro SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + 46190792Sgshapiro SL_FUDGE + j; 46264562Sgshapiro break; 46364562Sgshapiro 46490792Sgshapiro case 'Q': 46590792Sgshapiro case 'q': 46690792Sgshapiro /* just check if it is there */ 46790792Sgshapiro queuerun = true; 46864562Sgshapiro break; 46938032Speter } 47038032Speter } 47138032Speter opterr = 1; 47238032Speter 47390792Sgshapiro /* Don't leak queue information via debug flags */ 47490792Sgshapiro if (extraprivs && queuerun && debug) 47590792Sgshapiro { 47690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 47790792Sgshapiro "WARNING: Can not use -d with -q. Disabling debugging.\n"); 478132943Sgshapiro sm_debug_close(); 47990792Sgshapiro sm_debug_setfile(NULL); 48090792Sgshapiro (void) memset(tTdvect, '\0', sizeof tTdvect); 48190792Sgshapiro } 48290792Sgshapiro 48377349Sgshapiro#if LOG 48464562Sgshapiro if (sysloglabel != NULL) 48564562Sgshapiro { 48677349Sgshapiro /* Sanitize the string */ 48777349Sgshapiro for (p = sysloglabel; *p != '\0'; p++) 48877349Sgshapiro { 48977349Sgshapiro if (!isascii(*p) || !isprint(*p) || *p == '%') 49077349Sgshapiro *p = '*'; 49177349Sgshapiro } 49264562Sgshapiro closelog(); 49364562Sgshapiro# ifdef LOG_MAIL 49464562Sgshapiro openlog(sysloglabel, LOG_PID, LOG_MAIL); 49564562Sgshapiro# else /* LOG_MAIL */ 49664562Sgshapiro openlog(sysloglabel, LOG_PID); 49764562Sgshapiro# endif /* LOG_MAIL */ 49877349Sgshapiro } 49964562Sgshapiro#endif /* LOG */ 50064562Sgshapiro 50138032Speter /* set up the blank envelope */ 50238032Speter BlankEnvelope.e_puthdr = putheader; 50338032Speter BlankEnvelope.e_putbody = putbody; 50438032Speter BlankEnvelope.e_xfp = NULL; 50538032Speter STRUCTCOPY(NullAddress, BlankEnvelope.e_from); 50638032Speter CurEnv = &BlankEnvelope; 50738032Speter STRUCTCOPY(NullAddress, MainEnvelope.e_from); 50838032Speter 50938032Speter /* 51038032Speter ** Set default values for variables. 51138032Speter ** These cannot be in initialized data space. 51238032Speter */ 51338032Speter 51438032Speter setdefaults(&BlankEnvelope); 51590792Sgshapiro initmacros(&BlankEnvelope); 51638032Speter 51790792Sgshapiro /* reset macro */ 51890792Sgshapiro set_op_mode(OpMode); 51938032Speter 52038032Speter pw = sm_getpwuid(RealUid); 52138032Speter if (pw != NULL) 52290792Sgshapiro (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); 52338032Speter else 52490792Sgshapiro (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", 52590792Sgshapiro (int) RealUid); 52664562Sgshapiro 52738032Speter RealUserName = rnamebuf; 52838032Speter 52938032Speter if (tTd(0, 101)) 53038032Speter { 53190792Sgshapiro sm_dprintf("Version %s\n", Version); 53290792Sgshapiro finis(false, true, EX_OK); 53390792Sgshapiro /* NOTREACHED */ 53438032Speter } 53538032Speter 53638032Speter /* 53790792Sgshapiro ** if running non-set-user-ID binary as non-root, pretend 53838032Speter ** we are the RunAsUid 53938032Speter */ 54077349Sgshapiro 54138032Speter if (RealUid != 0 && geteuid() == RealUid) 54238032Speter { 54338032Speter if (tTd(47, 1)) 54490792Sgshapiro sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n", 54590792Sgshapiro (int) RealUid); 54638032Speter RunAsUid = RealUid; 54738032Speter } 54838032Speter else if (geteuid() != 0) 54938032Speter RunAsUid = geteuid(); 55038032Speter 55190792Sgshapiro EffGid = getegid(); 55290792Sgshapiro if (RealUid != 0 && EffGid == RealGid) 55338032Speter RunAsGid = RealGid; 55438032Speter 55538032Speter if (tTd(47, 5)) 55638032Speter { 55790792Sgshapiro sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n", 55890792Sgshapiro (int) geteuid(), (int) getuid(), 55990792Sgshapiro (int) getegid(), (int) getgid()); 56090792Sgshapiro sm_dprintf("main: RunAsUser = %d:%d\n", 56190792Sgshapiro (int) RunAsUid, (int) RunAsGid); 56238032Speter } 56338032Speter 56438032Speter /* save command line arguments */ 56564562Sgshapiro j = 0; 56638032Speter for (av = argv; *av != NULL; ) 56764562Sgshapiro j += strlen(*av++) + 1; 56838032Speter SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); 56964562Sgshapiro CommandLineArgs = xalloc(j); 57038032Speter p = CommandLineArgs; 57138032Speter for (av = argv, i = 0; *av != NULL; ) 57238032Speter { 57364562Sgshapiro int h; 57464562Sgshapiro 57538032Speter SaveArgv[i++] = newstr(*av); 57638032Speter if (av != argv) 57738032Speter *p++ = ' '; 57890792Sgshapiro (void) sm_strlcpy(p, *av++, j); 57964562Sgshapiro h = strlen(p); 58064562Sgshapiro p += h; 58164562Sgshapiro j -= h + 1; 58238032Speter } 58338032Speter SaveArgv[i] = NULL; 58438032Speter 58538032Speter if (tTd(0, 1)) 58638032Speter { 58738032Speter extern char *CompileOptions[]; 58838032Speter 58990792Sgshapiro sm_dprintf("Version %s\n Compiled with:", Version); 59090792Sgshapiro sm_printoptions(CompileOptions); 59138032Speter } 59238032Speter if (tTd(0, 10)) 59338032Speter { 59438032Speter extern char *OsCompileOptions[]; 59538032Speter 59690792Sgshapiro sm_dprintf(" OS Defines:"); 59790792Sgshapiro sm_printoptions(OsCompileOptions); 59838032Speter#ifdef _PATH_UNIX 59990792Sgshapiro sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX); 60064562Sgshapiro#endif /* _PATH_UNIX */ 60190792Sgshapiro 60290792Sgshapiro sm_dprintf(" Conf file:\t%s (default for MSP)\n", 60390792Sgshapiro getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF, 60490792Sgshapiro conffile)); 60590792Sgshapiro sm_dprintf(" Conf file:\t%s (default for MTA)\n", 60690792Sgshapiro getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF, 60790792Sgshapiro conffile)); 60890792Sgshapiro sm_dprintf(" Pid file:\t%s (default)\n", PidFile); 60938032Speter } 61038032Speter 61190792Sgshapiro if (tTd(0, 12)) 61290792Sgshapiro { 61390792Sgshapiro extern char *SmCompileOptions[]; 61438032Speter 61590792Sgshapiro sm_dprintf(" libsm Defines:"); 61690792Sgshapiro sm_printoptions(SmCompileOptions); 61790792Sgshapiro } 61890792Sgshapiro 61990792Sgshapiro if (tTd(0, 13)) 62090792Sgshapiro { 62190792Sgshapiro extern char *FFRCompileOptions[]; 62290792Sgshapiro 62390792Sgshapiro sm_dprintf(" FFR Defines:"); 62490792Sgshapiro sm_printoptions(FFRCompileOptions); 62590792Sgshapiro } 62690792Sgshapiro 62738032Speter /* clear sendmail's environment */ 62838032Speter ExternalEnviron = environ; 62938032Speter emptyenviron[0] = NULL; 63038032Speter environ = emptyenviron; 63138032Speter 63238032Speter /* 63342575Speter ** restore any original TZ setting until TimeZoneSpec has been 63442575Speter ** determined - or early log messages may get bogus time stamps 63538032Speter */ 63690792Sgshapiro 63738032Speter if ((p = getextenv("TZ")) != NULL) 63838032Speter { 63938032Speter char *tz; 64038032Speter int tzlen; 64138032Speter 64290792Sgshapiro /* XXX check for reasonable length? */ 64338032Speter tzlen = strlen(p) + 4; 64438032Speter tz = xalloc(tzlen); 64590792Sgshapiro (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p); 64690792Sgshapiro 64790792Sgshapiro /* XXX check return code? */ 64864562Sgshapiro (void) putenv(tz); 64938032Speter } 65038032Speter 65138032Speter /* prime the child environment */ 65238032Speter setuserenv("AGENT", "sendmail"); 65338032Speter 65490792Sgshapiro (void) sm_signal(SIGPIPE, SIG_IGN); 65538032Speter OldUmask = umask(022); 65638032Speter FullName = getextenv("NAME"); 65795154Sgshapiro if (FullName != NULL) 65895154Sgshapiro FullName = newstr(FullName); 65938032Speter 66038032Speter /* 66138032Speter ** Initialize name server if it is going to be used. 66238032Speter */ 66338032Speter 66438032Speter#if NAMED_BIND 66538032Speter if (!bitset(RES_INIT, _res.options)) 66664562Sgshapiro (void) res_init(); 66738032Speter if (tTd(8, 8)) 66838032Speter _res.options |= RES_DEBUG; 66938032Speter else 67038032Speter _res.options &= ~RES_DEBUG; 67138032Speter# ifdef RES_NOALIASES 67238032Speter _res.options |= RES_NOALIASES; 67364562Sgshapiro# endif /* RES_NOALIASES */ 67464562Sgshapiro TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry; 67564562Sgshapiro TimeOuts.res_retry[RES_TO_FIRST] = _res.retry; 67664562Sgshapiro TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry; 67764562Sgshapiro TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans; 67864562Sgshapiro TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans; 67964562Sgshapiro TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans; 68064562Sgshapiro#endif /* NAMED_BIND */ 68138032Speter 68238032Speter errno = 0; 68338032Speter from = NULL; 68438032Speter 68538032Speter /* initialize some macros, etc. */ 68690792Sgshapiro init_vendor_macros(&BlankEnvelope); 68738032Speter 68838032Speter /* version */ 68990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version); 69038032Speter 69138032Speter /* hostname */ 69238032Speter hp = myhostname(jbuf, sizeof jbuf); 69338032Speter if (jbuf[0] != '\0') 69438032Speter { 69590792Sgshapiro struct utsname utsname; 69638032Speter 69738032Speter if (tTd(0, 4)) 69890792Sgshapiro sm_dprintf("Canonical name: %s\n", jbuf); 69990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf); 70090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf); 70138032Speter setclass('w', jbuf); 70238032Speter 70338032Speter p = strchr(jbuf, '.'); 704132943Sgshapiro if (p != NULL && p[1] != '\0') 705132943Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]); 70638032Speter 70738032Speter if (uname(&utsname) >= 0) 70838032Speter p = utsname.nodename; 70938032Speter else 71038032Speter { 71138032Speter if (tTd(0, 22)) 71290792Sgshapiro sm_dprintf("uname failed (%s)\n", 71390792Sgshapiro sm_errstring(errno)); 71438032Speter makelower(jbuf); 71538032Speter p = jbuf; 71638032Speter } 71738032Speter if (tTd(0, 4)) 71890792Sgshapiro sm_dprintf(" UUCP nodename: %s\n", p); 71990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p); 72038032Speter setclass('k', p); 72138032Speter setclass('w', p); 72238032Speter } 72338032Speter if (hp != NULL) 72438032Speter { 72538032Speter for (av = hp->h_aliases; av != NULL && *av != NULL; av++) 72638032Speter { 72738032Speter if (tTd(0, 4)) 72890792Sgshapiro sm_dprintf("\ta.k.a.: %s\n", *av); 72938032Speter setclass('w', *av); 73038032Speter } 73164562Sgshapiro#if NETINET || NETINET6 73290792Sgshapiro for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++) 73338032Speter { 73464562Sgshapiro# if NETINET6 73564562Sgshapiro char *addr; 73664562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 73764562Sgshapiro struct in6_addr ia6; 73864562Sgshapiro# endif /* NETINET6 */ 73964562Sgshapiro# if NETINET 74064562Sgshapiro struct in_addr ia; 74164562Sgshapiro# endif /* NETINET */ 74264562Sgshapiro char ipbuf[103]; 74364562Sgshapiro 74464562Sgshapiro ipbuf[0] = '\0'; 74564562Sgshapiro switch (hp->h_addrtype) 74638032Speter { 74764562Sgshapiro# if NETINET 74864562Sgshapiro case AF_INET: 74964562Sgshapiro if (hp->h_length != INADDRSZ) 75064562Sgshapiro break; 75138032Speter 75264562Sgshapiro memmove(&ia, hp->h_addr_list[i], INADDRSZ); 75390792Sgshapiro (void) sm_snprintf(ipbuf, sizeof ipbuf, 75490792Sgshapiro "[%.100s]", inet_ntoa(ia)); 75564562Sgshapiro break; 75664562Sgshapiro# endif /* NETINET */ 75764562Sgshapiro 75864562Sgshapiro# if NETINET6 75964562Sgshapiro case AF_INET6: 76064562Sgshapiro if (hp->h_length != IN6ADDRSZ) 76164562Sgshapiro break; 76264562Sgshapiro 76364562Sgshapiro memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ); 76464562Sgshapiro addr = anynet_ntop(&ia6, buf6, sizeof buf6); 76564562Sgshapiro if (addr != NULL) 76690792Sgshapiro (void) sm_snprintf(ipbuf, sizeof ipbuf, 76790792Sgshapiro "[%.100s]", addr); 76864562Sgshapiro break; 76964562Sgshapiro# endif /* NETINET6 */ 77038032Speter } 77164562Sgshapiro if (ipbuf[0] == '\0') 77264562Sgshapiro break; 77364562Sgshapiro 77464562Sgshapiro if (tTd(0, 4)) 77590792Sgshapiro sm_dprintf("\ta.k.a.: %s\n", ipbuf); 77664562Sgshapiro setclass('w', ipbuf); 77738032Speter } 77864562Sgshapiro#endif /* NETINET || NETINET6 */ 77990792Sgshapiro#if NETINET6 78071345Sgshapiro freehostent(hp); 78171345Sgshapiro hp = NULL; 78290792Sgshapiro#endif /* NETINET6 */ 78338032Speter } 78438032Speter 78538032Speter /* current time */ 78690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL)); 78790792Sgshapiro 78864562Sgshapiro /* current load average */ 78990792Sgshapiro sm_getla(); 79038032Speter 79138032Speter QueueLimitRecipient = (QUEUE_CHAR *) NULL; 79238032Speter QueueLimitSender = (QUEUE_CHAR *) NULL; 79338032Speter QueueLimitId = (QUEUE_CHAR *) NULL; 79490792Sgshapiro QueueLimitQuarantine = (QUEUE_CHAR *) NULL; 79538032Speter 79638032Speter /* 79742575Speter ** Crack argv. 79838032Speter */ 79938032Speter 80038032Speter optind = 1; 80138032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 80238032Speter { 80338032Speter switch (j) 80438032Speter { 80538032Speter case 'b': /* operations mode */ 80690792Sgshapiro /* already done */ 80790792Sgshapiro break; 80838032Speter 80990792Sgshapiro case 'A': /* use Alternate sendmail/submit.cf */ 81090792Sgshapiro cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF 81190792Sgshapiro : SM_GET_SENDMAIL_CF; 81238032Speter break; 81338032Speter 81438032Speter case 'B': /* body type */ 81590792Sgshapiro CHECK_AGAINST_OPMODE(j); 81690792Sgshapiro BlankEnvelope.e_bodytype = newstr(optarg); 81738032Speter break; 81838032Speter 81938032Speter case 'C': /* select configuration file (already done) */ 82038032Speter if (RealUid != 0) 82190792Sgshapiro warn_C_flag = true; 82290792Sgshapiro conffile = newstr(optarg); 82390792Sgshapiro dp = drop_privileges(true); 82464562Sgshapiro setstat(dp); 82590792Sgshapiro safecf = false; 82638032Speter break; 82738032Speter 828132943Sgshapiro case 'D': 82990792Sgshapiro case 'd': /* debugging */ 83090792Sgshapiro /* already done */ 83138032Speter break; 83238032Speter 83338032Speter case 'f': /* from address */ 83438032Speter case 'r': /* obsolete -f flag */ 83590792Sgshapiro CHECK_AGAINST_OPMODE(j); 83638032Speter if (from != NULL) 83738032Speter { 83838032Speter usrerr("More than one \"from\" person"); 83938032Speter ExitStat = EX_USAGE; 84038032Speter break; 84138032Speter } 842110560Sgshapiro if (optarg[0] == '\0') 843110560Sgshapiro from = newstr("<>"); 844110560Sgshapiro else 845110560Sgshapiro from = newstr(denlstring(optarg, true, true)); 84638032Speter if (strcmp(RealUserName, from) != 0) 84738032Speter warn_f_flag = j; 84838032Speter break; 84938032Speter 85038032Speter case 'F': /* set full name */ 85190792Sgshapiro CHECK_AGAINST_OPMODE(j); 85238032Speter FullName = newstr(optarg); 85338032Speter break; 85438032Speter 85564562Sgshapiro case 'G': /* relay (gateway) submission */ 85664562Sgshapiro /* already set */ 85790792Sgshapiro CHECK_AGAINST_OPMODE(j); 85864562Sgshapiro break; 85964562Sgshapiro 86038032Speter case 'h': /* hop count */ 86190792Sgshapiro CHECK_AGAINST_OPMODE(j); 86290792Sgshapiro BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep, 86390792Sgshapiro 10); 86490792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", 86590792Sgshapiro BlankEnvelope.e_hopcount); 86690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf); 86790792Sgshapiro 86838032Speter if (*ep) 86938032Speter { 87038032Speter usrerr("Bad hop count (%s)", optarg); 87138032Speter ExitStat = EX_USAGE; 87238032Speter } 87338032Speter break; 87464562Sgshapiro 87564562Sgshapiro case 'L': /* program label */ 87664562Sgshapiro /* already set */ 87764562Sgshapiro break; 87864562Sgshapiro 87938032Speter case 'n': /* don't alias */ 88090792Sgshapiro CHECK_AGAINST_OPMODE(j); 88190792Sgshapiro NoAlias = true; 88238032Speter break; 88338032Speter 88438032Speter case 'N': /* delivery status notifications */ 88590792Sgshapiro CHECK_AGAINST_OPMODE(j); 88638032Speter DefaultNotify |= QHASNOTIFY; 88790792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 88890792Sgshapiro macid("{dsn_notify}"), optarg); 88990792Sgshapiro if (sm_strcasecmp(optarg, "never") == 0) 89038032Speter break; 89138032Speter for (p = optarg; p != NULL; optarg = p) 89238032Speter { 89338032Speter p = strchr(p, ','); 89438032Speter if (p != NULL) 89538032Speter *p++ = '\0'; 89690792Sgshapiro if (sm_strcasecmp(optarg, "success") == 0) 89738032Speter DefaultNotify |= QPINGONSUCCESS; 89890792Sgshapiro else if (sm_strcasecmp(optarg, "failure") == 0) 89938032Speter DefaultNotify |= QPINGONFAILURE; 90090792Sgshapiro else if (sm_strcasecmp(optarg, "delay") == 0) 90138032Speter DefaultNotify |= QPINGONDELAY; 90238032Speter else 90338032Speter { 90438032Speter usrerr("Invalid -N argument"); 90538032Speter ExitStat = EX_USAGE; 90638032Speter } 90738032Speter } 90838032Speter break; 90938032Speter 91038032Speter case 'o': /* set option */ 91190792Sgshapiro setoption(*optarg, optarg + 1, false, true, 91290792Sgshapiro &BlankEnvelope); 91338032Speter break; 91438032Speter 91538032Speter case 'O': /* set option (long form) */ 91690792Sgshapiro setoption(' ', optarg, false, true, &BlankEnvelope); 91738032Speter break; 91838032Speter 91938032Speter case 'p': /* set protocol */ 92090792Sgshapiro CHECK_AGAINST_OPMODE(j); 92138032Speter p = strchr(optarg, ':'); 92238032Speter if (p != NULL) 92338032Speter { 92438032Speter *p++ = '\0'; 92538032Speter if (*p != '\0') 92638032Speter { 927120256Sgshapiro i = strlen(p) + 1; 928120256Sgshapiro ep = sm_malloc_x(i); 929120256Sgshapiro cleanstrcpy(ep, p, i); 93090792Sgshapiro macdefine(&BlankEnvelope.e_macro, 93190792Sgshapiro A_HEAP, 's', ep); 93238032Speter } 93338032Speter } 93438032Speter if (*optarg != '\0') 93538032Speter { 936120256Sgshapiro i = strlen(optarg) + 1; 937120256Sgshapiro ep = sm_malloc_x(i); 938120256Sgshapiro cleanstrcpy(ep, optarg, i); 93990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_HEAP, 94090792Sgshapiro 'r', ep); 94138032Speter } 94238032Speter break; 94338032Speter 94490792Sgshapiro case 'Q': /* change quarantining on queued items */ 94590792Sgshapiro /* sanity check */ 94690792Sgshapiro if (OpMode != MD_DELIVER && 94790792Sgshapiro OpMode != MD_QUEUERUN) 94890792Sgshapiro { 94990792Sgshapiro usrerr("Can not use -Q with -b%c", OpMode); 95090792Sgshapiro ExitStat = EX_USAGE; 95190792Sgshapiro break; 95290792Sgshapiro } 95390792Sgshapiro 95490792Sgshapiro if (OpMode == MD_DELIVER) 95590792Sgshapiro set_op_mode(MD_QUEUERUN); 95690792Sgshapiro 95790792Sgshapiro FullName = NULL; 95890792Sgshapiro 95990792Sgshapiro quarantining = newstr(optarg); 96090792Sgshapiro break; 96190792Sgshapiro 96238032Speter case 'q': /* run queue files at intervals */ 96364562Sgshapiro /* sanity check */ 96464562Sgshapiro if (OpMode != MD_DELIVER && 96564562Sgshapiro OpMode != MD_DAEMON && 96664562Sgshapiro OpMode != MD_FGDAEMON && 96764562Sgshapiro OpMode != MD_PRINT && 96890792Sgshapiro OpMode != MD_PRINTNQE && 96964562Sgshapiro OpMode != MD_QUEUERUN) 97064562Sgshapiro { 97164562Sgshapiro usrerr("Can not use -q with -b%c", OpMode); 97264562Sgshapiro ExitStat = EX_USAGE; 97364562Sgshapiro break; 97464562Sgshapiro } 97564562Sgshapiro 97664562Sgshapiro /* don't override -bd, -bD or -bp */ 97764562Sgshapiro if (OpMode == MD_DELIVER) 97890792Sgshapiro set_op_mode(MD_QUEUERUN); 97964562Sgshapiro 98038032Speter FullName = NULL; 98190792Sgshapiro negate = optarg[0] == '!'; 98290792Sgshapiro if (negate) 98390792Sgshapiro { 98490792Sgshapiro /* negate meaning of pattern match */ 98590792Sgshapiro optarg++; /* skip '!' for next switch */ 98690792Sgshapiro } 98764562Sgshapiro 98838032Speter switch (optarg[0]) 98938032Speter { 99090792Sgshapiro case 'G': /* Limit by queue group name */ 99190792Sgshapiro if (negate) 99290792Sgshapiro { 99390792Sgshapiro usrerr("Can not use -q!G"); 99490792Sgshapiro ExitStat = EX_USAGE; 99590792Sgshapiro break; 99690792Sgshapiro } 99790792Sgshapiro if (queuegroup != NULL) 99890792Sgshapiro { 99990792Sgshapiro usrerr("Can not use multiple -qG options"); 100090792Sgshapiro ExitStat = EX_USAGE; 100190792Sgshapiro break; 100290792Sgshapiro } 100390792Sgshapiro queuegroup = newstr(&optarg[1]); 100490792Sgshapiro break; 100590792Sgshapiro 100690792Sgshapiro case 'I': /* Limit by ID */ 100764562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 100838032Speter new->queue_match = newstr(&optarg[1]); 100990792Sgshapiro new->queue_negate = negate; 101038032Speter new->queue_next = QueueLimitId; 101138032Speter QueueLimitId = new; 101238032Speter break; 101338032Speter 101490792Sgshapiro case 'R': /* Limit by recipient */ 101564562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 101638032Speter new->queue_match = newstr(&optarg[1]); 101790792Sgshapiro new->queue_negate = negate; 101838032Speter new->queue_next = QueueLimitRecipient; 101938032Speter QueueLimitRecipient = new; 102038032Speter break; 102138032Speter 102290792Sgshapiro case 'S': /* Limit by sender */ 102364562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 102438032Speter new->queue_match = newstr(&optarg[1]); 102590792Sgshapiro new->queue_negate = negate; 102638032Speter new->queue_next = QueueLimitSender; 102738032Speter QueueLimitSender = new; 102838032Speter break; 102938032Speter 103090792Sgshapiro case 'f': /* foreground queue run */ 103190792Sgshapiro foregroundqueue = true; 103290792Sgshapiro break; 103390792Sgshapiro 103490792Sgshapiro case 'Q': /* Limit by quarantine message */ 103590792Sgshapiro if (optarg[1] != '\0') 103690792Sgshapiro { 103790792Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 103890792Sgshapiro new->queue_match = newstr(&optarg[1]); 103990792Sgshapiro new->queue_negate = negate; 104090792Sgshapiro new->queue_next = QueueLimitQuarantine; 104190792Sgshapiro QueueLimitQuarantine = new; 104290792Sgshapiro } 104390792Sgshapiro QueueMode = QM_QUARANTINE; 104490792Sgshapiro break; 104590792Sgshapiro 104690792Sgshapiro case 'L': /* act on lost items */ 104790792Sgshapiro QueueMode = QM_LOST; 104890792Sgshapiro break; 104990792Sgshapiro 105090792Sgshapiro case 'p': /* Persistent queue */ 105190792Sgshapiro queuepersistent = true; 105290792Sgshapiro if (QueueIntvl == 0) 105390792Sgshapiro QueueIntvl = 1; 105490792Sgshapiro if (optarg[1] == '\0') 105590792Sgshapiro break; 105690792Sgshapiro ++optarg; 105790792Sgshapiro /* FALLTHROUGH */ 105890792Sgshapiro 105938032Speter default: 106064562Sgshapiro i = Errors; 106138032Speter QueueIntvl = convtime(optarg, 'm'); 106298841Sgshapiro if (QueueIntvl < 0) 106398841Sgshapiro { 106498841Sgshapiro usrerr("Invalid -q value"); 106598841Sgshapiro ExitStat = EX_USAGE; 106698841Sgshapiro } 106764562Sgshapiro 106864562Sgshapiro /* check for bad conversion */ 106964562Sgshapiro if (i < Errors) 107064562Sgshapiro ExitStat = EX_USAGE; 107138032Speter break; 107238032Speter } 107338032Speter break; 107438032Speter 107538032Speter case 'R': /* DSN RET: what to return */ 107690792Sgshapiro CHECK_AGAINST_OPMODE(j); 107790792Sgshapiro if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags)) 107838032Speter { 107938032Speter usrerr("Duplicate -R flag"); 108038032Speter ExitStat = EX_USAGE; 108138032Speter break; 108238032Speter } 108390792Sgshapiro BlankEnvelope.e_flags |= EF_RET_PARAM; 108490792Sgshapiro if (sm_strcasecmp(optarg, "hdrs") == 0) 108590792Sgshapiro BlankEnvelope.e_flags |= EF_NO_BODY_RETN; 108690792Sgshapiro else if (sm_strcasecmp(optarg, "full") != 0) 108738032Speter { 108838032Speter usrerr("Invalid -R value"); 108938032Speter ExitStat = EX_USAGE; 109038032Speter } 109190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 109290792Sgshapiro macid("{dsn_ret}"), optarg); 109338032Speter break; 109438032Speter 109538032Speter case 't': /* read recipients from message */ 109690792Sgshapiro CHECK_AGAINST_OPMODE(j); 109790792Sgshapiro GrabTo = true; 109838032Speter break; 109938032Speter 110038032Speter case 'V': /* DSN ENVID: set "original" envelope id */ 110190792Sgshapiro CHECK_AGAINST_OPMODE(j); 110238032Speter if (!xtextok(optarg)) 110338032Speter { 110438032Speter usrerr("Invalid syntax in -V flag"); 110538032Speter ExitStat = EX_USAGE; 110638032Speter } 110738032Speter else 110864562Sgshapiro { 110990792Sgshapiro BlankEnvelope.e_envid = newstr(optarg); 111090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 111190792Sgshapiro macid("{dsn_envid}"), optarg); 111264562Sgshapiro } 111338032Speter break; 111438032Speter 111538032Speter case 'X': /* traffic log file */ 111690792Sgshapiro dp = drop_privileges(true); 111764562Sgshapiro setstat(dp); 111864562Sgshapiro if (stat(optarg, &traf_st) == 0 && 111964562Sgshapiro S_ISFIFO(traf_st.st_mode)) 112090792Sgshapiro TrafficLogFile = sm_io_open(SmFtStdio, 112190792Sgshapiro SM_TIME_DEFAULT, 112290792Sgshapiro optarg, 112390792Sgshapiro SM_IO_WRONLY, NULL); 112464562Sgshapiro else 112590792Sgshapiro TrafficLogFile = sm_io_open(SmFtStdio, 112690792Sgshapiro SM_TIME_DEFAULT, 112790792Sgshapiro optarg, 112890792Sgshapiro SM_IO_APPEND, NULL); 112938032Speter if (TrafficLogFile == NULL) 113038032Speter { 113138032Speter syserr("cannot open %s", optarg); 113238032Speter ExitStat = EX_CANTCREAT; 113338032Speter break; 113438032Speter } 113590792Sgshapiro (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT, 113690792Sgshapiro NULL, SM_IO_LBF, 0); 113738032Speter break; 113838032Speter 113938032Speter /* compatibility flags */ 114038032Speter case 'c': /* connect to non-local mailers */ 114138032Speter case 'i': /* don't let dot stop me */ 114238032Speter case 'm': /* send to me too */ 114338032Speter case 'T': /* set timeout interval */ 114438032Speter case 'v': /* give blow-by-blow description */ 114590792Sgshapiro setoption(j, "T", false, true, &BlankEnvelope); 114638032Speter break; 114738032Speter 114838032Speter case 'e': /* error message disposition */ 114938032Speter case 'M': /* define macro */ 115090792Sgshapiro setoption(j, optarg, false, true, &BlankEnvelope); 115138032Speter break; 115238032Speter 115338032Speter case 's': /* save From lines in headers */ 115490792Sgshapiro setoption('f', "T", false, true, &BlankEnvelope); 115538032Speter break; 115638032Speter 115764562Sgshapiro#ifdef DBM 115838032Speter case 'I': /* initialize alias DBM file */ 115990792Sgshapiro set_op_mode(MD_INITALIAS); 116038032Speter break; 116164562Sgshapiro#endif /* DBM */ 116238032Speter 116364562Sgshapiro#if defined(__osf__) || defined(_AIX3) 116438032Speter case 'x': /* random flag that OSF/1 & AIX mailx passes */ 116538032Speter break; 116664562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */ 116764562Sgshapiro#if defined(sony_news) 116838032Speter case 'E': 116938032Speter case 'J': /* ignore flags for Japanese code conversion 117064562Sgshapiro implemented on Sony NEWS */ 117138032Speter break; 117264562Sgshapiro#endif /* defined(sony_news) */ 117338032Speter 117438032Speter default: 117590792Sgshapiro finis(true, true, EX_USAGE); 117690792Sgshapiro /* NOTREACHED */ 117738032Speter break; 117838032Speter } 117938032Speter } 118038032Speter 118190792Sgshapiro /* if we've had errors so far, exit now */ 118290792Sgshapiro if ((ExitStat != EX_OK && OpMode != MD_TEST) || 118390792Sgshapiro ExitStat == EX_OSERR) 118464562Sgshapiro { 118590792Sgshapiro finis(false, true, ExitStat); 118690792Sgshapiro /* NOTREACHED */ 118764562Sgshapiro } 118890792Sgshapiro 118990792Sgshapiro if (bitset(SUBMIT_MTA, SubmitMode)) 119064562Sgshapiro { 119198841Sgshapiro /* If set daemon_flags on command line, don't reset it */ 119298841Sgshapiro if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL) 119398841Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 119498841Sgshapiro macid("{daemon_flags}"), "CC f"); 119564562Sgshapiro } 119690792Sgshapiro else if (OpMode == MD_DELIVER || OpMode == MD_SMTP) 119764562Sgshapiro { 119890792Sgshapiro SubmitMode = SUBMIT_MSA; 119998841Sgshapiro 120098841Sgshapiro /* If set daemon_flags on command line, don't reset it */ 120198841Sgshapiro if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL) 120298841Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 120398841Sgshapiro macid("{daemon_flags}"), "c u"); 120464562Sgshapiro } 120564562Sgshapiro 120638032Speter /* 120738032Speter ** Do basic initialization. 120838032Speter ** Read system control file. 120938032Speter ** Extract special fields for local use. 121038032Speter */ 121138032Speter 121238032Speter#if XDEBUG 121338032Speter checkfd012("before readcf"); 121464562Sgshapiro#endif /* XDEBUG */ 121590792Sgshapiro vendor_pre_defaults(&BlankEnvelope); 121664562Sgshapiro 121790792Sgshapiro readcf(getcfname(OpMode, SubmitMode, cftype, conffile), 121890792Sgshapiro safecf, &BlankEnvelope); 121990792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) 122090792Sgshapiro ConfigFileRead = true; 122190792Sgshapiro#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */ 122290792Sgshapiro vendor_post_defaults(&BlankEnvelope); 122338032Speter 122490792Sgshapiro /* now we can complain about missing fds */ 122590792Sgshapiro if (MissingFds != 0 && LogLevel > 8) 122690792Sgshapiro { 122790792Sgshapiro char mbuf[MAXLINE]; 122890792Sgshapiro 122990792Sgshapiro mbuf[0] = '\0'; 123090792Sgshapiro if (bitset(1 << STDIN_FILENO, MissingFds)) 123190792Sgshapiro (void) sm_strlcat(mbuf, ", stdin", sizeof mbuf); 123290792Sgshapiro if (bitset(1 << STDOUT_FILENO, MissingFds)) 123390792Sgshapiro (void) sm_strlcat(mbuf, ", stdout", sizeof mbuf); 123490792Sgshapiro if (bitset(1 << STDERR_FILENO, MissingFds)) 123590792Sgshapiro (void) sm_strlcat(mbuf, ", stderr", sizeof mbuf); 123690792Sgshapiro 123790792Sgshapiro /* Notice: fill_errno is from high above: fill_fd() */ 123890792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 123990792Sgshapiro "File descriptors missing on startup: %s; %s", 124090792Sgshapiro &mbuf[2], sm_errstring(fill_errno)); 124190792Sgshapiro } 124290792Sgshapiro 124377349Sgshapiro /* Remove the ability for a normal user to send signals */ 124490792Sgshapiro if (RealUid != 0 && RealUid != geteuid()) 124577349Sgshapiro { 124677349Sgshapiro uid_t new_uid = geteuid(); 124777349Sgshapiro 124877349Sgshapiro#if HASSETREUID 124977349Sgshapiro /* 125077349Sgshapiro ** Since we can differentiate between uid and euid, 125177349Sgshapiro ** make the uid a different user so the real user 125277349Sgshapiro ** can't send signals. However, it doesn't need to be 125377349Sgshapiro ** root (euid has root). 125477349Sgshapiro */ 125577349Sgshapiro 125677349Sgshapiro if (new_uid == 0) 125777349Sgshapiro new_uid = DefUid; 125877349Sgshapiro if (tTd(47, 5)) 125990792Sgshapiro sm_dprintf("Changing real uid to %d\n", (int) new_uid); 126077349Sgshapiro if (setreuid(new_uid, geteuid()) < 0) 126177349Sgshapiro { 126277349Sgshapiro syserr("main: setreuid(%d, %d) failed", 126377349Sgshapiro (int) new_uid, (int) geteuid()); 126490792Sgshapiro finis(false, true, EX_OSERR); 126577349Sgshapiro /* NOTREACHED */ 126677349Sgshapiro } 126777349Sgshapiro if (tTd(47, 10)) 126890792Sgshapiro sm_dprintf("Now running as e/ruid %d:%d\n", 126990792Sgshapiro (int) geteuid(), (int) getuid()); 127077349Sgshapiro#else /* HASSETREUID */ 127177349Sgshapiro /* 127277349Sgshapiro ** Have to change both effective and real so need to 127377349Sgshapiro ** change them both to effective to keep privs. 127477349Sgshapiro */ 127577349Sgshapiro 127677349Sgshapiro if (tTd(47, 5)) 127790792Sgshapiro sm_dprintf("Changing uid to %d\n", (int) new_uid); 127877349Sgshapiro if (setuid(new_uid) < 0) 127977349Sgshapiro { 128077349Sgshapiro syserr("main: setuid(%d) failed", (int) new_uid); 128190792Sgshapiro finis(false, true, EX_OSERR); 128277349Sgshapiro /* NOTREACHED */ 128377349Sgshapiro } 128477349Sgshapiro if (tTd(47, 10)) 128590792Sgshapiro sm_dprintf("Now running as e/ruid %d:%d\n", 128690792Sgshapiro (int) geteuid(), (int) getuid()); 128777349Sgshapiro#endif /* HASSETREUID */ 128877349Sgshapiro } 128977349Sgshapiro 129090792Sgshapiro#if NAMED_BIND 1291132943Sgshapiro if (FallbackMX != NULL) 1292132943Sgshapiro (void) getfallbackmxrr(FallbackMX); 129390792Sgshapiro#endif /* NAMED_BIND */ 129490792Sgshapiro 129590792Sgshapiro if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER) 129690792Sgshapiro { 129790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 129890792Sgshapiro "WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n"); 129990792Sgshapiro } 130090792Sgshapiro 130190792Sgshapiro if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)) 130290792Sgshapiro { 130390792Sgshapiro usrerr("Mail submission program cannot be used as daemon"); 130490792Sgshapiro finis(false, true, EX_USAGE); 130590792Sgshapiro } 130690792Sgshapiro 130790792Sgshapiro if (OpMode == MD_DELIVER || OpMode == MD_SMTP || 130890792Sgshapiro OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP || 130990792Sgshapiro OpMode == MD_DAEMON || OpMode == MD_FGDAEMON) 131090792Sgshapiro makeworkgroups(); 131190792Sgshapiro 131277349Sgshapiro /* set up the basic signal handlers */ 131390792Sgshapiro if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN) 131490792Sgshapiro (void) sm_signal(SIGINT, intsig); 131590792Sgshapiro (void) sm_signal(SIGTERM, intsig); 131677349Sgshapiro 131738032Speter /* Enforce use of local time (null string overrides this) */ 131838032Speter if (TimeZoneSpec == NULL) 131938032Speter unsetenv("TZ"); 132038032Speter else if (TimeZoneSpec[0] != '\0') 132138032Speter setuserenv("TZ", TimeZoneSpec); 132238032Speter else 132338032Speter setuserenv("TZ", NULL); 132438032Speter tzset(); 132538032Speter 132690792Sgshapiro /* initialize mailbox database */ 132790792Sgshapiro i = sm_mbdb_initialize(Mbdb); 132890792Sgshapiro if (i != EX_OK) 132990792Sgshapiro { 133090792Sgshapiro usrerr("Can't initialize mailbox database \"%s\": %s", 133190792Sgshapiro Mbdb, sm_strexit(i)); 133290792Sgshapiro ExitStat = i; 133390792Sgshapiro } 133490792Sgshapiro 133538032Speter /* avoid denial-of-service attacks */ 133638032Speter resetlimits(); 133738032Speter 133890792Sgshapiro if (OpMode == MD_TEST) 133938032Speter { 134090792Sgshapiro /* can't be done after readcf if RunAs* is used */ 134190792Sgshapiro dp = drop_privileges(true); 134290792Sgshapiro if (dp != EX_OK) 134390792Sgshapiro { 134490792Sgshapiro finis(false, true, dp); 134590792Sgshapiro /* NOTREACHED */ 134690792Sgshapiro } 134790792Sgshapiro } 134890792Sgshapiro else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) 134990792Sgshapiro { 135038032Speter /* drop privileges -- daemon mode done after socket/bind */ 135190792Sgshapiro dp = drop_privileges(false); 135264562Sgshapiro setstat(dp); 135390792Sgshapiro if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0)) 135490792Sgshapiro { 135590792Sgshapiro usrerr("Mail submission program must have RunAsUser set to non root user"); 135690792Sgshapiro finis(false, true, EX_CONFIG); 135790792Sgshapiro /* NOTREACHED */ 135890792Sgshapiro } 135938032Speter } 136038032Speter 136164562Sgshapiro#if NAMED_BIND 136264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT]; 136364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT]; 136464562Sgshapiro#endif /* NAMED_BIND */ 136564562Sgshapiro 136638032Speter /* 136738032Speter ** Find our real host name for future logging. 136838032Speter */ 136938032Speter 137064562Sgshapiro authinfo = getauthinfo(STDIN_FILENO, &forged); 137190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo); 137238032Speter 137338032Speter /* suppress error printing if errors mailed back or whatever */ 137490792Sgshapiro if (BlankEnvelope.e_errormode != EM_PRINT) 137590792Sgshapiro HoldErrs = true; 137638032Speter 137738032Speter /* set up the $=m class now, after .cf has a chance to redefine $m */ 137890792Sgshapiro expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope); 137973188Sgshapiro if (jbuf[0] != '\0') 138073188Sgshapiro setclass('m', jbuf); 138138032Speter 138238032Speter /* probe interfaces and locate any additional names */ 138390792Sgshapiro if (DontProbeInterfaces != DPI_PROBENONE) 138438032Speter load_if_names(); 138538032Speter 138690792Sgshapiro if (tTd(0, 10)) 138790792Sgshapiro { 1388110560Sgshapiro char pidpath[MAXPATHLEN]; 1389110560Sgshapiro 139090792Sgshapiro /* Now we know which .cf file we use */ 139190792Sgshapiro sm_dprintf(" Conf file:\t%s (selected)\n", 139290792Sgshapiro getcfname(OpMode, SubmitMode, cftype, conffile)); 1393110560Sgshapiro expand(PidFile, pidpath, sizeof pidpath, &BlankEnvelope); 1394110560Sgshapiro sm_dprintf(" Pid file:\t%s (selected)\n", pidpath); 139590792Sgshapiro } 139690792Sgshapiro 139738032Speter if (tTd(0, 1)) 139838032Speter { 139990792Sgshapiro sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============"); 140090792Sgshapiro sm_dprintf("\n (short domain name) $w = "); 1401132943Sgshapiro xputs(sm_debug_file(), macvalue('w', &BlankEnvelope)); 140290792Sgshapiro sm_dprintf("\n (canonical domain name) $j = "); 1403132943Sgshapiro xputs(sm_debug_file(), macvalue('j', &BlankEnvelope)); 140490792Sgshapiro sm_dprintf("\n (subdomain name) $m = "); 1405132943Sgshapiro xputs(sm_debug_file(), macvalue('m', &BlankEnvelope)); 140690792Sgshapiro sm_dprintf("\n (node name) $k = "); 1407132943Sgshapiro xputs(sm_debug_file(), macvalue('k', &BlankEnvelope)); 140890792Sgshapiro sm_dprintf("\n========================================================\n\n"); 140938032Speter } 141038032Speter 141138032Speter /* 141238032Speter ** Do more command line checking -- these are things that 141338032Speter ** have to modify the results of reading the config file. 141438032Speter */ 141538032Speter 141638032Speter /* process authorization warnings from command line */ 141738032Speter if (warn_C_flag) 141890792Sgshapiro auth_warning(&BlankEnvelope, "Processed by %s with -C %s", 141990792Sgshapiro RealUserName, conffile); 142064562Sgshapiro if (Warn_Q_option && !wordinclass(RealUserName, 't')) 142190792Sgshapiro auth_warning(&BlankEnvelope, "Processed from queue %s", 142290792Sgshapiro QueueDir); 142390792Sgshapiro if (sysloglabel != NULL && !wordinclass(RealUserName, 't') && 142490792Sgshapiro RealUid != 0 && RealUid != TrustedUid && LogLevel > 1) 142590792Sgshapiro sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label", 142690792Sgshapiro (int) RealUid); 142738032Speter 142838032Speter /* check body type for legality */ 142990792Sgshapiro i = check_bodytype(BlankEnvelope.e_bodytype); 143090792Sgshapiro if (i == BODYTYPE_ILLEGAL) 143138032Speter { 143290792Sgshapiro usrerr("Illegal body type %s", BlankEnvelope.e_bodytype); 143390792Sgshapiro BlankEnvelope.e_bodytype = NULL; 143438032Speter } 143590792Sgshapiro else if (i != BODYTYPE_NONE) 143690792Sgshapiro SevenBitInput = (i == BODYTYPE_7BIT); 143738032Speter 143838032Speter /* tweak default DSN notifications */ 143938032Speter if (DefaultNotify == 0) 144038032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 144138032Speter 144238032Speter /* check for sane configuration level */ 144338032Speter if (ConfigLevel > MAXCONFIGLEVEL) 144438032Speter { 144538032Speter syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", 144690792Sgshapiro ConfigLevel, Version, MAXCONFIGLEVEL); 144738032Speter } 144838032Speter 144938032Speter /* need MCI cache to have persistence */ 145038032Speter if (HostStatDir != NULL && MaxMciCache == 0) 145138032Speter { 145238032Speter HostStatDir = NULL; 145390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 145490792Sgshapiro "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); 145538032Speter } 145638032Speter 145738032Speter /* need HostStatusDir in order to have SingleThreadDelivery */ 145838032Speter if (SingleThreadDelivery && HostStatDir == NULL) 145938032Speter { 146090792Sgshapiro SingleThreadDelivery = false; 146190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 146290792Sgshapiro "Warning: HostStatusDirectory required for SingleThreadDelivery\n"); 146338032Speter } 146438032Speter 146538032Speter /* check for permissions */ 146690792Sgshapiro if (RealUid != 0 && 146742575Speter RealUid != TrustedUid) 146838032Speter { 146990792Sgshapiro char *action = NULL; 147090792Sgshapiro 147190792Sgshapiro switch (OpMode) 147290792Sgshapiro { 147390792Sgshapiro case MD_QUEUERUN: 147490792Sgshapiro if (quarantining != NULL) 147590792Sgshapiro action = "quarantine jobs"; 147690792Sgshapiro else 1477132943Sgshapiro { 1478132943Sgshapiro /* Normal users can do a single queue run */ 1479132943Sgshapiro if (QueueIntvl == 0) 1480132943Sgshapiro break; 1481132943Sgshapiro } 148290792Sgshapiro 148390792Sgshapiro /* but not persistent queue runners */ 148490792Sgshapiro if (action == NULL) 148590792Sgshapiro action = "start a queue runner daemon"; 148690792Sgshapiro /* FALLTHROUGH */ 148790792Sgshapiro 148890792Sgshapiro case MD_PURGESTAT: 148990792Sgshapiro if (action == NULL) 149090792Sgshapiro action = "purge host status"; 149190792Sgshapiro /* FALLTHROUGH */ 149290792Sgshapiro 149390792Sgshapiro case MD_DAEMON: 149490792Sgshapiro case MD_FGDAEMON: 149590792Sgshapiro if (action == NULL) 149690792Sgshapiro action = "run daemon"; 149790792Sgshapiro 149890792Sgshapiro if (tTd(65, 1)) 149990792Sgshapiro sm_dprintf("Deny user %d attempt to %s\n", 150090792Sgshapiro (int) RealUid, action); 150190792Sgshapiro 150290792Sgshapiro if (LogLevel > 1) 150390792Sgshapiro sm_syslog(LOG_ALERT, NOQID, 150490792Sgshapiro "user %d attempted to %s", 150590792Sgshapiro (int) RealUid, action); 150690792Sgshapiro HoldErrs = false; 150790792Sgshapiro usrerr("Permission denied (real uid not trusted)"); 150890792Sgshapiro finis(false, true, EX_USAGE); 150990792Sgshapiro /* NOTREACHED */ 151090792Sgshapiro break; 151190792Sgshapiro 151290792Sgshapiro case MD_VERIFY: 151390792Sgshapiro if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags)) 151490792Sgshapiro { 151590792Sgshapiro /* 151690792Sgshapiro ** If -bv and RestrictExpand, 151790792Sgshapiro ** drop privs to prevent normal 151890792Sgshapiro ** users from reading private 151990792Sgshapiro ** aliases/forwards/:include:s 152090792Sgshapiro */ 152190792Sgshapiro 152290792Sgshapiro if (tTd(65, 1)) 152390792Sgshapiro sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n", 152490792Sgshapiro (int) RealUid); 152590792Sgshapiro 152690792Sgshapiro dp = drop_privileges(true); 152790792Sgshapiro 152890792Sgshapiro /* Fake address safety */ 152990792Sgshapiro if (tTd(65, 1)) 153090792Sgshapiro sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n"); 153190792Sgshapiro setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail); 153290792Sgshapiro 153390792Sgshapiro if (dp != EX_OK) 153490792Sgshapiro { 153590792Sgshapiro if (tTd(65, 1)) 153690792Sgshapiro sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n", 153790792Sgshapiro (int) RealUid); 153890792Sgshapiro CurEnv->e_id = NULL; 153990792Sgshapiro finis(true, true, dp); 154090792Sgshapiro /* NOTREACHED */ 154190792Sgshapiro } 154290792Sgshapiro } 154390792Sgshapiro break; 154490792Sgshapiro 154590792Sgshapiro case MD_TEST: 154690792Sgshapiro case MD_PRINT: 154790792Sgshapiro case MD_PRINTNQE: 154890792Sgshapiro case MD_FREEZE: 154990792Sgshapiro case MD_HOSTSTAT: 155090792Sgshapiro /* Nothing special to check */ 155190792Sgshapiro break; 155290792Sgshapiro 155390792Sgshapiro case MD_INITALIAS: 155490792Sgshapiro if (!wordinclass(RealUserName, 't')) 155590792Sgshapiro { 155690792Sgshapiro if (tTd(65, 1)) 155790792Sgshapiro sm_dprintf("Deny user %d attempt to rebuild the alias map\n", 155890792Sgshapiro (int) RealUid); 155990792Sgshapiro if (LogLevel > 1) 156090792Sgshapiro sm_syslog(LOG_ALERT, NOQID, 156190792Sgshapiro "user %d attempted to rebuild the alias map", 156290792Sgshapiro (int) RealUid); 156390792Sgshapiro HoldErrs = false; 156490792Sgshapiro usrerr("Permission denied (real uid not trusted)"); 156590792Sgshapiro finis(false, true, EX_USAGE); 156690792Sgshapiro /* NOTREACHED */ 156790792Sgshapiro } 156890792Sgshapiro if (UseMSP) 156990792Sgshapiro { 157090792Sgshapiro HoldErrs = false; 157190792Sgshapiro usrerr("User %d cannot rebuild aliases in mail submission program", 157290792Sgshapiro (int) RealUid); 157390792Sgshapiro finis(false, true, EX_USAGE); 157490792Sgshapiro /* NOTREACHED */ 157590792Sgshapiro } 157690792Sgshapiro /* FALLTHROUGH */ 157790792Sgshapiro 157890792Sgshapiro default: 157990792Sgshapiro if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) && 158090792Sgshapiro Verbose != 0) 158190792Sgshapiro { 158290792Sgshapiro /* 158390792Sgshapiro ** If -v and RestrictExpand, reset 158490792Sgshapiro ** Verbose to prevent normal users 158590792Sgshapiro ** from seeing the expansion of 158690792Sgshapiro ** aliases/forwards/:include:s 158790792Sgshapiro */ 158890792Sgshapiro 158990792Sgshapiro if (tTd(65, 1)) 159090792Sgshapiro sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n", 159190792Sgshapiro (int) RealUid); 159290792Sgshapiro Verbose = 0; 159390792Sgshapiro } 159490792Sgshapiro break; 159590792Sgshapiro } 159638032Speter } 159738032Speter 159838032Speter if (MeToo) 159938032Speter BlankEnvelope.e_flags |= EF_METOO; 160038032Speter 160138032Speter switch (OpMode) 160238032Speter { 160338032Speter case MD_TEST: 160438032Speter /* don't have persistent host status in test mode */ 160538032Speter HostStatDir = NULL; 160638032Speter if (Verbose == 0) 160738032Speter Verbose = 2; 160890792Sgshapiro BlankEnvelope.e_errormode = EM_PRINT; 160990792Sgshapiro HoldErrs = false; 161038032Speter break; 161138032Speter 161238032Speter case MD_VERIFY: 161390792Sgshapiro BlankEnvelope.e_errormode = EM_PRINT; 161490792Sgshapiro HoldErrs = false; 161538032Speter /* arrange to exit cleanly on hangup signal */ 161690792Sgshapiro if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 161790792Sgshapiro (void) sm_signal(SIGHUP, intsig); 161890792Sgshapiro if (geteuid() != 0) 161990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 162090792Sgshapiro "Notice: -bv may give misleading output for non-privileged user\n"); 162138032Speter break; 162238032Speter 162338032Speter case MD_FGDAEMON: 162490792Sgshapiro run_in_foreground = true; 162590792Sgshapiro set_op_mode(MD_DAEMON); 162664562Sgshapiro /* FALLTHROUGH */ 162738032Speter 162838032Speter case MD_DAEMON: 162990792Sgshapiro vendor_daemon_setup(&BlankEnvelope); 163038032Speter 163138032Speter /* remove things that don't make sense in daemon mode */ 163238032Speter FullName = NULL; 163390792Sgshapiro GrabTo = false; 163438032Speter 163538032Speter /* arrange to restart on hangup signal */ 163638032Speter if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') 163738032Speter sm_syslog(LOG_WARNING, NOQID, 163864562Sgshapiro "daemon invoked without full pathname; kill -1 won't work"); 163938032Speter break; 164038032Speter 164138032Speter case MD_INITALIAS: 164238032Speter Verbose = 2; 164390792Sgshapiro BlankEnvelope.e_errormode = EM_PRINT; 164490792Sgshapiro HoldErrs = false; 164564562Sgshapiro /* FALLTHROUGH */ 164638032Speter 164738032Speter default: 164838032Speter /* arrange to exit cleanly on hangup signal */ 164990792Sgshapiro if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 165090792Sgshapiro (void) sm_signal(SIGHUP, intsig); 165138032Speter break; 165238032Speter } 165338032Speter 165438032Speter /* special considerations for FullName */ 165538032Speter if (FullName != NULL) 165638032Speter { 165738032Speter char *full = NULL; 165838032Speter 165938032Speter /* full names can't have newlines */ 166064562Sgshapiro if (strchr(FullName, '\n') != NULL) 166138032Speter { 166290792Sgshapiro full = newstr(denlstring(FullName, true, true)); 166373188Sgshapiro FullName = full; 166438032Speter } 166573188Sgshapiro 166638032Speter /* check for characters that may have to be quoted */ 166738032Speter if (!rfc822_string(FullName)) 166838032Speter { 166938032Speter /* 167038032Speter ** Quote a full name with special characters 167138032Speter ** as a comment so crackaddr() doesn't destroy 167238032Speter ** the name portion of the address. 167338032Speter */ 167473188Sgshapiro 167590792Sgshapiro FullName = addquotes(FullName, NULL); 167638032Speter if (full != NULL) 167790792Sgshapiro sm_free(full); /* XXX */ 167838032Speter } 167938032Speter } 168038032Speter 168138032Speter /* do heuristic mode adjustment */ 168238032Speter if (Verbose) 168338032Speter { 168438032Speter /* turn off noconnect option */ 168590792Sgshapiro setoption('c', "F", true, false, &BlankEnvelope); 168638032Speter 168738032Speter /* turn on interactive delivery */ 168890792Sgshapiro setoption('d', "", true, false, &BlankEnvelope); 168938032Speter } 169038032Speter 169142575Speter#ifdef VENDOR_CODE 169242575Speter /* check for vendor mismatch */ 169342575Speter if (VendorCode != VENDOR_CODE) 169442575Speter { 169542575Speter message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s", 169642575Speter getvendor(VENDOR_CODE), getvendor(VendorCode)); 169742575Speter } 169864562Sgshapiro#endif /* VENDOR_CODE */ 169964562Sgshapiro 170038032Speter /* check for out of date configuration level */ 170138032Speter if (ConfigLevel < MAXCONFIGLEVEL) 170238032Speter { 170338032Speter message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d", 170438032Speter Version, MAXCONFIGLEVEL, ConfigLevel); 170538032Speter } 170638032Speter 170738032Speter if (ConfigLevel < 3) 170890792Sgshapiro UseErrorsTo = true; 170938032Speter 171038032Speter /* set options that were previous macros */ 171138032Speter if (SmtpGreeting == NULL) 171238032Speter { 171390792Sgshapiro if (ConfigLevel < 7 && 171490792Sgshapiro (p = macvalue('e', &BlankEnvelope)) != NULL) 171538032Speter SmtpGreeting = newstr(p); 171638032Speter else 171738032Speter SmtpGreeting = "\201j Sendmail \201v ready at \201b"; 171838032Speter } 171938032Speter if (UnixFromLine == NULL) 172038032Speter { 172190792Sgshapiro if (ConfigLevel < 7 && 172290792Sgshapiro (p = macvalue('l', &BlankEnvelope)) != NULL) 172338032Speter UnixFromLine = newstr(p); 172438032Speter else 172538032Speter UnixFromLine = "From \201g \201d"; 172638032Speter } 172764562Sgshapiro SmtpError[0] = '\0'; 172838032Speter 172938032Speter /* our name for SMTP codes */ 173090792Sgshapiro expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); 173173188Sgshapiro if (jbuf[0] == '\0') 173290792Sgshapiro PSTRSET(MyHostName, "localhost"); 173373188Sgshapiro else 173490792Sgshapiro PSTRSET(MyHostName, jbuf); 173573188Sgshapiro if (strchr(MyHostName, '.') == NULL) 1736132943Sgshapiro message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?", 173773188Sgshapiro MyHostName); 173838032Speter 173938032Speter /* make certain that this name is part of the $=w class */ 174038032Speter setclass('w', MyHostName); 174138032Speter 174290792Sgshapiro /* fill in the structure of the *default* queue */ 174390792Sgshapiro st = stab("mqueue", ST_QUEUE, ST_FIND); 174490792Sgshapiro if (st == NULL) 174590792Sgshapiro syserr("No default queue (mqueue) defined"); 174690792Sgshapiro else 174790792Sgshapiro set_def_queueval(st->s_quegrp, true); 174890792Sgshapiro 174938032Speter /* the indices of built-in mailers */ 175038032Speter st = stab("local", ST_MAILER, ST_FIND); 175138032Speter if (st != NULL) 175238032Speter LocalMailer = st->s_mailer; 175338032Speter else if (OpMode != MD_TEST || !warn_C_flag) 175438032Speter syserr("No local mailer defined"); 175538032Speter 175638032Speter st = stab("prog", ST_MAILER, ST_FIND); 175738032Speter if (st == NULL) 175838032Speter syserr("No prog mailer defined"); 175938032Speter else 176038032Speter { 176138032Speter ProgMailer = st->s_mailer; 176238032Speter clrbitn(M_MUSER, ProgMailer->m_flags); 176338032Speter } 176438032Speter 176538032Speter st = stab("*file*", ST_MAILER, ST_FIND); 176638032Speter if (st == NULL) 176738032Speter syserr("No *file* mailer defined"); 176838032Speter else 176938032Speter { 177038032Speter FileMailer = st->s_mailer; 177138032Speter clrbitn(M_MUSER, FileMailer->m_flags); 177238032Speter } 177338032Speter 177438032Speter st = stab("*include*", ST_MAILER, ST_FIND); 177538032Speter if (st == NULL) 177638032Speter syserr("No *include* mailer defined"); 177738032Speter else 177838032Speter InclMailer = st->s_mailer; 177938032Speter 178038032Speter if (ConfigLevel < 6) 178138032Speter { 178238032Speter /* heuristic tweaking of local mailer for back compat */ 178338032Speter if (LocalMailer != NULL) 178438032Speter { 178538032Speter setbitn(M_ALIASABLE, LocalMailer->m_flags); 178638032Speter setbitn(M_HASPWENT, LocalMailer->m_flags); 178738032Speter setbitn(M_TRYRULESET5, LocalMailer->m_flags); 178838032Speter setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); 178938032Speter setbitn(M_CHECKPROG, LocalMailer->m_flags); 179038032Speter setbitn(M_CHECKFILE, LocalMailer->m_flags); 179138032Speter setbitn(M_CHECKUDB, LocalMailer->m_flags); 179238032Speter } 179338032Speter if (ProgMailer != NULL) 179438032Speter setbitn(M_RUNASRCPT, ProgMailer->m_flags); 179538032Speter if (FileMailer != NULL) 179638032Speter setbitn(M_RUNASRCPT, FileMailer->m_flags); 179738032Speter } 179838032Speter if (ConfigLevel < 7) 179938032Speter { 180038032Speter if (LocalMailer != NULL) 180138032Speter setbitn(M_VRFY250, LocalMailer->m_flags); 180238032Speter if (ProgMailer != NULL) 180338032Speter setbitn(M_VRFY250, ProgMailer->m_flags); 180438032Speter if (FileMailer != NULL) 180538032Speter setbitn(M_VRFY250, FileMailer->m_flags); 180638032Speter } 180738032Speter 180838032Speter /* MIME Content-Types that cannot be transfer encoded */ 180938032Speter setclass('n', "multipart/signed"); 181038032Speter 181138032Speter /* MIME message/xxx subtypes that can be treated as messages */ 181238032Speter setclass('s', "rfc822"); 181338032Speter 181438032Speter /* MIME Content-Transfer-Encodings that can be encoded */ 181538032Speter setclass('e', "7bit"); 181638032Speter setclass('e', "8bit"); 181738032Speter setclass('e', "binary"); 181838032Speter 181938032Speter#ifdef USE_B_CLASS 182038032Speter /* MIME Content-Types that should be treated as binary */ 182138032Speter setclass('b', "image"); 182238032Speter setclass('b', "audio"); 182338032Speter setclass('b', "video"); 182438032Speter setclass('b', "application/octet-stream"); 182564562Sgshapiro#endif /* USE_B_CLASS */ 182638032Speter 182742575Speter /* MIME headers which have fields to check for overflow */ 182890792Sgshapiro setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition"); 182990792Sgshapiro setclass(macid("{checkMIMEFieldHeaders}"), "content-type"); 183042575Speter 183142575Speter /* MIME headers to check for length overflow */ 183290792Sgshapiro setclass(macid("{checkMIMETextHeaders}"), "content-description"); 183342575Speter 183442575Speter /* MIME headers to check for overflow and rebalance */ 183590792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-disposition"); 183690792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-id"); 183790792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding"); 183890792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-type"); 183990792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "mime-version"); 184042575Speter 184190792Sgshapiro /* Macros to save in the queue file -- don't remove any */ 184290792Sgshapiro setclass(macid("{persistentMacros}"), "r"); 184390792Sgshapiro setclass(macid("{persistentMacros}"), "s"); 184490792Sgshapiro setclass(macid("{persistentMacros}"), "_"); 184590792Sgshapiro setclass(macid("{persistentMacros}"), "{if_addr}"); 184690792Sgshapiro setclass(macid("{persistentMacros}"), "{daemon_flags}"); 184764562Sgshapiro 184838032Speter /* operate in queue directory */ 184990792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 185038032Speter { 185138032Speter if (OpMode != MD_TEST) 185238032Speter { 185338032Speter syserr("QueueDirectory (Q) option must be set"); 185438032Speter ExitStat = EX_CONFIG; 185538032Speter } 185638032Speter } 185738032Speter else 185838032Speter { 185964562Sgshapiro if (OpMode != MD_TEST) 186090792Sgshapiro setup_queues(OpMode == MD_DAEMON); 186138032Speter } 186238032Speter 186338032Speter /* check host status directory for validity */ 186490792Sgshapiro if (HostStatDir != NULL && !path_is_dir(HostStatDir, false)) 186538032Speter { 186638032Speter /* cannot use this value */ 186790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 186890792Sgshapiro "Warning: Cannot use HostStatusDirectory = %s: %s\n", 186990792Sgshapiro HostStatDir, sm_errstring(errno)); 187038032Speter HostStatDir = NULL; 187138032Speter } 187238032Speter 187390792Sgshapiro if (OpMode == MD_QUEUERUN && 187490792Sgshapiro RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) 187538032Speter { 187638032Speter struct stat stbuf; 187738032Speter 187838032Speter /* check to see if we own the queue directory */ 187938032Speter if (stat(".", &stbuf) < 0) 188038032Speter syserr("main: cannot stat %s", QueueDir); 188138032Speter if (stbuf.st_uid != RealUid) 188238032Speter { 188338032Speter /* nope, really a botch */ 188490792Sgshapiro HoldErrs = false; 188538032Speter usrerr("You do not have permission to process the queue"); 188690792Sgshapiro finis(false, true, EX_NOPERM); 188790792Sgshapiro /* NOTREACHED */ 188838032Speter } 188938032Speter } 189038032Speter 189190792Sgshapiro#if MILTER 189264562Sgshapiro /* sanity checks on milter filters */ 189364562Sgshapiro if (OpMode == MD_DAEMON || OpMode == MD_SMTP) 189490792Sgshapiro { 189590792Sgshapiro milter_config(InputFilterList, InputFilters, MAXFILTERS); 189690792Sgshapiro setup_daemon_milters(); 189790792Sgshapiro } 189890792Sgshapiro#endif /* MILTER */ 189964562Sgshapiro 190090792Sgshapiro /* Convert queuegroup string to qgrp number */ 190190792Sgshapiro if (queuegroup != NULL) 190290792Sgshapiro { 190390792Sgshapiro qgrp = name2qid(queuegroup); 190490792Sgshapiro if (qgrp == NOQGRP) 190590792Sgshapiro { 190690792Sgshapiro HoldErrs = false; 190790792Sgshapiro usrerr("Queue group %s unknown", queuegroup); 190890792Sgshapiro finis(false, true, ExitStat); 190990792Sgshapiro /* NOTREACHED */ 191090792Sgshapiro } 191190792Sgshapiro } 191266494Sgshapiro 191338032Speter /* if we've had errors so far, exit now */ 191438032Speter if (ExitStat != EX_OK && OpMode != MD_TEST) 191590792Sgshapiro { 191690792Sgshapiro finis(false, true, ExitStat); 191790792Sgshapiro /* NOTREACHED */ 191890792Sgshapiro } 191938032Speter 192090792Sgshapiro#if SASL 192190792Sgshapiro /* sendmail specific SASL initialization */ 192290792Sgshapiro sm_sasl_init(); 192390792Sgshapiro#endif /* SASL */ 192490792Sgshapiro 192538032Speter#if XDEBUG 192638032Speter checkfd012("before main() initmaps"); 192764562Sgshapiro#endif /* XDEBUG */ 192838032Speter 192938032Speter /* 193038032Speter ** Do operation-mode-dependent initialization. 193138032Speter */ 193238032Speter 193338032Speter switch (OpMode) 193438032Speter { 193538032Speter case MD_PRINT: 193638032Speter /* print the queue */ 193790792Sgshapiro HoldErrs = false; 193890792Sgshapiro dropenvelope(&BlankEnvelope, true, false); 193990792Sgshapiro (void) sm_signal(SIGPIPE, sigpipe); 194090792Sgshapiro if (qgrp != NOQGRP) 194190792Sgshapiro { 194290792Sgshapiro int j; 194390792Sgshapiro 194490792Sgshapiro /* Selecting a particular queue group to run */ 194590792Sgshapiro for (j = 0; j < Queue[qgrp]->qg_numqueues; j++) 194690792Sgshapiro { 194790792Sgshapiro if (StopRequest) 194890792Sgshapiro stop_sendmail(); 194990792Sgshapiro (void) print_single_queue(qgrp, j); 195090792Sgshapiro } 195190792Sgshapiro finis(false, true, EX_OK); 195290792Sgshapiro /* NOTREACHED */ 195390792Sgshapiro } 195438032Speter printqueue(); 195590792Sgshapiro finis(false, true, EX_OK); 195690792Sgshapiro /* NOTREACHED */ 195742575Speter break; 195838032Speter 195990792Sgshapiro case MD_PRINTNQE: 196090792Sgshapiro /* print number of entries in queue */ 196190792Sgshapiro dropenvelope(&BlankEnvelope, true, false); 196290792Sgshapiro (void) sm_signal(SIGPIPE, sigpipe); 196390792Sgshapiro printnqe(smioout, NULL); 196490792Sgshapiro finis(false, true, EX_OK); 196590792Sgshapiro /* NOTREACHED */ 196690792Sgshapiro break; 196790792Sgshapiro 196890792Sgshapiro case MD_QUEUERUN: 196990792Sgshapiro /* only handle quarantining here */ 197090792Sgshapiro if (quarantining == NULL) 197190792Sgshapiro break; 197290792Sgshapiro 197390792Sgshapiro if (QueueMode != QM_QUARANTINE && 197490792Sgshapiro QueueMode != QM_NORMAL) 197590792Sgshapiro { 197690792Sgshapiro HoldErrs = false; 197790792Sgshapiro usrerr("Can not use -Q with -q%c", QueueMode); 197890792Sgshapiro ExitStat = EX_USAGE; 197990792Sgshapiro finis(false, true, ExitStat); 198090792Sgshapiro /* NOTREACHED */ 198190792Sgshapiro } 198290792Sgshapiro quarantine_queue(quarantining, qgrp); 198390792Sgshapiro finis(false, true, EX_OK); 198490792Sgshapiro break; 198590792Sgshapiro 198638032Speter case MD_HOSTSTAT: 198790792Sgshapiro (void) sm_signal(SIGPIPE, sigpipe); 198864562Sgshapiro (void) mci_traverse_persistent(mci_print_persistent, NULL); 198990792Sgshapiro finis(false, true, EX_OK); 199090792Sgshapiro /* NOTREACHED */ 199164562Sgshapiro break; 199238032Speter 199338032Speter case MD_PURGESTAT: 199464562Sgshapiro (void) mci_traverse_persistent(mci_purge_persistent, NULL); 199590792Sgshapiro finis(false, true, EX_OK); 199690792Sgshapiro /* NOTREACHED */ 199764562Sgshapiro break; 199838032Speter 199938032Speter case MD_INITALIAS: 200042575Speter /* initialize maps */ 200164562Sgshapiro initmaps(); 200290792Sgshapiro finis(false, true, ExitStat); 200390792Sgshapiro /* NOTREACHED */ 200442575Speter break; 200538032Speter 200638032Speter case MD_SMTP: 200738032Speter case MD_DAEMON: 200838032Speter /* reset DSN parameters */ 200938032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 201090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 201190792Sgshapiro macid("{dsn_notify}"), NULL); 201290792Sgshapiro BlankEnvelope.e_envid = NULL; 201390792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 201490792Sgshapiro macid("{dsn_envid}"), NULL); 201590792Sgshapiro BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); 201690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 201790792Sgshapiro macid("{dsn_ret}"), NULL); 201838032Speter 201942575Speter /* don't open maps for daemon -- done below in child */ 202038032Speter break; 202138032Speter } 202238032Speter 202338032Speter if (tTd(0, 15)) 202438032Speter { 202538032Speter /* print configuration table (or at least part of it) */ 202638032Speter if (tTd(0, 90)) 202738032Speter printrules(); 202838032Speter for (i = 0; i < MAXMAILERS; i++) 202938032Speter { 203038032Speter if (Mailer[i] != NULL) 2031132943Sgshapiro printmailer(sm_debug_file(), Mailer[i]); 203238032Speter } 203338032Speter } 203438032Speter 203538032Speter /* 203638032Speter ** Switch to the main envelope. 203738032Speter */ 203838032Speter 203990792Sgshapiro CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope, 204090792Sgshapiro sm_rpool_new_x(NULL)); 204138032Speter MainEnvelope.e_flags = BlankEnvelope.e_flags; 204238032Speter 204338032Speter /* 204438032Speter ** If test mode, read addresses from stdin and process. 204538032Speter */ 204638032Speter 204738032Speter if (OpMode == MD_TEST) 204838032Speter { 204990792Sgshapiro if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL))) 205038032Speter Verbose = 2; 205138032Speter 205238032Speter if (Verbose) 205338032Speter { 205490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 205590792Sgshapiro "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); 205690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 205790792Sgshapiro "Enter <ruleset> <address>\n"); 205838032Speter } 205990792Sgshapiro macdefine(&(MainEnvelope.e_macro), A_PERM, 206090792Sgshapiro macid("{addr_type}"), "e r"); 206138032Speter for (;;) 206238032Speter { 206390792Sgshapiro SM_TRY 206490792Sgshapiro { 206590792Sgshapiro (void) sm_signal(SIGINT, intindebug); 206690792Sgshapiro (void) sm_releasesignal(SIGINT); 206790792Sgshapiro if (Verbose == 2) 206890792Sgshapiro (void) sm_io_fprintf(smioout, 206990792Sgshapiro SM_TIME_DEFAULT, 207090792Sgshapiro "> "); 207190792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 207290792Sgshapiro if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, 207390792Sgshapiro sizeof buf) == NULL) 207490792Sgshapiro testmodeline("/quit", &MainEnvelope); 207590792Sgshapiro p = strchr(buf, '\n'); 207690792Sgshapiro if (p != NULL) 207790792Sgshapiro *p = '\0'; 207890792Sgshapiro if (Verbose < 2) 207990792Sgshapiro (void) sm_io_fprintf(smioout, 208090792Sgshapiro SM_TIME_DEFAULT, 208190792Sgshapiro "> %s\n", buf); 208290792Sgshapiro testmodeline(buf, &MainEnvelope); 208390792Sgshapiro } 208490792Sgshapiro SM_EXCEPT(exc, "[!F]*") 208590792Sgshapiro { 208690792Sgshapiro /* 208790792Sgshapiro ** 8.10 just prints \n on interrupt. 208890792Sgshapiro ** I'm printing the exception here in case 208990792Sgshapiro ** sendmail is extended to raise additional 209090792Sgshapiro ** exceptions in this context. 209190792Sgshapiro */ 209290792Sgshapiro 209390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 209490792Sgshapiro "\n"); 209590792Sgshapiro sm_exc_print(exc, smioout); 209690792Sgshapiro } 209790792Sgshapiro SM_END_TRY 209838032Speter } 209938032Speter } 210038032Speter 210190792Sgshapiro#if STARTTLS 210290792Sgshapiro tls_ok = true; 210390792Sgshapiro if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER) 210490792Sgshapiro { 210590792Sgshapiro /* check whether STARTTLS is turned off for the client */ 210690792Sgshapiro if (chkclientmodifiers(D_NOTLS)) 210790792Sgshapiro tls_ok = false; 210890792Sgshapiro } 210990792Sgshapiro else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON || 211090792Sgshapiro OpMode == MD_SMTP) 211190792Sgshapiro { 211290792Sgshapiro /* check whether STARTTLS is turned off for the server */ 211390792Sgshapiro if (chkdaemonmodifiers(D_NOTLS)) 211490792Sgshapiro tls_ok = false; 211590792Sgshapiro } 211690792Sgshapiro else /* other modes don't need STARTTLS */ 211790792Sgshapiro tls_ok = false; 211864562Sgshapiro 211990792Sgshapiro if (tls_ok) 212090792Sgshapiro { 212190792Sgshapiro /* basic TLS initialization */ 212290792Sgshapiro tls_ok = init_tls_library(); 212390792Sgshapiro } 212490792Sgshapiro 212590792Sgshapiro if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)) 212690792Sgshapiro { 212790792Sgshapiro /* disable TLS for client */ 212890792Sgshapiro setclttls(false); 212990792Sgshapiro } 213090792Sgshapiro#endif /* STARTTLS */ 213190792Sgshapiro 213264562Sgshapiro /* 213338032Speter ** If collecting stuff from the queue, go start doing that. 213438032Speter */ 213538032Speter 213664562Sgshapiro if (OpMode == MD_QUEUERUN && QueueIntvl == 0) 213738032Speter { 213890792Sgshapiro pid_t pid = -1; 213990792Sgshapiro 214090792Sgshapiro#if STARTTLS 214190792Sgshapiro /* init TLS for client, ignore result for now */ 214290792Sgshapiro (void) initclttls(tls_ok); 214390792Sgshapiro#endif /* STARTTLS */ 214490792Sgshapiro 214590792Sgshapiro /* 214690792Sgshapiro ** The parent process of the caller of runqueue() needs 214790792Sgshapiro ** to stay around for a possible SIGTERM. The SIGTERM will 214890792Sgshapiro ** tell this process that all of the queue runners children 214990792Sgshapiro ** need to be sent SIGTERM as well. At the same time, we 215090792Sgshapiro ** want to return control to the command line. So we do an 215190792Sgshapiro ** extra fork(). 215290792Sgshapiro */ 215390792Sgshapiro 215490792Sgshapiro if (Verbose || foregroundqueue || (pid = fork()) <= 0) 215564562Sgshapiro { 215690792Sgshapiro /* 215790792Sgshapiro ** If the fork() failed we should still try to do 215890792Sgshapiro ** the queue run. If it succeeded then the child 215990792Sgshapiro ** is going to start the run and wait for all 216090792Sgshapiro ** of the children to finish. 216190792Sgshapiro */ 216290792Sgshapiro 216390792Sgshapiro if (pid == 0) 216490792Sgshapiro { 216590792Sgshapiro /* Reset global flags */ 216690792Sgshapiro RestartRequest = NULL; 216790792Sgshapiro ShutdownRequest = NULL; 216890792Sgshapiro PendingSignal = 0; 216990792Sgshapiro 217090792Sgshapiro /* disconnect from terminal */ 217190792Sgshapiro disconnect(2, CurEnv); 217290792Sgshapiro } 217390792Sgshapiro 217490792Sgshapiro CurrentPid = getpid(); 217590792Sgshapiro if (qgrp != NOQGRP) 217690792Sgshapiro { 2177110560Sgshapiro int rwgflags = RWG_NONE; 2178110560Sgshapiro 217990792Sgshapiro /* 218090792Sgshapiro ** To run a specific queue group mark it to 218190792Sgshapiro ** be run, select the work group it's in and 218290792Sgshapiro ** increment the work counter. 218390792Sgshapiro */ 218490792Sgshapiro 218594334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; 218694334Sgshapiro i++) 218794334Sgshapiro Queue[i]->qg_nextrun = (time_t) -1; 218894334Sgshapiro Queue[qgrp]->qg_nextrun = 0; 2189110560Sgshapiro if (Verbose) 2190110560Sgshapiro rwgflags |= RWG_VERBOSE; 2191110560Sgshapiro if (queuepersistent) 2192110560Sgshapiro rwgflags |= RWG_PERSISTENT; 2193110560Sgshapiro rwgflags |= RWG_FORCE; 219490792Sgshapiro (void) run_work_group(Queue[qgrp]->qg_wgrp, 2195110560Sgshapiro rwgflags); 219690792Sgshapiro } 219790792Sgshapiro else 219890792Sgshapiro (void) runqueue(false, Verbose, 219990792Sgshapiro queuepersistent, true); 220090792Sgshapiro 220190792Sgshapiro /* set the title to make it easier to find */ 220290792Sgshapiro sm_setproctitle(true, CurEnv, "Queue control"); 220390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 220490792Sgshapiro while (CurChildren > 0) 220590792Sgshapiro { 220690792Sgshapiro int status; 220790792Sgshapiro pid_t ret; 220890792Sgshapiro 2209125820Sgshapiro errno = 0; 221090792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 2211125820Sgshapiro { 2212125820Sgshapiro if (errno == ECHILD) 2213125820Sgshapiro { 2214125820Sgshapiro /* 2215125820Sgshapiro ** Oops... something got messed 2216125820Sgshapiro ** up really bad. Waiting for 2217125820Sgshapiro ** non-existent children 2218125820Sgshapiro ** shouldn't happen. Let's get 2219125820Sgshapiro ** out of here. 2220125820Sgshapiro */ 2221125820Sgshapiro 2222125820Sgshapiro CurChildren = 0; 2223125820Sgshapiro break; 2224125820Sgshapiro } 222590792Sgshapiro continue; 2226125820Sgshapiro } 222790792Sgshapiro 2228125820Sgshapiro /* something is really really wrong */ 2229125820Sgshapiro if (errno == ECHILD) 2230125820Sgshapiro { 2231125820Sgshapiro sm_syslog(LOG_ERR, NOQID, 2232125820Sgshapiro "queue control process: lost all children: wait returned ECHILD"); 2233125820Sgshapiro break; 2234125820Sgshapiro } 2235125820Sgshapiro 223690792Sgshapiro /* Only drop when a child gives status */ 223790792Sgshapiro if (WIFSTOPPED(status)) 223890792Sgshapiro continue; 223990792Sgshapiro 224090792Sgshapiro proc_list_drop(ret, status, NULL); 224190792Sgshapiro } 224264562Sgshapiro } 224390792Sgshapiro finis(true, true, ExitStat); 224490792Sgshapiro /* NOTREACHED */ 224538032Speter } 224638032Speter 224771345Sgshapiro# if SASL 224871345Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 224971345Sgshapiro { 225090792Sgshapiro /* check whether AUTH is turned off for the server */ 225190792Sgshapiro if (!chkdaemonmodifiers(D_NOAUTH) && 225290792Sgshapiro (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK) 225371345Sgshapiro syserr("!sasl_server_init failed! [%s]", 225490792Sgshapiro sasl_errstring(i, NULL, NULL)); 225571345Sgshapiro } 225671345Sgshapiro# endif /* SASL */ 225771345Sgshapiro 225890792Sgshapiro if (OpMode == MD_SMTP) 225990792Sgshapiro { 226090792Sgshapiro proc_list_add(CurrentPid, "Sendmail SMTP Agent", 2261132943Sgshapiro PROC_DAEMON, 0, -1, NULL); 226290792Sgshapiro 226390792Sgshapiro /* clean up background delivery children */ 226490792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 226590792Sgshapiro } 226690792Sgshapiro 226738032Speter /* 226838032Speter ** If a daemon, wait for a request. 226938032Speter ** getrequests will always return in a child. 227038032Speter ** If we should also be processing the queue, start 227138032Speter ** doing it in background. 227238032Speter ** We check for any errors that might have happened 227338032Speter ** during startup. 227438032Speter */ 227538032Speter 227698841Sgshapiro if (OpMode == MD_DAEMON || QueueIntvl > 0) 227738032Speter { 227838032Speter char dtype[200]; 227938032Speter 228038032Speter if (!run_in_foreground && !tTd(99, 100)) 228138032Speter { 228238032Speter /* put us in background */ 228338032Speter i = fork(); 228438032Speter if (i < 0) 228538032Speter syserr("daemon: cannot fork"); 228638032Speter if (i != 0) 228790792Sgshapiro { 228890792Sgshapiro finis(false, true, EX_OK); 228990792Sgshapiro /* NOTREACHED */ 229090792Sgshapiro } 229138032Speter 229290792Sgshapiro /* 229390792Sgshapiro ** Initialize exception stack and default exception 229490792Sgshapiro ** handler for child process. 229590792Sgshapiro */ 229690792Sgshapiro 229790792Sgshapiro /* Reset global flags */ 229890792Sgshapiro RestartRequest = NULL; 229990792Sgshapiro RestartWorkGroup = false; 230090792Sgshapiro ShutdownRequest = NULL; 230190792Sgshapiro PendingSignal = 0; 230290792Sgshapiro CurrentPid = getpid(); 230390792Sgshapiro 230490792Sgshapiro sm_exc_newthread(fatal_error); 230590792Sgshapiro 230638032Speter /* disconnect from our controlling tty */ 230790792Sgshapiro disconnect(2, &MainEnvelope); 230838032Speter } 230938032Speter 231038032Speter dtype[0] = '\0'; 231138032Speter if (OpMode == MD_DAEMON) 231290792Sgshapiro { 231390792Sgshapiro (void) sm_strlcat(dtype, "+SMTP", sizeof dtype); 231490792Sgshapiro DaemonPid = CurrentPid; 231590792Sgshapiro } 231698841Sgshapiro if (QueueIntvl > 0) 231738032Speter { 231890792Sgshapiro (void) sm_strlcat2(dtype, 231990792Sgshapiro queuepersistent 232090792Sgshapiro ? "+persistent-queueing@" 232190792Sgshapiro : "+queueing@", 232290792Sgshapiro pintvl(QueueIntvl, true), 232390792Sgshapiro sizeof dtype); 232438032Speter } 232538032Speter if (tTd(0, 1)) 232690792Sgshapiro (void) sm_strlcat(dtype, "+debugging", sizeof dtype); 232738032Speter 232838032Speter sm_syslog(LOG_INFO, NOQID, 232964562Sgshapiro "starting daemon (%s): %s", Version, dtype + 1); 233090792Sgshapiro#if XLA 233138032Speter xla_create_file(); 233264562Sgshapiro#endif /* XLA */ 233338032Speter 233464562Sgshapiro /* save daemon type in a macro for possible PidFile use */ 233590792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 233690792Sgshapiro macid("{daemon_info}"), dtype + 1); 233764562Sgshapiro 233864562Sgshapiro /* save queue interval in a macro for possible PidFile use */ 233990792Sgshapiro macdefine(&MainEnvelope.e_macro, A_TEMP, 234090792Sgshapiro macid("{queue_interval}"), pintvl(QueueIntvl, true)); 234164562Sgshapiro 234290792Sgshapiro /* workaround: can't seem to release the signal in the parent */ 234390792Sgshapiro (void) sm_signal(SIGHUP, sighup); 234490792Sgshapiro (void) sm_releasesignal(SIGHUP); 234590792Sgshapiro (void) sm_signal(SIGTERM, sigterm); 234690792Sgshapiro 234798841Sgshapiro if (QueueIntvl > 0) 234838032Speter { 234990792Sgshapiro (void) runqueue(true, false, queuepersistent, true); 235090792Sgshapiro 235190792Sgshapiro /* 235290792Sgshapiro ** If queuepersistent but not in daemon mode then 235390792Sgshapiro ** we're going to do the queue runner monitoring here. 235490792Sgshapiro ** If in daemon mode then the monitoring will happen 235590792Sgshapiro ** elsewhere. 235690792Sgshapiro */ 235790792Sgshapiro 235890792Sgshapiro if (OpMode != MD_DAEMON && queuepersistent) 235990792Sgshapiro { 2360132943Sgshapiro /* 2361132943Sgshapiro ** Write the pid to file 2362132943Sgshapiro ** XXX Overwrites sendmail.pid 2363132943Sgshapiro */ 2364132943Sgshapiro 2365132943Sgshapiro log_sendmail_pid(&MainEnvelope); 2366132943Sgshapiro 236790792Sgshapiro /* set the title to make it easier to find */ 236890792Sgshapiro sm_setproctitle(true, CurEnv, "Queue control"); 236990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 237090792Sgshapiro while (CurChildren > 0) 237190792Sgshapiro { 237290792Sgshapiro int status; 237390792Sgshapiro pid_t ret; 237490792Sgshapiro int group; 237590792Sgshapiro 2376120256Sgshapiro CHECK_RESTART; 2377125820Sgshapiro errno = 0; 237890792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 2379125820Sgshapiro { 2380125820Sgshapiro /* 2381125820Sgshapiro ** Waiting for non-existent 2382125820Sgshapiro ** children shouldn't happen. 2383125820Sgshapiro ** Let's get out of here if 2384125820Sgshapiro ** it occurs. 2385125820Sgshapiro */ 2386125820Sgshapiro 2387125820Sgshapiro if (errno == ECHILD) 2388125820Sgshapiro { 2389125820Sgshapiro CurChildren = 0; 2390125820Sgshapiro break; 2391125820Sgshapiro } 239290792Sgshapiro continue; 2393125820Sgshapiro } 239490792Sgshapiro 2395125820Sgshapiro /* something is really really wrong */ 2396125820Sgshapiro if (errno == ECHILD) 2397125820Sgshapiro { 2398125820Sgshapiro sm_syslog(LOG_ERR, NOQID, 2399125820Sgshapiro "persistent queue runner control process: lost all children: wait returned ECHILD"); 2400125820Sgshapiro break; 2401125820Sgshapiro } 2402125820Sgshapiro 240390792Sgshapiro if (WIFSTOPPED(status)) 240490792Sgshapiro continue; 240590792Sgshapiro 240690792Sgshapiro /* Probe only on a child status */ 240790792Sgshapiro proc_list_drop(ret, status, &group); 240890792Sgshapiro 240990792Sgshapiro if (WIFSIGNALED(status)) 241090792Sgshapiro { 241190792Sgshapiro if (WCOREDUMP(status)) 241290792Sgshapiro { 241390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 241490792Sgshapiro "persistent queue runner=%d core dumped, signal=%d", 241590792Sgshapiro group, WTERMSIG(status)); 241690792Sgshapiro 2417120256Sgshapiro /* don't restart this */ 2418120256Sgshapiro mark_work_group_restart( 2419120256Sgshapiro group, -1); 242090792Sgshapiro continue; 242190792Sgshapiro } 242290792Sgshapiro 242390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 242490792Sgshapiro "persistent queue runner=%d died, signal=%d", 242590792Sgshapiro group, WTERMSIG(status)); 242690792Sgshapiro } 242790792Sgshapiro 242890792Sgshapiro /* 242990792Sgshapiro ** When debugging active, don't 243090792Sgshapiro ** restart the persistent queues. 243190792Sgshapiro ** But do log this as info. 243290792Sgshapiro */ 243390792Sgshapiro 243490792Sgshapiro if (sm_debug_active(&DebugNoPRestart, 243590792Sgshapiro 1)) 243690792Sgshapiro { 243790792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 243890792Sgshapiro "persistent queue runner=%d, exited", 243990792Sgshapiro group); 2440120256Sgshapiro mark_work_group_restart(group, 2441120256Sgshapiro -1); 244290792Sgshapiro } 244390792Sgshapiro } 244490792Sgshapiro finis(true, true, ExitStat); 244590792Sgshapiro /* NOTREACHED */ 244690792Sgshapiro } 244790792Sgshapiro 244838032Speter if (OpMode != MD_DAEMON) 244938032Speter { 245090792Sgshapiro char qtype[200]; 245190792Sgshapiro 245290792Sgshapiro /* 245390792Sgshapiro ** Write the pid to file 245490792Sgshapiro ** XXX Overwrites sendmail.pid 245590792Sgshapiro */ 245690792Sgshapiro 245790792Sgshapiro log_sendmail_pid(&MainEnvelope); 245890792Sgshapiro 245990792Sgshapiro /* set the title to make it easier to find */ 246090792Sgshapiro qtype[0] = '\0'; 246190792Sgshapiro (void) sm_strlcpyn(qtype, sizeof qtype, 4, 246290792Sgshapiro "Queue runner@", 246390792Sgshapiro pintvl(QueueIntvl, true), 246490792Sgshapiro " for ", 246590792Sgshapiro QueueDir); 246690792Sgshapiro sm_setproctitle(true, CurEnv, qtype); 246738032Speter for (;;) 246838032Speter { 246964562Sgshapiro (void) pause(); 2470132943Sgshapiro 2471120256Sgshapiro CHECK_RESTART; 2472132943Sgshapiro 247390792Sgshapiro if (doqueuerun()) 247490792Sgshapiro (void) runqueue(true, false, 247590792Sgshapiro false, false); 247638032Speter } 247738032Speter } 247838032Speter } 247990792Sgshapiro dropenvelope(&MainEnvelope, true, false); 248038032Speter 248190792Sgshapiro#if STARTTLS 248264562Sgshapiro /* init TLS for server, ignore result for now */ 248390792Sgshapiro (void) initsrvtls(tls_ok); 248490792Sgshapiro#endif /* STARTTLS */ 2485110560Sgshapiro 248690792Sgshapiro nextreq: 248790792Sgshapiro p_flags = getrequests(&MainEnvelope); 248838032Speter 248938032Speter /* drop privileges */ 249090792Sgshapiro (void) drop_privileges(false); 249138032Speter 249238032Speter /* 249338032Speter ** Get authentication data 249490792Sgshapiro ** Set _ macro in BlankEnvelope before calling newenvelope(). 249538032Speter */ 249638032Speter 249790792Sgshapiro authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 249890792Sgshapiro NULL), &forged); 249990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo); 250090792Sgshapiro 250190792Sgshapiro /* at this point we are in a child: reset state */ 250290792Sgshapiro sm_rpool_free(MainEnvelope.e_rpool); 250390792Sgshapiro (void) newenvelope(&MainEnvelope, &MainEnvelope, 250490792Sgshapiro sm_rpool_new_x(NULL)); 250538032Speter } 250638032Speter 250764562Sgshapiro if (LogLevel > 9) 250864562Sgshapiro { 250964562Sgshapiro /* log connection information */ 2510110560Sgshapiro sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo); 251164562Sgshapiro } 251264562Sgshapiro 251338032Speter /* 251438032Speter ** If running SMTP protocol, start collecting and executing 251538032Speter ** commands. This will never return. 251638032Speter */ 251738032Speter 251838032Speter if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 251938032Speter { 252038032Speter char pbuf[20]; 252138032Speter 252238032Speter /* 252338032Speter ** Save some macros for check_* rulesets. 252438032Speter */ 252538032Speter 252638032Speter if (forged) 252738032Speter { 252838032Speter char ipbuf[103]; 252938032Speter 253090792Sgshapiro (void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]", 253190792Sgshapiro anynet_ntoa(&RealHostAddr)); 253290792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 253390792Sgshapiro macid("{client_name}"), ipbuf); 253438032Speter } 253538032Speter else 253690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 253790792Sgshapiro macid("{client_name}"), RealHostName); 2538132943Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 2539132943Sgshapiro macid("{client_ptr}"), RealHostName); 254090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 254190792Sgshapiro macid("{client_addr}"), anynet_ntoa(&RealHostAddr)); 254290792Sgshapiro sm_getla(); 254338032Speter 254490792Sgshapiro switch (RealHostAddr.sa.sa_family) 254564562Sgshapiro { 254690792Sgshapiro#if NETINET 254764562Sgshapiro case AF_INET: 254890792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, "%d", 254990792Sgshapiro RealHostAddr.sin.sin_port); 255064562Sgshapiro break; 255190792Sgshapiro#endif /* NETINET */ 255290792Sgshapiro#if NETINET6 255364562Sgshapiro case AF_INET6: 255490792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, "%d", 255590792Sgshapiro RealHostAddr.sin6.sin6_port); 255664562Sgshapiro break; 255790792Sgshapiro#endif /* NETINET6 */ 255864562Sgshapiro default: 255990792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, "0"); 256064562Sgshapiro break; 256164562Sgshapiro } 256290792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 256390792Sgshapiro macid("{client_port}"), pbuf); 256442575Speter 256538032Speter if (OpMode == MD_DAEMON) 256638032Speter { 256738032Speter /* validate the connection */ 256890792Sgshapiro HoldErrs = true; 256938032Speter nullserver = validate_connection(&RealHostAddr, 2570132943Sgshapiro macvalue(macid("{client_name}"), 2571132943Sgshapiro &MainEnvelope), 2572132943Sgshapiro &MainEnvelope); 257390792Sgshapiro HoldErrs = false; 257438032Speter } 257564562Sgshapiro else if (p_flags == NULL) 257664562Sgshapiro { 257764562Sgshapiro p_flags = (BITMAP256 *) xalloc(sizeof *p_flags); 257864562Sgshapiro clrbitmap(p_flags); 257964562Sgshapiro } 258090792Sgshapiro#if STARTTLS 258164562Sgshapiro if (OpMode == MD_SMTP) 258290792Sgshapiro (void) initsrvtls(tls_ok); 258390792Sgshapiro#endif /* STARTTLS */ 258471345Sgshapiro 258590792Sgshapiro /* turn off profiling */ 258690792Sgshapiro SM_PROF(1); 258790792Sgshapiro smtp(nullserver, *p_flags, &MainEnvelope); 2588110560Sgshapiro 2589110560Sgshapiro if (tTd(93, 100)) 2590110560Sgshapiro { 2591110560Sgshapiro /* turn off profiling */ 2592110560Sgshapiro SM_PROF(0); 2593110560Sgshapiro if (OpMode == MD_DAEMON) 2594110560Sgshapiro goto nextreq; 2595110560Sgshapiro } 259638032Speter } 259738032Speter 259890792Sgshapiro sm_rpool_free(MainEnvelope.e_rpool); 259990792Sgshapiro clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL)); 260038032Speter if (OpMode == MD_VERIFY) 260138032Speter { 260290792Sgshapiro set_delivery_mode(SM_VERIFY, &MainEnvelope); 260338032Speter PostMasterCopy = NULL; 260438032Speter } 260538032Speter else 260638032Speter { 260738032Speter /* interactive -- all errors are global */ 260890792Sgshapiro MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER; 260938032Speter } 261038032Speter 261138032Speter /* 261238032Speter ** Do basic system initialization and set the sender 261338032Speter */ 261438032Speter 261590792Sgshapiro initsys(&MainEnvelope); 261690792Sgshapiro macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0"); 261790792Sgshapiro macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0"); 261890792Sgshapiro setsender(from, &MainEnvelope, NULL, '\0', false); 261964562Sgshapiro if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') && 262090792Sgshapiro (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) || 262190792Sgshapiro strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0)) 262264562Sgshapiro { 262390792Sgshapiro auth_warning(&MainEnvelope, "%s set sender to %s using -%c", 262490792Sgshapiro RealUserName, from, warn_f_flag); 262564562Sgshapiro#if SASL 262690792Sgshapiro auth = false; 262764562Sgshapiro#endif /* SASL */ 262864562Sgshapiro } 262964562Sgshapiro if (auth) 263064562Sgshapiro { 263164562Sgshapiro char *fv; 263264562Sgshapiro 263364562Sgshapiro /* set the initial sender for AUTH= to $f@$j */ 263490792Sgshapiro fv = macvalue('f', &MainEnvelope); 263564562Sgshapiro if (fv == NULL || *fv == '\0') 263690792Sgshapiro MainEnvelope.e_auth_param = NULL; 263764562Sgshapiro else 263864562Sgshapiro { 263964562Sgshapiro if (strchr(fv, '@') == NULL) 264064562Sgshapiro { 264190792Sgshapiro i = strlen(fv) + strlen(macvalue('j', 264290792Sgshapiro &MainEnvelope)) + 2; 264390792Sgshapiro p = sm_malloc_x(i); 264490792Sgshapiro (void) sm_strlcpyn(p, i, 3, fv, "@", 264590792Sgshapiro macvalue('j', 264690792Sgshapiro &MainEnvelope)); 264764562Sgshapiro } 264864562Sgshapiro else 264990792Sgshapiro p = sm_strdup_x(fv); 265090792Sgshapiro MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool, 265190792Sgshapiro xtextify(p, "=")); 265290792Sgshapiro sm_free(p); /* XXX */ 265364562Sgshapiro } 265464562Sgshapiro } 265590792Sgshapiro if (macvalue('s', &MainEnvelope) == NULL) 265690792Sgshapiro macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName); 265738032Speter 265890792Sgshapiro av = argv + optind; 265938032Speter if (*av == NULL && !GrabTo) 266038032Speter { 266190792Sgshapiro MainEnvelope.e_to = NULL; 266290792Sgshapiro MainEnvelope.e_flags |= EF_GLOBALERRS; 266390792Sgshapiro HoldErrs = false; 266490792Sgshapiro SuperSafe = SAFE_NO; 266538032Speter usrerr("Recipient names must be specified"); 266638032Speter 266738032Speter /* collect body for UUCP return */ 266838032Speter if (OpMode != MD_VERIFY) 2669120256Sgshapiro collect(InChannel, false, NULL, &MainEnvelope, true); 267090792Sgshapiro finis(true, true, EX_USAGE); 267190792Sgshapiro /* NOTREACHED */ 267238032Speter } 267338032Speter 267438032Speter /* 267538032Speter ** Scan argv and deliver the message to everyone. 267638032Speter */ 267738032Speter 267890792Sgshapiro save_val = LogUsrErrs; 267990792Sgshapiro LogUsrErrs = true; 268090792Sgshapiro sendtoargv(av, &MainEnvelope); 268190792Sgshapiro LogUsrErrs = save_val; 268238032Speter 268338032Speter /* if we have had errors sofar, arrange a meaningful exit stat */ 268438032Speter if (Errors > 0 && ExitStat == EX_OK) 268538032Speter ExitStat = EX_USAGE; 268638032Speter 268738032Speter#if _FFR_FIX_DASHT 268838032Speter /* 268938032Speter ** If using -t, force not sending to argv recipients, even 269038032Speter ** if they are mentioned in the headers. 269138032Speter */ 269238032Speter 269338032Speter if (GrabTo) 269438032Speter { 269538032Speter ADDRESS *q; 269664562Sgshapiro 269790792Sgshapiro for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next) 269864562Sgshapiro q->q_state = QS_REMOVED; 269938032Speter } 270064562Sgshapiro#endif /* _FFR_FIX_DASHT */ 270138032Speter 270238032Speter /* 270338032Speter ** Read the input mail. 270438032Speter */ 270538032Speter 270690792Sgshapiro MainEnvelope.e_to = NULL; 270738032Speter if (OpMode != MD_VERIFY || GrabTo) 270838032Speter { 270990792Sgshapiro int savederrors; 271090792Sgshapiro unsigned long savedflags; 271138032Speter 271290792Sgshapiro /* 271390792Sgshapiro ** workaround for compiler warning on Irix: 271490792Sgshapiro ** do not initialize variable in the definition, but 271590792Sgshapiro ** later on: 271690792Sgshapiro ** warning(1548): transfer of control bypasses 271790792Sgshapiro ** initialization of: 271890792Sgshapiro ** variable "savederrors" (declared at line 2570) 271990792Sgshapiro ** variable "savedflags" (declared at line 2571) 272090792Sgshapiro ** goto giveup; 272190792Sgshapiro */ 272290792Sgshapiro 272390792Sgshapiro savederrors = Errors; 272490792Sgshapiro savedflags = MainEnvelope.e_flags & EF_FATALERRS; 272590792Sgshapiro MainEnvelope.e_flags |= EF_GLOBALERRS; 272690792Sgshapiro MainEnvelope.e_flags &= ~EF_FATALERRS; 272764562Sgshapiro Errors = 0; 272864562Sgshapiro buffer_errors(); 2729120256Sgshapiro collect(InChannel, false, NULL, &MainEnvelope, true); 273038032Speter 273164562Sgshapiro /* header checks failed */ 273264562Sgshapiro if (Errors > 0) 273364562Sgshapiro { 273490792Sgshapiro giveup: 273590792Sgshapiro if (!GrabTo) 273664562Sgshapiro { 273790792Sgshapiro /* Log who the mail would have gone to */ 273890792Sgshapiro logundelrcpts(&MainEnvelope, 273990792Sgshapiro MainEnvelope.e_message, 274090792Sgshapiro 8, false); 274164562Sgshapiro } 274290792Sgshapiro flush_errors(true); 274390792Sgshapiro finis(true, true, ExitStat); 274464562Sgshapiro /* NOTREACHED */ 274564562Sgshapiro return -1; 274664562Sgshapiro } 274764562Sgshapiro 274838032Speter /* bail out if message too large */ 274990792Sgshapiro if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags)) 275038032Speter { 275190792Sgshapiro finis(true, true, ExitStat != EX_OK ? ExitStat 275290792Sgshapiro : EX_DATAERR); 275364562Sgshapiro /* NOTREACHED */ 275438032Speter return -1; 275538032Speter } 275698121Sgshapiro 275798121Sgshapiro /* set message size */ 275898121Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", 275998121Sgshapiro MainEnvelope.e_msgsize); 276098121Sgshapiro macdefine(&MainEnvelope.e_macro, A_TEMP, 276198121Sgshapiro macid("{msg_size}"), buf); 276298121Sgshapiro 276364562Sgshapiro Errors = savederrors; 276490792Sgshapiro MainEnvelope.e_flags |= savedflags; 276538032Speter } 276638032Speter errno = 0; 276738032Speter 276838032Speter if (tTd(1, 1)) 276990792Sgshapiro sm_dprintf("From person = \"%s\"\n", 277090792Sgshapiro MainEnvelope.e_from.q_paddr); 277138032Speter 277290792Sgshapiro /* Check if quarantining stats should be updated */ 277390792Sgshapiro if (MainEnvelope.e_quarmsg != NULL) 277490792Sgshapiro markstats(&MainEnvelope, NULL, STATS_QUARANTINE); 277590792Sgshapiro 277638032Speter /* 277738032Speter ** Actually send everything. 277838032Speter ** If verifying, just ack. 277938032Speter */ 278038032Speter 278190792Sgshapiro if (Errors == 0) 278238032Speter { 278390792Sgshapiro if (!split_by_recipient(&MainEnvelope) && 278490792Sgshapiro bitset(EF_FATALERRS, MainEnvelope.e_flags)) 278590792Sgshapiro goto giveup; 278638032Speter } 278790792Sgshapiro 278890792Sgshapiro /* make sure we deliver at least the first envelope */ 278990792Sgshapiro i = FastSplit > 0 ? 0 : -1; 279090792Sgshapiro for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++) 279190792Sgshapiro { 279290792Sgshapiro ENVELOPE *next; 279390792Sgshapiro 279490792Sgshapiro e->e_from.q_state = QS_SENDER; 279590792Sgshapiro if (tTd(1, 5)) 279690792Sgshapiro { 279790792Sgshapiro sm_dprintf("main[%d]: QS_SENDER ", i); 2798132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 279990792Sgshapiro } 280090792Sgshapiro e->e_to = NULL; 280190792Sgshapiro sm_getla(); 280290792Sgshapiro GrabTo = false; 280364562Sgshapiro#if NAMED_BIND 280490792Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 280590792Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 280664562Sgshapiro#endif /* NAMED_BIND */ 280790792Sgshapiro next = e->e_sibling; 280890792Sgshapiro e->e_sibling = NULL; 280938032Speter 281090792Sgshapiro /* after FastSplit envelopes: queue up */ 281190792Sgshapiro sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT); 281290792Sgshapiro e->e_sibling = next; 281390792Sgshapiro } 281490792Sgshapiro 281538032Speter /* 281638032Speter ** All done. 281738032Speter ** Don't send return error message if in VERIFY mode. 281838032Speter */ 281938032Speter 282090792Sgshapiro finis(true, true, ExitStat); 282164562Sgshapiro /* NOTREACHED */ 282264562Sgshapiro return ExitStat; 282338032Speter} 282490792Sgshapiro/* 282577349Sgshapiro** STOP_SENDMAIL -- Stop the running program 282677349Sgshapiro** 282777349Sgshapiro** Parameters: 282877349Sgshapiro** none. 282977349Sgshapiro** 283077349Sgshapiro** Returns: 283177349Sgshapiro** none. 283277349Sgshapiro** 283377349Sgshapiro** Side Effects: 283477349Sgshapiro** exits. 283577349Sgshapiro*/ 283638032Speter 283777349Sgshapirovoid 283877349Sgshapirostop_sendmail() 283977349Sgshapiro{ 284077349Sgshapiro /* reset uid for process accounting */ 284177349Sgshapiro endpwent(); 284277349Sgshapiro (void) setuid(RealUid); 284377349Sgshapiro exit(EX_OK); 284477349Sgshapiro} 284590792Sgshapiro/* 284638032Speter** FINIS -- Clean up and exit. 284738032Speter** 284838032Speter** Parameters: 284942575Speter** drop -- whether or not to drop CurEnv envelope 285090792Sgshapiro** cleanup -- call exit() or _exit()? 285142575Speter** exitstat -- exit status to use for exit() call 285238032Speter** 285338032Speter** Returns: 285438032Speter** never 285538032Speter** 285638032Speter** Side Effects: 285738032Speter** exits sendmail 285838032Speter*/ 285938032Speter 286038032Spetervoid 286190792Sgshapirofinis(drop, cleanup, exitstat) 286242575Speter bool drop; 286390792Sgshapiro bool cleanup; 286442575Speter volatile int exitstat; 286538032Speter{ 2866132943Sgshapiro char pidpath[MAXPATHLEN]; 286798121Sgshapiro 286877349Sgshapiro /* Still want to process new timeouts added below */ 286990792Sgshapiro sm_clear_events(); 287090792Sgshapiro (void) sm_releasesignal(SIGALRM); 287142575Speter 287238032Speter if (tTd(2, 1)) 287338032Speter { 287490792Sgshapiro sm_dprintf("\n====finis: stat %d e_id=%s e_flags=", 287590792Sgshapiro exitstat, 287690792Sgshapiro CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); 287738032Speter printenvflags(CurEnv); 287838032Speter } 287938032Speter if (tTd(2, 9)) 288090792Sgshapiro printopenfds(false); 288138032Speter 288290792Sgshapiro SM_TRY 288390792Sgshapiro /* 288490792Sgshapiro ** Clean up. This might raise E:mta.quickabort 288590792Sgshapiro */ 288638032Speter 288790792Sgshapiro /* clean up temp files */ 288890792Sgshapiro CurEnv->e_to = NULL; 288990792Sgshapiro if (drop) 289090792Sgshapiro { 289190792Sgshapiro if (CurEnv->e_id != NULL) 289290792Sgshapiro { 289390792Sgshapiro dropenvelope(CurEnv, true, false); 289490792Sgshapiro sm_rpool_free(CurEnv->e_rpool); 289590792Sgshapiro CurEnv->e_rpool = NULL; 289690792Sgshapiro } 289790792Sgshapiro else 289890792Sgshapiro poststats(StatFile); 289990792Sgshapiro } 290038032Speter 290190792Sgshapiro /* flush any cached connections */ 290290792Sgshapiro mci_flush(true, NULL); 290338032Speter 290490792Sgshapiro /* close maps belonging to this pid */ 290590792Sgshapiro closemaps(false); 290642575Speter 290764562Sgshapiro#if USERDB 290890792Sgshapiro /* close UserDatabase */ 290990792Sgshapiro _udbx_close(); 291064562Sgshapiro#endif /* USERDB */ 291142575Speter 291290792Sgshapiro#if SASL 291390792Sgshapiro stop_sasl_client(); 291490792Sgshapiro#endif /* SASL */ 291590792Sgshapiro 291690792Sgshapiro#if XLA 291790792Sgshapiro /* clean up extended load average stuff */ 291890792Sgshapiro xla_all_end(); 291964562Sgshapiro#endif /* XLA */ 292038032Speter 292190792Sgshapiro SM_FINALLY 292290792Sgshapiro /* 292390792Sgshapiro ** And exit. 292490792Sgshapiro */ 292538032Speter 292690792Sgshapiro if (LogLevel > 78) 292790792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d", 292890792Sgshapiro (int) CurrentPid); 292990792Sgshapiro if (exitstat == EX_TEMPFAIL || 293090792Sgshapiro CurEnv->e_errormode == EM_BERKNET) 293190792Sgshapiro exitstat = EX_OK; 293264562Sgshapiro 293390792Sgshapiro /* XXX clean up queues and related data structures */ 293490792Sgshapiro cleanup_queues(); 293590792Sgshapiro#if SM_CONF_SHM 293690792Sgshapiro cleanup_shm(DaemonPid == getpid()); 293790792Sgshapiro#endif /* SM_CONF_SHM */ 293890792Sgshapiro 2939132943Sgshapiro /* close locked pid file */ 2940132943Sgshapiro close_sendmail_pid(); 2941132943Sgshapiro 2942132943Sgshapiro if (DaemonPid == getpid() || PidFilePid == getpid()) 2943132943Sgshapiro { 2944132943Sgshapiro /* blow away the pid file */ 2945132943Sgshapiro expand(PidFile, pidpath, sizeof pidpath, CurEnv); 2946132943Sgshapiro (void) unlink(pidpath); 2947132943Sgshapiro } 2948132943Sgshapiro 294990792Sgshapiro /* reset uid for process accounting */ 295090792Sgshapiro endpwent(); 295190792Sgshapiro sm_mbdb_terminate(); 295290792Sgshapiro (void) setuid(RealUid); 295390792Sgshapiro#if SM_HEAP_CHECK 295490792Sgshapiro /* dump the heap, if we are checking for memory leaks */ 295590792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2)) 295690792Sgshapiro sm_heap_report(smioout, 295790792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 295890792Sgshapiro#endif /* SM_HEAP_CHECK */ 295990792Sgshapiro if (sm_debug_active(&SmXtrapReport, 1)) 296090792Sgshapiro sm_dprintf("xtrap count = %d\n", SmXtrapCount); 296190792Sgshapiro if (cleanup) 296290792Sgshapiro exit(exitstat); 296390792Sgshapiro else 296490792Sgshapiro _exit(exitstat); 296590792Sgshapiro SM_END_TRY 296638032Speter} 296790792Sgshapiro/* 296890792Sgshapiro** INTINDEBUG -- signal handler for SIGINT in -bt mode 296977349Sgshapiro** 297077349Sgshapiro** Parameters: 297190792Sgshapiro** sig -- incoming signal. 297290792Sgshapiro** 297390792Sgshapiro** Returns: 297490792Sgshapiro** none. 297590792Sgshapiro** 297690792Sgshapiro** Side Effects: 297790792Sgshapiro** longjmps back to test mode loop. 297890792Sgshapiro** 297990792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 298090792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 298190792Sgshapiro** DOING. 298290792Sgshapiro*/ 298390792Sgshapiro 298490792Sgshapiro/* Type of an exception generated on SIGINT during address test mode. */ 298590792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt = 298690792Sgshapiro{ 298790792Sgshapiro SmExcTypeMagic, 298890792Sgshapiro "S:mta.interrupt", 298990792Sgshapiro "", 299090792Sgshapiro sm_etype_printf, 299190792Sgshapiro "interrupt", 299290792Sgshapiro}; 299390792Sgshapiro 299490792Sgshapiro/* ARGSUSED */ 299590792Sgshapirostatic SIGFUNC_DECL 299690792Sgshapirointindebug(sig) 299790792Sgshapiro int sig; 299890792Sgshapiro{ 299990792Sgshapiro int save_errno = errno; 300090792Sgshapiro 300190792Sgshapiro FIX_SYSV_SIGNAL(sig, intindebug); 300290792Sgshapiro errno = save_errno; 300390792Sgshapiro CHECK_CRITICAL(sig); 300490792Sgshapiro errno = save_errno; 300590792Sgshapiro sm_exc_raisenew_x(&EtypeInterrupt); 300690792Sgshapiro errno = save_errno; 300790792Sgshapiro return SIGFUNC_RETURN; 300890792Sgshapiro} 300990792Sgshapiro/* 301090792Sgshapiro** SIGTERM -- SIGTERM handler for the daemon 301190792Sgshapiro** 301290792Sgshapiro** Parameters: 301377349Sgshapiro** sig -- signal number. 301477349Sgshapiro** 301577349Sgshapiro** Returns: 301677349Sgshapiro** none. 301777349Sgshapiro** 301877349Sgshapiro** Side Effects: 301977349Sgshapiro** Sets ShutdownRequest which will hopefully trigger 302077349Sgshapiro** the daemon to exit. 302177349Sgshapiro** 302277349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 302377349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 302477349Sgshapiro** DOING. 302577349Sgshapiro*/ 302677349Sgshapiro 302777349Sgshapiro/* ARGSUSED */ 302877349Sgshapirostatic SIGFUNC_DECL 302990792Sgshapirosigterm(sig) 303077349Sgshapiro int sig; 303177349Sgshapiro{ 303277349Sgshapiro int save_errno = errno; 303377349Sgshapiro 303490792Sgshapiro FIX_SYSV_SIGNAL(sig, sigterm); 303577349Sgshapiro ShutdownRequest = "signal"; 303677349Sgshapiro errno = save_errno; 303777349Sgshapiro return SIGFUNC_RETURN; 303877349Sgshapiro} 303990792Sgshapiro/* 304090792Sgshapiro** SIGHUP -- handle a SIGHUP signal 304177349Sgshapiro** 304277349Sgshapiro** Parameters: 304390792Sgshapiro** sig -- incoming signal. 304477349Sgshapiro** 304577349Sgshapiro** Returns: 304677349Sgshapiro** none. 304777349Sgshapiro** 304877349Sgshapiro** Side Effects: 304990792Sgshapiro** Sets RestartRequest which should cause the daemon 305090792Sgshapiro** to restart. 305190792Sgshapiro** 305290792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 305390792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 305490792Sgshapiro** DOING. 305577349Sgshapiro*/ 305677349Sgshapiro 305790792Sgshapiro/* ARGSUSED */ 305890792Sgshapirostatic SIGFUNC_DECL 305990792Sgshapirosighup(sig) 306090792Sgshapiro int sig; 306177349Sgshapiro{ 306290792Sgshapiro int save_errno = errno; 306377349Sgshapiro 306490792Sgshapiro FIX_SYSV_SIGNAL(sig, sighup); 306590792Sgshapiro RestartRequest = "signal"; 306690792Sgshapiro errno = save_errno; 306790792Sgshapiro return SIGFUNC_RETURN; 306890792Sgshapiro} 306990792Sgshapiro/* 307090792Sgshapiro** SIGPIPE -- signal handler for SIGPIPE 307190792Sgshapiro** 307290792Sgshapiro** Parameters: 307390792Sgshapiro** sig -- incoming signal. 307490792Sgshapiro** 307590792Sgshapiro** Returns: 307690792Sgshapiro** none. 307790792Sgshapiro** 307890792Sgshapiro** Side Effects: 307990792Sgshapiro** Sets StopRequest which should cause the mailq/hoststatus 308090792Sgshapiro** display to stop. 308190792Sgshapiro** 308290792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 308390792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 308490792Sgshapiro** DOING. 308590792Sgshapiro*/ 308677349Sgshapiro 308790792Sgshapiro/* ARGSUSED */ 308890792Sgshapirostatic SIGFUNC_DECL 308990792Sgshapirosigpipe(sig) 309090792Sgshapiro int sig; 309190792Sgshapiro{ 309290792Sgshapiro int save_errno = errno; 309377349Sgshapiro 309490792Sgshapiro FIX_SYSV_SIGNAL(sig, sigpipe); 309590792Sgshapiro StopRequest = true; 309690792Sgshapiro errno = save_errno; 309790792Sgshapiro return SIGFUNC_RETURN; 309877349Sgshapiro} 309990792Sgshapiro/* 310038032Speter** INTSIG -- clean up on interrupt 310138032Speter** 310264562Sgshapiro** This just arranges to exit. It pessimizes in that it 310338032Speter** may resend a message. 310438032Speter** 310538032Speter** Parameters: 310638032Speter** none. 310738032Speter** 310838032Speter** Returns: 310938032Speter** none. 311038032Speter** 311138032Speter** Side Effects: 311238032Speter** Unlocks the current job. 311377349Sgshapiro** 311477349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 311577349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 311677349Sgshapiro** DOING. 311777349Sgshapiro** 311877349Sgshapiro** XXX: More work is needed for this signal handler. 311938032Speter*/ 312038032Speter 312138032Speter/* ARGSUSED */ 312238032SpeterSIGFUNC_DECL 312338032Speterintsig(sig) 312438032Speter int sig; 312538032Speter{ 312690792Sgshapiro bool drop = false; 312777349Sgshapiro int save_errno = errno; 312864562Sgshapiro 312977349Sgshapiro FIX_SYSV_SIGNAL(sig, intsig); 313077349Sgshapiro errno = save_errno; 313177349Sgshapiro CHECK_CRITICAL(sig); 313290792Sgshapiro sm_allsignals(true); 313390792Sgshapiro 313464562Sgshapiro if (sig != 0 && LogLevel > 79) 313538032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); 313638032Speter FileName = NULL; 313764562Sgshapiro 313864562Sgshapiro /* Clean-up on aborted stdin message submission */ 313964562Sgshapiro if (CurEnv->e_id != NULL && 314064562Sgshapiro (OpMode == MD_SMTP || 314164562Sgshapiro OpMode == MD_DELIVER || 314264562Sgshapiro OpMode == MD_ARPAFTP)) 314364562Sgshapiro { 314464562Sgshapiro register ADDRESS *q; 314564562Sgshapiro 314664562Sgshapiro /* don't return an error indication */ 314764562Sgshapiro CurEnv->e_to = NULL; 314864562Sgshapiro CurEnv->e_flags &= ~EF_FATALERRS; 314964562Sgshapiro CurEnv->e_flags |= EF_CLRQUEUE; 315064562Sgshapiro 315164562Sgshapiro /* 315264562Sgshapiro ** Spin through the addresses and 315364562Sgshapiro ** mark them dead to prevent bounces 315464562Sgshapiro */ 315564562Sgshapiro 315664562Sgshapiro for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 315764562Sgshapiro q->q_state = QS_DONTSEND; 315864562Sgshapiro 315990792Sgshapiro drop = true; 316064562Sgshapiro } 316180785Sgshapiro else if (OpMode != MD_TEST) 316290792Sgshapiro { 316364562Sgshapiro unlockqueue(CurEnv); 316438032Speter } 316538032Speter 316690792Sgshapiro finis(drop, false, EX_OK); 316790792Sgshapiro /* NOTREACHED */ 316838032Speter} 316990792Sgshapiro/* 317038032Speter** DISCONNECT -- remove our connection with any foreground process 317138032Speter** 317238032Speter** Parameters: 317338032Speter** droplev -- how "deeply" we should drop the line. 317438032Speter** 0 -- ignore signals, mail back errors, make sure 317538032Speter** output goes to stdout. 317664562Sgshapiro** 1 -- also, make stdout go to /dev/null. 317738032Speter** 2 -- also, disconnect from controlling terminal 317838032Speter** (only for daemon mode). 317938032Speter** e -- the current envelope. 318038032Speter** 318138032Speter** Returns: 318238032Speter** none 318338032Speter** 318438032Speter** Side Effects: 318538032Speter** Trys to insure that we are immune to vagaries of 318638032Speter** the controlling tty. 318738032Speter*/ 318838032Speter 318938032Spetervoid 319038032Speterdisconnect(droplev, e) 319138032Speter int droplev; 319238032Speter register ENVELOPE *e; 319338032Speter{ 319438032Speter int fd; 319538032Speter 319638032Speter if (tTd(52, 1)) 319790792Sgshapiro sm_dprintf("disconnect: In %d Out %d, e=%p\n", 319890792Sgshapiro sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL), 319990792Sgshapiro sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e); 320038032Speter if (tTd(52, 100)) 320138032Speter { 320290792Sgshapiro sm_dprintf("don't\n"); 320338032Speter return; 320438032Speter } 320538032Speter if (LogLevel > 93) 320638032Speter sm_syslog(LOG_DEBUG, e->e_id, 320764562Sgshapiro "disconnect level %d", 320864562Sgshapiro droplev); 320938032Speter 321038032Speter /* be sure we don't get nasty signals */ 321190792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 321290792Sgshapiro (void) sm_signal(SIGQUIT, SIG_IGN); 321338032Speter 321438032Speter /* we can't communicate with our caller, so.... */ 321590792Sgshapiro HoldErrs = true; 321638032Speter CurEnv->e_errormode = EM_MAIL; 321738032Speter Verbose = 0; 321890792Sgshapiro DisConnected = true; 321938032Speter 322038032Speter /* all input from /dev/null */ 322190792Sgshapiro if (InChannel != smioin) 322238032Speter { 322390792Sgshapiro (void) sm_io_close(InChannel, SM_TIME_DEFAULT); 322490792Sgshapiro InChannel = smioin; 322538032Speter } 322690792Sgshapiro if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, 322790792Sgshapiro SM_IO_RDONLY, NULL, smioin) == NULL) 322838032Speter sm_syslog(LOG_ERR, e->e_id, 322990792Sgshapiro "disconnect: sm_io_reopen(\"%s\") failed: %s", 323090792Sgshapiro SM_PATH_DEVNULL, sm_errstring(errno)); 323138032Speter 323290792Sgshapiro /* 323390792Sgshapiro ** output to the transcript 323490792Sgshapiro ** We also compare the fd numbers here since OutChannel 323590792Sgshapiro ** might be a layer on top of smioout due to encryption 323690792Sgshapiro ** (see sfsasl.c). 323790792Sgshapiro */ 323890792Sgshapiro 323990792Sgshapiro if (OutChannel != smioout && 324090792Sgshapiro sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) != 324190792Sgshapiro sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL)) 324238032Speter { 324390792Sgshapiro (void) sm_io_close(OutChannel, SM_TIME_DEFAULT); 324490792Sgshapiro OutChannel = smioout; 324590792Sgshapiro 324690792Sgshapiro#if 0 324790792Sgshapiro /* 324890792Sgshapiro ** Has smioout been closed? Reopen it. 324990792Sgshapiro ** This shouldn't happen anymore, the code is here 325090792Sgshapiro ** just as a reminder. 325190792Sgshapiro */ 325290792Sgshapiro 325390792Sgshapiro if (smioout->sm_magic == NULL && 325490792Sgshapiro sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, 325590792Sgshapiro SM_IO_WRONLY, NULL, smioout) == NULL) 325690792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 325790792Sgshapiro "disconnect: sm_io_reopen(\"%s\") failed: %s", 325890792Sgshapiro SM_PATH_DEVNULL, sm_errstring(errno)); 325990792Sgshapiro#endif /* 0 */ 326038032Speter } 326138032Speter if (droplev > 0) 326238032Speter { 326390792Sgshapiro fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666); 326464562Sgshapiro if (fd == -1) 326564562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 326690792Sgshapiro "disconnect: open(\"%s\") failed: %s", 326790792Sgshapiro SM_PATH_DEVNULL, sm_errstring(errno)); 326890792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 326964562Sgshapiro (void) dup2(fd, STDOUT_FILENO); 327064562Sgshapiro (void) dup2(fd, STDERR_FILENO); 327164562Sgshapiro (void) close(fd); 327238032Speter } 327338032Speter 327438032Speter /* drop our controlling TTY completely if possible */ 327538032Speter if (droplev > 1) 327638032Speter { 327738032Speter (void) setsid(); 327838032Speter errno = 0; 327938032Speter } 328038032Speter 328138032Speter#if XDEBUG 328238032Speter checkfd012("disconnect"); 328364562Sgshapiro#endif /* XDEBUG */ 328438032Speter 328538032Speter if (LogLevel > 71) 328690792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d", 328790792Sgshapiro (int) CurrentPid); 328838032Speter 328938032Speter errno = 0; 329038032Speter} 329138032Speter 329238032Speterstatic void 329338032Speterobsolete(argv) 329438032Speter char *argv[]; 329538032Speter{ 329638032Speter register char *ap; 329738032Speter register char *op; 329838032Speter 329938032Speter while ((ap = *++argv) != NULL) 330038032Speter { 330138032Speter /* Return if "--" or not an option of any form. */ 330238032Speter if (ap[0] != '-' || ap[1] == '-') 330338032Speter return; 330438032Speter 330590792Sgshapiro /* Don't allow users to use "-Q." or "-Q ." */ 330690792Sgshapiro if ((ap[1] == 'Q' && ap[2] == '.') || 330790792Sgshapiro (ap[1] == 'Q' && argv[1] != NULL && 330890792Sgshapiro argv[1][0] == '.' && argv[1][1] == '\0')) 330990792Sgshapiro { 331090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 331190792Sgshapiro "Can not use -Q.\n"); 331290792Sgshapiro exit(EX_USAGE); 331390792Sgshapiro } 331490792Sgshapiro 331538032Speter /* skip over options that do have a value */ 331638032Speter op = strchr(OPTIONS, ap[1]); 331738032Speter if (op != NULL && *++op == ':' && ap[2] == '\0' && 331838032Speter ap[1] != 'd' && 331938032Speter#if defined(sony_news) 332038032Speter ap[1] != 'E' && ap[1] != 'J' && 332164562Sgshapiro#endif /* defined(sony_news) */ 332238032Speter argv[1] != NULL && argv[1][0] != '-') 332338032Speter { 332438032Speter argv++; 332538032Speter continue; 332638032Speter } 332738032Speter 332838032Speter /* If -C doesn't have an argument, use sendmail.cf. */ 332990792Sgshapiro#define __DEFPATH "sendmail.cf" 333038032Speter if (ap[1] == 'C' && ap[2] == '\0') 333138032Speter { 333238032Speter *argv = xalloc(sizeof(__DEFPATH) + 2); 333390792Sgshapiro (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2, 333490792Sgshapiro "-C", __DEFPATH); 333538032Speter } 333638032Speter 333738032Speter /* If -q doesn't have an argument, run it once. */ 333838032Speter if (ap[1] == 'q' && ap[2] == '\0') 333938032Speter *argv = "-q0"; 334038032Speter 334190792Sgshapiro /* If -Q doesn't have an argument, disable quarantining */ 334290792Sgshapiro if (ap[1] == 'Q' && ap[2] == '\0') 334390792Sgshapiro *argv = "-Q."; 334490792Sgshapiro 334538032Speter /* if -d doesn't have an argument, use 0-99.1 */ 334638032Speter if (ap[1] == 'd' && ap[2] == '\0') 334738032Speter *argv = "-d0-99.1"; 334838032Speter 334964562Sgshapiro#if defined(sony_news) 335038032Speter /* if -E doesn't have an argument, use -EC */ 335138032Speter if (ap[1] == 'E' && ap[2] == '\0') 335238032Speter *argv = "-EC"; 335338032Speter 335438032Speter /* if -J doesn't have an argument, use -JJ */ 335538032Speter if (ap[1] == 'J' && ap[2] == '\0') 335638032Speter *argv = "-JJ"; 335764562Sgshapiro#endif /* defined(sony_news) */ 335838032Speter } 335938032Speter} 336090792Sgshapiro/* 336138032Speter** AUTH_WARNING -- specify authorization warning 336238032Speter** 336338032Speter** Parameters: 336438032Speter** e -- the current envelope. 336538032Speter** msg -- the text of the message. 336638032Speter** args -- arguments to the message. 336738032Speter** 336838032Speter** Returns: 336938032Speter** none. 337038032Speter*/ 337138032Speter 337238032Spetervoid 337338032Speter#ifdef __STDC__ 337438032Speterauth_warning(register ENVELOPE *e, const char *msg, ...) 337564562Sgshapiro#else /* __STDC__ */ 337638032Speterauth_warning(e, msg, va_alist) 337738032Speter register ENVELOPE *e; 337838032Speter const char *msg; 337938032Speter va_dcl 338064562Sgshapiro#endif /* __STDC__ */ 338138032Speter{ 338238032Speter char buf[MAXLINE]; 338390792Sgshapiro SM_VA_LOCAL_DECL 338438032Speter 338538032Speter if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) 338638032Speter { 338738032Speter register char *p; 338838032Speter static char hostbuf[48]; 338938032Speter 339038032Speter if (hostbuf[0] == '\0') 339171345Sgshapiro { 339271345Sgshapiro struct hostent *hp; 339338032Speter 339471345Sgshapiro hp = myhostname(hostbuf, sizeof hostbuf); 339590792Sgshapiro#if NETINET6 339671345Sgshapiro if (hp != NULL) 339771345Sgshapiro { 339871345Sgshapiro freehostent(hp); 339971345Sgshapiro hp = NULL; 340071345Sgshapiro } 340190792Sgshapiro#endif /* NETINET6 */ 340271345Sgshapiro } 340371345Sgshapiro 340490792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": "); 340538032Speter p = &buf[strlen(buf)]; 340690792Sgshapiro SM_VA_START(ap, msg); 340790792Sgshapiro (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap); 340890792Sgshapiro SM_VA_END(ap); 340990792Sgshapiro addheader("X-Authentication-Warning", buf, 0, e); 341038032Speter if (LogLevel > 3) 341138032Speter sm_syslog(LOG_INFO, e->e_id, 341264562Sgshapiro "Authentication-Warning: %.400s", 341364562Sgshapiro buf); 341438032Speter } 341538032Speter} 341690792Sgshapiro/* 341738032Speter** GETEXTENV -- get from external environment 341838032Speter** 341938032Speter** Parameters: 342038032Speter** envar -- the name of the variable to retrieve 342138032Speter** 342238032Speter** Returns: 342338032Speter** The value, if any. 342438032Speter*/ 342538032Speter 342690792Sgshapirostatic char * 342738032Spetergetextenv(envar) 342838032Speter const char *envar; 342938032Speter{ 343038032Speter char **envp; 343138032Speter int l; 343238032Speter 343338032Speter l = strlen(envar); 3434102528Sgshapiro for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++) 343538032Speter { 343638032Speter if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') 343738032Speter return &(*envp)[l + 1]; 343838032Speter } 343938032Speter return NULL; 344038032Speter} 344190792Sgshapiro/* 344290792Sgshapiro** SETUSERENV -- set an environment in the propagated environment 344338032Speter** 344438032Speter** Parameters: 344538032Speter** envar -- the name of the environment variable. 344638032Speter** value -- the value to which it should be set. If 344738032Speter** null, this is extracted from the incoming 344838032Speter** environment. If that is not set, the call 344938032Speter** to setuserenv is ignored. 345038032Speter** 345138032Speter** Returns: 345238032Speter** none. 345338032Speter*/ 345438032Speter 345538032Spetervoid 345638032Spetersetuserenv(envar, value) 345738032Speter const char *envar; 345838032Speter const char *value; 345938032Speter{ 346064562Sgshapiro int i, l; 346138032Speter char **evp = UserEnviron; 346238032Speter char *p; 346338032Speter 346438032Speter if (value == NULL) 346538032Speter { 346638032Speter value = getextenv(envar); 346738032Speter if (value == NULL) 346838032Speter return; 346938032Speter } 347038032Speter 347190792Sgshapiro /* XXX enforce reasonable size? */ 347264562Sgshapiro i = strlen(envar) + 1; 347364562Sgshapiro l = strlen(value) + i + 1; 347464562Sgshapiro p = (char *) xalloc(l); 347590792Sgshapiro (void) sm_strlcpyn(p, l, 3, envar, "=", value); 347638032Speter 347738032Speter while (*evp != NULL && strncmp(*evp, p, i) != 0) 347838032Speter evp++; 347938032Speter if (*evp != NULL) 348038032Speter { 348138032Speter *evp++ = p; 348238032Speter } 348338032Speter else if (evp < &UserEnviron[MAXUSERENVIRON]) 348438032Speter { 348538032Speter *evp++ = p; 348638032Speter *evp = NULL; 348738032Speter } 348838032Speter 348938032Speter /* make sure it is in our environment as well */ 349038032Speter if (putenv(p) < 0) 349138032Speter syserr("setuserenv: putenv(%s) failed", p); 349238032Speter} 349390792Sgshapiro/* 349438032Speter** DUMPSTATE -- dump state 349538032Speter** 349638032Speter** For debugging. 349738032Speter*/ 349838032Speter 349938032Spetervoid 350038032Speterdumpstate(when) 350138032Speter char *when; 350238032Speter{ 350338032Speter register char *j = macvalue('j', CurEnv); 350438032Speter int rs; 350564562Sgshapiro extern int NextMacroId; 350638032Speter 350738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 350864562Sgshapiro "--- dumping state on %s: $j = %s ---", 350964562Sgshapiro when, 351064562Sgshapiro j == NULL ? "<NULL>" : j); 351138032Speter if (j != NULL) 351238032Speter { 351338032Speter if (!wordinclass(j, 'w')) 351438032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 351564562Sgshapiro "*** $j not in $=w ***"); 351638032Speter } 351738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); 351890792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)", 351964562Sgshapiro NextMacroId, MAXMACROID); 352038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); 352190792Sgshapiro printopenfds(true); 352238032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); 3523132943Sgshapiro mci_dump_all(smioout, true); 352438032Speter rs = strtorwset("debug_dumpstate", NULL, ST_FIND); 352538032Speter if (rs > 0) 352638032Speter { 352764562Sgshapiro int status; 352838032Speter register char **pvp; 352938032Speter char *pv[MAXATOM + 1]; 353038032Speter 353138032Speter pv[0] = NULL; 353290792Sgshapiro status = REWRITE(pv, rs, CurEnv); 353338032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 353464562Sgshapiro "--- ruleset debug_dumpstate returns stat %d, pv: ---", 353564562Sgshapiro status); 353638032Speter for (pvp = pv; *pvp != NULL; pvp++) 353738032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); 353838032Speter } 353938032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); 354038032Speter} 354190792Sgshapiro 354280785Sgshapiro#ifdef SIGUSR1 354390792Sgshapiro/* 354477349Sgshapiro** SIGUSR1 -- Signal a request to dump state. 354577349Sgshapiro** 354677349Sgshapiro** Parameters: 354777349Sgshapiro** sig -- calling signal. 354877349Sgshapiro** 354977349Sgshapiro** Returns: 355077349Sgshapiro** none. 355177349Sgshapiro** 355277349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 355377349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 355477349Sgshapiro** DOING. 355577349Sgshapiro** 355677349Sgshapiro** XXX: More work is needed for this signal handler. 355777349Sgshapiro*/ 355838032Speter 355938032Speter/* ARGSUSED */ 356077349Sgshapirostatic SIGFUNC_DECL 356138032Spetersigusr1(sig) 356238032Speter int sig; 356338032Speter{ 356477349Sgshapiro int save_errno = errno; 356590792Sgshapiro# if SM_HEAP_CHECK 356690792Sgshapiro extern void dumpstab __P((void)); 356790792Sgshapiro# endif /* SM_HEAP_CHECK */ 356877349Sgshapiro 356977349Sgshapiro FIX_SYSV_SIGNAL(sig, sigusr1); 357077349Sgshapiro errno = save_errno; 357177349Sgshapiro CHECK_CRITICAL(sig); 357238032Speter dumpstate("user signal"); 357390792Sgshapiro# if SM_HEAP_CHECK 357490792Sgshapiro dumpstab(); 357590792Sgshapiro# endif /* SM_HEAP_CHECK */ 357677349Sgshapiro errno = save_errno; 357738032Speter return SIGFUNC_RETURN; 357838032Speter} 357990792Sgshapiro#endif /* SIGUSR1 */ 358090792Sgshapiro 358190792Sgshapiro/* 358238032Speter** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option 358338032Speter** 358438032Speter** Parameters: 358538032Speter** to_real_uid -- if set, drop to the real uid instead 358638032Speter** of the RunAsUser. 358738032Speter** 358838032Speter** Returns: 358938032Speter** EX_OSERR if the setuid failed. 359038032Speter** EX_OK otherwise. 359138032Speter*/ 359238032Speter 359338032Speterint 359438032Speterdrop_privileges(to_real_uid) 359538032Speter bool to_real_uid; 359638032Speter{ 359738032Speter int rval = EX_OK; 359838032Speter GIDSET_T emptygidset[1]; 359938032Speter 360038032Speter if (tTd(47, 1)) 360190792Sgshapiro sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n", 360290792Sgshapiro (int) to_real_uid, 360390792Sgshapiro (int) RealUid, (int) RealGid, 360490792Sgshapiro (int) getuid(), (int) getgid(), 360590792Sgshapiro (int) geteuid(), (int) getegid(), 360690792Sgshapiro (int) RunAsUid, (int) RunAsGid); 360738032Speter 360838032Speter if (to_real_uid) 360938032Speter { 361038032Speter RunAsUserName = RealUserName; 361138032Speter RunAsUid = RealUid; 361238032Speter RunAsGid = RealGid; 361394334Sgshapiro EffGid = RunAsGid; 361438032Speter } 361538032Speter 361638032Speter /* make sure no one can grab open descriptors for secret files */ 361738032Speter endpwent(); 361890792Sgshapiro sm_mbdb_terminate(); 361938032Speter 362038032Speter /* reset group permissions; these can be set later */ 362138032Speter emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); 362290792Sgshapiro 362390792Sgshapiro /* 362490792Sgshapiro ** Notice: on some OS (Linux...) the setgroups() call causes 362590792Sgshapiro ** a logfile entry if sendmail is not run by root. 362690792Sgshapiro ** However, it is unclear (no POSIX standard) whether 362790792Sgshapiro ** setgroups() can only succeed if executed by root. 362890792Sgshapiro ** So for now we keep it as it is; if you want to change it, use 362990792Sgshapiro ** if (geteuid() == 0 && setgroups(1, emptygidset) == -1) 363090792Sgshapiro */ 363190792Sgshapiro 363238032Speter if (setgroups(1, emptygidset) == -1 && geteuid() == 0) 363364562Sgshapiro { 363464562Sgshapiro syserr("drop_privileges: setgroups(1, %d) failed", 363590792Sgshapiro (int) emptygidset[0]); 363638032Speter rval = EX_OSERR; 363764562Sgshapiro } 363838032Speter 363990792Sgshapiro /* reset primary group id */ 364090792Sgshapiro if (to_real_uid) 364164562Sgshapiro { 364290792Sgshapiro /* 364390792Sgshapiro ** Drop gid to real gid. 364490792Sgshapiro ** On some OS we must reset the effective[/real[/saved]] gid, 364590792Sgshapiro ** and then use setgid() to finally drop all group privileges. 364690792Sgshapiro ** Later on we check whether we can get back the 364790792Sgshapiro ** effective gid. 364890792Sgshapiro */ 364990792Sgshapiro 365090792Sgshapiro#if HASSETEGID 365190792Sgshapiro if (setegid(RunAsGid) < 0) 365290792Sgshapiro { 365390792Sgshapiro syserr("drop_privileges: setegid(%d) failed", 365490792Sgshapiro (int) RunAsGid); 365590792Sgshapiro rval = EX_OSERR; 365690792Sgshapiro } 365790792Sgshapiro#else /* HASSETEGID */ 365890792Sgshapiro# if HASSETREGID 365990792Sgshapiro if (setregid(RunAsGid, RunAsGid) < 0) 366090792Sgshapiro { 366190792Sgshapiro syserr("drop_privileges: setregid(%d, %d) failed", 366290792Sgshapiro (int) RunAsGid, (int) RunAsGid); 366390792Sgshapiro rval = EX_OSERR; 366490792Sgshapiro } 366590792Sgshapiro# else /* HASSETREGID */ 366690792Sgshapiro# if HASSETRESGID 366790792Sgshapiro if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0) 366890792Sgshapiro { 366990792Sgshapiro syserr("drop_privileges: setresgid(%d, %d, %d) failed", 367090792Sgshapiro (int) RunAsGid, (int) RunAsGid, (int) RunAsGid); 367190792Sgshapiro rval = EX_OSERR; 367290792Sgshapiro } 367390792Sgshapiro# endif /* HASSETRESGID */ 367490792Sgshapiro# endif /* HASSETREGID */ 367590792Sgshapiro#endif /* HASSETEGID */ 367664562Sgshapiro } 367790792Sgshapiro if (rval == EX_OK && (to_real_uid || RunAsGid != 0)) 367890792Sgshapiro { 367990792Sgshapiro if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid)) 368090792Sgshapiro { 368190792Sgshapiro syserr("drop_privileges: setgid(%d) failed", 368290792Sgshapiro (int) RunAsGid); 368390792Sgshapiro rval = EX_OSERR; 368490792Sgshapiro } 368590792Sgshapiro errno = 0; 368690792Sgshapiro if (rval == EX_OK && getegid() != RunAsGid) 368790792Sgshapiro { 368890792Sgshapiro syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d", 368990792Sgshapiro (int) getegid(), (int) RunAsGid); 369090792Sgshapiro rval = EX_OSERR; 369190792Sgshapiro } 369290792Sgshapiro } 369390792Sgshapiro 369490792Sgshapiro /* fiddle with uid */ 369564562Sgshapiro if (to_real_uid || RunAsUid != 0) 369664562Sgshapiro { 369794334Sgshapiro uid_t euid; 369864562Sgshapiro 369990792Sgshapiro /* 370090792Sgshapiro ** Try to setuid(RunAsUid). 370190792Sgshapiro ** euid must be RunAsUid, 370294334Sgshapiro ** ruid must be RunAsUid unless (e|r)uid wasn't 0 370394334Sgshapiro ** and we didn't have to drop privileges to the real uid. 370490792Sgshapiro */ 370590792Sgshapiro 370690792Sgshapiro if (setuid(RunAsUid) < 0 || 370794334Sgshapiro geteuid() != RunAsUid || 370894334Sgshapiro (getuid() != RunAsUid && 370994334Sgshapiro (to_real_uid || geteuid() == 0 || getuid() == 0))) 371064562Sgshapiro { 371190792Sgshapiro#if HASSETREUID 371290792Sgshapiro /* 371390792Sgshapiro ** if ruid != RunAsUid, euid == RunAsUid, then 371490792Sgshapiro ** try resetting just the real uid, then using 371590792Sgshapiro ** setuid() to drop the saved-uid as well. 371690792Sgshapiro */ 371790792Sgshapiro 371894334Sgshapiro if (geteuid() == RunAsUid) 371990792Sgshapiro { 372090792Sgshapiro if (setreuid(RunAsUid, -1) < 0) 372190792Sgshapiro { 372290792Sgshapiro syserr("drop_privileges: setreuid(%d, -1) failed", 372390792Sgshapiro (int) RunAsUid); 372490792Sgshapiro rval = EX_OSERR; 372590792Sgshapiro } 372690792Sgshapiro if (setuid(RunAsUid) < 0) 372790792Sgshapiro { 372890792Sgshapiro syserr("drop_privileges: second setuid(%d) attempt failed", 372990792Sgshapiro (int) RunAsUid); 373090792Sgshapiro rval = EX_OSERR; 373190792Sgshapiro } 373290792Sgshapiro } 373390792Sgshapiro else 373490792Sgshapiro#endif /* HASSETREUID */ 373590792Sgshapiro { 373690792Sgshapiro syserr("drop_privileges: setuid(%d) failed", 373790792Sgshapiro (int) RunAsUid); 373890792Sgshapiro rval = EX_OSERR; 373990792Sgshapiro } 374064562Sgshapiro } 374194334Sgshapiro euid = geteuid(); 374290792Sgshapiro if (RunAsUid != 0 && setuid(0) == 0) 374364562Sgshapiro { 374464562Sgshapiro /* 374564562Sgshapiro ** Believe it or not, the Linux capability model 374664562Sgshapiro ** allows a non-root process to override setuid() 374764562Sgshapiro ** on a process running as root and prevent that 374864562Sgshapiro ** process from dropping privileges. 374964562Sgshapiro */ 375064562Sgshapiro 375164562Sgshapiro syserr("drop_privileges: setuid(0) succeeded (when it should not)"); 375264562Sgshapiro rval = EX_OSERR; 375364562Sgshapiro } 375464562Sgshapiro else if (RunAsUid != euid && setuid(euid) == 0) 375564562Sgshapiro { 375664562Sgshapiro /* 375764562Sgshapiro ** Some operating systems will keep the saved-uid 375864562Sgshapiro ** if a non-root effective-uid calls setuid(real-uid) 375964562Sgshapiro ** making it possible to set it back again later. 376064562Sgshapiro */ 376164562Sgshapiro 376290792Sgshapiro syserr("drop_privileges: Unable to drop non-root set-user-ID privileges"); 376364562Sgshapiro rval = EX_OSERR; 376464562Sgshapiro } 376564562Sgshapiro } 376690792Sgshapiro 376790792Sgshapiro if ((to_real_uid || RunAsGid != 0) && 376890792Sgshapiro rval == EX_OK && RunAsGid != EffGid && 376990792Sgshapiro getuid() != 0 && geteuid() != 0) 377090792Sgshapiro { 377190792Sgshapiro errno = 0; 377290792Sgshapiro if (setgid(EffGid) == 0) 377390792Sgshapiro { 377490792Sgshapiro syserr("drop_privileges: setgid(%d) succeeded (when it should not)", 377590792Sgshapiro (int) EffGid); 377690792Sgshapiro rval = EX_OSERR; 377790792Sgshapiro } 377890792Sgshapiro } 377990792Sgshapiro 378038032Speter if (tTd(47, 5)) 378138032Speter { 378290792Sgshapiro sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", 378390792Sgshapiro (int) geteuid(), (int) getuid(), 378490792Sgshapiro (int) getegid(), (int) getgid()); 378590792Sgshapiro sm_dprintf("drop_privileges: RunAsUser = %d:%d\n", 378690792Sgshapiro (int) RunAsUid, (int) RunAsGid); 378764562Sgshapiro if (tTd(47, 10)) 378890792Sgshapiro sm_dprintf("drop_privileges: rval = %d\n", rval); 378938032Speter } 379038032Speter return rval; 379138032Speter} 379290792Sgshapiro/* 379338032Speter** FILL_FD -- make sure a file descriptor has been properly allocated 379438032Speter** 379538032Speter** Used to make sure that stdin/out/err are allocated on startup 379638032Speter** 379738032Speter** Parameters: 379838032Speter** fd -- the file descriptor to be filled. 379938032Speter** where -- a string used for logging. If NULL, this is 380038032Speter** being called on startup, and logging should 380138032Speter** not be done. 380238032Speter** 380338032Speter** Returns: 380438032Speter** none 380590792Sgshapiro** 380690792Sgshapiro** Side Effects: 380790792Sgshapiro** possibly changes MissingFds 380838032Speter*/ 380938032Speter 381038032Spetervoid 381138032Speterfill_fd(fd, where) 381238032Speter int fd; 381338032Speter char *where; 381438032Speter{ 381538032Speter int i; 381638032Speter struct stat stbuf; 381738032Speter 381838032Speter if (fstat(fd, &stbuf) >= 0 || errno != EBADF) 381938032Speter return; 382038032Speter 382138032Speter if (where != NULL) 382238032Speter syserr("fill_fd: %s: fd %d not open", where, fd); 382338032Speter else 382438032Speter MissingFds |= 1 << fd; 382590792Sgshapiro i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666); 382638032Speter if (i < 0) 382738032Speter { 382890792Sgshapiro syserr("!fill_fd: %s: cannot open %s", 382990792Sgshapiro where == NULL ? "startup" : where, SM_PATH_DEVNULL); 383038032Speter } 383138032Speter if (fd != i) 383238032Speter { 383338032Speter (void) dup2(i, fd); 383438032Speter (void) close(i); 383538032Speter } 383638032Speter} 383790792Sgshapiro/* 383890792Sgshapiro** SM_PRINTOPTIONS -- print options 383990792Sgshapiro** 384090792Sgshapiro** Parameters: 384190792Sgshapiro** options -- array of options. 384290792Sgshapiro** 384390792Sgshapiro** Returns: 384490792Sgshapiro** none. 384590792Sgshapiro*/ 384690792Sgshapiro 384790792Sgshapirostatic void 384890792Sgshapirosm_printoptions(options) 384990792Sgshapiro char **options; 385090792Sgshapiro{ 385190792Sgshapiro int ll; 385290792Sgshapiro char **av; 385390792Sgshapiro 385490792Sgshapiro av = options; 385590792Sgshapiro ll = 7; 385690792Sgshapiro while (*av != NULL) 385790792Sgshapiro { 385890792Sgshapiro if (ll + strlen(*av) > 63) 385990792Sgshapiro { 386090792Sgshapiro sm_dprintf("\n"); 386190792Sgshapiro ll = 0; 386290792Sgshapiro } 386390792Sgshapiro if (ll == 0) 386490792Sgshapiro sm_dprintf("\t\t"); 386590792Sgshapiro else 386690792Sgshapiro sm_dprintf(" "); 386790792Sgshapiro sm_dprintf("%s", *av); 386890792Sgshapiro ll += strlen(*av++) + 1; 386990792Sgshapiro } 387090792Sgshapiro sm_dprintf("\n"); 387190792Sgshapiro} 387290792Sgshapiro/* 387338032Speter** TESTMODELINE -- process a test mode input line 387438032Speter** 387538032Speter** Parameters: 387638032Speter** line -- the input line. 387738032Speter** e -- the current environment. 387838032Speter** Syntax: 387938032Speter** # a comment 388038032Speter** .X process X as a configuration line 388138032Speter** =X dump a configuration item (such as mailers) 388238032Speter** $X dump a macro or class 388338032Speter** /X try an activity 388438032Speter** X normal process through rule set X 388538032Speter*/ 388638032Speter 388764562Sgshapirostatic void 388838032Spetertestmodeline(line, e) 388938032Speter char *line; 389038032Speter ENVELOPE *e; 389138032Speter{ 389238032Speter register char *p; 389338032Speter char *q; 389438032Speter auto char *delimptr; 389538032Speter int mid; 389638032Speter int i, rs; 389738032Speter STAB *map; 389838032Speter char **s; 389938032Speter struct rewrite *rw; 390038032Speter ADDRESS a; 390138032Speter static int tryflags = RF_COPYNONE; 390238032Speter char exbuf[MAXLINE]; 390390792Sgshapiro extern unsigned char TokTypeNoC[]; 390438032Speter 390566494Sgshapiro /* skip leading spaces */ 390666494Sgshapiro while (*line == ' ') 390766494Sgshapiro line++; 390866494Sgshapiro 390938032Speter switch (line[0]) 391038032Speter { 391138032Speter case '#': 391264562Sgshapiro case '\0': 391338032Speter return; 391438032Speter 391538032Speter case '?': 391664562Sgshapiro help("-bt", e); 391738032Speter return; 391838032Speter 391938032Speter case '.': /* config-style settings */ 392038032Speter switch (line[1]) 392138032Speter { 392238032Speter case 'D': 392390792Sgshapiro mid = macid_parse(&line[2], &delimptr); 392471345Sgshapiro if (mid == 0) 392538032Speter return; 392638032Speter translate_dollars(delimptr); 392790792Sgshapiro macdefine(&e->e_macro, A_TEMP, mid, delimptr); 392838032Speter break; 392938032Speter 393038032Speter case 'C': 393138032Speter if (line[2] == '\0') /* not to call syserr() */ 393238032Speter return; 393338032Speter 393490792Sgshapiro mid = macid_parse(&line[2], &delimptr); 393571345Sgshapiro if (mid == 0) 393638032Speter return; 393738032Speter translate_dollars(delimptr); 393838032Speter expand(delimptr, exbuf, sizeof exbuf, e); 393938032Speter p = exbuf; 394038032Speter while (*p != '\0') 394138032Speter { 394238032Speter register char *wd; 394338032Speter char delim; 394438032Speter 394538032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 394638032Speter p++; 394738032Speter wd = p; 394838032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 394938032Speter p++; 395038032Speter delim = *p; 395138032Speter *p = '\0'; 395238032Speter if (wd[0] != '\0') 395338032Speter setclass(mid, wd); 395438032Speter *p = delim; 395538032Speter } 395638032Speter break; 395738032Speter 395838032Speter case '\0': 395990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 396090792Sgshapiro "Usage: .[DC]macro value(s)\n"); 396138032Speter break; 396238032Speter 396338032Speter default: 396490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 396590792Sgshapiro "Unknown \".\" command %s\n", line); 396638032Speter break; 396738032Speter } 396838032Speter return; 396938032Speter 397038032Speter case '=': /* config-style settings */ 397138032Speter switch (line[1]) 397238032Speter { 397338032Speter case 'S': /* dump rule set */ 397438032Speter rs = strtorwset(&line[2], NULL, ST_FIND); 397538032Speter if (rs < 0) 397638032Speter { 397790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 397890792Sgshapiro "Undefined ruleset %s\n", &line[2]); 397938032Speter return; 398038032Speter } 398138032Speter rw = RewriteRules[rs]; 398238032Speter if (rw == NULL) 398338032Speter return; 398438032Speter do 398538032Speter { 398690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 398790792Sgshapiro 'R'); 398838032Speter s = rw->r_lhs; 398938032Speter while (*s != NULL) 399038032Speter { 3991132943Sgshapiro xputs(smioout, *s++); 399290792Sgshapiro (void) sm_io_putc(smioout, 399390792Sgshapiro SM_TIME_DEFAULT, ' '); 399438032Speter } 399590792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 399690792Sgshapiro '\t'); 399790792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 399890792Sgshapiro '\t'); 399938032Speter s = rw->r_rhs; 400038032Speter while (*s != NULL) 400138032Speter { 4002132943Sgshapiro xputs(smioout, *s++); 400390792Sgshapiro (void) sm_io_putc(smioout, 400490792Sgshapiro SM_TIME_DEFAULT, ' '); 400538032Speter } 400690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 400790792Sgshapiro '\n'); 400838032Speter } while ((rw = rw->r_next) != NULL); 400938032Speter break; 401038032Speter 401138032Speter case 'M': 401238032Speter for (i = 0; i < MAXMAILERS; i++) 401338032Speter { 401438032Speter if (Mailer[i] != NULL) 4015132943Sgshapiro printmailer(smioout, Mailer[i]); 401638032Speter } 401738032Speter break; 401838032Speter 401938032Speter case '\0': 402090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 402190792Sgshapiro "Usage: =Sruleset or =M\n"); 402238032Speter break; 402338032Speter 402438032Speter default: 402590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 402690792Sgshapiro "Unknown \"=\" command %s\n", line); 402738032Speter break; 402838032Speter } 402938032Speter return; 403038032Speter 403138032Speter case '-': /* set command-line-like opts */ 403238032Speter switch (line[1]) 403338032Speter { 403438032Speter case 'd': 403538032Speter tTflag(&line[2]); 403638032Speter break; 403738032Speter 403838032Speter case '\0': 403990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 404090792Sgshapiro "Usage: -d{debug arguments}\n"); 404138032Speter break; 404238032Speter 404338032Speter default: 404490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 404590792Sgshapiro "Unknown \"-\" command %s\n", line); 404638032Speter break; 404738032Speter } 404838032Speter return; 404938032Speter 405038032Speter case '$': 405138032Speter if (line[1] == '=') 405238032Speter { 405390792Sgshapiro mid = macid(&line[2]); 405471345Sgshapiro if (mid != 0) 405538032Speter stabapply(dump_class, mid); 405638032Speter return; 405738032Speter } 405890792Sgshapiro mid = macid(&line[1]); 405971345Sgshapiro if (mid == 0) 406038032Speter return; 406138032Speter p = macvalue(mid, e); 406238032Speter if (p == NULL) 406390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 406490792Sgshapiro "Undefined\n"); 406538032Speter else 406638032Speter { 4067132943Sgshapiro xputs(smioout, p); 406890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 406990792Sgshapiro "\n"); 407038032Speter } 407138032Speter return; 407238032Speter 407338032Speter case '/': /* miscellaneous commands */ 407438032Speter p = &line[strlen(line)]; 407538032Speter while (--p >= line && isascii(*p) && isspace(*p)) 407638032Speter *p = '\0'; 407738032Speter p = strpbrk(line, " \t"); 407838032Speter if (p != NULL) 407938032Speter { 408038032Speter while (isascii(*p) && isspace(*p)) 408138032Speter *p++ = '\0'; 408238032Speter } 408338032Speter else 408438032Speter p = ""; 408538032Speter if (line[1] == '\0') 408638032Speter { 408790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 408890792Sgshapiro "Usage: /[canon|map|mx|parse|try|tryflags]\n"); 408938032Speter return; 409038032Speter } 409190792Sgshapiro if (sm_strcasecmp(&line[1], "quit") == 0) 409264562Sgshapiro { 409364562Sgshapiro CurEnv->e_id = NULL; 409490792Sgshapiro finis(true, true, ExitStat); 409590792Sgshapiro /* NOTREACHED */ 409664562Sgshapiro } 409790792Sgshapiro if (sm_strcasecmp(&line[1], "mx") == 0) 409838032Speter { 409938032Speter#if NAMED_BIND 410038032Speter /* look up MX records */ 410138032Speter int nmx; 410238032Speter auto int rcode; 410338032Speter char *mxhosts[MAXMXHOSTS + 1]; 410438032Speter 410538032Speter if (*p == '\0') 410638032Speter { 410790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 410890792Sgshapiro "Usage: /mx address\n"); 410938032Speter return; 411038032Speter } 411190792Sgshapiro nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true, 411290792Sgshapiro NULL); 411390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 411490792Sgshapiro "getmxrr(%s) returns %d value(s):\n", 411590792Sgshapiro p, nmx); 411638032Speter for (i = 0; i < nmx; i++) 411790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 411890792Sgshapiro "\t%s\n", mxhosts[i]); 411964562Sgshapiro#else /* NAMED_BIND */ 412090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 412190792Sgshapiro "No MX code compiled in\n"); 412264562Sgshapiro#endif /* NAMED_BIND */ 412338032Speter } 412490792Sgshapiro else if (sm_strcasecmp(&line[1], "canon") == 0) 412538032Speter { 412638032Speter char host[MAXHOSTNAMELEN]; 412738032Speter 412838032Speter if (*p == '\0') 412938032Speter { 413090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 413190792Sgshapiro "Usage: /canon address\n"); 413238032Speter return; 413338032Speter } 413490792Sgshapiro else if (sm_strlcpy(host, p, sizeof host) >= sizeof host) 413538032Speter { 413690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 413790792Sgshapiro "Name too long\n"); 413838032Speter return; 413938032Speter } 4140110560Sgshapiro (void) getcanonname(host, sizeof host, !HasWildcardMX, 414190792Sgshapiro NULL); 414290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 414390792Sgshapiro "getcanonname(%s) returns %s\n", 414490792Sgshapiro p, host); 414538032Speter } 414690792Sgshapiro else if (sm_strcasecmp(&line[1], "map") == 0) 414738032Speter { 414838032Speter auto int rcode = EX_OK; 414938032Speter char *av[2]; 415038032Speter 415138032Speter if (*p == '\0') 415238032Speter { 415390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 415490792Sgshapiro "Usage: /map mapname key\n"); 415538032Speter return; 415638032Speter } 415790792Sgshapiro for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) 415838032Speter continue; 415938032Speter if (*q == '\0') 416038032Speter { 416190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 416290792Sgshapiro "No key specified\n"); 416338032Speter return; 416438032Speter } 416538032Speter *q++ = '\0'; 416638032Speter map = stab(p, ST_MAP, ST_FIND); 416738032Speter if (map == NULL) 416838032Speter { 416990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 417090792Sgshapiro "Map named \"%s\" not found\n", p); 417138032Speter return; 417238032Speter } 417364562Sgshapiro if (!bitset(MF_OPEN, map->s_map.map_mflags) && 417464562Sgshapiro !openmap(&(map->s_map))) 417538032Speter { 417690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 417790792Sgshapiro "Map named \"%s\" not open\n", p); 417838032Speter return; 417938032Speter } 418090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 418190792Sgshapiro "map_lookup: %s (%s) ", p, q); 418238032Speter av[0] = q; 418338032Speter av[1] = NULL; 418438032Speter p = (*map->s_map.map_class->map_lookup) 418538032Speter (&map->s_map, q, av, &rcode); 418638032Speter if (p == NULL) 418790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 418890792Sgshapiro "no match (%d)\n", 418990792Sgshapiro rcode); 419038032Speter else 419190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 419290792Sgshapiro "returns %s (%d)\n", p, 419390792Sgshapiro rcode); 419438032Speter } 419590792Sgshapiro else if (sm_strcasecmp(&line[1], "try") == 0) 419638032Speter { 419738032Speter MAILER *m; 419864562Sgshapiro STAB *st; 419938032Speter auto int rcode = EX_OK; 420038032Speter 420138032Speter q = strpbrk(p, " \t"); 420238032Speter if (q != NULL) 420338032Speter { 420438032Speter while (isascii(*q) && isspace(*q)) 420538032Speter *q++ = '\0'; 420638032Speter } 420738032Speter if (q == NULL || *q == '\0') 420838032Speter { 420990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 421090792Sgshapiro "Usage: /try mailer address\n"); 421138032Speter return; 421238032Speter } 421364562Sgshapiro st = stab(p, ST_MAILER, ST_FIND); 421464562Sgshapiro if (st == NULL) 421538032Speter { 421690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 421790792Sgshapiro "Unknown mailer %s\n", p); 421838032Speter return; 421938032Speter } 422064562Sgshapiro m = st->s_mailer; 422190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 422290792Sgshapiro "Trying %s %s address %s for mailer %s\n", 422390792Sgshapiro bitset(RF_HEADERADDR, tryflags) ? "header" 422490792Sgshapiro : "envelope", 422590792Sgshapiro bitset(RF_SENDERADDR, tryflags) ? "sender" 422690792Sgshapiro : "recipient", q, p); 422738032Speter p = remotename(q, m, tryflags, &rcode, CurEnv); 422890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 422990792Sgshapiro "Rcode = %d, addr = %s\n", 423090792Sgshapiro rcode, p == NULL ? "<NULL>" : p); 423138032Speter e->e_to = NULL; 423238032Speter } 423390792Sgshapiro else if (sm_strcasecmp(&line[1], "tryflags") == 0) 423438032Speter { 423538032Speter if (*p == '\0') 423638032Speter { 423790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 423890792Sgshapiro "Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); 423938032Speter return; 424038032Speter } 424138032Speter for (; *p != '\0'; p++) 424238032Speter { 424338032Speter switch (*p) 424438032Speter { 424538032Speter case 'H': 424638032Speter case 'h': 424738032Speter tryflags |= RF_HEADERADDR; 424838032Speter break; 424938032Speter 425038032Speter case 'E': 425138032Speter case 'e': 425238032Speter tryflags &= ~RF_HEADERADDR; 425338032Speter break; 425438032Speter 425538032Speter case 'S': 425638032Speter case 's': 425738032Speter tryflags |= RF_SENDERADDR; 425838032Speter break; 425938032Speter 426038032Speter case 'R': 426138032Speter case 'r': 426238032Speter tryflags &= ~RF_SENDERADDR; 426338032Speter break; 426438032Speter } 426538032Speter } 426664562Sgshapiro exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e'; 426764562Sgshapiro exbuf[1] = ' '; 426864562Sgshapiro exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r'; 426964562Sgshapiro exbuf[3] = '\0'; 427090792Sgshapiro macdefine(&e->e_macro, A_TEMP, 427190792Sgshapiro macid("{addr_type}"), exbuf); 427238032Speter } 427390792Sgshapiro else if (sm_strcasecmp(&line[1], "parse") == 0) 427438032Speter { 427538032Speter if (*p == '\0') 427638032Speter { 427790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 427890792Sgshapiro "Usage: /parse address\n"); 427938032Speter return; 428038032Speter } 4281111823Sgshapiro q = crackaddr(p, e); 428290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 428390792Sgshapiro "Cracked address = "); 4284132943Sgshapiro xputs(smioout, q); 428590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 428690792Sgshapiro "\nParsing %s %s address\n", 428790792Sgshapiro bitset(RF_HEADERADDR, tryflags) ? 428890792Sgshapiro "header" : "envelope", 428990792Sgshapiro bitset(RF_SENDERADDR, tryflags) ? 429090792Sgshapiro "sender" : "recipient"); 429190792Sgshapiro if (parseaddr(p, &a, tryflags, '\0', NULL, e, true) 429290792Sgshapiro == NULL) 429390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 429490792Sgshapiro "Cannot parse\n"); 429538032Speter else if (a.q_host != NULL && a.q_host[0] != '\0') 429690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 429790792Sgshapiro "mailer %s, host %s, user %s\n", 429890792Sgshapiro a.q_mailer->m_name, 429990792Sgshapiro a.q_host, 430090792Sgshapiro a.q_user); 430138032Speter else 430290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 430390792Sgshapiro "mailer %s, user %s\n", 430490792Sgshapiro a.q_mailer->m_name, 430590792Sgshapiro a.q_user); 430638032Speter e->e_to = NULL; 430738032Speter } 430838032Speter else 430938032Speter { 431090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 431190792Sgshapiro "Unknown \"/\" command %s\n", 431290792Sgshapiro line); 431338032Speter } 431438032Speter return; 431538032Speter } 431638032Speter 431738032Speter for (p = line; isascii(*p) && isspace(*p); p++) 431838032Speter continue; 431938032Speter q = p; 432038032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 432138032Speter p++; 432238032Speter if (*p == '\0') 432338032Speter { 432490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 432590792Sgshapiro "No address!\n"); 432638032Speter return; 432738032Speter } 432838032Speter *p = '\0'; 432990792Sgshapiro if (invalidaddr(p + 1, NULL, true)) 433038032Speter return; 433138032Speter do 433238032Speter { 433338032Speter register char **pvp; 433438032Speter char pvpbuf[PSBUFSIZE]; 433538032Speter 4336132943Sgshapiro pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, &delimptr, 4337132943Sgshapiro ConfigLevel >= 9 ? TokTypeNoC : NULL, false); 433838032Speter if (pvp == NULL) 433938032Speter continue; 434038032Speter p = q; 434138032Speter while (*p != '\0') 434238032Speter { 434364562Sgshapiro int status; 434438032Speter 434538032Speter rs = strtorwset(p, NULL, ST_FIND); 434638032Speter if (rs < 0) 434738032Speter { 434890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 434990792Sgshapiro "Undefined ruleset %s\n", 435090792Sgshapiro p); 435138032Speter break; 435238032Speter } 435390792Sgshapiro status = REWRITE(pvp, rs, e); 435464562Sgshapiro if (status != EX_OK) 435590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 435690792Sgshapiro "== Ruleset %s (%d) status %d\n", 435790792Sgshapiro p, rs, status); 435838032Speter while (*p != '\0' && *p++ != ',') 435938032Speter continue; 436038032Speter } 436138032Speter } while (*(p = delimptr) != '\0'); 436238032Speter} 436338032Speter 436464562Sgshapirostatic void 436538032Speterdump_class(s, id) 436638032Speter register STAB *s; 436738032Speter int id; 436838032Speter{ 436990792Sgshapiro if (s->s_symtype != ST_CLASS) 437038032Speter return; 437171345Sgshapiro if (bitnset(bitidx(id), s->s_class)) 437290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 437390792Sgshapiro "%s\n", s->s_name); 437438032Speter} 437590792Sgshapiro 437690792Sgshapiro/* 437790792Sgshapiro** An exception type used to create QuickAbort exceptions. 437890792Sgshapiro** This is my first cut at converting QuickAbort from longjmp to exceptions. 437990792Sgshapiro** These exceptions have a single integer argument, which is the argument 438090792Sgshapiro** to longjmp in the original code (either 1 or 2). I don't know the 438190792Sgshapiro** significance of 1 vs 2: the calls to setjmp don't care. 438290792Sgshapiro*/ 438390792Sgshapiro 438490792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort = 438590792Sgshapiro{ 438690792Sgshapiro SmExcTypeMagic, 438790792Sgshapiro "E:mta.quickabort", 438890792Sgshapiro "i", 438990792Sgshapiro sm_etype_printf, 439090792Sgshapiro "quick abort %0", 439190792Sgshapiro}; 4392