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