summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src/srvrsmtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/src/srvrsmtp.c')
-rw-r--r--contrib/sendmail/src/srvrsmtp.c620
1 files changed, 423 insertions, 197 deletions
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
index e752aa0..cb0366e 100644
--- a/contrib/sendmail/src/srvrsmtp.c
+++ b/contrib/sendmail/src/srvrsmtp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -13,13 +13,16 @@
#include <sendmail.h>
#if MILTER
+# include <libmilter/mfapi.h>
# include <libmilter/mfdef.h>
#endif /* MILTER */
-SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.829.2.34 2004/01/14 19:13:46 ca Exp $")
+SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.900 2004/07/08 23:29:33 ca Exp $")
+
+#include <sys/time.h>
+#include <sm/fdset.h>
#if SASL || STARTTLS
-# include <sys/time.h>
# include "sfsasl.h"
#endif /* SASL || STARTTLS */
#if SASL
@@ -56,12 +59,14 @@ extern void tls_set_verify __P((SSL_CTX *, SSL *, bool));
# endif /* _FFR_NO_PIPE */
#endif /* PIPELINING */
#define SRV_REQ_AUTH 0x0400 /* require AUTH */
+#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */
#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
-static time_t checksmtpattack __P((volatile unsigned int *, int, bool,
- char *, ENVELOPE *));
+#define STOP_ATTACK ((time_t) -1)
+static time_t checksmtpattack __P((volatile unsigned int *, unsigned 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 *));
@@ -75,7 +80,7 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
char *_auth_id, sasl_ssf_t *_ext_ssf));
# define RESET_SASLCONN \
- result = reset_saslconn(&conn, hostname, remoteip, localip, auth_id, \
+ result = reset_saslconn(&conn, AuthRealm, remoteip, localip, auth_id, \
&ext_ssf); \
if (result != SASL_OK) \
{ \
@@ -89,7 +94,7 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
struct sockaddr_in *_saddr_l,
sasl_external_properties_t *_ext_ssf));
# define RESET_SASLCONN \
- result = reset_saslconn(&conn, hostname, &saddr_r, &saddr_l, &ext_ssf); \
+ result = reset_saslconn(&conn, AuthRealm, &saddr_r, &saddr_l, &ext_ssf); \
if (result != SASL_OK) \
{ \
/* This is pretty fatal */ \
@@ -101,6 +106,16 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
extern ENVELOPE BlankEnvelope;
+#define NBADRCPTS \
+ do \
+ { \
+ char buf[16]; \
+ (void) sm_snprintf(buf, sizeof buf, "%d", \
+ BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
+ ? n_badrcpts - 1 : n_badrcpts); \
+ macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
+ } while (0)
+
#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
(s)++
@@ -221,6 +236,31 @@ static char *CurSmtpClient; /* who's at the other end of channel */
# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
#endif /* ! MAXTIMEOUT */
+/*
+** Maximum shift value to compute timeout for bad commands.
+** This introduces an upper limit of 2^MAXSHIFT for the timeout.
+*/
+
+#ifndef MAXSHIFT
+# define MAXSHIFT 8
+#endif /* ! MAXSHIFT */
+#if MAXSHIFT > 31
+ ERROR _MAXSHIFT > 31 is invalid
+#endif /* MAXSHIFT */
+
+
+#if MAXBADCOMMANDS > 0
+# define STOP_IF_ATTACK(r) do \
+ { \
+ if ((r) == STOP_ATTACK) \
+ goto stopattack; \
+ } while (0)
+
+#else /* MAXBADCOMMANDS > 0 */
+# define STOP_IF_ATTACK(r) r
+#endif /* MAXBADCOMMANDS > 0 */
+
+
#if SM_HEAP_CHECK
static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
@@ -230,38 +270,21 @@ typedef struct
{
bool sm_gotmail; /* mail command received */
unsigned int sm_nrcpts; /* number of successful RCPT commands */
-#if _FFR_ADAPTIVE_EOL
-WARNING: do NOT use this FFR, it is most likely broken
- bool sm_crlf; /* input in CRLF form? */
-#endif /* _FFR_ADAPTIVE_EOL */
bool sm_discard;
#if MILTER
bool sm_milterize;
bool sm_milterlist; /* any filters in the list? */
#endif /* MILTER */
-#if _FFR_QUARANTINE
char *sm_quarmsg; /* carry quarantining across messages */
-#endif /* _FFR_QUARANTINE */
} SMTP_T;
-static void smtp_data __P((SMTP_T *, ENVELOPE *));
+static bool smtp_data __P((SMTP_T *, ENVELOPE *));
-#define MSG_TEMPFAIL "451 4.7.1 Please try again later"
+#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
#if MILTER
# define MILTER_ABORT(e) milter_abort((e))
-#if _FFR_MILTER_421
-# define MILTER_SHUTDOWN \
- if (strncmp(response, "421 ", 4) == 0) \
- { \
- e->e_sendqueue = NULL; \
- goto doquit; \
- }
-#else /* _FFR_MILTER_421 */
-# define MILTER_SHUTDOWN
-#endif /* _FFR_MILTER_421 */
-
# define MILTER_REPLY(str) \
{ \
int savelogusrerrs = LogUsrErrs; \
@@ -276,8 +299,18 @@ static void smtp_data __P((SMTP_T *, ENVELOPE *));
str, addr, response); \
LogUsrErrs = false; \
} \
- usrerr(response); \
- MILTER_SHUTDOWN \
+ if (strncmp(response, "421 ", 4) == 0) \
+ { \
+ bool tsave = QuickAbort; \
+ \
+ QuickAbort = false; \
+ usrerr(response); \
+ QuickAbort = tsave; \
+ e->e_sendqueue = NULL; \
+ goto doquit; \
+ } \
+ else \
+ usrerr(response); \
break; \
\
case SMFIR_REJECT: \
@@ -321,6 +354,7 @@ static void smtp_data __P((SMTP_T *, ENVELOPE *));
/* clear all SMTP state (for HELO/EHLO/RSET) */
#define CLEAR_STATE(cmd) \
+do \
{ \
/* abort milter filters */ \
MILTER_ABORT(e); \
@@ -347,7 +381,26 @@ static void smtp_data __P((SMTP_T *, ENVELOPE *));
sm_rpool_free(e->e_rpool); \
e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
CurEnv = e; \
-}
+ \
+ /* put back discard bit */ \
+ if (smtp.sm_discard) \
+ e->e_flags |= EF_DISCARD; \
+ \
+ /* restore connection quarantining */ \
+ if (smtp.sm_quarmsg == NULL) \
+ { \
+ e->e_quarmsg = NULL; \
+ macdefine(&e->e_macro, A_PERM, \
+ macid("{quarantine}"), ""); \
+ } \
+ else \
+ { \
+ e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
+ smtp.sm_quarmsg); \
+ macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
+ e->e_quarmsg); \
+ } \
+} while (0)
/* sleep to flatten out connection load */
#define MIN_DELAY_LOG 15 /* wait before logging this again */
@@ -401,9 +454,9 @@ smtp(nullserver, d_flags, e)
volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
volatile int save_sevenbitinput;
bool ok;
-#if _FFR_BLOCK_PROXIES || _FFR_ADAPTIVE_EOL
+#if _FFR_BLOCK_PROXIES
volatile bool first;
-#endif /* _FFR_BLOCK_PROXIES || _FFR_ADAPTIVE_EOL */
+#endif /* _FFR_BLOCK_PROXIES */
volatile bool tempfail = false;
volatile time_t wt; /* timeout after too many commands */
volatile time_t previous; /* time after checksmtpattack() */
@@ -448,13 +501,12 @@ smtp(nullserver, d_flags, e)
volatile unsigned int n_mechs;
unsigned int len;
#endif /* SASL */
-#if STARTTLS
int r;
+#if STARTTLS
+ int fdfl;
int rfd, wfd;
volatile bool tls_active = false;
-# if _FFR_SMTP_SSL
- volatile bool smtps = false;
-# endif /* _FFR_SMTP_SSL */
+ volatile bool smtps = bitnset(D_SMTPS, d_flags);
bool saveQuickAbort;
bool saveSuprErrs;
time_t tlsstart;
@@ -521,6 +573,8 @@ smtp(nullserver, d_flags, e)
: SRV_OFFER_DSN)
#if SASL
| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
+ | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
+ : SRV_NONE)
#endif /* SASL */
#if PIPELINING
| SRV_OFFER_PIPE
@@ -542,19 +596,35 @@ smtp(nullserver, d_flags, e)
CurSmtpClient);
nullserver = "450 4.3.0 Please try again later.";
}
+ else
+ {
#if PIPELINING
# if _FFR_NO_PIPE
- else if (bitset(SRV_NO_PIPE, features))
- {
- /* for consistency */
- features &= ~SRV_OFFER_PIPE;
- }
+ if (bitset(SRV_NO_PIPE, features))
+ {
+ /* for consistency */
+ features &= ~SRV_OFFER_PIPE;
+ }
# endif /* _FFR_NO_PIPE */
#endif /* PIPELINING */
+#if SASL
+ if (bitset(SRV_REQ_SEC, features))
+ SASLOpts |= SASL_SEC_NOPLAINTEXT;
+ else
+ SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
+#endif /* SASL */
+ }
+ }
+ else if (strncmp(nullserver, "421 ", 4) == 0)
+ {
+ message(nullserver);
+ goto doquit;
}
hostname = macvalue('j', e);
#if SASL
+ if (AuthRealm == NULL)
+ AuthRealm = hostname;
sasl_ok = bitset(SRV_OFFER_AUTH, features);
n_mechs = 0;
authenticating = SASL_NOT_AUTH;
@@ -563,14 +633,14 @@ smtp(nullserver, d_flags, e)
if (sasl_ok)
{
# if SASL >= 20000
- result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
+ result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
NULL, 0, &conn);
# elif SASL > 10505
/* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
+ result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
# else /* SASL >= 20000 */
/* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
+ result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
&conn);
# endif /* SASL >= 20000 */
sasl_ok = result == SASL_OK;
@@ -775,7 +845,6 @@ smtp(nullserver, d_flags, e)
smtp.sm_milterize = false;
break;
-#if _FFR_MILTER_421
case SMFIR_SHUTDOWN:
if (MilterLogLevel > 3)
sm_syslog(LOG_INFO, e->e_id,
@@ -790,18 +859,75 @@ smtp(nullserver, d_flags, e)
/* arrange to ignore send list */
e->e_sendqueue = NULL;
goto doquit;
-#endif /* _FFR_MILTER_421 */
}
if (response != NULL)
-
sm_free(response); /* XXX */
}
#endif /* MILTER */
+ /*
+ ** Broken proxies and SMTP slammers
+ ** push data without waiting, catch them
+ */
+
+ if (
+#if STARTTLS
+ !smtps &&
+#endif /* STARTTLS */
+ *greetcode == '2')
+ {
+ time_t msecs = 0;
+ char **pvp;
+ char pvpbuf[PSBUFSIZE];
+
+ /* Ask the rulesets how long to pause */
+ pvp = NULL;
+ r = rscap("greet_pause", peerhostname,
+ anynet_ntoa(&RealHostAddr), e,
+ &pvp, pvpbuf, sizeof(pvpbuf));
+ if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
+ (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
+ {
+ msecs = strtol(pvp[1], NULL, 10);
+ }
+
+ if (msecs > 0)
+ {
+ int fd;
+ fd_set readfds;
+ struct timeval timeout;
+
+ /* pause for a moment */
+ timeout.tv_sec = msecs / 1000;
+ timeout.tv_usec = (msecs % 1000) * 1000;
+
+ /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
+ if (timeout.tv_sec >= 300)
+ {
+ timeout.tv_sec = 300;
+ timeout.tv_usec = 0;
+ }
+
+ /* check if data is on the socket during the pause */
+ fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
+ FD_ZERO(&readfds);
+ SM_FD_SET(fd, &readfds);
+ if (select(fd + 1, FDSET_CAST &readfds,
+ NULL, NULL, &timeout) > 0 &&
+ FD_ISSET(fd, &readfds))
+ {
+ greetcode = "554";
+ nullserver = "Command rejected";
+ sm_syslog(LOG_INFO, e->e_id,
+ "rejecting commands from %s [%s] due to pre-greeting traffic",
+ peerhostname,
+ anynet_ntoa(&RealHostAddr));
+ }
+ }
+ }
+
#if STARTTLS
-# if _FFR_SMTP_SSL
/* If this an smtps connection, start TLS now */
- smtps = bitnset(D_SMTPS, d_flags);
if (smtps)
{
Errors = 0;
@@ -810,7 +936,6 @@ smtp(nullserver, d_flags, e)
greeting:
-# endif /* _FFR_SMTP_SSL */
#endif /* STARTTLS */
/* output the first line, inserting "ESMTP" as second word */
@@ -854,20 +979,18 @@ smtp(nullserver, d_flags, e)
protocol = NULL;
sendinghost = macvalue('s', e);
-#if _FFR_QUARANTINE
/* If quarantining by a connect/ehlo action, save between messages */
if (e->e_quarmsg == NULL)
smtp.sm_quarmsg = NULL;
else
smtp.sm_quarmsg = newstr(e->e_quarmsg);
-#endif /* _FFR_QUARANTINE */
/* sendinghost's storage must outlive the current envelope */
if (sendinghost != NULL)
sendinghost = sm_strdup_x(sendinghost);
-#if _FFR_BLOCK_PROXIES || _FFR_ADAPTIVE_EOL
+#if _FFR_BLOCK_PROXIES
first = true;
-#endif /* _FFR_BLOCK_PROXIES || _FFR_ADAPTIVE_EOL */
+#endif /* _FFR_BLOCK_PROXIES */
gothello = false;
smtp.sm_gotmail = false;
for (;;)
@@ -932,10 +1055,9 @@ smtp(nullserver, d_flags, e)
goto doquit;
}
-#if _FFR_BLOCK_PROXIES || _FFR_ADAPTIVE_EOL
+#if _FFR_BLOCK_PROXIES
if (first)
{
-#if _FFR_BLOCK_PROXIES
size_t inplen, cmdlen;
int idx;
char *http_cmd;
@@ -960,27 +1082,9 @@ smtp(nullserver, d_flags, e)
goto doquit;
}
}
-#endif /* _FFR_BLOCK_PROXIES */
-#if _FFR_ADAPTIVE_EOL
- char *p;
-
- smtp.sm_crlf = true;
- p = strchr(inp, '\n');
- if (p == NULL || p <= inp || p[-1] != '\r')
- {
- smtp.sm_crlf = false;
- if (tTd(66, 1) && LogLevel > 8)
- {
- /* how many bad guys are there? */
- sm_syslog(LOG_INFO, NOQID,
- "%s did not use CRLF",
- CurSmtpClient);
- }
- }
-#endif /* _FFR_ADAPTIVE_EOL */
first = false;
}
-#endif /* _FFR_BLOCK_PROXIES || _FFR_ADAPTIVE_EOL */
+#endif /* _FFR_BLOCK_PROXIES */
/* clean up end of line */
fixcrlf(inp, true);
@@ -1090,7 +1194,8 @@ smtp(nullserver, d_flags, e)
{
macdefine(&BlankEnvelope.e_macro,
A_TEMP,
- macid("{auth_authen}"), user);
+ macid("{auth_authen}"),
+ xtextify(user, "<>\")"));
}
# if 0
@@ -1353,15 +1458,15 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
p, CurSmtpClient);
- usrerr("454 4.7.1 Please try again later");
+ usrerr("454 4.3.0 Please try again later");
break;
}
ismore = false;
/* crude way to avoid crack attempts */
- (void) checksmtpattack(&n_auth, n_mechs + 1, true,
- "AUTH", e);
+ STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
+ true, "AUTH", e));
/* make sure mechanism (p) is a valid string */
for (q = p; *q != '\0' && isascii(*q); q++)
@@ -1524,12 +1629,10 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
p, CurSmtpClient);
- usrerr("454 4.7.1 Please try again later");
+ usrerr("454 4.7.0 Please try again later");
break;
}
-# if _FFR_SMTP_SSL
starttls:
-# endif /* _FFR_SMTP_SSL */
# if TLS_NO_RSA
/*
** XXX do we need a temp key ?
@@ -1554,11 +1657,7 @@ smtp(nullserver, d_flags, e)
message("454 4.3.3 TLS not available: error generating SSL handle");
if (LogLevel > 8)
tlslogerr("server");
-# if _FFR_SMTP_SSL
goto tls_done;
-# else /* _FFR_SMTP_SSL */
- break;
-# endif /* _FFR_SMTP_SSL */
}
# if !TLS_VRFY_PER_CTX
@@ -1581,15 +1680,9 @@ smtp(nullserver, d_flags, e)
message("454 4.3.3 TLS not available: error set fd");
SSL_free(srv_ssl);
srv_ssl = NULL;
-# if _FFR_SMTP_SSL
goto tls_done;
-# else /* _FFR_SMTP_SSL */
- break;
-# endif /* _FFR_SMTP_SSL */
}
-# if _FFR_SMTP_SSL
if (!smtps)
-# endif /* _FFR_SMTP_SSL */
message("220 2.0.0 Ready to start TLS");
# if PIPELINING
(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
@@ -1600,6 +1693,9 @@ smtp(nullserver, d_flags, e)
# define SSL_ACC(s) SSL_accept(s)
tlsstart = curtime();
+ fdfl = fcntl(rfd, F_GETFL);
+ if (fdfl != -1)
+ fcntl(rfd, F_SETFL, fdfl|O_NONBLOCK);
ssl_retry:
if ((r = SSL_ACC(srv_ssl)) <= 0)
{
@@ -1702,6 +1798,9 @@ tlsfail:
goto doquit;
}
+ if (fdfl != -1)
+ fcntl(rfd, F_SETFL, fdfl);
+
/* ignore return code for now, it's in {verify} */
(void) tls_get_info(srv_ssl, true,
CurSmtpClient,
@@ -1739,25 +1838,31 @@ tlsfail:
# if SASL
if (sasl_ok)
{
- char *s;
+ int cipher_bits;
+ bool verified;
+ char *s, *v, *c;
s = macvalue(macid("{cipher_bits}"), e);
-# if SASL >= 20000
- if (s != NULL && (ext_ssf = atoi(s)) > 0)
+ v = macvalue(macid("{verify}"), e);
+ c = macvalue(macid("{cert_subject}"), e);
+ verified = (v != NULL && strcmp(v, "OK") == 0);
+ if (s != NULL && (cipher_bits = atoi(s)) > 0)
{
- auth_id = macvalue(macid("{cert_subject}"),
- e);
- sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
+# if SASL >= 20000
+ ext_ssf = cipher_bits;
+ auth_id = verified ? c : NULL;
+ sasl_ok = ((sasl_setprop(conn,
+ SASL_SSF_EXTERNAL,
+ &ext_ssf) == SASL_OK) &&
+ (sasl_setprop(conn,
+ SASL_AUTH_EXTERNAL,
+ auth_id) == SASL_OK));
# else /* SASL >= 20000 */
- if (s != NULL && (ext_ssf.ssf = atoi(s)) > 0)
- {
- ext_ssf.auth_id = macvalue(macid("{cert_subject}"),
- e);
- sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
+ ext_ssf.ssf = cipher_bits;
+ ext_ssf.auth_id = verified ? c : NULL;
+ sasl_ok = sasl_setprop(conn,
+ SASL_SSF_EXTERNAL,
+ &ext_ssf) == SASL_OK;
# endif /* SASL >= 20000 */
mechlist = NULL;
if (sasl_ok)
@@ -1789,7 +1894,6 @@ tlsfail:
nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
syserr("STARTTLS: can't switch to encrypted layer");
}
-# if _FFR_SMTP_SSL
tls_done:
if (smtps)
{
@@ -1798,7 +1902,6 @@ tlsfail:
else
goto doquit;
}
-# endif /* _FFR_SMTP_SSL */
break;
#endif /* STARTTLS */
@@ -1817,8 +1920,8 @@ tlsfail:
}
/* avoid denial-of-service */
- (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, true,
- "HELO/EHLO", e);
+ STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
+ true, "HELO/EHLO", e));
#if 0
/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
@@ -1891,24 +1994,6 @@ tlsfail:
if (gothello)
{
CLEAR_STATE(cmdbuf);
-
-#if _FFR_QUARANTINE
- /* restore connection quarantining */
- if (smtp.sm_quarmsg == NULL)
- {
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), "");
- }
- else
- {
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
- smtp.sm_quarmsg);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"),
- e->e_quarmsg);
- }
-#endif /* _FFR_QUARANTINE */
}
#if MILTER
@@ -1951,7 +2036,6 @@ tlsfail:
if (response != NULL)
sm_free(response);
-# if _FFR_QUARANTINE
/*
** If quarantining by a connect/ehlo action,
** save between messages
@@ -1960,7 +2044,6 @@ tlsfail:
if (smtp.sm_quarmsg == NULL &&
e->e_quarmsg != NULL)
smtp.sm_quarmsg = newstr(e->e_quarmsg);
-# endif /* _FFR_QUARANTINE */
}
#endif /* MILTER */
gothello = true;
@@ -2113,6 +2196,8 @@ tlsfail:
n_badrcpts = 0;
macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
+ macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
+ "0");
e->e_flags |= EF_CLRQUEUE;
sm_setproctitle(true, e, "%s %s: %.80s",
qid_printname(e),
@@ -2352,6 +2437,7 @@ tlsfail:
/* To avoid duplicated message */
n_badrcpts++;
}
+ NBADRCPTS;
/*
** Don't use exponential backoff for now.
@@ -2556,20 +2642,25 @@ tlsfail:
}
rcpt_done:
if (Errors > 0)
+ {
++n_badrcpts;
+ NBADRCPTS;
+ }
}
SM_EXCEPT(exc, "[!F]*")
{
/* An exception occurred while processing RCPT */
e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
++n_badrcpts;
+ NBADRCPTS;
}
SM_END_TRY
break;
case CMDDATA: /* data -- text of mail */
DELAY_CONN("DATA");
- smtp_data(&smtp, e);
+ if (!smtp_data(&smtp, e))
+ goto doquit;
break;
case CMDRSET: /* rset -- reset state */
@@ -2578,22 +2669,6 @@ tlsfail:
else
message("250 2.0.0 Reset state");
CLEAR_STATE(cmdbuf);
-#if _FFR_QUARANTINE
- /* restore connection quarantining */
- if (smtp.sm_quarmsg == NULL)
- {
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), "");
- }
- else
- {
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
- smtp.sm_quarmsg);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- }
-#endif /* _FFR_QUARANTINE */
break;
case CMDVRFY: /* vrfy -- verify address */
@@ -2614,6 +2689,7 @@ tlsfail:
}
wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
false, vrfy ? "VRFY" : "EXPN", e);
+ STOP_IF_ATTACK(wt);
previous = curtime();
if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
(!vrfy && !bitset(SRV_OFFER_EXPN, features)))
@@ -2741,8 +2817,8 @@ tlsfail:
}
/* crude way to avoid denial-of-service attacks */
- (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, true,
- "ETRN", e);
+ STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
+ true, "ETRN", e));
/*
** Do config file checking of the parameter.
@@ -2817,8 +2893,8 @@ tlsfail:
case CMDNOOP: /* noop -- do nothing */
DELAY_CONN("NOOP");
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
- "NOOP", e);
+ STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
+ true, "NOOP", e));
message("250 2.0.0 OK");
break;
@@ -2900,8 +2976,8 @@ doquit:
message("502 5.7.0 Verbose unavailable");
break;
}
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
- "VERB", e);
+ STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
+ true, "VERB", e));
Verbose = 1;
set_delivery_mode(SM_DELIVER, e);
message("250 2.0.0 Verbose mode");
@@ -2911,7 +2987,7 @@ doquit:
case CMDDBGQSHOW: /* show queues */
(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
"Send Queue=");
- printaddr(e->e_sendqueue, true);
+ printaddr(smioout, e->e_sendqueue, true);
break;
case CMDDBGDEBUG: /* set debug mode */
@@ -2937,6 +3013,7 @@ doquit:
#if MAXBADCOMMANDS > 0
if (++n_badcmds > MAXBADCOMMANDS)
{
+ stopattack:
message("421 4.7.0 %s Too many bad commands; closing connection",
MyHostName);
@@ -2946,6 +3023,28 @@ doquit:
}
#endif /* MAXBADCOMMANDS > 0 */
+#if MILTER && SMFI_VERSION > 2
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ char state;
+ char *response;
+
+ if (MilterLogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Sending \"%s\" to Milter", inp);
+ response = milter_unknown(inp, e, &state);
+ MILTER_REPLY("unknown");
+ if (state == SMFIR_REPLYCODE ||
+ state == SMFIR_REJECT ||
+ state == SMFIR_TEMPFAIL)
+ {
+ /* MILTER_REPLY already gave an error */
+ break;
+ }
+ }
+#endif /* MILTER && SMFI_VERSION > 2 */
+
usrerr("500 5.5.1 Command unrecognized: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
@@ -2984,13 +3083,13 @@ doquit:
** e -- envelope.
**
** Returns:
-** none.
+** true iff SMTP session can continue.
**
** Side Effects:
** possibly sends message.
*/
-static void
+static bool
smtp_data(smtp, e)
SMTP_T *smtp;
ENVELOPE *e;
@@ -3010,18 +3109,79 @@ smtp_data(smtp, e)
if (!smtp->sm_gotmail)
{
usrerr("503 5.0.0 Need MAIL command");
- return;
+ return true;
}
else if (smtp->sm_nrcpts <= 0)
{
usrerr("503 5.0.0 Need RCPT (recipient)");
- return;
+ return true;
}
(void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
if (rscheck("check_data", buf, NULL, e,
RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
e->e_id) != EX_OK)
- return;
+ return true;
+
+#if MILTER && SMFI_VERSION > 3
+ if (smtp->sm_milterlist && smtp->sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ char state;
+ char *response;
+ int savelogusrerrs = LogUsrErrs;
+
+ response = milter_data_cmd(e, &state);
+ switch (state)
+ {
+ case SMFIR_REPLYCODE:
+ if (MilterLogLevel > 3)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: cmd=data, reject=%s",
+ response);
+ LogUsrErrs = false;
+ }
+ usrerr(response);
+ if (strncmp(response, "421 ", 4) == 0)
+ {
+ e->e_sendqueue = NULL;
+ return false;
+ }
+ return true;
+
+ case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: cmd=data, reject=550 5.7.1 Command rejected");
+ LogUsrErrs = false;
+ }
+ usrerr("550 5.7.1 Command rejected");
+ return true;
+
+ case SMFIR_DISCARD:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: cmd=data, discard");
+ e->e_flags |= EF_DISCARD;
+ break;
+
+ case SMFIR_TEMPFAIL:
+ if (MilterLogLevel > 3)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: cmd=data, reject=%s",
+ MSG_TEMPFAIL);
+ LogUsrErrs = false;
+ }
+ usrerr(MSG_TEMPFAIL);
+ return true;
+ }
+ LogUsrErrs = savelogusrerrs;
+ if (response != NULL)
+ sm_free(response); /* XXX */
+ }
+#endif /* MILTER && SMFI_VERSION > 3 */
/* put back discard bit */
if (smtp->sm_discard)
@@ -3049,12 +3209,6 @@ smtp_data(smtp, e)
SmtpPhase = "collect";
buffer_errors();
-#if _FFR_ADAPTIVE_EOL
- /* triggers error in collect, disabled for now */
- if (smtp->sm_crlf)
- e->e_flags |= EF_NL_NOT_EOL;
-#endif /* _FFR_ADAPTIVE_EOL */
-
collect(InChannel, true, NULL, e, true);
/* redefine message size */
@@ -3128,13 +3282,86 @@ smtp_data(smtp, e)
if (milteraccept && MilterLogLevel > 9)
sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
}
+
+ /*
+ ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
+ ** milter accepted message, sync it now
+ **
+ ** XXX This is almost a copy of the code in collect(): put it into
+ ** a function that is called from both places?
+ */
+
+ if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
+ {
+ int afd;
+ SM_FILE_T *volatile df;
+ char *dfname;
+
+ df = e->e_dfp;
+ dfname = queuename(e, DATAFL_LETTER);
+ if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
+ && errno != EINVAL)
+ {
+ int save_errno;
+
+ save_errno = errno;
+ if (save_errno == EEXIST)
+ {
+ struct stat st;
+ int dfd;
+
+ if (stat(dfname, &st) < 0)
+ st.st_size = -1;
+ errno = EEXIST;
+ syserr("@collect: bfcommit(%s): already on disk, size=%ld",
+ dfname, (long) st.st_size);
+ dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
+ if (dfd >= 0)
+ dumpfd(dfd, true, true);
+ }
+ errno = save_errno;
+ dferror(df, "bfcommit", e);
+ flush_errors(true);
+ finis(save_errno != EEXIST, true, ExitStat);
+ }
+ else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
+ {
+ dferror(df, "sm_io_getinfo", e);
+ flush_errors(true);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+ else if (fsync(afd) < 0)
+ {
+ dferror(df, "fsync", e);
+ flush_errors(true);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+ else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
+ {
+ dferror(df, "sm_io_close", e);
+ flush_errors(true);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+
+ /* Now reopen the df file */
+ e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
+ SM_IO_RDONLY, NULL);
+ if (e->e_dfp == NULL)
+ {
+ /* we haven't acked receipt yet, so just chuck this */
+ syserr("@Cannot reopen %s", dfname);
+ finis(true, true, ExitStat);
+ /* NOTREACHED */
+ }
+ }
#endif /* MILTER */
-#if _FFR_QUARANTINE
/* Check if quarantining stats should be updated */
if (e->e_quarmsg != NULL)
markstats(e, NULL, STATS_QUARANTINE);
-#endif /* _FFR_QUARANTINE */
/*
** If a header/body check (header checks or milter)
@@ -3148,9 +3375,7 @@ smtp_data(smtp, e)
aborting = Errors > 0;
if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
-#if _FFR_QUARANTINE
(QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
-#endif /* _FFR_QUARANTINE */
!split_by_recipient(e))
aborting = bitset(EF_FATALERRS, e->e_flags);
@@ -3248,14 +3473,12 @@ smtp_data(smtp, e)
ee->e_sendmode = SM_QUEUE;
continue;
}
-#if _FFR_QUARANTINE
else if (QueueMode != QM_QUARANTINE &&
ee->e_quarmsg != NULL)
{
ee->e_sendmode = SM_QUEUE;
continue;
}
-#endif /* _FFR_QUARANTINE */
anything_to_send = true;
/* close all the queue files */
@@ -3300,7 +3523,6 @@ smtp_data(smtp, e)
{
for (ee = e; ee != NULL; ee = ee->e_sibling)
{
-#if _FFR_QUARANTINE
if (!doublequeue &&
QueueMode != QM_QUARANTINE &&
ee->e_quarmsg != NULL)
@@ -3308,7 +3530,6 @@ smtp_data(smtp, e)
dropenvelope(ee, true, false);
continue;
}
-#endif /* _FFR_QUARANTINE */
if (WILL_BE_QUEUED(ee->e_sendmode))
dropenvelope(ee, true, false);
}
@@ -3326,7 +3547,6 @@ smtp_data(smtp, e)
newenvelope(e, e, sm_rpool_new_x(NULL));
e->e_flags = BlankEnvelope.e_flags;
-#if _FFR_QUARANTINE
/* restore connection quarantining */
if (smtp->sm_quarmsg == NULL)
{
@@ -3339,7 +3559,7 @@ smtp_data(smtp, e)
macdefine(&e->e_macro, A_PERM,
macid("{quarantine}"), e->e_quarmsg);
}
-#endif /* _FFR_QUARANTINE */
+ return true;
}
/*
** LOGUNDELRCPTS -- log undelivered (or all) recipients.
@@ -3395,7 +3615,9 @@ logundelrcpts(e, msg, level, all)
** e -- the current envelope.
**
** Returns:
-** time to wait.
+** time to wait,
+** STOP_ATTACK if twice as many commands as allowed and
+** MaxChildren > 0.
**
** Side Effects:
** Slows down if we seem to be under attack.
@@ -3404,7 +3626,7 @@ logundelrcpts(e, msg, level, all)
static time_t
checksmtpattack(pcounter, maxcount, waitnow, cname, e)
volatile unsigned int *pcounter;
- int maxcount;
+ unsigned int maxcount;
bool waitnow;
char *cname;
ENVELOPE *e;
@@ -3414,6 +3636,7 @@ checksmtpattack(pcounter, maxcount, waitnow, cname, e)
if (++(*pcounter) >= maxcount)
{
+ unsigned int shift;
time_t s;
if (*pcounter == maxcount && LogLevel > 5)
@@ -3422,19 +3645,25 @@ checksmtpattack(pcounter, maxcount, waitnow, cname, e)
"%s: possible SMTP attack: command=%.40s, count=%u",
CurSmtpClient, cname, *pcounter);
}
- s = 1 << (*pcounter - maxcount);
- if (s >= MAXTIMEOUT || s <= 0)
+ shift = *pcounter - maxcount;
+ s = 1 << shift;
+ if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
s = MAXTIMEOUT;
+#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \
+ ? STOP_ATTACK : (time_t) s)
+
/* sleep at least 1 second before returning */
(void) sleep(*pcounter / maxcount);
s -= *pcounter / maxcount;
- if (waitnow)
+ if (s >= MAXTIMEOUT || s < 0)
+ s = MAXTIMEOUT;
+ if (waitnow && s > 0)
{
(void) sleep(s);
- return 0;
+ return IS_ATTACK(0);
}
- return s;
+ return IS_ATTACK(s);
}
return (time_t) 0;
}
@@ -3688,7 +3917,6 @@ mail_esmtp_args(kp, vp, e)
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
bool saveExitStat = ExitStat;
- char pbuf[256];
if (vp == NULL)
{
@@ -3713,12 +3941,9 @@ mail_esmtp_args(kp, vp, e)
/* NOTREACHED */
}
- /* XXX this might be cut off */
- (void) sm_strlcpy(pbuf, xuntextify(auth_param), sizeof pbuf);
- /* xalloc() the buffer instead? */
-
/* XXX define this always or only if trusted? */
- macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), pbuf);
+ macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
+ auth_param);
/*
** call Strust_auth to find out whether
@@ -3730,14 +3955,14 @@ mail_esmtp_args(kp, vp, e)
SuprErrs = true;
QuickAbort = false;
if (strcmp(auth_param, "<>") != 0 &&
- (rscheck("trust_auth", pbuf, NULL, e, RSF_RMCOMM,
+ (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
9, NULL, NOQID) != EX_OK || Errors > 0))
{
if (tTd(95, 8))
{
q = e->e_auth_param;
sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
- pbuf, (q == NULL) ? "" : q);
+ auth_param, (q == NULL) ? "" : q);
}
/* not trusted */
@@ -3750,7 +3975,7 @@ mail_esmtp_args(kp, vp, e)
else
{
if (tTd(95, 8))
- sm_dprintf("auth=\"%.100s\" trusted\n", pbuf);
+ sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
auth_param);
}
@@ -4169,21 +4394,22 @@ static struct
} srv_feat_table[] =
{
{ 'A', SRV_OFFER_AUTH },
- { 'B', SRV_OFFER_VERB }, /* FFR; not documented in 8.12 */
- { 'D', SRV_OFFER_DSN }, /* FFR; not documented in 8.12 */
- { 'E', SRV_OFFER_ETRN }, /* FFR; not documented in 8.12 */
- { 'L', SRV_REQ_AUTH }, /* FFR; not documented in 8.12 */
+ { 'B', SRV_OFFER_VERB },
+ { 'C', SRV_REQ_SEC },
+ { 'D', SRV_OFFER_DSN },
+ { 'E', SRV_OFFER_ETRN },
+ { 'L', SRV_REQ_AUTH },
#if PIPELINING
# if _FFR_NO_PIPE
{ 'N', SRV_NO_PIPE },
# endif /* _FFR_NO_PIPE */
{ 'P', SRV_OFFER_PIPE },
#endif /* PIPELINING */
- { 'R', SRV_VRFY_CLT }, /* FFR; not documented in 8.12 */
+ { 'R', SRV_VRFY_CLT }, /* same as V; not documented */
{ 'S', SRV_OFFER_TLS },
/* { 'T', SRV_TMP_FAIL }, */
{ 'V', SRV_VRFY_CLT },
- { 'X', SRV_OFFER_EXPN }, /* FFR; not documented in 8.12 */
+ { 'X', SRV_OFFER_EXPN },
/* { 'Y', SRV_OFFER_VRFY }, */
{ '\0', SRV_NONE }
};
OpenPOWER on IntegriCloud