main.c revision 102528
138032Speter/* 294334Sgshapiro * Copyright (c) 1998-2002 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[]) = 2173188Sgshapiro"@(#) Copyright (c) 1998-2001 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 28102528SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.887.2.1 2002/08/04 17:36:06 gshapiro 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). 8038032Speter** The support of the 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 && \ 13190792Sgshapiro OpMode != MD_VERIFY && OpMode != MD_TEST) \ 13290792Sgshapiro { \ 13390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \ 13490792Sgshapiro "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \ 13590792Sgshapiro (cmd)); \ 13690792Sgshapiro break; \ 13790792Sgshapiro } \ 13890792Sgshapiro if (extraprivs && queuerun) \ 13990792Sgshapiro { \ 14090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, \ 14190792Sgshapiro "WARNING: Ignoring submission mode -%c option with -q\n", \ 14290792Sgshapiro (cmd)); \ 14390792Sgshapiro break; \ 14490792Sgshapiro } \ 14590792Sgshapiro} 14690792Sgshapiro 14738032Speterint 14838032Spetermain(argc, argv, envp) 14938032Speter int argc; 15038032Speter char **argv; 15138032Speter char **envp; 15238032Speter{ 15338032Speter register char *p; 15438032Speter char **av; 15538032Speter extern char Version[]; 15638032Speter char *ep, *from; 15738032Speter STAB *st; 15838032Speter register int i; 15938032Speter int j; 16064562Sgshapiro int dp; 16190792Sgshapiro int fill_errno; 16290792Sgshapiro int qgrp = NOQGRP; /* queue group to process */ 16390792Sgshapiro bool safecf = true; 16464562Sgshapiro BITMAP256 *p_flags = NULL; /* daemon flags */ 16590792Sgshapiro bool warn_C_flag = false; 16690792Sgshapiro bool auth = true; /* whether to set e_auth_param */ 16738032Speter char warn_f_flag = '\0'; 16890792Sgshapiro bool run_in_foreground = false; /* -bD mode */ 16990792Sgshapiro bool queuerun = false, debug = false; 17038032Speter struct passwd *pw; 17138032Speter struct hostent *hp; 17238032Speter char *nullserver = NULL; 17364562Sgshapiro char *authinfo = NULL; 17464562Sgshapiro char *sysloglabel = NULL; /* label for syslog */ 17590792Sgshapiro char *conffile = NULL; /* name of .cf file */ 17690792Sgshapiro char *queuegroup = NULL; /* queue group to process */ 17790792Sgshapiro#if _FFR_QUARANTINE 17890792Sgshapiro char *quarantining = NULL; /* quarantine queue items? */ 17990792Sgshapiro#endif /* _FFR_QUARANTINE */ 18090792Sgshapiro bool extraprivs; 18190792Sgshapiro bool forged, negate; 18290792Sgshapiro bool queuepersistent = false; /* queue runner process runs forever */ 18390792Sgshapiro bool foregroundqueue = false; /* queue run in foreground */ 18490792Sgshapiro bool save_val; /* to save some bool var. */ 18590792Sgshapiro int cftype; /* which cf file to use? */ 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 21638032Speter /* 21738032Speter ** Check to see if we reentered. 21838032Speter ** This would normally happen if e_putheader or e_putbody 21938032Speter ** were NULL when invoked. 22038032Speter */ 22138032Speter 22290792Sgshapiro if (starttime != 0) 22338032Speter { 22438032Speter syserr("main: reentered!"); 22538032Speter abort(); 22638032Speter } 22790792Sgshapiro starttime = curtime(); 22838032Speter 22938032Speter /* avoid null pointer dereferences */ 23038032Speter TermEscape.te_rv_on = TermEscape.te_rv_off = ""; 23138032Speter 23290792Sgshapiro RealUid = getuid(); 23390792Sgshapiro RealGid = getgid(); 23477349Sgshapiro 23590792Sgshapiro /* Check if sendmail is running with extra privs */ 23690792Sgshapiro extraprivs = (RealUid != 0 && 23790792Sgshapiro (geteuid() != getuid() || getegid() != getgid())); 23877349Sgshapiro 23990792Sgshapiro CurrentPid = getpid(); 24038032Speter 24190792Sgshapiro /* get whatever .cf file is right for the opmode */ 24290792Sgshapiro cftype = SM_GET_RIGHT_CF; 24364562Sgshapiro 24438032Speter /* in 4.4BSD, the table can be huge; impose a reasonable limit */ 24538032Speter DtableSize = getdtsize(); 24638032Speter if (DtableSize > 256) 24738032Speter DtableSize = 256; 24838032Speter 24938032Speter /* 25038032Speter ** Be sure we have enough file descriptors. 25138032Speter ** But also be sure that 0, 1, & 2 are open. 25238032Speter */ 25338032Speter 25490792Sgshapiro /* reset errno and fill_errno; the latter is used way down below */ 25590792Sgshapiro errno = fill_errno = 0; 25638032Speter fill_fd(STDIN_FILENO, NULL); 25790792Sgshapiro if (errno != 0) 25890792Sgshapiro fill_errno = errno; 25938032Speter fill_fd(STDOUT_FILENO, NULL); 26090792Sgshapiro if (errno != 0) 26190792Sgshapiro fill_errno = errno; 26238032Speter fill_fd(STDERR_FILENO, NULL); 26390792Sgshapiro if (errno != 0) 26490792Sgshapiro fill_errno = errno; 26538032Speter 26638032Speter i = DtableSize; 26738032Speter while (--i > 0) 26838032Speter { 26990792Sgshapiro if (i != STDIN_FILENO && i != STDOUT_FILENO && 27090792Sgshapiro i != STDERR_FILENO) 27138032Speter (void) close(i); 27238032Speter } 27338032Speter errno = 0; 27438032Speter 27538032Speter#if LOG 27690792Sgshapiro# ifndef SM_LOG_STR 27790792Sgshapiro# define SM_LOG_STR "sendmail" 27890792Sgshapiro# endif /* ! SM_LOG_STR */ 27964562Sgshapiro# ifdef LOG_MAIL 28090792Sgshapiro openlog(SM_LOG_STR, LOG_PID, LOG_MAIL); 28164562Sgshapiro# else /* LOG_MAIL */ 28290792Sgshapiro openlog(SM_LOG_STR, LOG_PID); 28364562Sgshapiro# endif /* LOG_MAIL */ 28464562Sgshapiro#endif /* LOG */ 28538032Speter 28690792Sgshapiro /* 28790792Sgshapiro ** Seed the random number generator. 28890792Sgshapiro ** Used for queue file names, picking a queue directory, and 28990792Sgshapiro ** MX randomization. 29090792Sgshapiro */ 29138032Speter 29290792Sgshapiro seed_random(); 29338032Speter 29490792Sgshapiro /* do machine-dependent initializations */ 29590792Sgshapiro init_md(argc, argv); 29690792Sgshapiro 29790792Sgshapiro 29890792Sgshapiro SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL; 29990792Sgshapiro 30038032Speter /* reset status from syserr() calls for missing file descriptors */ 30138032Speter Errors = 0; 30238032Speter ExitStat = EX_OK; 30338032Speter 30464562Sgshapiro SubmitMode = SUBMIT_UNKNOWN; 30538032Speter#if XDEBUG 30638032Speter checkfd012("after openlog"); 30764562Sgshapiro#endif /* XDEBUG */ 30838032Speter 30990792Sgshapiro tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1"); 31038032Speter 31138032Speter#ifdef NGROUPS_MAX 31238032Speter /* save initial group set for future checks */ 31338032Speter i = getgroups(NGROUPS_MAX, InitialGidSet); 31490792Sgshapiro if (i <= 0) 31590792Sgshapiro { 31638032Speter InitialGidSet[0] = (GID_T) -1; 31790792Sgshapiro i = 0; 31890792Sgshapiro } 31938032Speter while (i < NGROUPS_MAX) 32038032Speter InitialGidSet[i++] = InitialGidSet[0]; 32164562Sgshapiro#endif /* NGROUPS_MAX */ 32238032Speter 32338032Speter /* drop group id privileges (RunAsUser not yet set) */ 32490792Sgshapiro dp = drop_privileges(false); 32564562Sgshapiro setstat(dp); 32638032Speter 32790792Sgshapiro#ifdef SIGUSR1 32877349Sgshapiro /* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */ 32994334Sgshapiro if (!extraprivs) 33077349Sgshapiro { 33177349Sgshapiro /* arrange to dump state on user-1 signal */ 33290792Sgshapiro (void) sm_signal(SIGUSR1, sigusr1); 33377349Sgshapiro } 33494334Sgshapiro else 33594334Sgshapiro { 33694334Sgshapiro /* ignore user-1 signal */ 33794334Sgshapiro (void) sm_signal(SIGUSR1, SIG_IGN); 33894334Sgshapiro } 33990792Sgshapiro#endif /* SIGUSR1 */ 34038032Speter 34138032Speter /* initialize for setproctitle */ 34238032Speter initsetproctitle(argc, argv, envp); 34338032Speter 34438032Speter /* Handle any non-getoptable constructions. */ 34538032Speter obsolete(argv); 34638032Speter 34738032Speter /* 34838032Speter ** Do a quick prescan of the argument list. 34938032Speter */ 35038032Speter 35164562Sgshapiro 35290792Sgshapiro /* find initial opMode */ 35390792Sgshapiro OpMode = MD_DELIVER; 35490792Sgshapiro av = argv; 35590792Sgshapiro p = strrchr(*av, '/'); 35690792Sgshapiro if (p++ == NULL) 35790792Sgshapiro p = *av; 35890792Sgshapiro if (strcmp(p, "newaliases") == 0) 35990792Sgshapiro OpMode = MD_INITALIAS; 36090792Sgshapiro else if (strcmp(p, "mailq") == 0) 36190792Sgshapiro OpMode = MD_PRINT; 36290792Sgshapiro else if (strcmp(p, "smtpd") == 0) 36390792Sgshapiro OpMode = MD_DAEMON; 36490792Sgshapiro else if (strcmp(p, "hoststat") == 0) 36590792Sgshapiro OpMode = MD_HOSTSTAT; 36690792Sgshapiro else if (strcmp(p, "purgestat") == 0) 36790792Sgshapiro OpMode = MD_PURGESTAT; 36890792Sgshapiro 36990792Sgshapiro#if _FFR_QUARANTINE 37090792Sgshapiro# if defined(__osf__) || defined(_AIX3) 37190792Sgshapiro# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:xQ:" 37290792Sgshapiro# endif /* defined(__osf__) || defined(_AIX3) */ 37390792Sgshapiro# if defined(sony_news) 37490792Sgshapiro# define OPTIONS "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:" 37590792Sgshapiro# endif /* defined(sony_news) */ 37690792Sgshapiro# ifndef OPTIONS 37790792Sgshapiro# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:" 37890792Sgshapiro# endif /* ! OPTIONS */ 37990792Sgshapiro#else /* _FFR_QUARANTINE */ 38090792Sgshapiro# if defined(__osf__) || defined(_AIX3) 38190792Sgshapiro# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x" 38290792Sgshapiro# endif /* defined(__osf__) || defined(_AIX3) */ 38390792Sgshapiro# if defined(sony_news) 38490792Sgshapiro# define OPTIONS "A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:" 38590792Sgshapiro# endif /* defined(sony_news) */ 38690792Sgshapiro# ifndef OPTIONS 38790792Sgshapiro# define OPTIONS "A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:" 38890792Sgshapiro# endif /* ! OPTIONS */ 38990792Sgshapiro#endif /* _FFR_QUARANTINE */ 39090792Sgshapiro 39138032Speter opterr = 0; 39238032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 39338032Speter { 39438032Speter switch (j) 39538032Speter { 39690792Sgshapiro case 'b': /* operations mode */ 39794334Sgshapiro j = (optarg == NULL) ? ' ' : *optarg; 39894334Sgshapiro switch (j) 39938032Speter { 40090792Sgshapiro case MD_DAEMON: 40190792Sgshapiro case MD_FGDAEMON: 40290792Sgshapiro case MD_SMTP: 40390792Sgshapiro case MD_INITALIAS: 40490792Sgshapiro case MD_DELIVER: 40590792Sgshapiro case MD_VERIFY: 40690792Sgshapiro case MD_TEST: 40790792Sgshapiro case MD_PRINT: 40890792Sgshapiro case MD_PRINTNQE: 40990792Sgshapiro case MD_HOSTSTAT: 41090792Sgshapiro case MD_PURGESTAT: 41190792Sgshapiro case MD_ARPAFTP: 41290792Sgshapiro OpMode = j; 41338032Speter break; 41490792Sgshapiro 41590792Sgshapiro case MD_FREEZE: 41690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 41790792Sgshapiro "Frozen configurations unsupported\n"); 41890792Sgshapiro return EX_USAGE; 41990792Sgshapiro 42090792Sgshapiro default: 42190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 42290792Sgshapiro "Invalid operation mode %c\n", 42390792Sgshapiro j); 42490792Sgshapiro return EX_USAGE; 42538032Speter } 42690792Sgshapiro break; 42790792Sgshapiro 42890792Sgshapiro case 'd': 42990792Sgshapiro debug = true; 43038032Speter tTflag(optarg); 43190792Sgshapiro (void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT, 43290792Sgshapiro (char *) NULL, SM_IO_NBF, 43390792Sgshapiro SM_IO_BUFSIZ); 43438032Speter break; 43564562Sgshapiro 43664562Sgshapiro case 'G': /* relay (gateway) submission */ 43790792Sgshapiro SubmitMode = SUBMIT_MTA; 43864562Sgshapiro break; 43964562Sgshapiro 44064562Sgshapiro case 'L': 44190792Sgshapiro j = SM_MIN(strlen(optarg), 24) + 1; 44264562Sgshapiro sysloglabel = xalloc(j); 44390792Sgshapiro (void) sm_strlcpy(sysloglabel, optarg, j); 44490792Sgshapiro SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + 44590792Sgshapiro SL_FUDGE + j; 44664562Sgshapiro break; 44764562Sgshapiro 44890792Sgshapiro#if _FFR_QUARANTINE 44990792Sgshapiro case 'Q': 45090792Sgshapiro#endif /* _FFR_QUARANTINE */ 45190792Sgshapiro case 'q': 45290792Sgshapiro /* just check if it is there */ 45390792Sgshapiro queuerun = true; 45464562Sgshapiro break; 45538032Speter } 45638032Speter } 45738032Speter opterr = 1; 45838032Speter 45990792Sgshapiro /* Don't leak queue information via debug flags */ 46090792Sgshapiro if (extraprivs && queuerun && debug) 46190792Sgshapiro { 46290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 46390792Sgshapiro "WARNING: Can not use -d with -q. Disabling debugging.\n"); 46490792Sgshapiro sm_debug_setfile(NULL); 46590792Sgshapiro (void) memset(tTdvect, '\0', sizeof tTdvect); 46690792Sgshapiro } 46790792Sgshapiro 46877349Sgshapiro#if LOG 46964562Sgshapiro if (sysloglabel != NULL) 47064562Sgshapiro { 47177349Sgshapiro /* Sanitize the string */ 47277349Sgshapiro for (p = sysloglabel; *p != '\0'; p++) 47377349Sgshapiro { 47477349Sgshapiro if (!isascii(*p) || !isprint(*p) || *p == '%') 47577349Sgshapiro *p = '*'; 47677349Sgshapiro } 47764562Sgshapiro closelog(); 47864562Sgshapiro# ifdef LOG_MAIL 47964562Sgshapiro openlog(sysloglabel, LOG_PID, LOG_MAIL); 48064562Sgshapiro# else /* LOG_MAIL */ 48164562Sgshapiro openlog(sysloglabel, LOG_PID); 48264562Sgshapiro# endif /* LOG_MAIL */ 48377349Sgshapiro } 48464562Sgshapiro#endif /* LOG */ 48564562Sgshapiro 48638032Speter /* set up the blank envelope */ 48738032Speter BlankEnvelope.e_puthdr = putheader; 48838032Speter BlankEnvelope.e_putbody = putbody; 48938032Speter BlankEnvelope.e_xfp = NULL; 49038032Speter STRUCTCOPY(NullAddress, BlankEnvelope.e_from); 49138032Speter CurEnv = &BlankEnvelope; 49238032Speter STRUCTCOPY(NullAddress, MainEnvelope.e_from); 49338032Speter 49438032Speter /* 49538032Speter ** Set default values for variables. 49638032Speter ** These cannot be in initialized data space. 49738032Speter */ 49838032Speter 49938032Speter setdefaults(&BlankEnvelope); 50090792Sgshapiro initmacros(&BlankEnvelope); 50138032Speter 50290792Sgshapiro /* reset macro */ 50390792Sgshapiro set_op_mode(OpMode); 50438032Speter 50538032Speter pw = sm_getpwuid(RealUid); 50638032Speter if (pw != NULL) 50790792Sgshapiro (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); 50838032Speter else 50990792Sgshapiro (void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", 51090792Sgshapiro (int) RealUid); 51164562Sgshapiro 51238032Speter RealUserName = rnamebuf; 51338032Speter 51438032Speter if (tTd(0, 101)) 51538032Speter { 51690792Sgshapiro sm_dprintf("Version %s\n", Version); 51790792Sgshapiro finis(false, true, EX_OK); 51890792Sgshapiro /* NOTREACHED */ 51938032Speter } 52038032Speter 52138032Speter /* 52290792Sgshapiro ** if running non-set-user-ID binary as non-root, pretend 52338032Speter ** we are the RunAsUid 52438032Speter */ 52577349Sgshapiro 52638032Speter if (RealUid != 0 && geteuid() == RealUid) 52738032Speter { 52838032Speter if (tTd(47, 1)) 52990792Sgshapiro sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n", 53090792Sgshapiro (int) RealUid); 53138032Speter RunAsUid = RealUid; 53238032Speter } 53338032Speter else if (geteuid() != 0) 53438032Speter RunAsUid = geteuid(); 53538032Speter 53690792Sgshapiro EffGid = getegid(); 53790792Sgshapiro if (RealUid != 0 && EffGid == RealGid) 53838032Speter RunAsGid = RealGid; 53938032Speter 54038032Speter if (tTd(47, 5)) 54138032Speter { 54290792Sgshapiro sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n", 54390792Sgshapiro (int) geteuid(), (int) getuid(), 54490792Sgshapiro (int) getegid(), (int) getgid()); 54590792Sgshapiro sm_dprintf("main: RunAsUser = %d:%d\n", 54690792Sgshapiro (int) RunAsUid, (int) RunAsGid); 54738032Speter } 54838032Speter 54938032Speter /* save command line arguments */ 55064562Sgshapiro j = 0; 55138032Speter for (av = argv; *av != NULL; ) 55264562Sgshapiro j += strlen(*av++) + 1; 55338032Speter SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); 55464562Sgshapiro CommandLineArgs = xalloc(j); 55538032Speter p = CommandLineArgs; 55638032Speter for (av = argv, i = 0; *av != NULL; ) 55738032Speter { 55864562Sgshapiro int h; 55964562Sgshapiro 56038032Speter SaveArgv[i++] = newstr(*av); 56138032Speter if (av != argv) 56238032Speter *p++ = ' '; 56390792Sgshapiro (void) sm_strlcpy(p, *av++, j); 56464562Sgshapiro h = strlen(p); 56564562Sgshapiro p += h; 56664562Sgshapiro j -= h + 1; 56738032Speter } 56838032Speter SaveArgv[i] = NULL; 56938032Speter 57038032Speter if (tTd(0, 1)) 57138032Speter { 57238032Speter extern char *CompileOptions[]; 57338032Speter 57490792Sgshapiro sm_dprintf("Version %s\n Compiled with:", Version); 57590792Sgshapiro sm_printoptions(CompileOptions); 57638032Speter } 57738032Speter if (tTd(0, 10)) 57838032Speter { 57938032Speter extern char *OsCompileOptions[]; 58038032Speter 58190792Sgshapiro sm_dprintf(" OS Defines:"); 58290792Sgshapiro sm_printoptions(OsCompileOptions); 58338032Speter#ifdef _PATH_UNIX 58490792Sgshapiro sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX); 58564562Sgshapiro#endif /* _PATH_UNIX */ 58690792Sgshapiro 58790792Sgshapiro sm_dprintf(" Conf file:\t%s (default for MSP)\n", 58890792Sgshapiro getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF, 58990792Sgshapiro conffile)); 59090792Sgshapiro sm_dprintf(" Conf file:\t%s (default for MTA)\n", 59190792Sgshapiro getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF, 59290792Sgshapiro conffile)); 59390792Sgshapiro sm_dprintf(" Pid file:\t%s (default)\n", PidFile); 59438032Speter } 59538032Speter 59690792Sgshapiro if (tTd(0, 12)) 59790792Sgshapiro { 59890792Sgshapiro extern char *SmCompileOptions[]; 59938032Speter 60090792Sgshapiro sm_dprintf(" libsm Defines:"); 60190792Sgshapiro sm_printoptions(SmCompileOptions); 60290792Sgshapiro } 60390792Sgshapiro 60490792Sgshapiro if (tTd(0, 13)) 60590792Sgshapiro { 60690792Sgshapiro extern char *FFRCompileOptions[]; 60790792Sgshapiro 60890792Sgshapiro sm_dprintf(" FFR Defines:"); 60990792Sgshapiro sm_printoptions(FFRCompileOptions); 61090792Sgshapiro } 61190792Sgshapiro 61290792Sgshapiro InChannel = smioin; 61390792Sgshapiro OutChannel = smioout; 61490792Sgshapiro 61538032Speter /* clear sendmail's environment */ 61638032Speter ExternalEnviron = environ; 61738032Speter emptyenviron[0] = NULL; 61838032Speter environ = emptyenviron; 61938032Speter 62038032Speter /* 62142575Speter ** restore any original TZ setting until TimeZoneSpec has been 62242575Speter ** determined - or early log messages may get bogus time stamps 62338032Speter */ 62490792Sgshapiro 62538032Speter if ((p = getextenv("TZ")) != NULL) 62638032Speter { 62738032Speter char *tz; 62838032Speter int tzlen; 62938032Speter 63090792Sgshapiro /* XXX check for reasonable length? */ 63138032Speter tzlen = strlen(p) + 4; 63238032Speter tz = xalloc(tzlen); 63390792Sgshapiro (void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p); 63490792Sgshapiro 63590792Sgshapiro /* XXX check return code? */ 63664562Sgshapiro (void) putenv(tz); 63738032Speter } 63838032Speter 63938032Speter /* prime the child environment */ 64038032Speter setuserenv("AGENT", "sendmail"); 64138032Speter 64290792Sgshapiro (void) sm_signal(SIGPIPE, SIG_IGN); 64338032Speter OldUmask = umask(022); 64438032Speter FullName = getextenv("NAME"); 64595154Sgshapiro if (FullName != NULL) 64695154Sgshapiro FullName = newstr(FullName); 64738032Speter 64838032Speter /* 64938032Speter ** Initialize name server if it is going to be used. 65038032Speter */ 65138032Speter 65238032Speter#if NAMED_BIND 65338032Speter if (!bitset(RES_INIT, _res.options)) 65464562Sgshapiro (void) res_init(); 65538032Speter if (tTd(8, 8)) 65638032Speter _res.options |= RES_DEBUG; 65738032Speter else 65838032Speter _res.options &= ~RES_DEBUG; 65938032Speter# ifdef RES_NOALIASES 66090792Sgshapiro if (bitset(RES_NOALIASES, _res.options)) 66190792Sgshapiro ResNoAliases = true; 66238032Speter _res.options |= RES_NOALIASES; 66364562Sgshapiro# endif /* RES_NOALIASES */ 66464562Sgshapiro TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry; 66564562Sgshapiro TimeOuts.res_retry[RES_TO_FIRST] = _res.retry; 66664562Sgshapiro TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry; 66764562Sgshapiro TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans; 66864562Sgshapiro TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans; 66964562Sgshapiro TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans; 67064562Sgshapiro#endif /* NAMED_BIND */ 67138032Speter 67238032Speter errno = 0; 67338032Speter from = NULL; 67438032Speter 67538032Speter /* initialize some macros, etc. */ 67690792Sgshapiro init_vendor_macros(&BlankEnvelope); 67738032Speter 67838032Speter /* version */ 67990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version); 68038032Speter 68138032Speter /* hostname */ 68238032Speter hp = myhostname(jbuf, sizeof jbuf); 68338032Speter if (jbuf[0] != '\0') 68438032Speter { 68590792Sgshapiro struct utsname utsname; 68638032Speter 68738032Speter if (tTd(0, 4)) 68890792Sgshapiro sm_dprintf("Canonical name: %s\n", jbuf); 68990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf); 69090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf); 69138032Speter setclass('w', jbuf); 69238032Speter 69338032Speter p = strchr(jbuf, '.'); 69438032Speter if (p != NULL) 69538032Speter { 69638032Speter if (p[1] != '\0') 69738032Speter { 69890792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', 69990792Sgshapiro &p[1]); 70038032Speter } 70138032Speter while (p != NULL && strchr(&p[1], '.') != NULL) 70238032Speter { 70338032Speter *p = '\0'; 70438032Speter if (tTd(0, 4)) 70590792Sgshapiro sm_dprintf("\ta.k.a.: %s\n", jbuf); 70638032Speter setclass('w', jbuf); 70738032Speter *p++ = '.'; 70838032Speter p = strchr(p, '.'); 70938032Speter } 71038032Speter } 71138032Speter 71238032Speter if (uname(&utsname) >= 0) 71338032Speter p = utsname.nodename; 71438032Speter else 71538032Speter { 71638032Speter if (tTd(0, 22)) 71790792Sgshapiro sm_dprintf("uname failed (%s)\n", 71890792Sgshapiro sm_errstring(errno)); 71938032Speter makelower(jbuf); 72038032Speter p = jbuf; 72138032Speter } 72238032Speter if (tTd(0, 4)) 72390792Sgshapiro sm_dprintf(" UUCP nodename: %s\n", p); 72490792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p); 72538032Speter setclass('k', p); 72638032Speter setclass('w', p); 72738032Speter } 72838032Speter if (hp != NULL) 72938032Speter { 73038032Speter for (av = hp->h_aliases; av != NULL && *av != NULL; av++) 73138032Speter { 73238032Speter if (tTd(0, 4)) 73390792Sgshapiro sm_dprintf("\ta.k.a.: %s\n", *av); 73438032Speter setclass('w', *av); 73538032Speter } 73664562Sgshapiro#if NETINET || NETINET6 73790792Sgshapiro for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++) 73838032Speter { 73964562Sgshapiro# if NETINET6 74064562Sgshapiro char *addr; 74164562Sgshapiro char buf6[INET6_ADDRSTRLEN]; 74264562Sgshapiro struct in6_addr ia6; 74364562Sgshapiro# endif /* NETINET6 */ 74464562Sgshapiro# if NETINET 74564562Sgshapiro struct in_addr ia; 74664562Sgshapiro# endif /* NETINET */ 74764562Sgshapiro char ipbuf[103]; 74864562Sgshapiro 74964562Sgshapiro ipbuf[0] = '\0'; 75064562Sgshapiro switch (hp->h_addrtype) 75138032Speter { 75264562Sgshapiro# if NETINET 75364562Sgshapiro case AF_INET: 75464562Sgshapiro if (hp->h_length != INADDRSZ) 75564562Sgshapiro break; 75638032Speter 75764562Sgshapiro memmove(&ia, hp->h_addr_list[i], INADDRSZ); 75890792Sgshapiro (void) sm_snprintf(ipbuf, sizeof ipbuf, 75990792Sgshapiro "[%.100s]", inet_ntoa(ia)); 76064562Sgshapiro break; 76164562Sgshapiro# endif /* NETINET */ 76264562Sgshapiro 76364562Sgshapiro# if NETINET6 76464562Sgshapiro case AF_INET6: 76564562Sgshapiro if (hp->h_length != IN6ADDRSZ) 76664562Sgshapiro break; 76764562Sgshapiro 76864562Sgshapiro memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ); 76964562Sgshapiro addr = anynet_ntop(&ia6, buf6, sizeof buf6); 77064562Sgshapiro if (addr != NULL) 77190792Sgshapiro (void) sm_snprintf(ipbuf, sizeof ipbuf, 77290792Sgshapiro "[%.100s]", addr); 77364562Sgshapiro break; 77464562Sgshapiro# endif /* NETINET6 */ 77538032Speter } 77664562Sgshapiro if (ipbuf[0] == '\0') 77764562Sgshapiro break; 77864562Sgshapiro 77964562Sgshapiro if (tTd(0, 4)) 78090792Sgshapiro sm_dprintf("\ta.k.a.: %s\n", ipbuf); 78164562Sgshapiro setclass('w', ipbuf); 78238032Speter } 78364562Sgshapiro#endif /* NETINET || NETINET6 */ 78490792Sgshapiro#if NETINET6 78571345Sgshapiro freehostent(hp); 78671345Sgshapiro hp = NULL; 78790792Sgshapiro#endif /* NETINET6 */ 78838032Speter } 78938032Speter 79038032Speter /* current time */ 79190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL)); 79290792Sgshapiro 79364562Sgshapiro /* current load average */ 79490792Sgshapiro sm_getla(); 79538032Speter 79638032Speter QueueLimitRecipient = (QUEUE_CHAR *) NULL; 79738032Speter QueueLimitSender = (QUEUE_CHAR *) NULL; 79838032Speter QueueLimitId = (QUEUE_CHAR *) NULL; 79990792Sgshapiro#if _FFR_QUARANTINE 80090792Sgshapiro QueueLimitQuarantine = (QUEUE_CHAR *) NULL; 80190792Sgshapiro#endif /* _FFR_QUARANTINE */ 80238032Speter 80338032Speter /* 80442575Speter ** Crack argv. 80538032Speter */ 80638032Speter 80738032Speter optind = 1; 80838032Speter while ((j = getopt(argc, argv, OPTIONS)) != -1) 80938032Speter { 81038032Speter switch (j) 81138032Speter { 81238032Speter case 'b': /* operations mode */ 81390792Sgshapiro /* already done */ 81490792Sgshapiro break; 81538032Speter 81690792Sgshapiro case 'A': /* use Alternate sendmail/submit.cf */ 81790792Sgshapiro cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF 81890792Sgshapiro : SM_GET_SENDMAIL_CF; 81938032Speter break; 82038032Speter 82138032Speter case 'B': /* body type */ 82290792Sgshapiro CHECK_AGAINST_OPMODE(j); 82390792Sgshapiro BlankEnvelope.e_bodytype = newstr(optarg); 82438032Speter break; 82538032Speter 82638032Speter case 'C': /* select configuration file (already done) */ 82738032Speter if (RealUid != 0) 82890792Sgshapiro warn_C_flag = true; 82990792Sgshapiro conffile = newstr(optarg); 83090792Sgshapiro dp = drop_privileges(true); 83164562Sgshapiro setstat(dp); 83290792Sgshapiro safecf = false; 83338032Speter break; 83438032Speter 83590792Sgshapiro case 'd': /* debugging */ 83690792Sgshapiro /* already done */ 83738032Speter break; 83838032Speter 83938032Speter case 'f': /* from address */ 84038032Speter case 'r': /* obsolete -f flag */ 84190792Sgshapiro CHECK_AGAINST_OPMODE(j); 84238032Speter if (from != NULL) 84338032Speter { 84438032Speter usrerr("More than one \"from\" person"); 84538032Speter ExitStat = EX_USAGE; 84638032Speter break; 84738032Speter } 84890792Sgshapiro from = newstr(denlstring(optarg, true, true)); 84938032Speter if (strcmp(RealUserName, from) != 0) 85038032Speter warn_f_flag = j; 85138032Speter break; 85238032Speter 85338032Speter case 'F': /* set full name */ 85490792Sgshapiro CHECK_AGAINST_OPMODE(j); 85538032Speter FullName = newstr(optarg); 85638032Speter break; 85738032Speter 85864562Sgshapiro case 'G': /* relay (gateway) submission */ 85964562Sgshapiro /* already set */ 86090792Sgshapiro CHECK_AGAINST_OPMODE(j); 86164562Sgshapiro break; 86264562Sgshapiro 86338032Speter case 'h': /* hop count */ 86490792Sgshapiro CHECK_AGAINST_OPMODE(j); 86590792Sgshapiro BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep, 86690792Sgshapiro 10); 86790792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", 86890792Sgshapiro BlankEnvelope.e_hopcount); 86990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf); 87090792Sgshapiro 87138032Speter if (*ep) 87238032Speter { 87338032Speter usrerr("Bad hop count (%s)", optarg); 87438032Speter ExitStat = EX_USAGE; 87538032Speter } 87638032Speter break; 87764562Sgshapiro 87864562Sgshapiro case 'L': /* program label */ 87964562Sgshapiro /* already set */ 88064562Sgshapiro break; 88164562Sgshapiro 88238032Speter case 'n': /* don't alias */ 88390792Sgshapiro CHECK_AGAINST_OPMODE(j); 88490792Sgshapiro NoAlias = true; 88538032Speter break; 88638032Speter 88738032Speter case 'N': /* delivery status notifications */ 88890792Sgshapiro CHECK_AGAINST_OPMODE(j); 88938032Speter DefaultNotify |= QHASNOTIFY; 89090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 89190792Sgshapiro macid("{dsn_notify}"), optarg); 89290792Sgshapiro if (sm_strcasecmp(optarg, "never") == 0) 89338032Speter break; 89438032Speter for (p = optarg; p != NULL; optarg = p) 89538032Speter { 89638032Speter p = strchr(p, ','); 89738032Speter if (p != NULL) 89838032Speter *p++ = '\0'; 89990792Sgshapiro if (sm_strcasecmp(optarg, "success") == 0) 90038032Speter DefaultNotify |= QPINGONSUCCESS; 90190792Sgshapiro else if (sm_strcasecmp(optarg, "failure") == 0) 90238032Speter DefaultNotify |= QPINGONFAILURE; 90390792Sgshapiro else if (sm_strcasecmp(optarg, "delay") == 0) 90438032Speter DefaultNotify |= QPINGONDELAY; 90538032Speter else 90638032Speter { 90738032Speter usrerr("Invalid -N argument"); 90838032Speter ExitStat = EX_USAGE; 90938032Speter } 91038032Speter } 91138032Speter break; 91238032Speter 91338032Speter case 'o': /* set option */ 91490792Sgshapiro setoption(*optarg, optarg + 1, false, true, 91590792Sgshapiro &BlankEnvelope); 91638032Speter break; 91738032Speter 91838032Speter case 'O': /* set option (long form) */ 91990792Sgshapiro setoption(' ', optarg, false, true, &BlankEnvelope); 92038032Speter break; 92138032Speter 92238032Speter case 'p': /* set protocol */ 92390792Sgshapiro CHECK_AGAINST_OPMODE(j); 92438032Speter p = strchr(optarg, ':'); 92538032Speter if (p != NULL) 92638032Speter { 92738032Speter *p++ = '\0'; 92838032Speter if (*p != '\0') 92938032Speter { 93090792Sgshapiro ep = sm_malloc_x(strlen(p) + 1); 93138032Speter cleanstrcpy(ep, p, MAXNAME); 93290792Sgshapiro macdefine(&BlankEnvelope.e_macro, 93390792Sgshapiro A_HEAP, 's', ep); 93438032Speter } 93538032Speter } 93638032Speter if (*optarg != '\0') 93738032Speter { 93890792Sgshapiro ep = sm_malloc_x(strlen(optarg) + 1); 93938032Speter cleanstrcpy(ep, optarg, MAXNAME); 94090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_HEAP, 94190792Sgshapiro 'r', ep); 94238032Speter } 94338032Speter break; 94438032Speter 94590792Sgshapiro#if _FFR_QUARANTINE 94690792Sgshapiro case 'Q': /* change quarantining on queued items */ 94790792Sgshapiro /* sanity check */ 94890792Sgshapiro if (OpMode != MD_DELIVER && 94990792Sgshapiro OpMode != MD_QUEUERUN) 95090792Sgshapiro { 95190792Sgshapiro usrerr("Can not use -Q with -b%c", OpMode); 95290792Sgshapiro ExitStat = EX_USAGE; 95390792Sgshapiro break; 95490792Sgshapiro } 95590792Sgshapiro 95690792Sgshapiro if (OpMode == MD_DELIVER) 95790792Sgshapiro set_op_mode(MD_QUEUERUN); 95890792Sgshapiro 95990792Sgshapiro FullName = NULL; 96090792Sgshapiro 96190792Sgshapiro quarantining = newstr(optarg); 96290792Sgshapiro break; 96390792Sgshapiro#endif /* _FFR_QUARANTINE */ 96490792Sgshapiro 96538032Speter case 'q': /* run queue files at intervals */ 96664562Sgshapiro /* sanity check */ 96764562Sgshapiro if (OpMode != MD_DELIVER && 96864562Sgshapiro OpMode != MD_DAEMON && 96964562Sgshapiro OpMode != MD_FGDAEMON && 97064562Sgshapiro OpMode != MD_PRINT && 97190792Sgshapiro OpMode != MD_PRINTNQE && 97264562Sgshapiro OpMode != MD_QUEUERUN) 97364562Sgshapiro { 97464562Sgshapiro usrerr("Can not use -q with -b%c", OpMode); 97564562Sgshapiro ExitStat = EX_USAGE; 97664562Sgshapiro break; 97764562Sgshapiro } 97864562Sgshapiro 97964562Sgshapiro /* don't override -bd, -bD or -bp */ 98064562Sgshapiro if (OpMode == MD_DELIVER) 98190792Sgshapiro set_op_mode(MD_QUEUERUN); 98264562Sgshapiro 98338032Speter FullName = NULL; 98490792Sgshapiro negate = optarg[0] == '!'; 98590792Sgshapiro if (negate) 98690792Sgshapiro { 98790792Sgshapiro /* negate meaning of pattern match */ 98890792Sgshapiro optarg++; /* skip '!' for next switch */ 98990792Sgshapiro } 99064562Sgshapiro 99138032Speter switch (optarg[0]) 99238032Speter { 99390792Sgshapiro case 'G': /* Limit by queue group name */ 99490792Sgshapiro if (negate) 99590792Sgshapiro { 99690792Sgshapiro usrerr("Can not use -q!G"); 99790792Sgshapiro ExitStat = EX_USAGE; 99890792Sgshapiro break; 99990792Sgshapiro } 100090792Sgshapiro if (queuegroup != NULL) 100190792Sgshapiro { 100290792Sgshapiro usrerr("Can not use multiple -qG options"); 100390792Sgshapiro ExitStat = EX_USAGE; 100490792Sgshapiro break; 100590792Sgshapiro } 100690792Sgshapiro queuegroup = newstr(&optarg[1]); 100790792Sgshapiro break; 100890792Sgshapiro 100990792Sgshapiro case 'I': /* Limit by ID */ 101064562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 101138032Speter new->queue_match = newstr(&optarg[1]); 101290792Sgshapiro new->queue_negate = negate; 101338032Speter new->queue_next = QueueLimitId; 101438032Speter QueueLimitId = new; 101538032Speter break; 101638032Speter 101790792Sgshapiro case 'R': /* Limit by recipient */ 101864562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 101938032Speter new->queue_match = newstr(&optarg[1]); 102090792Sgshapiro new->queue_negate = negate; 102138032Speter new->queue_next = QueueLimitRecipient; 102238032Speter QueueLimitRecipient = new; 102338032Speter break; 102438032Speter 102590792Sgshapiro case 'S': /* Limit by sender */ 102664562Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 102738032Speter new->queue_match = newstr(&optarg[1]); 102890792Sgshapiro new->queue_negate = negate; 102938032Speter new->queue_next = QueueLimitSender; 103038032Speter QueueLimitSender = new; 103138032Speter break; 103238032Speter 103390792Sgshapiro case 'f': /* foreground queue run */ 103490792Sgshapiro foregroundqueue = true; 103590792Sgshapiro break; 103690792Sgshapiro 103790792Sgshapiro#if _FFR_QUARANTINE 103890792Sgshapiro case 'Q': /* Limit by quarantine message */ 103990792Sgshapiro if (optarg[1] != '\0') 104090792Sgshapiro { 104190792Sgshapiro new = (QUEUE_CHAR *) xalloc(sizeof *new); 104290792Sgshapiro new->queue_match = newstr(&optarg[1]); 104390792Sgshapiro new->queue_negate = negate; 104490792Sgshapiro new->queue_next = QueueLimitQuarantine; 104590792Sgshapiro QueueLimitQuarantine = new; 104690792Sgshapiro } 104790792Sgshapiro QueueMode = QM_QUARANTINE; 104890792Sgshapiro break; 104990792Sgshapiro 105090792Sgshapiro case 'L': /* act on lost items */ 105190792Sgshapiro QueueMode = QM_LOST; 105290792Sgshapiro break; 105390792Sgshapiro#endif /* _FFR_QUARANTINE */ 105490792Sgshapiro 105590792Sgshapiro case 'p': /* Persistent queue */ 105690792Sgshapiro queuepersistent = true; 105790792Sgshapiro if (QueueIntvl == 0) 105890792Sgshapiro QueueIntvl = 1; 105990792Sgshapiro if (optarg[1] == '\0') 106090792Sgshapiro break; 106190792Sgshapiro ++optarg; 106290792Sgshapiro /* FALLTHROUGH */ 106390792Sgshapiro 106438032Speter default: 106564562Sgshapiro i = Errors; 106638032Speter QueueIntvl = convtime(optarg, 'm'); 106798841Sgshapiro if (QueueIntvl < 0) 106898841Sgshapiro { 106998841Sgshapiro usrerr("Invalid -q value"); 107098841Sgshapiro ExitStat = EX_USAGE; 107198841Sgshapiro } 107264562Sgshapiro 107364562Sgshapiro /* check for bad conversion */ 107464562Sgshapiro if (i < Errors) 107564562Sgshapiro ExitStat = EX_USAGE; 107638032Speter break; 107738032Speter } 107838032Speter break; 107938032Speter 108038032Speter case 'R': /* DSN RET: what to return */ 108190792Sgshapiro CHECK_AGAINST_OPMODE(j); 108290792Sgshapiro if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags)) 108338032Speter { 108438032Speter usrerr("Duplicate -R flag"); 108538032Speter ExitStat = EX_USAGE; 108638032Speter break; 108738032Speter } 108890792Sgshapiro BlankEnvelope.e_flags |= EF_RET_PARAM; 108990792Sgshapiro if (sm_strcasecmp(optarg, "hdrs") == 0) 109090792Sgshapiro BlankEnvelope.e_flags |= EF_NO_BODY_RETN; 109190792Sgshapiro else if (sm_strcasecmp(optarg, "full") != 0) 109238032Speter { 109338032Speter usrerr("Invalid -R value"); 109438032Speter ExitStat = EX_USAGE; 109538032Speter } 109690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 109790792Sgshapiro macid("{dsn_ret}"), optarg); 109838032Speter break; 109938032Speter 110038032Speter case 't': /* read recipients from message */ 110190792Sgshapiro CHECK_AGAINST_OPMODE(j); 110290792Sgshapiro GrabTo = true; 110338032Speter break; 110438032Speter 110538032Speter case 'V': /* DSN ENVID: set "original" envelope id */ 110690792Sgshapiro CHECK_AGAINST_OPMODE(j); 110738032Speter if (!xtextok(optarg)) 110838032Speter { 110938032Speter usrerr("Invalid syntax in -V flag"); 111038032Speter ExitStat = EX_USAGE; 111138032Speter } 111238032Speter else 111364562Sgshapiro { 111490792Sgshapiro BlankEnvelope.e_envid = newstr(optarg); 111590792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 111690792Sgshapiro macid("{dsn_envid}"), optarg); 111764562Sgshapiro } 111838032Speter break; 111938032Speter 112038032Speter case 'X': /* traffic log file */ 112190792Sgshapiro dp = drop_privileges(true); 112264562Sgshapiro setstat(dp); 112364562Sgshapiro if (stat(optarg, &traf_st) == 0 && 112464562Sgshapiro S_ISFIFO(traf_st.st_mode)) 112590792Sgshapiro TrafficLogFile = sm_io_open(SmFtStdio, 112690792Sgshapiro SM_TIME_DEFAULT, 112790792Sgshapiro optarg, 112890792Sgshapiro SM_IO_WRONLY, NULL); 112964562Sgshapiro else 113090792Sgshapiro TrafficLogFile = sm_io_open(SmFtStdio, 113190792Sgshapiro SM_TIME_DEFAULT, 113290792Sgshapiro optarg, 113390792Sgshapiro SM_IO_APPEND, NULL); 113438032Speter if (TrafficLogFile == NULL) 113538032Speter { 113638032Speter syserr("cannot open %s", optarg); 113738032Speter ExitStat = EX_CANTCREAT; 113838032Speter break; 113938032Speter } 114090792Sgshapiro (void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT, 114190792Sgshapiro NULL, SM_IO_LBF, 0); 114238032Speter break; 114338032Speter 114438032Speter /* compatibility flags */ 114538032Speter case 'c': /* connect to non-local mailers */ 114638032Speter case 'i': /* don't let dot stop me */ 114738032Speter case 'm': /* send to me too */ 114838032Speter case 'T': /* set timeout interval */ 114938032Speter case 'v': /* give blow-by-blow description */ 115090792Sgshapiro setoption(j, "T", false, true, &BlankEnvelope); 115138032Speter break; 115238032Speter 115338032Speter case 'e': /* error message disposition */ 115438032Speter case 'M': /* define macro */ 115590792Sgshapiro setoption(j, optarg, false, true, &BlankEnvelope); 115638032Speter break; 115738032Speter 115838032Speter case 's': /* save From lines in headers */ 115990792Sgshapiro setoption('f', "T", false, true, &BlankEnvelope); 116038032Speter break; 116138032Speter 116264562Sgshapiro#ifdef DBM 116338032Speter case 'I': /* initialize alias DBM file */ 116490792Sgshapiro set_op_mode(MD_INITALIAS); 116538032Speter break; 116664562Sgshapiro#endif /* DBM */ 116738032Speter 116864562Sgshapiro#if defined(__osf__) || defined(_AIX3) 116938032Speter case 'x': /* random flag that OSF/1 & AIX mailx passes */ 117038032Speter break; 117164562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */ 117264562Sgshapiro#if defined(sony_news) 117338032Speter case 'E': 117438032Speter case 'J': /* ignore flags for Japanese code conversion 117564562Sgshapiro implemented on Sony NEWS */ 117638032Speter break; 117764562Sgshapiro#endif /* defined(sony_news) */ 117838032Speter 117938032Speter default: 118090792Sgshapiro finis(true, true, EX_USAGE); 118190792Sgshapiro /* NOTREACHED */ 118238032Speter break; 118338032Speter } 118438032Speter } 118538032Speter 118690792Sgshapiro /* if we've had errors so far, exit now */ 118790792Sgshapiro if ((ExitStat != EX_OK && OpMode != MD_TEST) || 118890792Sgshapiro ExitStat == EX_OSERR) 118964562Sgshapiro { 119090792Sgshapiro finis(false, true, ExitStat); 119190792Sgshapiro /* NOTREACHED */ 119264562Sgshapiro } 119390792Sgshapiro 119490792Sgshapiro if (bitset(SUBMIT_MTA, SubmitMode)) 119564562Sgshapiro { 119698841Sgshapiro /* If set daemon_flags on command line, don't reset it */ 119798841Sgshapiro if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL) 119898841Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 119998841Sgshapiro macid("{daemon_flags}"), "CC f"); 120064562Sgshapiro } 120190792Sgshapiro else if (OpMode == MD_DELIVER || OpMode == MD_SMTP) 120264562Sgshapiro { 120390792Sgshapiro SubmitMode = SUBMIT_MSA; 120498841Sgshapiro 120598841Sgshapiro /* If set daemon_flags on command line, don't reset it */ 120698841Sgshapiro if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL) 120798841Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 120898841Sgshapiro macid("{daemon_flags}"), "c u"); 120964562Sgshapiro } 121064562Sgshapiro 121138032Speter /* 121238032Speter ** Do basic initialization. 121338032Speter ** Read system control file. 121438032Speter ** Extract special fields for local use. 121538032Speter */ 121638032Speter 121738032Speter#if XDEBUG 121838032Speter checkfd012("before readcf"); 121964562Sgshapiro#endif /* XDEBUG */ 122090792Sgshapiro vendor_pre_defaults(&BlankEnvelope); 122164562Sgshapiro 122290792Sgshapiro readcf(getcfname(OpMode, SubmitMode, cftype, conffile), 122390792Sgshapiro safecf, &BlankEnvelope); 122490792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) 122590792Sgshapiro ConfigFileRead = true; 122690792Sgshapiro#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */ 122790792Sgshapiro vendor_post_defaults(&BlankEnvelope); 122838032Speter 122990792Sgshapiro /* now we can complain about missing fds */ 123090792Sgshapiro if (MissingFds != 0 && LogLevel > 8) 123190792Sgshapiro { 123290792Sgshapiro char mbuf[MAXLINE]; 123390792Sgshapiro 123490792Sgshapiro mbuf[0] = '\0'; 123590792Sgshapiro if (bitset(1 << STDIN_FILENO, MissingFds)) 123690792Sgshapiro (void) sm_strlcat(mbuf, ", stdin", sizeof mbuf); 123790792Sgshapiro if (bitset(1 << STDOUT_FILENO, MissingFds)) 123890792Sgshapiro (void) sm_strlcat(mbuf, ", stdout", sizeof mbuf); 123990792Sgshapiro if (bitset(1 << STDERR_FILENO, MissingFds)) 124090792Sgshapiro (void) sm_strlcat(mbuf, ", stderr", sizeof mbuf); 124190792Sgshapiro 124290792Sgshapiro /* Notice: fill_errno is from high above: fill_fd() */ 124390792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 124490792Sgshapiro "File descriptors missing on startup: %s; %s", 124590792Sgshapiro &mbuf[2], sm_errstring(fill_errno)); 124690792Sgshapiro } 124790792Sgshapiro 124877349Sgshapiro /* Remove the ability for a normal user to send signals */ 124990792Sgshapiro if (RealUid != 0 && RealUid != geteuid()) 125077349Sgshapiro { 125177349Sgshapiro uid_t new_uid = geteuid(); 125277349Sgshapiro 125377349Sgshapiro#if HASSETREUID 125477349Sgshapiro /* 125577349Sgshapiro ** Since we can differentiate between uid and euid, 125677349Sgshapiro ** make the uid a different user so the real user 125777349Sgshapiro ** can't send signals. However, it doesn't need to be 125877349Sgshapiro ** root (euid has root). 125977349Sgshapiro */ 126077349Sgshapiro 126177349Sgshapiro if (new_uid == 0) 126277349Sgshapiro new_uid = DefUid; 126377349Sgshapiro if (tTd(47, 5)) 126490792Sgshapiro sm_dprintf("Changing real uid to %d\n", (int) new_uid); 126577349Sgshapiro if (setreuid(new_uid, geteuid()) < 0) 126677349Sgshapiro { 126777349Sgshapiro syserr("main: setreuid(%d, %d) failed", 126877349Sgshapiro (int) new_uid, (int) geteuid()); 126990792Sgshapiro finis(false, true, EX_OSERR); 127077349Sgshapiro /* NOTREACHED */ 127177349Sgshapiro } 127277349Sgshapiro if (tTd(47, 10)) 127390792Sgshapiro sm_dprintf("Now running as e/ruid %d:%d\n", 127490792Sgshapiro (int) geteuid(), (int) getuid()); 127577349Sgshapiro#else /* HASSETREUID */ 127677349Sgshapiro /* 127777349Sgshapiro ** Have to change both effective and real so need to 127877349Sgshapiro ** change them both to effective to keep privs. 127977349Sgshapiro */ 128077349Sgshapiro 128177349Sgshapiro if (tTd(47, 5)) 128290792Sgshapiro sm_dprintf("Changing uid to %d\n", (int) new_uid); 128377349Sgshapiro if (setuid(new_uid) < 0) 128477349Sgshapiro { 128577349Sgshapiro syserr("main: setuid(%d) failed", (int) new_uid); 128690792Sgshapiro finis(false, true, EX_OSERR); 128777349Sgshapiro /* NOTREACHED */ 128877349Sgshapiro } 128977349Sgshapiro if (tTd(47, 10)) 129090792Sgshapiro sm_dprintf("Now running as e/ruid %d:%d\n", 129190792Sgshapiro (int) geteuid(), (int) getuid()); 129277349Sgshapiro#endif /* HASSETREUID */ 129377349Sgshapiro } 129477349Sgshapiro 129590792Sgshapiro#if NAMED_BIND 129690792Sgshapiro if (FallBackMX != NULL) 129790792Sgshapiro (void) getfallbackmxrr(FallBackMX); 129890792Sgshapiro#endif /* NAMED_BIND */ 129990792Sgshapiro 130090792Sgshapiro if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER) 130190792Sgshapiro { 130290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 130390792Sgshapiro "WARNING: SuperSafe=interactive should only be used with\n DeliveryMode=interactive\n"); 130490792Sgshapiro } 130590792Sgshapiro 130690792Sgshapiro if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)) 130790792Sgshapiro { 130890792Sgshapiro usrerr("Mail submission program cannot be used as daemon"); 130990792Sgshapiro finis(false, true, EX_USAGE); 131090792Sgshapiro } 131190792Sgshapiro 131290792Sgshapiro if (OpMode == MD_DELIVER || OpMode == MD_SMTP || 131390792Sgshapiro OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP || 131490792Sgshapiro OpMode == MD_DAEMON || OpMode == MD_FGDAEMON) 131590792Sgshapiro makeworkgroups(); 131690792Sgshapiro 131777349Sgshapiro /* set up the basic signal handlers */ 131890792Sgshapiro if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN) 131990792Sgshapiro (void) sm_signal(SIGINT, intsig); 132090792Sgshapiro (void) sm_signal(SIGTERM, intsig); 132177349Sgshapiro 132238032Speter /* Enforce use of local time (null string overrides this) */ 132338032Speter if (TimeZoneSpec == NULL) 132438032Speter unsetenv("TZ"); 132538032Speter else if (TimeZoneSpec[0] != '\0') 132638032Speter setuserenv("TZ", TimeZoneSpec); 132738032Speter else 132838032Speter setuserenv("TZ", NULL); 132938032Speter tzset(); 133038032Speter 133190792Sgshapiro /* initialize mailbox database */ 133290792Sgshapiro i = sm_mbdb_initialize(Mbdb); 133390792Sgshapiro if (i != EX_OK) 133490792Sgshapiro { 133590792Sgshapiro usrerr("Can't initialize mailbox database \"%s\": %s", 133690792Sgshapiro Mbdb, sm_strexit(i)); 133790792Sgshapiro ExitStat = i; 133890792Sgshapiro } 133990792Sgshapiro 134038032Speter /* avoid denial-of-service attacks */ 134138032Speter resetlimits(); 134238032Speter 134390792Sgshapiro if (OpMode == MD_TEST) 134438032Speter { 134590792Sgshapiro /* can't be done after readcf if RunAs* is used */ 134690792Sgshapiro dp = drop_privileges(true); 134790792Sgshapiro if (dp != EX_OK) 134890792Sgshapiro { 134990792Sgshapiro finis(false, true, dp); 135090792Sgshapiro /* NOTREACHED */ 135190792Sgshapiro } 135290792Sgshapiro } 135390792Sgshapiro else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) 135490792Sgshapiro { 135538032Speter /* drop privileges -- daemon mode done after socket/bind */ 135690792Sgshapiro dp = drop_privileges(false); 135764562Sgshapiro setstat(dp); 135890792Sgshapiro if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0)) 135990792Sgshapiro { 136090792Sgshapiro usrerr("Mail submission program must have RunAsUser set to non root user"); 136190792Sgshapiro finis(false, true, EX_CONFIG); 136290792Sgshapiro /* NOTREACHED */ 136390792Sgshapiro } 136438032Speter } 136538032Speter 136664562Sgshapiro#if NAMED_BIND 136764562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT]; 136864562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT]; 136964562Sgshapiro#endif /* NAMED_BIND */ 137064562Sgshapiro 137138032Speter /* 137238032Speter ** Find our real host name for future logging. 137338032Speter */ 137438032Speter 137564562Sgshapiro authinfo = getauthinfo(STDIN_FILENO, &forged); 137690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo); 137738032Speter 137838032Speter /* suppress error printing if errors mailed back or whatever */ 137990792Sgshapiro if (BlankEnvelope.e_errormode != EM_PRINT) 138090792Sgshapiro HoldErrs = true; 138138032Speter 138238032Speter /* set up the $=m class now, after .cf has a chance to redefine $m */ 138390792Sgshapiro expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope); 138473188Sgshapiro if (jbuf[0] != '\0') 138573188Sgshapiro setclass('m', jbuf); 138638032Speter 138738032Speter /* probe interfaces and locate any additional names */ 138890792Sgshapiro if (DontProbeInterfaces != DPI_PROBENONE) 138938032Speter load_if_names(); 139038032Speter 139190792Sgshapiro if (tTd(0, 10)) 139290792Sgshapiro { 139390792Sgshapiro /* Now we know which .cf file we use */ 139490792Sgshapiro sm_dprintf(" Conf file:\t%s (selected)\n", 139590792Sgshapiro getcfname(OpMode, SubmitMode, cftype, conffile)); 139690792Sgshapiro sm_dprintf(" Pid file:\t%s (selected)\n", PidFile); 139790792Sgshapiro } 139890792Sgshapiro 139938032Speter if (tTd(0, 1)) 140038032Speter { 140190792Sgshapiro sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============"); 140290792Sgshapiro sm_dprintf("\n (short domain name) $w = "); 140390792Sgshapiro xputs(macvalue('w', &BlankEnvelope)); 140490792Sgshapiro sm_dprintf("\n (canonical domain name) $j = "); 140590792Sgshapiro xputs(macvalue('j', &BlankEnvelope)); 140690792Sgshapiro sm_dprintf("\n (subdomain name) $m = "); 140790792Sgshapiro xputs(macvalue('m', &BlankEnvelope)); 140890792Sgshapiro sm_dprintf("\n (node name) $k = "); 140990792Sgshapiro xputs(macvalue('k', &BlankEnvelope)); 141090792Sgshapiro sm_dprintf("\n========================================================\n\n"); 141138032Speter } 141238032Speter 141338032Speter /* 141438032Speter ** Do more command line checking -- these are things that 141538032Speter ** have to modify the results of reading the config file. 141638032Speter */ 141738032Speter 141838032Speter /* process authorization warnings from command line */ 141938032Speter if (warn_C_flag) 142090792Sgshapiro auth_warning(&BlankEnvelope, "Processed by %s with -C %s", 142190792Sgshapiro RealUserName, conffile); 142264562Sgshapiro if (Warn_Q_option && !wordinclass(RealUserName, 't')) 142390792Sgshapiro auth_warning(&BlankEnvelope, "Processed from queue %s", 142490792Sgshapiro QueueDir); 142590792Sgshapiro if (sysloglabel != NULL && !wordinclass(RealUserName, 't') && 142690792Sgshapiro RealUid != 0 && RealUid != TrustedUid && LogLevel > 1) 142790792Sgshapiro sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label", 142890792Sgshapiro (int) RealUid); 142938032Speter 143038032Speter /* check body type for legality */ 143190792Sgshapiro i = check_bodytype(BlankEnvelope.e_bodytype); 143290792Sgshapiro if (i == BODYTYPE_ILLEGAL) 143338032Speter { 143490792Sgshapiro usrerr("Illegal body type %s", BlankEnvelope.e_bodytype); 143590792Sgshapiro BlankEnvelope.e_bodytype = NULL; 143638032Speter } 143790792Sgshapiro else if (i != BODYTYPE_NONE) 143890792Sgshapiro SevenBitInput = (i == BODYTYPE_7BIT); 143938032Speter 144038032Speter /* tweak default DSN notifications */ 144138032Speter if (DefaultNotify == 0) 144238032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 144338032Speter 144438032Speter /* be sure we don't pick up bogus HOSTALIASES environment variable */ 144564562Sgshapiro if (OpMode == MD_QUEUERUN && RealUid != 0) 144638032Speter (void) unsetenv("HOSTALIASES"); 144738032Speter 144838032Speter /* check for sane configuration level */ 144938032Speter if (ConfigLevel > MAXCONFIGLEVEL) 145038032Speter { 145138032Speter syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", 145290792Sgshapiro ConfigLevel, Version, MAXCONFIGLEVEL); 145338032Speter } 145438032Speter 145538032Speter /* need MCI cache to have persistence */ 145638032Speter if (HostStatDir != NULL && MaxMciCache == 0) 145738032Speter { 145838032Speter HostStatDir = NULL; 145990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 146090792Sgshapiro "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); 146138032Speter } 146238032Speter 146338032Speter /* need HostStatusDir in order to have SingleThreadDelivery */ 146438032Speter if (SingleThreadDelivery && HostStatDir == NULL) 146538032Speter { 146690792Sgshapiro SingleThreadDelivery = false; 146790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 146890792Sgshapiro "Warning: HostStatusDirectory required for SingleThreadDelivery\n"); 146938032Speter } 147038032Speter 147138032Speter /* check for permissions */ 147290792Sgshapiro if (RealUid != 0 && 147342575Speter RealUid != TrustedUid) 147438032Speter { 147590792Sgshapiro char *action = NULL; 147690792Sgshapiro 147790792Sgshapiro switch (OpMode) 147890792Sgshapiro { 147990792Sgshapiro case MD_QUEUERUN: 148090792Sgshapiro#if _FFR_QUARANTINE 148190792Sgshapiro if (quarantining != NULL) 148290792Sgshapiro action = "quarantine jobs"; 148390792Sgshapiro else 148490792Sgshapiro#endif /* _FFR_QUARANTINE */ 148590792Sgshapiro /* Normal users can do a single queue run */ 148690792Sgshapiro if (QueueIntvl == 0) 148790792Sgshapiro break; 148890792Sgshapiro 148990792Sgshapiro /* but not persistent queue runners */ 149090792Sgshapiro if (action == NULL) 149190792Sgshapiro action = "start a queue runner daemon"; 149290792Sgshapiro /* FALLTHROUGH */ 149390792Sgshapiro 149490792Sgshapiro case MD_PURGESTAT: 149590792Sgshapiro if (action == NULL) 149690792Sgshapiro action = "purge host status"; 149790792Sgshapiro /* FALLTHROUGH */ 149890792Sgshapiro 149990792Sgshapiro case MD_DAEMON: 150090792Sgshapiro case MD_FGDAEMON: 150190792Sgshapiro if (action == NULL) 150290792Sgshapiro action = "run daemon"; 150390792Sgshapiro 150490792Sgshapiro if (tTd(65, 1)) 150590792Sgshapiro sm_dprintf("Deny user %d attempt to %s\n", 150690792Sgshapiro (int) RealUid, action); 150790792Sgshapiro 150890792Sgshapiro if (LogLevel > 1) 150990792Sgshapiro sm_syslog(LOG_ALERT, NOQID, 151090792Sgshapiro "user %d attempted to %s", 151190792Sgshapiro (int) RealUid, action); 151290792Sgshapiro HoldErrs = false; 151390792Sgshapiro usrerr("Permission denied (real uid not trusted)"); 151490792Sgshapiro finis(false, true, EX_USAGE); 151590792Sgshapiro /* NOTREACHED */ 151690792Sgshapiro break; 151790792Sgshapiro 151890792Sgshapiro case MD_VERIFY: 151990792Sgshapiro if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags)) 152090792Sgshapiro { 152190792Sgshapiro /* 152290792Sgshapiro ** If -bv and RestrictExpand, 152390792Sgshapiro ** drop privs to prevent normal 152490792Sgshapiro ** users from reading private 152590792Sgshapiro ** aliases/forwards/:include:s 152690792Sgshapiro */ 152790792Sgshapiro 152890792Sgshapiro if (tTd(65, 1)) 152990792Sgshapiro sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n", 153090792Sgshapiro (int) RealUid); 153190792Sgshapiro 153290792Sgshapiro dp = drop_privileges(true); 153390792Sgshapiro 153490792Sgshapiro /* Fake address safety */ 153590792Sgshapiro if (tTd(65, 1)) 153690792Sgshapiro sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n"); 153790792Sgshapiro setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail); 153890792Sgshapiro 153990792Sgshapiro if (dp != EX_OK) 154090792Sgshapiro { 154190792Sgshapiro if (tTd(65, 1)) 154290792Sgshapiro sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n", 154390792Sgshapiro (int) RealUid); 154490792Sgshapiro CurEnv->e_id = NULL; 154590792Sgshapiro finis(true, true, dp); 154690792Sgshapiro /* NOTREACHED */ 154790792Sgshapiro } 154890792Sgshapiro } 154990792Sgshapiro break; 155090792Sgshapiro 155190792Sgshapiro case MD_TEST: 155290792Sgshapiro case MD_PRINT: 155390792Sgshapiro case MD_PRINTNQE: 155490792Sgshapiro case MD_FREEZE: 155590792Sgshapiro case MD_HOSTSTAT: 155690792Sgshapiro /* Nothing special to check */ 155790792Sgshapiro break; 155890792Sgshapiro 155990792Sgshapiro case MD_INITALIAS: 156090792Sgshapiro if (!wordinclass(RealUserName, 't')) 156190792Sgshapiro { 156290792Sgshapiro if (tTd(65, 1)) 156390792Sgshapiro sm_dprintf("Deny user %d attempt to rebuild the alias map\n", 156490792Sgshapiro (int) RealUid); 156590792Sgshapiro if (LogLevel > 1) 156690792Sgshapiro sm_syslog(LOG_ALERT, NOQID, 156790792Sgshapiro "user %d attempted to rebuild the alias map", 156890792Sgshapiro (int) RealUid); 156990792Sgshapiro HoldErrs = false; 157090792Sgshapiro usrerr("Permission denied (real uid not trusted)"); 157190792Sgshapiro finis(false, true, EX_USAGE); 157290792Sgshapiro /* NOTREACHED */ 157390792Sgshapiro } 157490792Sgshapiro if (UseMSP) 157590792Sgshapiro { 157690792Sgshapiro HoldErrs = false; 157790792Sgshapiro usrerr("User %d cannot rebuild aliases in mail submission program", 157890792Sgshapiro (int) RealUid); 157990792Sgshapiro finis(false, true, EX_USAGE); 158090792Sgshapiro /* NOTREACHED */ 158190792Sgshapiro } 158290792Sgshapiro /* FALLTHROUGH */ 158390792Sgshapiro 158490792Sgshapiro default: 158590792Sgshapiro if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) && 158690792Sgshapiro Verbose != 0) 158790792Sgshapiro { 158890792Sgshapiro /* 158990792Sgshapiro ** If -v and RestrictExpand, reset 159090792Sgshapiro ** Verbose to prevent normal users 159190792Sgshapiro ** from seeing the expansion of 159290792Sgshapiro ** aliases/forwards/:include:s 159390792Sgshapiro */ 159490792Sgshapiro 159590792Sgshapiro if (tTd(65, 1)) 159690792Sgshapiro sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n", 159790792Sgshapiro (int) RealUid); 159890792Sgshapiro Verbose = 0; 159990792Sgshapiro } 160090792Sgshapiro break; 160190792Sgshapiro } 160238032Speter } 160338032Speter 160438032Speter if (MeToo) 160538032Speter BlankEnvelope.e_flags |= EF_METOO; 160638032Speter 160738032Speter switch (OpMode) 160838032Speter { 160938032Speter case MD_TEST: 161038032Speter /* don't have persistent host status in test mode */ 161138032Speter HostStatDir = NULL; 161238032Speter if (Verbose == 0) 161338032Speter Verbose = 2; 161490792Sgshapiro BlankEnvelope.e_errormode = EM_PRINT; 161590792Sgshapiro HoldErrs = false; 161638032Speter break; 161738032Speter 161838032Speter case MD_VERIFY: 161990792Sgshapiro BlankEnvelope.e_errormode = EM_PRINT; 162090792Sgshapiro HoldErrs = false; 162138032Speter /* arrange to exit cleanly on hangup signal */ 162290792Sgshapiro if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 162390792Sgshapiro (void) sm_signal(SIGHUP, intsig); 162490792Sgshapiro if (geteuid() != 0) 162590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 162690792Sgshapiro "Notice: -bv may give misleading output for non-privileged user\n"); 162738032Speter break; 162838032Speter 162938032Speter case MD_FGDAEMON: 163090792Sgshapiro run_in_foreground = true; 163190792Sgshapiro set_op_mode(MD_DAEMON); 163264562Sgshapiro /* FALLTHROUGH */ 163338032Speter 163438032Speter case MD_DAEMON: 163590792Sgshapiro vendor_daemon_setup(&BlankEnvelope); 163638032Speter 163738032Speter /* remove things that don't make sense in daemon mode */ 163838032Speter FullName = NULL; 163990792Sgshapiro GrabTo = false; 164038032Speter 164138032Speter /* arrange to restart on hangup signal */ 164238032Speter if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') 164338032Speter sm_syslog(LOG_WARNING, NOQID, 164464562Sgshapiro "daemon invoked without full pathname; kill -1 won't work"); 164538032Speter break; 164638032Speter 164738032Speter case MD_INITALIAS: 164838032Speter Verbose = 2; 164990792Sgshapiro BlankEnvelope.e_errormode = EM_PRINT; 165090792Sgshapiro HoldErrs = false; 165164562Sgshapiro /* FALLTHROUGH */ 165238032Speter 165338032Speter default: 165438032Speter /* arrange to exit cleanly on hangup signal */ 165590792Sgshapiro if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 165690792Sgshapiro (void) sm_signal(SIGHUP, intsig); 165738032Speter break; 165838032Speter } 165938032Speter 166038032Speter /* special considerations for FullName */ 166138032Speter if (FullName != NULL) 166238032Speter { 166338032Speter char *full = NULL; 166438032Speter 166538032Speter /* full names can't have newlines */ 166664562Sgshapiro if (strchr(FullName, '\n') != NULL) 166738032Speter { 166890792Sgshapiro full = newstr(denlstring(FullName, true, true)); 166973188Sgshapiro FullName = full; 167038032Speter } 167173188Sgshapiro 167238032Speter /* check for characters that may have to be quoted */ 167338032Speter if (!rfc822_string(FullName)) 167438032Speter { 167538032Speter /* 167638032Speter ** Quote a full name with special characters 167738032Speter ** as a comment so crackaddr() doesn't destroy 167838032Speter ** the name portion of the address. 167938032Speter */ 168073188Sgshapiro 168190792Sgshapiro FullName = addquotes(FullName, NULL); 168238032Speter if (full != NULL) 168390792Sgshapiro sm_free(full); /* XXX */ 168438032Speter } 168538032Speter } 168638032Speter 168738032Speter /* do heuristic mode adjustment */ 168838032Speter if (Verbose) 168938032Speter { 169038032Speter /* turn off noconnect option */ 169190792Sgshapiro setoption('c', "F", true, false, &BlankEnvelope); 169238032Speter 169338032Speter /* turn on interactive delivery */ 169490792Sgshapiro setoption('d', "", true, false, &BlankEnvelope); 169538032Speter } 169638032Speter 169742575Speter#ifdef VENDOR_CODE 169842575Speter /* check for vendor mismatch */ 169942575Speter if (VendorCode != VENDOR_CODE) 170042575Speter { 170142575Speter message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s", 170242575Speter getvendor(VENDOR_CODE), getvendor(VendorCode)); 170342575Speter } 170464562Sgshapiro#endif /* VENDOR_CODE */ 170564562Sgshapiro 170638032Speter /* check for out of date configuration level */ 170738032Speter if (ConfigLevel < MAXCONFIGLEVEL) 170838032Speter { 170938032Speter message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d", 171038032Speter Version, MAXCONFIGLEVEL, ConfigLevel); 171138032Speter } 171238032Speter 171338032Speter if (ConfigLevel < 3) 171490792Sgshapiro UseErrorsTo = true; 171538032Speter 171638032Speter /* set options that were previous macros */ 171738032Speter if (SmtpGreeting == NULL) 171838032Speter { 171990792Sgshapiro if (ConfigLevel < 7 && 172090792Sgshapiro (p = macvalue('e', &BlankEnvelope)) != NULL) 172138032Speter SmtpGreeting = newstr(p); 172238032Speter else 172338032Speter SmtpGreeting = "\201j Sendmail \201v ready at \201b"; 172438032Speter } 172538032Speter if (UnixFromLine == NULL) 172638032Speter { 172790792Sgshapiro if (ConfigLevel < 7 && 172890792Sgshapiro (p = macvalue('l', &BlankEnvelope)) != NULL) 172938032Speter UnixFromLine = newstr(p); 173038032Speter else 173138032Speter UnixFromLine = "From \201g \201d"; 173238032Speter } 173364562Sgshapiro SmtpError[0] = '\0'; 173438032Speter 173538032Speter /* our name for SMTP codes */ 173690792Sgshapiro expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); 173773188Sgshapiro if (jbuf[0] == '\0') 173890792Sgshapiro PSTRSET(MyHostName, "localhost"); 173973188Sgshapiro else 174090792Sgshapiro PSTRSET(MyHostName, jbuf); 174173188Sgshapiro if (strchr(MyHostName, '.') == NULL) 174238032Speter message("WARNING: local host name (%s) is not qualified; fix $j in config file", 174373188Sgshapiro MyHostName); 174438032Speter 174538032Speter /* make certain that this name is part of the $=w class */ 174638032Speter setclass('w', MyHostName); 174738032Speter 174890792Sgshapiro /* fill in the structure of the *default* queue */ 174990792Sgshapiro st = stab("mqueue", ST_QUEUE, ST_FIND); 175090792Sgshapiro if (st == NULL) 175190792Sgshapiro syserr("No default queue (mqueue) defined"); 175290792Sgshapiro else 175390792Sgshapiro set_def_queueval(st->s_quegrp, true); 175490792Sgshapiro 175538032Speter /* the indices of built-in mailers */ 175638032Speter st = stab("local", ST_MAILER, ST_FIND); 175738032Speter if (st != NULL) 175838032Speter LocalMailer = st->s_mailer; 175938032Speter else if (OpMode != MD_TEST || !warn_C_flag) 176038032Speter syserr("No local mailer defined"); 176138032Speter 176238032Speter st = stab("prog", ST_MAILER, ST_FIND); 176338032Speter if (st == NULL) 176438032Speter syserr("No prog mailer defined"); 176538032Speter else 176638032Speter { 176738032Speter ProgMailer = st->s_mailer; 176838032Speter clrbitn(M_MUSER, ProgMailer->m_flags); 176938032Speter } 177038032Speter 177138032Speter st = stab("*file*", ST_MAILER, ST_FIND); 177238032Speter if (st == NULL) 177338032Speter syserr("No *file* mailer defined"); 177438032Speter else 177538032Speter { 177638032Speter FileMailer = st->s_mailer; 177738032Speter clrbitn(M_MUSER, FileMailer->m_flags); 177838032Speter } 177938032Speter 178038032Speter st = stab("*include*", ST_MAILER, ST_FIND); 178138032Speter if (st == NULL) 178238032Speter syserr("No *include* mailer defined"); 178338032Speter else 178438032Speter InclMailer = st->s_mailer; 178538032Speter 178638032Speter if (ConfigLevel < 6) 178738032Speter { 178838032Speter /* heuristic tweaking of local mailer for back compat */ 178938032Speter if (LocalMailer != NULL) 179038032Speter { 179138032Speter setbitn(M_ALIASABLE, LocalMailer->m_flags); 179238032Speter setbitn(M_HASPWENT, LocalMailer->m_flags); 179338032Speter setbitn(M_TRYRULESET5, LocalMailer->m_flags); 179438032Speter setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); 179538032Speter setbitn(M_CHECKPROG, LocalMailer->m_flags); 179638032Speter setbitn(M_CHECKFILE, LocalMailer->m_flags); 179738032Speter setbitn(M_CHECKUDB, LocalMailer->m_flags); 179838032Speter } 179938032Speter if (ProgMailer != NULL) 180038032Speter setbitn(M_RUNASRCPT, ProgMailer->m_flags); 180138032Speter if (FileMailer != NULL) 180238032Speter setbitn(M_RUNASRCPT, FileMailer->m_flags); 180338032Speter } 180438032Speter if (ConfigLevel < 7) 180538032Speter { 180638032Speter if (LocalMailer != NULL) 180738032Speter setbitn(M_VRFY250, LocalMailer->m_flags); 180838032Speter if (ProgMailer != NULL) 180938032Speter setbitn(M_VRFY250, ProgMailer->m_flags); 181038032Speter if (FileMailer != NULL) 181138032Speter setbitn(M_VRFY250, FileMailer->m_flags); 181238032Speter } 181338032Speter 181438032Speter /* MIME Content-Types that cannot be transfer encoded */ 181538032Speter setclass('n', "multipart/signed"); 181638032Speter 181738032Speter /* MIME message/xxx subtypes that can be treated as messages */ 181838032Speter setclass('s', "rfc822"); 181938032Speter 182038032Speter /* MIME Content-Transfer-Encodings that can be encoded */ 182138032Speter setclass('e', "7bit"); 182238032Speter setclass('e', "8bit"); 182338032Speter setclass('e', "binary"); 182438032Speter 182538032Speter#ifdef USE_B_CLASS 182638032Speter /* MIME Content-Types that should be treated as binary */ 182738032Speter setclass('b', "image"); 182838032Speter setclass('b', "audio"); 182938032Speter setclass('b', "video"); 183038032Speter setclass('b', "application/octet-stream"); 183164562Sgshapiro#endif /* USE_B_CLASS */ 183238032Speter 183342575Speter /* MIME headers which have fields to check for overflow */ 183490792Sgshapiro setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition"); 183590792Sgshapiro setclass(macid("{checkMIMEFieldHeaders}"), "content-type"); 183642575Speter 183742575Speter /* MIME headers to check for length overflow */ 183890792Sgshapiro setclass(macid("{checkMIMETextHeaders}"), "content-description"); 183942575Speter 184042575Speter /* MIME headers to check for overflow and rebalance */ 184190792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-disposition"); 184290792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-id"); 184390792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding"); 184490792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "content-type"); 184590792Sgshapiro setclass(macid("{checkMIMEHeaders}"), "mime-version"); 184642575Speter 184790792Sgshapiro /* Macros to save in the queue file -- don't remove any */ 184890792Sgshapiro setclass(macid("{persistentMacros}"), "r"); 184990792Sgshapiro setclass(macid("{persistentMacros}"), "s"); 185090792Sgshapiro setclass(macid("{persistentMacros}"), "_"); 185190792Sgshapiro setclass(macid("{persistentMacros}"), "{if_addr}"); 185290792Sgshapiro setclass(macid("{persistentMacros}"), "{daemon_flags}"); 185364562Sgshapiro 185438032Speter /* operate in queue directory */ 185590792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 185638032Speter { 185738032Speter if (OpMode != MD_TEST) 185838032Speter { 185938032Speter syserr("QueueDirectory (Q) option must be set"); 186038032Speter ExitStat = EX_CONFIG; 186138032Speter } 186238032Speter } 186338032Speter else 186438032Speter { 186564562Sgshapiro if (OpMode != MD_TEST) 186690792Sgshapiro setup_queues(OpMode == MD_DAEMON); 186738032Speter } 186838032Speter 186938032Speter /* check host status directory for validity */ 187090792Sgshapiro if (HostStatDir != NULL && !path_is_dir(HostStatDir, false)) 187138032Speter { 187238032Speter /* cannot use this value */ 187390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 187490792Sgshapiro "Warning: Cannot use HostStatusDirectory = %s: %s\n", 187590792Sgshapiro HostStatDir, sm_errstring(errno)); 187638032Speter HostStatDir = NULL; 187738032Speter } 187838032Speter 187990792Sgshapiro if (OpMode == MD_QUEUERUN && 188090792Sgshapiro RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) 188138032Speter { 188238032Speter struct stat stbuf; 188338032Speter 188438032Speter /* check to see if we own the queue directory */ 188538032Speter if (stat(".", &stbuf) < 0) 188638032Speter syserr("main: cannot stat %s", QueueDir); 188738032Speter if (stbuf.st_uid != RealUid) 188838032Speter { 188938032Speter /* nope, really a botch */ 189090792Sgshapiro HoldErrs = false; 189138032Speter usrerr("You do not have permission to process the queue"); 189290792Sgshapiro finis(false, true, EX_NOPERM); 189390792Sgshapiro /* NOTREACHED */ 189438032Speter } 189538032Speter } 189638032Speter 189790792Sgshapiro#if MILTER 189864562Sgshapiro /* sanity checks on milter filters */ 189964562Sgshapiro if (OpMode == MD_DAEMON || OpMode == MD_SMTP) 190090792Sgshapiro { 190190792Sgshapiro milter_config(InputFilterList, InputFilters, MAXFILTERS); 190290792Sgshapiro# if _FFR_MILTER_PERDAEMON 190390792Sgshapiro setup_daemon_milters(); 190490792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */ 190590792Sgshapiro } 190690792Sgshapiro#endif /* MILTER */ 190764562Sgshapiro 190890792Sgshapiro /* Convert queuegroup string to qgrp number */ 190990792Sgshapiro if (queuegroup != NULL) 191090792Sgshapiro { 191190792Sgshapiro qgrp = name2qid(queuegroup); 191290792Sgshapiro if (qgrp == NOQGRP) 191390792Sgshapiro { 191490792Sgshapiro HoldErrs = false; 191590792Sgshapiro usrerr("Queue group %s unknown", queuegroup); 191690792Sgshapiro finis(false, true, ExitStat); 191790792Sgshapiro /* NOTREACHED */ 191890792Sgshapiro } 191990792Sgshapiro } 192066494Sgshapiro 192138032Speter /* if we've had errors so far, exit now */ 192238032Speter if (ExitStat != EX_OK && OpMode != MD_TEST) 192390792Sgshapiro { 192490792Sgshapiro finis(false, true, ExitStat); 192590792Sgshapiro /* NOTREACHED */ 192690792Sgshapiro } 192738032Speter 192890792Sgshapiro#if SASL 192990792Sgshapiro /* sendmail specific SASL initialization */ 193090792Sgshapiro sm_sasl_init(); 193190792Sgshapiro#endif /* SASL */ 193290792Sgshapiro 193338032Speter#if XDEBUG 193438032Speter checkfd012("before main() initmaps"); 193564562Sgshapiro#endif /* XDEBUG */ 193638032Speter 193738032Speter /* 193838032Speter ** Do operation-mode-dependent initialization. 193938032Speter */ 194038032Speter 194138032Speter switch (OpMode) 194238032Speter { 194338032Speter case MD_PRINT: 194438032Speter /* print the queue */ 194590792Sgshapiro HoldErrs = false; 194690792Sgshapiro dropenvelope(&BlankEnvelope, true, false); 194790792Sgshapiro (void) sm_signal(SIGPIPE, sigpipe); 194890792Sgshapiro if (qgrp != NOQGRP) 194990792Sgshapiro { 195090792Sgshapiro int j; 195190792Sgshapiro 195290792Sgshapiro /* Selecting a particular queue group to run */ 195390792Sgshapiro for (j = 0; j < Queue[qgrp]->qg_numqueues; j++) 195490792Sgshapiro { 195590792Sgshapiro if (StopRequest) 195690792Sgshapiro stop_sendmail(); 195790792Sgshapiro (void) print_single_queue(qgrp, j); 195890792Sgshapiro } 195990792Sgshapiro finis(false, true, EX_OK); 196090792Sgshapiro /* NOTREACHED */ 196190792Sgshapiro } 196238032Speter printqueue(); 196390792Sgshapiro finis(false, true, EX_OK); 196490792Sgshapiro /* NOTREACHED */ 196542575Speter break; 196638032Speter 196790792Sgshapiro case MD_PRINTNQE: 196890792Sgshapiro /* print number of entries in queue */ 196990792Sgshapiro dropenvelope(&BlankEnvelope, true, false); 197090792Sgshapiro (void) sm_signal(SIGPIPE, sigpipe); 197190792Sgshapiro printnqe(smioout, NULL); 197290792Sgshapiro finis(false, true, EX_OK); 197390792Sgshapiro /* NOTREACHED */ 197490792Sgshapiro break; 197590792Sgshapiro 197690792Sgshapiro#if _FFR_QUARANTINE 197790792Sgshapiro case MD_QUEUERUN: 197890792Sgshapiro /* only handle quarantining here */ 197990792Sgshapiro if (quarantining == NULL) 198090792Sgshapiro break; 198190792Sgshapiro 198290792Sgshapiro if (QueueMode != QM_QUARANTINE && 198390792Sgshapiro QueueMode != QM_NORMAL) 198490792Sgshapiro { 198590792Sgshapiro HoldErrs = false; 198690792Sgshapiro usrerr("Can not use -Q with -q%c", QueueMode); 198790792Sgshapiro ExitStat = EX_USAGE; 198890792Sgshapiro finis(false, true, ExitStat); 198990792Sgshapiro /* NOTREACHED */ 199090792Sgshapiro } 199190792Sgshapiro quarantine_queue(quarantining, qgrp); 199290792Sgshapiro finis(false, true, EX_OK); 199390792Sgshapiro break; 199490792Sgshapiro#endif /* _FFR_QUARANTINE */ 199590792Sgshapiro 199638032Speter case MD_HOSTSTAT: 199790792Sgshapiro (void) sm_signal(SIGPIPE, sigpipe); 199864562Sgshapiro (void) mci_traverse_persistent(mci_print_persistent, NULL); 199990792Sgshapiro finis(false, true, EX_OK); 200090792Sgshapiro /* NOTREACHED */ 200164562Sgshapiro break; 200238032Speter 200338032Speter case MD_PURGESTAT: 200464562Sgshapiro (void) mci_traverse_persistent(mci_purge_persistent, NULL); 200590792Sgshapiro finis(false, true, EX_OK); 200690792Sgshapiro /* NOTREACHED */ 200764562Sgshapiro break; 200838032Speter 200938032Speter case MD_INITALIAS: 201042575Speter /* initialize maps */ 201164562Sgshapiro initmaps(); 201290792Sgshapiro finis(false, true, ExitStat); 201390792Sgshapiro /* NOTREACHED */ 201442575Speter break; 201538032Speter 201638032Speter case MD_SMTP: 201738032Speter case MD_DAEMON: 201838032Speter /* reset DSN parameters */ 201938032Speter DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 202090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 202190792Sgshapiro macid("{dsn_notify}"), NULL); 202290792Sgshapiro BlankEnvelope.e_envid = NULL; 202390792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 202490792Sgshapiro macid("{dsn_envid}"), NULL); 202590792Sgshapiro BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); 202690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 202790792Sgshapiro macid("{dsn_ret}"), NULL); 202838032Speter 202942575Speter /* don't open maps for daemon -- done below in child */ 203038032Speter break; 203138032Speter } 203238032Speter 203338032Speter if (tTd(0, 15)) 203438032Speter { 203538032Speter /* print configuration table (or at least part of it) */ 203638032Speter if (tTd(0, 90)) 203738032Speter printrules(); 203838032Speter for (i = 0; i < MAXMAILERS; i++) 203938032Speter { 204038032Speter if (Mailer[i] != NULL) 204138032Speter printmailer(Mailer[i]); 204238032Speter } 204338032Speter } 204438032Speter 204538032Speter /* 204638032Speter ** Switch to the main envelope. 204738032Speter */ 204838032Speter 204990792Sgshapiro CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope, 205090792Sgshapiro sm_rpool_new_x(NULL)); 205138032Speter MainEnvelope.e_flags = BlankEnvelope.e_flags; 205238032Speter 205338032Speter /* 205438032Speter ** If test mode, read addresses from stdin and process. 205538032Speter */ 205638032Speter 205738032Speter if (OpMode == MD_TEST) 205838032Speter { 205990792Sgshapiro if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL))) 206038032Speter Verbose = 2; 206138032Speter 206238032Speter if (Verbose) 206338032Speter { 206490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 206590792Sgshapiro "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); 206690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 206790792Sgshapiro "Enter <ruleset> <address>\n"); 206838032Speter } 206990792Sgshapiro macdefine(&(MainEnvelope.e_macro), A_PERM, 207090792Sgshapiro macid("{addr_type}"), "e r"); 207138032Speter for (;;) 207238032Speter { 207390792Sgshapiro SM_TRY 207490792Sgshapiro { 207590792Sgshapiro (void) sm_signal(SIGINT, intindebug); 207690792Sgshapiro (void) sm_releasesignal(SIGINT); 207790792Sgshapiro if (Verbose == 2) 207890792Sgshapiro (void) sm_io_fprintf(smioout, 207990792Sgshapiro SM_TIME_DEFAULT, 208090792Sgshapiro "> "); 208190792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 208290792Sgshapiro if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, 208390792Sgshapiro sizeof buf) == NULL) 208490792Sgshapiro testmodeline("/quit", &MainEnvelope); 208590792Sgshapiro p = strchr(buf, '\n'); 208690792Sgshapiro if (p != NULL) 208790792Sgshapiro *p = '\0'; 208890792Sgshapiro if (Verbose < 2) 208990792Sgshapiro (void) sm_io_fprintf(smioout, 209090792Sgshapiro SM_TIME_DEFAULT, 209190792Sgshapiro "> %s\n", buf); 209290792Sgshapiro testmodeline(buf, &MainEnvelope); 209390792Sgshapiro } 209490792Sgshapiro SM_EXCEPT(exc, "[!F]*") 209590792Sgshapiro { 209690792Sgshapiro /* 209790792Sgshapiro ** 8.10 just prints \n on interrupt. 209890792Sgshapiro ** I'm printing the exception here in case 209990792Sgshapiro ** sendmail is extended to raise additional 210090792Sgshapiro ** exceptions in this context. 210190792Sgshapiro */ 210290792Sgshapiro 210390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 210490792Sgshapiro "\n"); 210590792Sgshapiro sm_exc_print(exc, smioout); 210690792Sgshapiro } 210790792Sgshapiro SM_END_TRY 210838032Speter } 210938032Speter } 211038032Speter 211190792Sgshapiro#if STARTTLS 211290792Sgshapiro tls_ok = true; 211390792Sgshapiro if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER) 211490792Sgshapiro { 211590792Sgshapiro /* check whether STARTTLS is turned off for the client */ 211690792Sgshapiro if (chkclientmodifiers(D_NOTLS)) 211790792Sgshapiro tls_ok = false; 211890792Sgshapiro } 211990792Sgshapiro else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON || 212090792Sgshapiro OpMode == MD_SMTP) 212190792Sgshapiro { 212290792Sgshapiro /* check whether STARTTLS is turned off for the server */ 212390792Sgshapiro if (chkdaemonmodifiers(D_NOTLS)) 212490792Sgshapiro tls_ok = false; 212590792Sgshapiro } 212690792Sgshapiro else /* other modes don't need STARTTLS */ 212790792Sgshapiro tls_ok = false; 212864562Sgshapiro 212990792Sgshapiro if (tls_ok) 213090792Sgshapiro { 213190792Sgshapiro /* basic TLS initialization */ 213290792Sgshapiro tls_ok = init_tls_library(); 213390792Sgshapiro } 213490792Sgshapiro 213590792Sgshapiro if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)) 213690792Sgshapiro { 213790792Sgshapiro /* disable TLS for client */ 213890792Sgshapiro setclttls(false); 213990792Sgshapiro } 214090792Sgshapiro#endif /* STARTTLS */ 214190792Sgshapiro 214264562Sgshapiro /* 214338032Speter ** If collecting stuff from the queue, go start doing that. 214438032Speter */ 214538032Speter 214664562Sgshapiro if (OpMode == MD_QUEUERUN && QueueIntvl == 0) 214738032Speter { 214890792Sgshapiro pid_t pid = -1; 214990792Sgshapiro 215090792Sgshapiro#if STARTTLS 215190792Sgshapiro /* init TLS for client, ignore result for now */ 215290792Sgshapiro (void) initclttls(tls_ok); 215390792Sgshapiro#endif /* STARTTLS */ 215490792Sgshapiro 215590792Sgshapiro /* 215690792Sgshapiro ** The parent process of the caller of runqueue() needs 215790792Sgshapiro ** to stay around for a possible SIGTERM. The SIGTERM will 215890792Sgshapiro ** tell this process that all of the queue runners children 215990792Sgshapiro ** need to be sent SIGTERM as well. At the same time, we 216090792Sgshapiro ** want to return control to the command line. So we do an 216190792Sgshapiro ** extra fork(). 216290792Sgshapiro */ 216390792Sgshapiro 216490792Sgshapiro if (Verbose || foregroundqueue || (pid = fork()) <= 0) 216564562Sgshapiro { 216690792Sgshapiro /* 216790792Sgshapiro ** If the fork() failed we should still try to do 216890792Sgshapiro ** the queue run. If it succeeded then the child 216990792Sgshapiro ** is going to start the run and wait for all 217090792Sgshapiro ** of the children to finish. 217190792Sgshapiro */ 217290792Sgshapiro 217390792Sgshapiro if (pid == 0) 217490792Sgshapiro { 217590792Sgshapiro /* Reset global flags */ 217690792Sgshapiro RestartRequest = NULL; 217790792Sgshapiro ShutdownRequest = NULL; 217890792Sgshapiro PendingSignal = 0; 217990792Sgshapiro 218090792Sgshapiro /* disconnect from terminal */ 218190792Sgshapiro disconnect(2, CurEnv); 218290792Sgshapiro } 218390792Sgshapiro 218490792Sgshapiro CurrentPid = getpid(); 218590792Sgshapiro if (qgrp != NOQGRP) 218690792Sgshapiro { 218790792Sgshapiro /* 218890792Sgshapiro ** To run a specific queue group mark it to 218990792Sgshapiro ** be run, select the work group it's in and 219090792Sgshapiro ** increment the work counter. 219190792Sgshapiro */ 219290792Sgshapiro 219394334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; 219494334Sgshapiro i++) 219594334Sgshapiro Queue[i]->qg_nextrun = (time_t) -1; 219694334Sgshapiro Queue[qgrp]->qg_nextrun = 0; 219790792Sgshapiro (void) run_work_group(Queue[qgrp]->qg_wgrp, 219890792Sgshapiro false, Verbose, 219990792Sgshapiro queuepersistent, false); 220090792Sgshapiro } 220190792Sgshapiro else 220290792Sgshapiro (void) runqueue(false, Verbose, 220390792Sgshapiro queuepersistent, true); 220490792Sgshapiro 220590792Sgshapiro /* set the title to make it easier to find */ 220690792Sgshapiro sm_setproctitle(true, CurEnv, "Queue control"); 220790792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 220890792Sgshapiro while (CurChildren > 0) 220990792Sgshapiro { 221090792Sgshapiro int status; 221190792Sgshapiro pid_t ret; 221290792Sgshapiro 221390792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 221490792Sgshapiro continue; 221590792Sgshapiro 221690792Sgshapiro /* Only drop when a child gives status */ 221790792Sgshapiro if (WIFSTOPPED(status)) 221890792Sgshapiro continue; 221990792Sgshapiro 222090792Sgshapiro proc_list_drop(ret, status, NULL); 222190792Sgshapiro } 222264562Sgshapiro } 222390792Sgshapiro finis(true, true, ExitStat); 222490792Sgshapiro /* NOTREACHED */ 222538032Speter } 222638032Speter 222771345Sgshapiro# if SASL 222871345Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 222971345Sgshapiro { 223090792Sgshapiro /* check whether AUTH is turned off for the server */ 223190792Sgshapiro if (!chkdaemonmodifiers(D_NOAUTH) && 223290792Sgshapiro (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK) 223371345Sgshapiro syserr("!sasl_server_init failed! [%s]", 223490792Sgshapiro sasl_errstring(i, NULL, NULL)); 223571345Sgshapiro } 223671345Sgshapiro# endif /* SASL */ 223771345Sgshapiro 223890792Sgshapiro if (OpMode == MD_SMTP) 223990792Sgshapiro { 224090792Sgshapiro proc_list_add(CurrentPid, "Sendmail SMTP Agent", 224190792Sgshapiro PROC_DAEMON, 0, -1); 224290792Sgshapiro 224390792Sgshapiro /* clean up background delivery children */ 224490792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 224590792Sgshapiro } 224690792Sgshapiro 224738032Speter /* 224838032Speter ** If a daemon, wait for a request. 224938032Speter ** getrequests will always return in a child. 225038032Speter ** If we should also be processing the queue, start 225138032Speter ** doing it in background. 225238032Speter ** We check for any errors that might have happened 225338032Speter ** during startup. 225438032Speter */ 225538032Speter 225698841Sgshapiro if (OpMode == MD_DAEMON || QueueIntvl > 0) 225738032Speter { 225838032Speter char dtype[200]; 225938032Speter 226038032Speter if (!run_in_foreground && !tTd(99, 100)) 226138032Speter { 226238032Speter /* put us in background */ 226338032Speter i = fork(); 226438032Speter if (i < 0) 226538032Speter syserr("daemon: cannot fork"); 226638032Speter if (i != 0) 226790792Sgshapiro { 226890792Sgshapiro finis(false, true, EX_OK); 226990792Sgshapiro /* NOTREACHED */ 227090792Sgshapiro } 227138032Speter 227290792Sgshapiro /* 227390792Sgshapiro ** Initialize exception stack and default exception 227490792Sgshapiro ** handler for child process. 227590792Sgshapiro */ 227690792Sgshapiro 227790792Sgshapiro /* Reset global flags */ 227890792Sgshapiro RestartRequest = NULL; 227990792Sgshapiro RestartWorkGroup = false; 228090792Sgshapiro ShutdownRequest = NULL; 228190792Sgshapiro PendingSignal = 0; 228290792Sgshapiro CurrentPid = getpid(); 228390792Sgshapiro 228490792Sgshapiro sm_exc_newthread(fatal_error); 228590792Sgshapiro 228638032Speter /* disconnect from our controlling tty */ 228790792Sgshapiro disconnect(2, &MainEnvelope); 228838032Speter } 228938032Speter 229038032Speter dtype[0] = '\0'; 229138032Speter if (OpMode == MD_DAEMON) 229290792Sgshapiro { 229390792Sgshapiro (void) sm_strlcat(dtype, "+SMTP", sizeof dtype); 229490792Sgshapiro DaemonPid = CurrentPid; 229590792Sgshapiro } 229698841Sgshapiro if (QueueIntvl > 0) 229738032Speter { 229890792Sgshapiro (void) sm_strlcat2(dtype, 229990792Sgshapiro queuepersistent 230090792Sgshapiro ? "+persistent-queueing@" 230190792Sgshapiro : "+queueing@", 230290792Sgshapiro pintvl(QueueIntvl, true), 230390792Sgshapiro sizeof dtype); 230438032Speter } 230538032Speter if (tTd(0, 1)) 230690792Sgshapiro (void) sm_strlcat(dtype, "+debugging", sizeof dtype); 230738032Speter 230838032Speter sm_syslog(LOG_INFO, NOQID, 230964562Sgshapiro "starting daemon (%s): %s", Version, dtype + 1); 231090792Sgshapiro#if XLA 231138032Speter xla_create_file(); 231264562Sgshapiro#endif /* XLA */ 231338032Speter 231464562Sgshapiro /* save daemon type in a macro for possible PidFile use */ 231590792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 231690792Sgshapiro macid("{daemon_info}"), dtype + 1); 231764562Sgshapiro 231864562Sgshapiro /* save queue interval in a macro for possible PidFile use */ 231990792Sgshapiro macdefine(&MainEnvelope.e_macro, A_TEMP, 232090792Sgshapiro macid("{queue_interval}"), pintvl(QueueIntvl, true)); 232164562Sgshapiro 232290792Sgshapiro /* workaround: can't seem to release the signal in the parent */ 232390792Sgshapiro (void) sm_signal(SIGHUP, sighup); 232490792Sgshapiro (void) sm_releasesignal(SIGHUP); 232590792Sgshapiro (void) sm_signal(SIGTERM, sigterm); 232690792Sgshapiro 232798841Sgshapiro if (QueueIntvl > 0) 232838032Speter { 232990792Sgshapiro (void) runqueue(true, false, queuepersistent, true); 233090792Sgshapiro 233190792Sgshapiro /* 233290792Sgshapiro ** If queuepersistent but not in daemon mode then 233390792Sgshapiro ** we're going to do the queue runner monitoring here. 233490792Sgshapiro ** If in daemon mode then the monitoring will happen 233590792Sgshapiro ** elsewhere. 233690792Sgshapiro */ 233790792Sgshapiro 233890792Sgshapiro if (OpMode != MD_DAEMON && queuepersistent) 233990792Sgshapiro { 234090792Sgshapiro /* set the title to make it easier to find */ 234190792Sgshapiro sm_setproctitle(true, CurEnv, "Queue control"); 234290792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 234390792Sgshapiro while (CurChildren > 0) 234490792Sgshapiro { 234590792Sgshapiro int status; 234690792Sgshapiro pid_t ret; 234790792Sgshapiro int group; 234890792Sgshapiro 234990792Sgshapiro if (ShutdownRequest != NULL) 235090792Sgshapiro shutdown_daemon(); 235190792Sgshapiro else if (RestartRequest != NULL) 235290792Sgshapiro restart_daemon(); 235390792Sgshapiro else if (RestartWorkGroup) 235490792Sgshapiro restart_marked_work_groups(); 235590792Sgshapiro 235690792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 235790792Sgshapiro continue; 235890792Sgshapiro 235990792Sgshapiro if (WIFSTOPPED(status)) 236090792Sgshapiro continue; 236190792Sgshapiro 236290792Sgshapiro /* Probe only on a child status */ 236390792Sgshapiro proc_list_drop(ret, status, &group); 236490792Sgshapiro 236590792Sgshapiro if (WIFSIGNALED(status)) 236690792Sgshapiro { 236790792Sgshapiro if (WCOREDUMP(status)) 236890792Sgshapiro { 236990792Sgshapiro sm_syslog(LOG_ERR, NOQID, 237090792Sgshapiro "persistent queue runner=%d core dumped, signal=%d", 237190792Sgshapiro group, WTERMSIG(status)); 237290792Sgshapiro 237390792Sgshapiro /* don't restart this one */ 237490792Sgshapiro mark_work_group_restart(group, -1); 237590792Sgshapiro continue; 237690792Sgshapiro } 237790792Sgshapiro 237890792Sgshapiro sm_syslog(LOG_ERR, NOQID, 237990792Sgshapiro "persistent queue runner=%d died, signal=%d", 238090792Sgshapiro group, WTERMSIG(status)); 238190792Sgshapiro } 238290792Sgshapiro 238390792Sgshapiro /* 238490792Sgshapiro ** When debugging active, don't 238590792Sgshapiro ** restart the persistent queues. 238690792Sgshapiro ** But do log this as info. 238790792Sgshapiro */ 238890792Sgshapiro 238990792Sgshapiro if (sm_debug_active(&DebugNoPRestart, 239090792Sgshapiro 1)) 239190792Sgshapiro { 239290792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 239390792Sgshapiro "persistent queue runner=%d, exited", 239490792Sgshapiro group); 239590792Sgshapiro mark_work_group_restart(group, -1); 239690792Sgshapiro } 239790792Sgshapiro } 239890792Sgshapiro finis(true, true, ExitStat); 239990792Sgshapiro /* NOTREACHED */ 240090792Sgshapiro } 240190792Sgshapiro 240238032Speter if (OpMode != MD_DAEMON) 240338032Speter { 240490792Sgshapiro char qtype[200]; 240590792Sgshapiro 240690792Sgshapiro /* 240790792Sgshapiro ** Write the pid to file 240890792Sgshapiro ** XXX Overwrites sendmail.pid 240990792Sgshapiro */ 241090792Sgshapiro 241190792Sgshapiro log_sendmail_pid(&MainEnvelope); 241290792Sgshapiro 241390792Sgshapiro /* set the title to make it easier to find */ 241490792Sgshapiro qtype[0] = '\0'; 241590792Sgshapiro (void) sm_strlcpyn(qtype, sizeof qtype, 4, 241690792Sgshapiro "Queue runner@", 241790792Sgshapiro pintvl(QueueIntvl, true), 241890792Sgshapiro " for ", 241990792Sgshapiro QueueDir); 242090792Sgshapiro sm_setproctitle(true, CurEnv, qtype); 242138032Speter for (;;) 242238032Speter { 242364562Sgshapiro (void) pause(); 242477349Sgshapiro if (ShutdownRequest != NULL) 242577349Sgshapiro shutdown_daemon(); 242690792Sgshapiro else if (RestartRequest != NULL) 242790792Sgshapiro restart_daemon(); 242890792Sgshapiro else if (RestartWorkGroup) 242990792Sgshapiro restart_marked_work_groups(); 243090792Sgshapiro 243190792Sgshapiro if (doqueuerun()) 243290792Sgshapiro (void) runqueue(true, false, 243390792Sgshapiro false, false); 243438032Speter } 243538032Speter } 243638032Speter } 243790792Sgshapiro dropenvelope(&MainEnvelope, true, false); 243838032Speter 243990792Sgshapiro#if STARTTLS 244064562Sgshapiro /* init TLS for server, ignore result for now */ 244190792Sgshapiro (void) initsrvtls(tls_ok); 244290792Sgshapiro#endif /* STARTTLS */ 244390792Sgshapiro#if PROFILING 244490792Sgshapiro nextreq: 244590792Sgshapiro#endif /* PROFILING */ 244690792Sgshapiro p_flags = getrequests(&MainEnvelope); 244738032Speter 244838032Speter /* drop privileges */ 244990792Sgshapiro (void) drop_privileges(false); 245038032Speter 245138032Speter /* 245238032Speter ** Get authentication data 245390792Sgshapiro ** Set _ macro in BlankEnvelope before calling newenvelope(). 245438032Speter */ 245538032Speter 245690792Sgshapiro authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 245790792Sgshapiro NULL), &forged); 245890792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo); 245990792Sgshapiro 246090792Sgshapiro /* at this point we are in a child: reset state */ 246190792Sgshapiro sm_rpool_free(MainEnvelope.e_rpool); 246290792Sgshapiro (void) newenvelope(&MainEnvelope, &MainEnvelope, 246390792Sgshapiro sm_rpool_new_x(NULL)); 246438032Speter } 246538032Speter 246664562Sgshapiro if (LogLevel > 9) 246764562Sgshapiro { 246864562Sgshapiro /* log connection information */ 246964562Sgshapiro sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo); 247064562Sgshapiro } 247164562Sgshapiro 247238032Speter /* 247338032Speter ** If running SMTP protocol, start collecting and executing 247438032Speter ** commands. This will never return. 247538032Speter */ 247638032Speter 247738032Speter if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 247838032Speter { 247938032Speter char pbuf[20]; 248038032Speter 248138032Speter /* 248238032Speter ** Save some macros for check_* rulesets. 248338032Speter */ 248438032Speter 248538032Speter if (forged) 248638032Speter { 248738032Speter char ipbuf[103]; 248838032Speter 248990792Sgshapiro (void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]", 249090792Sgshapiro anynet_ntoa(&RealHostAddr)); 249190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 249290792Sgshapiro macid("{client_name}"), ipbuf); 249338032Speter } 249438032Speter else 249590792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 249690792Sgshapiro macid("{client_name}"), RealHostName); 249790792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 249890792Sgshapiro macid("{client_addr}"), anynet_ntoa(&RealHostAddr)); 249990792Sgshapiro sm_getla(); 250038032Speter 250190792Sgshapiro switch (RealHostAddr.sa.sa_family) 250264562Sgshapiro { 250390792Sgshapiro#if NETINET 250464562Sgshapiro case AF_INET: 250590792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, "%d", 250690792Sgshapiro RealHostAddr.sin.sin_port); 250764562Sgshapiro break; 250890792Sgshapiro#endif /* NETINET */ 250990792Sgshapiro#if NETINET6 251064562Sgshapiro case AF_INET6: 251190792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, "%d", 251290792Sgshapiro RealHostAddr.sin6.sin6_port); 251364562Sgshapiro break; 251490792Sgshapiro#endif /* NETINET6 */ 251564562Sgshapiro default: 251690792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, "0"); 251764562Sgshapiro break; 251864562Sgshapiro } 251990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 252090792Sgshapiro macid("{client_port}"), pbuf); 252142575Speter 252238032Speter if (OpMode == MD_DAEMON) 252338032Speter { 252438032Speter /* validate the connection */ 252590792Sgshapiro HoldErrs = true; 252638032Speter nullserver = validate_connection(&RealHostAddr, 252790792Sgshapiro RealHostName, 252890792Sgshapiro &MainEnvelope); 252990792Sgshapiro HoldErrs = false; 253038032Speter } 253164562Sgshapiro else if (p_flags == NULL) 253264562Sgshapiro { 253364562Sgshapiro p_flags = (BITMAP256 *) xalloc(sizeof *p_flags); 253464562Sgshapiro clrbitmap(p_flags); 253564562Sgshapiro } 253690792Sgshapiro#if STARTTLS 253764562Sgshapiro if (OpMode == MD_SMTP) 253890792Sgshapiro (void) initsrvtls(tls_ok); 253990792Sgshapiro#endif /* STARTTLS */ 254071345Sgshapiro 254190792Sgshapiro /* turn off profiling */ 254290792Sgshapiro SM_PROF(1); 254390792Sgshapiro smtp(nullserver, *p_flags, &MainEnvelope); 254490792Sgshapiro#if PROFILING 254590792Sgshapiro /* turn off profiling */ 254690792Sgshapiro SM_PROF(0); 254790792Sgshapiro if (OpMode == MD_DAEMON) 254890792Sgshapiro goto nextreq; 254990792Sgshapiro#endif /* PROFILING */ 255038032Speter } 255138032Speter 255290792Sgshapiro sm_rpool_free(MainEnvelope.e_rpool); 255390792Sgshapiro clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL)); 255438032Speter if (OpMode == MD_VERIFY) 255538032Speter { 255690792Sgshapiro set_delivery_mode(SM_VERIFY, &MainEnvelope); 255738032Speter PostMasterCopy = NULL; 255838032Speter } 255938032Speter else 256038032Speter { 256138032Speter /* interactive -- all errors are global */ 256290792Sgshapiro MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER; 256338032Speter } 256438032Speter 256538032Speter /* 256638032Speter ** Do basic system initialization and set the sender 256738032Speter */ 256838032Speter 256990792Sgshapiro initsys(&MainEnvelope); 257090792Sgshapiro macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0"); 257190792Sgshapiro macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0"); 257290792Sgshapiro setsender(from, &MainEnvelope, NULL, '\0', false); 257364562Sgshapiro if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') && 257490792Sgshapiro (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) || 257590792Sgshapiro strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0)) 257664562Sgshapiro { 257790792Sgshapiro auth_warning(&MainEnvelope, "%s set sender to %s using -%c", 257890792Sgshapiro RealUserName, from, warn_f_flag); 257964562Sgshapiro#if SASL 258090792Sgshapiro auth = false; 258164562Sgshapiro#endif /* SASL */ 258264562Sgshapiro } 258364562Sgshapiro if (auth) 258464562Sgshapiro { 258564562Sgshapiro char *fv; 258664562Sgshapiro 258764562Sgshapiro /* set the initial sender for AUTH= to $f@$j */ 258890792Sgshapiro fv = macvalue('f', &MainEnvelope); 258964562Sgshapiro if (fv == NULL || *fv == '\0') 259090792Sgshapiro MainEnvelope.e_auth_param = NULL; 259164562Sgshapiro else 259264562Sgshapiro { 259364562Sgshapiro if (strchr(fv, '@') == NULL) 259464562Sgshapiro { 259590792Sgshapiro i = strlen(fv) + strlen(macvalue('j', 259690792Sgshapiro &MainEnvelope)) + 2; 259790792Sgshapiro p = sm_malloc_x(i); 259890792Sgshapiro (void) sm_strlcpyn(p, i, 3, fv, "@", 259990792Sgshapiro macvalue('j', 260090792Sgshapiro &MainEnvelope)); 260164562Sgshapiro } 260264562Sgshapiro else 260390792Sgshapiro p = sm_strdup_x(fv); 260490792Sgshapiro MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool, 260590792Sgshapiro xtextify(p, "=")); 260690792Sgshapiro sm_free(p); /* XXX */ 260764562Sgshapiro } 260864562Sgshapiro } 260990792Sgshapiro if (macvalue('s', &MainEnvelope) == NULL) 261090792Sgshapiro macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName); 261138032Speter 261290792Sgshapiro av = argv + optind; 261338032Speter if (*av == NULL && !GrabTo) 261438032Speter { 261590792Sgshapiro MainEnvelope.e_to = NULL; 261690792Sgshapiro MainEnvelope.e_flags |= EF_GLOBALERRS; 261790792Sgshapiro HoldErrs = false; 261890792Sgshapiro SuperSafe = SAFE_NO; 261938032Speter usrerr("Recipient names must be specified"); 262038032Speter 262138032Speter /* collect body for UUCP return */ 262238032Speter if (OpMode != MD_VERIFY) 262390792Sgshapiro collect(InChannel, false, NULL, &MainEnvelope); 262490792Sgshapiro finis(true, true, EX_USAGE); 262590792Sgshapiro /* NOTREACHED */ 262638032Speter } 262738032Speter 262838032Speter /* 262938032Speter ** Scan argv and deliver the message to everyone. 263038032Speter */ 263138032Speter 263290792Sgshapiro save_val = LogUsrErrs; 263390792Sgshapiro LogUsrErrs = true; 263490792Sgshapiro sendtoargv(av, &MainEnvelope); 263590792Sgshapiro LogUsrErrs = save_val; 263638032Speter 263738032Speter /* if we have had errors sofar, arrange a meaningful exit stat */ 263838032Speter if (Errors > 0 && ExitStat == EX_OK) 263938032Speter ExitStat = EX_USAGE; 264038032Speter 264138032Speter#if _FFR_FIX_DASHT 264238032Speter /* 264338032Speter ** If using -t, force not sending to argv recipients, even 264438032Speter ** if they are mentioned in the headers. 264538032Speter */ 264638032Speter 264738032Speter if (GrabTo) 264838032Speter { 264938032Speter ADDRESS *q; 265064562Sgshapiro 265190792Sgshapiro for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next) 265264562Sgshapiro q->q_state = QS_REMOVED; 265338032Speter } 265464562Sgshapiro#endif /* _FFR_FIX_DASHT */ 265538032Speter 265638032Speter /* 265738032Speter ** Read the input mail. 265838032Speter */ 265938032Speter 266090792Sgshapiro MainEnvelope.e_to = NULL; 266138032Speter if (OpMode != MD_VERIFY || GrabTo) 266238032Speter { 266390792Sgshapiro int savederrors; 266490792Sgshapiro unsigned long savedflags; 266538032Speter 266690792Sgshapiro /* 266790792Sgshapiro ** workaround for compiler warning on Irix: 266890792Sgshapiro ** do not initialize variable in the definition, but 266990792Sgshapiro ** later on: 267090792Sgshapiro ** warning(1548): transfer of control bypasses 267190792Sgshapiro ** initialization of: 267290792Sgshapiro ** variable "savederrors" (declared at line 2570) 267390792Sgshapiro ** variable "savedflags" (declared at line 2571) 267490792Sgshapiro ** goto giveup; 267590792Sgshapiro */ 267690792Sgshapiro 267790792Sgshapiro savederrors = Errors; 267890792Sgshapiro savedflags = MainEnvelope.e_flags & EF_FATALERRS; 267990792Sgshapiro MainEnvelope.e_flags |= EF_GLOBALERRS; 268090792Sgshapiro MainEnvelope.e_flags &= ~EF_FATALERRS; 268164562Sgshapiro Errors = 0; 268264562Sgshapiro buffer_errors(); 268390792Sgshapiro collect(InChannel, false, NULL, &MainEnvelope); 268438032Speter 268564562Sgshapiro /* header checks failed */ 268664562Sgshapiro if (Errors > 0) 268764562Sgshapiro { 268890792Sgshapiro giveup: 268990792Sgshapiro if (!GrabTo) 269064562Sgshapiro { 269190792Sgshapiro /* Log who the mail would have gone to */ 269290792Sgshapiro logundelrcpts(&MainEnvelope, 269390792Sgshapiro MainEnvelope.e_message, 269490792Sgshapiro 8, false); 269564562Sgshapiro } 269690792Sgshapiro flush_errors(true); 269790792Sgshapiro finis(true, true, ExitStat); 269864562Sgshapiro /* NOTREACHED */ 269964562Sgshapiro return -1; 270064562Sgshapiro } 270164562Sgshapiro 270238032Speter /* bail out if message too large */ 270390792Sgshapiro if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags)) 270438032Speter { 270590792Sgshapiro finis(true, true, ExitStat != EX_OK ? ExitStat 270690792Sgshapiro : EX_DATAERR); 270764562Sgshapiro /* NOTREACHED */ 270838032Speter return -1; 270938032Speter } 271098121Sgshapiro 271198121Sgshapiro /* set message size */ 271298121Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", 271398121Sgshapiro MainEnvelope.e_msgsize); 271498121Sgshapiro macdefine(&MainEnvelope.e_macro, A_TEMP, 271598121Sgshapiro macid("{msg_size}"), buf); 271698121Sgshapiro 271764562Sgshapiro Errors = savederrors; 271890792Sgshapiro MainEnvelope.e_flags |= savedflags; 271938032Speter } 272038032Speter errno = 0; 272138032Speter 272238032Speter if (tTd(1, 1)) 272390792Sgshapiro sm_dprintf("From person = \"%s\"\n", 272490792Sgshapiro MainEnvelope.e_from.q_paddr); 272538032Speter 272690792Sgshapiro#if _FFR_QUARANTINE 272790792Sgshapiro /* Check if quarantining stats should be updated */ 272890792Sgshapiro if (MainEnvelope.e_quarmsg != NULL) 272990792Sgshapiro markstats(&MainEnvelope, NULL, STATS_QUARANTINE); 273090792Sgshapiro#endif /* _FFR_QUARANTINE */ 273190792Sgshapiro 273238032Speter /* 273338032Speter ** Actually send everything. 273438032Speter ** If verifying, just ack. 273538032Speter */ 273638032Speter 273790792Sgshapiro if (Errors == 0) 273838032Speter { 273990792Sgshapiro if (!split_by_recipient(&MainEnvelope) && 274090792Sgshapiro bitset(EF_FATALERRS, MainEnvelope.e_flags)) 274190792Sgshapiro goto giveup; 274238032Speter } 274390792Sgshapiro 274490792Sgshapiro /* make sure we deliver at least the first envelope */ 274590792Sgshapiro i = FastSplit > 0 ? 0 : -1; 274690792Sgshapiro for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++) 274790792Sgshapiro { 274890792Sgshapiro ENVELOPE *next; 274990792Sgshapiro 275090792Sgshapiro e->e_from.q_state = QS_SENDER; 275190792Sgshapiro if (tTd(1, 5)) 275290792Sgshapiro { 275390792Sgshapiro sm_dprintf("main[%d]: QS_SENDER ", i); 275490792Sgshapiro printaddr(&e->e_from, false); 275590792Sgshapiro } 275690792Sgshapiro e->e_to = NULL; 275790792Sgshapiro sm_getla(); 275890792Sgshapiro GrabTo = false; 275964562Sgshapiro#if NAMED_BIND 276090792Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 276190792Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 276264562Sgshapiro#endif /* NAMED_BIND */ 276390792Sgshapiro next = e->e_sibling; 276490792Sgshapiro e->e_sibling = NULL; 276538032Speter 276690792Sgshapiro /* after FastSplit envelopes: queue up */ 276790792Sgshapiro sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT); 276890792Sgshapiro e->e_sibling = next; 276990792Sgshapiro } 277090792Sgshapiro 277138032Speter /* 277238032Speter ** All done. 277338032Speter ** Don't send return error message if in VERIFY mode. 277438032Speter */ 277538032Speter 277690792Sgshapiro finis(true, true, ExitStat); 277764562Sgshapiro /* NOTREACHED */ 277864562Sgshapiro return ExitStat; 277938032Speter} 278090792Sgshapiro/* 278177349Sgshapiro** STOP_SENDMAIL -- Stop the running program 278277349Sgshapiro** 278377349Sgshapiro** Parameters: 278477349Sgshapiro** none. 278577349Sgshapiro** 278677349Sgshapiro** Returns: 278777349Sgshapiro** none. 278877349Sgshapiro** 278977349Sgshapiro** Side Effects: 279077349Sgshapiro** exits. 279177349Sgshapiro*/ 279238032Speter 279377349Sgshapirovoid 279477349Sgshapirostop_sendmail() 279577349Sgshapiro{ 279677349Sgshapiro /* reset uid for process accounting */ 279777349Sgshapiro endpwent(); 279877349Sgshapiro (void) setuid(RealUid); 279977349Sgshapiro exit(EX_OK); 280077349Sgshapiro} 280190792Sgshapiro/* 280238032Speter** FINIS -- Clean up and exit. 280338032Speter** 280438032Speter** Parameters: 280542575Speter** drop -- whether or not to drop CurEnv envelope 280690792Sgshapiro** cleanup -- call exit() or _exit()? 280742575Speter** exitstat -- exit status to use for exit() call 280838032Speter** 280938032Speter** Returns: 281038032Speter** never 281138032Speter** 281238032Speter** Side Effects: 281338032Speter** exits sendmail 281438032Speter*/ 281538032Speter 281638032Spetervoid 281790792Sgshapirofinis(drop, cleanup, exitstat) 281842575Speter bool drop; 281990792Sgshapiro bool cleanup; 282042575Speter volatile int exitstat; 282138032Speter{ 282298121Sgshapiro 282377349Sgshapiro /* Still want to process new timeouts added below */ 282490792Sgshapiro sm_clear_events(); 282590792Sgshapiro (void) sm_releasesignal(SIGALRM); 282642575Speter 282738032Speter if (tTd(2, 1)) 282838032Speter { 282990792Sgshapiro sm_dprintf("\n====finis: stat %d e_id=%s e_flags=", 283090792Sgshapiro exitstat, 283190792Sgshapiro CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); 283238032Speter printenvflags(CurEnv); 283338032Speter } 283438032Speter if (tTd(2, 9)) 283590792Sgshapiro printopenfds(false); 283638032Speter 283790792Sgshapiro SM_TRY 283890792Sgshapiro /* 283990792Sgshapiro ** Clean up. This might raise E:mta.quickabort 284090792Sgshapiro */ 284138032Speter 284290792Sgshapiro /* clean up temp files */ 284390792Sgshapiro CurEnv->e_to = NULL; 284490792Sgshapiro if (drop) 284590792Sgshapiro { 284690792Sgshapiro if (CurEnv->e_id != NULL) 284790792Sgshapiro { 284890792Sgshapiro dropenvelope(CurEnv, true, false); 284990792Sgshapiro sm_rpool_free(CurEnv->e_rpool); 285090792Sgshapiro CurEnv->e_rpool = NULL; 285190792Sgshapiro } 285290792Sgshapiro else 285390792Sgshapiro poststats(StatFile); 285490792Sgshapiro } 285538032Speter 285690792Sgshapiro /* flush any cached connections */ 285790792Sgshapiro mci_flush(true, NULL); 285838032Speter 285990792Sgshapiro /* close maps belonging to this pid */ 286090792Sgshapiro closemaps(false); 286142575Speter 286264562Sgshapiro#if USERDB 286390792Sgshapiro /* close UserDatabase */ 286490792Sgshapiro _udbx_close(); 286564562Sgshapiro#endif /* USERDB */ 286642575Speter 286790792Sgshapiro#if SASL 286890792Sgshapiro stop_sasl_client(); 286990792Sgshapiro#endif /* SASL */ 287090792Sgshapiro 287190792Sgshapiro#if XLA 287290792Sgshapiro /* clean up extended load average stuff */ 287390792Sgshapiro xla_all_end(); 287464562Sgshapiro#endif /* XLA */ 287538032Speter 287690792Sgshapiro SM_FINALLY 287790792Sgshapiro /* 287890792Sgshapiro ** And exit. 287990792Sgshapiro */ 288038032Speter 288190792Sgshapiro if (LogLevel > 78) 288290792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d", 288390792Sgshapiro (int) CurrentPid); 288490792Sgshapiro if (exitstat == EX_TEMPFAIL || 288590792Sgshapiro CurEnv->e_errormode == EM_BERKNET) 288690792Sgshapiro exitstat = EX_OK; 288764562Sgshapiro 288890792Sgshapiro /* XXX clean up queues and related data structures */ 288990792Sgshapiro cleanup_queues(); 289090792Sgshapiro#if SM_CONF_SHM 289190792Sgshapiro cleanup_shm(DaemonPid == getpid()); 289290792Sgshapiro#endif /* SM_CONF_SHM */ 289390792Sgshapiro 289490792Sgshapiro /* reset uid for process accounting */ 289590792Sgshapiro endpwent(); 289690792Sgshapiro sm_mbdb_terminate(); 289790792Sgshapiro (void) setuid(RealUid); 289890792Sgshapiro#if SM_HEAP_CHECK 289990792Sgshapiro /* dump the heap, if we are checking for memory leaks */ 290090792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2)) 290190792Sgshapiro sm_heap_report(smioout, 290290792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 290390792Sgshapiro#endif /* SM_HEAP_CHECK */ 290490792Sgshapiro if (sm_debug_active(&SmXtrapReport, 1)) 290590792Sgshapiro sm_dprintf("xtrap count = %d\n", SmXtrapCount); 290690792Sgshapiro if (cleanup) 290790792Sgshapiro exit(exitstat); 290890792Sgshapiro else 290990792Sgshapiro _exit(exitstat); 291090792Sgshapiro SM_END_TRY 291138032Speter} 291290792Sgshapiro/* 291390792Sgshapiro** INTINDEBUG -- signal handler for SIGINT in -bt mode 291477349Sgshapiro** 291577349Sgshapiro** Parameters: 291690792Sgshapiro** sig -- incoming signal. 291790792Sgshapiro** 291890792Sgshapiro** Returns: 291990792Sgshapiro** none. 292090792Sgshapiro** 292190792Sgshapiro** Side Effects: 292290792Sgshapiro** longjmps back to test mode loop. 292390792Sgshapiro** 292490792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 292590792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 292690792Sgshapiro** DOING. 292790792Sgshapiro*/ 292890792Sgshapiro 292990792Sgshapiro/* Type of an exception generated on SIGINT during address test mode. */ 293090792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt = 293190792Sgshapiro{ 293290792Sgshapiro SmExcTypeMagic, 293390792Sgshapiro "S:mta.interrupt", 293490792Sgshapiro "", 293590792Sgshapiro sm_etype_printf, 293690792Sgshapiro "interrupt", 293790792Sgshapiro}; 293890792Sgshapiro 293990792Sgshapiro/* ARGSUSED */ 294090792Sgshapirostatic SIGFUNC_DECL 294190792Sgshapirointindebug(sig) 294290792Sgshapiro int sig; 294390792Sgshapiro{ 294490792Sgshapiro int save_errno = errno; 294590792Sgshapiro 294690792Sgshapiro FIX_SYSV_SIGNAL(sig, intindebug); 294790792Sgshapiro errno = save_errno; 294890792Sgshapiro CHECK_CRITICAL(sig); 294990792Sgshapiro errno = save_errno; 295090792Sgshapiro sm_exc_raisenew_x(&EtypeInterrupt); 295190792Sgshapiro errno = save_errno; 295290792Sgshapiro return SIGFUNC_RETURN; 295390792Sgshapiro} 295490792Sgshapiro/* 295590792Sgshapiro** SIGTERM -- SIGTERM handler for the daemon 295690792Sgshapiro** 295790792Sgshapiro** Parameters: 295877349Sgshapiro** sig -- signal number. 295977349Sgshapiro** 296077349Sgshapiro** Returns: 296177349Sgshapiro** none. 296277349Sgshapiro** 296377349Sgshapiro** Side Effects: 296477349Sgshapiro** Sets ShutdownRequest which will hopefully trigger 296577349Sgshapiro** the daemon to exit. 296677349Sgshapiro** 296777349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 296877349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 296977349Sgshapiro** DOING. 297077349Sgshapiro*/ 297177349Sgshapiro 297277349Sgshapiro/* ARGSUSED */ 297377349Sgshapirostatic SIGFUNC_DECL 297490792Sgshapirosigterm(sig) 297577349Sgshapiro int sig; 297677349Sgshapiro{ 297777349Sgshapiro int save_errno = errno; 297877349Sgshapiro 297990792Sgshapiro FIX_SYSV_SIGNAL(sig, sigterm); 298077349Sgshapiro ShutdownRequest = "signal"; 298177349Sgshapiro errno = save_errno; 298277349Sgshapiro return SIGFUNC_RETURN; 298377349Sgshapiro} 298490792Sgshapiro/* 298590792Sgshapiro** SIGHUP -- handle a SIGHUP signal 298677349Sgshapiro** 298777349Sgshapiro** Parameters: 298890792Sgshapiro** sig -- incoming signal. 298977349Sgshapiro** 299077349Sgshapiro** Returns: 299177349Sgshapiro** none. 299277349Sgshapiro** 299377349Sgshapiro** Side Effects: 299490792Sgshapiro** Sets RestartRequest which should cause the daemon 299590792Sgshapiro** to restart. 299690792Sgshapiro** 299790792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 299890792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 299990792Sgshapiro** DOING. 300077349Sgshapiro*/ 300177349Sgshapiro 300290792Sgshapiro/* ARGSUSED */ 300390792Sgshapirostatic SIGFUNC_DECL 300490792Sgshapirosighup(sig) 300590792Sgshapiro int sig; 300677349Sgshapiro{ 300790792Sgshapiro int save_errno = errno; 300877349Sgshapiro 300990792Sgshapiro FIX_SYSV_SIGNAL(sig, sighup); 301090792Sgshapiro RestartRequest = "signal"; 301190792Sgshapiro errno = save_errno; 301290792Sgshapiro return SIGFUNC_RETURN; 301390792Sgshapiro} 301490792Sgshapiro/* 301590792Sgshapiro** SIGPIPE -- signal handler for SIGPIPE 301690792Sgshapiro** 301790792Sgshapiro** Parameters: 301890792Sgshapiro** sig -- incoming signal. 301990792Sgshapiro** 302090792Sgshapiro** Returns: 302190792Sgshapiro** none. 302290792Sgshapiro** 302390792Sgshapiro** Side Effects: 302490792Sgshapiro** Sets StopRequest which should cause the mailq/hoststatus 302590792Sgshapiro** display to stop. 302690792Sgshapiro** 302790792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 302890792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 302990792Sgshapiro** DOING. 303090792Sgshapiro*/ 303177349Sgshapiro 303290792Sgshapiro/* ARGSUSED */ 303390792Sgshapirostatic SIGFUNC_DECL 303490792Sgshapirosigpipe(sig) 303590792Sgshapiro int sig; 303690792Sgshapiro{ 303790792Sgshapiro int save_errno = errno; 303877349Sgshapiro 303990792Sgshapiro FIX_SYSV_SIGNAL(sig, sigpipe); 304090792Sgshapiro StopRequest = true; 304190792Sgshapiro errno = save_errno; 304290792Sgshapiro return SIGFUNC_RETURN; 304377349Sgshapiro} 304490792Sgshapiro/* 304538032Speter** INTSIG -- clean up on interrupt 304638032Speter** 304764562Sgshapiro** This just arranges to exit. It pessimizes in that it 304838032Speter** may resend a message. 304938032Speter** 305038032Speter** Parameters: 305138032Speter** none. 305238032Speter** 305338032Speter** Returns: 305438032Speter** none. 305538032Speter** 305638032Speter** Side Effects: 305738032Speter** Unlocks the current job. 305877349Sgshapiro** 305977349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 306077349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 306177349Sgshapiro** DOING. 306277349Sgshapiro** 306377349Sgshapiro** XXX: More work is needed for this signal handler. 306438032Speter*/ 306538032Speter 306638032Speter/* ARGSUSED */ 306738032SpeterSIGFUNC_DECL 306838032Speterintsig(sig) 306938032Speter int sig; 307038032Speter{ 307190792Sgshapiro bool drop = false; 307277349Sgshapiro int save_errno = errno; 307364562Sgshapiro 307477349Sgshapiro FIX_SYSV_SIGNAL(sig, intsig); 307577349Sgshapiro errno = save_errno; 307677349Sgshapiro CHECK_CRITICAL(sig); 307790792Sgshapiro sm_allsignals(true); 307890792Sgshapiro 307964562Sgshapiro if (sig != 0 && LogLevel > 79) 308038032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); 308138032Speter FileName = NULL; 308264562Sgshapiro 308364562Sgshapiro /* Clean-up on aborted stdin message submission */ 308464562Sgshapiro if (CurEnv->e_id != NULL && 308564562Sgshapiro (OpMode == MD_SMTP || 308664562Sgshapiro OpMode == MD_DELIVER || 308764562Sgshapiro OpMode == MD_ARPAFTP)) 308864562Sgshapiro { 308964562Sgshapiro register ADDRESS *q; 309064562Sgshapiro 309164562Sgshapiro /* don't return an error indication */ 309264562Sgshapiro CurEnv->e_to = NULL; 309364562Sgshapiro CurEnv->e_flags &= ~EF_FATALERRS; 309464562Sgshapiro CurEnv->e_flags |= EF_CLRQUEUE; 309564562Sgshapiro 309664562Sgshapiro /* 309764562Sgshapiro ** Spin through the addresses and 309864562Sgshapiro ** mark them dead to prevent bounces 309964562Sgshapiro */ 310064562Sgshapiro 310164562Sgshapiro for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 310264562Sgshapiro q->q_state = QS_DONTSEND; 310364562Sgshapiro 310490792Sgshapiro drop = true; 310564562Sgshapiro } 310680785Sgshapiro else if (OpMode != MD_TEST) 310790792Sgshapiro { 310864562Sgshapiro unlockqueue(CurEnv); 310938032Speter } 311038032Speter 311190792Sgshapiro finis(drop, false, EX_OK); 311290792Sgshapiro /* NOTREACHED */ 311338032Speter} 311490792Sgshapiro/* 311538032Speter** DISCONNECT -- remove our connection with any foreground process 311638032Speter** 311738032Speter** Parameters: 311838032Speter** droplev -- how "deeply" we should drop the line. 311938032Speter** 0 -- ignore signals, mail back errors, make sure 312038032Speter** output goes to stdout. 312164562Sgshapiro** 1 -- also, make stdout go to /dev/null. 312238032Speter** 2 -- also, disconnect from controlling terminal 312338032Speter** (only for daemon mode). 312438032Speter** e -- the current envelope. 312538032Speter** 312638032Speter** Returns: 312738032Speter** none 312838032Speter** 312938032Speter** Side Effects: 313038032Speter** Trys to insure that we are immune to vagaries of 313138032Speter** the controlling tty. 313238032Speter*/ 313338032Speter 313438032Spetervoid 313538032Speterdisconnect(droplev, e) 313638032Speter int droplev; 313738032Speter register ENVELOPE *e; 313838032Speter{ 313938032Speter int fd; 314038032Speter 314138032Speter if (tTd(52, 1)) 314290792Sgshapiro sm_dprintf("disconnect: In %d Out %d, e=%p\n", 314390792Sgshapiro sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL), 314490792Sgshapiro sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e); 314538032Speter if (tTd(52, 100)) 314638032Speter { 314790792Sgshapiro sm_dprintf("don't\n"); 314838032Speter return; 314938032Speter } 315038032Speter if (LogLevel > 93) 315138032Speter sm_syslog(LOG_DEBUG, e->e_id, 315264562Sgshapiro "disconnect level %d", 315364562Sgshapiro droplev); 315438032Speter 315538032Speter /* be sure we don't get nasty signals */ 315690792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 315790792Sgshapiro (void) sm_signal(SIGQUIT, SIG_IGN); 315838032Speter 315938032Speter /* we can't communicate with our caller, so.... */ 316090792Sgshapiro HoldErrs = true; 316138032Speter CurEnv->e_errormode = EM_MAIL; 316238032Speter Verbose = 0; 316390792Sgshapiro DisConnected = true; 316438032Speter 316538032Speter /* all input from /dev/null */ 316690792Sgshapiro if (InChannel != smioin) 316738032Speter { 316890792Sgshapiro (void) sm_io_close(InChannel, SM_TIME_DEFAULT); 316990792Sgshapiro InChannel = smioin; 317038032Speter } 317190792Sgshapiro if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, 317290792Sgshapiro SM_IO_RDONLY, NULL, smioin) == NULL) 317338032Speter sm_syslog(LOG_ERR, e->e_id, 317490792Sgshapiro "disconnect: sm_io_reopen(\"%s\") failed: %s", 317590792Sgshapiro SM_PATH_DEVNULL, sm_errstring(errno)); 317638032Speter 317790792Sgshapiro /* 317890792Sgshapiro ** output to the transcript 317990792Sgshapiro ** We also compare the fd numbers here since OutChannel 318090792Sgshapiro ** might be a layer on top of smioout due to encryption 318190792Sgshapiro ** (see sfsasl.c). 318290792Sgshapiro */ 318390792Sgshapiro 318490792Sgshapiro if (OutChannel != smioout && 318590792Sgshapiro sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) != 318690792Sgshapiro sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL)) 318738032Speter { 318890792Sgshapiro (void) sm_io_close(OutChannel, SM_TIME_DEFAULT); 318990792Sgshapiro OutChannel = smioout; 319090792Sgshapiro 319190792Sgshapiro#if 0 319290792Sgshapiro /* 319390792Sgshapiro ** Has smioout been closed? Reopen it. 319490792Sgshapiro ** This shouldn't happen anymore, the code is here 319590792Sgshapiro ** just as a reminder. 319690792Sgshapiro */ 319790792Sgshapiro 319890792Sgshapiro if (smioout->sm_magic == NULL && 319990792Sgshapiro sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL, 320090792Sgshapiro SM_IO_WRONLY, NULL, smioout) == NULL) 320190792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 320290792Sgshapiro "disconnect: sm_io_reopen(\"%s\") failed: %s", 320390792Sgshapiro SM_PATH_DEVNULL, sm_errstring(errno)); 320490792Sgshapiro#endif /* 0 */ 320538032Speter } 320638032Speter if (droplev > 0) 320738032Speter { 320890792Sgshapiro fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666); 320964562Sgshapiro if (fd == -1) 321064562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 321190792Sgshapiro "disconnect: open(\"%s\") failed: %s", 321290792Sgshapiro SM_PATH_DEVNULL, sm_errstring(errno)); 321390792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 321464562Sgshapiro (void) dup2(fd, STDOUT_FILENO); 321564562Sgshapiro (void) dup2(fd, STDERR_FILENO); 321664562Sgshapiro (void) close(fd); 321738032Speter } 321838032Speter 321938032Speter /* drop our controlling TTY completely if possible */ 322038032Speter if (droplev > 1) 322138032Speter { 322238032Speter (void) setsid(); 322338032Speter errno = 0; 322438032Speter } 322538032Speter 322638032Speter#if XDEBUG 322738032Speter checkfd012("disconnect"); 322864562Sgshapiro#endif /* XDEBUG */ 322938032Speter 323038032Speter if (LogLevel > 71) 323190792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d", 323290792Sgshapiro (int) CurrentPid); 323338032Speter 323438032Speter errno = 0; 323538032Speter} 323638032Speter 323738032Speterstatic void 323838032Speterobsolete(argv) 323938032Speter char *argv[]; 324038032Speter{ 324138032Speter register char *ap; 324238032Speter register char *op; 324338032Speter 324438032Speter while ((ap = *++argv) != NULL) 324538032Speter { 324638032Speter /* Return if "--" or not an option of any form. */ 324738032Speter if (ap[0] != '-' || ap[1] == '-') 324838032Speter return; 324938032Speter 325090792Sgshapiro#if _FFR_QUARANTINE 325190792Sgshapiro /* Don't allow users to use "-Q." or "-Q ." */ 325290792Sgshapiro if ((ap[1] == 'Q' && ap[2] == '.') || 325390792Sgshapiro (ap[1] == 'Q' && argv[1] != NULL && 325490792Sgshapiro argv[1][0] == '.' && argv[1][1] == '\0')) 325590792Sgshapiro { 325690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 325790792Sgshapiro "Can not use -Q.\n"); 325890792Sgshapiro exit(EX_USAGE); 325990792Sgshapiro } 326090792Sgshapiro#endif /* _FFR_QUARANTINE */ 326190792Sgshapiro 326238032Speter /* skip over options that do have a value */ 326338032Speter op = strchr(OPTIONS, ap[1]); 326438032Speter if (op != NULL && *++op == ':' && ap[2] == '\0' && 326538032Speter ap[1] != 'd' && 326638032Speter#if defined(sony_news) 326738032Speter ap[1] != 'E' && ap[1] != 'J' && 326864562Sgshapiro#endif /* defined(sony_news) */ 326938032Speter argv[1] != NULL && argv[1][0] != '-') 327038032Speter { 327138032Speter argv++; 327238032Speter continue; 327338032Speter } 327438032Speter 327538032Speter /* If -C doesn't have an argument, use sendmail.cf. */ 327690792Sgshapiro#define __DEFPATH "sendmail.cf" 327738032Speter if (ap[1] == 'C' && ap[2] == '\0') 327838032Speter { 327938032Speter *argv = xalloc(sizeof(__DEFPATH) + 2); 328090792Sgshapiro (void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2, 328190792Sgshapiro "-C", __DEFPATH); 328238032Speter } 328338032Speter 328438032Speter /* If -q doesn't have an argument, run it once. */ 328538032Speter if (ap[1] == 'q' && ap[2] == '\0') 328638032Speter *argv = "-q0"; 328738032Speter 328890792Sgshapiro#if _FFR_QUARANTINE 328990792Sgshapiro /* If -Q doesn't have an argument, disable quarantining */ 329090792Sgshapiro if (ap[1] == 'Q' && ap[2] == '\0') 329190792Sgshapiro *argv = "-Q."; 329290792Sgshapiro#endif /* _FFR_QUARANTINE */ 329390792Sgshapiro 329438032Speter /* if -d doesn't have an argument, use 0-99.1 */ 329538032Speter if (ap[1] == 'd' && ap[2] == '\0') 329638032Speter *argv = "-d0-99.1"; 329738032Speter 329864562Sgshapiro#if defined(sony_news) 329938032Speter /* if -E doesn't have an argument, use -EC */ 330038032Speter if (ap[1] == 'E' && ap[2] == '\0') 330138032Speter *argv = "-EC"; 330238032Speter 330338032Speter /* if -J doesn't have an argument, use -JJ */ 330438032Speter if (ap[1] == 'J' && ap[2] == '\0') 330538032Speter *argv = "-JJ"; 330664562Sgshapiro#endif /* defined(sony_news) */ 330738032Speter } 330838032Speter} 330990792Sgshapiro/* 331038032Speter** AUTH_WARNING -- specify authorization warning 331138032Speter** 331238032Speter** Parameters: 331338032Speter** e -- the current envelope. 331438032Speter** msg -- the text of the message. 331538032Speter** args -- arguments to the message. 331638032Speter** 331738032Speter** Returns: 331838032Speter** none. 331938032Speter*/ 332038032Speter 332138032Spetervoid 332238032Speter#ifdef __STDC__ 332338032Speterauth_warning(register ENVELOPE *e, const char *msg, ...) 332464562Sgshapiro#else /* __STDC__ */ 332538032Speterauth_warning(e, msg, va_alist) 332638032Speter register ENVELOPE *e; 332738032Speter const char *msg; 332838032Speter va_dcl 332964562Sgshapiro#endif /* __STDC__ */ 333038032Speter{ 333138032Speter char buf[MAXLINE]; 333290792Sgshapiro SM_VA_LOCAL_DECL 333338032Speter 333438032Speter if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) 333538032Speter { 333638032Speter register char *p; 333738032Speter static char hostbuf[48]; 333838032Speter 333938032Speter if (hostbuf[0] == '\0') 334071345Sgshapiro { 334171345Sgshapiro struct hostent *hp; 334238032Speter 334371345Sgshapiro hp = myhostname(hostbuf, sizeof hostbuf); 334490792Sgshapiro#if NETINET6 334571345Sgshapiro if (hp != NULL) 334671345Sgshapiro { 334771345Sgshapiro freehostent(hp); 334871345Sgshapiro hp = NULL; 334971345Sgshapiro } 335090792Sgshapiro#endif /* NETINET6 */ 335171345Sgshapiro } 335271345Sgshapiro 335390792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": "); 335438032Speter p = &buf[strlen(buf)]; 335590792Sgshapiro SM_VA_START(ap, msg); 335690792Sgshapiro (void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap); 335790792Sgshapiro SM_VA_END(ap); 335890792Sgshapiro addheader("X-Authentication-Warning", buf, 0, e); 335938032Speter if (LogLevel > 3) 336038032Speter sm_syslog(LOG_INFO, e->e_id, 336164562Sgshapiro "Authentication-Warning: %.400s", 336264562Sgshapiro buf); 336338032Speter } 336438032Speter} 336590792Sgshapiro/* 336638032Speter** GETEXTENV -- get from external environment 336738032Speter** 336838032Speter** Parameters: 336938032Speter** envar -- the name of the variable to retrieve 337038032Speter** 337138032Speter** Returns: 337238032Speter** The value, if any. 337338032Speter*/ 337438032Speter 337590792Sgshapirostatic char * 337638032Spetergetextenv(envar) 337738032Speter const char *envar; 337838032Speter{ 337938032Speter char **envp; 338038032Speter int l; 338138032Speter 338238032Speter l = strlen(envar); 3383102528Sgshapiro for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++) 338438032Speter { 338538032Speter if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') 338638032Speter return &(*envp)[l + 1]; 338738032Speter } 338838032Speter return NULL; 338938032Speter} 339090792Sgshapiro/* 339190792Sgshapiro** SETUSERENV -- set an environment in the propagated environment 339238032Speter** 339338032Speter** Parameters: 339438032Speter** envar -- the name of the environment variable. 339538032Speter** value -- the value to which it should be set. If 339638032Speter** null, this is extracted from the incoming 339738032Speter** environment. If that is not set, the call 339838032Speter** to setuserenv is ignored. 339938032Speter** 340038032Speter** Returns: 340138032Speter** none. 340238032Speter*/ 340338032Speter 340438032Spetervoid 340538032Spetersetuserenv(envar, value) 340638032Speter const char *envar; 340738032Speter const char *value; 340838032Speter{ 340964562Sgshapiro int i, l; 341038032Speter char **evp = UserEnviron; 341138032Speter char *p; 341238032Speter 341338032Speter if (value == NULL) 341438032Speter { 341538032Speter value = getextenv(envar); 341638032Speter if (value == NULL) 341738032Speter return; 341838032Speter } 341938032Speter 342090792Sgshapiro /* XXX enforce reasonable size? */ 342164562Sgshapiro i = strlen(envar) + 1; 342264562Sgshapiro l = strlen(value) + i + 1; 342364562Sgshapiro p = (char *) xalloc(l); 342490792Sgshapiro (void) sm_strlcpyn(p, l, 3, envar, "=", value); 342538032Speter 342638032Speter while (*evp != NULL && strncmp(*evp, p, i) != 0) 342738032Speter evp++; 342838032Speter if (*evp != NULL) 342938032Speter { 343038032Speter *evp++ = p; 343138032Speter } 343238032Speter else if (evp < &UserEnviron[MAXUSERENVIRON]) 343338032Speter { 343438032Speter *evp++ = p; 343538032Speter *evp = NULL; 343638032Speter } 343738032Speter 343838032Speter /* make sure it is in our environment as well */ 343938032Speter if (putenv(p) < 0) 344038032Speter syserr("setuserenv: putenv(%s) failed", p); 344138032Speter} 344290792Sgshapiro/* 344338032Speter** DUMPSTATE -- dump state 344438032Speter** 344538032Speter** For debugging. 344638032Speter*/ 344738032Speter 344838032Spetervoid 344938032Speterdumpstate(when) 345038032Speter char *when; 345138032Speter{ 345238032Speter register char *j = macvalue('j', CurEnv); 345338032Speter int rs; 345464562Sgshapiro extern int NextMacroId; 345538032Speter 345638032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 345764562Sgshapiro "--- dumping state on %s: $j = %s ---", 345864562Sgshapiro when, 345964562Sgshapiro j == NULL ? "<NULL>" : j); 346038032Speter if (j != NULL) 346138032Speter { 346238032Speter if (!wordinclass(j, 'w')) 346338032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 346464562Sgshapiro "*** $j not in $=w ***"); 346538032Speter } 346638032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); 346790792Sgshapiro sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)", 346864562Sgshapiro NextMacroId, MAXMACROID); 346938032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); 347090792Sgshapiro printopenfds(true); 347138032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); 347290792Sgshapiro mci_dump_all(true); 347338032Speter rs = strtorwset("debug_dumpstate", NULL, ST_FIND); 347438032Speter if (rs > 0) 347538032Speter { 347664562Sgshapiro int status; 347738032Speter register char **pvp; 347838032Speter char *pv[MAXATOM + 1]; 347938032Speter 348038032Speter pv[0] = NULL; 348190792Sgshapiro status = REWRITE(pv, rs, CurEnv); 348238032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, 348364562Sgshapiro "--- ruleset debug_dumpstate returns stat %d, pv: ---", 348464562Sgshapiro status); 348538032Speter for (pvp = pv; *pvp != NULL; pvp++) 348638032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); 348738032Speter } 348838032Speter sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); 348938032Speter} 349090792Sgshapiro 349180785Sgshapiro#ifdef SIGUSR1 349290792Sgshapiro/* 349377349Sgshapiro** SIGUSR1 -- Signal a request to dump state. 349477349Sgshapiro** 349577349Sgshapiro** Parameters: 349677349Sgshapiro** sig -- calling signal. 349777349Sgshapiro** 349877349Sgshapiro** Returns: 349977349Sgshapiro** none. 350077349Sgshapiro** 350177349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 350277349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 350377349Sgshapiro** DOING. 350477349Sgshapiro** 350577349Sgshapiro** XXX: More work is needed for this signal handler. 350677349Sgshapiro*/ 350738032Speter 350838032Speter/* ARGSUSED */ 350977349Sgshapirostatic SIGFUNC_DECL 351038032Spetersigusr1(sig) 351138032Speter int sig; 351238032Speter{ 351377349Sgshapiro int save_errno = errno; 351490792Sgshapiro# if SM_HEAP_CHECK 351590792Sgshapiro extern void dumpstab __P((void)); 351690792Sgshapiro# endif /* SM_HEAP_CHECK */ 351777349Sgshapiro 351877349Sgshapiro FIX_SYSV_SIGNAL(sig, sigusr1); 351977349Sgshapiro errno = save_errno; 352077349Sgshapiro CHECK_CRITICAL(sig); 352138032Speter dumpstate("user signal"); 352290792Sgshapiro# if SM_HEAP_CHECK 352390792Sgshapiro dumpstab(); 352490792Sgshapiro# endif /* SM_HEAP_CHECK */ 352577349Sgshapiro errno = save_errno; 352638032Speter return SIGFUNC_RETURN; 352738032Speter} 352890792Sgshapiro#endif /* SIGUSR1 */ 352990792Sgshapiro 353090792Sgshapiro/* 353138032Speter** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option 353238032Speter** 353338032Speter** Parameters: 353438032Speter** to_real_uid -- if set, drop to the real uid instead 353538032Speter** of the RunAsUser. 353638032Speter** 353738032Speter** Returns: 353838032Speter** EX_OSERR if the setuid failed. 353938032Speter** EX_OK otherwise. 354038032Speter*/ 354138032Speter 354238032Speterint 354338032Speterdrop_privileges(to_real_uid) 354438032Speter bool to_real_uid; 354538032Speter{ 354638032Speter int rval = EX_OK; 354738032Speter GIDSET_T emptygidset[1]; 354838032Speter 354938032Speter if (tTd(47, 1)) 355090792Sgshapiro 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", 355190792Sgshapiro (int) to_real_uid, 355290792Sgshapiro (int) RealUid, (int) RealGid, 355390792Sgshapiro (int) getuid(), (int) getgid(), 355490792Sgshapiro (int) geteuid(), (int) getegid(), 355590792Sgshapiro (int) RunAsUid, (int) RunAsGid); 355638032Speter 355738032Speter if (to_real_uid) 355838032Speter { 355938032Speter RunAsUserName = RealUserName; 356038032Speter RunAsUid = RealUid; 356138032Speter RunAsGid = RealGid; 356294334Sgshapiro EffGid = RunAsGid; 356338032Speter } 356438032Speter 356538032Speter /* make sure no one can grab open descriptors for secret files */ 356638032Speter endpwent(); 356790792Sgshapiro sm_mbdb_terminate(); 356838032Speter 356938032Speter /* reset group permissions; these can be set later */ 357038032Speter emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); 357190792Sgshapiro 357290792Sgshapiro /* 357390792Sgshapiro ** Notice: on some OS (Linux...) the setgroups() call causes 357490792Sgshapiro ** a logfile entry if sendmail is not run by root. 357590792Sgshapiro ** However, it is unclear (no POSIX standard) whether 357690792Sgshapiro ** setgroups() can only succeed if executed by root. 357790792Sgshapiro ** So for now we keep it as it is; if you want to change it, use 357890792Sgshapiro ** if (geteuid() == 0 && setgroups(1, emptygidset) == -1) 357990792Sgshapiro */ 358090792Sgshapiro 358138032Speter if (setgroups(1, emptygidset) == -1 && geteuid() == 0) 358264562Sgshapiro { 358364562Sgshapiro syserr("drop_privileges: setgroups(1, %d) failed", 358490792Sgshapiro (int) emptygidset[0]); 358538032Speter rval = EX_OSERR; 358664562Sgshapiro } 358738032Speter 358890792Sgshapiro /* reset primary group id */ 358990792Sgshapiro if (to_real_uid) 359064562Sgshapiro { 359190792Sgshapiro /* 359290792Sgshapiro ** Drop gid to real gid. 359390792Sgshapiro ** On some OS we must reset the effective[/real[/saved]] gid, 359490792Sgshapiro ** and then use setgid() to finally drop all group privileges. 359590792Sgshapiro ** Later on we check whether we can get back the 359690792Sgshapiro ** effective gid. 359790792Sgshapiro */ 359890792Sgshapiro 359990792Sgshapiro#if HASSETEGID 360090792Sgshapiro if (setegid(RunAsGid) < 0) 360190792Sgshapiro { 360290792Sgshapiro syserr("drop_privileges: setegid(%d) failed", 360390792Sgshapiro (int) RunAsGid); 360490792Sgshapiro rval = EX_OSERR; 360590792Sgshapiro } 360690792Sgshapiro#else /* HASSETEGID */ 360790792Sgshapiro# if HASSETREGID 360890792Sgshapiro if (setregid(RunAsGid, RunAsGid) < 0) 360990792Sgshapiro { 361090792Sgshapiro syserr("drop_privileges: setregid(%d, %d) failed", 361190792Sgshapiro (int) RunAsGid, (int) RunAsGid); 361290792Sgshapiro rval = EX_OSERR; 361390792Sgshapiro } 361490792Sgshapiro# else /* HASSETREGID */ 361590792Sgshapiro# if HASSETRESGID 361690792Sgshapiro if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0) 361790792Sgshapiro { 361890792Sgshapiro syserr("drop_privileges: setresgid(%d, %d, %d) failed", 361990792Sgshapiro (int) RunAsGid, (int) RunAsGid, (int) RunAsGid); 362090792Sgshapiro rval = EX_OSERR; 362190792Sgshapiro } 362290792Sgshapiro# endif /* HASSETRESGID */ 362390792Sgshapiro# endif /* HASSETREGID */ 362490792Sgshapiro#endif /* HASSETEGID */ 362564562Sgshapiro } 362690792Sgshapiro if (rval == EX_OK && (to_real_uid || RunAsGid != 0)) 362790792Sgshapiro { 362890792Sgshapiro if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid)) 362990792Sgshapiro { 363090792Sgshapiro syserr("drop_privileges: setgid(%d) failed", 363190792Sgshapiro (int) RunAsGid); 363290792Sgshapiro rval = EX_OSERR; 363390792Sgshapiro } 363490792Sgshapiro errno = 0; 363590792Sgshapiro if (rval == EX_OK && getegid() != RunAsGid) 363690792Sgshapiro { 363790792Sgshapiro syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d", 363890792Sgshapiro (int) getegid(), (int) RunAsGid); 363990792Sgshapiro rval = EX_OSERR; 364090792Sgshapiro } 364190792Sgshapiro } 364290792Sgshapiro 364390792Sgshapiro /* fiddle with uid */ 364464562Sgshapiro if (to_real_uid || RunAsUid != 0) 364564562Sgshapiro { 364694334Sgshapiro uid_t euid; 364764562Sgshapiro 364890792Sgshapiro /* 364990792Sgshapiro ** Try to setuid(RunAsUid). 365090792Sgshapiro ** euid must be RunAsUid, 365194334Sgshapiro ** ruid must be RunAsUid unless (e|r)uid wasn't 0 365294334Sgshapiro ** and we didn't have to drop privileges to the real uid. 365390792Sgshapiro */ 365490792Sgshapiro 365590792Sgshapiro if (setuid(RunAsUid) < 0 || 365694334Sgshapiro geteuid() != RunAsUid || 365794334Sgshapiro (getuid() != RunAsUid && 365894334Sgshapiro (to_real_uid || geteuid() == 0 || getuid() == 0))) 365964562Sgshapiro { 366090792Sgshapiro#if HASSETREUID 366190792Sgshapiro /* 366290792Sgshapiro ** if ruid != RunAsUid, euid == RunAsUid, then 366390792Sgshapiro ** try resetting just the real uid, then using 366490792Sgshapiro ** setuid() to drop the saved-uid as well. 366590792Sgshapiro */ 366690792Sgshapiro 366794334Sgshapiro if (geteuid() == RunAsUid) 366890792Sgshapiro { 366990792Sgshapiro if (setreuid(RunAsUid, -1) < 0) 367090792Sgshapiro { 367190792Sgshapiro syserr("drop_privileges: setreuid(%d, -1) failed", 367290792Sgshapiro (int) RunAsUid); 367390792Sgshapiro rval = EX_OSERR; 367490792Sgshapiro } 367590792Sgshapiro if (setuid(RunAsUid) < 0) 367690792Sgshapiro { 367790792Sgshapiro syserr("drop_privileges: second setuid(%d) attempt failed", 367890792Sgshapiro (int) RunAsUid); 367990792Sgshapiro rval = EX_OSERR; 368090792Sgshapiro } 368190792Sgshapiro } 368290792Sgshapiro else 368390792Sgshapiro#endif /* HASSETREUID */ 368490792Sgshapiro { 368590792Sgshapiro syserr("drop_privileges: setuid(%d) failed", 368690792Sgshapiro (int) RunAsUid); 368790792Sgshapiro rval = EX_OSERR; 368890792Sgshapiro } 368964562Sgshapiro } 369094334Sgshapiro euid = geteuid(); 369190792Sgshapiro if (RunAsUid != 0 && setuid(0) == 0) 369264562Sgshapiro { 369364562Sgshapiro /* 369464562Sgshapiro ** Believe it or not, the Linux capability model 369564562Sgshapiro ** allows a non-root process to override setuid() 369664562Sgshapiro ** on a process running as root and prevent that 369764562Sgshapiro ** process from dropping privileges. 369864562Sgshapiro */ 369964562Sgshapiro 370064562Sgshapiro syserr("drop_privileges: setuid(0) succeeded (when it should not)"); 370164562Sgshapiro rval = EX_OSERR; 370264562Sgshapiro } 370364562Sgshapiro else if (RunAsUid != euid && setuid(euid) == 0) 370464562Sgshapiro { 370564562Sgshapiro /* 370664562Sgshapiro ** Some operating systems will keep the saved-uid 370764562Sgshapiro ** if a non-root effective-uid calls setuid(real-uid) 370864562Sgshapiro ** making it possible to set it back again later. 370964562Sgshapiro */ 371064562Sgshapiro 371190792Sgshapiro syserr("drop_privileges: Unable to drop non-root set-user-ID privileges"); 371264562Sgshapiro rval = EX_OSERR; 371364562Sgshapiro } 371464562Sgshapiro } 371590792Sgshapiro 371690792Sgshapiro if ((to_real_uid || RunAsGid != 0) && 371790792Sgshapiro rval == EX_OK && RunAsGid != EffGid && 371890792Sgshapiro getuid() != 0 && geteuid() != 0) 371990792Sgshapiro { 372090792Sgshapiro errno = 0; 372190792Sgshapiro if (setgid(EffGid) == 0) 372290792Sgshapiro { 372390792Sgshapiro syserr("drop_privileges: setgid(%d) succeeded (when it should not)", 372490792Sgshapiro (int) EffGid); 372590792Sgshapiro rval = EX_OSERR; 372690792Sgshapiro } 372790792Sgshapiro } 372890792Sgshapiro 372938032Speter if (tTd(47, 5)) 373038032Speter { 373190792Sgshapiro sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", 373290792Sgshapiro (int) geteuid(), (int) getuid(), 373390792Sgshapiro (int) getegid(), (int) getgid()); 373490792Sgshapiro sm_dprintf("drop_privileges: RunAsUser = %d:%d\n", 373590792Sgshapiro (int) RunAsUid, (int) RunAsGid); 373664562Sgshapiro if (tTd(47, 10)) 373790792Sgshapiro sm_dprintf("drop_privileges: rval = %d\n", rval); 373838032Speter } 373938032Speter return rval; 374038032Speter} 374190792Sgshapiro/* 374238032Speter** FILL_FD -- make sure a file descriptor has been properly allocated 374338032Speter** 374438032Speter** Used to make sure that stdin/out/err are allocated on startup 374538032Speter** 374638032Speter** Parameters: 374738032Speter** fd -- the file descriptor to be filled. 374838032Speter** where -- a string used for logging. If NULL, this is 374938032Speter** being called on startup, and logging should 375038032Speter** not be done. 375138032Speter** 375238032Speter** Returns: 375338032Speter** none 375490792Sgshapiro** 375590792Sgshapiro** Side Effects: 375690792Sgshapiro** possibly changes MissingFds 375738032Speter*/ 375838032Speter 375938032Spetervoid 376038032Speterfill_fd(fd, where) 376138032Speter int fd; 376238032Speter char *where; 376338032Speter{ 376438032Speter int i; 376538032Speter struct stat stbuf; 376638032Speter 376738032Speter if (fstat(fd, &stbuf) >= 0 || errno != EBADF) 376838032Speter return; 376938032Speter 377038032Speter if (where != NULL) 377138032Speter syserr("fill_fd: %s: fd %d not open", where, fd); 377238032Speter else 377338032Speter MissingFds |= 1 << fd; 377490792Sgshapiro i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666); 377538032Speter if (i < 0) 377638032Speter { 377790792Sgshapiro syserr("!fill_fd: %s: cannot open %s", 377890792Sgshapiro where == NULL ? "startup" : where, SM_PATH_DEVNULL); 377938032Speter } 378038032Speter if (fd != i) 378138032Speter { 378238032Speter (void) dup2(i, fd); 378338032Speter (void) close(i); 378438032Speter } 378538032Speter} 378690792Sgshapiro/* 378790792Sgshapiro** SM_PRINTOPTIONS -- print options 378890792Sgshapiro** 378990792Sgshapiro** Parameters: 379090792Sgshapiro** options -- array of options. 379190792Sgshapiro** 379290792Sgshapiro** Returns: 379390792Sgshapiro** none. 379490792Sgshapiro*/ 379590792Sgshapiro 379690792Sgshapirostatic void 379790792Sgshapirosm_printoptions(options) 379890792Sgshapiro char **options; 379990792Sgshapiro{ 380090792Sgshapiro int ll; 380190792Sgshapiro char **av; 380290792Sgshapiro 380390792Sgshapiro av = options; 380490792Sgshapiro ll = 7; 380590792Sgshapiro while (*av != NULL) 380690792Sgshapiro { 380790792Sgshapiro if (ll + strlen(*av) > 63) 380890792Sgshapiro { 380990792Sgshapiro sm_dprintf("\n"); 381090792Sgshapiro ll = 0; 381190792Sgshapiro } 381290792Sgshapiro if (ll == 0) 381390792Sgshapiro sm_dprintf("\t\t"); 381490792Sgshapiro else 381590792Sgshapiro sm_dprintf(" "); 381690792Sgshapiro sm_dprintf("%s", *av); 381790792Sgshapiro ll += strlen(*av++) + 1; 381890792Sgshapiro } 381990792Sgshapiro sm_dprintf("\n"); 382090792Sgshapiro} 382190792Sgshapiro/* 382238032Speter** TESTMODELINE -- process a test mode input line 382338032Speter** 382438032Speter** Parameters: 382538032Speter** line -- the input line. 382638032Speter** e -- the current environment. 382738032Speter** Syntax: 382838032Speter** # a comment 382938032Speter** .X process X as a configuration line 383038032Speter** =X dump a configuration item (such as mailers) 383138032Speter** $X dump a macro or class 383238032Speter** /X try an activity 383338032Speter** X normal process through rule set X 383438032Speter*/ 383538032Speter 383664562Sgshapirostatic void 383738032Spetertestmodeline(line, e) 383838032Speter char *line; 383938032Speter ENVELOPE *e; 384038032Speter{ 384138032Speter register char *p; 384238032Speter char *q; 384338032Speter auto char *delimptr; 384438032Speter int mid; 384538032Speter int i, rs; 384638032Speter STAB *map; 384738032Speter char **s; 384838032Speter struct rewrite *rw; 384938032Speter ADDRESS a; 385038032Speter static int tryflags = RF_COPYNONE; 385138032Speter char exbuf[MAXLINE]; 385290792Sgshapiro extern unsigned char TokTypeNoC[]; 385338032Speter 385466494Sgshapiro /* skip leading spaces */ 385566494Sgshapiro while (*line == ' ') 385666494Sgshapiro line++; 385766494Sgshapiro 385838032Speter switch (line[0]) 385938032Speter { 386038032Speter case '#': 386164562Sgshapiro case '\0': 386238032Speter return; 386338032Speter 386438032Speter case '?': 386564562Sgshapiro help("-bt", e); 386638032Speter return; 386738032Speter 386838032Speter case '.': /* config-style settings */ 386938032Speter switch (line[1]) 387038032Speter { 387138032Speter case 'D': 387290792Sgshapiro mid = macid_parse(&line[2], &delimptr); 387371345Sgshapiro if (mid == 0) 387438032Speter return; 387538032Speter translate_dollars(delimptr); 387690792Sgshapiro macdefine(&e->e_macro, A_TEMP, mid, delimptr); 387738032Speter break; 387838032Speter 387938032Speter case 'C': 388038032Speter if (line[2] == '\0') /* not to call syserr() */ 388138032Speter return; 388238032Speter 388390792Sgshapiro mid = macid_parse(&line[2], &delimptr); 388471345Sgshapiro if (mid == 0) 388538032Speter return; 388638032Speter translate_dollars(delimptr); 388738032Speter expand(delimptr, exbuf, sizeof exbuf, e); 388838032Speter p = exbuf; 388938032Speter while (*p != '\0') 389038032Speter { 389138032Speter register char *wd; 389238032Speter char delim; 389338032Speter 389438032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 389538032Speter p++; 389638032Speter wd = p; 389738032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 389838032Speter p++; 389938032Speter delim = *p; 390038032Speter *p = '\0'; 390138032Speter if (wd[0] != '\0') 390238032Speter setclass(mid, wd); 390338032Speter *p = delim; 390438032Speter } 390538032Speter break; 390638032Speter 390738032Speter case '\0': 390890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 390990792Sgshapiro "Usage: .[DC]macro value(s)\n"); 391038032Speter break; 391138032Speter 391238032Speter default: 391390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 391490792Sgshapiro "Unknown \".\" command %s\n", line); 391538032Speter break; 391638032Speter } 391738032Speter return; 391838032Speter 391938032Speter case '=': /* config-style settings */ 392038032Speter switch (line[1]) 392138032Speter { 392238032Speter case 'S': /* dump rule set */ 392338032Speter rs = strtorwset(&line[2], NULL, ST_FIND); 392438032Speter if (rs < 0) 392538032Speter { 392690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 392790792Sgshapiro "Undefined ruleset %s\n", &line[2]); 392838032Speter return; 392938032Speter } 393038032Speter rw = RewriteRules[rs]; 393138032Speter if (rw == NULL) 393238032Speter return; 393338032Speter do 393438032Speter { 393590792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 393690792Sgshapiro 'R'); 393738032Speter s = rw->r_lhs; 393838032Speter while (*s != NULL) 393938032Speter { 394038032Speter xputs(*s++); 394190792Sgshapiro (void) sm_io_putc(smioout, 394290792Sgshapiro SM_TIME_DEFAULT, ' '); 394338032Speter } 394490792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 394590792Sgshapiro '\t'); 394690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 394790792Sgshapiro '\t'); 394838032Speter s = rw->r_rhs; 394938032Speter while (*s != NULL) 395038032Speter { 395138032Speter xputs(*s++); 395290792Sgshapiro (void) sm_io_putc(smioout, 395390792Sgshapiro SM_TIME_DEFAULT, ' '); 395438032Speter } 395590792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, 395690792Sgshapiro '\n'); 395738032Speter } while ((rw = rw->r_next) != NULL); 395838032Speter break; 395938032Speter 396038032Speter case 'M': 396138032Speter for (i = 0; i < MAXMAILERS; i++) 396238032Speter { 396338032Speter if (Mailer[i] != NULL) 396438032Speter printmailer(Mailer[i]); 396538032Speter } 396638032Speter break; 396738032Speter 396838032Speter case '\0': 396990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 397090792Sgshapiro "Usage: =Sruleset or =M\n"); 397138032Speter break; 397238032Speter 397338032Speter default: 397490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 397590792Sgshapiro "Unknown \"=\" command %s\n", line); 397638032Speter break; 397738032Speter } 397838032Speter return; 397938032Speter 398038032Speter case '-': /* set command-line-like opts */ 398138032Speter switch (line[1]) 398238032Speter { 398338032Speter case 'd': 398438032Speter tTflag(&line[2]); 398538032Speter break; 398638032Speter 398738032Speter case '\0': 398890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 398990792Sgshapiro "Usage: -d{debug arguments}\n"); 399038032Speter break; 399138032Speter 399238032Speter default: 399390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 399490792Sgshapiro "Unknown \"-\" command %s\n", line); 399538032Speter break; 399638032Speter } 399738032Speter return; 399838032Speter 399938032Speter case '$': 400038032Speter if (line[1] == '=') 400138032Speter { 400290792Sgshapiro mid = macid(&line[2]); 400371345Sgshapiro if (mid != 0) 400438032Speter stabapply(dump_class, mid); 400538032Speter return; 400638032Speter } 400790792Sgshapiro mid = macid(&line[1]); 400871345Sgshapiro if (mid == 0) 400938032Speter return; 401038032Speter p = macvalue(mid, e); 401138032Speter if (p == NULL) 401290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 401390792Sgshapiro "Undefined\n"); 401438032Speter else 401538032Speter { 401638032Speter xputs(p); 401790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 401890792Sgshapiro "\n"); 401938032Speter } 402038032Speter return; 402138032Speter 402238032Speter case '/': /* miscellaneous commands */ 402338032Speter p = &line[strlen(line)]; 402438032Speter while (--p >= line && isascii(*p) && isspace(*p)) 402538032Speter *p = '\0'; 402638032Speter p = strpbrk(line, " \t"); 402738032Speter if (p != NULL) 402838032Speter { 402938032Speter while (isascii(*p) && isspace(*p)) 403038032Speter *p++ = '\0'; 403138032Speter } 403238032Speter else 403338032Speter p = ""; 403438032Speter if (line[1] == '\0') 403538032Speter { 403690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 403790792Sgshapiro "Usage: /[canon|map|mx|parse|try|tryflags]\n"); 403838032Speter return; 403938032Speter } 404090792Sgshapiro if (sm_strcasecmp(&line[1], "quit") == 0) 404164562Sgshapiro { 404264562Sgshapiro CurEnv->e_id = NULL; 404390792Sgshapiro finis(true, true, ExitStat); 404490792Sgshapiro /* NOTREACHED */ 404564562Sgshapiro } 404690792Sgshapiro if (sm_strcasecmp(&line[1], "mx") == 0) 404738032Speter { 404838032Speter#if NAMED_BIND 404938032Speter /* look up MX records */ 405038032Speter int nmx; 405138032Speter auto int rcode; 405238032Speter char *mxhosts[MAXMXHOSTS + 1]; 405338032Speter 405438032Speter if (*p == '\0') 405538032Speter { 405690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 405790792Sgshapiro "Usage: /mx address\n"); 405838032Speter return; 405938032Speter } 406090792Sgshapiro nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true, 406190792Sgshapiro NULL); 406290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 406390792Sgshapiro "getmxrr(%s) returns %d value(s):\n", 406490792Sgshapiro p, nmx); 406538032Speter for (i = 0; i < nmx; i++) 406690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 406790792Sgshapiro "\t%s\n", mxhosts[i]); 406864562Sgshapiro#else /* NAMED_BIND */ 406990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 407090792Sgshapiro "No MX code compiled in\n"); 407164562Sgshapiro#endif /* NAMED_BIND */ 407238032Speter } 407390792Sgshapiro else if (sm_strcasecmp(&line[1], "canon") == 0) 407438032Speter { 407538032Speter char host[MAXHOSTNAMELEN]; 407638032Speter 407738032Speter if (*p == '\0') 407838032Speter { 407990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 408090792Sgshapiro "Usage: /canon address\n"); 408138032Speter return; 408238032Speter } 408390792Sgshapiro else if (sm_strlcpy(host, p, sizeof host) >= sizeof host) 408438032Speter { 408590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 408690792Sgshapiro "Name too long\n"); 408738032Speter return; 408838032Speter } 408990792Sgshapiro (void) getcanonname(host, sizeof host, HasWildcardMX, 409090792Sgshapiro NULL); 409190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 409290792Sgshapiro "getcanonname(%s) returns %s\n", 409390792Sgshapiro p, host); 409438032Speter } 409590792Sgshapiro else if (sm_strcasecmp(&line[1], "map") == 0) 409638032Speter { 409738032Speter auto int rcode = EX_OK; 409838032Speter char *av[2]; 409938032Speter 410038032Speter if (*p == '\0') 410138032Speter { 410290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 410390792Sgshapiro "Usage: /map mapname key\n"); 410438032Speter return; 410538032Speter } 410690792Sgshapiro for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) 410738032Speter continue; 410838032Speter if (*q == '\0') 410938032Speter { 411090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 411190792Sgshapiro "No key specified\n"); 411238032Speter return; 411338032Speter } 411438032Speter *q++ = '\0'; 411538032Speter map = stab(p, ST_MAP, ST_FIND); 411638032Speter if (map == NULL) 411738032Speter { 411890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 411990792Sgshapiro "Map named \"%s\" not found\n", p); 412038032Speter return; 412138032Speter } 412264562Sgshapiro if (!bitset(MF_OPEN, map->s_map.map_mflags) && 412364562Sgshapiro !openmap(&(map->s_map))) 412438032Speter { 412590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 412690792Sgshapiro "Map named \"%s\" not open\n", p); 412738032Speter return; 412838032Speter } 412990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 413090792Sgshapiro "map_lookup: %s (%s) ", p, q); 413138032Speter av[0] = q; 413238032Speter av[1] = NULL; 413338032Speter p = (*map->s_map.map_class->map_lookup) 413438032Speter (&map->s_map, q, av, &rcode); 413538032Speter if (p == NULL) 413690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 413790792Sgshapiro "no match (%d)\n", 413890792Sgshapiro rcode); 413938032Speter else 414090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 414190792Sgshapiro "returns %s (%d)\n", p, 414290792Sgshapiro rcode); 414338032Speter } 414490792Sgshapiro else if (sm_strcasecmp(&line[1], "try") == 0) 414538032Speter { 414638032Speter MAILER *m; 414764562Sgshapiro STAB *st; 414838032Speter auto int rcode = EX_OK; 414938032Speter 415038032Speter q = strpbrk(p, " \t"); 415138032Speter if (q != NULL) 415238032Speter { 415338032Speter while (isascii(*q) && isspace(*q)) 415438032Speter *q++ = '\0'; 415538032Speter } 415638032Speter if (q == NULL || *q == '\0') 415738032Speter { 415890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 415990792Sgshapiro "Usage: /try mailer address\n"); 416038032Speter return; 416138032Speter } 416264562Sgshapiro st = stab(p, ST_MAILER, ST_FIND); 416364562Sgshapiro if (st == NULL) 416438032Speter { 416590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 416690792Sgshapiro "Unknown mailer %s\n", p); 416738032Speter return; 416838032Speter } 416964562Sgshapiro m = st->s_mailer; 417090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 417190792Sgshapiro "Trying %s %s address %s for mailer %s\n", 417290792Sgshapiro bitset(RF_HEADERADDR, tryflags) ? "header" 417390792Sgshapiro : "envelope", 417490792Sgshapiro bitset(RF_SENDERADDR, tryflags) ? "sender" 417590792Sgshapiro : "recipient", q, p); 417638032Speter p = remotename(q, m, tryflags, &rcode, CurEnv); 417790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 417890792Sgshapiro "Rcode = %d, addr = %s\n", 417990792Sgshapiro rcode, p == NULL ? "<NULL>" : p); 418038032Speter e->e_to = NULL; 418138032Speter } 418290792Sgshapiro else if (sm_strcasecmp(&line[1], "tryflags") == 0) 418338032Speter { 418438032Speter if (*p == '\0') 418538032Speter { 418690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 418790792Sgshapiro "Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); 418838032Speter return; 418938032Speter } 419038032Speter for (; *p != '\0'; p++) 419138032Speter { 419238032Speter switch (*p) 419338032Speter { 419438032Speter case 'H': 419538032Speter case 'h': 419638032Speter tryflags |= RF_HEADERADDR; 419738032Speter break; 419838032Speter 419938032Speter case 'E': 420038032Speter case 'e': 420138032Speter tryflags &= ~RF_HEADERADDR; 420238032Speter break; 420338032Speter 420438032Speter case 'S': 420538032Speter case 's': 420638032Speter tryflags |= RF_SENDERADDR; 420738032Speter break; 420838032Speter 420938032Speter case 'R': 421038032Speter case 'r': 421138032Speter tryflags &= ~RF_SENDERADDR; 421238032Speter break; 421338032Speter } 421438032Speter } 421564562Sgshapiro exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e'; 421664562Sgshapiro exbuf[1] = ' '; 421764562Sgshapiro exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r'; 421864562Sgshapiro exbuf[3] = '\0'; 421990792Sgshapiro macdefine(&e->e_macro, A_TEMP, 422090792Sgshapiro macid("{addr_type}"), exbuf); 422138032Speter } 422290792Sgshapiro else if (sm_strcasecmp(&line[1], "parse") == 0) 422338032Speter { 422438032Speter if (*p == '\0') 422538032Speter { 422690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 422790792Sgshapiro "Usage: /parse address\n"); 422838032Speter return; 422938032Speter } 423038032Speter q = crackaddr(p); 423190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 423290792Sgshapiro "Cracked address = "); 423338032Speter xputs(q); 423490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 423590792Sgshapiro "\nParsing %s %s address\n", 423690792Sgshapiro bitset(RF_HEADERADDR, tryflags) ? 423790792Sgshapiro "header" : "envelope", 423890792Sgshapiro bitset(RF_SENDERADDR, tryflags) ? 423990792Sgshapiro "sender" : "recipient"); 424090792Sgshapiro if (parseaddr(p, &a, tryflags, '\0', NULL, e, true) 424190792Sgshapiro == NULL) 424290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 424390792Sgshapiro "Cannot parse\n"); 424438032Speter else if (a.q_host != NULL && a.q_host[0] != '\0') 424590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 424690792Sgshapiro "mailer %s, host %s, user %s\n", 424790792Sgshapiro a.q_mailer->m_name, 424890792Sgshapiro a.q_host, 424990792Sgshapiro a.q_user); 425038032Speter else 425190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 425290792Sgshapiro "mailer %s, user %s\n", 425390792Sgshapiro a.q_mailer->m_name, 425490792Sgshapiro a.q_user); 425538032Speter e->e_to = NULL; 425638032Speter } 425738032Speter else 425838032Speter { 425990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 426090792Sgshapiro "Unknown \"/\" command %s\n", 426190792Sgshapiro line); 426238032Speter } 426338032Speter return; 426438032Speter } 426538032Speter 426638032Speter for (p = line; isascii(*p) && isspace(*p); p++) 426738032Speter continue; 426838032Speter q = p; 426938032Speter while (*p != '\0' && !(isascii(*p) && isspace(*p))) 427038032Speter p++; 427138032Speter if (*p == '\0') 427238032Speter { 427390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 427490792Sgshapiro "No address!\n"); 427538032Speter return; 427638032Speter } 427738032Speter *p = '\0'; 427890792Sgshapiro if (invalidaddr(p + 1, NULL, true)) 427938032Speter return; 428038032Speter do 428138032Speter { 428238032Speter register char **pvp; 428338032Speter char pvpbuf[PSBUFSIZE]; 428438032Speter 428538032Speter pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, 428664562Sgshapiro &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL); 428738032Speter if (pvp == NULL) 428838032Speter continue; 428938032Speter p = q; 429038032Speter while (*p != '\0') 429138032Speter { 429264562Sgshapiro int status; 429338032Speter 429438032Speter rs = strtorwset(p, NULL, ST_FIND); 429538032Speter if (rs < 0) 429638032Speter { 429790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 429890792Sgshapiro "Undefined ruleset %s\n", 429990792Sgshapiro p); 430038032Speter break; 430138032Speter } 430290792Sgshapiro status = REWRITE(pvp, rs, e); 430364562Sgshapiro if (status != EX_OK) 430490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 430590792Sgshapiro "== Ruleset %s (%d) status %d\n", 430690792Sgshapiro p, rs, status); 430738032Speter while (*p != '\0' && *p++ != ',') 430838032Speter continue; 430938032Speter } 431038032Speter } while (*(p = delimptr) != '\0'); 431138032Speter} 431238032Speter 431364562Sgshapirostatic void 431438032Speterdump_class(s, id) 431538032Speter register STAB *s; 431638032Speter int id; 431738032Speter{ 431890792Sgshapiro if (s->s_symtype != ST_CLASS) 431938032Speter return; 432071345Sgshapiro if (bitnset(bitidx(id), s->s_class)) 432190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 432290792Sgshapiro "%s\n", s->s_name); 432338032Speter} 432490792Sgshapiro 432590792Sgshapiro/* 432690792Sgshapiro** An exception type used to create QuickAbort exceptions. 432790792Sgshapiro** This is my first cut at converting QuickAbort from longjmp to exceptions. 432890792Sgshapiro** These exceptions have a single integer argument, which is the argument 432990792Sgshapiro** to longjmp in the original code (either 1 or 2). I don't know the 433090792Sgshapiro** significance of 1 vs 2: the calls to setjmp don't care. 433190792Sgshapiro*/ 433290792Sgshapiro 433390792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort = 433490792Sgshapiro{ 433590792Sgshapiro SmExcTypeMagic, 433690792Sgshapiro "E:mta.quickabort", 433790792Sgshapiro "i", 433890792Sgshapiro sm_etype_printf, 433990792Sgshapiro "quick abort %0", 434090792Sgshapiro}; 4341