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