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*67880Seric static char sccsid[] = "@(#)recipient.c 8.51 (Berkeley) 11/04/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 39*67880Seric #define MAXRCRSN 10 /* maximum levels of alias recursion */ 404174Seric 41*67880Seric /* q_flags bits inherited from ctladdr */ 42*67880Seric #define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASRETPARAM|QNOBODYRETURN) 43*67880Seric 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; 564174Seric 5764131Seric if (list == NULL) 5864131Seric { 5964131Seric syserr("sendtolist: null list"); 6064131Seric return 0; 6164131Seric } 6264131Seric 637676Seric if (tTd(25, 1)) 644444Seric { 654444Seric printf("sendto: %s\n ctladdr=", list); 664444Seric printaddr(ctladdr, FALSE); 674444Seric } 684324Seric 698223Seric /* heuristic to determine old versus new style addresses */ 708230Seric if (ctladdr == NULL && 7156795Seric (strchr(list, ',') != NULL || strchr(list, ';') != NULL || 7256795Seric strchr(list, '<') != NULL || strchr(list, '(') != NULL)) 7355012Seric e->e_flags &= ~EF_OLDSTYLE; 7411446Seric delimiter = ' '; 7555012Seric if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) 7611446Seric delimiter = ','; 778223Seric 784423Seric firstone = TRUE; 794324Seric al = NULL; 8058082Seric naddrs = 0; 818223Seric 828081Seric for (p = list; *p != '\0'; ) 834174Seric { 8458333Seric auto char *delimptr; 858081Seric register ADDRESS *a; 864319Seric 878081Seric /* parse the address */ 8858050Seric while ((isascii(*p) && isspace(*p)) || *p == ',') 894174Seric p++; 9064284Seric a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); 9158333Seric p = delimptr; 929297Seric if (a == NULL) 934174Seric continue; 944324Seric a->q_next = al; 954399Seric a->q_alias = ctladdr; 964444Seric 974444Seric /* see if this should be marked as a primary address */ 984423Seric if (ctladdr == NULL || 998081Seric (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags))) 1004423Seric a->q_flags |= QPRIMARY; 1014444Seric 1029379Seric if (ctladdr != NULL && sameaddr(ctladdr, a)) 10358061Seric ctladdr->q_flags |= QSELFREF; 10457731Seric al = a; 1054423Seric firstone = FALSE; 1064324Seric } 1074324Seric 1084324Seric /* arrange to send to everyone on the local send list */ 1094324Seric while (al != NULL) 1104324Seric { 1114324Seric register ADDRESS *a = al; 1124324Seric 1134324Seric al = a->q_next; 11455012Seric a = recipient(a, sendq, e); 1154993Seric 116*67880Seric /* arrange to inherit attributes from parent */ 117*67880Seric if (ctladdr != NULL) 118*67880Seric { 119*67880Seric /* full name */ 120*67880Seric if (a->q_fullname == NULL) 121*67880Seric a->q_fullname = ctladdr->q_fullname; 122*67880Seric 123*67880Seric /* various flag bits */ 124*67880Seric a->q_flags &= ~QINHERITEDBITS; 125*67880Seric a->q_flags |= ctladdr->q_flags & QINHERITEDBITS; 126*67880Seric } 12758082Seric naddrs++; 1284174Seric } 1294324Seric 13063847Seric e->e_to = oldto; 13158082Seric return (naddrs); 1324174Seric } 1334174Seric /* 1344174Seric ** RECIPIENT -- Designate a message recipient 1354174Seric ** 1364174Seric ** Saves the named person for future mailing. 1374174Seric ** 1384174Seric ** Parameters: 1394174Seric ** a -- the (preparsed) address header for the recipient. 1405006Seric ** sendq -- a pointer to the head of a queue to put the 1415006Seric ** recipient in. Duplicate supression is done 1425006Seric ** in this queue. 14357731Seric ** e -- the current envelope. 1444174Seric ** 1454174Seric ** Returns: 14612613Seric ** The actual address in the queue. This will be "a" if 14712613Seric ** the address is not a duplicate, else the original address. 1484174Seric ** 1494174Seric ** Side Effects: 1504174Seric ** none. 1514174Seric */ 1524174Seric 15312613Seric ADDRESS * 15455012Seric recipient(a, sendq, e) 1554174Seric register ADDRESS *a; 1565006Seric register ADDRESS **sendq; 15755012Seric register ENVELOPE *e; 1584174Seric { 1594174Seric register ADDRESS *q; 1604319Seric ADDRESS **pq; 1614174Seric register struct mailer *m; 1629210Seric register char *p; 1639210Seric bool quoted = FALSE; /* set if the addr has a quote bit */ 16453735Seric int findusercount = 0; 16567264Seric int i; 16667264Seric char *buf; 16767264Seric char buf0[MAXNAME]; /* unquoted image of the user name */ 16858247Seric extern int safefile(); 1694174Seric 17055012Seric e->e_to = a->q_paddr; 1714600Seric m = a->q_mailer; 1724174Seric errno = 0; 1737676Seric if (tTd(26, 1)) 1744444Seric { 1754444Seric printf("\nrecipient: "); 1764444Seric printaddr(a, FALSE); 1774444Seric } 1784174Seric 17964146Seric /* if this is primary, add it to the original recipient list */ 18064146Seric if (a->q_alias == NULL) 18164146Seric { 18264146Seric if (e->e_origrcpt == NULL) 18364146Seric e->e_origrcpt = a->q_paddr; 18464146Seric else if (e->e_origrcpt != a->q_paddr) 18564146Seric e->e_origrcpt = ""; 18664146Seric } 18764146Seric 1884174Seric /* break aliasing loops */ 1894174Seric if (AliasLevel > MAXRCRSN) 1904174Seric { 19158151Seric usrerr("554 aliasing/forwarding loop broken"); 19212613Seric return (a); 1934174Seric } 1944174Seric 1954174Seric /* 1964627Seric ** Finish setting up address structure. 1974174Seric */ 1984174Seric 19916160Seric /* get unquoted user for file, program or user.name check */ 20067264Seric i = strlen(a->q_user); 20167264Seric if (i >= sizeof buf) 20267264Seric buf = xalloc(i + 1); 20367264Seric else 20467264Seric buf = buf0; 2059210Seric (void) strcpy(buf, a->q_user); 2069210Seric for (p = buf; *p != '\0' && !quoted; p++) 2079210Seric { 20854993Seric if (*p == '\\') 2099210Seric quoted = TRUE; 2109210Seric } 21154983Seric stripquotes(buf); 2129210Seric 21357402Seric /* check for direct mailing to restricted mailers */ 21465496Seric if (m == ProgMailer) 2154174Seric { 21665496Seric if (a->q_alias == NULL) 21765496Seric { 21865496Seric a->q_flags |= QBADADDR; 21965496Seric usrerr("550 Cannot mail directly to programs"); 22065496Seric } 22165496Seric else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) 22265496Seric { 22365496Seric a->q_flags |= QBADADDR; 22465496Seric usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", 22565496Seric a->q_alias->q_ruser, MyHostName); 22665496Seric } 22765496Seric else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) 22865496Seric { 22965496Seric a->q_flags |= QBADADDR; 23065496Seric usrerr("550 Address %s is unsafe for mailing to programs", 23165496Seric a->q_alias->q_paddr); 23265496Seric } 2334174Seric } 2344174Seric 2354174Seric /* 2364419Seric ** Look up this person in the recipient list. 2374419Seric ** If they are there already, return, otherwise continue. 2384419Seric ** If the list is empty, just add it. Notice the cute 2394419Seric ** hack to make from addresses suppress things correctly: 2404419Seric ** the QDONTSEND bit will be set in the send list. 2414419Seric ** [Please note: the emphasis is on "hack."] 2424174Seric */ 2434174Seric 2445006Seric for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) 2454174Seric { 24658294Seric if (sameaddr(q, a)) 2474174Seric { 2487676Seric if (tTd(26, 1)) 2494444Seric { 2504444Seric printf("%s in sendq: ", a->q_paddr); 2514444Seric printaddr(q, FALSE); 2524444Seric } 25365593Seric if (!bitset(QPRIMARY, q->q_flags)) 25458065Seric { 25565593Seric if (!bitset(QDONTSEND, a->q_flags)) 25658151Seric message("duplicate suppressed"); 25765593Seric q->q_flags |= a->q_flags; 25865593Seric } 25965593Seric else if (bitset(QSELFREF, q->q_flags)) 26065579Seric q->q_flags |= a->q_flags & ~QDONTSEND; 26163847Seric a = q; 26263847Seric goto testselfdestruct; 2634174Seric } 2644319Seric } 2654174Seric 2664319Seric /* add address on list */ 26758884Seric *pq = a; 26858884Seric a->q_next = NULL; 2694174Seric 2704174Seric /* 27157402Seric ** Alias the name and handle special mailer types. 2724174Seric */ 2734174Seric 27453735Seric trylocaluser: 27555354Seric if (tTd(29, 7)) 27655354Seric printf("at trylocaluser %s\n", a->q_user); 27755354Seric 27858680Seric if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) 27963847Seric goto testselfdestruct; 28057402Seric 28157402Seric if (m == InclMailer) 2824174Seric { 28357402Seric a->q_flags |= QDONTSEND; 28464761Seric if (a->q_alias == NULL) 2854174Seric { 28658680Seric a->q_flags |= QBADADDR; 28758151Seric usrerr("550 Cannot mail directly to :include:s"); 2884174Seric } 2894174Seric else 29050556Seric { 29159563Seric int ret; 29258247Seric 29358151Seric message("including file %s", a->q_user); 29459563Seric ret = include(a->q_user, FALSE, a, sendq, e); 29559563Seric if (transienterror(ret)) 29659563Seric { 29759563Seric #ifdef LOG 29859563Seric if (LogLevel > 2) 29966239Seric syslog(LOG_ERR, "%s: include %s: transient error: %s", 30066284Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 30166284Seric a->q_user, errstring(ret)); 30259563Seric #endif 30363853Seric a->q_flags |= QQUEUEUP; 30465215Seric a->q_flags &= ~QDONTSEND; 30559563Seric usrerr("451 Cannot open %s: %s", 30659563Seric a->q_user, errstring(ret)); 30759563Seric } 30859563Seric else if (ret != 0) 30959563Seric { 31063938Seric a->q_flags |= QBADADDR; 31159563Seric usrerr("550 Cannot open %s: %s", 31259563Seric a->q_user, errstring(ret)); 31359563Seric } 31450556Seric } 3154174Seric } 31657642Seric else if (m == FileMailer) 3174174Seric { 3184329Seric extern bool writable(); 3194174Seric 32051317Seric /* check if writable or creatable */ 32164761Seric if (a->q_alias == NULL) 3224174Seric { 32358680Seric a->q_flags |= QBADADDR; 32458151Seric usrerr("550 Cannot mail directly to files"); 3254174Seric } 32665496Seric else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) 32765496Seric { 32865496Seric a->q_flags |= QBADADDR; 32965496Seric usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", 33065496Seric a->q_alias->q_ruser, MyHostName); 33165496Seric } 33265496Seric else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) 33365496Seric { 33465496Seric a->q_flags |= QBADADDR; 33565496Seric usrerr("550 Address %s is unsafe for mailing to files", 33665496Seric a->q_alias->q_paddr); 33765496Seric } 33865112Seric else if (!writable(buf, getctladdr(a), SFF_ANYFILE)) 33951317Seric { 34058680Seric a->q_flags |= QBADADDR; 34164771Seric giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e); 34251317Seric } 34351317Seric } 34451317Seric 34557402Seric /* try aliasing */ 34667472Seric if (!bitset(QDONTSEND, a->q_flags) && bitnset(M_ALIASABLE, m->m_flags)) 34767472Seric alias(a, sendq, e); 34857402Seric 34957402Seric # ifdef USERDB 35057402Seric /* if not aliased, look it up in the user database */ 35167472Seric if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) && 35267472Seric bitnset(M_CHECKUDB, m->m_flags)) 35357402Seric { 35457402Seric extern int udbexpand(); 35557402Seric 35657402Seric if (udbexpand(a, sendq, e) == EX_TEMPFAIL) 35757402Seric { 35863853Seric a->q_flags |= QQUEUEUP; 35957402Seric if (e->e_message == NULL) 36057402Seric e->e_message = newstr("Deferred: user database error"); 36157402Seric # ifdef LOG 36258020Seric if (LogLevel > 8) 36359623Seric syslog(LOG_INFO, "%s: deferred: udbexpand: %s", 36466284Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 36566284Seric errstring(errno)); 36657402Seric # endif 36759615Seric message("queued (user database error): %s", 36859615Seric errstring(errno)); 36957642Seric e->e_nrcpts++; 37063847Seric goto testselfdestruct; 37157402Seric } 37257402Seric } 37357402Seric # endif 37457402Seric 37551317Seric /* 37651317Seric ** If we have a level two config file, then pass the name through 37751317Seric ** Ruleset 5 before sending it off. Ruleset 5 has the right 37851317Seric ** to send rewrite it to another mailer. This gives us a hook 37951317Seric ** after local aliasing has been done. 38051317Seric */ 38151317Seric 38251317Seric if (tTd(29, 5)) 38351317Seric { 38451317Seric printf("recipient: testing local? cl=%d, rr5=%x\n\t", 38551317Seric ConfigLevel, RewriteRules[5]); 38651317Seric printaddr(a, FALSE); 38751317Seric } 38867472Seric if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && 38967472Seric ConfigLevel >= 2 && RewriteRules[5] != NULL && 39067472Seric bitnset(M_TRYRULESET5, m->m_flags)) 39151317Seric { 39255012Seric maplocaluser(a, sendq, e); 39351317Seric } 39451317Seric 39551317Seric /* 39651317Seric ** If it didn't get rewritten to another mailer, go ahead 39751317Seric ** and deliver it. 39851317Seric */ 39951317Seric 40067472Seric if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && 40167472Seric bitnset(M_HASPWENT, m->m_flags)) 40251317Seric { 40355354Seric auto bool fuzzy; 40451317Seric register struct passwd *pw; 40551317Seric extern struct passwd *finduser(); 40651317Seric 40751317Seric /* warning -- finduser may trash buf */ 40855354Seric pw = finduser(buf, &fuzzy); 40951317Seric if (pw == NULL) 41051317Seric { 41158680Seric a->q_flags |= QBADADDR; 41264771Seric giveresponse(EX_NOUSER, m, NULL, a->q_alias, e); 41351317Seric } 4144174Seric else 4154174Seric { 41651317Seric char nbuf[MAXNAME]; 4174373Seric 41855354Seric if (fuzzy) 4194174Seric { 42053735Seric /* name was a fuzzy match */ 42151317Seric a->q_user = newstr(pw->pw_name); 42253735Seric if (findusercount++ > 3) 42353735Seric { 42458680Seric a->q_flags |= QBADADDR; 42558151Seric usrerr("554 aliasing/forwarding loop for %s broken", 42653735Seric pw->pw_name); 42767264Seric goto done; 42853735Seric } 42953735Seric 43053735Seric /* see if it aliases */ 43151317Seric (void) strcpy(buf, pw->pw_name); 43253735Seric goto trylocaluser; 4334174Seric } 43465822Seric if (strcmp(pw->pw_dir, "/") == 0) 43565822Seric a->q_home = ""; 43665822Seric else 43765822Seric a->q_home = newstr(pw->pw_dir); 43851317Seric a->q_uid = pw->pw_uid; 43951317Seric a->q_gid = pw->pw_gid; 44059083Seric a->q_ruser = newstr(pw->pw_name); 44151317Seric a->q_flags |= QGOODUID; 44251317Seric buildfname(pw->pw_gecos, pw->pw_name, nbuf); 44351317Seric if (nbuf[0] != '\0') 44451317Seric a->q_fullname = newstr(nbuf); 44565211Seric if (pw->pw_shell != NULL && pw->pw_shell[0] != '\0' && 44665211Seric !usershellok(pw->pw_shell)) 44765206Seric { 44865211Seric a->q_flags |= QBOGUSSHELL; 44965206Seric } 45051317Seric if (!quoted) 45155012Seric forward(a, sendq, e); 4524174Seric } 4534174Seric } 45457642Seric if (!bitset(QDONTSEND, a->q_flags)) 45557642Seric e->e_nrcpts++; 45663847Seric 45763847Seric testselfdestruct: 45863978Seric if (tTd(26, 8)) 45963847Seric { 46063978Seric printf("testselfdestruct: "); 46163978Seric printaddr(a, TRUE); 46263978Seric } 46363978Seric if (a->q_alias == NULL && a != &e->e_from && 46463978Seric bitset(QDONTSEND, a->q_flags)) 46563978Seric { 46663978Seric q = *sendq; 46763965Seric while (q != NULL && bitset(QDONTSEND, q->q_flags)) 46863847Seric q = q->q_next; 46963978Seric if (q == NULL) 47063847Seric { 47163847Seric a->q_flags |= QBADADDR; 47263847Seric usrerr("554 aliasing/forwarding loop broken"); 47363847Seric } 47463847Seric } 47567264Seric 47667264Seric done: 47767264Seric if (buf != buf0) 47867264Seric free(buf); 47912613Seric return (a); 4804174Seric } 4814174Seric /* 4824373Seric ** FINDUSER -- find the password entry for a user. 4834373Seric ** 4844373Seric ** This looks a lot like getpwnam, except that it may want to 4854373Seric ** do some fancier pattern matching in /etc/passwd. 4864373Seric ** 4879379Seric ** This routine contains most of the time of many sendmail runs. 4889379Seric ** It deserves to be optimized. 4899379Seric ** 4904373Seric ** Parameters: 4914373Seric ** name -- the name to match against. 49255354Seric ** fuzzyp -- an outarg that is set to TRUE if this entry 49355354Seric ** was found using the fuzzy matching algorithm; 49455354Seric ** set to FALSE otherwise. 4954373Seric ** 4964373Seric ** Returns: 4974373Seric ** A pointer to a pw struct. 4984373Seric ** NULL if name is unknown or ambiguous. 4994373Seric ** 5004373Seric ** Side Effects: 5014407Seric ** may modify name. 5024373Seric */ 5034373Seric 5044373Seric struct passwd * 50555354Seric finduser(name, fuzzyp) 5064373Seric char *name; 50755354Seric bool *fuzzyp; 5084373Seric { 5094376Seric register struct passwd *pw; 5104407Seric register char *p; 51115325Seric extern struct passwd *getpwent(); 51215325Seric extern struct passwd *getpwnam(); 5134373Seric 51455354Seric if (tTd(29, 4)) 51555354Seric printf("finduser(%s): ", name); 51655354Seric 51755354Seric *fuzzyp = FALSE; 5184407Seric 51967839Seric #ifdef HESIOD 52064673Seric /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ 52164673Seric for (p = name; *p != '\0'; p++) 52264673Seric if (!isascii(*p) || !isdigit(*p)) 52364673Seric break; 52464673Seric if (*p == '\0') 52564673Seric { 52664673Seric if (tTd(29, 4)) 52764673Seric printf("failed (numeric input)\n"); 52864673Seric return NULL; 52964673Seric } 53067839Seric #endif 53164673Seric 53225777Seric /* look up this login name using fast path */ 53312634Seric if ((pw = getpwnam(name)) != NULL) 53455354Seric { 53555354Seric if (tTd(29, 4)) 53655354Seric printf("found (non-fuzzy)\n"); 53712634Seric return (pw); 53855354Seric } 53912634Seric 54053735Seric #ifdef MATCHGECOS 54153735Seric /* see if fuzzy matching allowed */ 54253735Seric if (!MatchGecos) 54355354Seric { 54455354Seric if (tTd(29, 4)) 54555354Seric printf("not found (fuzzy disabled)\n"); 54653735Seric return NULL; 54755354Seric } 54853735Seric 54912634Seric /* search for a matching full name instead */ 55025777Seric for (p = name; *p != '\0'; p++) 55125777Seric { 55225777Seric if (*p == (SpaceSub & 0177) || *p == '_') 55325777Seric *p = ' '; 55425777Seric } 55523107Seric (void) setpwent(); 5564376Seric while ((pw = getpwent()) != NULL) 5574376Seric { 5584998Seric char buf[MAXNAME]; 5594376Seric 5604998Seric buildfname(pw->pw_gecos, pw->pw_name, buf); 56156795Seric if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) 5624381Seric { 56355354Seric if (tTd(29, 4)) 56455354Seric printf("fuzzy matches %s\n", pw->pw_name); 56558151Seric message("sending to login name %s", pw->pw_name); 56655354Seric *fuzzyp = TRUE; 5674376Seric return (pw); 5684377Seric } 5694376Seric } 57055354Seric if (tTd(29, 4)) 57155354Seric printf("no fuzzy match found\n"); 57259015Seric #else 57359015Seric if (tTd(29, 4)) 57459015Seric printf("not found (fuzzy disabled)\n"); 57559015Seric #endif 5764376Seric return (NULL); 5774373Seric } 5784373Seric /* 5794329Seric ** WRITABLE -- predicate returning if the file is writable. 5804329Seric ** 5814329Seric ** This routine must duplicate the algorithm in sys/fio.c. 5824329Seric ** Unfortunately, we cannot use the access call since we 5834329Seric ** won't necessarily be the real uid when we try to 5844329Seric ** actually open the file. 5854329Seric ** 5864329Seric ** Notice that ANY file with ANY execute bit is automatically 5874329Seric ** not writable. This is also enforced by mailfile. 5884329Seric ** 5894329Seric ** Parameters: 59065064Seric ** filename -- the file name to check. 59165112Seric ** ctladdr -- the controlling address for this file. 59265064Seric ** flags -- SFF_* flags to control the function. 5934329Seric ** 5944329Seric ** Returns: 5954329Seric ** TRUE -- if we will be able to write this file. 5964329Seric ** FALSE -- if we cannot write this file. 5974329Seric ** 5984329Seric ** Side Effects: 5994329Seric ** none. 6004329Seric */ 6014329Seric 6024329Seric bool 60365112Seric writable(filename, ctladdr, flags) 60464819Seric char *filename; 60565112Seric ADDRESS *ctladdr; 60665064Seric int flags; 6074329Seric { 60855372Seric uid_t euid; 60955372Seric gid_t egid; 6104329Seric int bits; 61164944Seric register char *p; 61264944Seric char *uname; 61364944Seric struct stat stb; 61464944Seric extern char RealUserName[]; 6154329Seric 61664819Seric if (tTd(29, 5)) 61765064Seric printf("writable(%s, %x)\n", filename, flags); 61864944Seric 61964944Seric #ifdef HASLSTAT 62065064Seric if ((bitset(SFF_NOSLINK, flags) ? lstat(filename, &stb) 62165064Seric : stat(filename, &stb)) < 0) 62264944Seric #else 62364944Seric if (stat(filename, &stb) < 0) 62464944Seric #endif 62564944Seric { 62664944Seric /* file does not exist -- see if directory is safe */ 62764944Seric p = strrchr(filename, '/'); 62864944Seric if (p == NULL) 62964944Seric { 63065067Seric errno = ENOTDIR; 63164944Seric return FALSE; 63264944Seric } 63365067Seric *p = '\0'; 63465067Seric errno = safefile(filename, RealUid, RealGid, RealUserName, 63565067Seric SFF_MUSTOWN, S_IWRITE|S_IEXEC); 63664944Seric *p = '/'; 63765067Seric return errno == 0; 63864944Seric } 63964944Seric 64065225Seric #ifdef SUID_ROOT_FILES_OK 64165225Seric /* really ought to be passed down -- and not a good idea */ 64265225Seric flags |= SFF_ROOTOK; 64365225Seric #endif 64465225Seric 64564944Seric /* 64664944Seric ** File does exist -- check that it is writable. 64764944Seric */ 64864944Seric 64964944Seric if (bitset(0111, stb.st_mode)) 65065022Seric { 65165022Seric if (tTd(29, 5)) 65265022Seric printf("failed (mode %o: x bits)\n", stb.st_mode); 65365067Seric errno = EPERM; 6544329Seric return (FALSE); 65565022Seric } 65664944Seric 65765112Seric if (ctladdr != NULL && geteuid() == 0) 65864944Seric { 65965112Seric euid = ctladdr->q_uid; 66065112Seric egid = ctladdr->q_gid; 66165112Seric uname = ctladdr->q_user; 66264944Seric } 66365112Seric else 66465112Seric { 66565112Seric euid = RealUid; 66665112Seric egid = RealGid; 66765112Seric uname = RealUserName; 66865112Seric } 66965138Seric if (euid == 0) 67065138Seric { 67165138Seric euid = DefUid; 67265138Seric uname = DefUser; 67365138Seric } 67465138Seric if (egid == 0) 67565138Seric egid = DefGid; 6764329Seric if (geteuid() == 0) 6774329Seric { 67865225Seric if (bitset(S_ISUID, stb.st_mode) && 67965225Seric (stb.st_uid != 0 || bitset(SFF_ROOTOK, flags))) 68064944Seric { 68164944Seric euid = stb.st_uid; 68264944Seric uname = NULL; 68364944Seric } 68465225Seric if (bitset(S_ISGID, stb.st_mode) && 68565225Seric (stb.st_gid != 0 || bitset(SFF_ROOTOK, flags))) 68664944Seric egid = stb.st_gid; 6874329Seric } 6884329Seric 68964819Seric if (tTd(29, 5)) 69064819Seric printf("\teu/gid=%d/%d, st_u/gid=%d/%d\n", 69164944Seric euid, egid, stb.st_uid, stb.st_gid); 69264819Seric 69365067Seric errno = safefile(filename, euid, egid, uname, flags, S_IWRITE); 69465067Seric return errno == 0; 6954329Seric } 6964329Seric /* 6974174Seric ** INCLUDE -- handle :include: specification. 6984174Seric ** 6994174Seric ** Parameters: 7004174Seric ** fname -- filename to include. 70153037Seric ** forwarding -- if TRUE, we are reading a .forward file. 70253037Seric ** if FALSE, it's a :include: file. 7034399Seric ** ctladdr -- address template to use to fill in these 7044399Seric ** addresses -- effective user/group id are 7054399Seric ** the important things. 7065006Seric ** sendq -- a pointer to the head of the send queue 7075006Seric ** to put these addresses in. 7084174Seric ** 7094174Seric ** Returns: 71057136Seric ** open error status 7114174Seric ** 7124174Seric ** Side Effects: 7134174Seric ** reads the :include: file and sends to everyone 7144174Seric ** listed in that file. 71565909Seric ** 71665909Seric ** Security Note: 71765909Seric ** If you have restricted chown (that is, you can't 71865909Seric ** give a file away), it is reasonable to allow programs 71965909Seric ** and files called from this :include: file to be to be 72065909Seric ** run as the owner of the :include: file. This is bogus 72165909Seric ** if there is any chance of someone giving away a file. 72265909Seric ** We assume that pre-POSIX systems can give away files. 72365909Seric ** 72465909Seric ** There is an additional restriction that if you 72565909Seric ** forward to a :include: file, it will not take on 72665909Seric ** the ownership of the :include: file. This may not 72765909Seric ** be necessary, but shouldn't hurt. 7284174Seric */ 7294174Seric 73053037Seric static jmp_buf CtxIncludeTimeout; 73163937Seric static int includetimeout(); 73253037Seric 73365496Seric #ifndef S_IWOTH 73465496Seric # define S_IWOTH (S_IWRITE >> 6) 73565496Seric #endif 73665496Seric 73757136Seric int 73855012Seric include(fname, forwarding, ctladdr, sendq, e) 7394174Seric char *fname; 74053037Seric bool forwarding; 7414399Seric ADDRESS *ctladdr; 7425006Seric ADDRESS **sendq; 74355012Seric ENVELOPE *e; 7444174Seric { 74564570Seric register FILE *fp = NULL; 74655012Seric char *oldto = e->e_to; 7479379Seric char *oldfilename = FileName; 7489379Seric int oldlinenumber = LineNumber; 74953037Seric register EVENT *ev = NULL; 75058082Seric int nincludes; 75164325Seric register ADDRESS *ca; 75264325Seric uid_t saveduid, uid; 75364325Seric gid_t savedgid, gid; 75464083Seric char *uname; 75564325Seric int rval = 0; 75665064Seric int sfflags = forwarding ? SFF_MUSTOWN : SFF_ANYFILE; 75765496Seric struct stat st; 75865948Seric char buf[MAXLINE]; 75965909Seric #ifdef _POSIX_CHOWN_RESTRICTED 76065948Seric # if _POSIX_CHOWN_RESTRICTED == -1 76165948Seric # define safechown FALSE 76265948Seric # else 76365948Seric # define safechown TRUE 76465948Seric # endif 76565948Seric #else 76665948Seric # ifdef _PC_CHOWN_RESTRICTED 76765909Seric bool safechown; 76865948Seric # else 76965948Seric # ifdef BSD 77065948Seric # define safechown TRUE 77165948Seric # else 77265948Seric # define safechown FALSE 77365948Seric # endif 77465948Seric # endif 77565909Seric #endif 77665948Seric extern bool chownsafe(); 7774174Seric 77857186Seric if (tTd(27, 2)) 77957186Seric printf("include(%s)\n", fname); 78063902Seric if (tTd(27, 4)) 78163902Seric printf(" ruid=%d euid=%d\n", getuid(), geteuid()); 78263581Seric if (tTd(27, 14)) 78363581Seric { 78463581Seric printf("ctladdr "); 78563581Seric printaddr(ctladdr, FALSE); 78663581Seric } 78757186Seric 78864325Seric if (tTd(27, 9)) 78964325Seric printf("include: old uid = %d/%d\n", getuid(), geteuid()); 79053037Seric 79163581Seric ca = getctladdr(ctladdr); 79263581Seric if (ca == NULL) 79364083Seric { 79464846Seric uid = DefUid; 79564846Seric gid = DefGid; 79664846Seric uname = DefUser; 79764325Seric saveduid = -1; 79864083Seric } 79963581Seric else 80064083Seric { 80163581Seric uid = ca->q_uid; 80264083Seric gid = ca->q_gid; 80364083Seric uname = ca->q_user; 80464325Seric #ifdef HASSETREUID 80564325Seric saveduid = geteuid(); 80664325Seric savedgid = getegid(); 80764325Seric if (saveduid == 0) 80864325Seric { 80964325Seric initgroups(uname, gid); 81064325Seric if (uid != 0) 81167827Seric { 81267827Seric if (setreuid(0, uid) < 0) 81367827Seric syserr("setreuid(0, %d) failure (real=%d, eff=%d)", 81467827Seric uid, getuid(), geteuid()); 81567827Seric } 81664325Seric } 81764325Seric #endif 81864083Seric } 81963581Seric 82064325Seric if (tTd(27, 9)) 82164325Seric printf("include: new uid = %d/%d\n", getuid(), geteuid()); 82264325Seric 82364325Seric /* 82464325Seric ** If home directory is remote mounted but server is down, 82564325Seric ** this can hang or give errors; use a timeout to avoid this 82664325Seric */ 82764325Seric 82853037Seric if (setjmp(CtxIncludeTimeout) != 0) 82953037Seric { 83063853Seric ctladdr->q_flags |= QQUEUEUP; 83153037Seric errno = 0; 83263993Seric 83363993Seric /* return pseudo-error code */ 83464325Seric rval = EOPENTIMEOUT; 83564325Seric goto resetuid; 83653037Seric } 83767711Seric if (TimeOuts.to_fileopen > 0) 83867711Seric ev = setevent(TimeOuts.to_fileopen, includetimeout, 0); 83967711Seric else 84067711Seric ev = NULL; 84153037Seric 84263581Seric /* the input file must be marked safe */ 84364944Seric rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD); 84464329Seric if (rval != 0) 84553037Seric { 84664325Seric /* don't use this :include: file */ 84757186Seric if (tTd(27, 4)) 84858247Seric printf("include: not safe (uid=%d): %s\n", 84964329Seric uid, errstring(rval)); 85053037Seric } 85165496Seric else 8524174Seric { 85365496Seric fp = fopen(fname, "r"); 85465496Seric if (fp == NULL) 85558061Seric { 85664329Seric rval = errno; 85765496Seric if (tTd(27, 4)) 85865496Seric printf("include: open: %s\n", errstring(rval)); 85958061Seric } 8604406Seric } 86167711Seric if (ev != NULL) 86267711Seric clrevent(ev); 86353037Seric 86464570Seric resetuid: 86564570Seric 86664570Seric #ifdef HASSETREUID 86764570Seric if (saveduid == 0) 86864570Seric { 86964570Seric if (uid != 0) 87067827Seric { 87167827Seric if (setreuid(-1, 0) < 0) 87267827Seric syserr("setreuid(-1, 0) failure (real=%d, eff=%d)", 87367827Seric getuid(), geteuid()); 87467827Seric if (setreuid(RealUid, 0) < 0) 87564570Seric syserr("setreuid(%d, 0) failure (real=%d, eff=%d)", 87664570Seric RealUid, getuid(), geteuid()); 87767827Seric } 87864570Seric setgid(savedgid); 87964570Seric } 88064570Seric #endif 88164570Seric 88264570Seric if (tTd(27, 9)) 88364570Seric printf("include: reset uid = %d/%d\n", getuid(), geteuid()); 88464570Seric 88565593Seric if (rval == EOPENTIMEOUT) 88665593Seric usrerr("451 open timeout on %s", fname); 88765593Seric 88864570Seric if (fp == NULL) 88964570Seric return rval; 89064570Seric 89165496Seric if (fstat(fileno(fp), &st) < 0) 89265496Seric { 89365496Seric rval = errno; 89465496Seric syserr("Cannot fstat %s!", fname); 89565496Seric return rval; 89665496Seric } 89765496Seric 89865948Seric #ifndef safechown 89965948Seric safechown = chownsafe(fileno(fp)); 90065948Seric #endif 90165909Seric if (ca == NULL && safechown) 90265496Seric { 90365496Seric ctladdr->q_uid = st.st_uid; 90465496Seric ctladdr->q_gid = st.st_gid; 90565496Seric ctladdr->q_flags |= QGOODUID; 90665496Seric } 90765496Seric if (ca != NULL && ca->q_uid == st.st_uid) 90865496Seric { 90965496Seric /* optimization -- avoid getpwuid if we already have info */ 91065496Seric ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL; 91165496Seric ctladdr->q_ruser = ca->q_ruser; 91265496Seric } 91365496Seric else 91465496Seric { 91565909Seric char *sh; 91665496Seric register struct passwd *pw; 91765496Seric 91865909Seric sh = "/SENDMAIL/ANY/SHELL/"; 91965496Seric pw = getpwuid(st.st_uid); 92065909Seric if (pw != NULL) 92165496Seric { 92265496Seric ctladdr->q_ruser = newstr(pw->pw_name); 92365909Seric if (safechown) 92465909Seric sh = pw->pw_shell; 92565909Seric } 92665909Seric if (pw == NULL) 92765496Seric ctladdr->q_flags |= QBOGUSSHELL; 92865909Seric else if(!usershellok(sh)) 92965909Seric { 93065909Seric if (safechown) 93165909Seric ctladdr->q_flags |= QBOGUSSHELL; 93265909Seric else 93365909Seric ctladdr->q_flags |= QUNSAFEADDR; 93465496Seric } 93565496Seric } 93665496Seric 93758092Seric if (bitset(EF_VRFYONLY, e->e_flags)) 93858092Seric { 93958092Seric /* don't do any more now */ 94058868Seric ctladdr->q_flags |= QVERIFIED; 94158884Seric e->e_nrcpts++; 94258680Seric xfclose(fp, "include", fname); 94364570Seric return rval; 94458092Seric } 94558092Seric 94665496Seric /* 94765496Seric ** Check to see if some bad guy can write this file 94865496Seric ** 94965496Seric ** This should really do something clever with group 95065496Seric ** permissions; currently we just view world writable 95165496Seric ** as unsafe. Also, we don't check for writable 95265496Seric ** directories in the path. We've got to leave 95365496Seric ** something for the local sysad to do. 95465496Seric */ 95565496Seric 95665496Seric if (bitset(S_IWOTH, st.st_mode)) 95765496Seric ctladdr->q_flags |= QUNSAFEADDR; 95865496Seric 9594174Seric /* read the file -- each line is a comma-separated list. */ 9609379Seric FileName = fname; 9619379Seric LineNumber = 0; 96258082Seric ctladdr->q_flags &= ~QSELFREF; 96358082Seric nincludes = 0; 9644174Seric while (fgets(buf, sizeof buf, fp) != NULL) 9654174Seric { 96656795Seric register char *p = strchr(buf, '\n'); 9674174Seric 96840963Sbostic LineNumber++; 9694174Seric if (p != NULL) 9704174Seric *p = '\0'; 97157186Seric if (buf[0] == '#' || buf[0] == '\0') 97257139Seric continue; 97358008Seric e->e_to = NULL; 97458151Seric message("%s to %s", 97553037Seric forwarding ? "forwarding" : "sending", buf); 97657977Seric #ifdef LOG 97758020Seric if (forwarding && LogLevel > 9) 97857977Seric syslog(LOG_INFO, "%s: forward %s => %s", 97966284Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 98066284Seric oldto, buf); 98157977Seric #endif 98257977Seric 9834176Seric AliasLevel++; 98458082Seric nincludes += sendtolist(buf, ctladdr, sendq, e); 9854176Seric AliasLevel--; 9864174Seric } 98763902Seric 98863902Seric if (ferror(fp) && tTd(27, 3)) 98963902Seric printf("include: read error: %s\n", errstring(errno)); 99058082Seric if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) 99158065Seric { 99258065Seric if (tTd(27, 5)) 99358065Seric { 99458065Seric printf("include: QDONTSEND "); 99558065Seric printaddr(ctladdr, FALSE); 99658065Seric } 99758065Seric ctladdr->q_flags |= QDONTSEND; 99858065Seric } 9994174Seric 100058680Seric (void) xfclose(fp, "include", fname); 10019379Seric FileName = oldfilename; 10029379Seric LineNumber = oldlinenumber; 100363847Seric e->e_to = oldto; 100464325Seric return rval; 10054174Seric } 100653037Seric 100753037Seric static 100853037Seric includetimeout() 100953037Seric { 101053037Seric longjmp(CtxIncludeTimeout, 1); 101153037Seric } 10124324Seric /* 10134324Seric ** SENDTOARGV -- send to an argument vector. 10144324Seric ** 10154324Seric ** Parameters: 10164324Seric ** argv -- argument vector to send to. 101758247Seric ** e -- the current envelope. 10184324Seric ** 10194324Seric ** Returns: 10204324Seric ** none. 10214324Seric ** 10224324Seric ** Side Effects: 10234324Seric ** puts all addresses on the argument vector onto the 10244324Seric ** send queue. 10254324Seric */ 10264324Seric 102755012Seric sendtoargv(argv, e) 10284324Seric register char **argv; 102955012Seric register ENVELOPE *e; 10304324Seric { 10314324Seric register char *p; 10324324Seric 10334324Seric while ((p = *argv++) != NULL) 10344324Seric { 103564284Seric (void) sendtolist(p, NULLADDR, &e->e_sendqueue, e); 10364324Seric } 10374324Seric } 10384399Seric /* 10394399Seric ** GETCTLADDR -- get controlling address from an address header. 10404399Seric ** 10414399Seric ** If none, get one corresponding to the effective userid. 10424399Seric ** 10434399Seric ** Parameters: 10444399Seric ** a -- the address to find the controller of. 10454399Seric ** 10464399Seric ** Returns: 10474399Seric ** the controlling address. 10484399Seric ** 10494399Seric ** Side Effects: 10504399Seric ** none. 10514399Seric */ 10524399Seric 10534399Seric ADDRESS * 10544399Seric getctladdr(a) 10554399Seric register ADDRESS *a; 10564399Seric { 10574404Seric while (a != NULL && !bitset(QGOODUID, a->q_flags)) 10584399Seric a = a->q_alias; 10594399Seric return (a); 10604399Seric } 1061