summaryrefslogtreecommitdiff
path: root/src/recipient.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/recipient.c')
-rw-r--r--src/recipient.c1456
1 files changed, 0 insertions, 1456 deletions
diff --git a/src/recipient.c b/src/recipient.c
deleted file mode 100644
index f7e221f..0000000
--- a/src/recipient.c
+++ /dev/null
@@ -1,1456 +0,0 @@
-/*
- * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#ifndef lint
-static char sccsid[] = "@(#)recipient.c 8.163 (Berkeley) 1/23/1999";
-#endif /* not lint */
-
-# include "sendmail.h"
-# include <grp.h>
-
-/*
-** SENDTOLIST -- Designate a send list.
-**
-** The parameter is a comma-separated list of people to send to.
-** This routine arranges to send to all of them.
-**
-** Parameters:
-** list -- the send list.
-** ctladdr -- the address template for the person to
-** send to -- effective uid/gid are important.
-** This is typically the alias that caused this
-** expansion.
-** sendq -- a pointer to the head of a queue to put
-** these people into.
-** aliaslevel -- the current alias nesting depth -- to
-** diagnose loops.
-** e -- the envelope in which to add these recipients.
-**
-** Returns:
-** The number of addresses actually on the list.
-**
-** Side Effects:
-** none.
-*/
-
-/* q_flags bits inherited from ctladdr */
-#define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY)
-
-int
-sendtolist(list, ctladdr, sendq, aliaslevel, e)
- char *list;
- ADDRESS *ctladdr;
- ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- register char *p;
- register ADDRESS *al; /* list of addresses to send to */
- char delimiter; /* the address delimiter */
- int naddrs;
- int i;
- char *oldto = e->e_to;
- char *bufp;
- char buf[MAXNAME + 1];
-
- if (list == NULL)
- {
- syserr("sendtolist: null list");
- return 0;
- }
-
- if (tTd(25, 1))
- {
- printf("sendto: %s\n ctladdr=", list);
- printaddr(ctladdr, FALSE);
- }
-
- /* heuristic to determine old versus new style addresses */
- if (ctladdr == NULL &&
- (strchr(list, ',') != NULL || strchr(list, ';') != NULL ||
- strchr(list, '<') != NULL || strchr(list, '(') != NULL))
- e->e_flags &= ~EF_OLDSTYLE;
- delimiter = ' ';
- if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL)
- delimiter = ',';
-
- al = NULL;
- naddrs = 0;
-
- /* make sure we have enough space to copy the string */
- i = strlen(list) + 1;
- if (i <= sizeof buf)
- bufp = buf;
- else
- bufp = xalloc(i);
- strcpy(bufp, denlstring(list, FALSE, TRUE));
-
- for (p = bufp; *p != '\0'; )
- {
- auto char *delimptr;
- register ADDRESS *a;
-
- /* parse the address */
- while ((isascii(*p) && isspace(*p)) || *p == ',')
- p++;
- a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e);
- p = delimptr;
- if (a == NULL)
- continue;
- a->q_next = al;
- a->q_alias = ctladdr;
-
- /* arrange to inherit attributes from parent */
- if (ctladdr != NULL)
- {
- ADDRESS *b;
- extern ADDRESS *self_reference __P((ADDRESS *, ENVELOPE *));
-
- /* self reference test */
- if (sameaddr(ctladdr, a))
- {
- if (tTd(27, 5))
- {
- printf("sendtolist: QSELFREF ");
- printaddr(ctladdr, FALSE);
- }
- ctladdr->q_flags |= QSELFREF;
- }
-
- /* check for address loops */
- b = self_reference(a, e);
- if (b != NULL)
- {
- b->q_flags |= QSELFREF;
- if (tTd(27, 5))
- {
- printf("sendtolist: QSELFREF ");
- printaddr(b, FALSE);
- }
- if (a != b)
- {
- if (tTd(27, 5))
- {
- printf("sendtolist: QDONTSEND ");
- printaddr(a, FALSE);
- }
- a->q_flags |= QDONTSEND;
- b->q_flags |= a->q_flags & QNOTREMOTE;
- continue;
- }
- }
-
- /* full name */
- if (a->q_fullname == NULL)
- a->q_fullname = ctladdr->q_fullname;
-
- /* various flag bits */
- a->q_flags &= ~QINHERITEDBITS;
- a->q_flags |= ctladdr->q_flags & QINHERITEDBITS;
-
- /* original recipient information */
- a->q_orcpt = ctladdr->q_orcpt;
- }
-
- al = a;
- }
-
- /* arrange to send to everyone on the local send list */
- while (al != NULL)
- {
- register ADDRESS *a = al;
-
- al = a->q_next;
- a = recipient(a, sendq, aliaslevel, e);
- naddrs++;
- }
-
- e->e_to = oldto;
- if (bufp != buf)
- free(bufp);
- return (naddrs);
-}
- /*
-** RECIPIENT -- Designate a message recipient
-**
-** Saves the named person for future mailing.
-**
-** Parameters:
-** a -- the (preparsed) address header for the recipient.
-** sendq -- a pointer to the head of a queue to put the
-** recipient in. Duplicate supression is done
-** in this queue.
-** aliaslevel -- the current alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** The actual address in the queue. This will be "a" if
-** the address is not a duplicate, else the original address.
-**
-** Side Effects:
-** none.
-*/
-
-ADDRESS *
-recipient(a, sendq, aliaslevel, e)
- register ADDRESS *a;
- register ADDRESS **sendq;
- int aliaslevel;
- register ENVELOPE *e;
-{
- register ADDRESS *q;
- ADDRESS **pq;
- register struct mailer *m;
- register char *p;
- bool quoted = FALSE; /* set if the addr has a quote bit */
- int findusercount = 0;
- bool initialdontsend = bitset(QDONTSEND, a->q_flags);
- int i;
- char *buf;
- char buf0[MAXNAME + 1]; /* unquoted image of the user name */
- extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-
- e->e_to = a->q_paddr;
- m = a->q_mailer;
- errno = 0;
- if (aliaslevel == 0)
- a->q_flags |= QPRIMARY;
- if (tTd(26, 1))
- {
- printf("\nrecipient (%d): ", aliaslevel);
- 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 > MaxAliasRecursion)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.4.6";
- usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
- aliaslevel, MaxAliasRecursion);
- return (a);
- }
-
- /*
- ** Finish setting up address structure.
- */
-
- /* get unquoted user for file, program or user.name check */
- i = strlen(a->q_user);
- if (i >= sizeof buf0)
- buf = xalloc(i + 1);
- else
- buf = buf0;
- (void) strcpy(buf, a->q_user);
- for (p = buf; *p != '\0' && !quoted; p++)
- {
- if (*p == '\\')
- quoted = TRUE;
- }
- stripquotes(buf);
-
- /* check for direct mailing to restricted mailers */
- if (m == ProgMailer)
- {
- if (a->q_alias == NULL)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.7.1";
- usrerr("550 Cannot mail directly to programs");
- }
- else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.7.1";
- if (a->q_alias->q_ruser == NULL)
- usrerr("550 UID %d is an unknown user: cannot mail to programs",
- a->q_alias->q_uid);
- else
- 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;
- a->q_status = "5.7.1";
- a->q_rstatus = newstr("Unsafe for mailing to programs");
- usrerr("550 Address %s is unsafe for mailing to programs",
- a->q_alias->q_paddr);
- }
- }
-
- /*
- ** Look up this person in the recipient list.
- ** If they are there already, return, otherwise continue.
- ** If the list is empty, just add it. Notice the cute
- ** hack to make from addresses suppress things correctly:
- ** the QDONTSEND bit will be set in the send list.
- ** [Please note: the emphasis is on "hack."]
- */
-
- for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
- {
- if (sameaddr(q, a) &&
- (bitset(QRCPTOK, q->q_flags) ||
- !bitset(QPRIMARY, q->q_flags)))
- {
- if (tTd(26, 1))
- {
- printf("%s in sendq: ", a->q_paddr);
- printaddr(q, FALSE);
- }
- if (!bitset(QPRIMARY, q->q_flags))
- {
- if (!bitset(QDONTSEND, a->q_flags))
- message("duplicate suppressed");
- q->q_flags |= a->q_flags;
- }
- else if (bitset(QSELFREF, q->q_flags))
- q->q_flags |= a->q_flags & ~QDONTSEND;
- a = q;
- goto done;
- }
- }
-
- /* add address on list */
- if (pq != NULL)
- {
- *pq = a;
- a->q_next = NULL;
- }
-
- /*
- ** Alias the name and handle special mailer types.
- */
-
- trylocaluser:
- if (tTd(29, 7))
- {
- printf("at trylocaluser: ");
- printaddr(a, FALSE);
- }
-
- if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
- goto testselfdestruct;
-
- if (m == InclMailer)
- {
- a->q_flags |= QDONTSEND;
- if (a->q_alias == NULL)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.7.1";
- usrerr("550 Cannot mail directly to :include:s");
- }
- else
- {
- int ret;
-
- message("including file %s", a->q_user);
- ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e);
- if (transienterror(ret))
- {
- if (LogLevel > 2)
- sm_syslog(LOG_ERR, e->e_id,
- "include %s: transient error: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
- a->q_flags |= QQUEUEUP;
- a->q_flags &= ~QDONTSEND;
- usrerr("451 Cannot open %s: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
- }
- else if (ret != 0)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.2.4";
- usrerr("550 Cannot open %s: %s",
- shortenstring(a->q_user, MAXSHORTSTR),
- errstring(ret));
- }
- }
- }
- else if (m == FileMailer)
- {
- extern bool writable __P((char *, ADDRESS *, int));
-
- /* check if writable or creatable */
- if (a->q_alias == NULL)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.7.1";
- usrerr("550 Cannot mail directly to files");
- }
- else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.7.1";
- if (a->q_alias->q_ruser == NULL)
- usrerr("550 UID %d is an unknown user: cannot mail to files",
- a->q_alias->q_uid);
- else
- 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;
- a->q_status = "5.7.1";
- a->q_rstatus = newstr("Unsafe for mailing to files");
- usrerr("550 Address %s is unsafe for mailing to files",
- a->q_alias->q_paddr);
- }
- else if (strcmp(buf, "/dev/null") == 0)
- {
- /* /dev/null is always accepted */
- }
- else if (!writable(buf, a->q_alias, SFF_CREAT))
- {
- a->q_flags |= QBADADDR;
- giveresponse(EX_CANTCREAT, m, NULL, a->q_alias,
- (time_t) 0, e);
- }
- }
-
- /* try aliasing */
- if (!quoted && !bitset(QDONTSEND, a->q_flags) &&
- bitnset(M_ALIASABLE, m->m_flags))
- alias(a, sendq, aliaslevel, e);
-
-# if USERDB
- /* if not aliased, look it up in the user database */
- if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) &&
- bitnset(M_CHECKUDB, m->m_flags))
- {
- extern int udbexpand __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-
- if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
- {
- a->q_flags |= QQUEUEUP;
- if (e->e_message == NULL)
- e->e_message = newstr("Deferred: user database error");
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, e->e_id,
- "deferred: udbexpand: %s",
- errstring(errno));
- message("queued (user database error): %s",
- errstring(errno));
- e->e_nrcpts++;
- goto testselfdestruct;
- }
- }
-# endif
-
- /*
- ** If we have a level two config file, then pass the name through
- ** Ruleset 5 before sending it off. Ruleset 5 has the right
- ** to send rewrite it to another mailer. This gives us a hook
- ** after local aliasing has been done.
- */
-
- if (tTd(29, 5))
- {
- printf("recipient: testing local? cl=%d, rr5=%lx\n\t",
- ConfigLevel, (u_long) RewriteRules[5]);
- printaddr(a, FALSE);
- }
- if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
- ConfigLevel >= 2 && RewriteRules[5] != NULL &&
- bitnset(M_TRYRULESET5, m->m_flags))
- {
- extern void maplocaluser __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-
- maplocaluser(a, sendq, aliaslevel + 1, e);
- }
-
- /*
- ** If it didn't get rewritten to another mailer, go ahead
- ** and deliver it.
- */
-
- if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) &&
- bitnset(M_HASPWENT, m->m_flags))
- {
- auto bool fuzzy;
- register struct passwd *pw;
- extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *));
-
- /* warning -- finduser may trash buf */
- pw = finduser(buf, &fuzzy);
- if (pw == NULL || strlen(pw->pw_name) > MAXNAME)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.1.1";
- giveresponse(EX_NOUSER, m, NULL, a->q_alias,
- (time_t) 0, e);
- }
- else
- {
- char nbuf[MAXNAME + 1];
-
- if (fuzzy)
- {
- /* name was a fuzzy match */
- a->q_user = newstr(pw->pw_name);
- if (findusercount++ > 3)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.4.6";
- usrerr("554 aliasing/forwarding loop for %s broken",
- pw->pw_name);
- goto done;
- }
-
- /* see if it aliases */
- (void) strcpy(buf, pw->pw_name);
- goto trylocaluser;
- }
- 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, sizeof nbuf);
- if (nbuf[0] != '\0')
- a->q_fullname = newstr(nbuf);
- if (!usershellok(pw->pw_name, pw->pw_shell))
- {
- a->q_flags |= QBOGUSSHELL;
- }
- if (bitset(EF_VRFYONLY, e->e_flags))
- {
- /* don't do any more now */
- a->q_flags |= QVERIFIED;
- }
- else if (!quoted)
- forward(a, sendq, aliaslevel, e);
- }
- }
- if (!bitset(QDONTSEND, a->q_flags))
- e->e_nrcpts++;
-
- testselfdestruct:
- a->q_flags |= QTHISPASS;
- if (tTd(26, 8))
- {
- printf("testselfdestruct: ");
- printaddr(a, FALSE);
- if (tTd(26, 10))
- {
- printf("SENDQ:\n");
- printaddr(*sendq, TRUE);
- printf("----\n");
- }
- }
- if (a->q_alias == NULL && a != &e->e_from &&
- bitset(QDONTSEND, a->q_flags))
- {
- for (q = *sendq; q != NULL; q = q->q_next)
- {
- if (!bitset(QDONTSEND, q->q_flags))
- break;
- }
- if (q == NULL)
- {
- a->q_flags |= QBADADDR;
- a->q_status = "5.4.6";
- usrerr("554 aliasing/forwarding loop broken");
- }
- }
-
- done:
- a->q_flags |= QTHISPASS;
- if (buf != buf0)
- free(buf);
-
- /*
- ** If we are at the top level, check to see if this has
- ** expanded to exactly one address. If so, it can inherit
- ** the primaryness of the address.
- **
- ** While we're at it, clear the QTHISPASS bits.
- */
-
- if (aliaslevel == 0)
- {
- int nrcpts = 0;
- ADDRESS *only = NULL;
-
- for (q = *sendq; q != NULL; q = q->q_next)
- {
- if (bitset(QTHISPASS, q->q_flags) &&
- !bitset(QDONTSEND|QBADADDR, q->q_flags))
- {
- nrcpts++;
- only = q;
- }
- q->q_flags &= ~QTHISPASS;
- }
- if (nrcpts == 1)
- {
- /* check to see if this actually got a new owner */
- q = only;
- while ((q = q->q_alias) != NULL)
- {
- if (q->q_owner != NULL)
- break;
- }
- if (q == NULL)
- only->q_flags |= QPRIMARY;
- }
- else if (!initialdontsend && nrcpts > 0)
- {
- /* arrange for return receipt */
- e->e_flags |= EF_SENDRECEIPT;
- a->q_flags |= QEXPANDED;
- if (e->e_xfp != NULL && bitset(QPINGONSUCCESS, a->q_flags))
- fprintf(e->e_xfp,
- "%s... expanded to multiple addresses\n",
- a->q_paddr);
- }
- }
- a->q_flags |= QRCPTOK;
- return (a);
-}
- /*
-** FINDUSER -- find the password entry for a user.
-**
-** This looks a lot like getpwnam, except that it may want to
-** do some fancier pattern matching in /etc/passwd.
-**
-** This routine contains most of the time of many sendmail runs.
-** It deserves to be optimized.
-**
-** Parameters:
-** name -- the name to match against.
-** fuzzyp -- an outarg that is set to TRUE if this entry
-** was found using the fuzzy matching algorithm;
-** set to FALSE otherwise.
-**
-** Returns:
-** A pointer to a pw struct.
-** NULL if name is unknown or ambiguous.
-**
-** Side Effects:
-** may modify name.
-*/
-
-struct passwd *
-finduser(name, fuzzyp)
- char *name;
- bool *fuzzyp;
-{
- register struct passwd *pw;
- register char *p;
- bool tryagain;
-
- if (tTd(29, 4))
- printf("finduser(%s): ", name);
-
- *fuzzyp = FALSE;
-
-#ifdef HESIOD
- /* 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;
- }
-#endif
-
- /* look up this login name using fast path */
- if ((pw = sm_getpwnam(name)) != NULL)
- {
- if (tTd(29, 4))
- printf("found (non-fuzzy)\n");
- return (pw);
- }
-
- /* try mapping it to lower case */
- tryagain = FALSE;
- for (p = name; *p != '\0'; p++)
- {
- if (isascii(*p) && isupper(*p))
- {
- *p = tolower(*p);
- tryagain = TRUE;
- }
- }
- if (tryagain && (pw = sm_getpwnam(name)) != NULL)
- {
- if (tTd(29, 4))
- printf("found (lower case)\n");
- *fuzzyp = TRUE;
- return pw;
- }
-
-#if MATCHGECOS
- /* see if fuzzy matching allowed */
- if (!MatchGecos)
- {
- if (tTd(29, 4))
- printf("not found (fuzzy disabled)\n");
- return NULL;
- }
-
- /* search for a matching full name instead */
- for (p = name; *p != '\0'; p++)
- {
- if (*p == (SpaceSub & 0177) || *p == '_')
- *p = ' ';
- }
- (void) setpwent();
- while ((pw = getpwent()) != NULL)
- {
- char buf[MAXNAME + 1];
-
-# if 0
- if (strcasecmp(pw->pw_name, name) == 0)
- {
- if (tTd(29, 4))
- printf("found (case wrapped)\n");
- break;
- }
-# endif
-
- buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
- if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name))
- {
- if (tTd(29, 4))
- printf("fuzzy matches %s\n", pw->pw_name);
- message("sending to login name %s", pw->pw_name);
- break;
- }
- }
- if (pw != NULL)
- *fuzzyp = TRUE;
- else if (tTd(29, 4))
- printf("no fuzzy match found\n");
-# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
- endpwent();
-# endif
- return pw;
-#else
- if (tTd(29, 4))
- printf("not found (fuzzy disabled)\n");
- return NULL;
-#endif
-}
- /*
-** WRITABLE -- predicate returning if the file is writable.
-**
-** This routine must duplicate the algorithm in sys/fio.c.
-** Unfortunately, we cannot use the access call since we
-** won't necessarily be the real uid when we try to
-** actually open the file.
-**
-** Notice that ANY file with ANY execute bit is automatically
-** not writable. This is also enforced by mailfile.
-**
-** Parameters:
-** 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.
-** FALSE -- if we cannot write this file.
-**
-** Side Effects:
-** none.
-*/
-
-bool
-writable(filename, ctladdr, flags)
- char *filename;
- ADDRESS *ctladdr;
- int flags;
-{
- uid_t euid;
- gid_t egid;
- char *uname;
-
- if (tTd(44, 5))
- printf("writable(%s, 0x%x)\n", filename, flags);
-
- /*
- ** File does exist -- check that it is writable.
- */
-
- if (geteuid() != 0)
- {
- euid = geteuid();
- egid = getegid();
- uname = NULL;
- }
- else if (ctladdr != NULL)
- {
- euid = ctladdr->q_uid;
- egid = ctladdr->q_gid;
- uname = ctladdr->q_user;
- }
- else if (bitset(SFF_RUNASREALUID, flags))
- {
- euid = RealUid;
- egid = RealGid;
- uname = RealUserName;
- }
- else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
- {
- euid = FileMailer->m_uid;
- egid = FileMailer->m_gid;
- uname = NULL;
- }
- else
- {
- euid = egid = 0;
- uname = NULL;
- }
- if (!bitset(SFF_ROOTOK, flags))
- {
- if (euid == 0)
- {
- euid = DefUid;
- uname = DefUser;
- }
- if (egid == 0)
- egid = DefGid;
- }
- if (geteuid() == 0 &&
- (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
- flags |= SFF_SETUIDOK;
-
- if (!bitset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
- flags |= SFF_NOSLINK;
- if (!bitset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
- flags |= SFF_NOHLINK;
-
- errno = safefile(filename, euid, egid, uname, flags, S_IWRITE, NULL);
- return errno == 0;
-}
- /*
-** INCLUDE -- handle :include: specification.
-**
-** Parameters:
-** fname -- filename to include.
-** forwarding -- if TRUE, we are reading a .forward file.
-** if FALSE, it's a :include: file.
-** ctladdr -- address template to use to fill in these
-** addresses -- effective user/group id are
-** the important things.
-** sendq -- a pointer to the head of the send queue
-** to put these addresses in.
-** aliaslevel -- the alias nesting depth.
-** e -- the current envelope.
-**
-** Returns:
-** open error status
-**
-** 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 void includetimeout __P((void));
-
-int
-include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
- char *fname;
- bool forwarding;
- ADDRESS *ctladdr;
- ADDRESS **sendq;
- int aliaslevel;
- ENVELOPE *e;
-{
- FILE *volatile fp = NULL;
- char *oldto = e->e_to;
- char *oldfilename = FileName;
- int oldlinenumber = LineNumber;
- register EVENT *ev = NULL;
- int nincludes;
- int mode;
- register ADDRESS *ca;
- volatile uid_t saveduid, uid;
- volatile gid_t savedgid, gid;
- char *volatile uname;
- int rval = 0;
- volatile int sfflags = SFF_REGONLY;
- register char *p;
- bool safechown = FALSE;
- volatile bool safedir = FALSE;
- struct stat st;
- char buf[MAXLINE];
- extern bool chownsafe __P((int, bool));
-
- if (tTd(27, 2))
- printf("include(%s)\n", fname);
- if (tTd(27, 4))
- printf(" ruid=%d euid=%d\n", (int) getuid(), (int) geteuid());
- if (tTd(27, 14))
- {
- printf("ctladdr ");
- printaddr(ctladdr, FALSE);
- }
-
- if (tTd(27, 9))
- printf("include: old uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
-
- if (forwarding)
- sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
-
- ca = getctladdr(ctladdr);
- if (ca == NULL)
- {
- uid = DefUid;
- gid = DefGid;
- uname = DefUser;
- }
- else
- {
- uid = ca->q_uid;
- gid = ca->q_gid;
- uname = ca->q_user;
- }
-#if HASSETREUID || USESETEUID
- saveduid = geteuid();
- savedgid = getegid();
- if (saveduid == 0)
- {
- if (!DontInitGroups)
- {
- if (initgroups(uname, gid) == -1)
- syserr("include: initgroups(%s, %d) failed",
- uname, gid);
- }
- else
- {
- GIDSET_T gidset[1];
-
- gidset[0] = gid;
- if (setgroups(1, gidset) == -1)
- syserr("include: setgroups() failed");
- }
-
- if (gid != 0 && setgid(gid) < -1)
- syserr("setgid(%d) failure", gid);
- if (uid != 0)
- {
-# if USESETEUID
- if (seteuid(uid) < 0)
- syserr("seteuid(%d) failure (real=%d, eff=%d)",
- uid, getuid(), geteuid());
-# else
- if (setreuid(0, uid) < 0)
- syserr("setreuid(0, %d) failure (real=%d, eff=%d)",
- uid, getuid(), geteuid());
-# endif
- }
- }
-#endif
-
- if (tTd(27, 9))
- printf("include: new uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
-
- /*
- ** If home directory is remote mounted but server is down,
- ** this can hang or give errors; use a timeout to avoid this
- */
-
- if (setjmp(CtxIncludeTimeout) != 0)
- {
- ctladdr->q_flags |= QQUEUEUP;
- errno = 0;
-
- /* return pseudo-error code */
- rval = E_SM_OPENTIMEOUT;
- goto resetuid;
- }
- if (TimeOuts.to_fileopen > 0)
- ev = setevent(TimeOuts.to_fileopen, includetimeout, 0);
- else
- ev = NULL;
-
- /* check for writable parent directory */
- p = strrchr(fname, '/');
- if (p != NULL)
- {
- int ret;
-
- *p = '\0';
- ret = safedirpath(fname, uid, gid, uname, sfflags|SFF_SAFEDIRPATH);
- if (ret == 0)
- {
- /* in safe directory: relax chown & link rules */
- safedir = TRUE;
- sfflags |= SFF_NOPATHCHECK;
- }
- else
- {
- if (bitset((forwarding ?
- DBS_FORWARDFILEINUNSAFEDIRPATH :
- DBS_INCLUDEFILEINUNSAFEDIRPATH),
- DontBlameSendmail))
- sfflags |= SFF_NOPATHCHECK;
- else if (bitset((forwarding ?
- DBS_FORWARDFILEINGROUPWRITABLEDIRPATH :
- DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH),
- DontBlameSendmail) &&
- ret == E_SM_GWDIR)
- {
- DontBlameSendmail |= DBS_GROUPWRITABLEDIRPATHSAFE;
- ret = safedirpath(fname, uid,
- gid, uname,
- sfflags|SFF_SAFEDIRPATH);
- DontBlameSendmail &= ~DBS_GROUPWRITABLEDIRPATHSAFE;
- if (ret == 0)
- sfflags |= SFF_NOPATHCHECK;
- else
- sfflags |= SFF_SAFEDIRPATH;
- }
- else
- sfflags |= SFF_SAFEDIRPATH;
- if (ret > E_PSEUDOBASE &&
- !bitset((forwarding ?
- DBS_FORWARDFILEINUNSAFEDIRPATHSAFE :
- DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE),
- DontBlameSendmail))
- {
- if (LogLevel >= 12)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: unsafe directory path, marked unsafe",
- shortenstring(fname, MAXSHORTSTR));
- ctladdr->q_flags |= QUNSAFEADDR;
- }
- }
- *p = '/';
- }
-
- /* allow links only in unwritable directories */
- if (!safedir &&
- !bitset((forwarding ?
- DBS_LINKEDFORWARDFILEINWRITABLEDIR :
- DBS_LINKEDINCLUDEFILEINWRITABLEDIR),
- DontBlameSendmail))
- sfflags |= SFF_NOLINK;
-
- rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, &st);
- if (rval != 0)
- {
- /* don't use this :include: file */
- if (tTd(27, 4))
- printf("include: not safe (uid=%d): %s\n",
- (int) uid, errstring(rval));
- }
- else if ((fp = fopen(fname, "r")) == NULL)
- {
- rval = errno;
- if (tTd(27, 4))
- printf("include: open: %s\n", errstring(rval));
- }
- else if (filechanged(fname, fileno(fp), &st))
- {
- rval = E_SM_FILECHANGE;
- if (tTd(27, 4))
- printf("include: file changed after open\n");
- }
- if (ev != NULL)
- clrevent(ev);
-
-resetuid:
-
-#if HASSETREUID || USESETEUID
- if (saveduid == 0)
- {
- if (uid != 0)
- {
-# if USESETEUID
- if (seteuid(0) < 0)
- syserr("seteuid(0) failure (real=%d, eff=%d)",
- getuid(), geteuid());
-# else
- if (setreuid(-1, 0) < 0)
- syserr("setreuid(-1, 0) failure (real=%d, eff=%d)",
- getuid(), geteuid());
- if (setreuid(RealUid, 0) < 0)
- syserr("setreuid(%d, 0) failure (real=%d, eff=%d)",
- RealUid, getuid(), geteuid());
-# endif
- }
- setgid(savedgid);
- }
-#endif
-
- if (tTd(27, 9))
- printf("include: reset uid = %d/%d\n",
- (int) getuid(), (int) geteuid());
-
- if (rval == E_SM_OPENTIMEOUT)
- 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;
- }
-
- /* if path was writable, check to avoid file giveaway tricks */
- safechown = chownsafe(fileno(fp), safedir);
- if (tTd(27, 6))
- printf("include: parent of %s is %s, chown is %ssafe\n",
- fname,
- safedir ? "safe" : "dangerous",
- safechown ? "" : "un");
-
- 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 if (!forwarding)
- {
- register struct passwd *pw;
-
- pw = sm_getpwuid(st.st_uid);
- if (pw == NULL)
- ctladdr->q_flags |= QBOGUSSHELL;
- else
- {
- char *sh;
-
- ctladdr->q_ruser = newstr(pw->pw_name);
- if (safechown)
- sh = pw->pw_shell;
- else
- sh = "/SENDMAIL/ANY/SHELL/";
- if (!usershellok(pw->pw_name, sh))
- {
- if (LogLevel >= 12)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: user %s has bad shell %s, marked %s",
- shortenstring(fname, MAXSHORTSTR),
- pw->pw_name, sh,
- safechown ? "bogus" : "unsafe");
- 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;
- ctladdr->q_flags &= ~QDONTSEND;
- e->e_nrcpts++;
- xfclose(fp, "include", fname);
- return rval;
- }
-
- /*
- ** Check to see if some bad guy can write this file
- **
- ** Group write checking could be more clever, e.g.,
- ** guessing as to which groups are actually safe ("sys"
- ** may be; "user" probably is not).
- */
-
- mode = S_IWOTH;
- if (!bitset((forwarding ?
- DBS_GROUPWRITABLEFORWARDFILESAFE :
- DBS_GROUPWRITABLEINCLUDEFILESAFE),
- DontBlameSendmail))
- mode |= S_IWGRP;
-
- if (bitset(mode, st.st_mode))
- {
- if (tTd(27, 6))
- printf("include: %s is %s writable, marked unsafe\n",
- shortenstring(fname, MAXSHORTSTR),
- bitset(S_IWOTH, st.st_mode) ? "world" : "group");
- if (LogLevel >= 12)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s writable %s file, marked unsafe",
- shortenstring(fname, MAXSHORTSTR),
- bitset(S_IWOTH, st.st_mode) ? "world" : "group",
- forwarding ? "forward" : ":include:");
- ctladdr->q_flags |= QUNSAFEADDR;
- }
-
- /* read the file -- each line is a comma-separated list. */
- FileName = fname;
- LineNumber = 0;
- ctladdr->q_flags &= ~QSELFREF;
- nincludes = 0;
- while (fgets(buf, sizeof buf, fp) != NULL)
- {
- register char *p = strchr(buf, '\n');
-
- LineNumber++;
- if (p != NULL)
- *p = '\0';
- if (buf[0] == '#' || buf[0] == '\0')
- continue;
-
- /* <sp>#@# introduces a comment anywhere */
- /* for Japanese character sets */
- for (p = buf; (p = strchr(++p, '#')) != NULL; )
- {
- if (p[1] == '@' && p[2] == '#' &&
- isascii(p[-1]) && isspace(p[-1]) &&
- (p[3] == '\0' || (isascii(p[3]) && isspace(p[3]))))
- {
- p[-1] = '\0';
- break;
- }
- }
- if (buf[0] == '\0')
- continue;
-
- e->e_to = NULL;
- message("%s to %s",
- forwarding ? "forwarding" : "sending", buf);
- if (forwarding && LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "forward %.200s => %s",
- oldto, shortenstring(buf, MAXSHORTSTR));
-
- nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e);
- }
-
- 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))
- {
- printf("include: QDONTSEND ");
- printaddr(ctladdr, FALSE);
- }
- ctladdr->q_flags |= QDONTSEND;
- }
-
- (void) xfclose(fp, "include", fname);
- FileName = oldfilename;
- LineNumber = oldlinenumber;
- e->e_to = oldto;
- return rval;
-}
-
-static void
-includetimeout()
-{
- longjmp(CtxIncludeTimeout, 1);
-}
- /*
-** SENDTOARGV -- send to an argument vector.
-**
-** Parameters:
-** argv -- argument vector to send to.
-** e -- the current envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** puts all addresses on the argument vector onto the
-** send queue.
-*/
-
-void
-sendtoargv(argv, e)
- register char **argv;
- register ENVELOPE *e;
-{
- register char *p;
-
- while ((p = *argv++) != NULL)
- {
- (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e);
- }
-}
- /*
-** GETCTLADDR -- get controlling address from an address header.
-**
-** If none, get one corresponding to the effective userid.
-**
-** Parameters:
-** a -- the address to find the controller of.
-**
-** Returns:
-** the controlling address.
-**
-** Side Effects:
-** none.
-*/
-
-ADDRESS *
-getctladdr(a)
- register ADDRESS *a;
-{
- while (a != NULL && !bitset(QGOODUID, a->q_flags))
- a = a->q_alias;
- return (a);
-}
- /*
-** SELF_REFERENCE -- check to see if an address references itself
-**
-** The check is done through a chain of aliases. If it is part of
-** a loop, break the loop at the "best" address, that is, the one
-** that exists as a real user.
-**
-** This is to handle the case of:
-** awc: Andrew.Chang
-** Andrew.Chang: awc@mail.server
-** which is a problem only on mail.server.
-**
-** Parameters:
-** a -- the address to check.
-** e -- the current envelope.
-**
-** Returns:
-** The address that should be retained.
-*/
-
-ADDRESS *
-self_reference(a, e)
- ADDRESS *a;
- ENVELOPE *e;
-{
- ADDRESS *b; /* top entry in self ref loop */
- ADDRESS *c; /* entry that point to a real mail box */
-
- if (tTd(27, 1))
- printf("self_reference(%s)\n", a->q_paddr);
-
- for (b = a->q_alias; b != NULL; b = b->q_alias)
- {
- if (sameaddr(a, b))
- break;
- }
-
- if (b == NULL)
- {
- if (tTd(27, 1))
- printf("\t... no self ref\n");
- return NULL;
- }
-
- /*
- ** Pick the first address that resolved to a real mail box
- ** i.e has a pw entry. The returned value will be marked
- ** QSELFREF in recipient(), which in turn will disable alias()
- ** from marking it QDONTSEND, which mean it will be used
- ** as a deliverable address.
- **
- ** The 2 key thing to note here are:
- ** 1) we are in a recursive call sequence:
- ** alias->sentolist->recipient->alias
- ** 2) normally, when we return back to alias(), the address
- ** will be marked QDONTSEND, since alias() assumes the
- ** expanded form will be used instead of the current address.
- ** This behaviour is turned off if the address is marked
- ** QSELFREF We set QSELFREF when we return to recipient().
- */
-
- c = a;
- while (c != NULL)
- {
- if (tTd(27, 10))
- printf(" %s", c->q_user);
- if (bitnset(M_HASPWENT, c->q_mailer->m_flags))
- {
- if (tTd(27, 2))
- printf("\t... getpwnam(%s)... ", c->q_user);
- if (sm_getpwnam(c->q_user) != NULL)
- {
- if (tTd(27, 2))
- printf("found\n");
-
- /* ought to cache results here */
- if (sameaddr(b, c))
- return b;
- else
- return c;
- }
- if (tTd(27, 2))
- printf("failed\n");
- }
- else
- {
- /* if local delivery, compare usernames */
- if (bitnset(M_LOCALMAILER, c->q_mailer->m_flags) &&
- b->q_mailer == c->q_mailer)
- {
- if (tTd(27, 2))
- printf("\t... local match (%s)\n", c->q_user);
- if (sameaddr(b, c))
- return b;
- else
- return c;
- }
- }
- if (tTd(27, 10))
- printf("\n");
- c = c->q_alias;
- }
-
- if (tTd(27, 1))
- printf("\t... cannot break loop for \"%s\"\n", a->q_paddr);
-
- return NULL;
-}