|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1983 Eric P. Allman ! 3: * Copyright (c) 1988 Regents of the University of California. ! 4: * All rights reserved. ! 5: * ! 6: * Redistribution and use in source and binary forms are permitted ! 7: * provided that the above copyright notice and this paragraph are ! 8: * duplicated in all such forms and that any documentation, ! 9: * advertising materials, and other materials related to such ! 10: * distribution and use acknowledge that the software was developed ! 11: * by the University of California, Berkeley. The name of the ! 12: * University may not be used to endorse or promote products derived ! 13: * from this software without specific prior written permission. ! 14: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 15: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 16: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 17: */ ! 18: ! 19: #ifndef lint ! 20: static char sccsid[] = "@(#)recipient.c 5.12 (Berkeley) 6/30/88"; ! 21: #endif /* not lint */ ! 22: ! 23: # include <pwd.h> ! 24: # include "sendmail.h" ! 25: # include <sys/stat.h> ! 26: ! 27: /* ! 28: ** SENDTOLIST -- Designate a send list. ! 29: ** ! 30: ** The parameter is a comma-separated list of people to send to. ! 31: ** This routine arranges to send to all of them. ! 32: ** ! 33: ** Parameters: ! 34: ** list -- the send list. ! 35: ** ctladdr -- the address template for the person to ! 36: ** send to -- effective uid/gid are important. ! 37: ** This is typically the alias that caused this ! 38: ** expansion. ! 39: ** sendq -- a pointer to the head of a queue to put ! 40: ** these people into. ! 41: ** ! 42: ** Returns: ! 43: ** none ! 44: ** ! 45: ** Side Effects: ! 46: ** none. ! 47: */ ! 48: ! 49: # define MAXRCRSN 10 ! 50: ! 51: sendtolist(list, ctladdr, sendq) ! 52: char *list; ! 53: ADDRESS *ctladdr; ! 54: ADDRESS **sendq; ! 55: { ! 56: register char *p; ! 57: register ADDRESS *al; /* list of addresses to send to */ ! 58: bool firstone; /* set on first address sent */ ! 59: bool selfref; /* set if this list includes ctladdr */ ! 60: char delimiter; /* the address delimiter */ ! 61: ! 62: # ifdef DEBUG ! 63: if (tTd(25, 1)) ! 64: { ! 65: printf("sendto: %s\n ctladdr=", list); ! 66: printaddr(ctladdr, FALSE); ! 67: } ! 68: # endif DEBUG ! 69: ! 70: /* heuristic to determine old versus new style addresses */ ! 71: if (ctladdr == NULL && ! 72: (index(list, ',') != NULL || index(list, ';') != NULL || ! 73: index(list, '<') != NULL || index(list, '(') != NULL)) ! 74: CurEnv->e_flags &= ~EF_OLDSTYLE; ! 75: delimiter = ' '; ! 76: if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL) ! 77: delimiter = ','; ! 78: ! 79: firstone = TRUE; ! 80: selfref = FALSE; ! 81: al = NULL; ! 82: ! 83: for (p = list; *p != '\0'; ) ! 84: { ! 85: register ADDRESS *a; ! 86: extern char *DelimChar; /* defined in prescan */ ! 87: ! 88: /* parse the address */ ! 89: while (isspace(*p) || *p == ',') ! 90: p++; ! 91: a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter); ! 92: p = DelimChar; ! 93: if (a == NULL) ! 94: continue; ! 95: a->q_next = al; ! 96: a->q_alias = ctladdr; ! 97: ! 98: /* see if this should be marked as a primary address */ ! 99: if (ctladdr == NULL || ! 100: (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags))) ! 101: a->q_flags |= QPRIMARY; ! 102: ! 103: /* put on send queue or suppress self-reference */ ! 104: if (ctladdr != NULL && sameaddr(ctladdr, a)) ! 105: selfref = TRUE; ! 106: else ! 107: al = a; ! 108: firstone = FALSE; ! 109: } ! 110: ! 111: /* if this alias doesn't include itself, delete ctladdr */ ! 112: if (!selfref && ctladdr != NULL) ! 113: ctladdr->q_flags |= QDONTSEND; ! 114: ! 115: /* arrange to send to everyone on the local send list */ ! 116: while (al != NULL) ! 117: { ! 118: register ADDRESS *a = al; ! 119: extern ADDRESS *recipient(); ! 120: ! 121: al = a->q_next; ! 122: a = recipient(a, sendq); ! 123: ! 124: /* arrange to inherit full name */ ! 125: if (a->q_fullname == NULL && ctladdr != NULL) ! 126: a->q_fullname = ctladdr->q_fullname; ! 127: } ! 128: ! 129: CurEnv->e_to = NULL; ! 130: } ! 131: /* ! 132: ** RECIPIENT -- Designate a message recipient ! 133: ** ! 134: ** Saves the named person for future mailing. ! 135: ** ! 136: ** Parameters: ! 137: ** a -- the (preparsed) address header for the recipient. ! 138: ** sendq -- a pointer to the head of a queue to put the ! 139: ** recipient in. Duplicate supression is done ! 140: ** in this queue. ! 141: ** ! 142: ** Returns: ! 143: ** The actual address in the queue. This will be "a" if ! 144: ** the address is not a duplicate, else the original address. ! 145: ** ! 146: ** Side Effects: ! 147: ** none. ! 148: */ ! 149: ! 150: ADDRESS * ! 151: recipient(a, sendq) ! 152: register ADDRESS *a; ! 153: register ADDRESS **sendq; ! 154: { ! 155: register ADDRESS *q; ! 156: ADDRESS **pq; ! 157: register struct mailer *m; ! 158: register char *p; ! 159: bool quoted = FALSE; /* set if the addr has a quote bit */ ! 160: char buf[MAXNAME]; /* unquoted image of the user name */ ! 161: extern ADDRESS *getctladdr(); ! 162: extern bool safefile(); ! 163: ! 164: CurEnv->e_to = a->q_paddr; ! 165: m = a->q_mailer; ! 166: errno = 0; ! 167: # ifdef DEBUG ! 168: if (tTd(26, 1)) ! 169: { ! 170: printf("\nrecipient: "); ! 171: printaddr(a, FALSE); ! 172: } ! 173: # endif DEBUG ! 174: ! 175: /* break aliasing loops */ ! 176: if (AliasLevel > MAXRCRSN) ! 177: { ! 178: usrerr("aliasing/forwarding loop broken"); ! 179: return (a); ! 180: } ! 181: ! 182: /* ! 183: ** Finish setting up address structure. ! 184: */ ! 185: ! 186: /* set the queue timeout */ ! 187: a->q_timeout = TimeOut; ! 188: ! 189: /* map user & host to lower case if requested on non-aliases */ ! 190: if (a->q_alias == NULL) ! 191: loweraddr(a); ! 192: ! 193: /* get unquoted user for file, program or user.name check */ ! 194: (void) strcpy(buf, a->q_user); ! 195: for (p = buf; *p != '\0' && !quoted; p++) ! 196: { ! 197: if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377)) ! 198: quoted = TRUE; ! 199: } ! 200: stripquotes(buf, TRUE); ! 201: ! 202: /* do sickly crude mapping for program mailing, etc. */ ! 203: if (m == LocalMailer && buf[0] == '|') ! 204: { ! 205: a->q_mailer = m = ProgMailer; ! 206: a->q_user++; ! 207: if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail) ! 208: { ! 209: a->q_flags |= QDONTSEND|QBADADDR; ! 210: usrerr("Cannot mail directly to programs"); ! 211: } ! 212: } ! 213: ! 214: /* ! 215: ** Look up this person in the recipient list. ! 216: ** If they are there already, return, otherwise continue. ! 217: ** If the list is empty, just add it. Notice the cute ! 218: ** hack to make from addresses suppress things correctly: ! 219: ** the QDONTSEND bit will be set in the send list. ! 220: ** [Please note: the emphasis is on "hack."] ! 221: */ ! 222: ! 223: for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) ! 224: { ! 225: if (!ForceMail && sameaddr(q, a)) ! 226: { ! 227: # ifdef DEBUG ! 228: if (tTd(26, 1)) ! 229: { ! 230: printf("%s in sendq: ", a->q_paddr); ! 231: printaddr(q, FALSE); ! 232: } ! 233: # endif DEBUG ! 234: if (!bitset(QDONTSEND, a->q_flags)) ! 235: message(Arpa_Info, "duplicate suppressed"); ! 236: if (!bitset(QPRIMARY, q->q_flags)) ! 237: q->q_flags |= a->q_flags; ! 238: return (q); ! 239: } ! 240: } ! 241: ! 242: /* add address on list */ ! 243: *pq = a; ! 244: a->q_next = NULL; ! 245: CurEnv->e_nrcpts++; ! 246: ! 247: /* ! 248: ** Alias the name and handle :include: specs. ! 249: */ ! 250: ! 251: if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags)) ! 252: { ! 253: if (strncmp(a->q_user, ":include:", 9) == 0) ! 254: { ! 255: a->q_flags |= QDONTSEND; ! 256: if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail) ! 257: { ! 258: a->q_flags |= QBADADDR; ! 259: usrerr("Cannot mail directly to :include:s"); ! 260: } ! 261: else ! 262: { ! 263: message(Arpa_Info, "including file %s", &a->q_user[9]); ! 264: include(&a->q_user[9], " sending", a, sendq); ! 265: } ! 266: } ! 267: else ! 268: alias(a, sendq); ! 269: } ! 270: ! 271: /* ! 272: ** If the user is local and still being sent, verify that ! 273: ** the address is good. If it is, try to forward. ! 274: ** If the address is already good, we have a forwarding ! 275: ** loop. This can be broken by just sending directly to ! 276: ** the user (which is probably correct anyway). ! 277: */ ! 278: ! 279: if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer) ! 280: { ! 281: struct stat stb; ! 282: extern bool writable(); ! 283: ! 284: /* see if this is to a file */ ! 285: if (buf[0] == '/') ! 286: { ! 287: p = rindex(buf, '/'); ! 288: /* check if writable or creatable */ ! 289: if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail) ! 290: { ! 291: a->q_flags |= QDONTSEND|QBADADDR; ! 292: usrerr("Cannot mail directly to files"); ! 293: } ! 294: else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : ! 295: (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC))) ! 296: { ! 297: a->q_flags |= QBADADDR; ! 298: giveresponse(EX_CANTCREAT, m, CurEnv); ! 299: } ! 300: } ! 301: else ! 302: { ! 303: register struct passwd *pw; ! 304: extern struct passwd *finduser(); ! 305: ! 306: /* warning -- finduser may trash buf */ ! 307: pw = finduser(buf); ! 308: if (pw == NULL) ! 309: { ! 310: a->q_flags |= QBADADDR; ! 311: giveresponse(EX_NOUSER, m, CurEnv); ! 312: } ! 313: else ! 314: { ! 315: char nbuf[MAXNAME]; ! 316: ! 317: if (strcmp(a->q_user, pw->pw_name) != 0) ! 318: { ! 319: a->q_user = newstr(pw->pw_name); ! 320: (void) strcpy(buf, pw->pw_name); ! 321: } ! 322: a->q_home = newstr(pw->pw_dir); ! 323: a->q_uid = pw->pw_uid; ! 324: a->q_gid = pw->pw_gid; ! 325: a->q_flags |= QGOODUID; ! 326: buildfname(pw->pw_gecos, pw->pw_name, nbuf); ! 327: if (nbuf[0] != '\0') ! 328: a->q_fullname = newstr(nbuf); ! 329: if (!quoted) ! 330: forward(a, sendq); ! 331: } ! 332: } ! 333: } ! 334: return (a); ! 335: } ! 336: /* ! 337: ** FINDUSER -- find the password entry for a user. ! 338: ** ! 339: ** This looks a lot like getpwnam, except that it may want to ! 340: ** do some fancier pattern matching in /etc/passwd. ! 341: ** ! 342: ** This routine contains most of the time of many sendmail runs. ! 343: ** It deserves to be optimized. ! 344: ** ! 345: ** Parameters: ! 346: ** name -- the name to match against. ! 347: ** ! 348: ** Returns: ! 349: ** A pointer to a pw struct. ! 350: ** NULL if name is unknown or ambiguous. ! 351: ** ! 352: ** Side Effects: ! 353: ** may modify name. ! 354: */ ! 355: ! 356: struct passwd * ! 357: finduser(name) ! 358: char *name; ! 359: { ! 360: register struct passwd *pw; ! 361: register char *p; ! 362: extern struct passwd *getpwent(); ! 363: extern struct passwd *getpwnam(); ! 364: ! 365: /* map upper => lower case */ ! 366: for (p = name; *p != '\0'; p++) ! 367: { ! 368: if (isascii(*p) && isupper(*p)) ! 369: *p = tolower(*p); ! 370: } ! 371: ! 372: /* look up this login name using fast path */ ! 373: if ((pw = getpwnam(name)) != NULL) ! 374: return (pw); ! 375: ! 376: /* search for a matching full name instead */ ! 377: for (p = name; *p != '\0'; p++) ! 378: { ! 379: if (*p == (SpaceSub & 0177) || *p == '_') ! 380: *p = ' '; ! 381: } ! 382: (void) setpwent(); ! 383: while ((pw = getpwent()) != NULL) ! 384: { ! 385: char buf[MAXNAME]; ! 386: ! 387: buildfname(pw->pw_gecos, pw->pw_name, buf); ! 388: if (index(buf, ' ') != NULL && !strcasecmp(buf, name)) ! 389: { ! 390: message(Arpa_Info, "sending to login name %s", pw->pw_name); ! 391: return (pw); ! 392: } ! 393: } ! 394: return (NULL); ! 395: } ! 396: /* ! 397: ** WRITABLE -- predicate returning if the file is writable. ! 398: ** ! 399: ** This routine must duplicate the algorithm in sys/fio.c. ! 400: ** Unfortunately, we cannot use the access call since we ! 401: ** won't necessarily be the real uid when we try to ! 402: ** actually open the file. ! 403: ** ! 404: ** Notice that ANY file with ANY execute bit is automatically ! 405: ** not writable. This is also enforced by mailfile. ! 406: ** ! 407: ** Parameters: ! 408: ** s -- pointer to a stat struct for the file. ! 409: ** ! 410: ** Returns: ! 411: ** TRUE -- if we will be able to write this file. ! 412: ** FALSE -- if we cannot write this file. ! 413: ** ! 414: ** Side Effects: ! 415: ** none. ! 416: */ ! 417: ! 418: bool ! 419: writable(s) ! 420: register struct stat *s; ! 421: { ! 422: int euid, egid; ! 423: int bits; ! 424: ! 425: if (bitset(0111, s->st_mode)) ! 426: return (FALSE); ! 427: euid = getruid(); ! 428: egid = getrgid(); ! 429: if (geteuid() == 0) ! 430: { ! 431: if (bitset(S_ISUID, s->st_mode)) ! 432: euid = s->st_uid; ! 433: if (bitset(S_ISGID, s->st_mode)) ! 434: egid = s->st_gid; ! 435: } ! 436: ! 437: if (euid == 0) ! 438: return (TRUE); ! 439: bits = S_IWRITE; ! 440: if (euid != s->st_uid) ! 441: { ! 442: bits >>= 3; ! 443: if (egid != s->st_gid) ! 444: bits >>= 3; ! 445: } ! 446: return ((s->st_mode & bits) != 0); ! 447: } ! 448: /* ! 449: ** INCLUDE -- handle :include: specification. ! 450: ** ! 451: ** Parameters: ! 452: ** fname -- filename to include. ! 453: ** msg -- message to print in verbose mode. ! 454: ** ctladdr -- address template to use to fill in these ! 455: ** addresses -- effective user/group id are ! 456: ** the important things. ! 457: ** sendq -- a pointer to the head of the send queue ! 458: ** to put these addresses in. ! 459: ** ! 460: ** Returns: ! 461: ** none. ! 462: ** ! 463: ** Side Effects: ! 464: ** reads the :include: file and sends to everyone ! 465: ** listed in that file. ! 466: */ ! 467: ! 468: include(fname, msg, ctladdr, sendq) ! 469: char *fname; ! 470: char *msg; ! 471: ADDRESS *ctladdr; ! 472: ADDRESS **sendq; ! 473: { ! 474: char buf[MAXLINE]; ! 475: register FILE *fp; ! 476: char *oldto = CurEnv->e_to; ! 477: char *oldfilename = FileName; ! 478: int oldlinenumber = LineNumber; ! 479: ! 480: fp = fopen(fname, "r"); ! 481: if (fp == NULL) ! 482: { ! 483: usrerr("Cannot open %s", fname); ! 484: return; ! 485: } ! 486: if (getctladdr(ctladdr) == NULL) ! 487: { ! 488: struct stat st; ! 489: ! 490: if (fstat(fileno(fp), &st) < 0) ! 491: syserr("Cannot fstat %s!", fname); ! 492: ctladdr->q_uid = st.st_uid; ! 493: ctladdr->q_gid = st.st_gid; ! 494: ctladdr->q_flags |= QGOODUID; ! 495: } ! 496: ! 497: /* read the file -- each line is a comma-separated list. */ ! 498: FileName = fname; ! 499: LineNumber = 0; ! 500: while (fgets(buf, sizeof buf, fp) != NULL) ! 501: { ! 502: register char *p = index(buf, '\n'); ! 503: ! 504: if (p != NULL) ! 505: *p = '\0'; ! 506: if (buf[0] == '\0') ! 507: continue; ! 508: CurEnv->e_to = oldto; ! 509: message(Arpa_Info, "%s to %s", msg, buf); ! 510: AliasLevel++; ! 511: sendtolist(buf, ctladdr, sendq); ! 512: AliasLevel--; ! 513: } ! 514: ! 515: (void) fclose(fp); ! 516: FileName = oldfilename; ! 517: LineNumber = oldlinenumber; ! 518: } ! 519: /* ! 520: ** SENDTOARGV -- send to an argument vector. ! 521: ** ! 522: ** Parameters: ! 523: ** argv -- argument vector to send to. ! 524: ** ! 525: ** Returns: ! 526: ** none. ! 527: ** ! 528: ** Side Effects: ! 529: ** puts all addresses on the argument vector onto the ! 530: ** send queue. ! 531: */ ! 532: ! 533: sendtoargv(argv) ! 534: register char **argv; ! 535: { ! 536: register char *p; ! 537: ! 538: while ((p = *argv++) != NULL) ! 539: { ! 540: if (argv[0] != NULL && argv[1] != NULL && !strcasecmp(argv[0], "at")) ! 541: { ! 542: char nbuf[MAXNAME]; ! 543: ! 544: if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf) ! 545: usrerr("address overflow"); ! 546: else ! 547: { ! 548: (void) strcpy(nbuf, p); ! 549: (void) strcat(nbuf, "@"); ! 550: (void) strcat(nbuf, argv[1]); ! 551: p = newstr(nbuf); ! 552: argv += 2; ! 553: } ! 554: } ! 555: sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue); ! 556: } ! 557: } ! 558: /* ! 559: ** GETCTLADDR -- get controlling address from an address header. ! 560: ** ! 561: ** If none, get one corresponding to the effective userid. ! 562: ** ! 563: ** Parameters: ! 564: ** a -- the address to find the controller of. ! 565: ** ! 566: ** Returns: ! 567: ** the controlling address. ! 568: ** ! 569: ** Side Effects: ! 570: ** none. ! 571: */ ! 572: ! 573: ADDRESS * ! 574: getctladdr(a) ! 575: register ADDRESS *a; ! 576: { ! 577: while (a != NULL && !bitset(QGOODUID, a->q_flags)) ! 578: a = a->q_alias; ! 579: return (a); ! 580: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.