summaryrefslogtreecommitdiff
path: root/src/srvrsmtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/srvrsmtp.c')
-rw-r--r--src/srvrsmtp.c636
1 files changed, 465 insertions, 171 deletions
diff --git a/src/srvrsmtp.c b/src/srvrsmtp.c
index b05348d4b2e29..b6263079a90e8 100644
--- a/src/srvrsmtp.c
+++ b/src/srvrsmtp.c
@@ -15,7 +15,7 @@
#if MILTER
# include <libmilter/mfapi.h>
# include <libmilter/mfdef.h>
-#endif /* MILTER */
+#endif
SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $")
@@ -23,18 +23,20 @@ SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $")
#include <sm/fdset.h>
#if SASL || STARTTLS
+# include <tls.h>
# include "sfsasl.h"
-#endif /* SASL || STARTTLS */
+#endif
#if SASL
# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
static int saslmechs __P((sasl_conn_t *, char **));
-#endif /* SASL */
+#endif
#if STARTTLS
# include <openssl/err.h>
# include <sysexits.h>
static SSL_CTX *srv_ctx = NULL; /* TLS server context */
static SSL *srv_ssl = NULL; /* per connection context */
+static tlsi_ctx_T tlsi_ctx; /* TLS information context */
static bool tls_ok_srv = false;
@@ -44,7 +46,7 @@ static bool tls_ok_srv = false;
#if _FFR_DM_ONE
static bool NotFirstDelivery = false;
-#endif /* _FFR_DM_ONE */
+#endif
/* server features */
#define SRV_NONE 0x0000 /* none... */
@@ -60,11 +62,14 @@ static bool NotFirstDelivery = false;
# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
# if _FFR_NO_PIPE
# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
-# endif /* _FFR_NO_PIPE */
+# endif
#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 */
+#if _FFR_EAI
+# define SRV_OFFER_EAI 0x2000 /* offer SMTPUTF* */
+#endif
static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
@@ -76,6 +81,32 @@ static char *skipword __P((char *volatile, char *));
static void setup_smtpd_io __P((void));
#if SASL
+# ifndef MAX_AUTH_USER_LEN
+# define MAX_AUTH_USER_LEN 256
+# endif
+# ifndef MAX_AUTH_LOG_LEN
+# define MAX_AUTH_LOG_LEN 64
+# endif
+static void get_sasl_user __P((char *, unsigned int, const char *, char *out, size_t));
+# define RESET_AUTH_FAIL_LOG_USER \
+ do \
+ { \
+ (void) memset(auth_user, 0, sizeof(auth_user)); \
+ (void) memset(auth_user_tmp, 0, sizeof(auth_user_tmp)); \
+ auth_user_len = 0; \
+ } while (0)
+# define SET_AUTH_USER_TMP(s, len) \
+ do \
+ { \
+ auth_user_len = SM_MIN(len, MAX_AUTH_USER_LEN-1); \
+ (void) memcpy(auth_user_tmp, s, auth_user_len); \
+ } while (0)
+# define SET_AUTH_USER \
+ get_sasl_user(auth_user_tmp, auth_user_len, auth_type, auth_user, sizeof(auth_user))
+# define SET_AUTH_USER_CONDITIONALLY \
+ if ('\0' == auth_user[0]) \
+ SET_AUTH_USER;
+# define LOG_AUTH_FAIL_USER ", user=", (int)MAX_AUTH_LOG_LEN, auth_user
# if SASL >= 20000
static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
char *_remoteip, char *_localip,
@@ -84,6 +115,7 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
# define RESET_SASLCONN \
do \
{ \
+ RESET_AUTH_FAIL_LOG_USER; \
result = reset_saslconn(&conn, AuthRealm, remoteip, \
localip, auth_id, &ext_ssf); \
if (result != SASL_OK) \
@@ -98,6 +130,7 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
# define RESET_SASLCONN \
do \
{ \
+ RESET_AUTH_FAIL_LOG_USER; \
result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
&saddr_l, &ext_ssf); \
if (result != SASL_OK) \
@@ -107,6 +140,10 @@ static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
# endif /* SASL >= 20000 */
#endif /* SASL */
+#if !defined(RESET_AUTH_FAIL_LOG_USER)
+# define RESET_AUTH_FAIL_LOG_USER
+#endif
+
extern ENVELOPE BlankEnvelope;
#define NBADRCPTS \
@@ -119,11 +156,34 @@ extern ENVELOPE BlankEnvelope;
macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
} while (0)
-#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
+#define SKIP_SPACE(s) while (SM_ISSPACE(*s)) \
(s)++
+#if _FFR_EAI
+/*
+** ADDR_IS_ASCII -- check whether an address is 100% printable ASCII
+**
+** Parameters:
+** a -- an address (or other string)
+**
+** Returns:
+** TRUE if a is non-NULL and points to only printable ASCII
+** FALSE if a is NULL and points to printable ASCII
+** FALSE if a is non-NULL and points to something containing 8-bittery
+*/
+
+bool
+addr_is_ascii(a)
+ const char * a;
+{
+ while (a != NULL && *a != '\0' && *a >= ' ' && (unsigned char)*a < 127)
+ a++;
+ return (a != NULL && *a == '\0');
+}
+#endif
+
/*
-** PARSE_ESMTP_ARGS -- parse EMSTP arguments (for MAIL, RCPT)
+** PARSE_ESMTP_ARGS -- parse ESMTP arguments (for MAIL, RCPT)
**
** Parameters:
** e -- the envelope
@@ -417,10 +477,10 @@ struct cmd
#define CMDETRN 12 /* etrn -- flush queue */
#if SASL
# define CMDAUTH 13 /* auth -- SASL authenticate */
-#endif /* SASL */
+#endif
#if STARTTLS
# define CMDSTLS 14 /* STARTTLS -- start TLS session */
-#endif /* STARTTLS */
+#endif
/* non-standard commands */
#define CMDVERB 17 /* verb -- go into verbose mode */
/* unimplemented commands from RFC 821 */
@@ -456,10 +516,10 @@ static struct cmd CmdTab[] =
{ "turn", CMDUNIMPL },
#if SASL
{ "auth", CMDAUTH, },
-#endif /* SASL */
+#endif
#if STARTTLS
{ "starttls", CMDSTLS, },
-#endif /* STARTTLS */
+#endif
/* remaining commands are here only to trap and log attempts to use them */
{ "showq", CMDDBGQSHOW },
{ "debug", CMDDBGDEBUG },
@@ -472,19 +532,19 @@ static char *CurSmtpClient; /* who's at the other end of channel */
#ifndef MAXBADCOMMANDS
# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
-#endif /* ! MAXBADCOMMANDS */
+#endif
#ifndef MAXHELOCOMMANDS
# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
-#endif /* ! MAXHELOCOMMANDS */
+#endif
#ifndef MAXVRFYCOMMANDS
# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
-#endif /* ! MAXVRFYCOMMANDS */
+#endif
#ifndef MAXETRNCOMMANDS
# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
-#endif /* ! MAXETRNCOMMANDS */
+#endif
#ifndef MAXTIMEOUT
# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
-#endif /* ! MAXTIMEOUT */
+#endif
/*
** Maximum shift value to compute timeout for bad commands.
@@ -493,10 +553,10 @@ static char *CurSmtpClient; /* who's at the other end of channel */
#ifndef MAXSHIFT
# define MAXSHIFT 8
-#endif /* ! MAXSHIFT */
+#endif
#if MAXSHIFT > 31
ERROR _MAXSHIFT > 31 is invalid
-#endif /* MAXSHIFT */
+#endif
#if MAXBADCOMMANDS > 0
@@ -514,7 +574,7 @@ static char *CurSmtpClient; /* who's at the other end of channel */
#if SM_HEAP_CHECK
static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
-#endif /* SM_HEAP_CHECK */
+#endif
typedef struct
{
@@ -722,10 +782,21 @@ do \
#else
# define auth_active false
#endif
+#if _FFR_EAI
+#define GET_PROTOCOL() \
+ (e->e_smtputf8 \
+ ? (auth_active \
+ ? (tls_active ? "UTF8SMTPSA" : "UTF8SMTPA") \
+ : (tls_active ? "UTF8SMTPS" : "UTF8SMTP")) \
+ : (auth_active \
+ ? (tls_active ? "ESMTPSA" : "ESMTPA") \
+ : (tls_active ? "ESMTPS" : "ESMTP")))
+#else /* _FFR_EAI */
#define GET_PROTOCOL() \
(auth_active \
? (tls_active ? "ESMTPSA" : "ESMTPA") \
: (tls_active ? "ESMTPS" : "ESMTP"))
+#endif /* _FFR_EAI */
static bool SevenBitInput_Saved; /* saved version of SevenBitInput */
@@ -770,7 +841,7 @@ smtp(nullserver, d_flags, e)
char inp[MAXINPLINE];
#if MAXINPLINE < MAXLINE
ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
-#endif /* MAXINPLINE < MAXLINE */
+#endif
char cmdbuf[MAXLINE];
#if SASL
sasl_conn_t *conn;
@@ -781,6 +852,8 @@ smtp(nullserver, d_flags, e)
volatile int authenticating;
char *user;
char *in, *out2;
+ char auth_user[MAX_AUTH_USER_LEN], auth_user_tmp[MAX_AUTH_USER_LEN];
+ unsigned int auth_user_len;
# if SASL >= 20000
char *auth_id = NULL;
const char *out;
@@ -801,7 +874,6 @@ smtp(nullserver, d_flags, e)
char *mechlist;
volatile unsigned int n_mechs;
unsigned int len;
-#else /* SASL */
#endif /* SASL */
int r;
#if STARTTLS
@@ -811,12 +883,15 @@ smtp(nullserver, d_flags, e)
bool saveQuickAbort;
bool saveSuprErrs;
time_t tlsstart;
+ int ssl_err, tlsret;
+ int save_errno;
+ extern int TLSsslidx;
#endif /* STARTTLS */
volatile unsigned int features;
#if PIPELINING
# if _FFR_NO_PIPE
int np_log = 0;
-# endif /* _FFR_NO_PIPE */
+# endif
#endif /* PIPELINING */
volatile time_t log_delay = (time_t) 0;
#if MILTER
@@ -830,15 +905,16 @@ smtp(nullserver, d_flags, e)
size_t inplen;
#if _FFR_BADRCPT_SHUTDOWN
int n_badrcpts_adj;
-#endif /* _FFR_BADRCPT_SHUTDOWN */
+#endif
+ RESET_AUTH_FAIL_LOG_USER;
SevenBitInput_Saved = SevenBitInput;
smtp.sm_nrcpts = 0;
#if MILTER
smtp.sm_milterize = (nullserver == NULL);
smtp.sm_milterlist = false;
addr = NULL;
-#endif /* MILTER */
+#endif
/* setup I/O fd correctly for the SMTP server */
setup_smtpd_io();
@@ -892,12 +968,15 @@ smtp(nullserver, d_flags, e)
#endif /* SASL */
#if PIPELINING
| SRV_OFFER_PIPE
-#endif /* PIPELINING */
+#endif
#if STARTTLS
| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
: SRV_VRFY_CLT)
-#endif /* STARTTLS */
+#endif
+#if _FFR_EAI
+ | SRV_OFFER_EAI
+#endif /* _FFR_EAI */
;
if (nullserver == NULL)
{
@@ -931,6 +1010,7 @@ smtp(nullserver, d_flags, e)
}
else if (strncmp(nullserver, "421 ", 4) == 0)
{
+ /* Can't use ("%s", ...) due to message() requirements */
message(nullserver);
goto doquit;
}
@@ -985,7 +1065,7 @@ smtp(nullserver, d_flags, e)
if (in != NULL && (
# if NETINET6
strcmp(in, "inet6") == 0 ||
-# endif /* NETINET6 */
+# endif
strcmp(in, "inet") == 0))
{
SOCKADDR_LEN_T addrsize;
@@ -1056,7 +1136,7 @@ smtp(nullserver, d_flags, e)
# if 0
macdefine(&BlankEnvelope.e_macro, A_PERM,
macid("{auth_author}"), NULL);
-# endif /* 0 */
+# endif
/* set properties */
(void) memset(&ssp, '\0', sizeof(ssp));
@@ -1267,7 +1347,7 @@ smtp(nullserver, d_flags, e)
if (
#if STARTTLS
!smtps &&
-#endif /* STARTTLS */
+#endif
*greetcode == '2' && nullserver == NULL)
{
time_t msecs = 0;
@@ -1336,7 +1416,16 @@ smtp(nullserver, d_flags, e)
/* If this an smtps connection, start TLS now */
if (smtps)
{
+ if (!tls_ok_srv || srv_ctx == NULL)
+ {
+ sm_syslog(LOG_ERR, e->e_id,
+ "smtps: TLS not available, exiting");
+ exit(EX_CONFIG);
+ }
Errors = 0;
+ first = true;
+ gothello = false;
+ smtp.sm_gotmail = false;
goto starttls;
}
@@ -1369,14 +1458,14 @@ smtp(nullserver, d_flags, e)
while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
{
*p++ = '\0';
- if (isascii(*id) && isspace(*id))
+ if (SM_ISSPACE(*id))
id++;
(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
message(cmdbuf, id);
}
if (id != NULL)
{
- if (isascii(*id) && isspace(*id))
+ if (SM_ISSPACE(*id))
id++;
(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
message(cmdbuf, id);
@@ -1409,7 +1498,7 @@ smtp(nullserver, d_flags, e)
e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
#if MILTER
milter_cmd_fail = false;
-#endif /* MILTER */
+#endif
/* setup for the read */
e->e_to = NULL;
@@ -1437,7 +1526,7 @@ smtp(nullserver, d_flags, e)
#if MILTER
/* close out milter filters */
milter_quit(e);
-#endif /* MILTER */
+#endif
message("421 4.4.1 %s Lost input channel from %s",
MyHostName, CurSmtpClient);
@@ -1492,7 +1581,7 @@ smtp(nullserver, d_flags, e)
cmdlen = strlen(http_cmd);
if (cmdlen < inplen &&
sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
- isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
+ SM_ISSPACE(inp[cmdlen]))
{
/* Open proxy, drop it */
message("421 4.7.0 %s Rejecting open proxy %s",
@@ -1572,16 +1661,18 @@ smtp(nullserver, d_flags, e)
inp);
# if SASL >= 20000
sm_free(in);
-# endif /* SASL >= 20000 */
+# endif
RESET_SASLCONN;
continue;
}
# if SASL >= 20000
+ SET_AUTH_USER_TMP(in, inlen);
result = sasl_server_step(conn, in, inlen,
&out, &outlen);
sm_free(in);
# else /* SASL >= 20000 */
+ SET_AUTH_USER_TMP(out, outlen);
result = sasl_server_step(conn, out, outlen,
&out, &outlen, &errstr);
# endif /* SASL >= 20000 */
@@ -1622,7 +1713,7 @@ smtp(nullserver, d_flags, e)
# if 0
/* get realm? */
sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif /* 0 */
+# endif
/* get security strength (features) */
result = sasl_getprop(conn, SASL_SSF,
@@ -1691,6 +1782,8 @@ smtp(nullserver, d_flags, e)
}
else if (result == SASL_CONTINUE)
{
+ SET_AUTH_USER;
+
len = ENC64LEN(outlen);
out2 = xalloc(len);
result = sasl_encode64(out, outlen, out2, len,
@@ -1717,26 +1810,35 @@ smtp(nullserver, d_flags, e)
}
# if SASL >= 20000
sm_free(out2);
-# endif /* SASL >= 20000 */
+# endif
}
else
{
- /* not SASL_OK or SASL_CONT */
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH failure (%s): %s (%d) %s, relay=%.100s",
- auth_type,
- sasl_errstring(result, NULL,
- NULL),
- result,
+
# if SASL >= 20000
- sasl_errdetail(conn),
-# else /* SASL >= 20000 */
- errstr == NULL ? "" : errstr,
-# endif /* SASL >= 20000 */
- CurSmtpClient);
- RESET_SASLCONN;
+# define SASLERR sasl_errdetail(conn)
+# else
+# define SASLERR errstr == NULL ? "" : errstr
+# endif
+#define LOGAUTHFAIL \
+ do \
+ { \
+ SET_AUTH_USER_CONDITIONALLY \
+ message("535 5.7.0 authentication failed"); \
+ if (LogLevel >= 9) \
+ sm_syslog(LOG_WARNING, e->e_id, \
+ "AUTH failure (%s): %s (%d) %s%s%.*s, relay=%.100s", \
+ (auth_type != NULL) ? auth_type : "unknown", \
+ sasl_errstring(result, NULL, NULL), \
+ result, \
+ SASLERR, \
+ LOG_AUTH_FAIL_USER, \
+ CurSmtpClient); \
+ RESET_SASLCONN; \
+ } while (0)
+
+
+ LOGAUTHFAIL;
authenticating = SASL_NOT_AUTH;
}
}
@@ -1754,11 +1856,11 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
/* break off command */
- for (p = inp; isascii(*p) && isspace(*p); p++)
+ for (p = inp; SM_ISSPACE(*p); p++)
continue;
cmd = cmdbuf;
while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)) &&
+ !(SM_ISSPACE(*p)) &&
cmd < &cmdbuf[sizeof(cmdbuf) - 2])
*cmd++ = *p++;
*cmd = '\0';
@@ -1849,10 +1951,15 @@ smtp(nullserver, d_flags, e)
if (nullserver != NULL)
{
if (ISSMTPREPLY(nullserver))
+ {
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(nullserver);
+ }
else
+ {
usrerr("550 5.0.0 %s",
nullserver);
+ }
}
else
usrerr("452 4.4.5 Insufficient disk space; try again later");
@@ -1902,8 +2009,7 @@ smtp(nullserver, d_flags, e)
if (isspace(*q))
{
*q = '\0';
- while (*++q != '\0' &&
- isascii(*q) && isspace(*q))
+ while (*++q != '\0' && SM_ISSPACE(*q))
continue;
*(q - 1) = '\0';
ismore = (*q != '\0');
@@ -1951,6 +2057,7 @@ smtp(nullserver, d_flags, e)
result = sasl_decode64(q, strlen(q), in,
&inlen);
# endif /* SASL >= 20000 */
+
if (result != SASL_OK)
{
message("501 5.5.4 cannot BASE64 decode '%s'",
@@ -1964,11 +2071,12 @@ smtp(nullserver, d_flags, e)
authenticating = SASL_NOT_AUTH;
# if SASL >= 20000
sm_free(in);
-# endif /* SASL >= 20000 */
+# endif
in = NULL;
inlen = 0;
break;
}
+ SET_AUTH_USER_TMP(in, inlen);
}
else
{
@@ -1980,33 +2088,19 @@ smtp(nullserver, d_flags, e)
# if SASL >= 20000
result = sasl_server_start(conn, p, in, inlen,
&out, &outlen);
- if (in != NULL)
- sm_free(in);
+ SM_FREE(in);
# else /* SASL >= 20000 */
result = sasl_server_start(conn, p, in, inlen,
&out, &outlen, &errstr);
# endif /* SASL >= 20000 */
+ if (p != NULL)
+ auth_type = newstr(p);
if (result != SASL_OK && result != SASL_CONTINUE)
{
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_ERR, e->e_id,
- "AUTH failure (%s): %s (%d) %s, relay=%.100s",
- p,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn),
-# else /* SASL >= 20000 */
- errstr,
-# endif /* SASL >= 20000 */
- CurSmtpClient);
- RESET_SASLCONN;
+ LOGAUTHFAIL;
break;
}
- auth_type = newstr(p);
if (result == SASL_OK)
{
@@ -2015,6 +2109,8 @@ smtp(nullserver, d_flags, e)
/* authenticated by the initial response */
}
+ SET_AUTH_USER;
+
/* len is at least 2 */
len = ENC64LEN(outlen);
out2 = xalloc(len);
@@ -2040,7 +2136,7 @@ smtp(nullserver, d_flags, e)
}
# if SASL >= 20000
sm_free(out2);
-# endif /* SASL >= 20000 */
+# endif
break;
#endif /* SASL */
@@ -2057,6 +2153,7 @@ smtp(nullserver, d_flags, e)
message("503 5.5.0 TLS not available");
break;
}
+ starttls:
if (!tls_ok_srv)
{
message("454 4.3.3 TLS not available after start");
@@ -2076,28 +2173,20 @@ smtp(nullserver, d_flags, e)
usrerr("454 4.7.0 Please try again later");
break;
}
- starttls:
-# if USE_OPENSSL_ENGINE
- if (!SSLEngineInitialized)
+ if (!TLS_set_engine(SSLEngine, false))
{
- if (!SSL_set_engine(NULL))
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=server, SSL_set_engine=failed");
- tls_ok_srv = false;
- message("454 4.3.3 TLS not available right now");
- break;
- }
- else
- SSLEngineInitialized = true;
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS=server, engine=%s, TLS_set_engine=failed",
+ SSLEngine);
+ tls_ok_srv = false;
+ message("454 4.3.3 TLS not available right now");
+ break;
}
-# endif /* USE_OPENSSL_ENGINE */
# if TLS_NO_RSA
/*
** XXX do we need a temp key ?
*/
-# else /* TLS_NO_RSA */
-# endif /* TLS_NO_RSA */
+# endif
# if TLS_VRFY_PER_CTX
/*
@@ -2109,22 +2198,37 @@ smtp(nullserver, d_flags, e)
TLS_VERIFY_CLIENT();
# endif /* TLS_VRFY_PER_CTX */
+#define SMTLSFAILED \
+ do { \
+ SM_SSL_FREE(srv_ssl); \
+ goto tls_done; \
+ } while (0)
+
if (srv_ssl != NULL)
SSL_clear(srv_ssl);
else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
{
message("454 4.3.3 TLS not available: error generating SSL handle");
- if (LogLevel > 8)
- tlslogerr(LOG_WARNING, "server");
+ tlslogerr(LOG_WARNING, 8, "server");
goto tls_done;
}
-
- if (get_tls_se_options(e, srv_ssl, true) != 0)
+ if (get_tls_se_options(e, srv_ssl, &tlsi_ctx, true)
+ != 0)
{
message("454 4.3.3 TLS not available: error setting options");
- SSL_free(srv_ssl);
- srv_ssl = NULL;
- goto tls_done;
+ SMTLSFAILED;
+ }
+ r = SSL_set_ex_data(srv_ssl, TLSsslidx, &tlsi_ctx);
+ if (0 == r)
+ {
+ if (LogLevel > 5)
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "STARTTLS=server, error: SSL_set_ex_data failed=%d",
+ r);
+ tlslogerr(LOG_WARNING, 9, "server");
+ }
+ SMTLSFAILED;
}
# if !TLS_VRFY_PER_CTX
@@ -2145,61 +2249,64 @@ smtp(nullserver, d_flags, e)
SSL_set_wfd(srv_ssl, wfd) <= 0)
{
message("454 4.3.3 TLS not available: error set fd");
- SSL_free(srv_ssl);
- srv_ssl = NULL;
- goto tls_done;
+ SMTLSFAILED;
}
if (!smtps)
message("220 2.0.0 Ready to start TLS");
# if PIPELINING
(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-# endif /* PIPELINING */
+# endif
SSL_set_accept_state(srv_ssl);
tlsstart = curtime();
- ssl_retry:
- if ((r = SSL_accept(srv_ssl)) <= 0)
- {
- int i, ssl_err;
- int save_errno = errno;
- ssl_err = SSL_get_error(srv_ssl, r);
- i = tls_retry(srv_ssl, rfd, wfd, tlsstart,
+ ssl_err = SSL_ERROR_WANT_READ;
+ save_errno = 0;
+ do
+ {
+ tlsret = tls_retry(srv_ssl, rfd, wfd, tlsstart,
TimeOuts.to_starttls, ssl_err,
"server");
- if (i > 0)
- goto ssl_retry;
-
- if (LogLevel > 5)
+ if (tlsret <= 0)
{
- unsigned long l;
- const char *sr;
+ if (LogLevel > 5)
+ {
+ unsigned long l;
+ const char *sr;
- l = ERR_peek_error();
- sr = ERR_reason_error_string(l);
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=server, error: accept failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d, relay=%.100s",
- r, sr == NULL ? "unknown"
- : sr,
- ssl_err, save_errno, i,
- CurSmtpClient);
- if (LogLevel > 9)
- tlslogerr(LOG_WARNING, "server");
- }
- tls_ok_srv = false;
- SSL_free(srv_ssl);
- srv_ssl = NULL;
+ l = ERR_peek_error();
+ sr = ERR_reason_error_string(l);
- /*
- ** according to the next draft of
- ** RFC 2487 the connection should be dropped
- */
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=server, error: accept failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d, relay=%.100s",
+ r, sr == NULL ? "unknown"
+ : sr,
+ ssl_err, save_errno,
+ tlsret, CurSmtpClient);
+ tlslogerr(LOG_WARNING, 9, "server");
+ }
+ tls_ok_srv = false;
+ SM_SSL_FREE(srv_ssl);
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
+ /*
+ ** according to the next draft of
+ ** RFC 2487 the connection should
+ ** be dropped
+ **
+ ** arrange to ignore any current
+ ** send list
+ */
+
+ e->e_sendqueue = NULL;
+ goto doquit;
+ }
+
+ r = SSL_accept(srv_ssl);
+ save_errno = 0;
+ if (r <= 0)
+ ssl_err = SSL_get_error(srv_ssl, r);
+ } while (r <= 0);
/* ignore return code for now, it's in {verify} */
(void) tls_get_info(srv_ssl, true,
@@ -2278,7 +2385,7 @@ smtp(nullserver, d_flags, e)
tls_active = true;
# if PIPELINING
(void) sm_io_autoflush(InChannel, OutChannel);
-# endif /* PIPELINING */
+# endif
}
else
{
@@ -2452,10 +2559,15 @@ smtp(nullserver, d_flags, e)
tempfail = true;
smtp.sm_milterize = false;
if (response != NULL)
+ {
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(response);
+ }
else
+ {
message("421 4.7.0 %s closing connection",
MyHostName);
+ }
/* arrange to ignore send list */
e->e_sendqueue = NULL;
lognullconnection = false;
@@ -2505,7 +2617,7 @@ smtp(nullserver, d_flags, e)
#if PIPELINING
if (bitset(SRV_OFFER_PIPE, features))
message("250-PIPELINING");
-#endif /* PIPELINING */
+#endif
if (bitset(SRV_OFFER_EXPN, features))
{
message("250-EXPN");
@@ -2514,7 +2626,7 @@ smtp(nullserver, d_flags, e)
}
#if MIME8TO7
message("250-8BITMIME");
-#endif /* MIME8TO7 */
+#endif
if (MaxMessageSize > 0)
message("250-SIZE %ld", MaxMessageSize);
else
@@ -2522,17 +2634,21 @@ smtp(nullserver, d_flags, e)
#if DSN
if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
message("250-DSN");
-#endif /* DSN */
+#endif
+#if _FFR_EAI
+ if (bitset(SRV_OFFER_EAI, features))
+ message("250-SMTPUTF8");
+#endif /* _FFR_EAI */
if (bitset(SRV_OFFER_ETRN, features))
message("250-ETRN");
#if SASL
if (sasl_ok && mechlist != NULL && *mechlist != '\0')
message("250-AUTH %s", mechlist);
-#endif /* SASL */
+#endif
#if STARTTLS
if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
message("250-STARTTLS");
-#endif /* STARTTLS */
+#endif
if (DeliverByMin > 0)
message("250-DELIVERBY %ld",
(long) DeliverByMin);
@@ -2577,6 +2693,7 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
p, CurSmtpClient);
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(MSG_TEMPFAIL);
break;
}
@@ -2636,7 +2753,7 @@ smtp(nullserver, d_flags, e)
extern char *FullName;
QuickAbort = true;
- SM_FREE_CLR(FullName);
+ SM_FREE(FullName);
/* must parse sender first */
delimptr = NULL;
@@ -2696,6 +2813,21 @@ smtp(nullserver, d_flags, e)
if (Errors > 0)
sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+#if _FFR_EAI
+ if (e->e_smtputf8)
+ {
+ protocol = GET_PROTOCOL();
+ macdefine(&e->e_macro, A_PERM, 'r', protocol);
+ }
+
+ /* UTF8 addresses are only legal with SMTPUTF8 */
+ if (!e->e_smtputf8 && !addr_is_ascii(e->e_from.q_paddr))
+ {
+ usrerr("553 5.6.7 That address requires SMTPUTF8");
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+ }
+#endif
+
#if SASL
# if _FFR_AUTH_PASSING
/* set the default AUTH= if the sender didn't */
@@ -2723,7 +2855,7 @@ smtp(nullserver, d_flags, e)
/* make the "real" sender address available */
macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
e->e_from.q_paddr);
-#endif /* _FFR_MAIL_MACRO */
+#endif
if (rscheck("check_mail", addr,
NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
NULL, e->e_id, NULL, NULL) != EX_OK ||
@@ -2753,7 +2885,7 @@ smtp(nullserver, d_flags, e)
!enoughdiskspace(e->e_msgsize, e)
#if _FFR_ANY_FREE_FS
&& !filesys_free(e->e_msgsize)
-#endif /* _FFR_ANY_FREE_FS */
+#endif
)
{
/*
@@ -2894,7 +3026,7 @@ smtp(nullserver, d_flags, e)
if (!SM_IS_INTERACTIVE(e->e_sendmode)
#if _FFR_DM_ONE
&& (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
-#endif /* _FFR_DM_ONE */
+#endif
)
e->e_flags |= EF_VRFYONLY;
@@ -2908,8 +3040,8 @@ smtp(nullserver, d_flags, e)
** as QS_DONTSEND.
*/
- if (!(smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags)) &&
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags) &&
(smtp.sm_milters.mis_flags &
(MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0)
e->e_flags |= EF_VRFYONLY;
@@ -2933,6 +3065,13 @@ smtp(nullserver, d_flags, e)
usrerr("501 5.0.0 Missing recipient");
goto rcpt_done;
}
+#if _FFR_EAI
+ if (!e->e_smtputf8 && !addr_is_ascii(a->q_paddr))
+ {
+ usrerr("553 5.6.7 Address requires SMTPUTF8");
+ goto rcpt_done;
+ }
+#endif
if (delimptr != NULL && *delimptr != '\0')
*delimptr++ = '\0';
@@ -3081,8 +3220,8 @@ smtp(nullserver, d_flags, e)
/* Is this needed? */
#if !MILTER
rcpt_done:
-#endif /* !MILTER */
-
+#endif
+
macdefine(&e->e_macro, A_PERM,
macid("{rcpt_mailer}"), NULL);
macdefine(&e->e_macro, A_PERM,
@@ -3226,7 +3365,7 @@ smtp(nullserver, d_flags, e)
vrfyqueue = NULL;
if (vrfy)
e->e_flags |= EF_VRFYONLY;
- while (*p != '\0' && isascii(*p) && isspace(*p))
+ while (*p != '\0' && SM_ISSPACE(*p))
p++;
if (*p == '\0')
{
@@ -3308,6 +3447,7 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
p, CurSmtpClient);
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(MSG_TEMPFAIL);
break;
}
@@ -3404,7 +3544,7 @@ smtp(nullserver, d_flags, e)
message("221 2.0.0 %s closing connection", MyHostName);
#if PIPELINING
(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
+#endif
if (smtp.sm_nrcpts > 0)
logundelrcpts(e, "aborted by sender", 9, false);
@@ -3416,7 +3556,7 @@ smtp(nullserver, d_flags, e)
/* shutdown TLS connection */
if (tls_active)
{
- (void) endtls(srv_ssl, "server");
+ (void) endtls(&srv_ssl, "server");
tls_active = false;
}
#endif /* STARTTLS */
@@ -3436,7 +3576,7 @@ doquit:
#if MILTER
/* close out milter filters */
milter_quit(e);
-#endif /* MILTER */
+#endif
if (tTd(92, 2))
sm_dprintf("QUIT: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
@@ -3574,7 +3714,7 @@ doquit:
}
#if SASL
}
-#endif /* SASL */
+#endif
}
SM_EXCEPT(exc, "[!F]*")
{
@@ -3607,7 +3747,7 @@ smtp_data(smtp, e)
{
#if MILTER
bool milteraccept;
-#endif /* MILTER */
+#endif
bool aborting;
bool doublequeue;
bool rv = true;
@@ -3657,8 +3797,9 @@ smtp_data(smtp, e)
#if _FFR_MILTER_ENHSC
if (ISSMTPCODE(response))
(void) extenhsc(response + 4, ' ', e->e_enhsc);
-#endif /* _FFR_MILTER_ENHSC */
+#endif
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(response);
if (strncmp(response, "421 ", 4) == 0
|| strncmp(response, "421-", 4) == 0)
@@ -3678,7 +3819,7 @@ smtp_data(smtp, e)
#if _FFR_MILTER_ENHSC
(void) sm_strlcpy(e->e_enhsc, "5.7.1",
sizeof(e->e_enhsc));
-#endif /* _FFR_MILTER_ENHSC */
+#endif
usrerr("550 5.7.1 Command rejected");
return true;
@@ -3699,7 +3840,8 @@ smtp_data(smtp, e)
}
#if _FFR_MILTER_ENHSC
(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
-#endif /* _FFR_MILTER_ENHSC */
+#endif
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(MSG_TEMPFAIL);
return true;
@@ -3778,7 +3920,8 @@ smtp_data(smtp, e)
#if _FFR_MILTER_ENHSC
if (ISSMTPCODE(response))
(void) extenhsc(response + 4, ' ', e->e_enhsc);
-#endif /* _FFR_MILTER_ENHSC */
+#endif
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(response);
if (strncmp(response, "421 ", 4) == 0
|| strncmp(response, "421-", 4) == 0)
@@ -3809,7 +3952,8 @@ smtp_data(smtp, e)
milteraccept = false;
#if _FFR_MILTER_ENHSC
(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
-#endif /* _FFR_MILTER_ENHSC */
+#endif
+ /* Can't use ("%s", ...) due to usrerr() requirements */
usrerr(MSG_TEMPFAIL);
break;
@@ -3994,7 +4138,7 @@ smtp_data(smtp, e)
#if NAMED_BIND
_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-#endif /* NAMED_BIND */
+#endif
#if _FFR_PROXY
if (SM_PROXY_REQ == e->e_sendmode)
@@ -4164,7 +4308,7 @@ smtp_data(smtp, e)
message("250 2.0.0 %s Message accepted for delivery", id);
#if _FFR_PROXY
}
-#endif /* _FFR_PROXY */
+#endif
CurEnv->e_id = oldid;
/* if we just queued, poke it */
@@ -4309,7 +4453,7 @@ logundelrcpts(e, msg, level, all)
#if _FFR_MILTER_ENHSC
(a->q_status == NULL && e->e_enhsc[0] != '\0')
? e->e_enhsc :
-#endif /* _FFR_MILTER_ENHSC */
+#endif
a->q_status,
msg, NULL, (time_t) 0, e, a, EX_OK /* ??? */);
}
@@ -4483,9 +4627,9 @@ skipword(p, w)
q = p;
/* find end of word */
- while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
+ while (*p != '\0' && *p != ':' && !(SM_ISSPACE(*p)))
p++;
- while (isascii(*p) && isspace(*p))
+ while (SM_ISSPACE(*p))
*p++ = '\0';
if (*p != ':')
{
@@ -4542,7 +4686,7 @@ reset_mail_esmtp_args(e)
# if _FFR_AUTH_PASSING
macdefine(&BlankEnvelope.e_macro, A_PERM,
macid("{auth_author}"), NULL);
-# endif /* _FFR_AUTH_PASSING */
+# endif
#endif /* SASL */
/* "by" */
@@ -4728,7 +4872,7 @@ mail_esmtp_args(a, kp, vp, e)
# if _FFR_AUTH_PASSING
macdefine(&BlankEnvelope.e_macro, A_PERM,
macid("{auth_author}"), NULL);
-# endif /* _FFR_AUTH_PASSING */
+# endif
}
else
{
@@ -4820,6 +4964,17 @@ mail_esmtp_args(a, kp, vp, e)
/* XXX: check whether more characters follow? */
}
+#if _FFR_EAI
+ else if (sm_strcasecmp(kp, "smtputf8") == 0)
+ {
+ if (!bitset(SRV_OFFER_EAI, e->e_features))
+ {
+ usrerr("504 5.7.0 Sorry, SMTPUTF8 not supported/enabled");
+ /* NOTREACHED */
+ }
+ e->e_smtputf8 = true;
+ }
+#endif
else
{
usrerr("555 5.5.4 %s parameter unrecognized", kp);
@@ -5174,11 +5329,14 @@ static struct
{ 'C', SRV_REQ_SEC },
{ 'D', SRV_OFFER_DSN },
{ 'E', SRV_OFFER_ETRN },
+#if _FFR_EAI
+ { 'I', SRV_OFFER_EAI },
+#endif
{ 'L', SRV_REQ_AUTH },
#if PIPELINING
# if _FFR_NO_PIPE
{ 'N', SRV_NO_PIPE },
-# endif /* _FFR_NO_PIPE */
+# endif
{ 'P', SRV_OFFER_PIPE },
#endif /* PIPELINING */
{ 'R', SRV_VRFY_CLT }, /* same as V; not documented */
@@ -5453,4 +5611,140 @@ reset_saslconn(sasl_conn_t **conn, char *hostname,
# endif /* SASL >= 20000 */
return SASL_OK;
}
+
+/*
+** GET_SASL_USER -- extract user part from SASL reply
+**
+** Parameters:
+** val -- sasl reply (may contain NUL)
+** len -- length of val
+** auth_type -- auth_type (can be NULL)
+** user -- output buffer for extract user
+** user_len -- length of output buffer (user)
+**
+** Returns:
+** none.
+**
+** Note: val is supplied by the client and hence may contain "bad"
+** (non-printable) characters, but the returned value (user)
+** is only used for logging which converts those characters.
+*/
+
+static void
+get_sasl_user(val, len, auth_type, user, user_len)
+ char *val;
+ unsigned int len;
+ const char *auth_type;
+ char *user;
+ size_t user_len;
+{
+ unsigned int u;
+
+ SM_ASSERT(val != NULL);
+ SM_ASSERT(user != NULL);
+ SM_ASSERT(user_len > 0);
+
+ *user = '\0';
+ if (NULL == auth_type || '\0' == *auth_type)
+ return;
+ if (0 == len)
+ return;
+
+# define DIGMD5U "username=\""
+# define DIGMD5U_L (sizeof(DIGMD5U) - 1)
+ if (sm_strcasecmp(auth_type, "digest-md5") == 0 &&
+ strncmp(val, DIGMD5U, DIGMD5U_L) == 0)
+ {
+ char *s;
+
+ val += DIGMD5U_L;
+ if (len <= DIGMD5U_L)
+ return;
+ len -= DIGMD5U_L;
+
+ /* format? could there be a quoted '"'? */
+ for (s = val, u = 0; *s != '\0' && u < len; s++)
+ {
+ if ('"' == *s)
+ {
+ *s = '\0';
+ break;
+ }
+ if ('\\' == *s)
+ {
+ ++s;
+ if ('\0' == *s)
+ break;
+ }
+ }
+ }
+ else if (sm_strcasecmp(auth_type, "cram-md5") == 0)
+ {
+ char *s;
+
+ for (s = val, u = 0; *s != '\0' && u < len; s++)
+ {
+ if (' ' == *s)
+ {
+ *s = '\0';
+ break;
+ }
+ }
+ }
+
+ else if (sm_strcasecmp(auth_type, "plain") == 0 ||
+ sm_strcasecmp(auth_type, "login") == 0)
+ {
+ /*
+ ** RFC 4616: The PLAIN Simple Authentication and
+ ** Security Layer (SASL) Mechanism
+ ** message = [authzid] UTF8NUL authcid UTF8NUL passwd
+ ** each part: 1*SAFE ; MUST accept up to 255 octets
+ ** UTF8NUL = %x00 ; UTF-8 encoded NUL character
+ **
+ ** draft-murchison-sasl-login: it's just username by its own
+ */
+
+ for (u = 0; u < len; u++)
+ {
+ if (val[u] == '\0')
+ {
+ val[u] = '/';
+ (void) sm_strlcpy(user,
+ val + ((0 == u) ? 1 : 0),
+ user_len);
+ return;
+ }
+ }
+ }
+ else
+ {
+ /*
+ ** Extracting the "user" from other mechanisms
+ ** is currently not supported.
+ */
+
+ return;
+ }
+
+ /*
+ ** Does the input buffer has an NUL in it so it can be treated
+ ** as a C string?
+ */
+
+ /* SM_ASSERT(len > 0); see above */
+ u = len - 1;
+ if (val[u] != '\0')
+ {
+ for (u = 0; u < len; u++)
+ {
+ if (val[u] == '\0')
+ break;
+ }
+ }
+ if (val[u] != '\0')
+ user_len = SM_MIN(len, user_len);
+
+ (void) sm_strlcpy(user, val, user_len);
+}
#endif /* SASL */