This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.sbin / sendmail / src / main.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
6f14531a
RG
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
15637ed4
RG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
6f14531a
RG
36static char copyright[] =
37"@(#) Copyright (c) 1988, 1993\n\
38 The Regents of the University of California. All rights reserved.\n";
15637ed4
RG
39#endif /* not lint */
40
41#ifndef lint
d747e748 42static char sccsid[] = "@(#)main.c 8.33 (Berkeley) 10/24/93";
15637ed4
RG
43#endif /* not lint */
44
45#define _DEFINE
46
6f14531a 47#include "sendmail.h"
15637ed4 48#include <sgtty.h>
6f14531a 49#ifdef NAMED_BIND
15637ed4
RG
50#include <arpa/nameser.h>
51#include <resolv.h>
6f14531a
RG
52#endif
53#include <pwd.h>
15637ed4
RG
54
55# ifdef lint
56char edata, end;
d747e748 57# endif /* lint */
15637ed4
RG
58
59/*
60** SENDMAIL -- Post mail to a set of destinations.
61**
62** This is the basic mail router. All user mail programs should
63** call this routine to actually deliver mail. Sendmail in
64** turn calls a bunch of mail servers that do the real work of
65** delivering the mail.
66**
67** Sendmail is driven by tables read in from /usr/lib/sendmail.cf
68** (read by readcf.c). Some more static configuration info,
69** including some code that you may want to tailor for your
70** installation, is in conf.c. You may also want to touch
71** daemon.c (if you have some other IPC mechanism), acct.c
72** (to change your accounting), names.c (to adjust the name
73** server mechanism).
74**
75** Usage:
76** /usr/lib/sendmail [flags] addr ...
77**
78** See the associated documentation for details.
79**
80** Author:
81** Eric Allman, UCB/INGRES (until 10/81)
82** Britton-Lee, Inc., purveyors of fine
83** database computers (from 11/81)
6f14531a 84** Now back at UCB at the Mammoth project.
15637ed4
RG
85** The support of the INGRES Project and Britton-Lee is
86** gratefully acknowledged. Britton-Lee in
87** particular had absolutely nothing to gain from
88** my involvement in this project.
89*/
90
91
92int NextMailer; /* "free" index into Mailer struct */
93char *FullName; /* sender's full name */
94ENVELOPE BlankEnvelope; /* a "blank" envelope */
95ENVELOPE MainEnvelope; /* the envelope around the basic letter */
96ADDRESS NullAddress = /* a null address */
97 { "", "", NULL, "" };
d747e748 98char *UserEnviron[MAXUSERENVIRON + 2];
6f14531a
RG
99 /* saved user environment */
100char RealUserName[256]; /* the actual user id on this host */
d747e748
JH
101char *CommandLineArgs; /* command line args for pid file */
102bool Warn_Q_option = FALSE; /* warn about Q option use */
15637ed4
RG
103
104/*
105** Pointers for setproctitle.
106** This allows "ps" listings to give more useful information.
15637ed4
RG
107*/
108
109# ifdef SETPROCTITLE
110char **Argv = NULL; /* pointer to argument vector */
111char *LastArgv = NULL; /* end of argv */
6f14531a
RG
112# endif /* SETPROCTITLE */
113
114static void obsolete();
15637ed4
RG
115
116#ifdef DAEMON
117#ifndef SMTP
118ERROR %%%% Cannot have daemon mode without SMTP %%%% ERROR
6f14531a
RG
119#endif /* SMTP */
120#endif /* DAEMON */
121
d747e748 122#define MAXCONFIGLEVEL 5 /* highest config version level known */
15637ed4
RG
123
124main(argc, argv, envp)
125 int argc;
126 char **argv;
127 char **envp;
128{
129 register char *p;
130 char **av;
131 extern int finis();
132 extern char Version[];
6f14531a 133 char *ep, *from;
15637ed4
RG
134 typedef int (*fnptr)();
135 STAB *st;
136 register int i;
6f14531a 137 int j;
15637ed4 138 bool queuemode = FALSE; /* process queue requests */
6f14531a 139 bool safecf = TRUE;
d747e748
JH
140 bool warn_C_flag = FALSE;
141 char warn_f_flag = '\0';
15637ed4 142 static bool reenter = FALSE;
6f14531a
RG
143 char *argv0 = argv[0];
144 struct passwd *pw;
145 struct stat stb;
146 char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
147 extern int DtableSize;
148 extern int optind;
15637ed4
RG
149 extern time_t convtime();
150 extern putheader(), putbody();
15637ed4
RG
151 extern void intsig();
152 extern char **myhostname();
153 extern char *arpadate();
6f14531a 154 extern char *getauthinfo();
d747e748 155 extern char *getcfname();
6f14531a 156 extern char *optarg;
15637ed4
RG
157 extern char **environ;
158
159 /*
160 ** Check to see if we reentered.
161 ** This would normally happen if e_putheader or e_putbody
162 ** were NULL when invoked.
163 */
164
165 if (reenter)
166 {
167 syserr("main: reentered!");
168 abort();
169 }
170 reenter = TRUE;
171
d747e748
JH
172 /* do machine-dependent initializations */
173 init_md();
6f14531a
RG
174
175 /* in 4.4BSD, the table can be huge; impose a reasonable limit */
3a363396 176 DtableSize = getdtsize();
6f14531a
RG
177 if (DtableSize > 256)
178 DtableSize = 256;
15637ed4
RG
179
180 /*
181 ** Be sure we have enough file descriptors.
182 ** But also be sure that 0, 1, & 2 are open.
183 */
184
185 i = open("/dev/null", O_RDWR);
d747e748 186 if (fstat(STDIN_FILENO, &stb) < 0 && errno != EOPNOTSUPP)
6f14531a 187 (void) dup2(i, STDIN_FILENO);
d747e748 188 if (fstat(STDOUT_FILENO, &stb) < 0 && errno != EOPNOTSUPP)
6f14531a 189 (void) dup2(i, STDOUT_FILENO);
d747e748 190 if (fstat(STDERR_FILENO, &stb) < 0 && errno != EOPNOTSUPP)
6f14531a 191 (void) dup2(i, STDERR_FILENO);
d747e748
JH
192 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
193 (void) close(i);
6f14531a
RG
194
195 i = DtableSize;
196 while (--i > 0)
197 {
198 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
199 (void) close(i);
200 }
15637ed4
RG
201 errno = 0;
202
203#ifdef LOG_MAIL
204 openlog("sendmail", LOG_PID, LOG_MAIL);
205#else
206 openlog("sendmail", LOG_PID);
207#endif
208
15637ed4
RG
209 /* set up the blank envelope */
210 BlankEnvelope.e_puthdr = putheader;
211 BlankEnvelope.e_putbody = putbody;
212 BlankEnvelope.e_xfp = NULL;
213 STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
214 CurEnv = &BlankEnvelope;
215 STRUCTCOPY(NullAddress, MainEnvelope.e_from);
216
6f14531a
RG
217 /*
218 ** Set default values for variables.
219 ** These cannot be in initialized data space.
220 */
221
222 setdefaults(&BlankEnvelope);
223
224 RealUid = getuid();
225 RealGid = getgid();
226
227 pw = getpwuid(RealUid);
228 if (pw != NULL)
229 (void) strcpy(RealUserName, pw->pw_name);
230 else
231 (void) sprintf(RealUserName, "Unknown UID %d", RealUid);
232
d747e748
JH
233 /* save command line arguments */
234 i = 0;
235 for (av = argv; *av != NULL; )
236 i += strlen(*av++) + 1;
237 CommandLineArgs = xalloc(i);
238 p = CommandLineArgs;
239 for (av = argv; *av != NULL; )
240 {
241 if (av != argv)
242 *p++ = ' ';
243 strcpy(p, *av++);
244 p += strlen(p);
245 }
3a363396 246
6f14531a
RG
247 /* Handle any non-getoptable constructions. */
248 obsolete(argv);
249
15637ed4
RG
250 /*
251 ** Do a quick prescan of the argument list.
15637ed4 252 */
d747e748 253
3a363396 254#if defined(__osf__) || defined(_AIX3)
d747e748 255# define OPTIONS "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:x"
6f14531a 256#else
d747e748
JH
257# if defined(ultrix)
258# define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mno:p:q:r:sTtvX:"
259# else
260# define OPTIONS "B:b:C:cd:e:F:f:h:Iimno:p:q:r:sTtvX:"
261# endif
6f14531a
RG
262#endif
263 while ((j = getopt(argc, argv, OPTIONS)) != EOF)
15637ed4 264 {
6f14531a 265 switch (j)
15637ed4 266 {
6f14531a 267 case 'd':
15637ed4 268 tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
6f14531a 269 tTflag(optarg);
15637ed4
RG
270 setbuf(stdout, (char *) NULL);
271 printf("Version %s\n", Version);
6f14531a 272 break;
15637ed4
RG
273 }
274 }
275
276 InChannel = stdin;
277 OutChannel = stdout;
278
6f14531a
RG
279 /*
280 ** Move the environment so setproctitle can use the space at
281 ** the top of memory.
282 */
283
284 for (i = j = 0; j < MAXUSERENVIRON && (p = envp[i]) != NULL; i++)
285 {
286 if (strncmp(p, "FS=", 3) == 0 || strncmp(p, "LD_", 3) == 0)
287 continue;
288 UserEnviron[j++] = newstr(p);
289 }
290 UserEnviron[j] = NULL;
15637ed4
RG
291 environ = UserEnviron;
292
d747e748 293# ifdef SETPROCTITLE
15637ed4
RG
294 /*
295 ** Save start and extent of argv for setproctitle.
296 */
297
298 Argv = argv;
299 if (i > 0)
300 LastArgv = envp[i - 1] + strlen(envp[i - 1]);
301 else
302 LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
6f14531a 303# endif /* SETPROCTITLE */
15637ed4 304
d747e748
JH
305 if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
306 (void) setsignal(SIGINT, intsig);
307 if (setsignal(SIGHUP, SIG_IGN) != SIG_IGN)
308 (void) setsignal(SIGHUP, intsig);
309 (void) setsignal(SIGTERM, intsig);
310 (void) setsignal(SIGPIPE, SIG_IGN);
6f14531a 311 OldUmask = umask(022);
15637ed4 312 OpMode = MD_DELIVER;
15637ed4
RG
313 FullName = getenv("NAME");
314
6f14531a
RG
315#ifdef NAMED_BIND
316 if (tTd(8, 8))
317 _res.options |= RES_DEBUG;
318#endif
319
15637ed4
RG
320 errno = 0;
321 from = NULL;
322
d747e748
JH
323 /* initialize some macros, etc. */
324 initmacros(CurEnv);
15637ed4 325
d747e748
JH
326 /* version */
327 define('v', Version, CurEnv);
6f14531a
RG
328
329 /* hostname */
330 av = myhostname(jbuf, sizeof jbuf);
331 if (jbuf[0] != '\0')
332 {
333 struct utsname utsname;
334
335 if (tTd(0, 4))
336 printf("canonical name: %s\n", jbuf);
337 p = newstr(jbuf);
d747e748
JH
338 define('w', newstr(jbuf), CurEnv); /* must be new string */
339 define('j', p, CurEnv);
6f14531a
RG
340 setclass('w', p);
341
d747e748
JH
342 p = strchr(jbuf, '.');
343 if (p != NULL)
15637ed4 344 {
d747e748
JH
345 if (p[1] != '\0')
346 {
347 define('m', newstr(&p[1]), CurEnv);
348 setclass('m', &p[1]);
349 }
350 while (p != NULL && strchr(&p[1], '.') != NULL)
351 {
352 *p = '\0';
353 setclass('w', jbuf);
354 *p++ = '.';
355 p = strchr(p, '.');
356 }
15637ed4 357 }
6f14531a
RG
358
359 if (uname(&utsname) >= 0)
360 p = utsname.nodename;
361 else
15637ed4 362 {
6f14531a
RG
363 makelower(jbuf);
364 p = jbuf;
15637ed4 365 }
6f14531a
RG
366 if (tTd(0, 4))
367 printf("UUCP nodename: %s\n", p);
368 p = newstr(p);
369 define('k', p, CurEnv);
370 setclass('w', p);
371 }
372 while (av != NULL && *av != NULL)
373 {
374 if (tTd(0, 4))
375 printf("\ta.k.a.: %s\n", *av);
376 setclass('w', *av++);
15637ed4
RG
377 }
378
379 /* current time */
380 define('b', arpadate((char *) NULL), CurEnv);
381
6f14531a
RG
382 /*
383 ** Find our real host name for future logging.
384 */
385
386 p = getauthinfo(STDIN_FILENO);
387 define('_', p, CurEnv);
388
15637ed4
RG
389 /*
390 ** Crack argv.
391 */
392
393 av = argv;
6f14531a 394 p = strrchr(*av, '/');
15637ed4
RG
395 if (p++ == NULL)
396 p = *av;
397 if (strcmp(p, "newaliases") == 0)
398 OpMode = MD_INITALIAS;
399 else if (strcmp(p, "mailq") == 0)
400 OpMode = MD_PRINT;
401 else if (strcmp(p, "smtpd") == 0)
402 OpMode = MD_DAEMON;
6f14531a
RG
403
404 optind = 1;
405 while ((j = getopt(argc, argv, OPTIONS)) != EOF)
15637ed4 406 {
6f14531a 407 switch (j)
15637ed4
RG
408 {
409 case 'b': /* operations mode */
6f14531a 410 switch (j = *optarg)
15637ed4
RG
411 {
412 case MD_DAEMON:
413# ifdef DAEMON
3a363396 414 if (RealUid != 0) {
15637ed4
RG
415 usrerr("Permission denied");
416 exit (EX_USAGE);
417 }
418 (void) unsetenv("HOSTALIASES");
419# else
420 usrerr("Daemon mode not implemented");
421 ExitStat = EX_USAGE;
422 break;
6f14531a 423# endif /* DAEMON */
15637ed4
RG
424 case MD_SMTP:
425# ifndef SMTP
426 usrerr("I don't speak SMTP");
427 ExitStat = EX_USAGE;
428 break;
6f14531a 429# endif /* SMTP */
15637ed4
RG
430 case MD_DELIVER:
431 case MD_VERIFY:
432 case MD_TEST:
433 case MD_INITALIAS:
434 case MD_PRINT:
6f14531a 435 OpMode = j;
15637ed4
RG
436 break;
437
6f14531a
RG
438 case MD_FREEZE:
439 usrerr("Frozen configurations unsupported");
440 ExitStat = EX_USAGE;
441 break;
6f14531a 442
15637ed4 443 default:
6f14531a 444 usrerr("Invalid operation mode %c", j);
15637ed4
RG
445 ExitStat = EX_USAGE;
446 break;
447 }
448 break;
449
6f14531a
RG
450 case 'B': /* body type */
451 CurEnv->e_bodytype = newstr(optarg);
452 break;
453
15637ed4 454 case 'C': /* select configuration file (already done) */
3a363396 455 if (RealUid != 0)
d747e748
JH
456 warn_C_flag = TRUE;
457 ConfFile = optarg;
458 (void) setgid(RealGid);
459 (void) setuid(RealUid);
460 safecf = FALSE;
15637ed4
RG
461 break;
462
d747e748 463 case 'd': /* debugging -- already done */
15637ed4
RG
464 break;
465
466 case 'f': /* from address */
467 case 'r': /* obsolete -f flag */
15637ed4
RG
468 if (from != NULL)
469 {
470 usrerr("More than one \"from\" person");
471 ExitStat = EX_USAGE;
472 break;
473 }
6f14531a
RG
474 from = newstr(optarg);
475 if (strcmp(RealUserName, from) != 0)
d747e748 476 warn_f_flag = j;
15637ed4
RG
477 break;
478
479 case 'F': /* set full name */
6f14531a 480 FullName = newstr(optarg);
15637ed4
RG
481 break;
482
483 case 'h': /* hop count */
6f14531a
RG
484 CurEnv->e_hopcount = strtol(optarg, &ep, 10);
485 if (*ep)
15637ed4 486 {
6f14531a 487 usrerr("Bad hop count (%s)", optarg);
15637ed4 488 ExitStat = EX_USAGE;
15637ed4
RG
489 break;
490 }
15637ed4
RG
491 break;
492
493 case 'n': /* don't alias */
494 NoAlias = TRUE;
495 break;
496
497 case 'o': /* set option */
6f14531a
RG
498 setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
499 break;
500
501 case 'p': /* set protocol */
d747e748
JH
502 p = strchr(optarg, ':');
503 if (p != NULL)
504 *p++ = '\0';
6f14531a
RG
505 if (*optarg != '\0')
506 define('r', newstr(optarg), CurEnv);
d747e748
JH
507 if (p != NULL && *p != '\0')
508 define('s', newstr(p), CurEnv);
15637ed4
RG
509 break;
510
511 case 'q': /* run queue files at intervals */
512# ifdef QUEUE
15637ed4 513 (void) unsetenv("HOSTALIASES");
6f14531a 514 FullName = NULL;
15637ed4 515 queuemode = TRUE;
6f14531a
RG
516 switch (optarg[0])
517 {
518 case 'I':
519 QueueLimitId = newstr(&optarg[1]);
520 break;
521
522 case 'R':
523 QueueLimitRecipient = newstr(&optarg[1]);
524 break;
525
526 case 'S':
527 QueueLimitSender = newstr(&optarg[1]);
528 break;
529
530 default:
531 QueueIntvl = convtime(optarg, 'm');
532 break;
533 }
534# else /* QUEUE */
15637ed4
RG
535 usrerr("I don't know about queues");
536 ExitStat = EX_USAGE;
6f14531a 537# endif /* QUEUE */
15637ed4
RG
538 break;
539
540 case 't': /* read recipients from message */
541 GrabTo = TRUE;
542 break;
543
3a363396
NW
544 case 'X': /* traffic log file */
545 setuid(RealUid);
546 TrafficLogFile = fopen(optarg, "a");
547 if (TrafficLogFile == NULL)
548 {
549 syserr("cannot open %s", optarg);
550 break;
551 }
552#ifdef HASSETVBUF
d747e748 553 setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
3a363396
NW
554#else
555 setlinebuf(TrafficLogFile);
556#endif
557 break;
558
15637ed4
RG
559 /* compatibility flags */
560 case 'c': /* connect to non-local mailers */
15637ed4
RG
561 case 'i': /* don't let dot stop me */
562 case 'm': /* send to me too */
563 case 'T': /* set timeout interval */
564 case 'v': /* give blow-by-blow description */
6f14531a
RG
565 setoption(j, "T", FALSE, TRUE, CurEnv);
566 break;
567
568 case 'e': /* error message disposition */
d747e748
JH
569# if defined(ultrix)
570 case 'M': /* define macro */
571# endif
6f14531a 572 setoption(j, optarg, FALSE, TRUE, CurEnv);
15637ed4
RG
573 break;
574
575 case 's': /* save From lines in headers */
6f14531a 576 setoption('f', "T", FALSE, TRUE, CurEnv);
15637ed4
RG
577 break;
578
579# ifdef DBM
580 case 'I': /* initialize alias DBM file */
581 OpMode = MD_INITALIAS;
582 break;
6f14531a
RG
583# endif /* DBM */
584
3a363396
NW
585# if defined(__osf__) || defined(_AIX3)
586 case 'x': /* random flag that OSF/1 & AIX mailx passes */
6f14531a
RG
587 break;
588# endif
589
590 default:
591 ExitStat = EX_USAGE;
592 finis();
593 break;
15637ed4
RG
594 }
595 }
6f14531a 596 av += optind;
15637ed4
RG
597
598 /*
599 ** Do basic initialization.
600 ** Read system control file.
601 ** Extract special fields for local use.
602 */
603
d747e748
JH
604 readcf(getcfname(), safecf, CurEnv);
605
606 if (tTd(0, 1))
607 {
608 printf("SYSTEM IDENTITY (after readcf):");
609 printf("\n\t (short domain name) $w = ");
610 xputs(macvalue('w', CurEnv));
611 printf("\n\t(canonical domain name) $j = ");
612 xputs(macvalue('j', CurEnv));
613 printf("\n\t (subdomain name) $m = ");
614 xputs(macvalue('m', CurEnv));
615 printf("\n\t (node name) $k = ");
616 xputs(macvalue('k', CurEnv));
617 printf("\n");
618 }
619
620 /*
621 ** Process authorization warnings from command line.
622 */
623
624 if (warn_C_flag)
625 auth_warning(CurEnv, "Processed by %s with -C %s",
626 RealUserName, ConfFile);
627/*
628 if (warn_f_flag != '\0')
629 auth_warning(CurEnv, "%s set sender to %s using -%c",
630 RealUserName, from, warn_f_flag);
631*/
632 if (Warn_Q_option)
633 auth_warning(CurEnv, "Processed from queue %s", QueueDir);
6f14531a 634
6f14531a
RG
635 /* Enforce use of local time (null string overrides this) */
636 if (TimeZoneSpec == NULL)
637 unsetenv("TZ");
638 else if (TimeZoneSpec[0] != '\0')
639 {
d747e748
JH
640 char **evp = UserEnviron;
641 char tzbuf[100];
642
643 strcpy(tzbuf, "TZ=");
644 strcpy(&tzbuf[3], TimeZoneSpec);
645
646 while (*evp != NULL && strncmp(*evp, "TZ=", 3) != 0)
647 evp++;
648 if (*evp == NULL)
649 {
650 *evp++ = newstr(tzbuf);
651 *evp = NULL;
652 }
653 else
654 *evp++ = newstr(tzbuf);
6f14531a 655 }
6f14531a
RG
656
657 if (ConfigLevel > MAXCONFIGLEVEL)
658 {
659 syserr("Warning: .cf version level (%d) exceeds program functionality (%d)",
660 ConfigLevel, MAXCONFIGLEVEL);
661 }
3a363396 662
d747e748
JH
663 if (MeToo)
664 BlankEnvelope.e_flags |= EF_METOO;
3a363396 665
6f14531a 666# ifdef QUEUE
d747e748 667 if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
6f14531a
RG
668 {
669 struct stat stbuf;
670
671 /* check to see if we own the queue directory */
672 if (stat(QueueDir, &stbuf) < 0)
673 syserr("main: cannot stat %s", QueueDir);
3a363396 674 if (stbuf.st_uid != RealUid)
6f14531a
RG
675 {
676 /* nope, really a botch */
d747e748 677 usrerr("You do not have permission to process the queue");
6f14531a
RG
678 exit (EX_NOPERM);
679 }
680 }
681# endif /* QUEUE */
15637ed4
RG
682
683 switch (OpMode)
684 {
15637ed4
RG
685 case MD_INITALIAS:
686 Verbose = TRUE;
687 break;
6f14531a
RG
688
689 case MD_DAEMON:
690 /* remove things that don't make sense in daemon mode */
691 FullName = NULL;
692 break;
693
694 case MD_SMTP:
695 if (RealUid != 0)
696 auth_warning(CurEnv,
697 "%s owned process doing -bs",
698 RealUserName);
699 break;
15637ed4
RG
700 }
701
702 /* do heuristic mode adjustment */
703 if (Verbose)
704 {
705 /* turn off noconnect option */
6f14531a 706 setoption('c', "F", TRUE, FALSE, CurEnv);
15637ed4
RG
707
708 /* turn on interactive delivery */
6f14531a 709 setoption('d', "", TRUE, FALSE, CurEnv);
15637ed4
RG
710 }
711
3a363396
NW
712 if (ConfigLevel < 3)
713 {
714 UseErrorsTo = TRUE;
715 }
716
15637ed4 717 /* our name for SMTP codes */
6f14531a 718 expand("\201j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
15637ed4
RG
719 MyHostName = jbuf;
720
d747e748
JH
721 /* make certain that this name is part of the $=w class */
722 setclass('w', MyHostName);
723
6f14531a 724 /* the indices of built-in mailers */
15637ed4
RG
725 st = stab("local", ST_MAILER, ST_FIND);
726 if (st == NULL)
727 syserr("No local mailer defined");
728 else
729 LocalMailer = st->s_mailer;
6f14531a 730
15637ed4
RG
731 st = stab("prog", ST_MAILER, ST_FIND);
732 if (st == NULL)
733 syserr("No prog mailer defined");
734 else
735 ProgMailer = st->s_mailer;
736
6f14531a
RG
737 st = stab("*file*", ST_MAILER, ST_FIND);
738 if (st == NULL)
739 syserr("No *file* mailer defined");
740 else
741 FileMailer = st->s_mailer;
742
743 st = stab("*include*", ST_MAILER, ST_FIND);
744 if (st == NULL)
745 syserr("No *include* mailer defined");
746 else
747 InclMailer = st->s_mailer;
748
749
15637ed4 750 /* operate in queue directory */
d747e748 751 if (OpMode != MD_TEST && chdir(QueueDir) < 0)
15637ed4
RG
752 {
753 syserr("cannot chdir(%s)", QueueDir);
3a363396 754 ExitStat = EX_SOFTWARE;
15637ed4
RG
755 }
756
6f14531a 757 /* if we've had errors so far, exit now */
3a363396
NW
758 if (ExitStat != EX_OK && OpMode != MD_TEST)
759 {
760 setuid(RealUid);
6f14531a 761 exit(ExitStat);
3a363396 762 }
6f14531a 763
15637ed4
RG
764 /*
765 ** Do operation-mode-dependent initialization.
766 */
767
768 switch (OpMode)
769 {
770 case MD_PRINT:
771 /* print the queue */
772#ifdef QUEUE
773 dropenvelope(CurEnv);
774 printqueue();
3a363396 775 setuid(RealUid);
15637ed4 776 exit(EX_OK);
6f14531a 777#else /* QUEUE */
15637ed4
RG
778 usrerr("No queue to print");
779 finis();
6f14531a 780#endif /* QUEUE */
15637ed4
RG
781
782 case MD_INITALIAS:
783 /* initialize alias database */
6f14531a 784 initmaps(TRUE, CurEnv);
3a363396 785 setuid(RealUid);
15637ed4
RG
786 exit(EX_OK);
787
788 case MD_DAEMON:
789 /* don't open alias database -- done in srvrsmtp */
790 break;
791
792 default:
793 /* open the alias database */
6f14531a 794 initmaps(FALSE, CurEnv);
15637ed4
RG
795 break;
796 }
797
798 if (tTd(0, 15))
799 {
800 /* print configuration table (or at least part of it) */
801 printrules();
802 for (i = 0; i < MAXMAILERS; i++)
803 {
804 register struct mailer *m = Mailer[i];
805 int j;
806
807 if (m == NULL)
808 continue;
6f14531a
RG
809 printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld F=", i, m->m_name,
810 m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
811 m->m_re_rwset, m->m_rh_rwset, m->m_maxsize);
15637ed4
RG
812 for (j = '\0'; j <= '\177'; j++)
813 if (bitnset(j, m->m_flags))
814 (void) putchar(j);
815 printf(" E=");
816 xputs(m->m_eol);
6f14531a
RG
817 if (m->m_argv != NULL)
818 {
819 char **a = m->m_argv;
820
821 printf(" A=");
822 while (*a != NULL)
823 {
824 if (a != m->m_argv)
825 printf(" ");
826 xputs(*a++);
827 }
828 }
15637ed4
RG
829 printf("\n");
830 }
831 }
832
833 /*
834 ** Switch to the main envelope.
835 */
836
6f14531a 837 CurEnv = newenvelope(&MainEnvelope, CurEnv);
15637ed4
RG
838 MainEnvelope.e_flags = BlankEnvelope.e_flags;
839
840 /*
841 ** If test mode, read addresses from stdin and process.
842 */
843
844 if (OpMode == MD_TEST)
845 {
846 char buf[MAXLINE];
847
6f14531a
RG
848 if (isatty(fileno(stdin)))
849 Verbose = TRUE;
850
851 if (Verbose)
852 {
853 printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
854 printf("Enter <ruleset> <address>\n");
855 }
15637ed4
RG
856 for (;;)
857 {
858 register char **pvp;
859 char *q;
6f14531a
RG
860 auto char *delimptr;
861 extern bool invalidaddr();
d747e748 862 extern char *crackaddr();
15637ed4 863
6f14531a
RG
864 if (Verbose)
865 printf("> ");
15637ed4
RG
866 (void) fflush(stdout);
867 if (fgets(buf, sizeof buf, stdin) == NULL)
868 finis();
6f14531a
RG
869 if (!Verbose)
870 printf("> %s", buf);
d747e748
JH
871 switch (buf[0])
872 {
873 case '#':
6f14531a 874 continue;
d747e748
JH
875
876#ifdef MAYBENEXTRELEASE
877 case 'C': /* try crackaddr */
878 q = crackaddr(&buf[1]);
879 xputs(q);
880 printf("\n");
881 continue;
882#endif
883 }
884
6f14531a 885 for (p = buf; isascii(*p) && isspace(*p); p++)
15637ed4
RG
886 continue;
887 q = p;
6f14531a 888 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
15637ed4
RG
889 p++;
890 if (*p == '\0')
6f14531a
RG
891 {
892 printf("No address!\n");
15637ed4 893 continue;
6f14531a 894 }
15637ed4 895 *p = '\0';
d747e748 896 if (invalidaddr(p + 1, NULL))
6f14531a 897 continue;
15637ed4
RG
898 do
899 {
15637ed4
RG
900 char pvpbuf[PSBUFSIZE];
901
6f14531a 902 pvp = prescan(++p, ',', pvpbuf, &delimptr);
15637ed4
RG
903 if (pvp == NULL)
904 continue;
15637ed4
RG
905 p = q;
906 while (*p != '\0')
907 {
6f14531a
RG
908 int stat;
909
910 stat = rewrite(pvp, atoi(p), CurEnv);
911 if (stat != EX_OK)
912 printf("== Ruleset %s status %d\n",
913 p, stat);
15637ed4
RG
914 while (*p != '\0' && *p++ != ',')
915 continue;
916 }
6f14531a 917 } while (*(p = delimptr) != '\0');
15637ed4
RG
918 }
919 }
920
921# ifdef QUEUE
922 /*
923 ** If collecting stuff from the queue, go start doing that.
924 */
925
926 if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
927 {
928 runqueue(FALSE);
929 finis();
930 }
6f14531a 931# endif /* QUEUE */
15637ed4
RG
932
933 /*
934 ** If a daemon, wait for a request.
935 ** getrequests will always return in a child.
936 ** If we should also be processing the queue, start
937 ** doing it in background.
938 ** We check for any errors that might have happened
939 ** during startup.
940 */
941
942 if (OpMode == MD_DAEMON || QueueIntvl != 0)
943 {
6f14531a
RG
944 char dtype[200];
945
15637ed4
RG
946 if (!tTd(0, 1))
947 {
948 /* put us in background */
949 i = fork();
950 if (i < 0)
951 syserr("daemon: cannot fork");
952 if (i != 0)
953 exit(0);
954
15637ed4 955 /* disconnect from our controlling tty */
d747e748 956 disconnect(2, CurEnv);
15637ed4
RG
957 }
958
6f14531a
RG
959 dtype[0] = '\0';
960 if (OpMode == MD_DAEMON)
961 strcat(dtype, "+SMTP");
962 if (QueueIntvl != 0)
963 {
964 strcat(dtype, "+queueing@");
965 strcat(dtype, pintvl(QueueIntvl, TRUE));
966 }
967 if (tTd(0, 1))
968 strcat(dtype, "+debugging");
969
970 syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1);
971#ifdef XLA
972 xla_create_file();
973#endif
974
15637ed4
RG
975# ifdef QUEUE
976 if (queuemode)
977 {
978 runqueue(TRUE);
979 if (OpMode != MD_DAEMON)
980 for (;;)
981 pause();
982 }
6f14531a 983# endif /* QUEUE */
15637ed4
RG
984 dropenvelope(CurEnv);
985
986#ifdef DAEMON
987 getrequests();
988
989 /* at this point we are in a child: reset state */
6f14531a
RG
990 (void) newenvelope(CurEnv, CurEnv);
991
992 /*
993 ** Get authentication data
994 */
995
996 p = getauthinfo(fileno(InChannel));
997 define('_', p, CurEnv);
998
999#endif /* DAEMON */
15637ed4
RG
1000 }
1001
1002# ifdef SMTP
1003 /*
1004 ** If running SMTP protocol, start collecting and executing
1005 ** commands. This will never return.
1006 */
1007
1008 if (OpMode == MD_SMTP)
6f14531a
RG
1009 smtp(CurEnv);
1010# endif /* SMTP */
15637ed4 1011
d747e748
JH
1012 if (OpMode == MD_VERIFY)
1013 {
1014 CurEnv->e_sendmode = SM_VERIFY;
1015 CurEnv->e_errormode = EM_QUIET;
1016 }
1017 else
1018 {
1019 /* interactive -- all errors are global */
1020 CurEnv->e_flags |= EF_GLOBALERRS;
1021 }
1022
15637ed4
RG
1023 /*
1024 ** Do basic system initialization and set the sender
1025 */
1026
6f14531a
RG
1027 initsys(CurEnv);
1028 setsender(from, CurEnv, NULL, FALSE);
1029 if (macvalue('s', CurEnv) == NULL)
1030 define('s', RealHostName, CurEnv);
15637ed4 1031
6f14531a 1032 if (*av == NULL && !GrabTo)
15637ed4 1033 {
d747e748 1034 CurEnv->e_flags |= EF_GLOBALERRS;
15637ed4
RG
1035 usrerr("Recipient names must be specified");
1036
1037 /* collect body for UUCP return */
1038 if (OpMode != MD_VERIFY)
6f14531a 1039 collect(FALSE, FALSE, CurEnv);
15637ed4
RG
1040 finis();
1041 }
15637ed4
RG
1042
1043 /*
1044 ** Scan argv and deliver the message to everyone.
1045 */
1046
6f14531a 1047 sendtoargv(av, CurEnv);
15637ed4
RG
1048
1049 /* if we have had errors sofar, arrange a meaningful exit stat */
1050 if (Errors > 0 && ExitStat == EX_OK)
1051 ExitStat = EX_USAGE;
1052
1053 /*
1054 ** Read the input mail.
1055 */
1056
1057 CurEnv->e_to = NULL;
1058 if (OpMode != MD_VERIFY || GrabTo)
d747e748
JH
1059 {
1060 CurEnv->e_flags |= EF_GLOBALERRS;
6f14531a 1061 collect(FALSE, FALSE, CurEnv);
d747e748 1062 }
15637ed4
RG
1063 errno = 0;
1064
15637ed4
RG
1065 if (tTd(1, 1))
1066 printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1067
1068 /*
1069 ** Actually send everything.
1070 ** If verifying, just ack.
1071 */
1072
1073 CurEnv->e_from.q_flags |= QDONTSEND;
6f14531a
RG
1074 if (tTd(1, 5))
1075 {
1076 printf("main: QDONTSEND ");
1077 printaddr(&CurEnv->e_from, FALSE);
1078 }
15637ed4
RG
1079 CurEnv->e_to = NULL;
1080 sendall(CurEnv, SM_DEFAULT);
1081
1082 /*
6f14531a
RG
1083 ** All done.
1084 ** Don't send return error message if in VERIFY mode.
15637ed4
RG
1085 */
1086
1087 finis();
1088}
1089\f/*
1090** FINIS -- Clean up and exit.
1091**
1092** Parameters:
1093** none
1094**
1095** Returns:
1096** never
1097**
1098** Side Effects:
1099** exits sendmail
1100*/
1101
1102finis()
1103{
1104 if (tTd(2, 1))
d747e748
JH
1105 printf("\n====finis: stat %d e_flags %o, e_id=%s\n",
1106 ExitStat, CurEnv->e_flags,
1107 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1108 if (tTd(2, 9))
1109 printopenfds(FALSE);
15637ed4
RG
1110
1111 /* clean up temp files */
1112 CurEnv->e_to = NULL;
1113 dropenvelope(CurEnv);
1114
6f14531a
RG
1115 /* flush any cached connections */
1116 mci_flush(TRUE, NULL);
1117
6f14531a
RG
1118# ifdef XLA
1119 /* clean up extended load average stuff */
1120 xla_all_end();
1121# endif
1122
15637ed4
RG
1123 /* and exit */
1124# ifdef LOG
6f14531a 1125 if (LogLevel > 78)
15637ed4 1126 syslog(LOG_DEBUG, "finis, pid=%d", getpid());
6f14531a 1127# endif /* LOG */
15637ed4
RG
1128 if (ExitStat == EX_TEMPFAIL)
1129 ExitStat = EX_OK;
3a363396
NW
1130
1131 /* reset uid for process accounting */
1132 setuid(RealUid);
1133
15637ed4
RG
1134 exit(ExitStat);
1135}
1136\f/*
1137** INTSIG -- clean up on interrupt
1138**
1139** This just arranges to exit. It pessimises in that it
1140** may resend a message.
1141**
1142** Parameters:
1143** none.
1144**
1145** Returns:
1146** none.
1147**
1148** Side Effects:
1149** Unlocks the current job.
1150*/
1151
1152void
1153intsig()
1154{
1155 FileName = NULL;
1156 unlockqueue(CurEnv);
6f14531a
RG
1157#ifdef XLA
1158 xla_all_end();
1159#endif
3a363396
NW
1160
1161 /* reset uid for process accounting */
1162 setuid(RealUid);
1163
15637ed4
RG
1164 exit(EX_OK);
1165}
1166\f/*
1167** INITMACROS -- initialize the macro system
1168**
1169** This just involves defining some macros that are actually
1170** used internally as metasymbols to be themselves.
1171**
1172** Parameters:
1173** none.
1174**
1175** Returns:
1176** none.
1177**
1178** Side Effects:
1179** initializes several macros to be themselves.
1180*/
1181
15637ed4
RG
1182struct metamac MetaMacros[] =
1183{
1184 /* LHS pattern matching characters */
6f14531a
RG
1185 '*', MATCHZANY, '+', MATCHANY, '-', MATCHONE,
1186 '=', MATCHCLASS, '~', MATCHNCLASS,
15637ed4
RG
1187
1188 /* these are RHS metasymbols */
6f14531a
RG
1189 '#', CANONNET, '@', CANONHOST, ':', CANONUSER,
1190 '>', CALLSUBR,
15637ed4
RG
1191
1192 /* the conditional operations */
6f14531a
RG
1193 '?', CONDIF, '|', CONDELSE, '.', CONDFI,
1194
1195 /* the hostname lookup characters */
1196 '[', HOSTBEGIN, ']', HOSTEND,
1197 '(', LOOKUPBEGIN, ')', LOOKUPEND,
15637ed4 1198
6f14531a
RG
1199 /* miscellaneous control characters */
1200 '&', MACRODEXPAND,
15637ed4
RG
1201
1202 '\0'
1203};
1204
d747e748
JH
1205initmacros(e)
1206 register ENVELOPE *e;
15637ed4
RG
1207{
1208 register struct metamac *m;
1209 char buf[5];
1210 register int c;
1211
1212 for (m = MetaMacros; m->metaname != '\0'; m++)
1213 {
1214 buf[0] = m->metaval;
1215 buf[1] = '\0';
d747e748 1216 define(m->metaname, newstr(buf), e);
15637ed4
RG
1217 }
1218 buf[0] = MATCHREPL;
1219 buf[2] = '\0';
1220 for (c = '0'; c <= '9'; c++)
1221 {
1222 buf[1] = c;
d747e748 1223 define(c, newstr(buf), e);
15637ed4
RG
1224 }
1225
d747e748
JH
1226 /* set defaults for some macros sendmail will use later */
1227 define('e', "\201j Sendmail \201v ready at \201b", e);
1228 define('l', "From \201g \201d", e);
1229 define('n', "MAILER-DAEMON", e);
1230 define('o', ".:@[]", e);
1231 define('q', "<\201g>", e);
15637ed4
RG
1232}
1233\f/*
15637ed4
RG
1234** DISCONNECT -- remove our connection with any foreground process
1235**
1236** Parameters:
d747e748
JH
1237** droplev -- how "deeply" we should drop the line.
1238** 0 -- ignore signals, mail back errors, make sure
1239** output goes to stdout.
1240** 1 -- also, make stdout go to transcript.
1241** 2 -- also, disconnect from controlling terminal
1242** (only for daemon mode).
1243** e -- the current envelope.
15637ed4
RG
1244**
1245** Returns:
1246** none
1247**
1248** Side Effects:
1249** Trys to insure that we are immune to vagaries of
1250** the controlling tty.
1251*/
1252
d747e748
JH
1253disconnect(droplev, e)
1254 int droplev;
6f14531a 1255 register ENVELOPE *e;
15637ed4
RG
1256{
1257 int fd;
1258
1259 if (tTd(52, 1))
6f14531a
RG
1260 printf("disconnect: In %d Out %d, e=%x\n",
1261 fileno(InChannel), fileno(OutChannel), e);
15637ed4
RG
1262 if (tTd(52, 5))
1263 {
1264 printf("don't\n");
1265 return;
1266 }
1267
1268 /* be sure we don't get nasty signals */
d747e748
JH
1269 (void) setsignal(SIGHUP, SIG_IGN);
1270 (void) setsignal(SIGINT, SIG_IGN);
1271 (void) setsignal(SIGQUIT, SIG_IGN);
15637ed4
RG
1272
1273 /* we can't communicate with our caller, so.... */
1274 HoldErrs = TRUE;
6f14531a 1275 CurEnv->e_errormode = EM_MAIL;
15637ed4
RG
1276 Verbose = FALSE;
1277
1278 /* all input from /dev/null */
1279 if (InChannel != stdin)
1280 {
1281 (void) fclose(InChannel);
1282 InChannel = stdin;
1283 }
1284 (void) freopen("/dev/null", "r", stdin);
1285
1286 /* output to the transcript */
1287 if (OutChannel != stdout)
1288 {
1289 (void) fclose(OutChannel);
1290 OutChannel = stdout;
1291 }
d747e748
JH
1292 if (droplev > 0)
1293 {
1294 if (e->e_xfp == NULL)
1295 fd = open("/dev/null", O_WRONLY, 0666);
1296 else
1297 fd = fileno(e->e_xfp);
1298 (void) fflush(stdout);
1299 dup2(fd, STDOUT_FILENO);
1300 dup2(fd, STDERR_FILENO);
1301 if (e->e_xfp == NULL)
1302 close(fd);
1303 }
15637ed4
RG
1304
1305 /* drop our controlling TTY completely if possible */
d747e748 1306 if (droplev > 1)
15637ed4 1307 {
6f14531a 1308 (void) setsid();
15637ed4
RG
1309 errno = 0;
1310 }
1311
d747e748
JH
1312#ifdef XDEBUG
1313 checkfd012("disconnect");
1314#endif
1315
15637ed4 1316# ifdef LOG
6f14531a 1317 if (LogLevel > 71)
15637ed4 1318 syslog(LOG_DEBUG, "in background, pid=%d", getpid());
6f14531a 1319# endif /* LOG */
15637ed4
RG
1320
1321 errno = 0;
1322}
6f14531a
RG
1323
1324static void
1325obsolete(argv)
1326 char *argv[];
1327{
1328 char *ap;
1329
1330 while ((ap = *++argv) != NULL)
1331 {
1332 /* Return if "--" or not an option of any form. */
1333 if (ap[0] != '-' || ap[1] == '-')
1334 return;
1335
1336 /* If -C doesn't have an argument, use sendmail.cf. */
1337#define __DEFPATH "sendmail.cf"
1338 if (ap[1] == 'C' && ap[2] == '\0' &&
1339 (argv[1] == NULL || argv[1][0] == '-'))
1340 {
1341 *argv = xalloc(sizeof(__DEFPATH) + 2);
1342 argv[0][0] = '-';
1343 argv[0][1] = 'C';
1344 (void)strcpy(&argv[0][2], __DEFPATH);
1345 }
1346
1347 /* If -q doesn't have an argument, run it once. */
1348 if (ap[1] == 'q' && ap[2] == '\0' &&
1349 (argv[1] == NULL || argv[1][0] == '-'))
1350 *argv = "-q0";
1351
1352 /* if -d doesn't have an argument, use 0-99.1 */
1353 if (ap[1] == 'd' && ap[2] == '\0' &&
1354 (argv[1] == NULL || !isdigit(argv[1][0])))
1355 *argv = "-d0-99.1";
1356 }
1357}
1358\f/*
1359** AUTH_WARNING -- specify authorization warning
1360**
1361** Parameters:
1362** e -- the current envelope.
1363** msg -- the text of the message.
1364** args -- arguments to the message.
1365**
1366** Returns:
1367** none.
1368*/
1369
1370void
1371#ifdef __STDC__
1372auth_warning(register ENVELOPE *e, const char *msg, ...)
1373#else
1374auth_warning(e, msg, va_alist)
1375 register ENVELOPE *e;
1376 const char *msg;
1377 va_dcl
1378#endif
1379{
1380 char buf[MAXLINE];
1381 VA_LOCAL_DECL
1382
1383 if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
1384 {
1385 register char *p;
1386 static char hostbuf[48];
1387 extern char **myhostname();
1388
1389 if (hostbuf[0] == '\0')
1390 (void) myhostname(hostbuf, sizeof hostbuf);
1391
1392 (void) sprintf(buf, "%s: ", hostbuf);
1393 p = &buf[strlen(buf)];
1394 VA_START(msg);
1395 vsprintf(p, msg, ap);
1396 VA_END;
1397 addheader("X-Authentication-Warning", buf, e);
1398 }
1399}