recipient.c revision 64562
138032Speter/* 264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1438032Speter#ifndef lint 1564562Sgshapirostatic char id[] = "@(#)$Id: recipient.c,v 8.231.14.5 2000/06/27 20:15:46 gshapiro Exp $"; 1664562Sgshapiro#endif /* ! lint */ 1738032Speter 1864562Sgshapiro#include <sendmail.h> 1938032Speter 2064562Sgshapiro 2164562Sgshapirostatic void includetimeout __P((void)); 2264562Sgshapirostatic ADDRESS *self_reference __P((ADDRESS *)); 2364562Sgshapiro 2438032Speter/* 2538032Speter** SENDTOLIST -- Designate a send list. 2638032Speter** 2738032Speter** The parameter is a comma-separated list of people to send to. 2838032Speter** This routine arranges to send to all of them. 2938032Speter** 3038032Speter** Parameters: 3138032Speter** list -- the send list. 3238032Speter** ctladdr -- the address template for the person to 3338032Speter** send to -- effective uid/gid are important. 3438032Speter** This is typically the alias that caused this 3538032Speter** expansion. 3638032Speter** sendq -- a pointer to the head of a queue to put 3738032Speter** these people into. 3838032Speter** aliaslevel -- the current alias nesting depth -- to 3938032Speter** diagnose loops. 4038032Speter** e -- the envelope in which to add these recipients. 4138032Speter** 4238032Speter** Returns: 4338032Speter** The number of addresses actually on the list. 4438032Speter** 4538032Speter** Side Effects: 4638032Speter** none. 4738032Speter*/ 4838032Speter 4938032Speter/* q_flags bits inherited from ctladdr */ 5038032Speter#define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY) 5138032Speter 5238032Speterint 5338032Spetersendtolist(list, ctladdr, sendq, aliaslevel, e) 5438032Speter char *list; 5538032Speter ADDRESS *ctladdr; 5638032Speter ADDRESS **sendq; 5738032Speter int aliaslevel; 5838032Speter register ENVELOPE *e; 5938032Speter{ 6038032Speter register char *p; 6138032Speter register ADDRESS *al; /* list of addresses to send to */ 6238032Speter char delimiter; /* the address delimiter */ 6338032Speter int naddrs; 6438032Speter int i; 6538032Speter char *oldto = e->e_to; 6638032Speter char *bufp; 6738032Speter char buf[MAXNAME + 1]; 6838032Speter 6938032Speter if (list == NULL) 7038032Speter { 7138032Speter syserr("sendtolist: null list"); 7238032Speter return 0; 7338032Speter } 7438032Speter 7538032Speter if (tTd(25, 1)) 7638032Speter { 7764562Sgshapiro dprintf("sendto: %s\n ctladdr=", list); 7838032Speter printaddr(ctladdr, FALSE); 7938032Speter } 8038032Speter 8138032Speter /* heuristic to determine old versus new style addresses */ 8238032Speter if (ctladdr == NULL && 8338032Speter (strchr(list, ',') != NULL || strchr(list, ';') != NULL || 8438032Speter strchr(list, '<') != NULL || strchr(list, '(') != NULL)) 8538032Speter e->e_flags &= ~EF_OLDSTYLE; 8638032Speter delimiter = ' '; 8738032Speter if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) 8838032Speter delimiter = ','; 8938032Speter 9038032Speter al = NULL; 9138032Speter naddrs = 0; 9238032Speter 9338032Speter /* make sure we have enough space to copy the string */ 9438032Speter i = strlen(list) + 1; 9538032Speter if (i <= sizeof buf) 9664562Sgshapiro { 9738032Speter bufp = buf; 9864562Sgshapiro i = sizeof buf; 9964562Sgshapiro } 10038032Speter else 10138032Speter bufp = xalloc(i); 10264562Sgshapiro (void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i); 10338032Speter 10464562Sgshapiro#if _FFR_ADDR_TYPE 10564562Sgshapiro define(macid("{addr_type}", NULL), "e r", e); 10664562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 10738032Speter for (p = bufp; *p != '\0'; ) 10838032Speter { 10938032Speter auto char *delimptr; 11038032Speter register ADDRESS *a; 11138032Speter 11238032Speter /* parse the address */ 11338032Speter while ((isascii(*p) && isspace(*p)) || *p == ',') 11438032Speter p++; 11538032Speter a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); 11638032Speter p = delimptr; 11738032Speter if (a == NULL) 11838032Speter continue; 11938032Speter a->q_next = al; 12038032Speter a->q_alias = ctladdr; 12138032Speter 12238032Speter /* arrange to inherit attributes from parent */ 12338032Speter if (ctladdr != NULL) 12438032Speter { 12538032Speter ADDRESS *b; 12638032Speter 12738032Speter /* self reference test */ 12838032Speter if (sameaddr(ctladdr, a)) 12938032Speter { 13038032Speter if (tTd(27, 5)) 13138032Speter { 13264562Sgshapiro dprintf("sendtolist: QSELFREF "); 13338032Speter printaddr(ctladdr, FALSE); 13438032Speter } 13538032Speter ctladdr->q_flags |= QSELFREF; 13638032Speter } 13738032Speter 13838032Speter /* check for address loops */ 13964562Sgshapiro b = self_reference(a); 14038032Speter if (b != NULL) 14138032Speter { 14238032Speter b->q_flags |= QSELFREF; 14338032Speter if (tTd(27, 5)) 14438032Speter { 14564562Sgshapiro dprintf("sendtolist: QSELFREF "); 14638032Speter printaddr(b, FALSE); 14738032Speter } 14838032Speter if (a != b) 14938032Speter { 15038032Speter if (tTd(27, 5)) 15138032Speter { 15264562Sgshapiro dprintf("sendtolist: QS_DONTSEND "); 15338032Speter printaddr(a, FALSE); 15438032Speter } 15564562Sgshapiro a->q_state = QS_DONTSEND; 15638032Speter b->q_flags |= a->q_flags & QNOTREMOTE; 15738032Speter continue; 15838032Speter } 15938032Speter } 16038032Speter 16138032Speter /* full name */ 16238032Speter if (a->q_fullname == NULL) 16338032Speter a->q_fullname = ctladdr->q_fullname; 16438032Speter 16538032Speter /* various flag bits */ 16638032Speter a->q_flags &= ~QINHERITEDBITS; 16738032Speter a->q_flags |= ctladdr->q_flags & QINHERITEDBITS; 16838032Speter 16938032Speter /* original recipient information */ 17038032Speter a->q_orcpt = ctladdr->q_orcpt; 17138032Speter } 17238032Speter 17338032Speter al = a; 17438032Speter } 17538032Speter 17638032Speter /* arrange to send to everyone on the local send list */ 17738032Speter while (al != NULL) 17838032Speter { 17938032Speter register ADDRESS *a = al; 18038032Speter 18138032Speter al = a->q_next; 18238032Speter a = recipient(a, sendq, aliaslevel, e); 18338032Speter naddrs++; 18438032Speter } 18538032Speter 18638032Speter e->e_to = oldto; 18738032Speter if (bufp != buf) 18838032Speter free(bufp); 18964562Sgshapiro#if _FFR_ADDR_TYPE 19064562Sgshapiro define(macid("{addr_type}", NULL), NULL, e); 19164562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 19264562Sgshapiro return naddrs; 19338032Speter} 19438032Speter/* 19564562Sgshapiro** REMOVEFROMLIST -- Remove addresses from a send list. 19664562Sgshapiro** 19764562Sgshapiro** The parameter is a comma-separated list of recipients to remove. 19864562Sgshapiro** Note that it only deletes matching addresses. If those addresses 19964562Sgshapiro** have been expended already in the sendq, it won't mark the 20064562Sgshapiro** expanded recipients as QS_REMOVED. 20164562Sgshapiro** 20264562Sgshapiro** Parameters: 20364562Sgshapiro** list -- the list to remove. 20464562Sgshapiro** sendq -- a pointer to the head of a queue to remove 20564562Sgshapiro** these addresses from. 20664562Sgshapiro** e -- the envelope in which to remove these recipients. 20764562Sgshapiro** 20864562Sgshapiro** Returns: 20964562Sgshapiro** The number of addresses removed from the list. 21064562Sgshapiro** 21164562Sgshapiro*/ 21264562Sgshapiro 21364562Sgshapiroint 21464562Sgshapiroremovefromlist(list, sendq, e) 21564562Sgshapiro char *list; 21664562Sgshapiro ADDRESS **sendq; 21764562Sgshapiro ENVELOPE *e; 21864562Sgshapiro{ 21964562Sgshapiro char delimiter; /* the address delimiter */ 22064562Sgshapiro int naddrs; 22164562Sgshapiro int i; 22264562Sgshapiro char *p; 22364562Sgshapiro char *oldto = e->e_to; 22464562Sgshapiro char *bufp; 22564562Sgshapiro char buf[MAXNAME + 1]; 22664562Sgshapiro 22764562Sgshapiro if (list == NULL) 22864562Sgshapiro { 22964562Sgshapiro syserr("removefromlist: null list"); 23064562Sgshapiro return 0; 23164562Sgshapiro } 23264562Sgshapiro 23364562Sgshapiro if (tTd(25, 1)) 23464562Sgshapiro dprintf("removefromlist: %s\n", list); 23564562Sgshapiro 23664562Sgshapiro /* heuristic to determine old versus new style addresses */ 23764562Sgshapiro if (strchr(list, ',') != NULL || strchr(list, ';') != NULL || 23864562Sgshapiro strchr(list, '<') != NULL || strchr(list, '(') != NULL) 23964562Sgshapiro e->e_flags &= ~EF_OLDSTYLE; 24064562Sgshapiro delimiter = ' '; 24164562Sgshapiro if (!bitset(EF_OLDSTYLE, e->e_flags)) 24264562Sgshapiro delimiter = ','; 24364562Sgshapiro 24464562Sgshapiro naddrs = 0; 24564562Sgshapiro 24664562Sgshapiro /* make sure we have enough space to copy the string */ 24764562Sgshapiro i = strlen(list) + 1; 24864562Sgshapiro if (i <= sizeof buf) 24964562Sgshapiro { 25064562Sgshapiro bufp = buf; 25164562Sgshapiro i = sizeof buf; 25264562Sgshapiro } 25364562Sgshapiro else 25464562Sgshapiro bufp = xalloc(i); 25564562Sgshapiro (void) strlcpy(bufp, denlstring(list, FALSE, TRUE), i); 25664562Sgshapiro 25764562Sgshapiro#if _FFR_ADDR_TYPE 25864562Sgshapiro define(macid("{addr_type}", NULL), "e r", e); 25964562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 26064562Sgshapiro for (p = bufp; *p != '\0'; ) 26164562Sgshapiro { 26264562Sgshapiro ADDRESS a; /* parsed address to be removed */ 26364562Sgshapiro ADDRESS *q; 26464562Sgshapiro ADDRESS **pq; 26564562Sgshapiro char *delimptr; 26664562Sgshapiro 26764562Sgshapiro /* parse the address */ 26864562Sgshapiro while ((isascii(*p) && isspace(*p)) || *p == ',') 26964562Sgshapiro p++; 27064562Sgshapiro if (parseaddr(p, &a, RF_COPYALL, 27164562Sgshapiro delimiter, &delimptr, e) == NULL) 27264562Sgshapiro { 27364562Sgshapiro p = delimptr; 27464562Sgshapiro continue; 27564562Sgshapiro } 27664562Sgshapiro p = delimptr; 27764562Sgshapiro for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) 27864562Sgshapiro { 27964562Sgshapiro if (!QS_IS_DEAD(q->q_state) && 28064562Sgshapiro sameaddr(q, &a)) 28164562Sgshapiro { 28264562Sgshapiro if (tTd(25, 5)) 28364562Sgshapiro { 28464562Sgshapiro dprintf("removefromlist: QS_REMOVED "); 28564562Sgshapiro printaddr(&a, FALSE); 28664562Sgshapiro } 28764562Sgshapiro q->q_state = QS_REMOVED; 28864562Sgshapiro naddrs++; 28964562Sgshapiro break; 29064562Sgshapiro } 29164562Sgshapiro } 29264562Sgshapiro } 29364562Sgshapiro 29464562Sgshapiro e->e_to = oldto; 29564562Sgshapiro if (bufp != buf) 29664562Sgshapiro free(bufp); 29764562Sgshapiro#if _FFR_ADDR_TYPE 29864562Sgshapiro define(macid("{addr_type}", NULL), NULL, e); 29964562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 30064562Sgshapiro return naddrs; 30164562Sgshapiro} 30264562Sgshapiro/* 30338032Speter** RECIPIENT -- Designate a message recipient 30438032Speter** 30538032Speter** Saves the named person for future mailing. 30638032Speter** 30738032Speter** Parameters: 30838032Speter** a -- the (preparsed) address header for the recipient. 30938032Speter** sendq -- a pointer to the head of a queue to put the 31064562Sgshapiro** recipient in. Duplicate suppression is done 31138032Speter** in this queue. 31238032Speter** aliaslevel -- the current alias nesting depth. 31338032Speter** e -- the current envelope. 31438032Speter** 31538032Speter** Returns: 31638032Speter** The actual address in the queue. This will be "a" if 31738032Speter** the address is not a duplicate, else the original address. 31838032Speter** 31938032Speter** Side Effects: 32038032Speter** none. 32138032Speter*/ 32238032Speter 32338032SpeterADDRESS * 32438032Speterrecipient(a, sendq, aliaslevel, e) 32538032Speter register ADDRESS *a; 32638032Speter register ADDRESS **sendq; 32738032Speter int aliaslevel; 32838032Speter register ENVELOPE *e; 32938032Speter{ 33038032Speter register ADDRESS *q; 33138032Speter ADDRESS **pq; 33238032Speter register struct mailer *m; 33364562Sgshapiro register char *p = NULL; 33438032Speter bool quoted = FALSE; /* set if the addr has a quote bit */ 33538032Speter int findusercount = 0; 33664562Sgshapiro bool initialdontsend = QS_IS_DEAD(a->q_state); 33764562Sgshapiro int i, buflen; 33838032Speter char *buf; 33938032Speter char buf0[MAXNAME + 1]; /* unquoted image of the user name */ 34038032Speter 34138032Speter e->e_to = a->q_paddr; 34238032Speter m = a->q_mailer; 34338032Speter errno = 0; 34438032Speter if (aliaslevel == 0) 34538032Speter a->q_flags |= QPRIMARY; 34638032Speter if (tTd(26, 1)) 34738032Speter { 34864562Sgshapiro dprintf("\nrecipient (%d): ", aliaslevel); 34938032Speter printaddr(a, FALSE); 35038032Speter } 35138032Speter 35238032Speter /* if this is primary, add it to the original recipient list */ 35338032Speter if (a->q_alias == NULL) 35438032Speter { 35538032Speter if (e->e_origrcpt == NULL) 35638032Speter e->e_origrcpt = a->q_paddr; 35738032Speter else if (e->e_origrcpt != a->q_paddr) 35838032Speter e->e_origrcpt = ""; 35938032Speter } 36038032Speter 36164562Sgshapiro#if _FFR_GEN_ORCPT 36264562Sgshapiro /* set ORCPT DSN arg if not already set */ 36364562Sgshapiro if (a->q_orcpt == NULL) 36464562Sgshapiro { 36564562Sgshapiro for (q = a; q->q_alias != NULL; q = q->q_alias) 36664562Sgshapiro continue; 36764562Sgshapiro 36864562Sgshapiro /* check for an existing ORCPT */ 36964562Sgshapiro if (q->q_orcpt != NULL) 37064562Sgshapiro a->q_orcpt = q->q_orcpt; 37164562Sgshapiro else 37264562Sgshapiro { 37364562Sgshapiro /* make our own */ 37464562Sgshapiro bool b = FALSE; 37564562Sgshapiro char *qp; 37664562Sgshapiro char obuf[MAXLINE]; 37764562Sgshapiro 37864562Sgshapiro if (e->e_from.q_mailer != NULL) 37964562Sgshapiro p = e->e_from.q_mailer->m_addrtype; 38064562Sgshapiro if (p == NULL) 38164562Sgshapiro p = "rfc822"; 38264562Sgshapiro (void) strlcpy(obuf, p, sizeof obuf); 38364562Sgshapiro (void) strlcat(obuf, ";", sizeof obuf); 38464562Sgshapiro 38564562Sgshapiro qp = q->q_paddr; 38664562Sgshapiro 38764562Sgshapiro /* FFR: Needs to strip comments from stdin addrs */ 38864562Sgshapiro 38964562Sgshapiro /* strip brackets from address */ 39064562Sgshapiro if (*qp == '<') 39164562Sgshapiro { 39264562Sgshapiro b = qp[strlen(qp) - 1] == '>'; 39364562Sgshapiro if (b) 39464562Sgshapiro qp[strlen(qp) - 1] = '\0'; 39564562Sgshapiro qp++; 39664562Sgshapiro } 39764562Sgshapiro 39864562Sgshapiro p = xtextify(denlstring(qp, TRUE, FALSE), NULL); 39964562Sgshapiro 40064562Sgshapiro if (strlcat(obuf, p, sizeof obuf) >= sizeof obuf) 40164562Sgshapiro { 40264562Sgshapiro /* if too big, don't use it */ 40364562Sgshapiro obuf[0] = '\0'; 40464562Sgshapiro } 40564562Sgshapiro 40664562Sgshapiro /* undo damage */ 40764562Sgshapiro if (b) 40864562Sgshapiro qp[strlen(qp)] = '>'; 40964562Sgshapiro 41064562Sgshapiro if (obuf[0] != '\0') 41164562Sgshapiro a->q_orcpt = newstr(obuf); 41264562Sgshapiro } 41364562Sgshapiro } 41464562Sgshapiro#endif /* _FFR_GEN_ORCPT */ 41564562Sgshapiro 41638032Speter /* break aliasing loops */ 41738032Speter if (aliaslevel > MaxAliasRecursion) 41838032Speter { 41964562Sgshapiro a->q_state = QS_BADADDR; 42038032Speter a->q_status = "5.4.6"; 42164562Sgshapiro usrerrenh(a->q_status, 42264562Sgshapiro "554 aliasing/forwarding loop broken (%d aliases deep; %d max)", 42364562Sgshapiro aliaslevel, MaxAliasRecursion); 42464562Sgshapiro return a; 42538032Speter } 42638032Speter 42738032Speter /* 42838032Speter ** Finish setting up address structure. 42938032Speter */ 43038032Speter 43138032Speter /* get unquoted user for file, program or user.name check */ 43238032Speter i = strlen(a->q_user); 43338032Speter if (i >= sizeof buf0) 43464562Sgshapiro { 43564562Sgshapiro buflen = i + 1; 43664562Sgshapiro buf = xalloc(buflen); 43764562Sgshapiro } 43838032Speter else 43964562Sgshapiro { 44038032Speter buf = buf0; 44164562Sgshapiro buflen = sizeof buf0; 44264562Sgshapiro } 44364562Sgshapiro (void) strlcpy(buf, a->q_user, buflen); 44438032Speter for (p = buf; *p != '\0' && !quoted; p++) 44538032Speter { 44638032Speter if (*p == '\\') 44738032Speter quoted = TRUE; 44838032Speter } 44938032Speter stripquotes(buf); 45038032Speter 45138032Speter /* check for direct mailing to restricted mailers */ 45238032Speter if (m == ProgMailer) 45338032Speter { 45438032Speter if (a->q_alias == NULL) 45538032Speter { 45664562Sgshapiro a->q_state = QS_BADADDR; 45738032Speter a->q_status = "5.7.1"; 45864562Sgshapiro usrerrenh(a->q_status, 45964562Sgshapiro "550 Cannot mail directly to programs"); 46038032Speter } 46138032Speter else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) 46238032Speter { 46364562Sgshapiro a->q_state = QS_BADADDR; 46438032Speter a->q_status = "5.7.1"; 46538032Speter if (a->q_alias->q_ruser == NULL) 46664562Sgshapiro usrerrenh(a->q_status, 46764562Sgshapiro "550 UID %d is an unknown user: cannot mail to programs", 46864562Sgshapiro a->q_alias->q_uid); 46938032Speter else 47064562Sgshapiro usrerrenh(a->q_status, 47164562Sgshapiro "550 User %s@%s doesn't have a valid shell for mailing to programs", 47264562Sgshapiro a->q_alias->q_ruser, MyHostName); 47338032Speter } 47438032Speter else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) 47538032Speter { 47664562Sgshapiro a->q_state = QS_BADADDR; 47738032Speter a->q_status = "5.7.1"; 47864562Sgshapiro a->q_rstatus = newstr("550 Unsafe for mailing to programs"); 47964562Sgshapiro usrerrenh(a->q_status, 48064562Sgshapiro "550 Address %s is unsafe for mailing to programs", 48164562Sgshapiro a->q_alias->q_paddr); 48238032Speter } 48338032Speter } 48438032Speter 48538032Speter /* 48638032Speter ** Look up this person in the recipient list. 48738032Speter ** If they are there already, return, otherwise continue. 48838032Speter ** If the list is empty, just add it. Notice the cute 48938032Speter ** hack to make from addresses suppress things correctly: 49064562Sgshapiro ** the QS_DUPLICATE state will be set in the send list. 49138032Speter ** [Please note: the emphasis is on "hack."] 49238032Speter */ 49338032Speter 49438032Speter for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) 49538032Speter { 49638032Speter if (sameaddr(q, a) && 49738032Speter (bitset(QRCPTOK, q->q_flags) || 49838032Speter !bitset(QPRIMARY, q->q_flags))) 49938032Speter { 50038032Speter if (tTd(26, 1)) 50138032Speter { 50264562Sgshapiro dprintf("%s in sendq: ", a->q_paddr); 50338032Speter printaddr(q, FALSE); 50438032Speter } 50538032Speter if (!bitset(QPRIMARY, q->q_flags)) 50638032Speter { 50764562Sgshapiro if (!QS_IS_DEAD(a->q_state)) 50838032Speter message("duplicate suppressed"); 50964562Sgshapiro else 51064562Sgshapiro q->q_state = QS_DUPLICATE; 51138032Speter q->q_flags |= a->q_flags; 51238032Speter } 51364562Sgshapiro else if (bitset(QSELFREF, q->q_flags) 51464562Sgshapiro#if _FFR_MILTER 51564562Sgshapiro || q->q_state == QS_REMOVED 51664562Sgshapiro#endif /* _FFR_MILTER */ 51764562Sgshapiro ) 51864562Sgshapiro { 51964562Sgshapiro#if _FFR_MILTER 52064562Sgshapiro /* 52164562Sgshapiro ** If an earlier milter removed the address, 52264562Sgshapiro ** a later one can still add it back. 52364562Sgshapiro */ 52464562Sgshapiro#endif /* _FFR_MILTER */ 52564562Sgshapiro q->q_state = a->q_state; 52664562Sgshapiro q->q_flags |= a->q_flags; 52764562Sgshapiro } 52838032Speter a = q; 52938032Speter goto done; 53038032Speter } 53138032Speter } 53238032Speter 53338032Speter /* add address on list */ 53442575Speter if (pq != NULL) 53542575Speter { 53642575Speter *pq = a; 53742575Speter a->q_next = NULL; 53842575Speter } 53938032Speter 54038032Speter /* 54138032Speter ** Alias the name and handle special mailer types. 54238032Speter */ 54338032Speter 54438032Speter trylocaluser: 54538032Speter if (tTd(29, 7)) 54642575Speter { 54764562Sgshapiro dprintf("at trylocaluser: "); 54842575Speter printaddr(a, FALSE); 54942575Speter } 55038032Speter 55164562Sgshapiro if (!QS_IS_OK(a->q_state)) 55238032Speter goto testselfdestruct; 55338032Speter 55438032Speter if (m == InclMailer) 55538032Speter { 55664562Sgshapiro a->q_state = QS_INCLUDED; 55738032Speter if (a->q_alias == NULL) 55838032Speter { 55964562Sgshapiro a->q_state = QS_BADADDR; 56038032Speter a->q_status = "5.7.1"; 56164562Sgshapiro usrerrenh(a->q_status, 56264562Sgshapiro "550 Cannot mail directly to :include:s"); 56338032Speter } 56438032Speter else 56538032Speter { 56638032Speter int ret; 56738032Speter 56838032Speter message("including file %s", a->q_user); 56938032Speter ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e); 57038032Speter if (transienterror(ret)) 57138032Speter { 57238032Speter if (LogLevel > 2) 57338032Speter sm_syslog(LOG_ERR, e->e_id, 57464562Sgshapiro "include %s: transient error: %s", 57564562Sgshapiro shortenstring(a->q_user, MAXSHORTSTR), 57664562Sgshapiro errstring(ret)); 57764562Sgshapiro a->q_state = QS_QUEUEUP; 57864562Sgshapiro usrerr("451 4.2.4 Cannot open %s: %s", 57938032Speter shortenstring(a->q_user, MAXSHORTSTR), 58038032Speter errstring(ret)); 58138032Speter } 58238032Speter else if (ret != 0) 58338032Speter { 58464562Sgshapiro a->q_state = QS_BADADDR; 58538032Speter a->q_status = "5.2.4"; 58664562Sgshapiro usrerrenh(a->q_status, 58764562Sgshapiro "550 Cannot open %s: %s", 58864562Sgshapiro shortenstring(a->q_user, MAXSHORTSTR), 58964562Sgshapiro errstring(ret)); 59038032Speter } 59138032Speter } 59238032Speter } 59338032Speter else if (m == FileMailer) 59438032Speter { 59538032Speter /* check if writable or creatable */ 59638032Speter if (a->q_alias == NULL) 59738032Speter { 59864562Sgshapiro a->q_state = QS_BADADDR; 59938032Speter a->q_status = "5.7.1"; 60064562Sgshapiro usrerrenh(a->q_status, 60164562Sgshapiro "550 Cannot mail directly to files"); 60238032Speter } 60338032Speter else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) 60438032Speter { 60564562Sgshapiro a->q_state = QS_BADADDR; 60638032Speter a->q_status = "5.7.1"; 60738032Speter if (a->q_alias->q_ruser == NULL) 60864562Sgshapiro usrerrenh(a->q_status, 60964562Sgshapiro "550 UID %d is an unknown user: cannot mail to files", 61064562Sgshapiro a->q_alias->q_uid); 61138032Speter else 61264562Sgshapiro usrerrenh(a->q_status, 61364562Sgshapiro "550 User %s@%s doesn't have a valid shell for mailing to files", 61464562Sgshapiro a->q_alias->q_ruser, MyHostName); 61538032Speter } 61638032Speter else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) 61738032Speter { 61864562Sgshapiro a->q_state = QS_BADADDR; 61938032Speter a->q_status = "5.7.1"; 62064562Sgshapiro a->q_rstatus = newstr("550 Unsafe for mailing to files"); 62164562Sgshapiro usrerrenh(a->q_status, 62264562Sgshapiro "550 Address %s is unsafe for mailing to files", 62364562Sgshapiro a->q_alias->q_paddr); 62438032Speter } 62538032Speter } 62638032Speter 62738032Speter /* try aliasing */ 62864562Sgshapiro if (!quoted && QS_IS_OK(a->q_state) && 62938032Speter bitnset(M_ALIASABLE, m->m_flags)) 63038032Speter alias(a, sendq, aliaslevel, e); 63138032Speter 63264562Sgshapiro#if USERDB 63338032Speter /* if not aliased, look it up in the user database */ 63464562Sgshapiro if (!bitset(QNOTREMOTE, a->q_flags) && 63564562Sgshapiro QS_IS_SENDABLE(a->q_state) && 63638032Speter bitnset(M_CHECKUDB, m->m_flags)) 63738032Speter { 63838032Speter if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL) 63938032Speter { 64064562Sgshapiro a->q_state = QS_QUEUEUP; 64138032Speter if (e->e_message == NULL) 64238032Speter e->e_message = newstr("Deferred: user database error"); 64338032Speter if (LogLevel > 8) 64438032Speter sm_syslog(LOG_INFO, e->e_id, 64564562Sgshapiro "deferred: udbexpand: %s", 64664562Sgshapiro errstring(errno)); 64738032Speter message("queued (user database error): %s", 64838032Speter errstring(errno)); 64938032Speter e->e_nrcpts++; 65038032Speter goto testselfdestruct; 65138032Speter } 65238032Speter } 65364562Sgshapiro#endif /* USERDB */ 65438032Speter 65538032Speter /* 65638032Speter ** If we have a level two config file, then pass the name through 65738032Speter ** Ruleset 5 before sending it off. Ruleset 5 has the right 65838032Speter ** to send rewrite it to another mailer. This gives us a hook 65938032Speter ** after local aliasing has been done. 66038032Speter */ 66138032Speter 66238032Speter if (tTd(29, 5)) 66338032Speter { 66464562Sgshapiro dprintf("recipient: testing local? cl=%d, rr5=%lx\n\t", 66538032Speter ConfigLevel, (u_long) RewriteRules[5]); 66638032Speter printaddr(a, FALSE); 66738032Speter } 66864562Sgshapiro if (ConfigLevel >= 2 && RewriteRules[5] != NULL && 66964562Sgshapiro bitnset(M_TRYRULESET5, m->m_flags) && 67064562Sgshapiro !bitset(QNOTREMOTE, a->q_flags) && 67164562Sgshapiro QS_IS_OK(a->q_state)) 67238032Speter { 67338032Speter maplocaluser(a, sendq, aliaslevel + 1, e); 67438032Speter } 67538032Speter 67638032Speter /* 67738032Speter ** If it didn't get rewritten to another mailer, go ahead 67838032Speter ** and deliver it. 67938032Speter */ 68038032Speter 68164562Sgshapiro if (QS_IS_OK(a->q_state) && 68238032Speter bitnset(M_HASPWENT, m->m_flags)) 68338032Speter { 68438032Speter auto bool fuzzy; 68538032Speter register struct passwd *pw; 68638032Speter 68738032Speter /* warning -- finduser may trash buf */ 68838032Speter pw = finduser(buf, &fuzzy); 68938032Speter if (pw == NULL || strlen(pw->pw_name) > MAXNAME) 69038032Speter { 69164562Sgshapiro { 69264562Sgshapiro a->q_state = QS_BADADDR; 69364562Sgshapiro a->q_status = "5.1.1"; 69464562Sgshapiro a->q_rstatus = newstr("550 5.1.1 User unknown"); 69564562Sgshapiro giveresponse(EX_NOUSER, a->q_status, m, NULL, 69664562Sgshapiro a->q_alias, (time_t) 0, e); 69764562Sgshapiro } 69838032Speter } 69938032Speter else 70038032Speter { 70138032Speter char nbuf[MAXNAME + 1]; 70238032Speter 70338032Speter if (fuzzy) 70438032Speter { 70538032Speter /* name was a fuzzy match */ 70638032Speter a->q_user = newstr(pw->pw_name); 70738032Speter if (findusercount++ > 3) 70838032Speter { 70964562Sgshapiro a->q_state = QS_BADADDR; 71038032Speter a->q_status = "5.4.6"; 71164562Sgshapiro usrerrenh(a->q_status, 71264562Sgshapiro "554 aliasing/forwarding loop for %s broken", 71364562Sgshapiro pw->pw_name); 71438032Speter goto done; 71538032Speter } 71638032Speter 71738032Speter /* see if it aliases */ 71864562Sgshapiro (void) strlcpy(buf, pw->pw_name, buflen); 71938032Speter goto trylocaluser; 72038032Speter } 72138032Speter if (strcmp(pw->pw_dir, "/") == 0) 72238032Speter a->q_home = ""; 72338032Speter else 72438032Speter a->q_home = newstr(pw->pw_dir); 72538032Speter a->q_uid = pw->pw_uid; 72638032Speter a->q_gid = pw->pw_gid; 72738032Speter a->q_ruser = newstr(pw->pw_name); 72838032Speter a->q_flags |= QGOODUID; 72938032Speter buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf); 73038032Speter if (nbuf[0] != '\0') 73138032Speter a->q_fullname = newstr(nbuf); 73238032Speter if (!usershellok(pw->pw_name, pw->pw_shell)) 73338032Speter { 73438032Speter a->q_flags |= QBOGUSSHELL; 73538032Speter } 73638032Speter if (bitset(EF_VRFYONLY, e->e_flags)) 73738032Speter { 73838032Speter /* don't do any more now */ 73964562Sgshapiro a->q_state = QS_VERIFIED; 74038032Speter } 74138032Speter else if (!quoted) 74238032Speter forward(a, sendq, aliaslevel, e); 74338032Speter } 74438032Speter } 74564562Sgshapiro if (!QS_IS_DEAD(a->q_state)) 74638032Speter e->e_nrcpts++; 74738032Speter 74838032Speter testselfdestruct: 74938032Speter a->q_flags |= QTHISPASS; 75038032Speter if (tTd(26, 8)) 75138032Speter { 75264562Sgshapiro dprintf("testselfdestruct: "); 75338032Speter printaddr(a, FALSE); 75438032Speter if (tTd(26, 10)) 75538032Speter { 75664562Sgshapiro dprintf("SENDQ:\n"); 75738032Speter printaddr(*sendq, TRUE); 75864562Sgshapiro dprintf("----\n"); 75938032Speter } 76038032Speter } 76138032Speter if (a->q_alias == NULL && a != &e->e_from && 76264562Sgshapiro QS_IS_DEAD(a->q_state)) 76338032Speter { 76438032Speter for (q = *sendq; q != NULL; q = q->q_next) 76538032Speter { 76664562Sgshapiro if (!QS_IS_DEAD(q->q_state)) 76738032Speter break; 76838032Speter } 76938032Speter if (q == NULL) 77038032Speter { 77164562Sgshapiro a->q_state = QS_BADADDR; 77238032Speter a->q_status = "5.4.6"; 77364562Sgshapiro usrerrenh(a->q_status, 77464562Sgshapiro "554 aliasing/forwarding loop broken"); 77538032Speter } 77638032Speter } 77738032Speter 77838032Speter done: 77938032Speter a->q_flags |= QTHISPASS; 78038032Speter if (buf != buf0) 78138032Speter free(buf); 78238032Speter 78338032Speter /* 78438032Speter ** If we are at the top level, check to see if this has 78538032Speter ** expanded to exactly one address. If so, it can inherit 78638032Speter ** the primaryness of the address. 78738032Speter ** 78838032Speter ** While we're at it, clear the QTHISPASS bits. 78938032Speter */ 79038032Speter 79138032Speter if (aliaslevel == 0) 79238032Speter { 79338032Speter int nrcpts = 0; 79438032Speter ADDRESS *only = NULL; 79538032Speter 79638032Speter for (q = *sendq; q != NULL; q = q->q_next) 79738032Speter { 79838032Speter if (bitset(QTHISPASS, q->q_flags) && 79964562Sgshapiro QS_IS_SENDABLE(q->q_state)) 80038032Speter { 80138032Speter nrcpts++; 80238032Speter only = q; 80338032Speter } 80438032Speter q->q_flags &= ~QTHISPASS; 80538032Speter } 80638032Speter if (nrcpts == 1) 80738032Speter { 80838032Speter /* check to see if this actually got a new owner */ 80938032Speter q = only; 81038032Speter while ((q = q->q_alias) != NULL) 81138032Speter { 81238032Speter if (q->q_owner != NULL) 81338032Speter break; 81438032Speter } 81538032Speter if (q == NULL) 81638032Speter only->q_flags |= QPRIMARY; 81738032Speter } 81838032Speter else if (!initialdontsend && nrcpts > 0) 81938032Speter { 82038032Speter /* arrange for return receipt */ 82138032Speter e->e_flags |= EF_SENDRECEIPT; 82238032Speter a->q_flags |= QEXPANDED; 82364562Sgshapiro if (e->e_xfp != NULL && 82464562Sgshapiro bitset(QPINGONSUCCESS, a->q_flags)) 82538032Speter fprintf(e->e_xfp, 82638032Speter "%s... expanded to multiple addresses\n", 82738032Speter a->q_paddr); 82838032Speter } 82938032Speter } 83038032Speter a->q_flags |= QRCPTOK; 83164562Sgshapiro return a; 83238032Speter} 83338032Speter/* 83438032Speter** FINDUSER -- find the password entry for a user. 83538032Speter** 83638032Speter** This looks a lot like getpwnam, except that it may want to 83738032Speter** do some fancier pattern matching in /etc/passwd. 83838032Speter** 83938032Speter** This routine contains most of the time of many sendmail runs. 84038032Speter** It deserves to be optimized. 84138032Speter** 84238032Speter** Parameters: 84338032Speter** name -- the name to match against. 84438032Speter** fuzzyp -- an outarg that is set to TRUE if this entry 84538032Speter** was found using the fuzzy matching algorithm; 84638032Speter** set to FALSE otherwise. 84738032Speter** 84838032Speter** Returns: 84938032Speter** A pointer to a pw struct. 85038032Speter** NULL if name is unknown or ambiguous. 85138032Speter** 85238032Speter** Side Effects: 85338032Speter** may modify name. 85438032Speter*/ 85538032Speter 85638032Speterstruct passwd * 85738032Speterfinduser(name, fuzzyp) 85838032Speter char *name; 85938032Speter bool *fuzzyp; 86038032Speter{ 86138032Speter register struct passwd *pw; 86238032Speter register char *p; 86338032Speter bool tryagain; 86438032Speter 86538032Speter if (tTd(29, 4)) 86664562Sgshapiro dprintf("finduser(%s): ", name); 86738032Speter 86838032Speter *fuzzyp = FALSE; 86938032Speter 87038032Speter#ifdef HESIOD 87138032Speter /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ 87238032Speter for (p = name; *p != '\0'; p++) 87338032Speter if (!isascii(*p) || !isdigit(*p)) 87438032Speter break; 87538032Speter if (*p == '\0') 87638032Speter { 87738032Speter if (tTd(29, 4)) 87864562Sgshapiro dprintf("failed (numeric input)\n"); 87938032Speter return NULL; 88038032Speter } 88164562Sgshapiro#endif /* HESIOD */ 88238032Speter 88338032Speter /* look up this login name using fast path */ 88438032Speter if ((pw = sm_getpwnam(name)) != NULL) 88538032Speter { 88638032Speter if (tTd(29, 4)) 88764562Sgshapiro dprintf("found (non-fuzzy)\n"); 88864562Sgshapiro return pw; 88938032Speter } 89038032Speter 89138032Speter /* try mapping it to lower case */ 89238032Speter tryagain = FALSE; 89338032Speter for (p = name; *p != '\0'; p++) 89438032Speter { 89538032Speter if (isascii(*p) && isupper(*p)) 89638032Speter { 89738032Speter *p = tolower(*p); 89838032Speter tryagain = TRUE; 89938032Speter } 90038032Speter } 90138032Speter if (tryagain && (pw = sm_getpwnam(name)) != NULL) 90238032Speter { 90338032Speter if (tTd(29, 4)) 90464562Sgshapiro dprintf("found (lower case)\n"); 90538032Speter *fuzzyp = TRUE; 90638032Speter return pw; 90738032Speter } 90838032Speter 90938032Speter#if MATCHGECOS 91038032Speter /* see if fuzzy matching allowed */ 91138032Speter if (!MatchGecos) 91238032Speter { 91338032Speter if (tTd(29, 4)) 91464562Sgshapiro dprintf("not found (fuzzy disabled)\n"); 91538032Speter return NULL; 91638032Speter } 91738032Speter 91838032Speter /* search for a matching full name instead */ 91938032Speter for (p = name; *p != '\0'; p++) 92038032Speter { 92138032Speter if (*p == (SpaceSub & 0177) || *p == '_') 92238032Speter *p = ' '; 92338032Speter } 92438032Speter (void) setpwent(); 92538032Speter while ((pw = getpwent()) != NULL) 92638032Speter { 92738032Speter char buf[MAXNAME + 1]; 92838032Speter 92938032Speter# if 0 93038032Speter if (strcasecmp(pw->pw_name, name) == 0) 93138032Speter { 93238032Speter if (tTd(29, 4)) 93364562Sgshapiro dprintf("found (case wrapped)\n"); 93438032Speter break; 93538032Speter } 93664562Sgshapiro# endif /* 0 */ 93738032Speter 93838032Speter buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf); 93964562Sgshapiro if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0) 94038032Speter { 94138032Speter if (tTd(29, 4)) 94264562Sgshapiro dprintf("fuzzy matches %s\n", pw->pw_name); 94338032Speter message("sending to login name %s", pw->pw_name); 94438032Speter break; 94538032Speter } 94638032Speter } 94738032Speter if (pw != NULL) 94838032Speter *fuzzyp = TRUE; 94938032Speter else if (tTd(29, 4)) 95064562Sgshapiro dprintf("no fuzzy match found\n"); 95138032Speter# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */ 95238032Speter endpwent(); 95364562Sgshapiro# endif /* DEC_OSF_BROKEN_GETPWENT */ 95438032Speter return pw; 95564562Sgshapiro#else /* MATCHGECOS */ 95638032Speter if (tTd(29, 4)) 95764562Sgshapiro dprintf("not found (fuzzy disabled)\n"); 95838032Speter return NULL; 95964562Sgshapiro#endif /* MATCHGECOS */ 96038032Speter} 96138032Speter/* 96238032Speter** WRITABLE -- predicate returning if the file is writable. 96338032Speter** 96438032Speter** This routine must duplicate the algorithm in sys/fio.c. 96538032Speter** Unfortunately, we cannot use the access call since we 96638032Speter** won't necessarily be the real uid when we try to 96738032Speter** actually open the file. 96838032Speter** 96938032Speter** Notice that ANY file with ANY execute bit is automatically 97038032Speter** not writable. This is also enforced by mailfile. 97138032Speter** 97238032Speter** Parameters: 97338032Speter** filename -- the file name to check. 97438032Speter** ctladdr -- the controlling address for this file. 97538032Speter** flags -- SFF_* flags to control the function. 97638032Speter** 97738032Speter** Returns: 97838032Speter** TRUE -- if we will be able to write this file. 97938032Speter** FALSE -- if we cannot write this file. 98038032Speter** 98138032Speter** Side Effects: 98238032Speter** none. 98338032Speter*/ 98438032Speter 98538032Speterbool 98638032Speterwritable(filename, ctladdr, flags) 98738032Speter char *filename; 98838032Speter ADDRESS *ctladdr; 98964562Sgshapiro long flags; 99038032Speter{ 99164562Sgshapiro uid_t euid = 0; 99264562Sgshapiro gid_t egid = 0; 99364562Sgshapiro char *user = NULL; 99438032Speter 99538032Speter if (tTd(44, 5)) 99664562Sgshapiro dprintf("writable(%s, 0x%lx)\n", filename, flags); 99738032Speter 99838032Speter /* 99938032Speter ** File does exist -- check that it is writable. 100038032Speter */ 100138032Speter 100238032Speter if (geteuid() != 0) 100338032Speter { 100438032Speter euid = geteuid(); 100538032Speter egid = getegid(); 100664562Sgshapiro user = NULL; 100738032Speter } 100838032Speter else if (ctladdr != NULL) 100938032Speter { 101038032Speter euid = ctladdr->q_uid; 101138032Speter egid = ctladdr->q_gid; 101264562Sgshapiro user = ctladdr->q_user; 101338032Speter } 101438032Speter else if (bitset(SFF_RUNASREALUID, flags)) 101538032Speter { 101638032Speter euid = RealUid; 101738032Speter egid = RealGid; 101864562Sgshapiro user = RealUserName; 101938032Speter } 102038032Speter else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags)) 102138032Speter { 102238032Speter euid = FileMailer->m_uid; 102338032Speter egid = FileMailer->m_gid; 102464562Sgshapiro user = NULL; 102538032Speter } 102638032Speter else 102738032Speter { 102838032Speter euid = egid = 0; 102964562Sgshapiro user = NULL; 103038032Speter } 103138032Speter if (!bitset(SFF_ROOTOK, flags)) 103238032Speter { 103338032Speter if (euid == 0) 103438032Speter { 103538032Speter euid = DefUid; 103664562Sgshapiro user = DefUser; 103738032Speter } 103838032Speter if (egid == 0) 103938032Speter egid = DefGid; 104038032Speter } 104138032Speter if (geteuid() == 0 && 104238032Speter (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags))) 104338032Speter flags |= SFF_SETUIDOK; 104438032Speter 104564562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 104638032Speter flags |= SFF_NOSLINK; 104764562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 104838032Speter flags |= SFF_NOHLINK; 104938032Speter 105064562Sgshapiro errno = safefile(filename, euid, egid, user, flags, S_IWRITE, NULL); 105138032Speter return errno == 0; 105238032Speter} 105338032Speter/* 105438032Speter** INCLUDE -- handle :include: specification. 105538032Speter** 105638032Speter** Parameters: 105738032Speter** fname -- filename to include. 105838032Speter** forwarding -- if TRUE, we are reading a .forward file. 105938032Speter** if FALSE, it's a :include: file. 106038032Speter** ctladdr -- address template to use to fill in these 106138032Speter** addresses -- effective user/group id are 106238032Speter** the important things. 106338032Speter** sendq -- a pointer to the head of the send queue 106438032Speter** to put these addresses in. 106538032Speter** aliaslevel -- the alias nesting depth. 106638032Speter** e -- the current envelope. 106738032Speter** 106838032Speter** Returns: 106938032Speter** open error status 107038032Speter** 107138032Speter** Side Effects: 107238032Speter** reads the :include: file and sends to everyone 107338032Speter** listed in that file. 107438032Speter** 107538032Speter** Security Note: 107638032Speter** If you have restricted chown (that is, you can't 107738032Speter** give a file away), it is reasonable to allow programs 107838032Speter** and files called from this :include: file to be to be 107938032Speter** run as the owner of the :include: file. This is bogus 108038032Speter** if there is any chance of someone giving away a file. 108138032Speter** We assume that pre-POSIX systems can give away files. 108238032Speter** 108338032Speter** There is an additional restriction that if you 108438032Speter** forward to a :include: file, it will not take on 108538032Speter** the ownership of the :include: file. This may not 108638032Speter** be necessary, but shouldn't hurt. 108738032Speter*/ 108838032Speter 108938032Speterstatic jmp_buf CtxIncludeTimeout; 109038032Speter 109138032Speterint 109238032Speterinclude(fname, forwarding, ctladdr, sendq, aliaslevel, e) 109338032Speter char *fname; 109438032Speter bool forwarding; 109538032Speter ADDRESS *ctladdr; 109638032Speter ADDRESS **sendq; 109738032Speter int aliaslevel; 109838032Speter ENVELOPE *e; 109938032Speter{ 110038032Speter FILE *volatile fp = NULL; 110138032Speter char *oldto = e->e_to; 110238032Speter char *oldfilename = FileName; 110338032Speter int oldlinenumber = LineNumber; 110438032Speter register EVENT *ev = NULL; 110538032Speter int nincludes; 110638032Speter int mode; 110764562Sgshapiro volatile bool maxreached = FALSE; 110838032Speter register ADDRESS *ca; 110964562Sgshapiro volatile uid_t saveduid; 111064562Sgshapiro volatile gid_t savedgid; 111164562Sgshapiro volatile uid_t uid; 111264562Sgshapiro volatile gid_t gid; 111364562Sgshapiro char *volatile user; 111438032Speter int rval = 0; 111564562Sgshapiro volatile long sfflags = SFF_REGONLY; 111638032Speter register char *p; 111738032Speter bool safechown = FALSE; 111838032Speter volatile bool safedir = FALSE; 111938032Speter struct stat st; 112038032Speter char buf[MAXLINE]; 112138032Speter 112238032Speter if (tTd(27, 2)) 112364562Sgshapiro dprintf("include(%s)\n", fname); 112438032Speter if (tTd(27, 4)) 112564562Sgshapiro dprintf(" ruid=%d euid=%d\n", 112664562Sgshapiro (int) getuid(), (int) geteuid()); 112738032Speter if (tTd(27, 14)) 112838032Speter { 112964562Sgshapiro dprintf("ctladdr "); 113038032Speter printaddr(ctladdr, FALSE); 113138032Speter } 113238032Speter 113338032Speter if (tTd(27, 9)) 113464562Sgshapiro dprintf("include: old uid = %d/%d\n", 113564562Sgshapiro (int) getuid(), (int) geteuid()); 113638032Speter 113738032Speter if (forwarding) 113842575Speter sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK; 113938032Speter 114064562Sgshapiro /* 114164562Sgshapiro ** If RunAsUser set, won't be able to run programs as user 114264562Sgshapiro ** so mark them as unsafe unless the administrator knows better. 114364562Sgshapiro */ 114464562Sgshapiro 114564562Sgshapiro if ((geteuid() != 0 || RunAsUid != 0) && 114664562Sgshapiro !bitnset(DBS_NONROOTSAFEADDR, DontBlameSendmail)) 114764562Sgshapiro { 114864562Sgshapiro if (tTd(27, 4)) 114964562Sgshapiro dprintf("include: not safe (euid=%d, RunAsUid=%d)\n", 115064562Sgshapiro (int) geteuid(), (int) RunAsUid); 115164562Sgshapiro ctladdr->q_flags |= QUNSAFEADDR; 115264562Sgshapiro } 115364562Sgshapiro 115438032Speter ca = getctladdr(ctladdr); 115564562Sgshapiro if (ca == NULL || 115664562Sgshapiro (ca->q_uid == DefUid && ca->q_gid == 0)) 115738032Speter { 115838032Speter uid = DefUid; 115938032Speter gid = DefGid; 116064562Sgshapiro user = DefUser; 116138032Speter } 116238032Speter else 116338032Speter { 116438032Speter uid = ca->q_uid; 116538032Speter gid = ca->q_gid; 116664562Sgshapiro user = ca->q_user; 116738032Speter } 116864562Sgshapiro#if MAILER_SETUID_METHOD != USE_SETUID 116938032Speter saveduid = geteuid(); 117038032Speter savedgid = getegid(); 117138032Speter if (saveduid == 0) 117238032Speter { 117338032Speter if (!DontInitGroups) 117438032Speter { 117564562Sgshapiro if (initgroups(user, gid) == -1) 117664562Sgshapiro { 117764562Sgshapiro rval = EAGAIN; 117838032Speter syserr("include: initgroups(%s, %d) failed", 117964562Sgshapiro user, gid); 118064562Sgshapiro goto resetuid; 118164562Sgshapiro } 118238032Speter } 118338032Speter else 118438032Speter { 118538032Speter GIDSET_T gidset[1]; 118638032Speter 118738032Speter gidset[0] = gid; 118838032Speter if (setgroups(1, gidset) == -1) 118964562Sgshapiro { 119064562Sgshapiro rval = EAGAIN; 119138032Speter syserr("include: setgroups() failed"); 119264562Sgshapiro goto resetuid; 119364562Sgshapiro } 119438032Speter } 119538032Speter 119638032Speter if (gid != 0 && setgid(gid) < -1) 119764562Sgshapiro { 119864562Sgshapiro rval = EAGAIN; 119938032Speter syserr("setgid(%d) failure", gid); 120064562Sgshapiro goto resetuid; 120164562Sgshapiro } 120238032Speter if (uid != 0) 120338032Speter { 120464562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETEUID 120538032Speter if (seteuid(uid) < 0) 120664562Sgshapiro { 120764562Sgshapiro rval = EAGAIN; 120838032Speter syserr("seteuid(%d) failure (real=%d, eff=%d)", 120938032Speter uid, getuid(), geteuid()); 121064562Sgshapiro goto resetuid; 121164562Sgshapiro } 121264562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 121364562Sgshapiro# if MAILER_SETUID_METHOD == USE_SETREUID 121438032Speter if (setreuid(0, uid) < 0) 121564562Sgshapiro { 121664562Sgshapiro rval = EAGAIN; 121738032Speter syserr("setreuid(0, %d) failure (real=%d, eff=%d)", 121838032Speter uid, getuid(), geteuid()); 121964562Sgshapiro goto resetuid; 122064562Sgshapiro } 122164562Sgshapiro# endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 122238032Speter } 122338032Speter } 122464562Sgshapiro#endif /* MAILER_SETUID_METHOD != USE_SETUID */ 122538032Speter 122638032Speter if (tTd(27, 9)) 122764562Sgshapiro dprintf("include: new uid = %d/%d\n", 122864562Sgshapiro (int) getuid(), (int) geteuid()); 122938032Speter 123038032Speter /* 123138032Speter ** If home directory is remote mounted but server is down, 123238032Speter ** this can hang or give errors; use a timeout to avoid this 123338032Speter */ 123438032Speter 123538032Speter if (setjmp(CtxIncludeTimeout) != 0) 123638032Speter { 123764562Sgshapiro ctladdr->q_state = QS_QUEUEUP; 123838032Speter errno = 0; 123938032Speter 124038032Speter /* return pseudo-error code */ 124138032Speter rval = E_SM_OPENTIMEOUT; 124238032Speter goto resetuid; 124338032Speter } 124438032Speter if (TimeOuts.to_fileopen > 0) 124538032Speter ev = setevent(TimeOuts.to_fileopen, includetimeout, 0); 124638032Speter else 124738032Speter ev = NULL; 124838032Speter 124964562Sgshapiro 125038032Speter /* check for writable parent directory */ 125138032Speter p = strrchr(fname, '/'); 125238032Speter if (p != NULL) 125338032Speter { 125438032Speter int ret; 125538032Speter 125638032Speter *p = '\0'; 125764562Sgshapiro ret = safedirpath(fname, uid, gid, user, 125864562Sgshapiro sfflags|SFF_SAFEDIRPATH, 0, 0); 125938032Speter if (ret == 0) 126038032Speter { 126138032Speter /* in safe directory: relax chown & link rules */ 126238032Speter safedir = TRUE; 126338032Speter sfflags |= SFF_NOPATHCHECK; 126438032Speter } 126538032Speter else 126638032Speter { 126764562Sgshapiro if (bitnset((forwarding ? 126864562Sgshapiro DBS_FORWARDFILEINUNSAFEDIRPATH : 126964562Sgshapiro DBS_INCLUDEFILEINUNSAFEDIRPATH), 127064562Sgshapiro DontBlameSendmail)) 127138032Speter sfflags |= SFF_NOPATHCHECK; 127264562Sgshapiro else if (bitnset((forwarding ? 127364562Sgshapiro DBS_FORWARDFILEINGROUPWRITABLEDIRPATH : 127464562Sgshapiro DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH), 127564562Sgshapiro DontBlameSendmail) && 127638032Speter ret == E_SM_GWDIR) 127738032Speter { 127864562Sgshapiro setbitn(DBS_GROUPWRITABLEDIRPATHSAFE, 127964562Sgshapiro DontBlameSendmail); 128064562Sgshapiro ret = safedirpath(fname, uid, gid, user, 128164562Sgshapiro sfflags|SFF_SAFEDIRPATH, 128264562Sgshapiro 0, 0); 128364562Sgshapiro clrbitn(DBS_GROUPWRITABLEDIRPATHSAFE, 128464562Sgshapiro DontBlameSendmail); 128538032Speter if (ret == 0) 128638032Speter sfflags |= SFF_NOPATHCHECK; 128738032Speter else 128838032Speter sfflags |= SFF_SAFEDIRPATH; 128938032Speter } 129038032Speter else 129138032Speter sfflags |= SFF_SAFEDIRPATH; 129238032Speter if (ret > E_PSEUDOBASE && 129364562Sgshapiro !bitnset((forwarding ? 129464562Sgshapiro DBS_FORWARDFILEINUNSAFEDIRPATHSAFE : 129564562Sgshapiro DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE), 129664562Sgshapiro DontBlameSendmail)) 129738032Speter { 129838032Speter if (LogLevel >= 12) 129938032Speter sm_syslog(LOG_INFO, e->e_id, 130038032Speter "%s: unsafe directory path, marked unsafe", 130138032Speter shortenstring(fname, MAXSHORTSTR)); 130238032Speter ctladdr->q_flags |= QUNSAFEADDR; 130338032Speter } 130438032Speter } 130538032Speter *p = '/'; 130638032Speter } 130738032Speter 130838032Speter /* allow links only in unwritable directories */ 130938032Speter if (!safedir && 131064562Sgshapiro !bitnset((forwarding ? 131164562Sgshapiro DBS_LINKEDFORWARDFILEINWRITABLEDIR : 131264562Sgshapiro DBS_LINKEDINCLUDEFILEINWRITABLEDIR), 131364562Sgshapiro DontBlameSendmail)) 131438032Speter sfflags |= SFF_NOLINK; 131538032Speter 131664562Sgshapiro rval = safefile(fname, uid, gid, user, sfflags, S_IREAD, &st); 131738032Speter if (rval != 0) 131838032Speter { 131938032Speter /* don't use this :include: file */ 132038032Speter if (tTd(27, 4)) 132164562Sgshapiro dprintf("include: not safe (uid=%d): %s\n", 132238032Speter (int) uid, errstring(rval)); 132338032Speter } 132438032Speter else if ((fp = fopen(fname, "r")) == NULL) 132538032Speter { 132638032Speter rval = errno; 132738032Speter if (tTd(27, 4)) 132864562Sgshapiro dprintf("include: open: %s\n", errstring(rval)); 132938032Speter } 133038032Speter else if (filechanged(fname, fileno(fp), &st)) 133138032Speter { 133238032Speter rval = E_SM_FILECHANGE; 133338032Speter if (tTd(27, 4)) 133464562Sgshapiro dprintf("include: file changed after open\n"); 133538032Speter } 133638032Speter if (ev != NULL) 133738032Speter clrevent(ev); 133838032Speter 133938032Speterresetuid: 134038032Speter 134138032Speter#if HASSETREUID || USESETEUID 134238032Speter if (saveduid == 0) 134338032Speter { 134438032Speter if (uid != 0) 134538032Speter { 134638032Speter# if USESETEUID 134738032Speter if (seteuid(0) < 0) 134864562Sgshapiro syserr("!seteuid(0) failure (real=%d, eff=%d)", 134938032Speter getuid(), geteuid()); 135064562Sgshapiro# else /* USESETEUID */ 135138032Speter if (setreuid(-1, 0) < 0) 135264562Sgshapiro syserr("!setreuid(-1, 0) failure (real=%d, eff=%d)", 135338032Speter getuid(), geteuid()); 135438032Speter if (setreuid(RealUid, 0) < 0) 135564562Sgshapiro syserr("!setreuid(%d, 0) failure (real=%d, eff=%d)", 135638032Speter RealUid, getuid(), geteuid()); 135764562Sgshapiro# endif /* USESETEUID */ 135838032Speter } 135964562Sgshapiro if (setgid(savedgid) < 0) 136064562Sgshapiro syserr("!setgid(%d) failure (real=%d eff=%d)", 136164562Sgshapiro savedgid, getgid(), getegid()); 136238032Speter } 136364562Sgshapiro#endif /* HASSETREUID || USESETEUID */ 136438032Speter 136538032Speter if (tTd(27, 9)) 136664562Sgshapiro dprintf("include: reset uid = %d/%d\n", 136764562Sgshapiro (int) getuid(), (int) geteuid()); 136838032Speter 136938032Speter if (rval == E_SM_OPENTIMEOUT) 137064562Sgshapiro usrerr("451 4.4.1 open timeout on %s", fname); 137138032Speter 137238032Speter if (fp == NULL) 137338032Speter return rval; 137438032Speter 137538032Speter if (fstat(fileno(fp), &st) < 0) 137638032Speter { 137738032Speter rval = errno; 137838032Speter syserr("Cannot fstat %s!", fname); 137964562Sgshapiro (void) fclose(fp); 138038032Speter return rval; 138138032Speter } 138238032Speter 138338032Speter /* if path was writable, check to avoid file giveaway tricks */ 138438032Speter safechown = chownsafe(fileno(fp), safedir); 138538032Speter if (tTd(27, 6)) 138664562Sgshapiro dprintf("include: parent of %s is %s, chown is %ssafe\n", 138738032Speter fname, 138838032Speter safedir ? "safe" : "dangerous", 138938032Speter safechown ? "" : "un"); 139038032Speter 139164562Sgshapiro /* if no controlling user or coming from an alias delivery */ 139264562Sgshapiro if (safechown && 139364562Sgshapiro (ca == NULL || 139464562Sgshapiro (ca->q_uid == DefUid && ca->q_gid == 0))) 139538032Speter { 139638032Speter ctladdr->q_uid = st.st_uid; 139738032Speter ctladdr->q_gid = st.st_gid; 139838032Speter ctladdr->q_flags |= QGOODUID; 139938032Speter } 140038032Speter if (ca != NULL && ca->q_uid == st.st_uid) 140138032Speter { 140238032Speter /* optimization -- avoid getpwuid if we already have info */ 140338032Speter ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL; 140438032Speter ctladdr->q_ruser = ca->q_ruser; 140538032Speter } 140638032Speter else if (!forwarding) 140738032Speter { 140838032Speter register struct passwd *pw; 140938032Speter 141038032Speter pw = sm_getpwuid(st.st_uid); 141138032Speter if (pw == NULL) 141264562Sgshapiro { 141364562Sgshapiro ctladdr->q_uid = st.st_uid; 141438032Speter ctladdr->q_flags |= QBOGUSSHELL; 141564562Sgshapiro } 141638032Speter else 141738032Speter { 141838032Speter char *sh; 141938032Speter 142038032Speter ctladdr->q_ruser = newstr(pw->pw_name); 142138032Speter if (safechown) 142238032Speter sh = pw->pw_shell; 142338032Speter else 142438032Speter sh = "/SENDMAIL/ANY/SHELL/"; 142538032Speter if (!usershellok(pw->pw_name, sh)) 142638032Speter { 142738032Speter if (LogLevel >= 12) 142838032Speter sm_syslog(LOG_INFO, e->e_id, 142964562Sgshapiro "%s: user %s has bad shell %s, marked %s", 143064562Sgshapiro shortenstring(fname, MAXSHORTSTR), 143164562Sgshapiro pw->pw_name, sh, 143264562Sgshapiro safechown ? "bogus" : "unsafe"); 143338032Speter if (safechown) 143438032Speter ctladdr->q_flags |= QBOGUSSHELL; 143538032Speter else 143638032Speter ctladdr->q_flags |= QUNSAFEADDR; 143738032Speter } 143838032Speter } 143938032Speter } 144038032Speter 144138032Speter if (bitset(EF_VRFYONLY, e->e_flags)) 144238032Speter { 144338032Speter /* don't do any more now */ 144464562Sgshapiro ctladdr->q_state = QS_VERIFIED; 144538032Speter e->e_nrcpts++; 144664562Sgshapiro (void) fclose(fp); 144738032Speter return rval; 144838032Speter } 144938032Speter 145038032Speter /* 145142575Speter ** Check to see if some bad guy can write this file 145238032Speter ** 145338032Speter ** Group write checking could be more clever, e.g., 145438032Speter ** guessing as to which groups are actually safe ("sys" 145538032Speter ** may be; "user" probably is not). 145638032Speter */ 145738032Speter 145838032Speter mode = S_IWOTH; 145964562Sgshapiro if (!bitnset((forwarding ? 146064562Sgshapiro DBS_GROUPWRITABLEFORWARDFILESAFE : 146164562Sgshapiro DBS_GROUPWRITABLEINCLUDEFILESAFE), 146264562Sgshapiro DontBlameSendmail)) 146338032Speter mode |= S_IWGRP; 146438032Speter 146538032Speter if (bitset(mode, st.st_mode)) 146638032Speter { 146738032Speter if (tTd(27, 6)) 146864562Sgshapiro dprintf("include: %s is %s writable, marked unsafe\n", 146938032Speter shortenstring(fname, MAXSHORTSTR), 147038032Speter bitset(S_IWOTH, st.st_mode) ? "world" : "group"); 147138032Speter if (LogLevel >= 12) 147238032Speter sm_syslog(LOG_INFO, e->e_id, 147364562Sgshapiro "%s: %s writable %s file, marked unsafe", 147464562Sgshapiro shortenstring(fname, MAXSHORTSTR), 147564562Sgshapiro bitset(S_IWOTH, st.st_mode) ? "world" : "group", 147664562Sgshapiro forwarding ? "forward" : ":include:"); 147738032Speter ctladdr->q_flags |= QUNSAFEADDR; 147838032Speter } 147938032Speter 148038032Speter /* read the file -- each line is a comma-separated list. */ 148138032Speter FileName = fname; 148238032Speter LineNumber = 0; 148338032Speter ctladdr->q_flags &= ~QSELFREF; 148438032Speter nincludes = 0; 148564562Sgshapiro while (fgets(buf, sizeof buf, fp) != NULL && !maxreached) 148638032Speter { 148764562Sgshapiro fixcrlf(buf, TRUE); 148838032Speter LineNumber++; 148938032Speter if (buf[0] == '#' || buf[0] == '\0') 149038032Speter continue; 149138032Speter 149238032Speter /* <sp>#@# introduces a comment anywhere */ 149338032Speter /* for Japanese character sets */ 149438032Speter for (p = buf; (p = strchr(++p, '#')) != NULL; ) 149538032Speter { 149638032Speter if (p[1] == '@' && p[2] == '#' && 149738032Speter isascii(p[-1]) && isspace(p[-1]) && 149838032Speter (p[3] == '\0' || (isascii(p[3]) && isspace(p[3])))) 149938032Speter { 150038032Speter p[-1] = '\0'; 150138032Speter break; 150238032Speter } 150338032Speter } 150438032Speter if (buf[0] == '\0') 150538032Speter continue; 150638032Speter 150738032Speter e->e_to = NULL; 150838032Speter message("%s to %s", 150938032Speter forwarding ? "forwarding" : "sending", buf); 151064562Sgshapiro if (forwarding && LogLevel > 10) 151138032Speter sm_syslog(LOG_INFO, e->e_id, 151264562Sgshapiro "forward %.200s => %s", 151364562Sgshapiro oldto, shortenstring(buf, MAXSHORTSTR)); 151438032Speter 151538032Speter nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e); 151664562Sgshapiro 151764562Sgshapiro if (forwarding && 151864562Sgshapiro MaxForwardEntries > 0 && 151964562Sgshapiro nincludes >= MaxForwardEntries) 152064562Sgshapiro { 152164562Sgshapiro /* just stop reading and processing further entries */ 152264562Sgshapiro /* additional: (?) 152364562Sgshapiro ctladdr->q_state = QS_DONTSEND; 152464562Sgshapiro **/ 152564562Sgshapiro syserr("Attempt to forward to more then %d addresses (in %s)!", 152664562Sgshapiro MaxForwardEntries,fname); 152764562Sgshapiro maxreached = TRUE; 152864562Sgshapiro } 152938032Speter } 153038032Speter 153138032Speter if (ferror(fp) && tTd(27, 3)) 153264562Sgshapiro dprintf("include: read error: %s\n", errstring(errno)); 153338032Speter if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) 153438032Speter { 153538032Speter if (tTd(27, 5)) 153638032Speter { 153764562Sgshapiro dprintf("include: QS_DONTSEND "); 153838032Speter printaddr(ctladdr, FALSE); 153938032Speter } 154064562Sgshapiro ctladdr->q_state = QS_DONTSEND; 154138032Speter } 154238032Speter 154364562Sgshapiro (void) fclose(fp); 154438032Speter FileName = oldfilename; 154538032Speter LineNumber = oldlinenumber; 154638032Speter e->e_to = oldto; 154738032Speter return rval; 154838032Speter} 154938032Speter 155038032Speterstatic void 155138032Speterincludetimeout() 155238032Speter{ 155338032Speter longjmp(CtxIncludeTimeout, 1); 155438032Speter} 155538032Speter/* 155638032Speter** SENDTOARGV -- send to an argument vector. 155738032Speter** 155838032Speter** Parameters: 155938032Speter** argv -- argument vector to send to. 156038032Speter** e -- the current envelope. 156138032Speter** 156238032Speter** Returns: 156338032Speter** none. 156438032Speter** 156538032Speter** Side Effects: 156638032Speter** puts all addresses on the argument vector onto the 156738032Speter** send queue. 156838032Speter*/ 156938032Speter 157038032Spetervoid 157138032Spetersendtoargv(argv, e) 157238032Speter register char **argv; 157338032Speter register ENVELOPE *e; 157438032Speter{ 157538032Speter register char *p; 157638032Speter 157738032Speter while ((p = *argv++) != NULL) 157838032Speter { 157938032Speter (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e); 158038032Speter } 158138032Speter} 158238032Speter/* 158338032Speter** GETCTLADDR -- get controlling address from an address header. 158438032Speter** 158538032Speter** If none, get one corresponding to the effective userid. 158638032Speter** 158738032Speter** Parameters: 158838032Speter** a -- the address to find the controller of. 158938032Speter** 159038032Speter** Returns: 159138032Speter** the controlling address. 159238032Speter** 159338032Speter** Side Effects: 159438032Speter** none. 159538032Speter*/ 159638032Speter 159738032SpeterADDRESS * 159838032Spetergetctladdr(a) 159938032Speter register ADDRESS *a; 160038032Speter{ 160138032Speter while (a != NULL && !bitset(QGOODUID, a->q_flags)) 160238032Speter a = a->q_alias; 160364562Sgshapiro return a; 160438032Speter} 160538032Speter/* 160638032Speter** SELF_REFERENCE -- check to see if an address references itself 160738032Speter** 160838032Speter** The check is done through a chain of aliases. If it is part of 160938032Speter** a loop, break the loop at the "best" address, that is, the one 161038032Speter** that exists as a real user. 161138032Speter** 161238032Speter** This is to handle the case of: 161338032Speter** awc: Andrew.Chang 161438032Speter** Andrew.Chang: awc@mail.server 161538032Speter** which is a problem only on mail.server. 161638032Speter** 161738032Speter** Parameters: 161838032Speter** a -- the address to check. 161938032Speter** 162038032Speter** Returns: 162138032Speter** The address that should be retained. 162238032Speter*/ 162338032Speter 162464562Sgshapirostatic ADDRESS * 162564562Sgshapiroself_reference(a) 162638032Speter ADDRESS *a; 162738032Speter{ 162838032Speter ADDRESS *b; /* top entry in self ref loop */ 162938032Speter ADDRESS *c; /* entry that point to a real mail box */ 163038032Speter 163138032Speter if (tTd(27, 1)) 163264562Sgshapiro dprintf("self_reference(%s)\n", a->q_paddr); 163338032Speter 163438032Speter for (b = a->q_alias; b != NULL; b = b->q_alias) 163538032Speter { 163638032Speter if (sameaddr(a, b)) 163738032Speter break; 163838032Speter } 163938032Speter 164038032Speter if (b == NULL) 164138032Speter { 164238032Speter if (tTd(27, 1)) 164364562Sgshapiro dprintf("\t... no self ref\n"); 164438032Speter return NULL; 164538032Speter } 164638032Speter 164738032Speter /* 164838032Speter ** Pick the first address that resolved to a real mail box 164938032Speter ** i.e has a pw entry. The returned value will be marked 165038032Speter ** QSELFREF in recipient(), which in turn will disable alias() 165164562Sgshapiro ** from marking it as QS_IS_DEAD(), which mean it will be used 165238032Speter ** as a deliverable address. 165338032Speter ** 165438032Speter ** The 2 key thing to note here are: 165538032Speter ** 1) we are in a recursive call sequence: 165638032Speter ** alias->sentolist->recipient->alias 165738032Speter ** 2) normally, when we return back to alias(), the address 165864562Sgshapiro ** will be marked QS_EXPANDED, since alias() assumes the 165938032Speter ** expanded form will be used instead of the current address. 166038032Speter ** This behaviour is turned off if the address is marked 166138032Speter ** QSELFREF We set QSELFREF when we return to recipient(). 166238032Speter */ 166338032Speter 166438032Speter c = a; 166538032Speter while (c != NULL) 166638032Speter { 166743730Speter if (tTd(27, 10)) 166864562Sgshapiro dprintf(" %s", c->q_user); 166938032Speter if (bitnset(M_HASPWENT, c->q_mailer->m_flags)) 167038032Speter { 167138032Speter if (tTd(27, 2)) 167264562Sgshapiro dprintf("\t... getpwnam(%s)... ", c->q_user); 167338032Speter if (sm_getpwnam(c->q_user) != NULL) 167438032Speter { 167538032Speter if (tTd(27, 2)) 167664562Sgshapiro dprintf("found\n"); 167738032Speter 167838032Speter /* ought to cache results here */ 167938032Speter if (sameaddr(b, c)) 168038032Speter return b; 168138032Speter else 168238032Speter return c; 168338032Speter } 168438032Speter if (tTd(27, 2)) 168564562Sgshapiro dprintf("failed\n"); 168638032Speter } 168743730Speter else 168843730Speter { 168943730Speter /* if local delivery, compare usernames */ 169043730Speter if (bitnset(M_LOCALMAILER, c->q_mailer->m_flags) && 169143730Speter b->q_mailer == c->q_mailer) 169243730Speter { 169343730Speter if (tTd(27, 2)) 169464562Sgshapiro dprintf("\t... local match (%s)\n", 169564562Sgshapiro c->q_user); 169643730Speter if (sameaddr(b, c)) 169743730Speter return b; 169843730Speter else 169943730Speter return c; 170043730Speter } 170143730Speter } 170243730Speter if (tTd(27, 10)) 170364562Sgshapiro dprintf("\n"); 170438032Speter c = c->q_alias; 170538032Speter } 170638032Speter 170738032Speter if (tTd(27, 1)) 170864562Sgshapiro dprintf("\t... cannot break loop for \"%s\"\n", a->q_paddr); 170938032Speter 171038032Speter return NULL; 171138032Speter} 1712