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