main.c revision 64562
1/* 2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14#ifndef lint 15static char copyright[] = 16"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\ 17 All rights reserved.\n\ 18 Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.\n\ 19 Copyright (c) 1988, 1993\n\ 20 The Regents of the University of California. All rights reserved.\n"; 21#endif /* ! lint */ 22 23#ifndef lint 24static char id[] = "@(#)$Id: main.c,v 8.485.4.19 2000/06/29 01:31:02 gshapiro Exp $"; 25#endif /* ! lint */ 26 27#define _DEFINE 28 29#include <sendmail.h> 30 31 32#if NETINET || NETINET6 33# include <arpa/inet.h> 34#endif /* NETINET || NETINET6 */ 35 36static void dump_class __P((STAB *, int)); 37static void obsolete __P((char **)); 38static void testmodeline __P((char *, ENVELOPE *)); 39 40/* 41** SENDMAIL -- Post mail to a set of destinations. 42** 43** This is the basic mail router. All user mail programs should 44** call this routine to actually deliver mail. Sendmail in 45** turn calls a bunch of mail servers that do the real work of 46** delivering the mail. 47** 48** Sendmail is driven by settings read in from /etc/mail/sendmail.cf 49** (read by readcf.c). 50** 51** Usage: 52** /usr/lib/sendmail [flags] addr ... 53** 54** See the associated documentation for details. 55** 56** Author: 57** Eric Allman, UCB/INGRES (until 10/81). 58** Britton-Lee, Inc., purveyors of fine 59** database computers (11/81 - 10/88). 60** International Computer Science Institute 61** (11/88 - 9/89). 62** UCB/Mammoth Project (10/89 - 7/95). 63** InReference, Inc. (8/95 - 1/97). 64** Sendmail, Inc. (1/98 - present). 65** The support of the my employers is gratefully acknowledged. 66** Few of them (Britton-Lee in particular) have had 67** anything to gain from my involvement in this project. 68*/ 69 70 71int NextMailer; /* "free" index into Mailer struct */ 72char *FullName; /* sender's full name */ 73ENVELOPE BlankEnvelope; /* a "blank" envelope */ 74static ENVELOPE MainEnvelope; /* the envelope around the basic letter */ 75ADDRESS NullAddress = /* a null address */ 76 { "", "", NULL, "" }; 77char *CommandLineArgs; /* command line args for pid file */ 78bool Warn_Q_option = FALSE; /* warn about Q option use */ 79char **SaveArgv; /* argument vector for re-execing */ 80static int MissingFds = 0; /* bit map of fds missing on startup */ 81 82#ifdef NGROUPS_MAX 83GIDSET_T InitialGidSet[NGROUPS_MAX]; 84#endif /* NGROUPS_MAX */ 85 86#if DAEMON && !SMTP 87ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR 88#endif /* DAEMON && !SMTP */ 89#if SMTP && !QUEUE 90ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR 91#endif /* SMTP && !QUEUE */ 92 93#define MAXCONFIGLEVEL 9 /* highest config version level known */ 94 95#if SASL 96static sasl_callback_t srvcallbacks[] = 97{ 98 { SASL_CB_VERIFYFILE, &safesaslfile, NULL }, 99 { SASL_CB_PROXY_POLICY, &proxy_policy, NULL }, 100 { SASL_CB_LIST_END, NULL, NULL } 101}; 102#endif /* SASL */ 103 104int SubmitMode; 105 106int 107main(argc, argv, envp) 108 int argc; 109 char **argv; 110 char **envp; 111{ 112 register char *p; 113 char **av; 114 extern char Version[]; 115 char *ep, *from; 116 STAB *st; 117 register int i; 118 int j; 119 int dp; 120 bool safecf = TRUE; 121 BITMAP256 *p_flags = NULL; /* daemon flags */ 122 bool warn_C_flag = FALSE; 123 bool auth = TRUE; /* whether to set e_auth_param */ 124 char warn_f_flag = '\0'; 125 bool run_in_foreground = FALSE; /* -bD mode */ 126 static bool reenter = FALSE; 127 struct passwd *pw; 128 struct hostent *hp; 129 char *nullserver = NULL; 130 char *authinfo = NULL; 131 char *sysloglabel = NULL; /* label for syslog */ 132 bool forged; 133 struct stat traf_st; /* for TrafficLog FIFO check */ 134 char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ 135 static char rnamebuf[MAXNAME]; /* holds RealUserName */ 136 char *emptyenviron[1]; 137 QUEUE_CHAR *new; 138 extern int DtableSize; 139 extern int optind; 140 extern int opterr; 141 extern char *optarg; 142 extern char **environ; 143 144 /* 145 ** Check to see if we reentered. 146 ** This would normally happen if e_putheader or e_putbody 147 ** were NULL when invoked. 148 */ 149 150 if (reenter) 151 { 152 syserr("main: reentered!"); 153 abort(); 154 } 155 reenter = TRUE; 156 157 /* avoid null pointer dereferences */ 158 TermEscape.te_rv_on = TermEscape.te_rv_off = ""; 159 160 /* do machine-dependent initializations */ 161 init_md(argc, argv); 162 163 164 /* in 4.4BSD, the table can be huge; impose a reasonable limit */ 165 DtableSize = getdtsize(); 166 if (DtableSize > 256) 167 DtableSize = 256; 168 169 /* 170 ** Be sure we have enough file descriptors. 171 ** But also be sure that 0, 1, & 2 are open. 172 */ 173 174 fill_fd(STDIN_FILENO, NULL); 175 fill_fd(STDOUT_FILENO, NULL); 176 fill_fd(STDERR_FILENO, NULL); 177 178 i = DtableSize; 179 while (--i > 0) 180 { 181 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) 182 (void) close(i); 183 } 184 errno = 0; 185 186#if LOG 187# ifdef LOG_MAIL 188 openlog("sendmail", LOG_PID, LOG_MAIL); 189# else /* LOG_MAIL */ 190 openlog("sendmail", LOG_PID); 191# endif /* LOG_MAIL */ 192#endif /* LOG */ 193 194 if (MissingFds != 0) 195 { 196 char mbuf[MAXLINE]; 197 198 mbuf[0] = '\0'; 199 if (bitset(1 << STDIN_FILENO, MissingFds)) 200 (void) strlcat(mbuf, ", stdin", sizeof mbuf); 201 if (bitset(1 << STDOUT_FILENO, MissingFds)) 202 (void) strlcat(mbuf, ", stdout", sizeof mbuf); 203 if (bitset(1 << STDERR_FILENO, MissingFds)) 204 (void) strlcat(mbuf, ", stderr", sizeof mbuf); 205 syserr("File descriptors missing on startup: %s", &mbuf[2]); 206 } 207 208 /* reset status from syserr() calls for missing file descriptors */ 209 Errors = 0; 210 ExitStat = EX_OK; 211 212 SubmitMode = SUBMIT_UNKNOWN; 213#if XDEBUG 214 checkfd012("after openlog"); 215#endif /* XDEBUG */ 216 217 /* 218 ** Seed the random number generator. 219 ** Used for queue file names, picking a queue directory, and 220 ** MX randomization. 221 */ 222 223 seed_random(); 224 225 tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 226 227#ifdef NGROUPS_MAX 228 /* save initial group set for future checks */ 229 i = getgroups(NGROUPS_MAX, InitialGidSet); 230 if (i == 0) 231 InitialGidSet[0] = (GID_T) -1; 232 while (i < NGROUPS_MAX) 233 InitialGidSet[i++] = InitialGidSet[0]; 234#endif /* NGROUPS_MAX */ 235 236 /* drop group id privileges (RunAsUser not yet set) */ 237 dp = drop_privileges(FALSE); 238 setstat(dp); 239 240# ifdef SIGUSR1 241 /* arrange to dump state on user-1 signal */ 242 (void) setsignal(SIGUSR1, sigusr1); 243# endif /* SIGUSR1 */ 244 245 /* initialize for setproctitle */ 246 initsetproctitle(argc, argv, envp); 247 248 /* Handle any non-getoptable constructions. */ 249 obsolete(argv); 250 251 /* 252 ** Do a quick prescan of the argument list. 253 */ 254 255 256#if defined(__osf__) || defined(_AIX3) 257# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x" 258#endif /* defined(__osf__) || defined(_AIX3) */ 259#if defined(sony_news) 260# define OPTIONS "B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:" 261#endif /* defined(sony_news) */ 262#ifndef OPTIONS 263# define OPTIONS "B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:" 264#endif /* ! OPTIONS */ 265 opterr = 0; 266 while ((j = getopt(argc, argv, OPTIONS)) != -1) 267 { 268 switch (j) 269 { 270 case 'd': 271 /* hack attack -- see if should use ANSI mode */ 272 if (strcmp(optarg, "ANSI") == 0) 273 { 274 TermEscape.te_rv_on = "\033[7m"; 275 TermEscape.te_rv_off = "\033[0m"; 276 break; 277 } 278 tTflag(optarg); 279 setbuf(stdout, (char *) NULL); 280 break; 281 282 case 'G': /* relay (gateway) submission */ 283 SubmitMode |= SUBMIT_MTA; 284 break; 285 286 case 'L': 287 j = min(strlen(optarg), 24) + 1; 288 sysloglabel = xalloc(j); 289 (void) strlcpy(sysloglabel, optarg, j); 290 break; 291 292 case 'U': /* initial (user) submission */ 293 SubmitMode |= SUBMIT_MSA; 294 break; 295 } 296 } 297 opterr = 1; 298 299 if (sysloglabel != NULL) 300 { 301#if LOG 302 closelog(); 303# ifdef LOG_MAIL 304 openlog(sysloglabel, LOG_PID, LOG_MAIL); 305# else /* LOG_MAIL */ 306 openlog(sysloglabel, LOG_PID); 307# endif /* LOG_MAIL */ 308#endif /* LOG */ 309 } 310 311 312 /* set up the blank envelope */ 313 BlankEnvelope.e_puthdr = putheader; 314 BlankEnvelope.e_putbody = putbody; 315 BlankEnvelope.e_xfp = NULL; 316 STRUCTCOPY(NullAddress, BlankEnvelope.e_from); 317 CurEnv = &BlankEnvelope; 318 STRUCTCOPY(NullAddress, MainEnvelope.e_from); 319 320 /* 321 ** Set default values for variables. 322 ** These cannot be in initialized data space. 323 */ 324 325 setdefaults(&BlankEnvelope); 326 327 RealUid = getuid(); 328 RealGid = getgid(); 329 330 pw = sm_getpwuid(RealUid); 331 if (pw != NULL) 332 (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); 333 else 334 (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", 335 (int) RealUid); 336 337 RealUserName = rnamebuf; 338 339 if (tTd(0, 101)) 340 { 341 dprintf("Version %s\n", Version); 342 finis(FALSE, EX_OK); 343 } 344 345 /* 346 ** if running non-setuid binary as non-root, pretend 347 ** we are the RunAsUid 348 */ 349 if (RealUid != 0 && geteuid() == RealUid) 350 { 351 if (tTd(47, 1)) 352 dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n", 353 (int)RealUid); 354 RunAsUid = RealUid; 355 } 356 else if (geteuid() != 0) 357 RunAsUid = geteuid(); 358 359 if (RealUid != 0 && getegid() == RealGid) 360 RunAsGid = RealGid; 361 362 if (tTd(47, 5)) 363 { 364 dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n", 365 (int)geteuid(), (int)getuid(), 366 (int)getegid(), (int)getgid()); 367 dprintf("main: RunAsUser = %d:%d\n", 368 (int)RunAsUid, (int)RunAsGid); 369 } 370 371 /* save command line arguments */ 372 j = 0; 373 for (av = argv; *av != NULL; ) 374 j += strlen(*av++) + 1; 375 SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); 376 CommandLineArgs = xalloc(j); 377 p = CommandLineArgs; 378 for (av = argv, i = 0; *av != NULL; ) 379 { 380 int h; 381 382 SaveArgv[i++] = newstr(*av); 383 if (av != argv) 384 *p++ = ' '; 385 (void) strlcpy(p, *av++, j); 386 h = strlen(p); 387 p += h; 388 j -= h + 1; 389 } 390 SaveArgv[i] = NULL; 391 392 if (tTd(0, 1)) 393 { 394 int ll; 395 extern char *CompileOptions[]; 396 397 dprintf("Version %s\n Compiled with:", Version); 398 av = CompileOptions; 399 ll = 7; 400 while (*av != NULL) 401 { 402 if (ll + strlen(*av) > 63) 403 { 404 dprintf("\n"); 405 ll = 0; 406 } 407 if (ll == 0) 408 dprintf("\t\t"); 409 else 410 dprintf(" "); 411 dprintf("%s", *av); 412 ll += strlen(*av++) + 1; 413 } 414 dprintf("\n"); 415 } 416 if (tTd(0, 10)) 417 { 418 int ll; 419 extern char *OsCompileOptions[]; 420 421 dprintf(" OS Defines:"); 422 av = OsCompileOptions; 423 ll = 7; 424 while (*av != NULL) 425 { 426 if (ll + strlen(*av) > 63) 427 { 428 dprintf("\n"); 429 ll = 0; 430 } 431 if (ll == 0) 432 dprintf("\t\t"); 433 else 434 dprintf(" "); 435 dprintf("%s", *av); 436 ll += strlen(*av++) + 1; 437 } 438 dprintf("\n"); 439#ifdef _PATH_UNIX 440 dprintf("Kernel symbols:\t%s\n", _PATH_UNIX); 441#endif /* _PATH_UNIX */ 442 dprintf(" Def Conf file:\t%s\n", getcfname()); 443 dprintf(" Def Pid file:\t%s\n", PidFile); 444 } 445 446 InChannel = stdin; 447 OutChannel = stdout; 448 449 /* clear sendmail's environment */ 450 ExternalEnviron = environ; 451 emptyenviron[0] = NULL; 452 environ = emptyenviron; 453 454 /* 455 ** restore any original TZ setting until TimeZoneSpec has been 456 ** determined - or early log messages may get bogus time stamps 457 */ 458 if ((p = getextenv("TZ")) != NULL) 459 { 460 char *tz; 461 int tzlen; 462 463 tzlen = strlen(p) + 4; 464 tz = xalloc(tzlen); 465 (void) snprintf(tz, tzlen, "TZ=%s", p); 466 (void) putenv(tz); 467 } 468 469 /* prime the child environment */ 470 setuserenv("AGENT", "sendmail"); 471 472 if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) 473 (void) setsignal(SIGINT, intsig); 474 (void) setsignal(SIGTERM, intsig); 475 (void) setsignal(SIGPIPE, SIG_IGN); 476 OldUmask = umask(022); 477 OpMode = MD_DELIVER; 478 FullName = getextenv("NAME"); 479 480 /* 481 ** Initialize name server if it is going to be used. 482 */ 483 484#if NAMED_BIND 485 if (!bitset(RES_INIT, _res.options)) 486 (void) res_init(); 487 488 /* 489 ** hack to avoid crashes when debugging for the resolver is 490 ** turned on and sfio is used 491 */ 492 if (tTd(8, 8)) 493# if !SFIO || SFIO_STDIO_COMPAT 494 _res.options |= RES_DEBUG; 495# else /* !SFIO || SFIO_STDIO_COMPAT */ 496 dprintf("RES_DEBUG not available due to SFIO\n"); 497# endif /* !SFIO || SFIO_STDIO_COMPAT */ 498 else 499 _res.options &= ~RES_DEBUG; 500# ifdef RES_NOALIASES 501 _res.options |= RES_NOALIASES; 502# endif /* RES_NOALIASES */ 503 TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry; 504 TimeOuts.res_retry[RES_TO_FIRST] = _res.retry; 505 TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry; 506 TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans; 507 TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans; 508 TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans; 509#endif /* NAMED_BIND */ 510 511 errno = 0; 512 from = NULL; 513 514 /* initialize some macros, etc. */ 515 initmacros(CurEnv); 516 init_vendor_macros(CurEnv); 517 518 /* version */ 519 define('v', Version, CurEnv); 520 521 /* hostname */ 522 hp = myhostname(jbuf, sizeof jbuf); 523 if (jbuf[0] != '\0') 524 { 525 struct utsname utsname; 526 527 if (tTd(0, 4)) 528 dprintf("canonical name: %s\n", jbuf); 529 define('w', newstr(jbuf), CurEnv); /* must be new string */ 530 define('j', newstr(jbuf), CurEnv); 531 setclass('w', jbuf); 532 533 p = strchr(jbuf, '.'); 534 if (p != NULL) 535 { 536 if (p[1] != '\0') 537 { 538 define('m', newstr(&p[1]), CurEnv); 539 } 540 while (p != NULL && strchr(&p[1], '.') != NULL) 541 { 542 *p = '\0'; 543 if (tTd(0, 4)) 544 dprintf("\ta.k.a.: %s\n", jbuf); 545 setclass('w', jbuf); 546 *p++ = '.'; 547 p = strchr(p, '.'); 548 } 549 } 550 551 if (uname(&utsname) >= 0) 552 p = utsname.nodename; 553 else 554 { 555 if (tTd(0, 22)) 556 dprintf("uname failed (%s)\n", 557 errstring(errno)); 558 makelower(jbuf); 559 p = jbuf; 560 } 561 if (tTd(0, 4)) 562 dprintf(" UUCP nodename: %s\n", p); 563 p = newstr(p); 564 define('k', p, CurEnv); 565 setclass('k', p); 566 setclass('w', p); 567 } 568 if (hp != NULL) 569 { 570 for (av = hp->h_aliases; av != NULL && *av != NULL; av++) 571 { 572 if (tTd(0, 4)) 573 dprintf("\ta.k.a.: %s\n", *av); 574 setclass('w', *av); 575 } 576#if NETINET || NETINET6 577 for (i = 0; hp->h_addr_list[i] != NULL; i++) 578 { 579# if NETINET6 580 char *addr; 581 char buf6[INET6_ADDRSTRLEN]; 582 struct in6_addr ia6; 583# endif /* NETINET6 */ 584# if NETINET 585 struct in_addr ia; 586# endif /* NETINET */ 587 char ipbuf[103]; 588 589 ipbuf[0] = '\0'; 590 switch (hp->h_addrtype) 591 { 592# if NETINET 593 case AF_INET: 594 if (hp->h_length != INADDRSZ) 595 break; 596 597 memmove(&ia, hp->h_addr_list[i], INADDRSZ); 598 (void) snprintf(ipbuf, sizeof ipbuf, 599 "[%.100s]", inet_ntoa(ia)); 600 break; 601# endif /* NETINET */ 602 603# if NETINET6 604 case AF_INET6: 605 if (hp->h_length != IN6ADDRSZ) 606 break; 607 608 memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ); 609 addr = anynet_ntop(&ia6, buf6, sizeof buf6); 610 if (addr != NULL) 611 (void) snprintf(ipbuf, sizeof ipbuf, 612 "[%.100s]", addr); 613 break; 614# endif /* NETINET6 */ 615 } 616 if (ipbuf[0] == '\0') 617 break; 618 619 if (tTd(0, 4)) 620 dprintf("\ta.k.a.: %s\n", ipbuf); 621 setclass('w', ipbuf); 622 } 623#endif /* NETINET || NETINET6 */ 624 } 625 626 /* current time */ 627 define('b', arpadate((char *) NULL), CurEnv); 628 /* current load average */ 629 CurrentLA = sm_getla(CurEnv); 630 631 QueueLimitRecipient = (QUEUE_CHAR *) NULL; 632 QueueLimitSender = (QUEUE_CHAR *) NULL; 633 QueueLimitId = (QUEUE_CHAR *) NULL; 634 635 /* 636 ** Crack argv. 637 */ 638 639 av = argv; 640 p = strrchr(*av, '/'); 641 if (p++ == NULL) 642 p = *av; 643 if (strcmp(p, "newaliases") == 0) 644 OpMode = MD_INITALIAS; 645 else if (strcmp(p, "mailq") == 0) 646 OpMode = MD_PRINT; 647 else if (strcmp(p, "smtpd") == 0) 648 OpMode = MD_DAEMON; 649 else if (strcmp(p, "hoststat") == 0) 650 OpMode = MD_HOSTSTAT; 651 else if (strcmp(p, "purgestat") == 0) 652 OpMode = MD_PURGESTAT; 653 654 optind = 1; 655 while ((j = getopt(argc, argv, OPTIONS)) != -1) 656 { 657 switch (j) 658 { 659 case 'b': /* operations mode */ 660 switch (j = *optarg) 661 { 662 case MD_DAEMON: 663 case MD_FGDAEMON: 664#if !DAEMON 665 usrerr("Daemon mode not implemented"); 666 ExitStat = EX_USAGE; 667 break; 668#endif /* !DAEMON */ 669 case MD_SMTP: 670#if !SMTP 671 usrerr("I don't speak SMTP"); 672 ExitStat = EX_USAGE; 673 break; 674#endif /* !SMTP */ 675 676 case MD_INITALIAS: 677 case MD_DELIVER: 678 case MD_VERIFY: 679 case MD_TEST: 680 case MD_PRINT: 681 case MD_HOSTSTAT: 682 case MD_PURGESTAT: 683 case MD_ARPAFTP: 684 OpMode = j; 685 break; 686 687 case MD_FREEZE: 688 usrerr("Frozen configurations unsupported"); 689 ExitStat = EX_USAGE; 690 break; 691 692 default: 693 usrerr("Invalid operation mode %c", j); 694 ExitStat = EX_USAGE; 695 break; 696 } 697 break; 698 699 case 'B': /* body type */ 700 CurEnv->e_bodytype = optarg; 701 break; 702 703 case 'C': /* select configuration file (already done) */ 704 if (RealUid != 0) 705 warn_C_flag = TRUE; 706 ConfFile = optarg; 707 dp = drop_privileges(TRUE); 708 setstat(dp); 709 safecf = FALSE; 710 break; 711 712 case 'd': /* debugging -- already done */ 713 break; 714 715 case 'f': /* from address */ 716 case 'r': /* obsolete -f flag */ 717 if (from != NULL) 718 { 719 usrerr("More than one \"from\" person"); 720 ExitStat = EX_USAGE; 721 break; 722 } 723 from = newstr(denlstring(optarg, TRUE, TRUE)); 724 if (strcmp(RealUserName, from) != 0) 725 warn_f_flag = j; 726 break; 727 728 case 'F': /* set full name */ 729 FullName = newstr(optarg); 730 break; 731 732 case 'G': /* relay (gateway) submission */ 733 /* already set */ 734 break; 735 736 case 'h': /* hop count */ 737 CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10); 738 if (*ep) 739 { 740 usrerr("Bad hop count (%s)", optarg); 741 ExitStat = EX_USAGE; 742 } 743 break; 744 745 case 'L': /* program label */ 746 /* already set */ 747 break; 748 749 case 'n': /* don't alias */ 750 NoAlias = TRUE; 751 break; 752 753 case 'N': /* delivery status notifications */ 754 DefaultNotify |= QHASNOTIFY; 755 define(macid("{dsn_notify}", NULL), 756 newstr(optarg), CurEnv); 757 if (strcasecmp(optarg, "never") == 0) 758 break; 759 for (p = optarg; p != NULL; optarg = p) 760 { 761 p = strchr(p, ','); 762 if (p != NULL) 763 *p++ = '\0'; 764 if (strcasecmp(optarg, "success") == 0) 765 DefaultNotify |= QPINGONSUCCESS; 766 else if (strcasecmp(optarg, "failure") == 0) 767 DefaultNotify |= QPINGONFAILURE; 768 else if (strcasecmp(optarg, "delay") == 0) 769 DefaultNotify |= QPINGONDELAY; 770 else 771 { 772 usrerr("Invalid -N argument"); 773 ExitStat = EX_USAGE; 774 } 775 } 776 break; 777 778 case 'o': /* set option */ 779 setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); 780 break; 781 782 case 'O': /* set option (long form) */ 783 setoption(' ', optarg, FALSE, TRUE, CurEnv); 784 break; 785 786 case 'p': /* set protocol */ 787 p = strchr(optarg, ':'); 788 if (p != NULL) 789 { 790 *p++ = '\0'; 791 if (*p != '\0') 792 { 793 ep = xalloc(strlen(p) + 1); 794 cleanstrcpy(ep, p, MAXNAME); 795 define('s', ep, CurEnv); 796 } 797 } 798 if (*optarg != '\0') 799 { 800 ep = xalloc(strlen(optarg) + 1); 801 cleanstrcpy(ep, optarg, MAXNAME); 802 define('r', ep, CurEnv); 803 } 804 break; 805 806 case 'q': /* run queue files at intervals */ 807#if QUEUE 808 /* sanity check */ 809 if (OpMode != MD_DELIVER && 810 OpMode != MD_DAEMON && 811 OpMode != MD_FGDAEMON && 812 OpMode != MD_PRINT && 813 OpMode != MD_QUEUERUN) 814 { 815 usrerr("Can not use -q with -b%c", OpMode); 816 ExitStat = EX_USAGE; 817 break; 818 } 819 820 /* don't override -bd, -bD or -bp */ 821 if (OpMode == MD_DELIVER) 822 OpMode = MD_QUEUERUN; 823 824 FullName = NULL; 825 826 switch (optarg[0]) 827 { 828 case 'I': 829 new = (QUEUE_CHAR *) xalloc(sizeof *new); 830 new->queue_match = newstr(&optarg[1]); 831 new->queue_next = QueueLimitId; 832 QueueLimitId = new; 833 break; 834 835 case 'R': 836 new = (QUEUE_CHAR *) xalloc(sizeof *new); 837 new->queue_match = newstr(&optarg[1]); 838 new->queue_next = QueueLimitRecipient; 839 QueueLimitRecipient = new; 840 break; 841 842 case 'S': 843 new = (QUEUE_CHAR *) xalloc(sizeof *new); 844 new->queue_match = newstr(&optarg[1]); 845 new->queue_next = QueueLimitSender; 846 QueueLimitSender = new; 847 break; 848 849 default: 850 i = Errors; 851 QueueIntvl = convtime(optarg, 'm'); 852 853 /* check for bad conversion */ 854 if (i < Errors) 855 ExitStat = EX_USAGE; 856 break; 857 } 858#else /* QUEUE */ 859 usrerr("I don't know about queues"); 860 ExitStat = EX_USAGE; 861#endif /* QUEUE */ 862 break; 863 864 case 'R': /* DSN RET: what to return */ 865 if (bitset(EF_RET_PARAM, CurEnv->e_flags)) 866 { 867 usrerr("Duplicate -R flag"); 868 ExitStat = EX_USAGE; 869 break; 870 } 871 CurEnv->e_flags |= EF_RET_PARAM; 872 if (strcasecmp(optarg, "hdrs") == 0) 873 CurEnv->e_flags |= EF_NO_BODY_RETN; 874 else if (strcasecmp(optarg, "full") != 0) 875 { 876 usrerr("Invalid -R value"); 877 ExitStat = EX_USAGE; 878 } 879 define(macid("{dsn_ret}", NULL), 880 newstr(optarg), CurEnv); 881 break; 882 883 case 't': /* read recipients from message */ 884 GrabTo = TRUE; 885 break; 886 887 case 'U': /* initial (user) submission */ 888 /* already set */ 889 break; 890 891 case 'V': /* DSN ENVID: set "original" envelope id */ 892 if (!xtextok(optarg)) 893 { 894 usrerr("Invalid syntax in -V flag"); 895 ExitStat = EX_USAGE; 896 } 897 else 898 { 899 CurEnv->e_envid = newstr(optarg); 900 define(macid("{dsn_envid}", NULL), 901 newstr(optarg), CurEnv); 902 } 903 break; 904 905 case 'X': /* traffic log file */ 906 dp = drop_privileges(TRUE); 907 setstat(dp); 908 if (stat(optarg, &traf_st) == 0 && 909 S_ISFIFO(traf_st.st_mode)) 910 TrafficLogFile = fopen(optarg, "w"); 911 else 912 TrafficLogFile = fopen(optarg, "a"); 913 if (TrafficLogFile == NULL) 914 { 915 syserr("cannot open %s", optarg); 916 ExitStat = EX_CANTCREAT; 917 break; 918 } 919#if HASSETVBUF 920 (void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0); 921#else /* HASSETVBUF */ 922 (void) setlinebuf(TrafficLogFile); 923#endif /* HASSETVBUF */ 924 break; 925 926 /* compatibility flags */ 927 case 'c': /* connect to non-local mailers */ 928 case 'i': /* don't let dot stop me */ 929 case 'm': /* send to me too */ 930 case 'T': /* set timeout interval */ 931 case 'v': /* give blow-by-blow description */ 932 setoption(j, "T", FALSE, TRUE, CurEnv); 933 break; 934 935 case 'e': /* error message disposition */ 936 case 'M': /* define macro */ 937 setoption(j, optarg, FALSE, TRUE, CurEnv); 938 break; 939 940 case 's': /* save From lines in headers */ 941 setoption('f', "T", FALSE, TRUE, CurEnv); 942 break; 943 944#ifdef DBM 945 case 'I': /* initialize alias DBM file */ 946 OpMode = MD_INITALIAS; 947 break; 948#endif /* DBM */ 949 950#if defined(__osf__) || defined(_AIX3) 951 case 'x': /* random flag that OSF/1 & AIX mailx passes */ 952 break; 953#endif /* defined(__osf__) || defined(_AIX3) */ 954#if defined(sony_news) 955 case 'E': 956 case 'J': /* ignore flags for Japanese code conversion 957 implemented on Sony NEWS */ 958 break; 959#endif /* defined(sony_news) */ 960 961 default: 962 finis(TRUE, EX_USAGE); 963 break; 964 } 965 } 966 av += optind; 967 968 if (bitset(SUBMIT_MTA, SubmitMode) && 969 bitset(SUBMIT_MSA, SubmitMode)) 970 { 971 /* sanity check */ 972 errno = 0; /* reset to avoid bogus error messages */ 973 syserr("Cannot use both -G and -U together"); 974 } 975 else if (bitset(SUBMIT_MTA, SubmitMode)) 976 define(macid("{daemon_flags}", NULL), "CC f", CurEnv); 977 else if (bitset(SUBMIT_MSA, SubmitMode)) 978 { 979 define(macid("{daemon_flags}", NULL), "c u", CurEnv); 980 981 /* check for wrong OpMode */ 982 if (OpMode != MD_DELIVER && OpMode != MD_SMTP) 983 { 984 errno = 0; /* reset to avoid bogus error msgs */ 985 syserr("Cannot use -U and -b%c", OpMode); 986 } 987 } 988 else 989 { 990#if _FFR_DEFAULT_SUBMIT_TO_MSA 991 define(macid("{daemon_flags}", NULL), "c u", CurEnv); 992#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */ 993 /* EMPTY */ 994#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */ 995 } 996 997 /* 998 ** Do basic initialization. 999 ** Read system control file. 1000 ** Extract special fields for local use. 1001 */ 1002 1003 /* set up ${opMode} for use in config file */ 1004 { 1005 char mbuf[2]; 1006 1007 mbuf[0] = OpMode; 1008 mbuf[1] = '\0'; 1009 define(MID_OPMODE, newstr(mbuf), CurEnv); 1010 } 1011 1012#if XDEBUG 1013 checkfd012("before readcf"); 1014#endif /* XDEBUG */ 1015 vendor_pre_defaults(CurEnv); 1016 1017 readcf(getcfname(), safecf, CurEnv); 1018 ConfigFileRead = TRUE; 1019 vendor_post_defaults(CurEnv); 1020 1021 /* Enforce use of local time (null string overrides this) */ 1022 if (TimeZoneSpec == NULL) 1023 unsetenv("TZ"); 1024 else if (TimeZoneSpec[0] != '\0') 1025 setuserenv("TZ", TimeZoneSpec); 1026 else 1027 setuserenv("TZ", NULL); 1028 tzset(); 1029 1030 /* avoid denial-of-service attacks */ 1031 resetlimits(); 1032 1033 if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) 1034 { 1035 /* drop privileges -- daemon mode done after socket/bind */ 1036 dp = drop_privileges(FALSE); 1037 setstat(dp); 1038 } 1039 1040#if NAMED_BIND 1041 _res.retry = TimeOuts.res_retry[RES_TO_DEFAULT]; 1042 _res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT]; 1043#endif /* NAMED_BIND */ 1044 1045 /* 1046 ** Find our real host name for future logging. 1047 */ 1048 1049 authinfo = getauthinfo(STDIN_FILENO, &forged); 1050 define('_', authinfo, CurEnv); 1051 1052 /* suppress error printing if errors mailed back or whatever */ 1053 if (CurEnv->e_errormode != EM_PRINT) 1054 HoldErrs = TRUE; 1055 1056 /* set up the $=m class now, after .cf has a chance to redefine $m */ 1057 expand("\201m", jbuf, sizeof jbuf, CurEnv); 1058 setclass('m', jbuf); 1059 1060 /* probe interfaces and locate any additional names */ 1061 if (!DontProbeInterfaces) 1062 load_if_names(); 1063 1064 if (tTd(0, 1)) 1065 { 1066 dprintf("\n============ SYSTEM IDENTITY (after readcf) ============"); 1067 dprintf("\n (short domain name) $w = "); 1068 xputs(macvalue('w', CurEnv)); 1069 dprintf("\n (canonical domain name) $j = "); 1070 xputs(macvalue('j', CurEnv)); 1071 dprintf("\n (subdomain name) $m = "); 1072 xputs(macvalue('m', CurEnv)); 1073 dprintf("\n (node name) $k = "); 1074 xputs(macvalue('k', CurEnv)); 1075 dprintf("\n========================================================\n\n"); 1076 } 1077 1078 /* 1079 ** Do more command line checking -- these are things that 1080 ** have to modify the results of reading the config file. 1081 */ 1082 1083 /* process authorization warnings from command line */ 1084 if (warn_C_flag) 1085 auth_warning(CurEnv, "Processed by %s with -C %s", 1086 RealUserName, ConfFile); 1087 if (Warn_Q_option && !wordinclass(RealUserName, 't')) 1088 auth_warning(CurEnv, "Processed from queue %s", QueueDir); 1089 1090 /* check body type for legality */ 1091 if (CurEnv->e_bodytype == NULL) 1092 /* EMPTY */ 1093 /* nothing */ ; 1094 else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) 1095 SevenBitInput = TRUE; 1096 else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) 1097 SevenBitInput = FALSE; 1098 else 1099 { 1100 usrerr("Illegal body type %s", CurEnv->e_bodytype); 1101 CurEnv->e_bodytype = NULL; 1102 } 1103 1104 /* tweak default DSN notifications */ 1105 if (DefaultNotify == 0) 1106 DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 1107 1108 /* be sure we don't pick up bogus HOSTALIASES environment variable */ 1109 if (OpMode == MD_QUEUERUN && RealUid != 0) 1110 (void) unsetenv("HOSTALIASES"); 1111 1112 /* check for sane configuration level */ 1113 if (ConfigLevel > MAXCONFIGLEVEL) 1114 { 1115 syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", 1116 ConfigLevel, Version, MAXCONFIGLEVEL); 1117 } 1118 1119 /* need MCI cache to have persistence */ 1120 if (HostStatDir != NULL && MaxMciCache == 0) 1121 { 1122 HostStatDir = NULL; 1123 printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); 1124 } 1125 1126 /* need HostStatusDir in order to have SingleThreadDelivery */ 1127 if (SingleThreadDelivery && HostStatDir == NULL) 1128 { 1129 SingleThreadDelivery = FALSE; 1130 printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); 1131 } 1132 1133 /* check for permissions */ 1134 if ((OpMode == MD_DAEMON || 1135 OpMode == MD_FGDAEMON || 1136 OpMode == MD_PURGESTAT) && 1137 RealUid != 0 && 1138 RealUid != TrustedUid) 1139 { 1140 if (LogLevel > 1) 1141 sm_syslog(LOG_ALERT, NOQID, 1142 "user %d attempted to %s", 1143 RealUid, 1144 OpMode != MD_PURGESTAT ? "run daemon" 1145 : "purge host status"); 1146 usrerr("Permission denied"); 1147 finis(FALSE, EX_USAGE); 1148 } 1149 if (OpMode == MD_INITALIAS && 1150 RealUid != 0 && 1151 RealUid != TrustedUid && 1152 !wordinclass(RealUserName, 't')) 1153 { 1154 if (LogLevel > 1) 1155 sm_syslog(LOG_ALERT, NOQID, 1156 "user %d attempted to rebuild the alias map", 1157 RealUid); 1158 usrerr("Permission denied"); 1159 finis(FALSE, EX_USAGE); 1160 } 1161 1162 if (MeToo) 1163 BlankEnvelope.e_flags |= EF_METOO; 1164 1165 switch (OpMode) 1166 { 1167 case MD_TEST: 1168 /* don't have persistent host status in test mode */ 1169 HostStatDir = NULL; 1170 if (Verbose == 0) 1171 Verbose = 2; 1172 CurEnv->e_errormode = EM_PRINT; 1173 HoldErrs = FALSE; 1174 break; 1175 1176 case MD_VERIFY: 1177 CurEnv->e_errormode = EM_PRINT; 1178 HoldErrs = FALSE; 1179 /* arrange to exit cleanly on hangup signal */ 1180 if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 1181 (void) setsignal(SIGHUP, intsig); 1182 break; 1183 1184 case MD_FGDAEMON: 1185 run_in_foreground = TRUE; 1186 OpMode = MD_DAEMON; 1187 /* FALLTHROUGH */ 1188 1189 case MD_DAEMON: 1190 vendor_daemon_setup(CurEnv); 1191 1192 /* remove things that don't make sense in daemon mode */ 1193 FullName = NULL; 1194 GrabTo = FALSE; 1195 1196 /* arrange to restart on hangup signal */ 1197 if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') 1198 sm_syslog(LOG_WARNING, NOQID, 1199 "daemon invoked without full pathname; kill -1 won't work"); 1200 (void) setsignal(SIGHUP, sighup); 1201 1202 /* workaround: can't seem to release the signal in the parent */ 1203 (void) releasesignal(SIGHUP); 1204 break; 1205 1206 case MD_INITALIAS: 1207 Verbose = 2; 1208 CurEnv->e_errormode = EM_PRINT; 1209 HoldErrs = FALSE; 1210 /* FALLTHROUGH */ 1211 1212 default: 1213 /* arrange to exit cleanly on hangup signal */ 1214 if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) 1215 (void) setsignal(SIGHUP, intsig); 1216 break; 1217 } 1218 1219 /* special considerations for FullName */ 1220 if (FullName != NULL) 1221 { 1222 char *full = NULL; 1223 1224 /* full names can't have newlines */ 1225 if (strchr(FullName, '\n') != NULL) 1226 { 1227 FullName = full = newstr(denlstring(FullName, TRUE, TRUE)); 1228 } 1229 /* check for characters that may have to be quoted */ 1230 if (!rfc822_string(FullName)) 1231 { 1232 /* 1233 ** Quote a full name with special characters 1234 ** as a comment so crackaddr() doesn't destroy 1235 ** the name portion of the address. 1236 */ 1237 FullName = addquotes(FullName); 1238 if (full != NULL) 1239 free(full); 1240 } 1241 } 1242 1243 /* do heuristic mode adjustment */ 1244 if (Verbose) 1245 { 1246 /* turn off noconnect option */ 1247 setoption('c', "F", TRUE, FALSE, CurEnv); 1248 1249 /* turn on interactive delivery */ 1250 setoption('d', "", TRUE, FALSE, CurEnv); 1251 } 1252 1253#ifdef VENDOR_CODE 1254 /* check for vendor mismatch */ 1255 if (VendorCode != VENDOR_CODE) 1256 { 1257 message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s", 1258 getvendor(VENDOR_CODE), getvendor(VendorCode)); 1259 } 1260#endif /* VENDOR_CODE */ 1261 1262 /* check for out of date configuration level */ 1263 if (ConfigLevel < MAXCONFIGLEVEL) 1264 { 1265 message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d", 1266 Version, MAXCONFIGLEVEL, ConfigLevel); 1267 } 1268 1269 if (ConfigLevel < 3) 1270 UseErrorsTo = TRUE; 1271 1272 /* set options that were previous macros */ 1273 if (SmtpGreeting == NULL) 1274 { 1275 if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL) 1276 SmtpGreeting = newstr(p); 1277 else 1278 SmtpGreeting = "\201j Sendmail \201v ready at \201b"; 1279 } 1280 if (UnixFromLine == NULL) 1281 { 1282 if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) 1283 UnixFromLine = newstr(p); 1284 else 1285 UnixFromLine = "From \201g \201d"; 1286 } 1287 SmtpError[0] = '\0'; 1288 1289 /* our name for SMTP codes */ 1290 expand("\201j", jbuf, sizeof jbuf, CurEnv); 1291 MyHostName = jbuf; 1292 if (strchr(jbuf, '.') == NULL) 1293 message("WARNING: local host name (%s) is not qualified; fix $j in config file", 1294 jbuf); 1295 1296 /* make certain that this name is part of the $=w class */ 1297 setclass('w', MyHostName); 1298 1299 /* the indices of built-in mailers */ 1300 st = stab("local", ST_MAILER, ST_FIND); 1301 if (st != NULL) 1302 LocalMailer = st->s_mailer; 1303 else if (OpMode != MD_TEST || !warn_C_flag) 1304 syserr("No local mailer defined"); 1305 1306 st = stab("prog", ST_MAILER, ST_FIND); 1307 if (st == NULL) 1308 syserr("No prog mailer defined"); 1309 else 1310 { 1311 ProgMailer = st->s_mailer; 1312 clrbitn(M_MUSER, ProgMailer->m_flags); 1313 } 1314 1315 st = stab("*file*", ST_MAILER, ST_FIND); 1316 if (st == NULL) 1317 syserr("No *file* mailer defined"); 1318 else 1319 { 1320 FileMailer = st->s_mailer; 1321 clrbitn(M_MUSER, FileMailer->m_flags); 1322 } 1323 1324 st = stab("*include*", ST_MAILER, ST_FIND); 1325 if (st == NULL) 1326 syserr("No *include* mailer defined"); 1327 else 1328 InclMailer = st->s_mailer; 1329 1330 if (ConfigLevel < 6) 1331 { 1332 /* heuristic tweaking of local mailer for back compat */ 1333 if (LocalMailer != NULL) 1334 { 1335 setbitn(M_ALIASABLE, LocalMailer->m_flags); 1336 setbitn(M_HASPWENT, LocalMailer->m_flags); 1337 setbitn(M_TRYRULESET5, LocalMailer->m_flags); 1338 setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); 1339 setbitn(M_CHECKPROG, LocalMailer->m_flags); 1340 setbitn(M_CHECKFILE, LocalMailer->m_flags); 1341 setbitn(M_CHECKUDB, LocalMailer->m_flags); 1342 } 1343 if (ProgMailer != NULL) 1344 setbitn(M_RUNASRCPT, ProgMailer->m_flags); 1345 if (FileMailer != NULL) 1346 setbitn(M_RUNASRCPT, FileMailer->m_flags); 1347 } 1348 if (ConfigLevel < 7) 1349 { 1350 if (LocalMailer != NULL) 1351 setbitn(M_VRFY250, LocalMailer->m_flags); 1352 if (ProgMailer != NULL) 1353 setbitn(M_VRFY250, ProgMailer->m_flags); 1354 if (FileMailer != NULL) 1355 setbitn(M_VRFY250, FileMailer->m_flags); 1356 } 1357 1358 /* MIME Content-Types that cannot be transfer encoded */ 1359 setclass('n', "multipart/signed"); 1360 1361 /* MIME message/xxx subtypes that can be treated as messages */ 1362 setclass('s', "rfc822"); 1363 1364 /* MIME Content-Transfer-Encodings that can be encoded */ 1365 setclass('e', "7bit"); 1366 setclass('e', "8bit"); 1367 setclass('e', "binary"); 1368 1369#ifdef USE_B_CLASS 1370 /* MIME Content-Types that should be treated as binary */ 1371 setclass('b', "image"); 1372 setclass('b', "audio"); 1373 setclass('b', "video"); 1374 setclass('b', "application/octet-stream"); 1375#endif /* USE_B_CLASS */ 1376 1377 /* MIME headers which have fields to check for overflow */ 1378 setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition"); 1379 setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type"); 1380 1381 /* MIME headers to check for length overflow */ 1382 setclass(macid("{checkMIMETextHeaders}", NULL), "content-description"); 1383 1384 /* MIME headers to check for overflow and rebalance */ 1385 setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition"); 1386 setclass(macid("{checkMIMEHeaders}", NULL), "content-id"); 1387 setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding"); 1388 setclass(macid("{checkMIMEHeaders}", NULL), "content-type"); 1389 setclass(macid("{checkMIMEHeaders}", NULL), "mime-version"); 1390 1391 /* Macros to save in the qf file -- don't remove any */ 1392 setclass(macid("{persistentMacros}", NULL), "r"); 1393 setclass(macid("{persistentMacros}", NULL), "s"); 1394 setclass(macid("{persistentMacros}", NULL), "_"); 1395 setclass(macid("{persistentMacros}", NULL), "{if_addr}"); 1396 setclass(macid("{persistentMacros}", NULL), "{daemon_flags}"); 1397 setclass(macid("{persistentMacros}", NULL), "{client_flags}"); 1398 1399 /* operate in queue directory */ 1400 if (QueueDir == NULL) 1401 { 1402 if (OpMode != MD_TEST) 1403 { 1404 syserr("QueueDirectory (Q) option must be set"); 1405 ExitStat = EX_CONFIG; 1406 } 1407 } 1408 else 1409 { 1410 /* 1411 ** If multiple queues wildcarded, use one for 1412 ** the daemon's home. Note that this preconditions 1413 ** a wildcarded QueueDir to a real pathname. 1414 */ 1415 1416 if (OpMode != MD_TEST) 1417 multiqueue_cache(); 1418 } 1419 1420 /* check host status directory for validity */ 1421 if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) 1422 { 1423 /* cannot use this value */ 1424 if (tTd(0, 2)) 1425 dprintf("Cannot use HostStatusDirectory = %s: %s\n", 1426 HostStatDir, errstring(errno)); 1427 HostStatDir = NULL; 1428 } 1429 1430#if QUEUE 1431 if (OpMode == MD_QUEUERUN && RealUid != 0 && 1432 bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) 1433 { 1434 struct stat stbuf; 1435 1436 /* check to see if we own the queue directory */ 1437 if (stat(".", &stbuf) < 0) 1438 syserr("main: cannot stat %s", QueueDir); 1439 if (stbuf.st_uid != RealUid) 1440 { 1441 /* nope, really a botch */ 1442 usrerr("You do not have permission to process the queue"); 1443 finis(FALSE, EX_NOPERM); 1444 } 1445 } 1446#endif /* QUEUE */ 1447 1448#if _FFR_MILTER 1449 /* sanity checks on milter filters */ 1450 if (OpMode == MD_DAEMON || OpMode == MD_SMTP) 1451 milter_parse_list(InputFilterList, InputFilters, MAXFILTERS); 1452#endif /* _FFR_MILTER */ 1453 1454 /* if we've had errors so far, exit now */ 1455 if (ExitStat != EX_OK && OpMode != MD_TEST) 1456 finis(FALSE, ExitStat); 1457 1458#if XDEBUG 1459 checkfd012("before main() initmaps"); 1460#endif /* XDEBUG */ 1461 1462 /* 1463 ** Do operation-mode-dependent initialization. 1464 */ 1465 1466 switch (OpMode) 1467 { 1468 case MD_PRINT: 1469 /* print the queue */ 1470#if QUEUE 1471 dropenvelope(CurEnv, TRUE); 1472 (void) setsignal(SIGPIPE, quiesce); 1473 printqueue(); 1474 finis(FALSE, EX_OK); 1475#else /* QUEUE */ 1476 usrerr("No queue to print"); 1477 finis(FALSE, EX_UNAVAILABLE); 1478#endif /* QUEUE */ 1479 break; 1480 1481 case MD_HOSTSTAT: 1482 (void) setsignal(SIGPIPE, quiesce); 1483 (void) mci_traverse_persistent(mci_print_persistent, NULL); 1484 finis(FALSE, EX_OK); 1485 break; 1486 1487 case MD_PURGESTAT: 1488 (void) mci_traverse_persistent(mci_purge_persistent, NULL); 1489 finis(FALSE, EX_OK); 1490 break; 1491 1492 case MD_INITALIAS: 1493 /* initialize maps */ 1494 initmaps(); 1495 finis(FALSE, ExitStat); 1496 break; 1497 1498 case MD_SMTP: 1499 case MD_DAEMON: 1500 /* reset DSN parameters */ 1501 DefaultNotify = QPINGONFAILURE|QPINGONDELAY; 1502 define(macid("{dsn_notify}", NULL), NULL, CurEnv); 1503 CurEnv->e_envid = NULL; 1504 define(macid("{dsn_envid}", NULL), NULL, CurEnv); 1505 CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); 1506 define(macid("{dsn_ret}", NULL), NULL, CurEnv); 1507 1508 /* don't open maps for daemon -- done below in child */ 1509 break; 1510 } 1511 1512 if (tTd(0, 15)) 1513 { 1514 /* print configuration table (or at least part of it) */ 1515 if (tTd(0, 90)) 1516 printrules(); 1517 for (i = 0; i < MAXMAILERS; i++) 1518 { 1519 if (Mailer[i] != NULL) 1520 printmailer(Mailer[i]); 1521 } 1522 } 1523 1524 /* 1525 ** Switch to the main envelope. 1526 */ 1527 1528 CurEnv = newenvelope(&MainEnvelope, CurEnv); 1529 MainEnvelope.e_flags = BlankEnvelope.e_flags; 1530 1531 /* 1532 ** If test mode, read addresses from stdin and process. 1533 */ 1534 1535 if (OpMode == MD_TEST) 1536 { 1537 char buf[MAXLINE]; 1538 1539 if (isatty(fileno(stdin))) 1540 Verbose = 2; 1541 1542 if (Verbose) 1543 { 1544 printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); 1545 printf("Enter <ruleset> <address>\n"); 1546 } 1547 if (setjmp(TopFrame) > 0) 1548 printf("\n"); 1549 (void) setsignal(SIGINT, intindebug); 1550 for (;;) 1551 { 1552 if (Verbose == 2) 1553 printf("> "); 1554 (void) fflush(stdout); 1555 if (fgets(buf, sizeof buf, stdin) == NULL) 1556 testmodeline("/quit", CurEnv); 1557 p = strchr(buf, '\n'); 1558 if (p != NULL) 1559 *p = '\0'; 1560 if (Verbose < 2) 1561 printf("> %s\n", buf); 1562 testmodeline(buf, CurEnv); 1563 } 1564 } 1565 1566#if SMTP 1567# if STARTTLS 1568 /* 1569 ** basic TLS initialization 1570 ** ignore result for now 1571 */ 1572 SSL_library_init(); 1573 SSL_load_error_strings(); 1574# if 0 1575 /* this is currently a macro for SSL_library_init */ 1576 SSLeay_add_ssl_algorithms(); 1577# endif /* 0 */ 1578 1579 /* initialize PRNG */ 1580 tls_rand_init(RandFile, 7); 1581 1582# endif /* STARTTLS */ 1583#endif /* SMTP */ 1584 1585#if QUEUE 1586 /* 1587 ** If collecting stuff from the queue, go start doing that. 1588 */ 1589 1590 if (OpMode == MD_QUEUERUN && QueueIntvl == 0) 1591 { 1592# if SMTP 1593# if STARTTLS 1594 { 1595 /* init TLS for client, ignore result for now */ 1596 (void) initclttls(); 1597 } 1598# endif /* STARTTLS */ 1599# endif /* SMTP */ 1600 (void) runqueue(FALSE, Verbose); 1601 finis(TRUE, ExitStat); 1602 } 1603#endif /* QUEUE */ 1604 1605 /* 1606 ** If a daemon, wait for a request. 1607 ** getrequests will always return in a child. 1608 ** If we should also be processing the queue, start 1609 ** doing it in background. 1610 ** We check for any errors that might have happened 1611 ** during startup. 1612 */ 1613 1614 if (OpMode == MD_DAEMON || QueueIntvl != 0) 1615 { 1616 char dtype[200]; 1617 1618 if (!run_in_foreground && !tTd(99, 100)) 1619 { 1620 /* put us in background */ 1621 i = fork(); 1622 if (i < 0) 1623 syserr("daemon: cannot fork"); 1624 if (i != 0) 1625 finis(FALSE, EX_OK); 1626 1627 /* disconnect from our controlling tty */ 1628 disconnect(2, CurEnv); 1629 } 1630 1631 dtype[0] = '\0'; 1632 if (OpMode == MD_DAEMON) 1633 (void) strlcat(dtype, "+SMTP", sizeof dtype); 1634 if (QueueIntvl != 0) 1635 { 1636 (void) strlcat(dtype, "+queueing@", sizeof dtype); 1637 (void) strlcat(dtype, pintvl(QueueIntvl, TRUE), 1638 sizeof dtype); 1639 } 1640 if (tTd(0, 1)) 1641 (void) strlcat(dtype, "+debugging", sizeof dtype); 1642 1643 sm_syslog(LOG_INFO, NOQID, 1644 "starting daemon (%s): %s", Version, dtype + 1); 1645#ifdef XLA 1646 xla_create_file(); 1647#endif /* XLA */ 1648 1649 /* save daemon type in a macro for possible PidFile use */ 1650 define(macid("{daemon_info}", NULL), 1651 newstr(dtype + 1), &BlankEnvelope); 1652 1653 /* save queue interval in a macro for possible PidFile use */ 1654 define(macid("{queue_interval}", NULL), 1655 newstr(pintvl(QueueIntvl, TRUE)), CurEnv); 1656 1657#if QUEUE 1658 if (QueueIntvl != 0) 1659 { 1660 (void) runqueue(TRUE, FALSE); 1661 if (OpMode != MD_DAEMON) 1662 { 1663 /* write the pid to file */ 1664 log_sendmail_pid(CurEnv); 1665 for (;;) 1666 { 1667 (void) pause(); 1668 if (DoQueueRun) 1669 (void) runqueue(TRUE, FALSE); 1670 } 1671 } 1672 } 1673#endif /* QUEUE */ 1674 dropenvelope(CurEnv, TRUE); 1675 1676#if DAEMON 1677# if STARTTLS 1678 /* init TLS for server, ignore result for now */ 1679 (void) initsrvtls(); 1680# endif /* STARTTLS */ 1681 p_flags = getrequests(CurEnv); 1682 1683 /* drop privileges */ 1684 (void) drop_privileges(FALSE); 1685 1686 /* at this point we are in a child: reset state */ 1687 (void) newenvelope(CurEnv, CurEnv); 1688 1689 /* 1690 ** Get authentication data 1691 */ 1692 1693 authinfo = getauthinfo(fileno(InChannel), &forged); 1694 define('_', authinfo, &BlankEnvelope); 1695#endif /* DAEMON */ 1696 } 1697 1698 if (LogLevel > 9) 1699 { 1700 /* log connection information */ 1701 sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo); 1702 } 1703 1704#if SMTP 1705 /* 1706 ** If running SMTP protocol, start collecting and executing 1707 ** commands. This will never return. 1708 */ 1709 1710 if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 1711 { 1712 char pbuf[20]; 1713 1714 /* 1715 ** Save some macros for check_* rulesets. 1716 */ 1717 1718 if (forged) 1719 { 1720 char ipbuf[103]; 1721 1722 (void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]", 1723 anynet_ntoa(&RealHostAddr)); 1724 define(macid("{client_name}", NULL), 1725 newstr(ipbuf), &BlankEnvelope); 1726 define(macid("{client_resolve}", NULL), 1727 "FORGED", &BlankEnvelope); 1728 } 1729 else 1730 define(macid("{client_name}", NULL), RealHostName, 1731 &BlankEnvelope); 1732 define(macid("{client_addr}", NULL), 1733 newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); 1734 (void)sm_getla(&BlankEnvelope); 1735 1736 switch(RealHostAddr.sa.sa_family) 1737 { 1738# if NETINET 1739 case AF_INET: 1740 (void) snprintf(pbuf, sizeof pbuf, "%d", 1741 RealHostAddr.sin.sin_port); 1742 break; 1743# endif /* NETINET */ 1744# if NETINET6 1745 case AF_INET6: 1746 (void) snprintf(pbuf, sizeof pbuf, "%d", 1747 RealHostAddr.sin6.sin6_port); 1748 break; 1749# endif /* NETINET6 */ 1750 default: 1751 (void) snprintf(pbuf, sizeof pbuf, "0"); 1752 break; 1753 } 1754 define(macid("{client_port}", NULL), 1755 newstr(pbuf), &BlankEnvelope); 1756 1757#if SASL 1758 /* give a syserr or just disable AUTH ? */ 1759 if (sasl_server_init(srvcallbacks, "Sendmail") != SASL_OK) 1760 syserr("!sasl_server_init failed!"); 1761#endif /* SASL */ 1762 1763 if (OpMode == MD_DAEMON) 1764 { 1765 /* validate the connection */ 1766 HoldErrs = TRUE; 1767 nullserver = validate_connection(&RealHostAddr, 1768 RealHostName, CurEnv); 1769 HoldErrs = FALSE; 1770 } 1771 else if (p_flags == NULL) 1772 { 1773 p_flags = (BITMAP256 *) xalloc(sizeof *p_flags); 1774 clrbitmap(p_flags); 1775 } 1776# if STARTTLS 1777 if (OpMode == MD_SMTP) 1778 (void) initsrvtls(); 1779# endif /* STARTTLS */ 1780 smtp(nullserver, *p_flags, CurEnv); 1781 } 1782#endif /* SMTP */ 1783 1784 clearenvelope(CurEnv, FALSE); 1785 if (OpMode == MD_VERIFY) 1786 { 1787 set_delivery_mode(SM_VERIFY, CurEnv); 1788 PostMasterCopy = NULL; 1789 } 1790 else 1791 { 1792 /* interactive -- all errors are global */ 1793 CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; 1794 } 1795 1796 /* 1797 ** Do basic system initialization and set the sender 1798 */ 1799 1800 initsys(CurEnv); 1801 define(macid("{ntries}", NULL), "0", CurEnv); 1802 setsender(from, CurEnv, NULL, '\0', FALSE); 1803 if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') && 1804 (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) || 1805 strcmp(CurEnv->e_from.q_user, RealUserName) != 0)) 1806 { 1807 auth_warning(CurEnv, "%s set sender to %s using -%c", 1808 RealUserName, from, warn_f_flag); 1809#if SASL 1810 auth = FALSE; 1811#endif /* SASL */ 1812 } 1813 if (auth) 1814 { 1815 char *fv; 1816 1817 /* set the initial sender for AUTH= to $f@$j */ 1818 fv = macvalue('f', CurEnv); 1819 if (fv == NULL || *fv == '\0') 1820 CurEnv->e_auth_param = NULL; 1821 else 1822 { 1823 if (strchr(fv, '@') == NULL) 1824 { 1825 i = strlen(fv) + strlen(macvalue('j', CurEnv)) 1826 + 2; 1827 p = xalloc(i); 1828 (void) snprintf(p, i, "%s@%s", fv, 1829 macvalue('j', CurEnv)); 1830 } 1831 else 1832 p = newstr(fv); 1833 CurEnv->e_auth_param = newstr(xtextify(p, NULL)); 1834 } 1835 } 1836 if (macvalue('s', CurEnv) == NULL) 1837 define('s', RealHostName, CurEnv); 1838 1839 if (*av == NULL && !GrabTo) 1840 { 1841 CurEnv->e_to = NULL; 1842 CurEnv->e_flags |= EF_GLOBALERRS; 1843 HoldErrs = FALSE; 1844 usrerr("Recipient names must be specified"); 1845 1846 /* collect body for UUCP return */ 1847 if (OpMode != MD_VERIFY) 1848 collect(InChannel, FALSE, NULL, CurEnv); 1849 finis(TRUE, EX_USAGE); 1850 } 1851 1852 /* 1853 ** Scan argv and deliver the message to everyone. 1854 */ 1855 1856 sendtoargv(av, CurEnv); 1857 1858 /* if we have had errors sofar, arrange a meaningful exit stat */ 1859 if (Errors > 0 && ExitStat == EX_OK) 1860 ExitStat = EX_USAGE; 1861 1862#if _FFR_FIX_DASHT 1863 /* 1864 ** If using -t, force not sending to argv recipients, even 1865 ** if they are mentioned in the headers. 1866 */ 1867 1868 if (GrabTo) 1869 { 1870 ADDRESS *q; 1871 1872 for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 1873 q->q_state = QS_REMOVED; 1874 } 1875#endif /* _FFR_FIX_DASHT */ 1876 1877 /* 1878 ** Read the input mail. 1879 */ 1880 1881 CurEnv->e_to = NULL; 1882 if (OpMode != MD_VERIFY || GrabTo) 1883 { 1884 int savederrors = Errors; 1885 long savedflags = CurEnv->e_flags & EF_FATALERRS; 1886 1887 CurEnv->e_flags |= EF_GLOBALERRS; 1888 CurEnv->e_flags &= ~EF_FATALERRS; 1889 Errors = 0; 1890 buffer_errors(); 1891 collect(InChannel, FALSE, NULL, CurEnv); 1892 1893 /* header checks failed */ 1894 if (Errors > 0) 1895 { 1896 /* Log who the mail would have gone to */ 1897 if (LogLevel > 8 && CurEnv->e_message != NULL && 1898 !GrabTo) 1899 { 1900 ADDRESS *a; 1901 1902 for (a = CurEnv->e_sendqueue; 1903 a != NULL; 1904 a = a->q_next) 1905 { 1906 if (!QS_IS_UNDELIVERED(a->q_state)) 1907 continue; 1908 1909 CurEnv->e_to = a->q_paddr; 1910 logdelivery(NULL, NULL, NULL, 1911 CurEnv->e_message, 1912 NULL, (time_t) 0, CurEnv); 1913 } 1914 CurEnv->e_to = NULL; 1915 } 1916 flush_errors(TRUE); 1917 finis(TRUE, ExitStat); 1918 /* NOTREACHED */ 1919 return -1; 1920 } 1921 1922 /* bail out if message too large */ 1923 if (bitset(EF_CLRQUEUE, CurEnv->e_flags)) 1924 { 1925 finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR); 1926 /* NOTREACHED */ 1927 return -1; 1928 } 1929 Errors = savederrors; 1930 CurEnv->e_flags |= savedflags; 1931 } 1932 errno = 0; 1933 1934 if (tTd(1, 1)) 1935 dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); 1936 1937 /* 1938 ** Actually send everything. 1939 ** If verifying, just ack. 1940 */ 1941 1942 CurEnv->e_from.q_state = QS_SENDER; 1943 if (tTd(1, 5)) 1944 { 1945 dprintf("main: QS_SENDER "); 1946 printaddr(&CurEnv->e_from, FALSE); 1947 } 1948 CurEnv->e_to = NULL; 1949 CurrentLA = sm_getla(CurEnv); 1950 GrabTo = FALSE; 1951#if NAMED_BIND 1952 _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 1953 _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 1954#endif /* NAMED_BIND */ 1955 sendall(CurEnv, SM_DEFAULT); 1956 1957 /* 1958 ** All done. 1959 ** Don't send return error message if in VERIFY mode. 1960 */ 1961 1962 finis(TRUE, ExitStat); 1963 /* NOTREACHED */ 1964 return ExitStat; 1965} 1966 1967/* ARGSUSED */ 1968SIGFUNC_DECL 1969quiesce(sig) 1970 int sig; 1971{ 1972 clear_events(); 1973 finis(FALSE, EX_OK); 1974} 1975 1976/* ARGSUSED */ 1977SIGFUNC_DECL 1978intindebug(sig) 1979 int sig; 1980{ 1981 longjmp(TopFrame, 1); 1982 return SIGFUNC_RETURN; 1983} 1984/* 1985** FINIS -- Clean up and exit. 1986** 1987** Parameters: 1988** drop -- whether or not to drop CurEnv envelope 1989** exitstat -- exit status to use for exit() call 1990** 1991** Returns: 1992** never 1993** 1994** Side Effects: 1995** exits sendmail 1996*/ 1997 1998void 1999finis(drop, exitstat) 2000 bool drop; 2001 volatile int exitstat; 2002{ 2003 2004 if (tTd(2, 1)) 2005 { 2006 dprintf("\n====finis: stat %d e_id=%s e_flags=", 2007 exitstat, 2008 CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); 2009 printenvflags(CurEnv); 2010 } 2011 if (tTd(2, 9)) 2012 printopenfds(FALSE); 2013 2014 /* if we fail in finis(), just exit */ 2015 if (setjmp(TopFrame) != 0) 2016 { 2017 /* failed -- just give it up */ 2018 goto forceexit; 2019 } 2020 2021 /* clean up temp files */ 2022 CurEnv->e_to = NULL; 2023 if (drop) 2024 { 2025 if (CurEnv->e_id != NULL) 2026 dropenvelope(CurEnv, TRUE); 2027 else 2028 poststats(StatFile); 2029 } 2030 2031 /* flush any cached connections */ 2032 mci_flush(TRUE, NULL); 2033 2034 /* close maps belonging to this pid */ 2035 closemaps(); 2036 2037#if USERDB 2038 /* close UserDatabase */ 2039 _udbx_close(); 2040#endif /* USERDB */ 2041 2042#ifdef XLA 2043 /* clean up extended load average stuff */ 2044 xla_all_end(); 2045#endif /* XLA */ 2046 2047 /* and exit */ 2048 forceexit: 2049 if (LogLevel > 78) 2050 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2051 "finis, pid=%d", 2052 getpid()); 2053 if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) 2054 exitstat = EX_OK; 2055 2056 sync_queue_time(); 2057 2058 /* reset uid for process accounting */ 2059 endpwent(); 2060 (void) setuid(RealUid); 2061 exit(exitstat); 2062} 2063/* 2064** INTSIG -- clean up on interrupt 2065** 2066** This just arranges to exit. It pessimizes in that it 2067** may resend a message. 2068** 2069** Parameters: 2070** none. 2071** 2072** Returns: 2073** none. 2074** 2075** Side Effects: 2076** Unlocks the current job. 2077*/ 2078 2079/* ARGSUSED */ 2080SIGFUNC_DECL 2081intsig(sig) 2082 int sig; 2083{ 2084 bool drop = FALSE; 2085 2086 clear_events(); 2087 if (sig != 0 && LogLevel > 79) 2088 sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); 2089 FileName = NULL; 2090 closecontrolsocket(TRUE); 2091#ifdef XLA 2092 xla_all_end(); 2093#endif /* XLA */ 2094 2095 /* Clean-up on aborted stdin message submission */ 2096 if (CurEnv->e_id != NULL && 2097 (OpMode == MD_SMTP || 2098 OpMode == MD_DELIVER || 2099 OpMode == MD_ARPAFTP)) 2100 { 2101 register ADDRESS *q; 2102 2103 /* don't return an error indication */ 2104 CurEnv->e_to = NULL; 2105 CurEnv->e_flags &= ~EF_FATALERRS; 2106 CurEnv->e_flags |= EF_CLRQUEUE; 2107 2108 /* 2109 ** Spin through the addresses and 2110 ** mark them dead to prevent bounces 2111 */ 2112 2113 for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next) 2114 q->q_state = QS_DONTSEND; 2115 2116 /* and don't try to deliver the partial message either */ 2117 if (InChild) 2118 ExitStat = EX_QUIT; 2119 2120 drop = TRUE; 2121 } 2122 else 2123 unlockqueue(CurEnv); 2124 2125 finis(drop, EX_OK); 2126} 2127/* 2128** INITMACROS -- initialize the macro system 2129** 2130** This just involves defining some macros that are actually 2131** used internally as metasymbols to be themselves. 2132** 2133** Parameters: 2134** none. 2135** 2136** Returns: 2137** none. 2138** 2139** Side Effects: 2140** initializes several macros to be themselves. 2141*/ 2142 2143struct metamac MetaMacros[] = 2144{ 2145 /* LHS pattern matching characters */ 2146 { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, 2147 { '=', MATCHCLASS }, { '~', MATCHNCLASS }, 2148 2149 /* these are RHS metasymbols */ 2150 { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, 2151 { '>', CALLSUBR }, 2152 2153 /* the conditional operations */ 2154 { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, 2155 2156 /* the hostname lookup characters */ 2157 { '[', HOSTBEGIN }, { ']', HOSTEND }, 2158 { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, 2159 2160 /* miscellaneous control characters */ 2161 { '&', MACRODEXPAND }, 2162 2163 { '\0' } 2164}; 2165 2166#define MACBINDING(name, mid) \ 2167 stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ 2168 MacroName[mid] = name; 2169 2170void 2171initmacros(e) 2172 register ENVELOPE *e; 2173{ 2174 register struct metamac *m; 2175 register int c; 2176 char buf[5]; 2177 extern char *MacroName[256]; 2178 2179 for (m = MetaMacros; m->metaname != '\0'; m++) 2180 { 2181 buf[0] = m->metaval; 2182 buf[1] = '\0'; 2183 define(m->metaname, newstr(buf), e); 2184 } 2185 buf[0] = MATCHREPL; 2186 buf[2] = '\0'; 2187 for (c = '0'; c <= '9'; c++) 2188 { 2189 buf[1] = c; 2190 define(c, newstr(buf), e); 2191 } 2192 2193 /* set defaults for some macros sendmail will use later */ 2194 define('n', "MAILER-DAEMON", e); 2195 2196 /* set up external names for some internal macros */ 2197 MACBINDING("opMode", MID_OPMODE); 2198 /*XXX should probably add equivalents for all short macros here XXX*/ 2199} 2200/* 2201** DISCONNECT -- remove our connection with any foreground process 2202** 2203** Parameters: 2204** droplev -- how "deeply" we should drop the line. 2205** 0 -- ignore signals, mail back errors, make sure 2206** output goes to stdout. 2207** 1 -- also, make stdout go to /dev/null. 2208** 2 -- also, disconnect from controlling terminal 2209** (only for daemon mode). 2210** e -- the current envelope. 2211** 2212** Returns: 2213** none 2214** 2215** Side Effects: 2216** Trys to insure that we are immune to vagaries of 2217** the controlling tty. 2218*/ 2219 2220void 2221disconnect(droplev, e) 2222 int droplev; 2223 register ENVELOPE *e; 2224{ 2225 int fd; 2226 2227 if (tTd(52, 1)) 2228 dprintf("disconnect: In %d Out %d, e=%lx\n", 2229 fileno(InChannel), fileno(OutChannel), (u_long) e); 2230 if (tTd(52, 100)) 2231 { 2232 dprintf("don't\n"); 2233 return; 2234 } 2235 if (LogLevel > 93) 2236 sm_syslog(LOG_DEBUG, e->e_id, 2237 "disconnect level %d", 2238 droplev); 2239 2240 /* be sure we don't get nasty signals */ 2241 (void) setsignal(SIGINT, SIG_IGN); 2242 (void) setsignal(SIGQUIT, SIG_IGN); 2243 2244 /* we can't communicate with our caller, so.... */ 2245 HoldErrs = TRUE; 2246 CurEnv->e_errormode = EM_MAIL; 2247 Verbose = 0; 2248 DisConnected = TRUE; 2249 2250 /* all input from /dev/null */ 2251 if (InChannel != stdin) 2252 { 2253 (void) fclose(InChannel); 2254 InChannel = stdin; 2255 } 2256 if (freopen("/dev/null", "r", stdin) == NULL) 2257 sm_syslog(LOG_ERR, e->e_id, 2258 "disconnect: freopen(\"/dev/null\") failed: %s", 2259 errstring(errno)); 2260 2261 /* output to the transcript */ 2262 if (OutChannel != stdout) 2263 { 2264 (void) fclose(OutChannel); 2265 OutChannel = stdout; 2266 } 2267 if (droplev > 0) 2268 { 2269 fd = open("/dev/null", O_WRONLY, 0666); 2270 if (fd == -1) 2271 sm_syslog(LOG_ERR, e->e_id, 2272 "disconnect: open(\"/dev/null\") failed: %s", 2273 errstring(errno)); 2274 (void) fflush(stdout); 2275 (void) dup2(fd, STDOUT_FILENO); 2276 (void) dup2(fd, STDERR_FILENO); 2277 (void) close(fd); 2278 } 2279 2280 /* drop our controlling TTY completely if possible */ 2281 if (droplev > 1) 2282 { 2283 (void) setsid(); 2284 errno = 0; 2285 } 2286 2287#if XDEBUG 2288 checkfd012("disconnect"); 2289#endif /* XDEBUG */ 2290 2291 if (LogLevel > 71) 2292 sm_syslog(LOG_DEBUG, e->e_id, 2293 "in background, pid=%d", 2294 getpid()); 2295 2296 errno = 0; 2297} 2298 2299static void 2300obsolete(argv) 2301 char *argv[]; 2302{ 2303 register char *ap; 2304 register char *op; 2305 2306 while ((ap = *++argv) != NULL) 2307 { 2308 /* Return if "--" or not an option of any form. */ 2309 if (ap[0] != '-' || ap[1] == '-') 2310 return; 2311 2312 /* skip over options that do have a value */ 2313 op = strchr(OPTIONS, ap[1]); 2314 if (op != NULL && *++op == ':' && ap[2] == '\0' && 2315 ap[1] != 'd' && 2316#if defined(sony_news) 2317 ap[1] != 'E' && ap[1] != 'J' && 2318#endif /* defined(sony_news) */ 2319 argv[1] != NULL && argv[1][0] != '-') 2320 { 2321 argv++; 2322 continue; 2323 } 2324 2325 /* If -C doesn't have an argument, use sendmail.cf. */ 2326#define __DEFPATH "sendmail.cf" 2327 if (ap[1] == 'C' && ap[2] == '\0') 2328 { 2329 *argv = xalloc(sizeof(__DEFPATH) + 2); 2330 (void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s", 2331 __DEFPATH); 2332 } 2333 2334 /* If -q doesn't have an argument, run it once. */ 2335 if (ap[1] == 'q' && ap[2] == '\0') 2336 *argv = "-q0"; 2337 2338 /* if -d doesn't have an argument, use 0-99.1 */ 2339 if (ap[1] == 'd' && ap[2] == '\0') 2340 *argv = "-d0-99.1"; 2341 2342#if defined(sony_news) 2343 /* if -E doesn't have an argument, use -EC */ 2344 if (ap[1] == 'E' && ap[2] == '\0') 2345 *argv = "-EC"; 2346 2347 /* if -J doesn't have an argument, use -JJ */ 2348 if (ap[1] == 'J' && ap[2] == '\0') 2349 *argv = "-JJ"; 2350#endif /* defined(sony_news) */ 2351 } 2352} 2353/* 2354** AUTH_WARNING -- specify authorization warning 2355** 2356** Parameters: 2357** e -- the current envelope. 2358** msg -- the text of the message. 2359** args -- arguments to the message. 2360** 2361** Returns: 2362** none. 2363*/ 2364 2365void 2366#ifdef __STDC__ 2367auth_warning(register ENVELOPE *e, const char *msg, ...) 2368#else /* __STDC__ */ 2369auth_warning(e, msg, va_alist) 2370 register ENVELOPE *e; 2371 const char *msg; 2372 va_dcl 2373#endif /* __STDC__ */ 2374{ 2375 char buf[MAXLINE]; 2376 VA_LOCAL_DECL 2377 2378 if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) 2379 { 2380 register char *p; 2381 static char hostbuf[48]; 2382 2383 if (hostbuf[0] == '\0') 2384 (void) myhostname(hostbuf, sizeof hostbuf); 2385 2386 (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); 2387 p = &buf[strlen(buf)]; 2388 VA_START(msg); 2389 vsnprintf(p, SPACELEFT(buf, p), msg, ap); 2390 VA_END; 2391 addheader("X-Authentication-Warning", buf, 0, &e->e_header); 2392 if (LogLevel > 3) 2393 sm_syslog(LOG_INFO, e->e_id, 2394 "Authentication-Warning: %.400s", 2395 buf); 2396 } 2397} 2398/* 2399** GETEXTENV -- get from external environment 2400** 2401** Parameters: 2402** envar -- the name of the variable to retrieve 2403** 2404** Returns: 2405** The value, if any. 2406*/ 2407 2408char * 2409getextenv(envar) 2410 const char *envar; 2411{ 2412 char **envp; 2413 int l; 2414 2415 l = strlen(envar); 2416 for (envp = ExternalEnviron; *envp != NULL; envp++) 2417 { 2418 if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') 2419 return &(*envp)[l + 1]; 2420 } 2421 return NULL; 2422} 2423/* 2424** SETUSERENV -- set an environment in the propogated environment 2425** 2426** Parameters: 2427** envar -- the name of the environment variable. 2428** value -- the value to which it should be set. If 2429** null, this is extracted from the incoming 2430** environment. If that is not set, the call 2431** to setuserenv is ignored. 2432** 2433** Returns: 2434** none. 2435*/ 2436 2437void 2438setuserenv(envar, value) 2439 const char *envar; 2440 const char *value; 2441{ 2442 int i, l; 2443 char **evp = UserEnviron; 2444 char *p; 2445 2446 if (value == NULL) 2447 { 2448 value = getextenv(envar); 2449 if (value == NULL) 2450 return; 2451 } 2452 2453 i = strlen(envar) + 1; 2454 l = strlen(value) + i + 1; 2455 p = (char *) xalloc(l); 2456 (void) snprintf(p, l, "%s=%s", envar, value); 2457 2458 while (*evp != NULL && strncmp(*evp, p, i) != 0) 2459 evp++; 2460 if (*evp != NULL) 2461 { 2462 *evp++ = p; 2463 } 2464 else if (evp < &UserEnviron[MAXUSERENVIRON]) 2465 { 2466 *evp++ = p; 2467 *evp = NULL; 2468 } 2469 2470 /* make sure it is in our environment as well */ 2471 if (putenv(p) < 0) 2472 syserr("setuserenv: putenv(%s) failed", p); 2473} 2474/* 2475** DUMPSTATE -- dump state 2476** 2477** For debugging. 2478*/ 2479 2480void 2481dumpstate(when) 2482 char *when; 2483{ 2484 register char *j = macvalue('j', CurEnv); 2485 int rs; 2486 extern int NextMacroId; 2487 2488 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2489 "--- dumping state on %s: $j = %s ---", 2490 when, 2491 j == NULL ? "<NULL>" : j); 2492 if (j != NULL) 2493 { 2494 if (!wordinclass(j, 'w')) 2495 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2496 "*** $j not in $=w ***"); 2497 } 2498 sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); 2499 sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n", 2500 NextMacroId, MAXMACROID); 2501 sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); 2502 printopenfds(TRUE); 2503 sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); 2504 mci_dump_all(TRUE); 2505 rs = strtorwset("debug_dumpstate", NULL, ST_FIND); 2506 if (rs > 0) 2507 { 2508 int status; 2509 register char **pvp; 2510 char *pv[MAXATOM + 1]; 2511 2512 pv[0] = NULL; 2513 status = rewrite(pv, rs, 0, CurEnv); 2514 sm_syslog(LOG_DEBUG, CurEnv->e_id, 2515 "--- ruleset debug_dumpstate returns stat %d, pv: ---", 2516 status); 2517 for (pvp = pv; *pvp != NULL; pvp++) 2518 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); 2519 } 2520 sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); 2521} 2522 2523 2524/* ARGSUSED */ 2525SIGFUNC_DECL 2526sigusr1(sig) 2527 int sig; 2528{ 2529 dumpstate("user signal"); 2530 return SIGFUNC_RETURN; 2531} 2532 2533 2534/* ARGSUSED */ 2535SIGFUNC_DECL 2536sighup(sig) 2537 int sig; 2538{ 2539 int i; 2540 extern int DtableSize; 2541 2542 clear_events(); 2543 (void) alarm(0); 2544 if (SaveArgv[0][0] != '/') 2545 { 2546 if (LogLevel > 3) 2547 sm_syslog(LOG_INFO, NOQID, 2548 "could not restart: need full path"); 2549 finis(FALSE, EX_OSFILE); 2550 } 2551 if (LogLevel > 3) 2552 sm_syslog(LOG_INFO, NOQID, "restarting %s %s", 2553 sig == 0 ? "due to control command" : "on signal", 2554 SaveArgv[0]); 2555 2556 /* Control socket restart? */ 2557 if (sig != 0) 2558 (void) releasesignal(SIGHUP); 2559 2560 closecontrolsocket(TRUE); 2561 if (drop_privileges(TRUE) != EX_OK) 2562 { 2563 if (LogLevel > 0) 2564 sm_syslog(LOG_ALERT, NOQID, 2565 "could not set[ug]id(%d, %d): %m", 2566 RunAsUid, RunAsGid); 2567 finis(FALSE, EX_OSERR); 2568 } 2569 2570 /* arrange for all the files to be closed */ 2571 for (i = 3; i < DtableSize; i++) 2572 { 2573 register int j; 2574 2575 if ((j = fcntl(i, F_GETFD, 0)) != -1) 2576 (void) fcntl(i, F_SETFD, j | FD_CLOEXEC); 2577 } 2578 2579 (void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); 2580 if (LogLevel > 0) 2581 sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", 2582 SaveArgv[0]); 2583 finis(FALSE, EX_OSFILE); 2584} 2585/* 2586** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option 2587** 2588** Parameters: 2589** to_real_uid -- if set, drop to the real uid instead 2590** of the RunAsUser. 2591** 2592** Returns: 2593** EX_OSERR if the setuid failed. 2594** EX_OK otherwise. 2595*/ 2596 2597int 2598drop_privileges(to_real_uid) 2599 bool to_real_uid; 2600{ 2601 int rval = EX_OK; 2602 GIDSET_T emptygidset[1]; 2603 2604 if (tTd(47, 1)) 2605 dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n", 2606 (int)to_real_uid, (int)RealUid, 2607 (int)RealGid, (int)RunAsUid, (int)RunAsGid); 2608 2609 if (to_real_uid) 2610 { 2611 RunAsUserName = RealUserName; 2612 RunAsUid = RealUid; 2613 RunAsGid = RealGid; 2614 } 2615 2616 /* make sure no one can grab open descriptors for secret files */ 2617 endpwent(); 2618 2619 /* reset group permissions; these can be set later */ 2620 emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid(); 2621 if (setgroups(1, emptygidset) == -1 && geteuid() == 0) 2622 { 2623 syserr("drop_privileges: setgroups(1, %d) failed", 2624 (int)emptygidset[0]); 2625 rval = EX_OSERR; 2626 } 2627 2628 /* reset primary group and user id */ 2629 if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0) 2630 { 2631 syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid); 2632 rval = EX_OSERR; 2633 } 2634 if (to_real_uid || RunAsUid != 0) 2635 { 2636 uid_t euid = geteuid(); 2637 2638 if (setuid(RunAsUid) < 0) 2639 { 2640 syserr("drop_privileges: setuid(%d) failed", 2641 (int)RunAsUid); 2642 rval = EX_OSERR; 2643 } 2644 else if (RunAsUid != 0 && setuid(0) == 0) 2645 { 2646 /* 2647 ** Believe it or not, the Linux capability model 2648 ** allows a non-root process to override setuid() 2649 ** on a process running as root and prevent that 2650 ** process from dropping privileges. 2651 */ 2652 2653 syserr("drop_privileges: setuid(0) succeeded (when it should not)"); 2654 rval = EX_OSERR; 2655 } 2656 else if (RunAsUid != euid && setuid(euid) == 0) 2657 { 2658 /* 2659 ** Some operating systems will keep the saved-uid 2660 ** if a non-root effective-uid calls setuid(real-uid) 2661 ** making it possible to set it back again later. 2662 */ 2663 2664 syserr("drop_privileges: Unable to drop non-root set-user-id privileges"); 2665 rval = EX_OSERR; 2666 } 2667 } 2668 if (tTd(47, 5)) 2669 { 2670 dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n", 2671 (int)geteuid(), (int)getuid(), 2672 (int)getegid(), (int)getgid()); 2673 dprintf("drop_privileges: RunAsUser = %d:%d\n", 2674 (int)RunAsUid, (int)RunAsGid); 2675 if (tTd(47, 10)) 2676 dprintf("drop_privileges: rval = %d\n", rval); 2677 } 2678 return rval; 2679} 2680/* 2681** FILL_FD -- make sure a file descriptor has been properly allocated 2682** 2683** Used to make sure that stdin/out/err are allocated on startup 2684** 2685** Parameters: 2686** fd -- the file descriptor to be filled. 2687** where -- a string used for logging. If NULL, this is 2688** being called on startup, and logging should 2689** not be done. 2690** 2691** Returns: 2692** none 2693*/ 2694 2695void 2696fill_fd(fd, where) 2697 int fd; 2698 char *where; 2699{ 2700 int i; 2701 struct stat stbuf; 2702 2703 if (fstat(fd, &stbuf) >= 0 || errno != EBADF) 2704 return; 2705 2706 if (where != NULL) 2707 syserr("fill_fd: %s: fd %d not open", where, fd); 2708 else 2709 MissingFds |= 1 << fd; 2710 i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666); 2711 if (i < 0) 2712 { 2713 syserr("!fill_fd: %s: cannot open /dev/null", 2714 where == NULL ? "startup" : where); 2715 } 2716 if (fd != i) 2717 { 2718 (void) dup2(i, fd); 2719 (void) close(i); 2720 } 2721} 2722/* 2723** TESTMODELINE -- process a test mode input line 2724** 2725** Parameters: 2726** line -- the input line. 2727** e -- the current environment. 2728** Syntax: 2729** # a comment 2730** .X process X as a configuration line 2731** =X dump a configuration item (such as mailers) 2732** $X dump a macro or class 2733** /X try an activity 2734** X normal process through rule set X 2735*/ 2736 2737static void 2738testmodeline(line, e) 2739 char *line; 2740 ENVELOPE *e; 2741{ 2742 register char *p; 2743 char *q; 2744 auto char *delimptr; 2745 int mid; 2746 int i, rs; 2747 STAB *map; 2748 char **s; 2749 struct rewrite *rw; 2750 ADDRESS a; 2751 static int tryflags = RF_COPYNONE; 2752 char exbuf[MAXLINE]; 2753 extern u_char TokTypeNoC[]; 2754 2755#if _FFR_ADDR_TYPE 2756 define(macid("{addr_type}", NULL), "e r", e); 2757#endif /* _FFR_ADDR_TYPE */ 2758 switch (line[0]) 2759 { 2760 case '#': 2761 case '\0': 2762 return; 2763 2764 case '?': 2765 help("-bt", e); 2766 return; 2767 2768 case '.': /* config-style settings */ 2769 switch (line[1]) 2770 { 2771 case 'D': 2772 mid = macid(&line[2], &delimptr); 2773 if (mid == '\0') 2774 return; 2775 translate_dollars(delimptr); 2776 define(mid, newstr(delimptr), e); 2777 break; 2778 2779 case 'C': 2780 if (line[2] == '\0') /* not to call syserr() */ 2781 return; 2782 2783 mid = macid(&line[2], &delimptr); 2784 if (mid == '\0') 2785 return; 2786 translate_dollars(delimptr); 2787 expand(delimptr, exbuf, sizeof exbuf, e); 2788 p = exbuf; 2789 while (*p != '\0') 2790 { 2791 register char *wd; 2792 char delim; 2793 2794 while (*p != '\0' && isascii(*p) && isspace(*p)) 2795 p++; 2796 wd = p; 2797 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 2798 p++; 2799 delim = *p; 2800 *p = '\0'; 2801 if (wd[0] != '\0') 2802 setclass(mid, wd); 2803 *p = delim; 2804 } 2805 break; 2806 2807 case '\0': 2808 printf("Usage: .[DC]macro value(s)\n"); 2809 break; 2810 2811 default: 2812 printf("Unknown \".\" command %s\n", line); 2813 break; 2814 } 2815 return; 2816 2817 case '=': /* config-style settings */ 2818 switch (line[1]) 2819 { 2820 case 'S': /* dump rule set */ 2821 rs = strtorwset(&line[2], NULL, ST_FIND); 2822 if (rs < 0) 2823 { 2824 printf("Undefined ruleset %s\n", &line[2]); 2825 return; 2826 } 2827 rw = RewriteRules[rs]; 2828 if (rw == NULL) 2829 return; 2830 do 2831 { 2832 (void) putchar('R'); 2833 s = rw->r_lhs; 2834 while (*s != NULL) 2835 { 2836 xputs(*s++); 2837 (void) putchar(' '); 2838 } 2839 (void) putchar('\t'); 2840 (void) putchar('\t'); 2841 s = rw->r_rhs; 2842 while (*s != NULL) 2843 { 2844 xputs(*s++); 2845 (void) putchar(' '); 2846 } 2847 (void) putchar('\n'); 2848 } while ((rw = rw->r_next) != NULL); 2849 break; 2850 2851 case 'M': 2852 for (i = 0; i < MAXMAILERS; i++) 2853 { 2854 if (Mailer[i] != NULL) 2855 printmailer(Mailer[i]); 2856 } 2857 break; 2858 2859 case '\0': 2860 printf("Usage: =Sruleset or =M\n"); 2861 break; 2862 2863 default: 2864 printf("Unknown \"=\" command %s\n", line); 2865 break; 2866 } 2867 return; 2868 2869 case '-': /* set command-line-like opts */ 2870 switch (line[1]) 2871 { 2872 case 'd': 2873 tTflag(&line[2]); 2874 break; 2875 2876 case '\0': 2877 printf("Usage: -d{debug arguments}\n"); 2878 break; 2879 2880 default: 2881 printf("Unknown \"-\" command %s\n", line); 2882 break; 2883 } 2884 return; 2885 2886 case '$': 2887 if (line[1] == '=') 2888 { 2889 mid = macid(&line[2], NULL); 2890 if (mid != '\0') 2891 stabapply(dump_class, mid); 2892 return; 2893 } 2894 mid = macid(&line[1], NULL); 2895 if (mid == '\0') 2896 return; 2897 p = macvalue(mid, e); 2898 if (p == NULL) 2899 printf("Undefined\n"); 2900 else 2901 { 2902 xputs(p); 2903 printf("\n"); 2904 } 2905 return; 2906 2907 case '/': /* miscellaneous commands */ 2908 p = &line[strlen(line)]; 2909 while (--p >= line && isascii(*p) && isspace(*p)) 2910 *p = '\0'; 2911 p = strpbrk(line, " \t"); 2912 if (p != NULL) 2913 { 2914 while (isascii(*p) && isspace(*p)) 2915 *p++ = '\0'; 2916 } 2917 else 2918 p = ""; 2919 if (line[1] == '\0') 2920 { 2921 printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); 2922 return; 2923 } 2924 if (strcasecmp(&line[1], "quit") == 0) 2925 { 2926 CurEnv->e_id = NULL; 2927 finis(TRUE, ExitStat); 2928 } 2929 if (strcasecmp(&line[1], "mx") == 0) 2930 { 2931#if NAMED_BIND 2932 /* look up MX records */ 2933 int nmx; 2934 auto int rcode; 2935 char *mxhosts[MAXMXHOSTS + 1]; 2936 2937 if (*p == '\0') 2938 { 2939 printf("Usage: /mx address\n"); 2940 return; 2941 } 2942 nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode); 2943 printf("getmxrr(%s) returns %d value(s):\n", p, nmx); 2944 for (i = 0; i < nmx; i++) 2945 printf("\t%s\n", mxhosts[i]); 2946#else /* NAMED_BIND */ 2947 printf("No MX code compiled in\n"); 2948#endif /* NAMED_BIND */ 2949 } 2950 else if (strcasecmp(&line[1], "canon") == 0) 2951 { 2952 char host[MAXHOSTNAMELEN]; 2953 2954 if (*p == '\0') 2955 { 2956 printf("Usage: /canon address\n"); 2957 return; 2958 } 2959 else if (strlcpy(host, p, sizeof host) >= sizeof host) 2960 { 2961 printf("Name too long\n"); 2962 return; 2963 } 2964 (void) getcanonname(host, sizeof host, HasWildcardMX); 2965 printf("getcanonname(%s) returns %s\n", p, host); 2966 } 2967 else if (strcasecmp(&line[1], "map") == 0) 2968 { 2969 auto int rcode = EX_OK; 2970 char *av[2]; 2971 2972 if (*p == '\0') 2973 { 2974 printf("Usage: /map mapname key\n"); 2975 return; 2976 } 2977 for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++) 2978 continue; 2979 if (*q == '\0') 2980 { 2981 printf("No key specified\n"); 2982 return; 2983 } 2984 *q++ = '\0'; 2985 map = stab(p, ST_MAP, ST_FIND); 2986 if (map == NULL) 2987 { 2988 printf("Map named \"%s\" not found\n", p); 2989 return; 2990 } 2991 if (!bitset(MF_OPEN, map->s_map.map_mflags) && 2992 !openmap(&(map->s_map))) 2993 { 2994 printf("Map named \"%s\" not open\n", p); 2995 return; 2996 } 2997 printf("map_lookup: %s (%s) ", p, q); 2998 av[0] = q; 2999 av[1] = NULL; 3000 p = (*map->s_map.map_class->map_lookup) 3001 (&map->s_map, q, av, &rcode); 3002 if (p == NULL) 3003 printf("no match (%d)\n", rcode); 3004 else 3005 printf("returns %s (%d)\n", p, rcode); 3006 } 3007 else if (strcasecmp(&line[1], "try") == 0) 3008 { 3009 MAILER *m; 3010 STAB *st; 3011 auto int rcode = EX_OK; 3012 3013 q = strpbrk(p, " \t"); 3014 if (q != NULL) 3015 { 3016 while (isascii(*q) && isspace(*q)) 3017 *q++ = '\0'; 3018 } 3019 if (q == NULL || *q == '\0') 3020 { 3021 printf("Usage: /try mailer address\n"); 3022 return; 3023 } 3024 st = stab(p, ST_MAILER, ST_FIND); 3025 if (st == NULL) 3026 { 3027 printf("Unknown mailer %s\n", p); 3028 return; 3029 } 3030 m = st->s_mailer; 3031 printf("Trying %s %s address %s for mailer %s\n", 3032 bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", 3033 bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", 3034 q, p); 3035 p = remotename(q, m, tryflags, &rcode, CurEnv); 3036 printf("Rcode = %d, addr = %s\n", 3037 rcode, p == NULL ? "<NULL>" : p); 3038 e->e_to = NULL; 3039 } 3040 else if (strcasecmp(&line[1], "tryflags") == 0) 3041 { 3042 if (*p == '\0') 3043 { 3044 printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); 3045 return; 3046 } 3047 for (; *p != '\0'; p++) 3048 { 3049 switch (*p) 3050 { 3051 case 'H': 3052 case 'h': 3053 tryflags |= RF_HEADERADDR; 3054 break; 3055 3056 case 'E': 3057 case 'e': 3058 tryflags &= ~RF_HEADERADDR; 3059 break; 3060 3061 case 'S': 3062 case 's': 3063 tryflags |= RF_SENDERADDR; 3064 break; 3065 3066 case 'R': 3067 case 'r': 3068 tryflags &= ~RF_SENDERADDR; 3069 break; 3070 } 3071 } 3072#if _FFR_ADDR_TYPE 3073 exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e'; 3074 exbuf[1] = ' '; 3075 exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r'; 3076 exbuf[3] = '\0'; 3077 define(macid("{addr_type}", NULL), newstr(exbuf), e); 3078#endif /* _FFR_ADDR_TYPE */ 3079 } 3080 else if (strcasecmp(&line[1], "parse") == 0) 3081 { 3082 if (*p == '\0') 3083 { 3084 printf("Usage: /parse address\n"); 3085 return; 3086 } 3087 q = crackaddr(p); 3088 printf("Cracked address = "); 3089 xputs(q); 3090 printf("\nParsing %s %s address\n", 3091 bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", 3092 bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); 3093 if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) 3094 printf("Cannot parse\n"); 3095 else if (a.q_host != NULL && a.q_host[0] != '\0') 3096 printf("mailer %s, host %s, user %s\n", 3097 a.q_mailer->m_name, a.q_host, a.q_user); 3098 else 3099 printf("mailer %s, user %s\n", 3100 a.q_mailer->m_name, a.q_user); 3101 e->e_to = NULL; 3102 } 3103 else 3104 { 3105 printf("Unknown \"/\" command %s\n", line); 3106 } 3107 return; 3108 } 3109 3110 for (p = line; isascii(*p) && isspace(*p); p++) 3111 continue; 3112 q = p; 3113 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 3114 p++; 3115 if (*p == '\0') 3116 { 3117 printf("No address!\n"); 3118 return; 3119 } 3120 *p = '\0'; 3121 if (invalidaddr(p + 1, NULL)) 3122 return; 3123 do 3124 { 3125 register char **pvp; 3126 char pvpbuf[PSBUFSIZE]; 3127 3128 pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, 3129 &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL); 3130 if (pvp == NULL) 3131 continue; 3132 p = q; 3133 while (*p != '\0') 3134 { 3135 int status; 3136 3137 rs = strtorwset(p, NULL, ST_FIND); 3138 if (rs < 0) 3139 { 3140 printf("Undefined ruleset %s\n", p); 3141 break; 3142 } 3143 status = rewrite(pvp, rs, 0, e); 3144 if (status != EX_OK) 3145 printf("== Ruleset %s (%d) status %d\n", 3146 p, rs, status); 3147 while (*p != '\0' && *p++ != ',') 3148 continue; 3149 } 3150 } while (*(p = delimptr) != '\0'); 3151} 3152 3153static void 3154dump_class(s, id) 3155 register STAB *s; 3156 int id; 3157{ 3158 if (s->s_type != ST_CLASS) 3159 return; 3160 if (bitnset(id & 0xff, s->s_class)) 3161 printf("%s\n", s->s_name); 3162} 3163