📄 srvrsmtp.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: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (with SMTP)";
# else /* SMTP */
static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (without SMTP)";
# endif /* SMTP */
#endif /* ! lint */
#if SMTP
# if SASL || STARTTLS
# include "sfsasl.h"
# endif /* SASL || STARTTLS */
# if SASL
# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
static int saslmechs __P((sasl_conn_t *, char **));
# endif /* SASL */
# if STARTTLS
# include <sysexits.h>
# include <openssl/err.h>
# include <openssl/bio.h>
# include <openssl/pem.h>
# ifndef HASURANDOMDEV
# include <openssl/rand.h>
# endif /* !HASURANDOMDEV */
static SSL *srv_ssl = NULL;
static SSL_CTX *srv_ctx = NULL;
# if !TLS_NO_RSA
static RSA *rsa = NULL;
# endif /* !TLS_NO_RSA */
static bool tls_ok = FALSE;
static int tls_verify_cb __P((X509_STORE_CTX *));
# if !TLS_NO_RSA
# define RSA_KEYLENGTH 512
# endif /* !TLS_NO_RSA */
# endif /* STARTTLS */
static time_t checksmtpattack __P((volatile int *, int, bool,
char *, ENVELOPE *));
static void mail_esmtp_args __P((char *, char *, ENVELOPE *));
static void printvrfyaddr __P((ADDRESS *, bool, bool));
static void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
static int runinchild __P((char *, ENVELOPE *));
static char *skipword __P((char *volatile, char *));
extern ENVELOPE BlankEnvelope;
/*
** SMTP -- run the SMTP protocol.
**
** Parameters:
** nullserver -- if non-NULL, rejection message for
** all SMTP commands.
** e -- the envelope.
**
** Returns:
** never.
**
** Side Effects:
** Reads commands from the input channel and processes
** them.
*/
struct cmd
{
char *cmd_name; /* command name */
int cmd_code; /* internal code, see below */
};
/* values for cmd_code */
# define CMDERROR 0 /* bad command */
# define CMDMAIL 1 /* mail -- designate sender */
# define CMDRCPT 2 /* rcpt -- designate recipient */
# define CMDDATA 3 /* data -- send message text */
# define CMDRSET 4 /* rset -- reset state */
# define CMDVRFY 5 /* vrfy -- verify address */
# define CMDEXPN 6 /* expn -- expand address */
# define CMDNOOP 7 /* noop -- do nothing */
# define CMDQUIT 8 /* quit -- close connection and die */
# define CMDHELO 9 /* helo -- be polite */
# define CMDHELP 10 /* help -- give usage info */
# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
# define CMDETRN 12 /* etrn -- flush queue */
# if SASL
# define CMDAUTH 13 /* auth -- SASL authenticate */
# endif /* SASL */
# if STARTTLS
# define CMDSTLS 14 /* STARTTLS -- start TLS session */
# endif /* STARTTLS */
/* non-standard commands */
# define CMDONEX 16 /* onex -- sending one transaction only */
# define CMDVERB 17 /* verb -- go into verbose mode */
# define CMDXUSR 18 /* xusr -- initial (user) submission */
/* unimplemented commands from RFC 821 */
# define CMDUNIMPL 19 /* unimplemented rfc821 commands */
/* use this to catch and log "door handle" attempts on your system */
# define CMDLOGBOGUS 23 /* bogus command that should be logged */
/* debugging-only commands, only enabled if SMTPDEBUG is defined */
# define CMDDBGQSHOW 24 /* showq -- show send queue */
# define CMDDBGDEBUG 25 /* debug -- set debug mode */
/*
** Note: If you change this list,
** remember to update 'helpfile'
*/
static struct cmd CmdTab[] =
{
{ "mail", CMDMAIL },
{ "rcpt", CMDRCPT },
{ "data", CMDDATA },
{ "rset", CMDRSET },
{ "vrfy", CMDVRFY },
{ "expn", CMDEXPN },
{ "help", CMDHELP },
{ "noop", CMDNOOP },
{ "quit", CMDQUIT },
{ "helo", CMDHELO },
{ "ehlo", CMDEHLO },
{ "etrn", CMDETRN },
{ "verb", CMDVERB },
{ "onex", CMDONEX },
{ "xusr", CMDXUSR },
{ "send", CMDUNIMPL },
{ "saml", CMDUNIMPL },
{ "soml", CMDUNIMPL },
{ "turn", CMDUNIMPL },
# if SASL
{ "auth", CMDAUTH, },
# endif /* SASL */
# if STARTTLS
{ "starttls", CMDSTLS, },
# endif /* STARTTLS */
/* remaining commands are here only to trap and log attempts to use them */
{ "showq", CMDDBGQSHOW },
{ "debug", CMDDBGDEBUG },
{ "wiz", CMDLOGBOGUS },
{ NULL, CMDERROR }
};
static bool OneXact = FALSE; /* one xaction only this run */
static char *CurSmtpClient; /* who's at the other end of channel */
# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
/* runinchild() returns */
# define RIC_INCHILD 0 /* in a child process */
# define RIC_INPARENT 1 /* still in parent process */
# define RIC_TEMPFAIL 2 /* temporary failure occurred */
void
smtp(nullserver, d_flags, e)
char *volatile nullserver;
BITMAP256 d_flags;
register ENVELOPE *volatile e;
{
register char *volatile p;
register struct cmd *volatile c = NULL;
char *cmd;
auto ADDRESS *vrfyqueue;
ADDRESS *a;
volatile bool gotmail; /* mail command received */
volatile bool gothello; /* helo command received */
bool vrfy; /* set if this is a vrfy command */
char *volatile protocol; /* sending protocol */
char *volatile sendinghost; /* sending hostname */
char *volatile peerhostname; /* name of SMTP peer or "localhost" */
auto char *delimptr;
char *id;
volatile int nrcpts = 0; /* number of RCPT commands */
int ric;
bool doublequeue;
volatile bool discard;
volatile int badcommands = 0; /* count of bad commands */
volatile int nverifies = 0; /* count of VRFY/EXPN commands */
volatile int n_etrn = 0; /* count of ETRN commands */
volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */
volatile int n_helo = 0; /* count of HELO/EHLO commands */
volatile int delay = 1; /* timeout for bad commands */
bool ok;
volatile bool tempfail = FALSE;
# if _FFR_MILTER
volatile bool milterize = (nullserver == NULL);
# endif /* _FFR_MILTER */
volatile time_t wt; /* timeout after too many commands */
volatile time_t previous; /* time after checksmtpattack() */
volatile bool lognullconnection = TRUE;
register char *q;
char *addr;
char *greetcode = "220";
QUEUE_CHAR *new;
int argno;
char *args[MAXSMTPARGS];
char inp[MAXLINE];
char cmdbuf[MAXLINE];
# if SASL
sasl_conn_t *conn;
volatile bool sasl_ok;
volatile int n_auth = 0; /* count of AUTH commands */
bool ismore;
int result;
volatile int authenticating;
char *hostname;
char *user;
char *in, *out, *out2;
const char *errstr;
int inlen, out2len;
unsigned int outlen;
char *volatile auth_type;
char *mechlist;
volatile int n_mechs;
int len;
sasl_security_properties_t ssp;
sasl_external_properties_t ext_ssf;
# if SFIO
sasl_ssf_t *ssf;
# endif /* SFIO */
# endif /* SASL */
# if STARTTLS
int r;
int rfd, wfd;
volatile bool usetls = TRUE;
volatile bool tls_active = FALSE;
bool saveQuickAbort;
bool saveSuprErrs;
# endif /* STARTTLS */
if (fileno(OutChannel) != fileno(stdout))
{
/* arrange for debugging output to go to remote host */
(void) dup2(fileno(OutChannel), fileno(stdout));
}
settime(e);
(void)sm_getla(e);
peerhostname = RealHostName;
if (peerhostname == NULL)
peerhostname = "localhost";
CurHostName = peerhostname;
CurSmtpClient = macvalue('_', e);
if (CurSmtpClient == NULL)
CurSmtpClient = CurHostName;
/* check_relay may have set discard bit, save for later */
discard = bitset(EF_DISCARD, e->e_flags);
sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient);
# if SASL
sasl_ok = FALSE; /* SASL can't be used (yet) */
n_mechs = 0;
/* SASL server new connection */
hostname = macvalue('j', e);
# if SASL > 10505
/* use empty realm: only works in SASL > 1.5.5 */
result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
# else /* SASL > 10505 */
/* use no realm -> realm is set to hostname by SASL lib */
result = sasl_server_new("smtp", hostname, NULL, NULL, 0, &conn);
# endif /* SASL > 10505 */
if (result == SASL_OK)
{
sasl_ok = TRUE;
/*
** SASL set properties for sasl
** set local/remote IP
** XXX only IPv4: Cyrus SASL doesn't support anything else
**
** XXX where exactly are these used/required?
** Kerberos_v4
*/
# if NETINET
in = macvalue(macid("{daemon_family}", NULL), e);
if (in != NULL && strcmp(in, "inet") == 0)
{
SOCKADDR_LEN_T addrsize;
struct sockaddr_in saddr_l;
struct sockaddr_in saddr_r;
addrsize = sizeof(struct sockaddr_in);
if (getpeername(fileno(InChannel),
(struct sockaddr *)&saddr_r,
&addrsize) == 0)
{
sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
addrsize = sizeof(struct sockaddr_in);
if (getsockname(fileno(InChannel),
(struct sockaddr *)&saddr_l,
&addrsize) == 0)
sasl_setprop(conn, SASL_IP_LOCAL,
&saddr_l);
}
}
# endif /* NETINET */
authenticating = SASL_NOT_AUTH;
auth_type = NULL;
mechlist = NULL;
user = NULL;
# if 0
define(macid("{auth_author}", NULL), NULL, &BlankEnvelope);
# endif /* 0 */
/* set properties */
(void) memset(&ssp, '\0', sizeof ssp);
# if SFIO
/* XXX should these be options settable via .cf ? */
/* ssp.min_ssf = 0; is default due to memset() */
{
ssp.max_ssf = INT_MAX;
ssp.maxbufsize = MAXOUTLEN;
}
# endif /* SFIO */
# if _FFR_SASL_OPTS
ssp.security_flags = SASLOpts & SASL_SEC_MASK;
# endif /* _FFR_SASL_OPTS */
sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
if (sasl_ok)
{
/*
** external security strength factor;
** we have none so zero
# if STARTTLS
** we may have to change this for STARTTLS
** (dynamically)
# endif
*/
ext_ssf.ssf = 0;
ext_ssf.auth_id = NULL;
sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
&ext_ssf) == SASL_OK;
}
if (sasl_ok)
{
n_mechs = saslmechs(conn, &mechlist);
sasl_ok = n_mechs > 0;
}
}
else
{
if (LogLevel > 9)
sm_syslog(LOG_WARNING, NOQID,
"SASL error: sasl_server_new failed=%d",
result);
}
# endif /* SASL */
# if STARTTLS
# if _FFR_TLS_O_T
saveQuickAbort = QuickAbort;
saveSuprErrs = SuprErrs;
SuprErrs = TRUE;
QuickAbort = FALSE;
if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8) != EX_OK
|| Errors > 0)
usetls = FALSE;
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
# endif /* _FFR_TLS_O_T */
# endif /* STARTTLS */
# if _FFR_MILTER
if (milterize)
{
char state;
/* initialize mail filter connection */
milter_init(e, &state);
switch (state)
{
case SMFIR_REJECT:
greetcode = "554";
nullserver = "Command rejected";
milterize = FALSE;
break;
case SMFIR_TEMPFAIL:
tempfail = TRUE;
milterize = FALSE;
break;
}
}
if (milterize && !bitset(EF_DISCARD, e->e_flags))
{
char state;
(void) milter_connect(peerhostname, RealHostAddr,
e, &state);
switch (state)
{
case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
case SMFIR_REJECT:
greetcode = "554";
nullserver = "Command rejected";
milterize = FALSE;
break;
case SMFIR_TEMPFAIL:
tempfail = TRUE;
milterize = FALSE;
break;
}
}
# endif /* _FFR_MILTER */
/* output the first line, inserting "ESMTP" as second word */
expand(SmtpGreeting, inp, sizeof inp, e);
p = strchr(inp, '\n');
if (p != NULL)
*p++ = '\0';
id = strchr(inp, ' ');
if (id == NULL)
id = &inp[strlen(inp)];
if (p == NULL)
snprintf(cmdbuf, sizeof cmdbuf,
"%s %%.*s ESMTP%%s", greetcode);
else
snprintf(cmdbuf, sizeof cmdbuf,
"%s-%%.*s ESMTP%%s", greetcode);
message(cmdbuf, (int) (id - inp), inp, id);
/* output remaining lines */
while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
{
*p++ = '\0';
if (isascii(*id) && isspace(*id))
id++;
(void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode);
message(cmdbuf, id);
}
if (id != NULL)
{
if (isascii(*id) && isspace(*id))
id++;
(void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode);
message(cmdbuf, id);
}
protocol = NULL;
sendinghost = macvalue('s', e);
gothello = FALSE;
gotmail = FALSE;
for (;;)
{
/* arrange for backout */
(void) setjmp(TopFrame);
QuickAbort = FALSE;
HoldErrs = FALSE;
SuprErrs = FALSE;
LogUsrErrs = FALSE;
OnlyOneError = TRUE;
e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
/* setup for the read */
e->e_to = NULL;
Errors = 0;
FileName = NULL;
(void) fflush(stdout);
/* read the input line */
SmtpPhase = "server cmd read";
sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient);
# if SASL
/*
** SMTP AUTH requires accepting any length,
** at least for challenge/response
** XXX
*/
# endif /* SASL */
/* handle errors */
if (ferror(OutChannel) ||
(p = sfgets(inp, sizeof inp, InChannel,
TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
{
char *d;
d = macvalue(macid("{daemon_name}", NULL), e);
if (d == NULL)
d = "stdin";
/* end of file, just die */
disconnect(1, e);
# if _FFR_MILTER
/* close out milter filters */
milter_quit(e);
# endif /* _FFR_MILTER */
message("421 4.4.1 %s Lost input channel from %s",
MyHostName, CurSmtpClient);
if (LogLevel > (gotmail ? 1 : 19))
sm_syslog(LOG_NOTICE, e->e_id,
"lost input channel from %.100s to %s after %s",
CurSmtpClient, d,
(c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
/*
** If have not accepted mail (DATA), do not bounce
** bad addresses back to sender.
*/
if (bitset(EF_CLRQUEUE, e->e_flags))
e->e_sendqueue = NULL;
goto doquit;
}
/* clean up end of line */
fixcrlf(inp, TRUE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -