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