📄 recipient.c
字号:
errstring(ret));
}
else if (ret != 0)
{
a->q_state = QS_BADADDR;
a->q_status = "5.2.4";
usrerrenh(a->q_status,
"550 Cannot open %s: %s",
shortenstring(a->q_user, MAXSHORTSTR),
errstring(ret));
}
}
}
else if (m == FileMailer)
{
/* check if writable or creatable */
if (a->q_alias == NULL)
{
a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
usrerrenh(a->q_status,
"550 Cannot mail directly to files");
}
else if (bitset(QBOGUSSHELL, a->q_alias->q_flags))
{
a->q_state = QS_BADADDR;
a->q_status = "5.7.1";
if (a->q_alias->q_ruser == NULL)
usrerrenh(a->q_status,
"550 UID %d is an unknown user: cannot mail to files",
a->q_alias->q_uid);
else
usrerrenh(a->q_status,
"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_state = QS_BADADDR;
a->q_status = "5.7.1";
a->q_rstatus = newstr("550 Unsafe for mailing to files");
usrerrenh(a->q_status,
"550 Address %s is unsafe for mailing to files",
a->q_alias->q_paddr);
}
}
/* try aliasing */
if (!quoted && QS_IS_OK(a->q_state) &&
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(QNOTREMOTE, a->q_flags) &&
QS_IS_SENDABLE(a->q_state) &&
bitnset(M_CHECKUDB, m->m_flags))
{
if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL)
{
a->q_state = QS_QUEUEUP;
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 /* USERDB */
/*
** 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))
{
dprintf("recipient: testing local? cl=%d, rr5=%lx\n\t",
ConfigLevel, (u_long) RewriteRules[5]);
printaddr(a, FALSE);
}
if (ConfigLevel >= 2 && RewriteRules[5] != NULL &&
bitnset(M_TRYRULESET5, m->m_flags) &&
!bitset(QNOTREMOTE, a->q_flags) &&
QS_IS_OK(a->q_state))
{
maplocaluser(a, sendq, aliaslevel + 1, e);
}
/*
** If it didn't get rewritten to another mailer, go ahead
** and deliver it.
*/
if (QS_IS_OK(a->q_state) &&
bitnset(M_HASPWENT, m->m_flags))
{
auto bool fuzzy;
register struct passwd *pw;
/* warning -- finduser may trash buf */
pw = finduser(buf, &fuzzy);
if (pw == NULL || strlen(pw->pw_name) > MAXNAME)
{
{
a->q_state = QS_BADADDR;
a->q_status = "5.1.1";
a->q_rstatus = newstr("550 5.1.1 User unknown");
giveresponse(EX_NOUSER, a->q_status, 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_state = QS_BADADDR;
a->q_status = "5.4.6";
usrerrenh(a->q_status,
"554 aliasing/forwarding loop for %s broken",
pw->pw_name);
goto done;
}
/* see if it aliases */
(void) strlcpy(buf, pw->pw_name, buflen);
goto trylocaluser;
}
if (*pw->pw_dir == '\0')
a->q_home = NULL;
else 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_state = QS_VERIFIED;
}
else if (!quoted)
forward(a, sendq, aliaslevel, e);
}
}
if (!QS_IS_DEAD(a->q_state))
e->e_nrcpts++;
testselfdestruct:
a->q_flags |= QTHISPASS;
if (tTd(26, 8))
{
dprintf("testselfdestruct: ");
printaddr(a, FALSE);
if (tTd(26, 10))
{
dprintf("SENDQ:\n");
printaddr(*sendq, TRUE);
dprintf("----\n");
}
}
if (a->q_alias == NULL && a != &e->e_from &&
QS_IS_DEAD(a->q_state))
{
for (q = *sendq; q != NULL; q = q->q_next)
{
if (!QS_IS_DEAD(q->q_state))
break;
}
if (q == NULL)
{
a->q_state = QS_BADADDR;
a->q_status = "5.4.6";
usrerrenh(a->q_status,
"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) &&
QS_IS_SENDABLE(q->q_state))
{
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))
dprintf("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))
dprintf("failed (numeric input)\n");
return NULL;
}
#endif /* HESIOD */
/* look up this login name using fast path */
if ((pw = sm_getpwnam(name)) != NULL)
{
if (tTd(29, 4))
dprintf("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))
dprintf("found (lower case)\n");
*fuzzyp = TRUE;
return pw;
}
#if MATCHGECOS
/* see if fuzzy matching allowed */
if (!MatchGecos)
{
if (tTd(29, 4))
dprintf("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))
dprintf("found (case wrapped)\n");
break;
}
# endif /* 0 */
buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf);
if (strchr(buf, ' ') != NULL && strcasecmp(buf, name) == 0)
{
if (tTd(29, 4))
dprintf("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))
dprintf("no fuzzy match found\n");
# if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */
endpwent();
# endif /* DEC_OSF_BROKEN_GETPWENT */
return pw;
#else /* MATCHGECOS */
if (tTd(29, 4))
dprintf("not found (fuzzy disabled)\n");
return NULL;
#endif /* MATCHGECOS */
}
/*
** 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;
long flags;
{
uid_t euid = 0;
gid_t egid = 0;
char *user = NULL;
if (tTd(44, 5))
dprintf("writable(%s, 0x%lx)\n", filename, flags);
/*
** File does exist -- check that it is writable.
*/
if (geteuid() != 0)
{
euid = geteuid();
egid = getegid();
user = NULL;
}
else if (ctladdr != NULL)
{
euid = ctladdr->q_uid;
egid = ctladdr->q_gid;
user = ctladdr->q_user;
}
else if (bitset(SFF_RUNASREALUID, flags))
{
euid = RealUid;
egid = RealGid;
user = RealUserName;
}
else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags))
{
euid = FileMailer->m_uid;
egid = FileMailer->m_gid;
user = NULL;
}
else
{
euid = egid = 0;
user = NULL;
}
if (!bitset(SFF_ROOTOK, flags))
{
if (euid == 0)
{
euid = DefUid;
user = DefUser;
}
if (egid == 0)
egid = DefGid;
}
if (geteuid() == 0 &&
(ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags)))
flags |= SFF_SETUIDOK;
if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))
flags |= SFF_NOSLINK;
if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))
flags |= SFF_NOHLINK;
errno = safefile(filename, euid, egid, user, 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;
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;
volatile bool maxreached = FALSE;
register ADDRESS *ca;
volatile uid_t saveduid;
volatile gid_t savedgid;
volatile uid_t uid;
volatile gid_t gid;
char *volatile user;
int rval = 0;
volatile long sfflags = SFF_REGONLY;
register char *p;
bool safechown = FALSE;
volatile bool safedir = FALSE;
struct stat st;
char buf[MAXLINE];
if (tTd(27, 2))
dprintf("include(%s)\n", fname);
if (tTd(27, 4))
dprintf(" ruid=%d euid=%d\n",
(int) getuid(), (int) geteuid());
if (tTd(27, 14))
{
dprintf("ctladdr ");
printaddr(ctladdr, FALSE);
}
if (tTd(27, 9))
dprintf("include: old uid = %d/%d\n",
(int) getuid(), (int) geteuid());
#if _FFR_UNSAFE_WRITABLE_INCLUDE
if (forwarding)
{
if (!bitnset(DBS_GROUPWRITABLEFORWARDFILE, DontBlameSendmail))
sfflags |= SFF_NOGWFILES;
if (!bitnset(DBS_WORLDWRITABLEFORWARDFILE, DontBlameSendmail))
sfflags |= SFF_NOWWFILES;
}
else
{
if (!bitnset(DBS_GROUPWRITABLEINCLUDEFILE, DontBlameSendmail))
sfflags |= SFF_NOGWFILES;
if (!bitnset(DBS_WORLDWRITABLEINCLUDEFILE, DontBlameSendmail))
sfflags |= SFF_NOWWFILES;
}
#endif /* _FFR_UNSAFE_WRITABLE_INCLUDE */
if (forwarding)
sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOWLINK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -