📄 usersmtp.c
字号:
/*
* Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
* 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.
*
*/
#include <sendmail.h>
#ifndef lint
# if SMTP
static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.13 2000/09/26 00:46:21 gshapiro Exp $ (with SMTP)";
# else /* SMTP */
static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.13 2000/09/26 00:46:21 gshapiro Exp $ (without SMTP)";
# endif /* SMTP */
#endif /* ! lint */
#include <sysexits.h>
#if SMTP
static void datatimeout __P((void));
static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
/*
** USERSMTP -- run SMTP protocol from the user end.
**
** This protocol is described in RFC821.
*/
# define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
# define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
# define SMTPCLOSING 421 /* "Service Shutting Down" */
#define ENHSCN(e, d) (e) == NULL ? (d) : newstr(e)
static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
static bool SmtpNeedIntro; /* need "while talking" in transcript */
/*
** SMTPINIT -- initialize SMTP.
**
** Opens the connection and sends the initial protocol.
**
** Parameters:
** m -- mailer to create connection to.
** mci -- the mailer connection info.
** e -- the envelope.
** onlyhelo -- send only helo command?
**
** Returns:
** none.
**
** Side Effects:
** creates connection and sends initial protocol.
*/
void
smtpinit(m, mci, e, onlyhelo)
MAILER *m;
register MCI *mci;
ENVELOPE *e;
bool onlyhelo;
{
register int r;
register char *p;
register char *hn;
char *enhsc;
enhsc = NULL;
if (tTd(18, 1))
{
dprintf("smtpinit ");
mci_dump(mci, FALSE);
}
/*
** Open the connection to the mailer.
*/
SmtpError[0] = '\0';
CurHostName = mci->mci_host; /* XXX UGLY XXX */
if (CurHostName == NULL)
CurHostName = MyHostName;
SmtpNeedIntro = TRUE;
switch (mci->mci_state)
{
case MCIS_ACTIVE:
/* need to clear old information */
smtprset(m, mci, e);
/* FALLTHROUGH */
case MCIS_OPEN:
if (!onlyhelo)
return;
break;
case MCIS_ERROR:
case MCIS_QUITING:
case MCIS_SSD:
/* shouldn't happen */
smtpquit(m, mci, e);
/* FALLTHROUGH */
case MCIS_CLOSED:
syserr("451 4.4.0 smtpinit: state CLOSED");
return;
case MCIS_OPENING:
break;
}
if (onlyhelo)
goto helo;
mci->mci_state = MCIS_OPENING;
/*
** Get the greeting message.
** This should appear spontaneously. Give it five minutes to
** happen.
*/
SmtpPhase = mci->mci_phase = "client greeting";
sm_setproctitle(TRUE, e, "%s %s: %s",
qid_printname(e), CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
if (r < 0)
goto tempfail1;
if (REPLYTYPE(r) == 4)
goto tempfail2;
if (REPLYTYPE(r) != 2)
goto unavailable;
/*
** Send the HELO command.
** My mother taught me to always introduce myself.
*/
helo:
if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
mci->mci_flags |= MCIF_ESMTP;
hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
tryhelo:
if (bitnset(M_LMTP, m->m_flags))
{
smtpmessage("LHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client LHLO";
}
else if (bitset(MCIF_ESMTP, mci->mci_flags))
{
smtpmessage("EHLO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client EHLO";
}
else
{
smtpmessage("HELO %s", m, mci, hn);
SmtpPhase = mci->mci_phase = "client HELO";
}
sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
CurHostName, mci->mci_phase);
r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
if (r < 0)
goto tempfail1;
else if (REPLYTYPE(r) == 5)
{
if (bitset(MCIF_ESMTP, mci->mci_flags) &&
!bitnset(M_LMTP, m->m_flags))
{
/* try old SMTP instead */
mci->mci_flags &= ~MCIF_ESMTP;
goto tryhelo;
}
goto unavailable;
}
else if (REPLYTYPE(r) != 2)
goto tempfail2;
/*
** Check to see if we actually ended up talking to ourself.
** This means we didn't know about an alias or MX, or we managed
** to connect to an echo server.
*/
p = strchr(&SmtpReplyBuffer[4], ' ');
if (p != NULL)
*p = '\0';
if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
!bitnset(M_LMTP, m->m_flags) &&
strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
{
syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
CurHostName);
mci_setstat(mci, EX_CONFIG, "5.3.5", "system config error");
mci->mci_errno = 0;
smtpquit(m, mci, e);
return;
}
/*
** If this is expected to be another sendmail, send some internal
** commands.
*/
if (bitnset(M_INTERNAL, m->m_flags))
{
/* tell it to be verbose */
smtpmessage("VERB", m, mci);
r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
if (r < 0)
goto tempfail1;
}
if (mci->mci_state != MCIS_CLOSED)
{
mci->mci_state = MCIS_OPEN;
return;
}
/* got a 421 error code during startup */
tempfail1:
if (mci->mci_errno == 0)
mci->mci_errno = errno;
mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
if (mci->mci_state != MCIS_CLOSED)
smtpquit(m, mci, e);
return;
tempfail2:
if (mci->mci_errno == 0)
mci->mci_errno = errno;
/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
SmtpReplyBuffer);
if (mci->mci_state != MCIS_CLOSED)
smtpquit(m, mci, e);
return;
unavailable:
mci->mci_errno = errno;
mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
smtpquit(m, mci, e);
return;
}
/*
** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
**
** Parameters:
** line -- the response line.
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
** e -- the envelope.
**
** Returns:
** none.
*/
static void
esmtp_check(line, firstline, m, mci, e)
char *line;
bool firstline;
MAILER *m;
register MCI *mci;
ENVELOPE *e;
{
if (strstr(line, "ESMTP") != NULL)
mci->mci_flags |= MCIF_ESMTP;
if (strstr(line, "8BIT-OK") != NULL)
mci->mci_flags |= MCIF_8BITOK;
}
# if SASL
/*
** STR_UNION -- create the union of two lists
**
** Parameters:
** s1, s2 -- lists of items (separated by single blanks).
**
** Returns:
** the union of both lists.
*/
static char *
str_union(s1, s2)
char *s1, *s2;
{
char *hr, *h1, *h, *res;
int l1, l2, rl;
if (s1 == NULL || *s1 == '\0')
return s2;
if (s2 == NULL || *s2 == '\0')
return s1;
l1 = strlen(s1);
l2 = strlen(s2);
rl = l1 + l2;
res = (char *)malloc(rl + 2);
if (res == NULL)
{
if (l1 > l2)
return s1;
return s2;
}
(void) strlcpy(res, s1, rl);
hr = res;
h1 = s2;
h = s2;
/* walk through s2 */
while (h != NULL && *h1 != '\0')
{
/* is there something after the current word? */
if ((h = strchr(h1, ' ')) != NULL)
*h = '\0';
l1 = strlen(h1);
/* does the current word appear in s1 ? */
if (iteminlist(h1, s1, " ") == NULL)
{
/* add space as delimiter */
*hr++ = ' ';
/* copy the item */
memcpy(hr, h1, l1);
/* advance pointer in result list */
hr += l1;
*hr = '\0';
}
if (h != NULL)
{
/* there are more items */
*h = ' ';
h1 = h + 1;
}
}
return res;
}
# endif /* SASL */
/*
** HELO_OPTIONS -- process the options on a HELO line.
**
** Parameters:
** line -- the response line.
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
** e -- the envelope.
**
** Returns:
** none.
*/
static void
helo_options(line, firstline, m, mci, e)
char *line;
bool firstline;
MAILER *m;
register MCI *mci;
ENVELOPE *e;
{
register char *p;
if (firstline)
{
# if SASL
if (mci->mci_saslcap != NULL)
free(mci->mci_saslcap);
mci->mci_saslcap = NULL;
# endif /* SASL */
return;
}
if (strlen(line) < (SIZE_T) 5)
return;
line += 4;
p = strpbrk(line, " =");
if (p != NULL)
*p++ = '\0';
if (strcasecmp(line, "size") == 0)
{
mci->mci_flags |= MCIF_SIZE;
if (p != NULL)
mci->mci_maxsize = atol(p);
}
else if (strcasecmp(line, "8bitmime") == 0)
{
mci->mci_flags |= MCIF_8BITMIME;
mci->mci_flags &= ~MCIF_7BIT;
}
else if (strcasecmp(line, "expn") == 0)
mci->mci_flags |= MCIF_EXPN;
else if (strcasecmp(line, "dsn") == 0)
mci->mci_flags |= MCIF_DSN;
else if (strcasecmp(line, "enhancedstatuscodes") == 0)
mci->mci_flags |= MCIF_ENHSTAT;
# if STARTTLS
else if (strcasecmp(line, "starttls") == 0)
mci->mci_flags |= MCIF_TLS;
# endif /* STARTTLS */
# if SASL
else if (strcasecmp(line, "auth") == 0)
{
if (p != NULL && *p != '\0')
{
if (mci->mci_saslcap != NULL)
{
char *h;
/*
** create the union with previous auth
** offerings because we recognize "auth "
** and "auth=" (old format).
*/
h = mci->mci_saslcap;
mci->mci_saslcap = str_union(h, p);
if (h != mci->mci_saslcap)
free(h);
mci->mci_flags |= MCIF_AUTH;
}
else
{
int l;
l = strlen(p) + 1;
mci->mci_saslcap = (char *)malloc(l);
/* XXX this may be leaked */
if (mci->mci_saslcap != NULL)
{
(void) strlcpy(mci->mci_saslcap, p, l);
mci->mci_flags |= MCIF_AUTH;
}
}
}
}
# endif /* SASL */
}
# if SASL
/*
** GETSASLDATA -- process the challenges from the SASL protocol
**
** This gets the relevant sasl response data out of the reply
** from the server
**
** Parameters:
** line -- the response line.
** firstline -- set if this is the first line of the reply.
** m -- the mailer.
** mci -- the mailer connection info.
** e -- the envelope.
**
** Returns:
** none.
*/
void
getsasldata(line, firstline, m, mci, e)
char *line;
bool firstline;
MAILER *m;
register MCI *mci;
ENVELOPE *e;
{
int len;
char *out;
int result;
/* if not a continue we don't care about it */
if ((strlen(line) <= 4) ||
(line[0] != '3') ||
(line[1] != '3') ||
(line[2] != '4'))
{
mci->mci_sasl_string = NULL;
return;
}
/* forget about "334 " */
line += 4;
len = strlen(line);
out = xalloc(len + 1);
result = sasl_decode64(line, len, out, (u_int *)&len);
if (result != SASL_OK)
{
len = 0;
*out = '\0';
}
if (mci->mci_sasl_string != NULL)
{
if (mci->mci_sasl_string_len <= len)
{
free(mci->mci_sasl_string);
mci->mci_sasl_string = xalloc(len + 1);
}
}
else
mci->mci_sasl_string = xalloc(len + 1);
/* XXX this is probably leaked */
memcpy(mci->mci_sasl_string, out, len);
mci->mci_sasl_string[len] = '\0';
mci->mci_sasl_string_len = len;
free(out);
return;
}
/*
** READAUTH -- read auth value from a file
**
** Parameters:
** l -- line to define.
** filename -- name of file to read.
** safe -- if set, this is a safe read.
**
** Returns:
** line from file
**
** Side Effects:
** overwrites local static buffer. The caller should copy
** the result.
**
*/
/* lines in authinfo file */
# define SASL_USER 1
# define SASL_AUTHID 2
# define SASL_PASSWORD 3
# define SASL_DEFREALM 4
# define SASL_MECH 5
static char *sasl_info_name[] =
{
"",
"user id",
"authorization id",
"password",
"realm",
"mechanism"
};
static char *
readauth(l, filename, safe)
int l;
char *filename;
bool safe;
{
FILE *f;
long sff;
pid_t pid;
int lc;
static char buf[MAXLINE];
if (filename == NULL || filename[0] == '\0')
return "";
#if !_FFR_ALLOW_SASLINFO
/*
** make sure we don't use a program that is not
** accesible to the user who specified a different authinfo file.
** However, currently we don't pass this info (authinfo file
** specified by user) around, so we just turn off program access.
*/
if (filename[0] == '|')
{
auto int fd;
int i;
char *p;
char *argv[MAXPV + 1];
i = 0;
for (p = strtok(&filename[1], " \t"); p != NULL;
p = strtok(NULL, " \t"))
{
if (i >= MAXPV)
break;
argv[i++] = p;
}
argv[i] = NULL;
pid = prog_open(argv, &fd, CurEnv);
if (pid < 0)
f = NULL;
else
f = fdopen(fd, "r");
}
else
#endif /* !_FFR_ALLOW_SASLINFO */
{
pid = -1;
sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
| SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
if (DontLockReadFiles)
sff |= SFF_NOLOCK;
#if _FFR_ALLOW_SASLINFO
/*
** XXX: make sure we don't read or open files that are not
** accesible to the user who specified a different authinfo
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -