X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/1d303d9843e909158ae134f1eaac773cbb16f4dc..ed554bc5e4201344d7eaad78263566e79428759c:/usr/src/usr.sbin/sendmail/src/recipient.c diff --git a/usr/src/usr.sbin/sendmail/src/recipient.c b/usr/src/usr.sbin/sendmail/src/recipient.c index 9c5f2a1dcb..c6c15c4b6c 100644 --- a/usr/src/usr.sbin/sendmail/src/recipient.c +++ b/usr/src/usr.sbin/sendmail/src/recipient.c @@ -1,17 +1,42 @@ /* * Copyright (c) 1983 Eric P. Allman - * Copyright (c) 1988 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. * - * %sccs.include.redist.c% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)recipient.c 6.32.1.1 (Berkeley) %G%"; +static char sccsid[] = "@(#)recipient.c 8.44 (Berkeley) 2/28/94"; #endif /* not lint */ # include "sendmail.h" -# include # include /* @@ -20,12 +45,6 @@ static char sccsid[] = "@(#)recipient.c 6.32.1.1 (Berkeley) %G%"; ** The parameter is a comma-separated list of people to send to. ** This routine arranges to send to all of them. ** -** The `ctladdr' is the address that expanded to be this one, -** e.g., in an alias expansion. This is used for a number of -** purposed, most notably inheritance of uid/gid for protection -** purposes. It is also used to detect self-reference in group -** expansions and the like. -** ** Parameters: ** list -- the send list. ** ctladdr -- the address template for the person to @@ -35,10 +54,9 @@ static char sccsid[] = "@(#)recipient.c 6.32.1.1 (Berkeley) %G%"; ** sendq -- a pointer to the head of a queue to put ** these people into. ** e -- the envelope in which to add these recipients. -** qflags -- special flags to set in the q_flags field. ** ** Returns: -** pointer to chain of addresses. +** The number of addresses actually on the list. ** ** Side Effects: ** none. @@ -46,21 +64,24 @@ static char sccsid[] = "@(#)recipient.c 6.32.1.1 (Berkeley) %G%"; # define MAXRCRSN 10 -ADDRESS * -sendto(list, copyf, ctladdr, qflags) +sendtolist(list, ctladdr, sendq, e) char *list; ADDRESS *ctladdr; ADDRESS **sendq; register ENVELOPE *e; - u_short qflags; { register char *p; register ADDRESS *al; /* list of addresses to send to */ bool firstone; /* set on first address sent */ char delimiter; /* the address delimiter */ int naddrs; - ADDRESS *sibl; /* sibling pointer in tree */ - ADDRESS *prev; /* previous sibling */ + char *oldto = e->e_to; + + if (list == NULL) + { + syserr("sendtolist: null list"); + return 0; + } if (tTd(25, 1)) { @@ -89,15 +110,12 @@ sendto(list, copyf, ctladdr, qflags) /* parse the address */ while ((isascii(*p) && isspace(*p)) || *p == ',') p++; - a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter, &delimptr, e); + a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); p = delimptr; if (a == NULL) continue; a->q_next = al; a->q_alias = ctladdr; - if (ctladdr != NULL) - a->q_flags |= ctladdr->q_flags & ~QPRIMARY; - a->q_flags |= qflags; /* see if this should be marked as a primary address */ if (ctladdr == NULL || @@ -111,73 +129,21 @@ sendto(list, copyf, ctladdr, qflags) } /* arrange to send to everyone on the local send list */ - prev = sibl = NULL; - if (ctladdr != NULL) - prev = ctladdr->q_child; while (al != NULL) { register ADDRESS *a = al; - extern ADDRESS *recipient(); - extern ADDRESS *recipient(); al = a->q_next; - sibl = recipient(a); - if (sibl != NULL) - { - extern ADDRESS *addrref(); + a = recipient(a, sendq, e); - /* inherit full name */ - if (sibl->q_fullname == NULL && ctladdr != NULL) - sibl->q_fullname = ctladdr->q_fullname; - - /* link tree together (but only if the node is new) */ - if (sibl == a) - { - sibl->q_sibling = prev; - prev = sibl; - } - } + /* arrange to inherit full name */ + if (a->q_fullname == NULL && ctladdr != NULL) + a->q_fullname = ctladdr->q_fullname; + naddrs++; } - e->e_to = NULL; + e->e_to = oldto; return (naddrs); - if (ctladdr != NULL) - ctladdr->q_child = prev; - return (prev); -} - /* -** ADDRREF -- return pointer to address that references another address. -** -** Parameters: -** a -- address to check. -** r -- reference to find. -** -** Returns: -** address of node in tree rooted at 'a' that references -** 'r'. -** NULL if no such node exists. -** -** Side Effects: -** none. -*/ - -ADDRESS * -addrref(a, r) - register ADDRESS *a; - register ADDRESS *r; -{ - register ADDRESS *q; - - while (a != NULL) - { - if (a->q_child == r || a->q_sibling == r) - return (a); - q = addrref(a->q_child, r); - if (q != NULL) - return (q); - a = a->q_sibling; - } - return (NULL); } /* ** RECIPIENT -- Designate a message recipient @@ -192,15 +158,13 @@ addrref(a, r) ** e -- the current envelope. ** ** Returns: -** pointer to address actually inserted in send list. +** The actual address in the queue. This will be "a" if +** the address is not a duplicate, else the original address. ** ** Side Effects: ** none. */ -extern ADDRESS *getctladdr(); - -ADDRESS * ADDRESS * recipient(a, sendq, e) register ADDRESS *a; @@ -225,11 +189,20 @@ recipient(a, sendq, e) printaddr(a, FALSE); } + /* if this is primary, add it to the original recipient list */ + if (a->q_alias == NULL) + { + if (e->e_origrcpt == NULL) + e->e_origrcpt = a->q_paddr; + else if (e->e_origrcpt != a->q_paddr) + e->e_origrcpt = ""; + } + /* break aliasing loops */ if (AliasLevel > MAXRCRSN) { usrerr("554 aliasing/forwarding loop broken"); - return (NULL); + return (a); } /* @@ -249,11 +222,25 @@ recipient(a, sendq, e) stripquotes(buf); /* check for direct mailing to restricted mailers */ - if (a->q_alias == NULL && m == ProgMailer && - !bitset(EF_QUEUERUN, e->e_flags)) + if (m == ProgMailer) { - a->q_flags |= QBADADDR; - usrerr("550 Cannot mail directly to programs", m->m_name); + if (a->q_alias == NULL) + { + a->q_flags |= QBADADDR; + usrerr("550 Cannot mail directly to programs"); + } + else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", + a->q_alias->q_ruser, MyHostName); + } + else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + usrerr("550 Address %s is unsafe for mailing to programs", + a->q_alias->q_paddr); + } } /* @@ -269,10 +256,6 @@ recipient(a, sendq, e) { if (sameaddr(q, a)) { - /* if this is a reinsertion, just go ahead */ - if (bitset(QVERIFIED, q->q_flags)) - break; - if (tTd(26, 1)) { printf("%s in sendq: ", a->q_paddr); @@ -284,20 +267,16 @@ recipient(a, sendq, e) message("duplicate suppressed"); q->q_flags |= a->q_flags; } - if (!bitset(QPSEUDO, a->q_flags)) - q->q_flags &= ~QPSEUDO; - return (q); + else if (bitset(QSELFREF, q->q_flags)) + q->q_flags |= a->q_flags & ~QDONTSEND; + a = q; + goto testselfdestruct; } } /* add address on list */ - if (*pq != a) - { - *pq = a; - a->q_next = NULL; - } - - a->q_flags &= ~QVERIFIED; + *pq = a; + a->q_next = NULL; /* ** Alias the name and handle special mailer types. @@ -308,43 +287,69 @@ recipient(a, sendq, e) printf("at trylocaluser %s\n", a->q_user); if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) - return (a); + goto testselfdestruct; if (m == InclMailer) { a->q_flags |= QDONTSEND; - if (a->q_alias == NULL && !bitset(EF_QUEUERUN, e->e_flags)) + if (a->q_alias == NULL) { a->q_flags |= QBADADDR; usrerr("550 Cannot mail directly to :include:s"); } else { - int err; + int ret; message("including file %s", a->q_user); - err = include(a->q_user, FALSE, a, sendq, e); - if (transienterror(err)) - a->q_flags |= QQUEUEUP|QDONTSEND; + ret = include(a->q_user, FALSE, a, sendq, e); + if (transienterror(ret)) + { +#ifdef LOG + if (LogLevel > 2) + syslog(LOG_ERR, "%s: include %s: transient error: %s", + e->e_id == NULL ? "NOQUEUE" : e->e_id, + a->q_user, errstring(ret)); +#endif + a->q_flags |= QQUEUEUP; + a->q_flags &= ~QDONTSEND; + usrerr("451 Cannot open %s: %s", + a->q_user, errstring(ret)); + } + else if (ret != 0) + { + a->q_flags |= QBADADDR; + usrerr("550 Cannot open %s: %s", + a->q_user, errstring(ret)); + } } } else if (m == FileMailer) { - struct stat stb; extern bool writable(); - p = strrchr(buf, '/'); /* check if writable or creatable */ - if (a->q_alias == NULL && !bitset(EF_QUEUERUN, e->e_flags)) + if (a->q_alias == NULL) { a->q_flags |= QBADADDR; usrerr("550 Cannot mail directly to files"); } - else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : - (*p = '\0', safefile(buf, getruid(), S_IWRITE|S_IEXEC) != 0)) + else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) + { + a->q_flags |= QBADADDR; + usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", + a->q_alias->q_ruser, MyHostName); + } + else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) { a->q_flags |= QBADADDR; - giveresponse(EX_CANTCREAT, m, NULL, e); + usrerr("550 Address %s is unsafe for mailing to files", + a->q_alias->q_paddr); + } + else if (!writable(buf, getctladdr(a), SFF_ANYFILE)) + { + a->q_flags |= QBADADDR; + giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e); } } @@ -352,7 +357,7 @@ recipient(a, sendq, e) { if (!bitset(QDONTSEND, a->q_flags)) e->e_nrcpts++; - return (a); + goto testselfdestruct; } /* try aliasing */ @@ -360,30 +365,32 @@ recipient(a, sendq, e) # ifdef USERDB /* if not aliased, look it up in the user database */ - if (!bitset(QDONTSEND|QNOTREMOTE, a->q_flags)) + if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags)) { extern int udbexpand(); if (udbexpand(a, sendq, e) == EX_TEMPFAIL) { - a->q_flags |= QQUEUEUP|QDONTSEND; + a->q_flags |= QQUEUEUP; if (e->e_message == NULL) e->e_message = newstr("Deferred: user database error"); # ifdef LOG if (LogLevel > 8) - syslog(LOG_INFO, "%s: deferred: udbexpand", - e->e_id); + syslog(LOG_INFO, "%s: deferred: udbexpand: %s", + e->e_id == NULL ? "NOQUEUE" : e->e_id, + errstring(errno)); # endif - message("queued (user database error)"); + message("queued (user database error): %s", + errstring(errno)); e->e_nrcpts++; - return (a); + goto testselfdestruct; } } # endif /* if it was an alias or a UDB expansion, just return now */ if (bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags)) - return (a); + goto testselfdestruct; /* ** If we have a level two config file, then pass the name through @@ -420,7 +427,7 @@ recipient(a, sendq, e) if (pw == NULL) { a->q_flags |= QBADADDR; - giveresponse(EX_NOUSER, m, NULL, e); + giveresponse(EX_NOUSER, m, NULL, a->q_alias, e); } else { @@ -442,21 +449,47 @@ recipient(a, sendq, e) (void) strcpy(buf, pw->pw_name); goto trylocaluser; } - a->q_home = newstr(pw->pw_dir); + if (strcmp(pw->pw_dir, "/") == 0) + a->q_home = ""; + else + a->q_home = newstr(pw->pw_dir); a->q_uid = pw->pw_uid; a->q_gid = pw->pw_gid; + a->q_ruser = newstr(pw->pw_name); a->q_flags |= QGOODUID; buildfname(pw->pw_gecos, pw->pw_name, nbuf); if (nbuf[0] != '\0') a->q_fullname = newstr(nbuf); + if (pw->pw_shell != NULL && pw->pw_shell[0] != '\0' && + !usershellok(pw->pw_shell)) + { + a->q_flags |= QBOGUSSHELL; + } if (!quoted) forward(a, sendq, e); } } if (!bitset(QDONTSEND, a->q_flags)) e->e_nrcpts++; - return (a); + testselfdestruct: + if (tTd(26, 8)) + { + printf("testselfdestruct: "); + printaddr(a, TRUE); + } + if (a->q_alias == NULL && a != &e->e_from && + bitset(QDONTSEND, a->q_flags)) + { + q = *sendq; + while (q != NULL && bitset(QDONTSEND, q->q_flags)) + q = q->q_next; + if (q == NULL) + { + a->q_flags |= QBADADDR; + usrerr("554 aliasing/forwarding loop broken"); + } + } return (a); } /* @@ -497,6 +530,17 @@ finduser(name, fuzzyp) *fuzzyp = FALSE; + /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ + for (p = name; *p != '\0'; p++) + if (!isascii(*p) || !isdigit(*p)) + break; + if (*p == '\0') + { + if (tTd(29, 4)) + printf("failed (numeric input)\n"); + return NULL; + } + /* look up this login name using fast path */ if ((pw = getpwnam(name)) != NULL) { @@ -525,19 +569,22 @@ finduser(name, fuzzyp) { char buf[MAXNAME]; - fullname(pw, buf); + buildfname(pw->pw_gecos, pw->pw_name, buf); if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) { if (tTd(29, 4)) printf("fuzzy matches %s\n", pw->pw_name); - message(Arpa_Info, "sending to %s <%s>", - buf, pw->pw_name); + message("sending to login name %s", pw->pw_name); + *fuzzyp = TRUE; return (pw); } } -#endif if (tTd(29, 4)) printf("no fuzzy match found\n"); +#else + if (tTd(29, 4)) + printf("not found (fuzzy disabled)\n"); +#endif return (NULL); } /* @@ -552,7 +599,9 @@ finduser(name, fuzzyp) ** not writable. This is also enforced by mailfile. ** ** Parameters: -** s -- pointer to a stat struct for the file. +** filename -- the file name to check. +** ctladdr -- the controlling address for this file. +** flags -- SFF_* flags to control the function. ** ** Returns: ** TRUE -- if we will be able to write this file. @@ -563,35 +612,98 @@ finduser(name, fuzzyp) */ bool -writable(s) - register struct stat *s; +writable(filename, ctladdr, flags) + char *filename; + ADDRESS *ctladdr; + int flags; { uid_t euid; gid_t egid; int bits; + register char *p; + char *uname; + struct stat stb; + extern char RealUserName[]; - if (bitset(0111, s->st_mode)) - return (FALSE); - euid = getruid(); - egid = getrgid(); - if (geteuid() == 0) + if (tTd(29, 5)) + printf("writable(%s, %x)\n", filename, flags); + +#ifdef HASLSTAT + if ((bitset(SFF_NOSLINK, flags) ? lstat(filename, &stb) + : stat(filename, &stb)) < 0) +#else + if (stat(filename, &stb) < 0) +#endif + { + /* file does not exist -- see if directory is safe */ + p = strrchr(filename, '/'); + if (p == NULL) + { + errno = ENOTDIR; + return FALSE; + } + *p = '\0'; + errno = safefile(filename, RealUid, RealGid, RealUserName, + SFF_MUSTOWN, S_IWRITE|S_IEXEC); + *p = '/'; + return errno == 0; + } + +#ifdef SUID_ROOT_FILES_OK + /* really ought to be passed down -- and not a good idea */ + flags |= SFF_ROOTOK; +#endif + + /* + ** File does exist -- check that it is writable. + */ + + if (bitset(0111, stb.st_mode)) { - if (bitset(S_ISUID, s->st_mode)) - euid = s->st_uid; - if (bitset(S_ISGID, s->st_mode)) - egid = s->st_gid; + if (tTd(29, 5)) + printf("failed (mode %o: x bits)\n", stb.st_mode); + errno = EPERM; + return (FALSE); } + if (ctladdr != NULL && geteuid() == 0) + { + euid = ctladdr->q_uid; + egid = ctladdr->q_gid; + uname = ctladdr->q_user; + } + else + { + euid = RealUid; + egid = RealGid; + uname = RealUserName; + } if (euid == 0) - return (TRUE); - bits = S_IWRITE; - if (euid != s->st_uid) { - bits >>= 3; - if (egid != s->st_gid) - bits >>= 3; + euid = DefUid; + uname = DefUser; + } + if (egid == 0) + egid = DefGid; + if (geteuid() == 0) + { + if (bitset(S_ISUID, stb.st_mode) && + (stb.st_uid != 0 || bitset(SFF_ROOTOK, flags))) + { + euid = stb.st_uid; + uname = NULL; + } + if (bitset(S_ISGID, stb.st_mode) && + (stb.st_gid != 0 || bitset(SFF_ROOTOK, flags))) + egid = stb.st_gid; } - return ((s->st_mode & bits) != 0); + + if (tTd(29, 5)) + printf("\teu/gid=%d/%d, st_u/gid=%d/%d\n", + euid, egid, stb.st_uid, stb.st_gid); + + errno = safefile(filename, euid, egid, uname, flags, S_IWRITE); + return errno == 0; } /* ** INCLUDE -- handle :include: specification. @@ -612,9 +724,27 @@ writable(s) ** Side Effects: ** reads the :include: file and sends to everyone ** listed in that file. +** +** Security Note: +** If you have restricted chown (that is, you can't +** give a file away), it is reasonable to allow programs +** and files called from this :include: file to be to be +** run as the owner of the :include: file. This is bogus +** if there is any chance of someone giving away a file. +** We assume that pre-POSIX systems can give away files. +** +** There is an additional restriction that if you +** forward to a :include: file, it will not take on +** the ownership of the :include: file. This may not +** be necessary, but shouldn't hurt. */ static jmp_buf CtxIncludeTimeout; +static int includetimeout(); + +#ifndef S_IWOTH +# define S_IWOTH (S_IWRITE >> 6) +#endif int include(fname, forwarding, ctladdr, sendq, e) @@ -624,18 +754,79 @@ include(fname, forwarding, ctladdr, sendq, e) ADDRESS **sendq; ENVELOPE *e; { - register FILE *fp; + register FILE *fp = NULL; char *oldto = e->e_to; char *oldfilename = FileName; int oldlinenumber = LineNumber; register EVENT *ev = NULL; int nincludes; - int ret; + register ADDRESS *ca; + uid_t saveduid, uid; + gid_t savedgid, gid; + char *uname; + int rval = 0; + int sfflags = forwarding ? SFF_MUSTOWN : SFF_ANYFILE; + struct stat st; char buf[MAXLINE]; - static int includetimeout(); +#ifdef _POSIX_CHOWN_RESTRICTED +# if _POSIX_CHOWN_RESTRICTED == -1 +# define safechown FALSE +# else +# define safechown TRUE +# endif +#else +# ifdef _PC_CHOWN_RESTRICTED + bool safechown; +# else +# ifdef BSD +# define safechown TRUE +# else +# define safechown FALSE +# endif +# endif +#endif + extern bool chownsafe(); if (tTd(27, 2)) printf("include(%s)\n", fname); + if (tTd(27, 4)) + printf(" ruid=%d euid=%d\n", getuid(), geteuid()); + if (tTd(27, 14)) + { + printf("ctladdr "); + printaddr(ctladdr, FALSE); + } + + if (tTd(27, 9)) + printf("include: old uid = %d/%d\n", getuid(), geteuid()); + + ca = getctladdr(ctladdr); + if (ca == NULL) + { + uid = DefUid; + gid = DefGid; + uname = DefUser; + saveduid = -1; + } + else + { + uid = ca->q_uid; + gid = ca->q_gid; + uname = ca->q_user; +#ifdef HASSETREUID + saveduid = geteuid(); + savedgid = getegid(); + if (saveduid == 0) + { + initgroups(uname, gid); + if (uid != 0) + (void) setreuid(0, uid); + } +#endif + } + + if (tTd(27, 9)) + printf("include: new uid = %d/%d\n", getuid(), geteuid()); /* ** If home directory is remote mounted but server is down, @@ -644,70 +835,126 @@ include(fname, forwarding, ctladdr, sendq, e) if (setjmp(CtxIncludeTimeout) != 0) { - ctladdr->q_flags |= QQUEUEUP|QDONTSEND; + ctladdr->q_flags |= QQUEUEUP; errno = 0; - usrerr("451 open timeout on %s", fname); - return ETIMEDOUT; + + /* return pseudo-error code */ + rval = EOPENTIMEOUT; + goto resetuid; } ev = setevent((time_t) 60, includetimeout, 0); - /* if forwarding, the input file must be marked safe */ - if (forwarding && (ret = safefile(fname, ctladdr->q_uid, S_IREAD)) != 0) + /* the input file must be marked safe */ + rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD); + if (rval != 0) { - /* don't use this .forward file */ - clrevent(ev); + /* don't use this :include: file */ if (tTd(27, 4)) printf("include: not safe (uid=%d): %s\n", - ctladdr->q_uid, errstring(ret)); - return ret; + uid, errstring(rval)); } - - fp = fopen(fname, "r"); - if (fp == NULL) + else { - int ret = errno; - - clrevent(ev); - if (transienterror(ret)) + fp = fopen(fname, "r"); + if (fp == NULL) { - ctladdr->q_flags |= QQUEUEUP|QDONTSEND; - errno = 0; - usrerr("451 Cannot open %s: %s", fname, errstring(ret)); + rval = errno; + if (tTd(27, 4)) + printf("include: open: %s\n", errstring(rval)); } - else - { - usrerr("550 Cannot open %s: %s", fname, errstring(ret)); - } - return ret; } + clrevent(ev); - if (getctladdr(ctladdr) == NULL) +resetuid: + +#ifdef HASSETREUID + if (saveduid == 0) { - struct stat st; + if (uid != 0) + if (setreuid(-1, 0) < 0 || setreuid(RealUid, 0) < 0) + syserr("setreuid(%d, 0) failure (real=%d, eff=%d)", + RealUid, getuid(), geteuid()); + setgid(savedgid); + } +#endif - if (fstat(fileno(fp), &st) < 0) - { - int ret = errno; + if (tTd(27, 9)) + printf("include: reset uid = %d/%d\n", getuid(), geteuid()); - clrevent(ev); - syserr("Cannot fstat %s!", fname); - return ret; - } + if (rval == EOPENTIMEOUT) + usrerr("451 open timeout on %s", fname); + + if (fp == NULL) + return rval; + + if (fstat(fileno(fp), &st) < 0) + { + rval = errno; + syserr("Cannot fstat %s!", fname); + return rval; + } + +#ifndef safechown + safechown = chownsafe(fileno(fp)); +#endif + if (ca == NULL && safechown) + { ctladdr->q_uid = st.st_uid; ctladdr->q_gid = st.st_gid; ctladdr->q_flags |= QGOODUID; } + if (ca != NULL && ca->q_uid == st.st_uid) + { + /* optimization -- avoid getpwuid if we already have info */ + ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL; + ctladdr->q_ruser = ca->q_ruser; + } + else + { + char *sh; + register struct passwd *pw; - clrevent(ev); + sh = "/SENDMAIL/ANY/SHELL/"; + pw = getpwuid(st.st_uid); + if (pw != NULL) + { + ctladdr->q_ruser = newstr(pw->pw_name); + if (safechown) + sh = pw->pw_shell; + } + if (pw == NULL) + ctladdr->q_flags |= QBOGUSSHELL; + else if(!usershellok(sh)) + { + if (safechown) + ctladdr->q_flags |= QBOGUSSHELL; + else + ctladdr->q_flags |= QUNSAFEADDR; + } + } if (bitset(EF_VRFYONLY, e->e_flags)) { /* don't do any more now */ ctladdr->q_flags |= QVERIFIED; + e->e_nrcpts++; xfclose(fp, "include", fname); - return 0; + return rval; } + /* + ** Check to see if some bad guy can write this file + ** + ** This should really do something clever with group + ** permissions; currently we just view world writable + ** as unsafe. Also, we don't check for writable + ** directories in the path. We've got to leave + ** something for the local sysad to do. + */ + + if (bitset(S_IWOTH, st.st_mode)) + ctladdr->q_flags |= QUNSAFEADDR; + /* read the file -- each line is a comma-separated list. */ FileName = fname; LineNumber = 0; @@ -728,13 +975,17 @@ include(fname, forwarding, ctladdr, sendq, e) #ifdef LOG if (forwarding && LogLevel > 9) syslog(LOG_INFO, "%s: forward %s => %s", - e->e_id, oldto, buf); + e->e_id == NULL ? "NOQUEUE" : e->e_id, + oldto, buf); #endif AliasLevel++; - sendto(buf, 1, ctladdr, 0); + nincludes += sendtolist(buf, ctladdr, sendq, e); AliasLevel--; } + + if (ferror(fp) && tTd(27, 3)) + printf("include: read error: %s\n", errstring(errno)); if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) { if (tTd(27, 5)) @@ -748,7 +999,8 @@ include(fname, forwarding, ctladdr, sendq, e) (void) xfclose(fp, "include", fname); FileName = oldfilename; LineNumber = oldlinenumber; - return 0; + e->e_to = oldto; + return rval; } static @@ -779,7 +1031,7 @@ sendtoargv(argv, e) while ((p = *argv++) != NULL) { - sendto(p, 0, (ADDRESS *) NULL, 0); + (void) sendtolist(p, NULLADDR, &e->e_sendqueue, e); } } /*