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