122710Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 363589Sbostic * Copyright (c) 1988, 1993 463589Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822710Sdist 922710Sdist #ifndef lint 10*67894Seric static char sccsid[] = "@(#)recipient.c 8.52 (Berkeley) 11/08/94"; 1133731Sbostic #endif /* not lint */ 1222710Sdist 1358332Seric # include "sendmail.h" 144174Seric # include <pwd.h> 154174Seric 164174Seric /* 179622Seric ** SENDTOLIST -- Designate a send list. 184174Seric ** 194174Seric ** The parameter is a comma-separated list of people to send to. 204174Seric ** This routine arranges to send to all of them. 214174Seric ** 224174Seric ** Parameters: 234174Seric ** list -- the send list. 244399Seric ** ctladdr -- the address template for the person to 254399Seric ** send to -- effective uid/gid are important. 265006Seric ** This is typically the alias that caused this 275006Seric ** expansion. 285006Seric ** sendq -- a pointer to the head of a queue to put 295006Seric ** these people into. 3058247Seric ** e -- the envelope in which to add these recipients. 314174Seric ** 324174Seric ** Returns: 3358082Seric ** The number of addresses actually on the list. 344174Seric ** 354174Seric ** Side Effects: 364174Seric ** none. 374174Seric */ 384174Seric 3967880Seric #define MAXRCRSN 10 /* maximum levels of alias recursion */ 404174Seric 4167880Seric /* q_flags bits inherited from ctladdr */ 4267880Seric #define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASRETPARAM|QNOBODYRETURN) 4367880Seric 4455012Seric sendtolist(list, ctladdr, sendq, e) 454174Seric char *list; 464399Seric ADDRESS *ctladdr; 475198Seric ADDRESS **sendq; 4855012Seric register ENVELOPE *e; 494174Seric { 504174Seric register char *p; 518223Seric register ADDRESS *al; /* list of addresses to send to */ 524423Seric bool firstone; /* set on first address sent */ 5311446Seric char delimiter; /* the address delimiter */ 5458082Seric int naddrs; 5563847Seric char *oldto = e->e_to; 56*67894Seric static char *bufp = NULL; 57*67894Seric static int buflen; 58*67894Seric char buf[MAXNAME + 1]; 594174Seric 6064131Seric if (list == NULL) 6164131Seric { 6264131Seric syserr("sendtolist: null list"); 6364131Seric return 0; 6464131Seric } 6564131Seric 667676Seric if (tTd(25, 1)) 674444Seric { 684444Seric printf("sendto: %s\n ctladdr=", list); 694444Seric printaddr(ctladdr, FALSE); 704444Seric } 714324Seric 728223Seric /* heuristic to determine old versus new style addresses */ 738230Seric if (ctladdr == NULL && 7456795Seric (strchr(list, ',') != NULL || strchr(list, ';') != NULL || 7556795Seric strchr(list, '<') != NULL || strchr(list, '(') != NULL)) 7655012Seric e->e_flags &= ~EF_OLDSTYLE; 7711446Seric delimiter = ' '; 7855012Seric if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) 7911446Seric delimiter = ','; 808223Seric 814423Seric firstone = TRUE; 824324Seric al = NULL; 8358082Seric naddrs = 0; 848223Seric 85*67894Seric if (buf == NULL) 864174Seric { 87*67894Seric bufp = buf; 88*67894Seric buflen = sizeof buf - 1; 89*67894Seric } 90*67894Seric if (strlen(list) > buflen) 91*67894Seric { 92*67894Seric /* allocate additional space */ 93*67894Seric if (bufp != buf) 94*67894Seric free(bufp); 95*67894Seric buflen = strlen(list); 96*67894Seric bufp = malloc(buflen + 1); 97*67894Seric } 98*67894Seric strcpy(bufp, list); 99*67894Seric 100*67894Seric for (p = bufp; *p != '\0'; ) 101*67894Seric { 10258333Seric auto char *delimptr; 1038081Seric register ADDRESS *a; 1044319Seric 1058081Seric /* parse the address */ 10658050Seric while ((isascii(*p) && isspace(*p)) || *p == ',') 1074174Seric p++; 10864284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); 10958333Seric p = delimptr; 1109297Seric if (a == NULL) 1114174Seric continue; 1124324Seric a->q_next = al; 1134399Seric a->q_alias = ctladdr; 1144444Seric 1154444Seric /* see if this should be marked as a primary address */ 1164423Seric if (ctladdr == NULL || 1178081Seric (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags))) 1184423Seric a->q_flags |= QPRIMARY; 1194444Seric 1209379Seric if (ctladdr != NULL && sameaddr(ctladdr, a)) 12158061Seric ctladdr->q_flags |= QSELFREF; 12257731Seric al = a; 1234423Seric firstone = FALSE; 1244324Seric } 1254324Seric 1264324Seric /* arrange to send to everyone on the local send list */ 1274324Seric while (al != NULL) 1284324Seric { 1294324Seric register ADDRESS *a = al; 1304324Seric 1314324Seric al = a->q_next; 13255012Seric a = recipient(a, sendq, e); 1334993Seric 13467880Seric /* arrange to inherit attributes from parent */ 13567880Seric if (ctladdr != NULL) 13667880Seric { 13767880Seric /* full name */ 13867880Seric if (a->q_fullname == NULL) 13967880Seric a->q_fullname = ctladdr->q_fullname; 14067880Seric 14167880Seric /* various flag bits */ 14267880Seric a->q_flags &= ~QINHERITEDBITS; 14367880Seric a->q_flags |= ctladdr->q_flags & QINHERITEDBITS; 14467880Seric } 14558082Seric naddrs++; 1464174Seric } 1474324Seric 14863847Seric e->e_to = oldto; 14958082Seric return (naddrs); 1504174Seric } 1514174Seric /* 1524174Seric ** RECIPIENT -- Designate a message recipient 1534174Seric ** 1544174Seric ** Saves the named person for future mailing. 1554174Seric ** 1564174Seric ** Parameters: 1574174Seric ** a -- the (preparsed) address header for the recipient. 1585006Seric ** sendq -- a pointer to the head of a queue to put the 1595006Seric ** recipient in. Duplicate supression is done 1605006Seric ** in this queue. 16157731Seric ** e -- the current envelope. 1624174Seric ** 1634174Seric ** Returns: 16412613Seric ** The actual address in the queue. This will be "a" if 16512613Seric ** the address is not a duplicate, else the original address. 1664174Seric ** 1674174Seric ** Side Effects: 1684174Seric ** none. 1694174Seric */ 1704174Seric 17112613Seric ADDRESS * 17255012Seric recipient(a, sendq, e) 1734174Seric register ADDRESS *a; 1745006Seric register ADDRESS **sendq; 17555012Seric register ENVELOPE *e; 1764174Seric { 1774174Seric register ADDRESS *q; 1784319Seric ADDRESS **pq; 1794174Seric register struct mailer *m; 1809210Seric register char *p; 1819210Seric bool quoted = FALSE; /* set if the addr has a quote bit */ 18253735Seric int findusercount = 0; 18367264Seric int i; 18467264Seric char *buf; 18567264Seric char buf0[MAXNAME]; /* unquoted image of the user name */ 18658247Seric extern int safefile(); 1874174Seric 18855012Seric e->e_to = a->q_paddr; 1894600Seric m = a->q_mailer; 1904174Seric errno = 0; 1917676Seric if (tTd(26, 1)) 1924444Seric { 1934444Seric printf("\nrecipient: "); 1944444Seric printaddr(a, FALSE); 1954444Seric } 1964174Seric 19764146Seric /* if this is primary, add it to the original recipient list */ 19864146Seric if (a->q_alias == NULL) 19964146Seric { 20064146Seric if (e->e_origrcpt == NULL) 20164146Seric e->e_origrcpt = a->q_paddr; 20264146Seric else if (e->e_origrcpt != a->q_paddr) 20364146Seric e->e_origrcpt = ""; 20464146Seric } 20564146Seric 2064174Seric /* break aliasing loops */ 2074174Seric if (AliasLevel > MAXRCRSN) 2084174Seric { 20958151Seric usrerr("554 aliasing/forwarding loop broken"); 21012613Seric return (a); 2114174Seric } 2124174Seric 2134174Seric /* 2144627Seric ** Finish setting up address structure. 2154174Seric */ 2164174Seric 21716160Seric /* get unquoted user for file, program or user.name check */ 21867264Seric i = strlen(a->q_user); 21967264Seric if (i >= sizeof buf) 22067264Seric buf = xalloc(i + 1); 22167264Seric else 22267264Seric buf = buf0; 2239210Seric (void) strcpy(buf, a->q_user); 2249210Seric for (p = buf; *p != '\0' && !quoted; p++) 2259210Seric { 22654993Seric if (*p == '\\') 2279210Seric quoted = TRUE; 2289210Seric } 22954983Seric stripquotes(buf); 2309210Seric 23157402Seric /* check for direct mailing to restricted mailers */ 23265496Seric if (m == ProgMailer) 2334174Seric { 23465496Seric if (a->q_alias == NULL) 23565496Seric { 23665496Seric a->q_flags |= QBADADDR; 23765496Seric usrerr("550 Cannot mail directly to programs"); 23865496Seric } 23965496Seric else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) 24065496Seric { 24165496Seric a->q_flags |= QBADADDR; 24265496Seric usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", 24365496Seric a->q_alias->q_ruser, MyHostName); 24465496Seric } 24565496Seric else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) 24665496Seric { 24765496Seric a->q_flags |= QBADADDR; 24865496Seric usrerr("550 Address %s is unsafe for mailing to programs", 24965496Seric a->q_alias->q_paddr); 25065496Seric } 2514174Seric } 2524174Seric 2534174Seric /* 2544419Seric ** Look up this person in the recipient list. 2554419Seric ** If they are there already, return, otherwise continue. 2564419Seric ** If the list is empty, just add it. Notice the cute 2574419Seric ** hack to make from addresses suppress things correctly: 2584419Seric ** the QDONTSEND bit will be set in the send list. 2594419Seric ** [Please note: the emphasis is on "hack."] 2604174Seric */ 2614174Seric 2625006Seric for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) 2634174Seric { 26458294Seric if (sameaddr(q, a)) 2654174Seric { 2667676Seric if (tTd(26, 1)) 2674444Seric { 2684444Seric printf("%s in sendq: ", a->q_paddr); 2694444Seric printaddr(q, FALSE); 2704444Seric } 27165593Seric if (!bitset(QPRIMARY, q->q_flags)) 27258065Seric { 27365593Seric if (!bitset(QDONTSEND, a->q_flags)) 27458151Seric message("duplicate suppressed"); 27565593Seric q->q_flags |= a->q_flags; 27665593Seric } 27765593Seric else if (bitset(QSELFREF, q->q_flags)) 27865579Seric q->q_flags |= a->q_flags & ~QDONTSEND; 27963847Seric a = q; 28063847Seric goto testselfdestruct; 2814174Seric } 2824319Seric } 2834174Seric 2844319Seric /* add address on list */ 28558884Seric *pq = a; 28658884Seric a->q_next = NULL; 2874174Seric 2884174Seric /* 28957402Seric ** Alias the name and handle special mailer types. 2904174Seric */ 2914174Seric 29253735Seric trylocaluser: 29355354Seric if (tTd(29, 7)) 29455354Seric printf("at trylocaluser %s\n", a->q_user); 29555354Seric 29658680Seric if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) 29763847Seric goto testselfdestruct; 29857402Seric 29957402Seric if (m == InclMailer) 3004174Seric { 30157402Seric a->q_flags |= QDONTSEND; 30264761Seric if (a->q_alias == NULL) 3034174Seric { 30458680Seric a->q_flags |= QBADADDR; 30558151Seric usrerr("550 Cannot mail directly to :include:s"); 3064174Seric } 3074174Seric else 30850556Seric { 30959563Seric int ret; 31058247Seric 31158151Seric message("including file %s", a->q_user); 31259563Seric ret = include(a->q_user, FALSE, a, sendq, e); 31359563Seric if (transienterror(ret)) 31459563Seric { 31559563Seric #ifdef LOG 31659563Seric if (LogLevel > 2) 31766239Seric syslog(LOG_ERR, "%s: include %s: transient error: %s", 31866284Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 31966284Seric a->q_user, errstring(ret)); 32059563Seric #endif 32163853Seric a->q_flags |= QQUEUEUP; 32265215Seric a->q_flags &= ~QDONTSEND; 32359563Seric usrerr("451 Cannot open %s: %s", 32459563Seric a->q_user, errstring(ret)); 32559563Seric } 32659563Seric else if (ret != 0) 32759563Seric { 32863938Seric a->q_flags |= QBADADDR; 32959563Seric usrerr("550 Cannot open %s: %s", 33059563Seric a->q_user, errstring(ret)); 33159563Seric } 33250556Seric } 3334174Seric } 33457642Seric else if (m == FileMailer) 3354174Seric { 3364329Seric extern bool writable(); 3374174Seric 33851317Seric /* check if writable or creatable */ 33964761Seric if (a->q_alias == NULL) 3404174Seric { 34158680Seric a->q_flags |= QBADADDR; 34258151Seric usrerr("550 Cannot mail directly to files"); 3434174Seric } 34465496Seric else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) 34565496Seric { 34665496Seric a->q_flags |= QBADADDR; 34765496Seric usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", 34865496Seric a->q_alias->q_ruser, MyHostName); 34965496Seric } 35065496Seric else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) 35165496Seric { 35265496Seric a->q_flags |= QBADADDR; 35365496Seric usrerr("550 Address %s is unsafe for mailing to files", 35465496Seric a->q_alias->q_paddr); 35565496Seric } 35665112Seric else if (!writable(buf, getctladdr(a), SFF_ANYFILE)) 35751317Seric { 35858680Seric a->q_flags |= QBADADDR; 35964771Seric giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e); 36051317Seric } 36151317Seric } 36251317Seric 36357402Seric /* try aliasing */ 36467472Seric if (!bitset(QDONTSEND, a->q_flags) && bitnset(M_ALIASABLE, m->m_flags)) 36567472Seric alias(a, sendq, e); 36657402Seric 36757402Seric # ifdef USERDB 36857402Seric /* if not aliased, look it up in the user database */ 36967472Seric if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) && 37067472Seric bitnset(M_CHECKUDB, m->m_flags)) 37157402Seric { 37257402Seric extern int udbexpand(); 37357402Seric 37457402Seric if (udbexpand(a, sendq, e) == EX_TEMPFAIL) 37557402Seric { 37663853Seric a->q_flags |= QQUEUEUP; 37757402Seric if (e->e_message == NULL) 37857402Seric e->e_message = newstr("Deferred: user database error"); 37957402Seric # ifdef LOG 38058020Seric if (LogLevel > 8) 38159623Seric syslog(LOG_INFO, "%s: deferred: udbexpand: %s", 38266284Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 38366284Seric errstring(errno)); 38457402Seric # endif 38559615Seric message("queued (user database error): %s", 38659615Seric errstring(errno)); 38757642Seric e->e_nrcpts++; 38863847Seric goto testselfdestruct; 38957402Seric } 39057402Seric } 39157402Seric # endif 39257402Seric 39351317Seric /* 39451317Seric ** If we have a level two config file, then pass the name through 39551317Seric ** Ruleset 5 before sending it off. Ruleset 5 has the right 39651317Seric ** to send rewrite it to another mailer. This gives us a hook 39751317Seric ** after local aliasing has been done. 39851317Seric */ 39951317Seric 40051317Seric if (tTd(29, 5)) 40151317Seric { 40251317Seric printf("recipient: testing local? cl=%d, rr5=%x\n\t", 40351317Seric ConfigLevel, RewriteRules[5]); 40451317Seric printaddr(a, FALSE); 40551317Seric } 40667472Seric if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && 40767472Seric ConfigLevel >= 2 && RewriteRules[5] != NULL && 40867472Seric bitnset(M_TRYRULESET5, m->m_flags)) 40951317Seric { 41055012Seric maplocaluser(a, sendq, e); 41151317Seric } 41251317Seric 41351317Seric /* 41451317Seric ** If it didn't get rewritten to another mailer, go ahead 41551317Seric ** and deliver it. 41651317Seric */ 41751317Seric 41867472Seric if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && 41967472Seric bitnset(M_HASPWENT, m->m_flags)) 42051317Seric { 42155354Seric auto bool fuzzy; 42251317Seric register struct passwd *pw; 42351317Seric extern struct passwd *finduser(); 42451317Seric 42551317Seric /* warning -- finduser may trash buf */ 42655354Seric pw = finduser(buf, &fuzzy); 42751317Seric if (pw == NULL) 42851317Seric { 42958680Seric a->q_flags |= QBADADDR; 43064771Seric giveresponse(EX_NOUSER, m, NULL, a->q_alias, e); 43151317Seric } 4324174Seric else 4334174Seric { 43451317Seric char nbuf[MAXNAME]; 4354373Seric 43655354Seric if (fuzzy) 4374174Seric { 43853735Seric /* name was a fuzzy match */ 43951317Seric a->q_user = newstr(pw->pw_name); 44053735Seric if (findusercount++ > 3) 44153735Seric { 44258680Seric a->q_flags |= QBADADDR; 44358151Seric usrerr("554 aliasing/forwarding loop for %s broken", 44453735Seric pw->pw_name); 44567264Seric goto done; 44653735Seric } 44753735Seric 44853735Seric /* see if it aliases */ 44951317Seric (void) strcpy(buf, pw->pw_name); 45053735Seric goto trylocaluser; 4514174Seric } 45265822Seric if (strcmp(pw->pw_dir, "/") == 0) 45365822Seric a->q_home = ""; 45465822Seric else 45565822Seric a->q_home = newstr(pw->pw_dir); 45651317Seric a->q_uid = pw->pw_uid; 45751317Seric a->q_gid = pw->pw_gid; 45859083Seric a->q_ruser = newstr(pw->pw_name); 45951317Seric a->q_flags |= QGOODUID; 46051317Seric buildfname(pw->pw_gecos, pw->pw_name, nbuf); 46151317Seric if (nbuf[0] != '\0') 46251317Seric a->q_fullname = newstr(nbuf); 46365211Seric if (pw->pw_shell != NULL && pw->pw_shell[0] != '\0' && 46465211Seric !usershellok(pw->pw_shell)) 46565206Seric { 46665211Seric a->q_flags |= QBOGUSSHELL; 46765206Seric } 46851317Seric if (!quoted) 46955012Seric forward(a, sendq, e); 4704174Seric } 4714174Seric } 47257642Seric if (!bitset(QDONTSEND, a->q_flags)) 47357642Seric e->e_nrcpts++; 47463847Seric 47563847Seric testselfdestruct: 47663978Seric if (tTd(26, 8)) 47763847Seric { 47863978Seric printf("testselfdestruct: "); 47963978Seric printaddr(a, TRUE); 48063978Seric } 48163978Seric if (a->q_alias == NULL && a != &e->e_from && 48263978Seric bitset(QDONTSEND, a->q_flags)) 48363978Seric { 48463978Seric q = *sendq; 48563965Seric while (q != NULL && bitset(QDONTSEND, q->q_flags)) 48663847Seric q = q->q_next; 48763978Seric if (q == NULL) 48863847Seric { 48963847Seric a->q_flags |= QBADADDR; 49063847Seric usrerr("554 aliasing/forwarding loop broken"); 49163847Seric } 49263847Seric } 49367264Seric 49467264Seric done: 49567264Seric if (buf != buf0) 49667264Seric free(buf); 49712613Seric return (a); 4984174Seric } 4994174Seric /* 5004373Seric ** FINDUSER -- find the password entry for a user. 5014373Seric ** 5024373Seric ** This looks a lot like getpwnam, except that it may want to 5034373Seric ** do some fancier pattern matching in /etc/passwd. 5044373Seric ** 5059379Seric ** This routine contains most of the time of many sendmail runs. 5069379Seric ** It deserves to be optimized. 5079379Seric ** 5084373Seric ** Parameters: 5094373Seric ** name -- the name to match against. 51055354Seric ** fuzzyp -- an outarg that is set to TRUE if this entry 51155354Seric ** was found using the fuzzy matching algorithm; 51255354Seric ** set to FALSE otherwise. 5134373Seric ** 5144373Seric ** Returns: 5154373Seric ** A pointer to a pw struct. 5164373Seric ** NULL if name is unknown or ambiguous. 5174373Seric ** 5184373Seric ** Side Effects: 5194407Seric ** may modify name. 5204373Seric */ 5214373Seric 5224373Seric struct passwd * 52355354Seric finduser(name, fuzzyp) 5244373Seric char *name; 52555354Seric bool *fuzzyp; 5264373Seric { 5274376Seric register struct passwd *pw; 5284407Seric register char *p; 52915325Seric extern struct passwd *getpwent(); 53015325Seric extern struct passwd *getpwnam(); 5314373Seric 53255354Seric if (tTd(29, 4)) 53355354Seric printf("finduser(%s): ", name); 53455354Seric 53555354Seric *fuzzyp = FALSE; 5364407Seric 53767839Seric #ifdef HESIOD 53864673Seric /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ 53964673Seric for (p = name; *p != '\0'; p++) 54064673Seric if (!isascii(*p) || !isdigit(*p)) 54164673Seric break; 54264673Seric if (*p == '\0') 54364673Seric { 54464673Seric if (tTd(29, 4)) 54564673Seric printf("failed (numeric input)\n"); 54664673Seric return NULL; 54764673Seric } 54867839Seric #endif 54964673Seric 55025777Seric /* look up this login name using fast path */ 55112634Seric if ((pw = getpwnam(name)) != NULL) 55255354Seric { 55355354Seric if (tTd(29, 4)) 55455354Seric printf("found (non-fuzzy)\n"); 55512634Seric return (pw); 55655354Seric } 55712634Seric 55853735Seric #ifdef MATCHGECOS 55953735Seric /* see if fuzzy matching allowed */ 56053735Seric if (!MatchGecos) 56155354Seric { 56255354Seric if (tTd(29, 4)) 56355354Seric printf("not found (fuzzy disabled)\n"); 56453735Seric return NULL; 56555354Seric } 56653735Seric 56712634Seric /* search for a matching full name instead */ 56825777Seric for (p = name; *p != '\0'; p++) 56925777Seric { 57025777Seric if (*p == (SpaceSub & 0177) || *p == '_') 57125777Seric *p = ' '; 57225777Seric } 57323107Seric (void) setpwent(); 5744376Seric while ((pw = getpwent()) != NULL) 5754376Seric { 5764998Seric char buf[MAXNAME]; 5774376Seric 5784998Seric buildfname(pw->pw_gecos, pw->pw_name, buf); 57956795Seric if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) 5804381Seric { 58155354Seric if (tTd(29, 4)) 58255354Seric printf("fuzzy matches %s\n", pw->pw_name); 58358151Seric message("sending to login name %s", pw->pw_name); 58455354Seric *fuzzyp = TRUE; 5854376Seric return (pw); 5864377Seric } 5874376Seric } 58855354Seric if (tTd(29, 4)) 58955354Seric printf("no fuzzy match found\n"); 59059015Seric #else 59159015Seric if (tTd(29, 4)) 59259015Seric printf("not found (fuzzy disabled)\n"); 59359015Seric #endif 5944376Seric return (NULL); 5954373Seric } 5964373Seric /* 5974329Seric ** WRITABLE -- predicate returning if the file is writable. 5984329Seric ** 5994329Seric ** This routine must duplicate the algorithm in sys/fio.c. 6004329Seric ** Unfortunately, we cannot use the access call since we 6014329Seric ** won't necessarily be the real uid when we try to 6024329Seric ** actually open the file. 6034329Seric ** 6044329Seric ** Notice that ANY file with ANY execute bit is automatically 6054329Seric ** not writable. This is also enforced by mailfile. 6064329Seric ** 6074329Seric ** Parameters: 60865064Seric ** filename -- the file name to check. 60965112Seric ** ctladdr -- the controlling address for this file. 61065064Seric ** flags -- SFF_* flags to control the function. 6114329Seric ** 6124329Seric ** Returns: 6134329Seric ** TRUE -- if we will be able to write this file. 6144329Seric ** FALSE -- if we cannot write this file. 6154329Seric ** 6164329Seric ** Side Effects: 6174329Seric ** none. 6184329Seric */ 6194329Seric 6204329Seric bool 62165112Seric writable(filename, ctladdr, flags) 62264819Seric char *filename; 62365112Seric ADDRESS *ctladdr; 62465064Seric int flags; 6254329Seric { 62655372Seric uid_t euid; 62755372Seric gid_t egid; 6284329Seric int bits; 62964944Seric register char *p; 63064944Seric char *uname; 63164944Seric struct stat stb; 63264944Seric extern char RealUserName[]; 6334329Seric 63464819Seric if (tTd(29, 5)) 63565064Seric printf("writable(%s, %x)\n", filename, flags); 63664944Seric 63764944Seric #ifdef HASLSTAT 63865064Seric if ((bitset(SFF_NOSLINK, flags) ? lstat(filename, &stb) 63965064Seric : stat(filename, &stb)) < 0) 64064944Seric #else 64164944Seric if (stat(filename, &stb) < 0) 64264944Seric #endif 64364944Seric { 64464944Seric /* file does not exist -- see if directory is safe */ 64564944Seric p = strrchr(filename, '/'); 64664944Seric if (p == NULL) 64764944Seric { 64865067Seric errno = ENOTDIR; 64964944Seric return FALSE; 65064944Seric } 65165067Seric *p = '\0'; 65265067Seric errno = safefile(filename, RealUid, RealGid, RealUserName, 65365067Seric SFF_MUSTOWN, S_IWRITE|S_IEXEC); 65464944Seric *p = '/'; 65565067Seric return errno == 0; 65664944Seric } 65764944Seric 65865225Seric #ifdef SUID_ROOT_FILES_OK 65965225Seric /* really ought to be passed down -- and not a good idea */ 66065225Seric flags |= SFF_ROOTOK; 66165225Seric #endif 66265225Seric 66364944Seric /* 66464944Seric ** File does exist -- check that it is writable. 66564944Seric */ 66664944Seric 66764944Seric if (bitset(0111, stb.st_mode)) 66865022Seric { 66965022Seric if (tTd(29, 5)) 67065022Seric printf("failed (mode %o: x bits)\n", stb.st_mode); 67165067Seric errno = EPERM; 6724329Seric return (FALSE); 67365022Seric } 67464944Seric 67565112Seric if (ctladdr != NULL && geteuid() == 0) 67664944Seric { 67765112Seric euid = ctladdr->q_uid; 67865112Seric egid = ctladdr->q_gid; 67965112Seric uname = ctladdr->q_user; 68064944Seric } 68165112Seric else 68265112Seric { 68365112Seric euid = RealUid; 68465112Seric egid = RealGid; 68565112Seric uname = RealUserName; 68665112Seric } 68765138Seric if (euid == 0) 68865138Seric { 68965138Seric euid = DefUid; 69065138Seric uname = DefUser; 69165138Seric } 69265138Seric if (egid == 0) 69365138Seric egid = DefGid; 6944329Seric if (geteuid() == 0) 6954329Seric { 69665225Seric if (bitset(S_ISUID, stb.st_mode) && 69765225Seric (stb.st_uid != 0 || bitset(SFF_ROOTOK, flags))) 69864944Seric { 69964944Seric euid = stb.st_uid; 70064944Seric uname = NULL; 70164944Seric } 70265225Seric if (bitset(S_ISGID, stb.st_mode) && 70365225Seric (stb.st_gid != 0 || bitset(SFF_ROOTOK, flags))) 70464944Seric egid = stb.st_gid; 7054329Seric } 7064329Seric 70764819Seric if (tTd(29, 5)) 70864819Seric printf("\teu/gid=%d/%d, st_u/gid=%d/%d\n", 70964944Seric euid, egid, stb.st_uid, stb.st_gid); 71064819Seric 71165067Seric errno = safefile(filename, euid, egid, uname, flags, S_IWRITE); 71265067Seric return errno == 0; 7134329Seric } 7144329Seric /* 7154174Seric ** INCLUDE -- handle :include: specification. 7164174Seric ** 7174174Seric ** Parameters: 7184174Seric ** fname -- filename to include. 71953037Seric ** forwarding -- if TRUE, we are reading a .forward file. 72053037Seric ** if FALSE, it's a :include: file. 7214399Seric ** ctladdr -- address template to use to fill in these 7224399Seric ** addresses -- effective user/group id are 7234399Seric ** the important things. 7245006Seric ** sendq -- a pointer to the head of the send queue 7255006Seric ** to put these addresses in. 7264174Seric ** 7274174Seric ** Returns: 72857136Seric ** open error status 7294174Seric ** 7304174Seric ** Side Effects: 7314174Seric ** reads the :include: file and sends to everyone 7324174Seric ** listed in that file. 73365909Seric ** 73465909Seric ** Security Note: 73565909Seric ** If you have restricted chown (that is, you can't 73665909Seric ** give a file away), it is reasonable to allow programs 73765909Seric ** and files called from this :include: file to be to be 73865909Seric ** run as the owner of the :include: file. This is bogus 73965909Seric ** if there is any chance of someone giving away a file. 74065909Seric ** We assume that pre-POSIX systems can give away files. 74165909Seric ** 74265909Seric ** There is an additional restriction that if you 74365909Seric ** forward to a :include: file, it will not take on 74465909Seric ** the ownership of the :include: file. This may not 74565909Seric ** be necessary, but shouldn't hurt. 7464174Seric */ 7474174Seric 74853037Seric static jmp_buf CtxIncludeTimeout; 74963937Seric static int includetimeout(); 75053037Seric 75165496Seric #ifndef S_IWOTH 75265496Seric # define S_IWOTH (S_IWRITE >> 6) 75365496Seric #endif 75465496Seric 75557136Seric int 75655012Seric include(fname, forwarding, ctladdr, sendq, e) 7574174Seric char *fname; 75853037Seric bool forwarding; 7594399Seric ADDRESS *ctladdr; 7605006Seric ADDRESS **sendq; 76155012Seric ENVELOPE *e; 7624174Seric { 76364570Seric register FILE *fp = NULL; 76455012Seric char *oldto = e->e_to; 7659379Seric char *oldfilename = FileName; 7669379Seric int oldlinenumber = LineNumber; 76753037Seric register EVENT *ev = NULL; 76858082Seric int nincludes; 76964325Seric register ADDRESS *ca; 77064325Seric uid_t saveduid, uid; 77164325Seric gid_t savedgid, gid; 77264083Seric char *uname; 77364325Seric int rval = 0; 77465064Seric int sfflags = forwarding ? SFF_MUSTOWN : SFF_ANYFILE; 77565496Seric struct stat st; 77665948Seric char buf[MAXLINE]; 77765909Seric #ifdef _POSIX_CHOWN_RESTRICTED 77865948Seric # if _POSIX_CHOWN_RESTRICTED == -1 77965948Seric # define safechown FALSE 78065948Seric # else 78165948Seric # define safechown TRUE 78265948Seric # endif 78365948Seric #else 78465948Seric # ifdef _PC_CHOWN_RESTRICTED 78565909Seric bool safechown; 78665948Seric # else 78765948Seric # ifdef BSD 78865948Seric # define safechown TRUE 78965948Seric # else 79065948Seric # define safechown FALSE 79165948Seric # endif 79265948Seric # endif 79365909Seric #endif 79465948Seric extern bool chownsafe(); 7954174Seric 79657186Seric if (tTd(27, 2)) 79757186Seric printf("include(%s)\n", fname); 79863902Seric if (tTd(27, 4)) 79963902Seric printf(" ruid=%d euid=%d\n", getuid(), geteuid()); 80063581Seric if (tTd(27, 14)) 80163581Seric { 80263581Seric printf("ctladdr "); 80363581Seric printaddr(ctladdr, FALSE); 80463581Seric } 80557186Seric 80664325Seric if (tTd(27, 9)) 80764325Seric printf("include: old uid = %d/%d\n", getuid(), geteuid()); 80853037Seric 80963581Seric ca = getctladdr(ctladdr); 81063581Seric if (ca == NULL) 81164083Seric { 81264846Seric uid = DefUid; 81364846Seric gid = DefGid; 81464846Seric uname = DefUser; 81564325Seric saveduid = -1; 81664083Seric } 81763581Seric else 81864083Seric { 81963581Seric uid = ca->q_uid; 82064083Seric gid = ca->q_gid; 82164083Seric uname = ca->q_user; 82264325Seric #ifdef HASSETREUID 82364325Seric saveduid = geteuid(); 82464325Seric savedgid = getegid(); 82564325Seric if (saveduid == 0) 82664325Seric { 82764325Seric initgroups(uname, gid); 82864325Seric if (uid != 0) 82967827Seric { 83067827Seric if (setreuid(0, uid) < 0) 83167827Seric syserr("setreuid(0, %d) failure (real=%d, eff=%d)", 83267827Seric uid, getuid(), geteuid()); 83367827Seric } 83464325Seric } 83564325Seric #endif 83664083Seric } 83763581Seric 83864325Seric if (tTd(27, 9)) 83964325Seric printf("include: new uid = %d/%d\n", getuid(), geteuid()); 84064325Seric 84164325Seric /* 84264325Seric ** If home directory is remote mounted but server is down, 84364325Seric ** this can hang or give errors; use a timeout to avoid this 84464325Seric */ 84564325Seric 84653037Seric if (setjmp(CtxIncludeTimeout) != 0) 84753037Seric { 84863853Seric ctladdr->q_flags |= QQUEUEUP; 84953037Seric errno = 0; 85063993Seric 85163993Seric /* return pseudo-error code */ 85264325Seric rval = EOPENTIMEOUT; 85364325Seric goto resetuid; 85453037Seric } 85567711Seric if (TimeOuts.to_fileopen > 0) 85667711Seric ev = setevent(TimeOuts.to_fileopen, includetimeout, 0); 85767711Seric else 85867711Seric ev = NULL; 85953037Seric 86063581Seric /* the input file must be marked safe */ 86164944Seric rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD); 86264329Seric if (rval != 0) 86353037Seric { 86464325Seric /* don't use this :include: file */ 86557186Seric if (tTd(27, 4)) 86658247Seric printf("include: not safe (uid=%d): %s\n", 86764329Seric uid, errstring(rval)); 86853037Seric } 86965496Seric else 8704174Seric { 87165496Seric fp = fopen(fname, "r"); 87265496Seric if (fp == NULL) 87358061Seric { 87464329Seric rval = errno; 87565496Seric if (tTd(27, 4)) 87665496Seric printf("include: open: %s\n", errstring(rval)); 87758061Seric } 8784406Seric } 87967711Seric if (ev != NULL) 88067711Seric clrevent(ev); 88153037Seric 88264570Seric resetuid: 88364570Seric 88464570Seric #ifdef HASSETREUID 88564570Seric if (saveduid == 0) 88664570Seric { 88764570Seric if (uid != 0) 88867827Seric { 88967827Seric if (setreuid(-1, 0) < 0) 89067827Seric syserr("setreuid(-1, 0) failure (real=%d, eff=%d)", 89167827Seric getuid(), geteuid()); 89267827Seric if (setreuid(RealUid, 0) < 0) 89364570Seric syserr("setreuid(%d, 0) failure (real=%d, eff=%d)", 89464570Seric RealUid, getuid(), geteuid()); 89567827Seric } 89664570Seric setgid(savedgid); 89764570Seric } 89864570Seric #endif 89964570Seric 90064570Seric if (tTd(27, 9)) 90164570Seric printf("include: reset uid = %d/%d\n", getuid(), geteuid()); 90264570Seric 90365593Seric if (rval == EOPENTIMEOUT) 90465593Seric usrerr("451 open timeout on %s", fname); 90565593Seric 90664570Seric if (fp == NULL) 90764570Seric return rval; 90864570Seric 90965496Seric if (fstat(fileno(fp), &st) < 0) 91065496Seric { 91165496Seric rval = errno; 91265496Seric syserr("Cannot fstat %s!", fname); 91365496Seric return rval; 91465496Seric } 91565496Seric 91665948Seric #ifndef safechown 91765948Seric safechown = chownsafe(fileno(fp)); 91865948Seric #endif 91965909Seric if (ca == NULL && safechown) 92065496Seric { 92165496Seric ctladdr->q_uid = st.st_uid; 92265496Seric ctladdr->q_gid = st.st_gid; 92365496Seric ctladdr->q_flags |= QGOODUID; 92465496Seric } 92565496Seric if (ca != NULL && ca->q_uid == st.st_uid) 92665496Seric { 92765496Seric /* optimization -- avoid getpwuid if we already have info */ 92865496Seric ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL; 92965496Seric ctladdr->q_ruser = ca->q_ruser; 93065496Seric } 93165496Seric else 93265496Seric { 93365909Seric char *sh; 93465496Seric register struct passwd *pw; 93565496Seric 93665909Seric sh = "/SENDMAIL/ANY/SHELL/"; 93765496Seric pw = getpwuid(st.st_uid); 93865909Seric if (pw != NULL) 93965496Seric { 94065496Seric ctladdr->q_ruser = newstr(pw->pw_name); 94165909Seric if (safechown) 94265909Seric sh = pw->pw_shell; 94365909Seric } 94465909Seric if (pw == NULL) 94565496Seric ctladdr->q_flags |= QBOGUSSHELL; 94665909Seric else if(!usershellok(sh)) 94765909Seric { 94865909Seric if (safechown) 94965909Seric ctladdr->q_flags |= QBOGUSSHELL; 95065909Seric else 95165909Seric ctladdr->q_flags |= QUNSAFEADDR; 95265496Seric } 95365496Seric } 95465496Seric 95558092Seric if (bitset(EF_VRFYONLY, e->e_flags)) 95658092Seric { 95758092Seric /* don't do any more now */ 95858868Seric ctladdr->q_flags |= QVERIFIED; 95958884Seric e->e_nrcpts++; 96058680Seric xfclose(fp, "include", fname); 96164570Seric return rval; 96258092Seric } 96358092Seric 96465496Seric /* 96565496Seric ** Check to see if some bad guy can write this file 96665496Seric ** 96765496Seric ** This should really do something clever with group 96865496Seric ** permissions; currently we just view world writable 96965496Seric ** as unsafe. Also, we don't check for writable 97065496Seric ** directories in the path. We've got to leave 97165496Seric ** something for the local sysad to do. 97265496Seric */ 97365496Seric 97465496Seric if (bitset(S_IWOTH, st.st_mode)) 97565496Seric ctladdr->q_flags |= QUNSAFEADDR; 97665496Seric 9774174Seric /* read the file -- each line is a comma-separated list. */ 9789379Seric FileName = fname; 9799379Seric LineNumber = 0; 98058082Seric ctladdr->q_flags &= ~QSELFREF; 98158082Seric nincludes = 0; 9824174Seric while (fgets(buf, sizeof buf, fp) != NULL) 9834174Seric { 98456795Seric register char *p = strchr(buf, '\n'); 9854174Seric 98640963Sbostic LineNumber++; 9874174Seric if (p != NULL) 9884174Seric *p = '\0'; 98957186Seric if (buf[0] == '#' || buf[0] == '\0') 99057139Seric continue; 99158008Seric e->e_to = NULL; 99258151Seric message("%s to %s", 99353037Seric forwarding ? "forwarding" : "sending", buf); 99457977Seric #ifdef LOG 99558020Seric if (forwarding && LogLevel > 9) 99657977Seric syslog(LOG_INFO, "%s: forward %s => %s", 99766284Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 99866284Seric oldto, buf); 99957977Seric #endif 100057977Seric 10014176Seric AliasLevel++; 100258082Seric nincludes += sendtolist(buf, ctladdr, sendq, e); 10034176Seric AliasLevel--; 10044174Seric } 100563902Seric 100663902Seric if (ferror(fp) && tTd(27, 3)) 100763902Seric printf("include: read error: %s\n", errstring(errno)); 100858082Seric if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) 100958065Seric { 101058065Seric if (tTd(27, 5)) 101158065Seric { 101258065Seric printf("include: QDONTSEND "); 101358065Seric printaddr(ctladdr, FALSE); 101458065Seric } 101558065Seric ctladdr->q_flags |= QDONTSEND; 101658065Seric } 10174174Seric 101858680Seric (void) xfclose(fp, "include", fname); 10199379Seric FileName = oldfilename; 10209379Seric LineNumber = oldlinenumber; 102163847Seric e->e_to = oldto; 102264325Seric return rval; 10234174Seric } 102453037Seric 102553037Seric static 102653037Seric includetimeout() 102753037Seric { 102853037Seric longjmp(CtxIncludeTimeout, 1); 102953037Seric } 10304324Seric /* 10314324Seric ** SENDTOARGV -- send to an argument vector. 10324324Seric ** 10334324Seric ** Parameters: 10344324Seric ** argv -- argument vector to send to. 103558247Seric ** e -- the current envelope. 10364324Seric ** 10374324Seric ** Returns: 10384324Seric ** none. 10394324Seric ** 10404324Seric ** Side Effects: 10414324Seric ** puts all addresses on the argument vector onto the 10424324Seric ** send queue. 10434324Seric */ 10444324Seric 104555012Seric sendtoargv(argv, e) 10464324Seric register char **argv; 104755012Seric register ENVELOPE *e; 10484324Seric { 10494324Seric register char *p; 10504324Seric 10514324Seric while ((p = *argv++) != NULL) 10524324Seric { 105364284Seric (void) sendtolist(p, NULLADDR, &e->e_sendqueue, e); 10544324Seric } 10554324Seric } 10564399Seric /* 10574399Seric ** GETCTLADDR -- get controlling address from an address header. 10584399Seric ** 10594399Seric ** If none, get one corresponding to the effective userid. 10604399Seric ** 10614399Seric ** Parameters: 10624399Seric ** a -- the address to find the controller of. 10634399Seric ** 10644399Seric ** Returns: 10654399Seric ** the controlling address. 10664399Seric ** 10674399Seric ** Side Effects: 10684399Seric ** none. 10694399Seric */ 10704399Seric 10714399Seric ADDRESS * 10724399Seric getctladdr(a) 10734399Seric register ADDRESS *a; 10744399Seric { 10754404Seric while (a != NULL && !bitset(QGOODUID, a->q_flags)) 10764399Seric a = a->q_alias; 10774399Seric return (a); 10784399Seric } 1079