summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src/srvrsmtp.c
diff options
context:
space:
mode:
authorgshapiro <gshapiro@FreeBSD.org>2002-02-17 21:56:45 +0000
committergshapiro <gshapiro@FreeBSD.org>2002-02-17 21:56:45 +0000
commit8449595fe97f4474b9b9a7e4edee1ef35dcff393 (patch)
treee7a33b132264d449a512ddf4a8685df097669c1d /contrib/sendmail/src/srvrsmtp.c
parent289b381b31415647269c7520d881017e2dcb27f1 (diff)
downloadFreeBSD-src-8449595fe97f4474b9b9a7e4edee1ef35dcff393.zip
FreeBSD-src-8449595fe97f4474b9b9a7e4edee1ef35dcff393.tar.gz
Import sendmail 8.12.2
Diffstat (limited to 'contrib/sendmail/src/srvrsmtp.c')
-rw-r--r--contrib/sendmail/src/srvrsmtp.c4361
1 files changed, 2036 insertions, 2325 deletions
diff --git a/contrib/sendmail/src/srvrsmtp.c b/contrib/sendmail/src/srvrsmtp.c
index 89bcb0c..ab5d4ee 100644
--- a/contrib/sendmail/src/srvrsmtp.c
+++ b/contrib/sendmail/src/srvrsmtp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
+ * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
* All rights reserved.
* Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
* Copyright (c) 1988, 1993
@@ -11,69 +11,88 @@
*
*/
-
#include <sendmail.h>
-
-#ifndef lint
-# if SMTP
-static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.78 2001/06/26 18:52:21 gshapiro Exp $ (with SMTP)";
-# else /* SMTP */
-static char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.78 2001/06/26 18:52:21 gshapiro 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)
+#if MILTER
+# include <libmilter/mfdef.h>
+#endif /* MILTER */
+
+SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.814 2002/01/08 00:56:22 ca Exp $")
+
+#if SASL || STARTTLS
+# include <sys/time.h>
+# 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_srv = 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,
+#endif /* SASL */
+#if STARTTLS
+# include <sysexits.h>
+
+static SSL_CTX *srv_ctx = NULL; /* TLS server context */
+static SSL *srv_ssl = NULL; /* per connection context */
+
+static bool tls_ok_srv = false;
+
+extern void tls_set_verify __P((SSL_CTX *, SSL *, bool));
+# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
+ bitset(SRV_VRFY_CLT, features))
+#endif /* STARTTLS */
+
+/* server features */
+#define SRV_NONE 0x0000 /* none... */
+#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
+#define SRV_VRFY_CLT 0x0002 /* request a cert */
+#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
+#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
+#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
+#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
+#define SRV_OFFER_VERB 0x0040 /* offer VERB */
+#define SRV_OFFER_DSN 0x0080 /* offer DSN */
+#if PIPELINING
+# 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 /* PIPELINING */
+#define SRV_REQ_AUTH 0x0400 /* require AUTH */
+#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 *));
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 *));
+static void setup_smtpd_io __P((void));
extern ENVELOPE BlankEnvelope;
+#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
+ (s)++
+
/*
** SMTP -- run the SMTP protocol.
**
** Parameters:
** nullserver -- if non-NULL, rejection message for
-** all SMTP commands.
+** (almost) all SMTP commands.
+** d_flags -- daemon flags
** e -- the envelope.
**
** Returns:
** never.
**
** Side Effects:
-** Reads commands from the input channel and processes
-** them.
+** Reads commands from the input channel and processes them.
+*/
+
+/*
+** Notice: The smtp server doesn't have a session context like the client
+** side has (mci). Therefore some data (session oriented) is allocated
+** or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
+** This should be fixed in a successor version.
*/
struct cmd
@@ -83,40 +102,37 @@ struct cmd
};
/* 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 */
+#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 */
+#define CMDVERB 17 /* verb -- go into verbose mode */
/* unimplemented commands from RFC 821 */
-# define CMDUNIMPL 19 /* unimplemented rfc821 commands */
+#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 */
+#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 */
+#define CMDDBGQSHOW 24 /* showq -- show send queue */
+#define CMDDBGDEBUG 25 /* debug -- set debug mode */
/*
-** Note: If you change this list,
-** remember to update 'helpfile'
+** Note: If you change this list, remember to update 'helpfile'
*/
static struct cmd CmdTab[] =
@@ -134,18 +150,16 @@ static struct cmd CmdTab[] =
{ "ehlo", CMDEHLO },
{ "etrn", CMDETRN },
{ "verb", CMDVERB },
- { "onex", CMDONEX },
- { "xusr", CMDXUSR },
{ "send", CMDUNIMPL },
{ "saml", CMDUNIMPL },
{ "soml", CMDUNIMPL },
{ "turn", CMDUNIMPL },
-# if SASL
+#if SASL
{ "auth", CMDAUTH, },
-# endif /* SASL */
-# if STARTTLS
+#endif /* SASL */
+#if STARTTLS
{ "starttls", CMDSTLS, },
-# endif /* STARTTLS */
+#endif /* STARTTLS */
/* remaining commands are here only to trap and log attempts to use them */
{ "showq", CMDDBGQSHOW },
{ "debug", CMDDBGDEBUG },
@@ -154,20 +168,167 @@ static struct cmd CmdTab[] =
{ 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 */
+#ifndef MAXBADCOMMANDS
+# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
+#endif
+#ifndef MAXNOOPCOMMANDS
+# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
+#endif
+#ifndef MAXHELOCOMMANDS
+# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
+#endif
+#ifndef MAXVRFYCOMMANDS
+# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
+#endif
+#ifndef MAXETRNCOMMANDS
+# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
+#endif
+#ifndef MAXTIMEOUT
+# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
+#endif
+
+#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 */
+
+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 *));
+
+#define MSG_TEMPFAIL "451 4.7.1 Please try again later"
+
+#if MILTER
+# define MILTER_ABORT(e) milter_abort((e))
+# define MILTER_REPLY(str) \
+ { \
+ int savelogusrerrs = LogUsrErrs; \
+ \
+ switch (state) \
+ { \
+ case SMFIR_REPLYCODE: \
+ if (MilterLogLevel > 3) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, reject=%s", \
+ str, addr, response); \
+ LogUsrErrs = false; \
+ } \
+ usrerr(response); \
+ break; \
+ \
+ case SMFIR_REJECT: \
+ if (MilterLogLevel > 3) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
+ str, addr); \
+ LogUsrErrs = false; \
+ } \
+ usrerr("550 5.7.1 Command rejected"); \
+ break; \
+ \
+ case SMFIR_DISCARD: \
+ if (MilterLogLevel > 3) \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, discard", \
+ str, addr); \
+ e->e_flags |= EF_DISCARD; \
+ break; \
+ \
+ case SMFIR_TEMPFAIL: \
+ if (MilterLogLevel > 3) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "Milter: %s=%s, reject=%s", \
+ str, addr, MSG_TEMPFAIL); \
+ LogUsrErrs = false; \
+ } \
+ usrerr(MSG_TEMPFAIL); \
+ break; \
+ } \
+ LogUsrErrs = savelogusrerrs; \
+ if (response != NULL) \
+ sm_free(response); /* XXX */ \
+ }
+
+#else /* MILTER */
+# define MILTER_ABORT(e)
+#endif /* MILTER */
+
+/* clear all SMTP state (for HELO/EHLO/RSET) */
+#define CLEAR_STATE(cmd) \
+{ \
+ /* abort milter filters */ \
+ MILTER_ABORT(e); \
+ \
+ if (smtp.sm_nrcpts > 0) \
+ { \
+ logundelrcpts(e, cmd, 10, false); \
+ smtp.sm_nrcpts = 0; \
+ macdefine(&e->e_macro, A_PERM, \
+ macid("{nrcpts}"), "0"); \
+ } \
+ \
+ e->e_sendqueue = NULL; \
+ e->e_flags |= EF_CLRQUEUE; \
+ \
+ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \
+ logsender(e, NULL); \
+ e->e_flags &= ~EF_LOGSENDER; \
+ \
+ /* clean up a bit */ \
+ smtp.sm_gotmail = false; \
+ SuprErrs = true; \
+ dropenvelope(e, true, false); \
+ sm_rpool_free(e->e_rpool); \
+ e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
+ CurEnv = e; \
+}
+
+/* sleep to flatten out connection load */
+#define MIN_DELAY_LOG 15 /* wait before logging this again */
+
+/* is it worth setting the process title for 1s? */
+#define DELAY_CONN(cmd) \
+ if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \
+ { \
+ time_t dnow; \
+ \
+ sm_setproctitle(true, e, \
+ "%s: %s: delaying %s: load average: %d", \
+ qid_printname(e), CurSmtpClient, \
+ cmd, DelayLA); \
+ if (LogLevel > 8 && (dnow = curtime()) > log_delay) \
+ { \
+ sm_syslog(LOG_INFO, e->e_id, \
+ "delaying=%s, load average=%d >= %d", \
+ cmd, CurrentLA, DelayLA); \
+ log_delay = dnow + MIN_DELAY_LOG; \
+ } \
+ (void) sleep(1); \
+ sm_setproctitle(true, e, "%s %s: %.80s", \
+ qid_printname(e), CurSmtpClient, inp); \
+ }
-/* 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)
@@ -180,7 +341,6 @@ smtp(nullserver, d_flags, e)
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 */
@@ -188,72 +348,92 @@ smtp(nullserver, d_flags, e)
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 */
+ volatile unsigned int n_badcmds = 0; /* count of bad commands */
+ volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */
+ volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */
+ volatile unsigned int n_etrn = 0; /* count of ETRN */
+ volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */
+ volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
bool ok;
- volatile bool tempfail = FALSE;
-# if _FFR_MILTER
- volatile bool milterize = (nullserver == NULL);
-# endif /* _FFR_MILTER */
+#if _FFR_ADAPTIVE_EOL
+ volatile bool first;
+#endif /* _FFR_ADAPTIVE_EOL */
+ volatile bool tempfail = false;
volatile time_t wt; /* timeout after too many commands */
volatile time_t previous; /* time after checksmtpattack() */
- volatile bool lognullconnection = TRUE;
+ volatile bool lognullconnection = true;
register char *q;
+ SMTP_T smtp;
char *addr;
char *greetcode = "220";
+ char *hostname; /* my hostname ($j) */
QUEUE_CHAR *new;
int argno;
char *args[MAXSMTPARGS];
char inp[MAXLINE];
char cmdbuf[MAXLINE];
-# if SASL
+#if SASL
sasl_conn_t *conn;
volatile bool sasl_ok;
- volatile int n_auth = 0; /* count of AUTH commands */
+ volatile unsigned 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 inlen, out2len;
unsigned int outlen;
char *volatile auth_type;
char *mechlist;
- volatile int n_mechs;
- int len;
+ volatile unsigned int n_mechs;
+ unsigned 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
+#endif /* SASL */
+#if STARTTLS
int r;
int rfd, wfd;
- volatile bool usetls = TRUE;
- volatile bool tls_active = FALSE;
+ volatile bool tls_active = false;
+# if _FFR_SMTP_SSL
+ volatile bool smtps = false;
+# endif /* _FFR_SMTP_SSL */
bool saveQuickAbort;
bool saveSuprErrs;
-# endif /* STARTTLS */
-
- if (fileno(OutChannel) != fileno(stdout))
+ time_t tlsstart;
+#endif /* STARTTLS */
+ volatile unsigned int features;
+#if PIPELINING
+# if _FFR_NO_PIPE
+ int np_log = 0;
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+ volatile time_t log_delay = (time_t) 0;
+
+ smtp.sm_nrcpts = 0;
+#if MILTER
+ smtp.sm_milterize = (nullserver == NULL);
+ smtp.sm_milterlist = false;
+#endif /* MILTER */
+
+ /* setup I/O fd correctly for the SMTP server */
+ setup_smtpd_io();
+
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&DebugLeakSmtp, 1))
{
- /* arrange for debugging output to go to remote host */
- (void) dup2(fileno(OutChannel), fileno(stdout));
+ sm_heap_newgroup();
+ sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
}
+#endif /* SM_HEAP_CHECK */
+
+ /* XXX the rpool should be set when e is initialized in main() */
+ e->e_rpool = sm_rpool_new_x(NULL);
+ e->e_macro.mac_rpool = e->e_rpool;
settime(e);
- (void)sm_getla(e);
+ sm_getla();
peerhostname = RealHostName;
if (peerhostname == NULL)
peerhostname = "localhost";
@@ -263,27 +443,89 @@ smtp(nullserver, d_flags, e)
CurSmtpClient = CurHostName;
/* check_relay may have set discard bit, save for later */
- discard = bitset(EF_DISCARD, e->e_flags);
+ smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
+
+#if PIPELINING
+ /* auto-flush output when reading input */
+ (void) sm_io_autoflush(InChannel, OutChannel);
+#endif /* PIPELINING */
+
+ sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
+
+ /* Set default features for server. */
+ features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
+ bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
+ | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
+ | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
+ : (SRV_OFFER_EXPN
+ | (bitset(PRIV_NOVERB, PrivacyFlags)
+ ? SRV_NONE : SRV_OFFER_VERB)))
+ | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
+ : SRV_OFFER_DSN)
+#if SASL
+ | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
+#endif /* SASL */
+#if PIPELINING
+ | SRV_OFFER_PIPE
+#endif /* PIPELINING */
+#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 */
+ ;
+ if (nullserver == NULL)
+ {
+ features = srvfeatures(e, CurSmtpClient, features);
+ if (bitset(SRV_TMP_FAIL, features))
+ {
+ if (LogLevel > 4)
+ sm_syslog(LOG_ERR, NOQID,
+ "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
+ CurSmtpClient);
+ nullserver = "450 4.3.0 Please try again later.";
+ }
+#if PIPELINING
+# if _FFR_NO_PIPE
+ else if (bitset(SRV_NO_PIPE, features))
+ {
+ /* for consistency */
+ features &= ~SRV_OFFER_PIPE;
+ }
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+ }
- sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient);
+ hostname = macvalue('j', e);
-# if SASL
- sasl_ok = FALSE; /* SASL can't be used (yet) */
+
+#if SASL
+ sasl_ok = bitset(SRV_OFFER_AUTH, features);
n_mechs = 0;
+ authenticating = SASL_NOT_AUTH;
/* 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)
+ if (sasl_ok)
+ {
+# 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 */
+ sasl_ok = result == SASL_OK;
+ if (!sasl_ok)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, NOQID,
+ "AUTH error: sasl_server_new failed=%d",
+ result);
+ }
+ }
+ if (sasl_ok)
{
- sasl_ok = TRUE;
-
/*
** SASL set properties for sasl
** set local/remote IP
@@ -293,8 +535,8 @@ smtp(nullserver, d_flags, e)
** Kerberos_v4
*/
-# if NETINET
- in = macvalue(macid("{daemon_family}", NULL), e);
+#if NETINET
+ in = macvalue(macid("{daemon_family}"), e);
if (in != NULL && strcmp(in, "inet") == 0)
{
SOCKADDR_LEN_T addrsize;
@@ -302,135 +544,148 @@ smtp(nullserver, d_flags, e)
struct sockaddr_in saddr_r;
addrsize = sizeof(struct sockaddr_in);
- if (getpeername(fileno(InChannel),
+ if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
+ NULL),
(struct sockaddr *)&saddr_r,
&addrsize) == 0)
{
sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
addrsize = sizeof(struct sockaddr_in);
- if (getsockname(fileno(InChannel),
+ if (getsockname(sm_io_getinfo(InChannel,
+ SM_IO_WHAT_FD,
+ NULL),
(struct sockaddr *)&saddr_l,
&addrsize) == 0)
sasl_setprop(conn, SASL_IP_LOCAL,
&saddr_l);
}
}
-# endif /* NETINET */
+#endif /* NETINET */
- authenticating = SASL_NOT_AUTH;
auth_type = NULL;
mechlist = NULL;
user = NULL;
-# if 0
- define(macid("{auth_author}", NULL), NULL, &BlankEnvelope);
-# endif /* 0 */
+# if 0
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{auth_author}"), NULL);
+# 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() */
+# if STARTTLS
+# endif /* STARTTLS */
{
- ssp.max_ssf = INT_MAX;
+ ssp.max_ssf = MaxSLBits;
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
+ ** currently we have none so zero
*/
+
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 */
+#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,
- NULL) != EX_OK || Errors > 0)
- usetls = FALSE;
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
-# endif /* _FFR_TLS_O_T */
-# endif /* STARTTLS */
-
-# if _FFR_MILTER
- if (milterize)
+#if MILTER
+ if (smtp.sm_milterize)
{
char state;
/* initialize mail filter connection */
- milter_init(e, &state);
+ smtp.sm_milterlist = milter_init(e, &state);
switch (state)
{
case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: inititalization failed, rejecting commands");
greetcode = "554";
nullserver = "Command rejected";
- milterize = FALSE;
+ smtp.sm_milterize = false;
break;
case SMFIR_TEMPFAIL:
- tempfail = TRUE;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: inititalization failed, temp failing commands");
+ tempfail = true;
+ smtp.sm_milterize = false;
break;
}
}
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
+ char *response;
- (void) milter_connect(peerhostname, RealHostAddr,
- e, &state);
+ response = milter_connect(peerhostname, RealHostAddr,
+ e, &state);
switch (state)
{
case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: connect: host=%s, addr=%s, rejecting commands",
+ peerhostname,
+ anynet_ntoa(&RealHostAddr));
greetcode = "554";
nullserver = "Command rejected";
- milterize = FALSE;
+ smtp.sm_milterize = false;
break;
case SMFIR_TEMPFAIL:
- tempfail = TRUE;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: connect: host=%s, addr=%s, temp failing commands",
+ peerhostname,
+ anynet_ntoa(&RealHostAddr));
+ tempfail = true;
+ smtp.sm_milterize = false;
break;
}
+ if (response != NULL)
+
+ sm_free(response); /* XXX */
}
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
+
+#if STARTTLS
+# if _FFR_SMTP_SSL
+ /* If this an smtps connection, start TLS now */
+ smtps = bitnset(D_SMTPS, d_flags);
+ if (smtps)
+ goto starttls;
+
+ greeting:
+
+# endif /* _FFR_SMTP_SSL */
+#endif /* STARTTLS */
/* output the first line, inserting "ESMTP" as second word */
- expand(SmtpGreeting, inp, sizeof inp, e);
+ if (*greetcode == '5')
+ (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
+ hostname);
+ else
+ expand(SmtpGreeting, inp, sizeof inp, e);
+
p = strchr(inp, '\n');
if (p != NULL)
*p++ = '\0';
@@ -438,10 +693,10 @@ smtp(nullserver, d_flags, e)
if (id == NULL)
id = &inp[strlen(inp)];
if (p == NULL)
- snprintf(cmdbuf, sizeof cmdbuf,
+ (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
"%s %%.*s ESMTP%%s", greetcode);
else
- snprintf(cmdbuf, sizeof cmdbuf,
+ (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
"%s-%%.*s ESMTP%%s", greetcode);
message(cmdbuf, (int) (id - inp), inp, id);
@@ -451,70 +706,84 @@ smtp(nullserver, d_flags, e)
*p++ = '\0';
if (isascii(*id) && isspace(*id))
id++;
- (void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode);
+ (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
message(cmdbuf, id);
}
if (id != NULL)
{
if (isascii(*id) && isspace(*id))
id++;
- (void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode);
+ (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
message(cmdbuf, id);
}
protocol = NULL;
sendinghost = macvalue('s', e);
- gothello = FALSE;
- gotmail = FALSE;
+
+#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_ADAPTIVE_EOL
+ first = true;
+#endif /* _FFR_ADAPTIVE_EOL */
+ gothello = false;
+ smtp.sm_gotmail = false;
for (;;)
{
- /* arrange for backout */
- (void) setjmp(TopFrame);
- QuickAbort = FALSE;
- HoldErrs = FALSE;
- SuprErrs = FALSE;
- LogUsrErrs = FALSE;
- OnlyOneError = TRUE;
+ SM_TRY
+ {
+ 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);
+ (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
/* read the input line */
SmtpPhase = "server cmd read";
- sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient);
-# if SASL
+ sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
+#if SASL
/*
- ** SMTP AUTH requires accepting any length,
- ** at least for challenge/response
- ** XXX
+ ** XXX SMTP AUTH requires accepting any length,
+ ** at least for challenge/response
*/
-# endif /* SASL */
+#endif /* SASL */
/* handle errors */
- if (ferror(OutChannel) ||
+ if (sm_io_error(OutChannel) ||
(p = sfgets(inp, sizeof inp, InChannel,
TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
{
char *d;
- d = macvalue(macid("{daemon_name}", NULL), e);
+ d = macvalue(macid("{daemon_name}"), e);
if (d == NULL)
d = "stdin";
/* end of file, just die */
disconnect(1, e);
-# if _FFR_MILTER
+#if MILTER
/* close out milter filters */
milter_quit(e);
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
message("421 4.4.1 %s Lost input channel from %s",
MyHostName, CurSmtpClient);
- if (LogLevel > (gotmail ? 1 : 19))
+ if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
sm_syslog(LOG_NOTICE, e->e_id,
"lost input channel from %.100s to %s after %s",
CurSmtpClient, d,
@@ -529,20 +798,64 @@ smtp(nullserver, d_flags, e)
goto doquit;
}
+#if _FFR_ADAPTIVE_EOL
+ if (first)
+ {
+ 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,
+ "%.100s did not use CRLF",
+ CurSmtpClient);
+ }
+ }
+ first = false;
+ }
+#endif /* _FFR_ADAPTIVE_EOL */
+
/* clean up end of line */
- fixcrlf(inp, TRUE);
+ fixcrlf(inp, true);
-# if SASL
+#if PIPELINING
+# if _FFR_NO_PIPE
+ /*
+ ** if there is more input and pipelining is disabled:
+ ** delay ... (and maybe discard the input?)
+ ** XXX this doesn't really work, at least in tests using
+ ** telnet SM_IO_IS_READABLE only returns 1 if there were
+ ** more than 2 input lines available.
+ */
+
+ if (bitset(SRV_NO_PIPE, features) &&
+ sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL))
+ {
+ if (++np_log < 3)
+ sm_syslog(LOG_INFO, NOQID,
+ "unauthorized PIPELINING, sleeping");
+ sleep(1);
+ }
+
+# endif /* _FFR_NO_PIPE */
+#endif /* PIPELINING */
+
+#if SASL
if (authenticating == SASL_PROC_AUTH)
{
-# if 0
+# if 0
if (*inp == '\0')
{
authenticating = SASL_NOT_AUTH;
message("501 5.5.2 missing input");
continue;
}
-# endif /* 0 */
+# endif /* 0 */
if (*inp == '*' && *(inp + 1) == '\0')
{
authenticating = SASL_NOT_AUTH;
@@ -574,97 +887,95 @@ smtp(nullserver, d_flags, e)
authenticated:
message("235 2.0.0 OK Authenticated");
authenticating = SASL_IS_AUTH;
- define(macid("{auth_type}", NULL),
- newstr(auth_type), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro, A_TEMP,
+ macid("{auth_type}"), auth_type);
result = sasl_getprop(conn, SASL_USERNAME,
(void **)&user);
if (result != SASL_OK)
{
user = "";
- define(macid("{auth_authen}", NULL),
- NULL, &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_PERM,
+ macid("{auth_authen}"), NULL);
}
else
{
- define(macid("{auth_authen}", NULL),
- newstr(user), &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_TEMP,
+ macid("{auth_authen}"), user);
}
-# if 0
+# if 0
/* get realm? */
sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif /* 0 */
-
+# endif /* 0 */
-# if SFIO
/* get security strength (features) */
result = sasl_getprop(conn, SASL_SSF,
(void **) &ssf);
if (result != SASL_OK)
{
- define(macid("{auth_ssf}", NULL),
- "0", &BlankEnvelope);
+ macdefine(&BlankEnvelope.e_macro,
+ A_PERM,
+ macid("{auth_ssf}"), "0");
ssf = NULL;
}
else
{
char pbuf[8];
- snprintf(pbuf, sizeof pbuf, "%u", *ssf);
- define(macid("{auth_ssf}", NULL),
- newstr(pbuf), &BlankEnvelope);
+ (void) sm_snprintf(pbuf, sizeof pbuf,
+ "%u", *ssf);
+ macdefine(&BlankEnvelope.e_macro,
+ A_TEMP,
+ macid("{auth_ssf}"), pbuf);
if (tTd(95, 8))
- dprintf("SASL auth_ssf: %u\n",
- *ssf);
+ sm_dprintf("AUTH auth_ssf: %u\n",
+ *ssf);
}
+
/*
- ** only switch to encrypted connection
+ ** Only switch to encrypted connection
** if a security layer has been negotiated
*/
+
if (ssf != NULL && *ssf > 0)
{
/*
- ** convert sfio stuff to use SASL
- ** check return values
- ** if the call fails,
- ** fall back to unencrypted version
- ** unless some cf option requires
- ** encryption then the connection must
- ** be aborted
+ ** Convert I/O layer to use SASL.
+ ** If the call fails, the connection
+ ** is aborted.
*/
- if (sfdcsasl(InChannel, OutChannel,
- conn) == 0)
+
+ if (sfdcsasl(&InChannel, &OutChannel,
+ conn) == 0)
{
/* restart dialogue */
- gothello = FALSE;
- OneXact = TRUE;
n_helo = 0;
+#if PIPELINING
+ (void) sm_io_autoflush(InChannel,
+ OutChannel);
+#endif /* PIPELINING */
}
else
syserr("503 5.3.3 SASL TLS failed");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO,
- NOQID,
- "SASL: connection from %.64s: mech=%.16s, id=%.64s, bits=%d",
- CurSmtpClient,
- auth_type, user,
- *ssf);
}
-# else /* SFIO */
- if (LogLevel > 9)
+
+ /* NULL pointer ok since it's our function */
+ if (LogLevel > 8)
sm_syslog(LOG_INFO, NOQID,
- "SASL: connection from %.64s: mech=%.16s, id=%.64s",
- CurSmtpClient, auth_type,
- user);
-# endif /* SFIO */
+ "AUTH=server, relay=%.100s, authid=%.128s, mech=%.16s, bits=%d",
+ CurSmtpClient,
+ shortenstring(user, 128),
+ auth_type, *ssf);
}
else if (result == SASL_CONTINUE)
{
len = ENC64LEN(outlen);
out2 = xalloc(len);
result = sasl_encode64(out, outlen, out2, len,
- (u_int *)&out2len);
+ &out2len);
if (result != SASL_OK)
{
/* correct code? XXX */
@@ -672,7 +983,7 @@ smtp(nullserver, d_flags, e)
message("454 4.5.4 Internal error: unable to encode64");
if (LogLevel > 5)
sm_syslog(LOG_WARNING, e->e_id,
- "SASL encode64 error [%d for \"%s\"]",
+ "AUTH encode64 error [%d for \"%s\"]",
result, out);
/* start over? */
authenticating = SASL_NOT_AUTH;
@@ -681,8 +992,8 @@ smtp(nullserver, d_flags, e)
{
message("334 %s", out2);
if (tTd(95, 2))
- dprintf("SASL continue: msg='%s' len=%d\n",
- out2, out2len);
+ sm_dprintf("AUTH continue: msg='%s' len=%u\n",
+ out2, out2len);
}
}
else
@@ -691,35 +1002,27 @@ smtp(nullserver, d_flags, e)
message("500 5.7.0 authentication failed");
if (LogLevel > 9)
sm_syslog(LOG_WARNING, e->e_id,
- "AUTH failure (%s): %s (%d)",
+ "AUTH failure (%s): %s (%d) %s",
auth_type,
sasl_errstring(result, NULL,
NULL),
- result);
+ result,
+ errstr == NULL ? "" : errstr);
authenticating = SASL_NOT_AUTH;
}
}
else
{
/* don't want to do any of this if authenticating */
-# endif /* SASL */
+#endif /* SASL */
/* echo command to transcript */
if (e->e_xfp != NULL)
- fprintf(e->e_xfp, "<<< %s\n", inp);
-
- if (LogLevel >= 15)
- sm_syslog(LOG_INFO, e->e_id,
- "<-- %s",
- inp);
+ (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
+ "<<< %s\n", inp);
- if (e->e_id == NULL)
- sm_setproctitle(TRUE, e, "%s: %.80s",
- CurSmtpClient, inp);
- else
- sm_setproctitle(TRUE, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, inp);
+ if (LogLevel > 14)
+ sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
/* break off command */
for (p = inp; isascii(*p) && isspace(*p); p++)
@@ -732,24 +1035,52 @@ smtp(nullserver, d_flags, e)
*cmd = '\0';
/* throw away leading whitespace */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
/* decode command */
for (c = CmdTab; c->cmd_name != NULL; c++)
{
- if (strcasecmp(c->cmd_name, cmdbuf) == 0)
+ if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
break;
}
/* reset errors */
errno = 0;
+ /* check whether a "non-null" command has been used */
+ switch (c->cmd_code)
+ {
+#if SASL
+ case CMDAUTH:
+ /* avoid information leak; take first two words? */
+ q = "AUTH";
+ break;
+#endif /* SASL */
+
+ case CMDMAIL:
+ case CMDEXPN:
+ case CMDVRFY:
+ case CMDETRN:
+ lognullconnection = false;
+ /* FALLTHROUGH */
+ default:
+ q = inp;
+ break;
+ }
+
+ if (e->e_id == NULL)
+ sm_setproctitle(true, e, "%s: %.80s",
+ CurSmtpClient, q);
+ else
+ sm_setproctitle(true, e, "%s %s: %.80s",
+ qid_printname(e),
+ CurSmtpClient, q);
+
/*
** Process command.
**
** If we are running as a null server, return 550
- ** to everything.
+ ** to almost everything.
*/
if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
@@ -768,22 +1099,30 @@ smtp(nullserver, d_flags, e)
if (bitnset(D_ETRNONLY, d_flags) &&
nullserver == NULL)
break;
+ DELAY_CONN("ETRN");
/* FALLTHROUGH */
default:
- if (++badcommands > MAXBADCOMMANDS)
+#if MAXBADCOMMANDS > 0
+ /* theoretically this could overflow */
+ if (nullserver != NULL &&
+ ++n_badcmds > MAXBADCOMMANDS)
{
- delay *= 2;
- if (delay >= MAXTIMEOUT)
- delay = MAXTIMEOUT;
- (void) sleep(delay);
+ message("421 4.7.0 %s Too many bad commands; closing connection",
+ MyHostName);
+
+ /* arrange to ignore send list */
+ e->e_sendqueue = NULL;
+ goto doquit;
}
+#endif /* MAXBADCOMMANDS > 0 */
if (nullserver != NULL)
{
if (ISSMTPREPLY(nullserver))
usrerr(nullserver);
else
- usrerr("550 5.0.0 %s", nullserver);
+ usrerr("550 5.0.0 %s",
+ nullserver);
}
else
usrerr("452 4.4.5 Insufficient disk space; try again later");
@@ -791,21 +1130,12 @@ smtp(nullserver, d_flags, e)
}
}
- /* non-null server */
- switch (c->cmd_code)
- {
- case CMDMAIL:
- case CMDEXPN:
- case CMDVRFY:
- case CMDETRN:
- lognullconnection = FALSE;
- }
-
switch (c->cmd_code)
{
-# if SASL
+#if SASL
case CMDAUTH: /* sasl */
- if (!sasl_ok)
+ DELAY_CONN("AUTH");
+ if (!sasl_ok || n_mechs <= 0)
{
message("503 5.3.3 AUTH not available");
break;
@@ -815,7 +1145,7 @@ smtp(nullserver, d_flags, e)
message("503 5.5.0 Already Authenticated");
break;
}
- if (gotmail)
+ if (smtp.sm_gotmail)
{
message("503 5.5.0 AUTH not permitted during a mail transaction");
break;
@@ -830,13 +1160,13 @@ smtp(nullserver, d_flags, e)
break;
}
- ismore = FALSE;
+ ismore = false;
/* crude way to avoid crack attempts */
- (void) checksmtpattack(&n_auth, n_mechs + 1, TRUE,
+ (void) checksmtpattack(&n_auth, n_mechs + 1, true,
"AUTH", e);
- /* make sure it's a valid string */
+ /* make sure mechanism (p) is a valid string */
for (q = p; *q != '\0' && isascii(*q); q++)
{
if (isspace(*q))
@@ -854,7 +1184,7 @@ smtp(nullserver, d_flags, e)
/* check whether mechanism is available */
if (iteminlist(p, mechlist, " ") == NULL)
{
- message("503 5.3.3 AUTH mechanism %s not available",
+ message("503 5.3.3 AUTH mechanism %.32s not available",
p);
break;
}
@@ -862,16 +1192,16 @@ smtp(nullserver, d_flags, e)
if (ismore)
{
/* could this be shorter? XXX */
- in = xalloc(strlen(q));
+ in = sm_rpool_malloc(e->e_rpool, strlen(q));
result = sasl_decode64(q, strlen(q), in,
- (u_int *)&inlen);
+ &inlen);
if (result != SASL_OK)
{
message("501 5.5.4 cannot BASE64 decode '%s'",
q);
if (LogLevel > 5)
sm_syslog(LOG_WARNING, e->e_id,
- "SASL decode64 error [%d for \"%s\"]",
+ "AUTH decode64 error [%d for \"%s\"]",
result, q);
/* start over? */
authenticating = SASL_NOT_AUTH;
@@ -879,23 +1209,6 @@ smtp(nullserver, d_flags, e)
inlen = 0;
break;
}
-# if 0
- if (tTd(95, 99))
- {
- int i;
-
- dprintf("AUTH: more \"");
- for (i = 0; i < inlen; i++)
- {
- if (isascii(in[i]) &&
- isprint(in[i]))
- dprintf("%c", in[i]);
- else
- dprintf("_");
- }
- dprintf("\"\n");
- }
-# endif /* 0 */
}
else
{
@@ -912,11 +1225,12 @@ smtp(nullserver, d_flags, e)
message("500 5.7.0 authentication failed");
if (LogLevel > 9)
sm_syslog(LOG_ERR, e->e_id,
- "AUTH failure (%s): %s (%d)",
+ "AUTH failure (%s): %s (%d) %s",
p,
sasl_errstring(result, NULL,
NULL),
- result);
+ result,
+ errstr);
break;
}
auth_type = newstr(p);
@@ -932,14 +1246,14 @@ smtp(nullserver, d_flags, e)
len = ENC64LEN(outlen);
out2 = xalloc(len);
result = sasl_encode64(out, outlen, out2, len,
- (u_int *)&out2len);
+ &out2len);
if (result != SASL_OK)
{
message("454 4.5.4 Temporary authentication failure");
if (LogLevel > 5)
sm_syslog(LOG_WARNING, e->e_id,
- "SASL encode64 error [%d for \"%s\"]",
+ "AUTH encode64 error [%d for \"%s\"]",
result, out);
/* start over? */
@@ -950,18 +1264,18 @@ smtp(nullserver, d_flags, e)
message("334 %s", out2);
authenticating = SASL_PROC_AUTH;
}
-
break;
-# endif /* SASL */
+#endif /* SASL */
-# if STARTTLS
+#if STARTTLS
case CMDSTLS: /* starttls */
+ DELAY_CONN("STARTTLS");
if (*p != '\0')
{
message("501 5.5.2 Syntax error (no parameters allowed)");
break;
}
- if (!usetls)
+ if (!bitset(SRV_OFFER_TLS, features))
{
message("503 5.5.0 TLS not available");
break;
@@ -971,7 +1285,7 @@ smtp(nullserver, d_flags, e)
message("454 4.3.3 TLS not available after start");
break;
}
- if (gotmail)
+ if (smtp.sm_gotmail)
{
message("503 5.5.0 TLS not permitted during a mail transaction");
break;
@@ -985,32 +1299,51 @@ smtp(nullserver, d_flags, e)
usrerr("454 4.7.1 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 ?
*/
# else /* TLS_NO_RSA */
- if (SSL_CTX_need_tmp_RSA(srv_ctx) &&
- !SSL_CTX_set_tmp_rsa(srv_ctx,
- (rsa = RSA_generate_key(RSA_KEYLENGTH, RSA_F4,
- NULL, NULL)))
- )
- {
- message("454 4.3.3 TLS not available: error generating RSA temp key");
- if (rsa != NULL)
- RSA_free(rsa);
- break;
- }
# endif /* TLS_NO_RSA */
+
+# if TLS_VRFY_PER_CTX
+ /*
+ ** Note: this sets the verification globally
+ ** (per SSL_CTX)
+ ** it's ok since it applies only to one transaction
+ */
+
+ TLS_VERIFY_CLIENT();
+# endif /* TLS_VRFY_PER_CTX */
+
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 _FFR_SMTP_SSL
+ goto tls_done;
+# else /* _FFR_SMTP_SSL */
break;
+# endif /* _FFR_SMTP_SSL */
}
- rfd = fileno(InChannel);
- wfd = fileno(OutChannel);
+
+# if !TLS_VRFY_PER_CTX
+ /*
+ ** this could be used if it were possible to set
+ ** verification per SSL (connection)
+ ** not just per SSL_CTX (global)
+ */
+
+ TLS_VERIFY_CLIENT();
+# endif /* !TLS_VRFY_PER_CTX */
+
+ rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
+ wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
+
if (rfd < 0 || wfd < 0 ||
SSL_set_rfd(srv_ssl, rfd) <= 0 ||
SSL_set_wfd(srv_ssl, wfd) <= 0)
@@ -1018,27 +1351,97 @@ 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 */
}
- message("220 2.0.0 Ready to start TLS");
+# 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);
+# endif /* PIPELINING */
+
SSL_set_accept_state(srv_ssl);
# define SSL_ACC(s) SSL_accept(s)
+
+ tlsstart = curtime();
+ ssl_retry:
if ((r = SSL_ACC(srv_ssl)) <= 0)
{
int i;
+ bool timedout;
+ time_t left;
+ time_t now = curtime();
+ struct timeval tv;
/* what to do in this case? */
i = SSL_get_error(srv_ssl, r);
+
+ /*
+ ** For SSL_ERROR_WANT_{READ,WRITE}:
+ ** There is no SSL record available yet
+ ** or there is only a partial SSL record
+ ** removed from the network (socket) buffer
+ ** into the SSL buffer. The SSL_accept will
+ ** only succeed when a full SSL record is
+ ** available (assuming a "real" error
+ ** doesn't happen). To handle when a "real"
+ ** error does happen the select is set for
+ ** exceptions too.
+ ** The connection may be re-negotiated
+ ** during this time so both read and write
+ ** "want errors" need to be handled.
+ ** A select() exception loops back so that
+ ** a proper SSL error message can be gotten.
+ */
+
+ left = TimeOuts.to_starttls - (now - tlsstart);
+ timedout = left <= 0;
+ if (!timedout)
+ {
+ tv.tv_sec = left;
+ tv.tv_usec = 0;
+ }
+
+ /* XXX what about SSL_pending() ? */
+ if (!timedout && i == SSL_ERROR_WANT_READ)
+ {
+ fd_set ssl_maskr, ssl_maskx;
+
+ FD_ZERO(&ssl_maskr);
+ FD_SET(rfd, &ssl_maskr);
+ FD_ZERO(&ssl_maskx);
+ FD_SET(rfd, &ssl_maskx);
+ if (select(rfd + 1, &ssl_maskr, NULL,
+ &ssl_maskx, &tv) > 0)
+ goto ssl_retry;
+ }
+ if (!timedout && i == SSL_ERROR_WANT_WRITE)
+ {
+ fd_set ssl_maskw, ssl_maskx;
+
+ FD_ZERO(&ssl_maskw);
+ FD_SET(wfd, &ssl_maskw);
+ FD_ZERO(&ssl_maskx);
+ FD_SET(rfd, &ssl_maskx);
+ if (select(wfd + 1, NULL, &ssl_maskw,
+ &ssl_maskx, &tv) > 0)
+ goto ssl_retry;
+ }
if (LogLevel > 5)
{
- sm_syslog(LOG_WARNING, e->e_id,
- "TLS: error: accept failed=%d (%d)",
- r, i);
- if (LogLevel > 9)
- tlslogerr();
+ sm_syslog(LOG_WARNING, NOQID,
+ "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d",
+ r, i, (int) timedout);
+ if (LogLevel > 8)
+ tlslogerr("server");
}
- tls_ok_srv = FALSE;
+ tls_ok_srv = false;
SSL_free(srv_ssl);
srv_ssl = NULL;
@@ -1053,8 +1456,10 @@ smtp(nullserver, d_flags, e)
}
/* ignore return code for now, it's in {verify} */
- (void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE,
- CurSmtpClient, TRUE);
+ (void) tls_get_info(srv_ssl, true,
+ CurSmtpClient,
+ &BlankEnvelope.e_macro,
+ bitset(SRV_VRFY_CLT, features));
/*
** call Stls_client to find out whether
@@ -1063,12 +1468,13 @@ smtp(nullserver, d_flags, e)
saveQuickAbort = QuickAbort;
saveSuprErrs = SuprErrs;
- SuprErrs = TRUE;
- QuickAbort = FALSE;
+ SuprErrs = true;
+ QuickAbort = false;
if (rscheck("tls_client",
- macvalue(macid("{verify}", NULL), e),
- "STARTTLS", e, TRUE, TRUE, 6, NULL) !=
- EX_OK || Errors > 0)
+ macvalue(macid("{verify}"), e),
+ "STARTTLS", e, true, true, 5,
+ NULL, NOQID) != EX_OK ||
+ Errors > 0)
{
extern char MsgBuf[];
@@ -1080,48 +1486,36 @@ smtp(nullserver, d_flags, e)
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
- tls_ok_srv = FALSE; /* don't offer STARTTLS again */
- gothello = FALSE; /* discard info */
+ tls_ok_srv = false; /* don't offer STARTTLS again */
n_helo = 0;
- OneXact = TRUE; /* only one xaction this run */
-# if SASL
+# if SASL
if (sasl_ok)
{
char *s;
- if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL &&
- (ext_ssf.ssf = atoi(s)) > 0)
+ s = macvalue(macid("{cipher_bits}"), e);
+ if (s != NULL && (ext_ssf.ssf = atoi(s)) > 0)
{
-# if _FFR_EXT_MECH
- ext_ssf.auth_id = macvalue(macid("{cert_subject}",
- NULL),
+ ext_ssf.auth_id = macvalue(macid("{cert_subject}"),
e);
-# endif /* _FFR_EXT_MECH */
sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
&ext_ssf) == SASL_OK;
- if (mechlist != NULL)
- sm_free(mechlist);
mechlist = NULL;
if (sasl_ok)
- {
n_mechs = saslmechs(conn,
&mechlist);
- sasl_ok = n_mechs > 0;
- }
}
}
-# endif /* SASL */
+# endif /* SASL */
/* switch to secure connection */
-#if SFIO
- r = sfdctls(InChannel, OutChannel, srv_ssl);
-#else /* SFIO */
-# if _FFR_TLS_TOREK
- r = sfdctls(&InChannel, &OutChannel, srv_ssl);
-# endif /* _FFR_TLS_TOREK */
-#endif /* SFIO */
- if (r == 0)
- tls_active = TRUE;
+ if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
+ {
+ tls_active = true;
+# if PIPELINING
+ (void) sm_io_autoflush(InChannel, OutChannel);
+# endif /* PIPELINING */
+ }
else
{
/*
@@ -1132,14 +1526,26 @@ smtp(nullserver, d_flags, e)
** encrypted layer, but we could not...
** just "hang up"?
*/
+
nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
- syserr("TLS: can't switch to encrypted layer");
+ syserr("STARTTLS: can't switch to encrypted layer");
}
+# if _FFR_SMTP_SSL
+ tls_done:
+ if (smtps)
+ {
+ if (tls_active)
+ goto greeting;
+ else
+ goto doquit;
+ }
+# endif /* _FFR_SMTP_SSL */
break;
-# endif /* STARTTLS */
+#endif /* STARTTLS */
case CMDHELO: /* hello -- introduce yourself */
case CMDEHLO: /* extended hello */
+ DELAY_CONN("EHLO");
if (c->cmd_code == CMDEHLO)
{
protocol = "ESMTP";
@@ -1152,9 +1558,11 @@ smtp(nullserver, d_flags, e)
}
/* avoid denial-of-service */
- (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, TRUE,
+ (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, true,
"HELO/EHLO", e);
+#if 0
+ /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
if (gothello)
{
@@ -1162,6 +1570,7 @@ smtp(nullserver, d_flags, e)
MyHostName);
break;
}
+#endif /* 0 */
/* check for valid domain name (re 1123 5.2.5) */
if (*p == '\0' && !AllowBogusHELO)
@@ -1200,7 +1609,7 @@ smtp(nullserver, d_flags, e)
if (*q == '\0')
{
q = "pleased to meet you";
- sendinghost = newstr(p);
+ sendinghost = sm_strdup_x(p);
}
else if (!AllowBogusHELO)
{
@@ -1216,10 +1625,32 @@ smtp(nullserver, d_flags, e)
q = "accepting invalid domain name";
}
- gothello = TRUE;
+ if (gothello)
+ {
+ CLEAR_STATE(cmdbuf);
-# if _FFR_MILTER
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+#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
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
char *response;
@@ -1228,22 +1659,48 @@ smtp(nullserver, d_flags, e)
switch (state)
{
case SMFIR_REPLYCODE:
- nullserver = response;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: helo=%s, reject=%s",
+ p, response);
+ nullserver = newstr(response);
+ smtp.sm_milterize = false;
break;
case SMFIR_REJECT:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: helo=%s, reject=Command rejected",
+ p);
nullserver = "Command rejected";
- milterize = FALSE;
+ smtp.sm_milterize = false;
break;
case SMFIR_TEMPFAIL:
- tempfail = TRUE;
- milterize = FALSE;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: helo=%s, reject=%s",
+ p, MSG_TEMPFAIL);
+ tempfail = true;
+ smtp.sm_milterize = false;
break;
}
+ if (response != NULL)
+ sm_free(response);
+
+#if _FFR_QUARANTINE
+ /*
+ ** If quarantining by a connect/ehlo action,
+ ** save between messages
+ */
+
+ if (smtp.sm_quarmsg == NULL &&
+ e->e_quarmsg != NULL)
+ smtp.sm_quarmsg = newstr(e->e_quarmsg);
+#endif /* _FFR_QUARANTINE */
}
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
+ gothello = true;
/* print HELO response message */
if (c->cmd_code != CMDEHLO)
@@ -1267,48 +1724,56 @@ smtp(nullserver, d_flags, e)
** print EHLO features list
**
** Note: If you change this list,
- ** remember to update 'helpfile'
+ ** remember to update 'helpfile'
*/
message("250-ENHANCEDSTATUSCODES");
- if (!bitset(PRIV_NOEXPN, PrivacyFlags))
+#if PIPELINING
+ if (bitset(SRV_OFFER_PIPE, features))
+ message("250-PIPELINING");
+#endif /* PIPELINING */
+ if (bitset(SRV_OFFER_EXPN, features))
{
message("250-EXPN");
- if (!bitset(PRIV_NOVERB, PrivacyFlags))
+ if (bitset(SRV_OFFER_VERB, features))
message("250-VERB");
}
-# if MIME8TO7
+#if MIME8TO7
message("250-8BITMIME");
-# endif /* MIME8TO7 */
+#endif /* MIME8TO7 */
if (MaxMessageSize > 0)
message("250-SIZE %ld", MaxMessageSize);
else
message("250-SIZE");
-# if DSN
- if (SendMIMEErrors &&
- !bitset(PRIV_NORECEIPTS, PrivacyFlags))
+#if DSN
+ if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
message("250-DSN");
-# endif /* DSN */
- message("250-ONEX");
- if (!bitset(PRIV_NOETRN, PrivacyFlags) &&
- !bitnset(D_NOETRN, d_flags))
+#endif /* DSN */
+ if (bitset(SRV_OFFER_ETRN, features))
message("250-ETRN");
- message("250-XUSR");
-
-# if SASL
+#if SASL
if (sasl_ok && mechlist != NULL && *mechlist != '\0')
message("250-AUTH %s", mechlist);
-# endif /* SASL */
-# if STARTTLS
- if (tls_ok_srv && usetls)
+#endif /* SASL */
+#if STARTTLS
+ if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
message("250-STARTTLS");
-# endif /* STARTTLS */
+#endif /* STARTTLS */
+ if (DeliverByMin > 0)
+ message("250-DELIVERBY %ld",
+ (long) DeliverByMin);
+ else if (DeliverByMin == 0)
+ message("250-DELIVERBY");
+
+ /* < 0: no deliver-by */
+
message("250 HELP");
break;
case CMDMAIL: /* mail -- designate sender */
SmtpPhase = "server MAIL";
+ DELAY_CONN("MAIL");
/* check for validity of this command */
if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
@@ -1316,25 +1781,19 @@ smtp(nullserver, d_flags, e)
usrerr("503 5.0.0 Polite people say HELO first");
break;
}
- if (gotmail)
+ if (smtp.sm_gotmail)
{
usrerr("503 5.5.0 Sender already specified");
break;
}
- if (InChild)
- {
- errno = 0;
- syserr("503 5.5.0 Nested MAIL command: MAIL %s", p);
- finis(TRUE, ExitStat);
- }
-# if SASL
- if (bitnset(D_AUTHREQ, d_flags) &&
+#if SASL
+ if (bitset(SRV_REQ_AUTH, features) &&
authenticating != SASL_IS_AUTH)
{
usrerr("530 5.7.0 Authentication required");
break;
}
-# endif /* SASL */
+#endif /* SASL */
p = skipword(p, "from");
if (p == NULL)
@@ -1345,7 +1804,7 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)",
p, CurSmtpClient);
- usrerr("451 4.7.1 Please try again later");
+ usrerr(MSG_TEMPFAIL);
break;
}
@@ -1354,103 +1813,88 @@ smtp(nullserver, d_flags, e)
sendinghost = peerhostname;
- /* fork a subprocess to process this command */
- ric = runinchild("SMTP-MAIL", e);
-
- /* Catch a problem and stop processing */
- if (ric == RIC_TEMPFAIL && nullserver == NULL)
- nullserver = "452 4.3.0 Internal software error";
- if (ric != RIC_INCHILD)
- break;
+#if SM_HEAP_CHECK
+ if (sm_debug_active(&DebugLeakSmtp, 1))
+ {
+ sm_heap_newgroup();
+ sm_dprintf("smtp() heap group #%d\n",
+ sm_heap_group());
+ }
+#endif /* SM_HEAP_CHECK */
if (Errors > 0)
- goto undo_subproc_no_pm;
+ goto undo_no_pm;
if (!gothello)
{
- auth_warning(e,
- "%s didn't use HELO protocol",
- CurSmtpClient);
+ auth_warning(e, "%s didn't use HELO protocol",
+ CurSmtpClient);
}
-# ifdef PICKY_HELO_CHECK
- if (strcasecmp(sendinghost, peerhostname) != 0 &&
- (strcasecmp(peerhostname, "localhost") != 0 ||
- strcasecmp(sendinghost, MyHostName) != 0))
+#ifdef PICKY_HELO_CHECK
+ if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
+ (sm_strcasecmp(peerhostname, "localhost") != 0 ||
+ sm_strcasecmp(sendinghost, MyHostName) != 0))
{
auth_warning(e, "Host %s claimed to be %s",
- CurSmtpClient, sendinghost);
+ CurSmtpClient, sendinghost);
}
-# endif /* PICKY_HELO_CHECK */
+#endif /* PICKY_HELO_CHECK */
if (protocol == NULL)
protocol = "SMTP";
- define('r', protocol, e);
- define('s', sendinghost, e);
+ macdefine(&e->e_macro, A_PERM, 'r', protocol);
+ macdefine(&e->e_macro, A_PERM, 's', sendinghost);
if (Errors > 0)
- goto undo_subproc_no_pm;
- nrcpts = 0;
- define(macid("{ntries}", NULL), "0", e);
+ goto undo_no_pm;
+ smtp.sm_nrcpts = 0;
+ n_badrcpts = 0;
+ macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
+ macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
e->e_flags |= EF_CLRQUEUE;
- sm_setproctitle(TRUE, e, "%s %s: %.80s",
+ sm_setproctitle(true, e, "%s %s: %.80s",
qid_printname(e),
CurSmtpClient, inp);
- /* child -- go do the processing */
- if (setjmp(TopFrame) > 0)
- {
- /* this failed -- undo work */
- undo_subproc_no_pm:
- e->e_flags &= ~EF_PM_NOTIFY;
- undo_subproc:
- if (InChild)
- {
- QuickAbort = FALSE;
- SuprErrs = TRUE;
- e->e_flags &= ~EF_FATALERRS;
-
- if (LogLevel > 4 &&
- bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- finis(TRUE, ExitStat);
- }
- break;
- }
- QuickAbort = TRUE;
+ /* do the processing */
+ SM_TRY
+ {
+ QuickAbort = true;
/* must parse sender first */
delimptr = NULL;
- setsender(p, e, &delimptr, ' ', FALSE);
+ setsender(p, e, &delimptr, ' ', false);
if (delimptr != NULL && *delimptr != '\0')
*delimptr++ = '\0';
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
/* Successfully set e_from, allow logging */
e->e_flags |= EF_LOGSENDER;
/* put resulting triple from parseaddr() into macros */
if (e->e_from.q_mailer != NULL)
- define(macid("{mail_mailer}", NULL),
- e->e_from.q_mailer->m_name, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_mailer}"),
+ e->e_from.q_mailer->m_name);
else
- define(macid("{mail_mailer}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_mailer}"), NULL);
if (e->e_from.q_host != NULL)
- define(macid("{mail_host}", NULL),
- e->e_from.q_host, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_host}"),
+ e->e_from.q_host);
else
- define(macid("{mail_host}", NULL),
- "localhost", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_host}"), "localhost");
if (e->e_from.q_user != NULL)
- define(macid("{mail_addr}", NULL),
- e->e_from.q_user, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_addr}"),
+ e->e_from.q_user);
else
- define(macid("{mail_addr}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{mail_addr}"), NULL);
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
/* check for possible spoofing */
if (RealUid != 0 && OpMode == MD_SMTP &&
@@ -1476,8 +1920,7 @@ smtp(nullserver, d_flags, e)
char *equal = NULL;
/* locate the beginning of the keyword */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
if (*p == '\0')
break;
kp = p;
@@ -1502,7 +1945,7 @@ smtp(nullserver, d_flags, e)
*p++ = '\0';
if (tTd(19, 1))
- dprintf("MAIL: got arg %s=\"%s\"\n", kp,
+ sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
vp == NULL ? "<null>" : vp);
mail_esmtp_args(kp, vp, e);
@@ -1512,17 +1955,47 @@ smtp(nullserver, d_flags, e)
if (argno >= MAXSMTPARGS - 1)
usrerr("501 5.5.4 Too many parameters");
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
}
args[argno] = NULL;
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+
+#if SASL
+# if _FFR_AUTH_PASSING
+ /* set the default AUTH= if the sender didn't */
+ if (e->e_auth_param == NULL)
+ {
+ /* XXX only do this for an MSA? */
+ e->e_auth_param = macvalue(macid("{auth_authen}"),
+ e);
+ if (e->e_auth_param == NULL)
+ e->e_auth_param = "<>";
+
+ /*
+ ** XXX should we invoke Strust_auth now?
+ ** authorizing as the client that just
+ ** authenticated, so we'll trust implicitly
+ */
+ }
+# endif /* _FFR_AUTH_PASSING */
+#endif /* SASL */
/* do config file checking of the sender */
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), "e s");
+#if _FFR_MAIL_MACRO
+ /* make the "real" sender address available */
+ macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
+ e->e_from.q_paddr);
+#endif /* _FFR_MAIL_MACRO */
if (rscheck("check_mail", addr,
- NULL, e, TRUE, TRUE, 4, NULL) != EX_OK ||
+ NULL, e, true, true, 3, NULL,
+ e->e_id) != EX_OK ||
Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), NULL);
if (MaxMessageSize > 0 &&
(e->e_msgsize > MaxMessageSize ||
@@ -1530,80 +2003,97 @@ smtp(nullserver, d_flags, e)
{
usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
MaxMessageSize);
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
}
- if (!enoughdiskspace(e->e_msgsize, TRUE))
+ /*
+ ** XXX always check whether there is at least one fs
+ ** with enough space?
+ ** However, this may not help much: the queue group
+ ** selection may later on select a FS that hasn't
+ ** enough space.
+ */
+
+ if ((NumFileSys == 1 || NumQueue == 1) &&
+ !enoughdiskspace(e->e_msgsize, e)
+#if _FFR_ANY_FREE_FS
+ && !filesys_free(e->e_msgsize)
+#endif /* _FFR_ANY_FREE_FS */
+ )
{
+ /*
+ ** We perform this test again when the
+ ** queue directory is selected, in collect.
+ */
+
usrerr("452 4.4.5 Insufficient disk space; try again later");
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
}
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-# if _FFR_MILTER
- LogUsrErrs = TRUE;
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+ LogUsrErrs = true;
+#if MILTER
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
char *response;
response = milter_envfrom(args, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- usrerr("550 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- usrerr("451 4.7.1 Please try again later");
- break;
- }
- if (response != NULL)
- sm_free(response);
+ MILTER_REPLY("from");
}
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
if (Errors > 0)
- goto undo_subproc_no_pm;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
message("250 2.1.0 Sender ok");
- gotmail = TRUE;
+ smtp.sm_gotmail = true;
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** An error occurred while processing a MAIL command.
+ ** Jump to the common error handling code.
+ */
+
+ sm_exc_free(exc);
+ goto undo_no_pm;
+ }
+ SM_END_TRY
+ break;
+
+ undo_no_pm:
+ e->e_flags &= ~EF_PM_NOTIFY;
+ undo:
break;
case CMDRCPT: /* rcpt -- designate recipient */
- if (!gotmail)
+ DELAY_CONN("RCPT");
+ if (!smtp.sm_gotmail)
{
usrerr("503 5.0.0 Need MAIL before RCPT");
break;
}
SmtpPhase = "server RCPT";
- if (setjmp(TopFrame) > 0)
- {
- e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
- break;
- }
- QuickAbort = TRUE;
- LogUsrErrs = TRUE;
+ SM_TRY
+ {
+ QuickAbort = true;
+ LogUsrErrs = true;
/* limit flooding of our machine */
- if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
+ if (MaxRcptPerMsg > 0 &&
+ smtp.sm_nrcpts >= MaxRcptPerMsg)
{
+ /* sleep(1); / * slow down? */
usrerr("452 4.5.3 Too many recipients");
- break;
+ goto rcpt_done;
}
if (e->e_sendmode != SM_DELIVER)
e->e_flags |= EF_VRFYONLY;
-# if _FFR_MILTER
+#if MILTER
/*
** If the filter will be deleting recipients,
** don't expand them at RCPT time (in the call
@@ -1615,24 +2105,46 @@ smtp(nullserver, d_flags, e)
if (milter_can_delrcpts())
e->e_flags |= EF_VRFYONLY;
-# endif /* _FFR_MILTER */
+#endif /* MILTER */
p = skipword(p, "to");
if (p == NULL)
- break;
-# if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), "e r", e);
-# endif /* _FFR_ADDR_TYPE */
- a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
-#if _FFR_ADDR_TYPE
- define(macid("{addr_type}", NULL), NULL, e);
-#endif /* _FFR_ADDR_TYPE */
+ goto rcpt_done;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), "e r");
+ a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
+ e, true);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), NULL);
+ if (BadRcptThrottle > 0 &&
+ n_badrcpts >= BadRcptThrottle)
+ {
+ if (LogLevel > 5 &&
+ n_badrcpts == BadRcptThrottle)
+ {
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: Possible SMTP RCPT flood, throttling.",
+ CurSmtpClient);
+
+ /* To avoid duplicated message */
+ n_badrcpts++;
+ }
+
+ /*
+ ** Don't use exponential backoff for now.
+ ** Some servers will open more connections
+ ** and actually overload the receiver even
+ ** more.
+ */
+
+ (void) sleep(1);
+ }
if (Errors > 0)
- break;
+ goto rcpt_done;
if (a == NULL)
{
usrerr("501 5.0.0 Missing recipient");
- break;
+ goto rcpt_done;
}
if (delimptr != NULL && *delimptr != '\0')
@@ -1640,25 +2152,26 @@ smtp(nullserver, d_flags, e)
/* put resulting triple from parseaddr() into macros */
if (a->q_mailer != NULL)
- define(macid("{rcpt_mailer}", NULL),
- a->q_mailer->m_name, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_mailer}"),
+ a->q_mailer->m_name);
else
- define(macid("{rcpt_mailer}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_mailer}"), NULL);
if (a->q_host != NULL)
- define(macid("{rcpt_host}", NULL),
- a->q_host, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_host}"), a->q_host);
else
- define(macid("{rcpt_host}", NULL),
- "localhost", e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_host}"), "localhost");
if (a->q_user != NULL)
- define(macid("{rcpt_addr}", NULL),
- a->q_user, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_addr}"), a->q_user);
else
- define(macid("{rcpt_addr}", NULL),
- NULL, e);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_addr}"), NULL);
if (Errors > 0)
- break;
+ goto rcpt_done;
/* now parse ESMTP arguments */
addr = p;
@@ -1672,8 +2185,7 @@ smtp(nullserver, d_flags, e)
char *equal = NULL;
/* locate the beginning of the keyword */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
if (*p == '\0')
break;
kp = p;
@@ -1698,7 +2210,7 @@ smtp(nullserver, d_flags, e)
*p++ = '\0';
if (tTd(19, 1))
- dprintf("RCPT: got arg %s=\"%s\"\n", kp,
+ sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
vp == NULL ? "<null>" : vp);
rcpt_esmtp_args(a, kp, vp, e);
@@ -1712,325 +2224,126 @@ smtp(nullserver, d_flags, e)
}
args[argno] = NULL;
if (Errors > 0)
- break;
+ goto rcpt_done;
/* do config file checking of the recipient */
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), "e r");
if (rscheck("check_rcpt", addr,
- NULL, e, TRUE, TRUE, 4, NULL) != EX_OK ||
+ NULL, e, true, true, 3, NULL,
+ e->e_id) != EX_OK ||
Errors > 0)
- break;
+ goto rcpt_done;
+ macdefine(&e->e_macro, A_PERM,
+ macid("{addr_type}"), NULL);
-# if _FFR_MILTER
- if (milterize && !bitset(EF_DISCARD, e->e_flags))
+#if MILTER
+ if (smtp.sm_milterlist && smtp.sm_milterize &&
+ !bitset(EF_DISCARD, e->e_flags))
{
char state;
char *response;
response = milter_envrcpt(args, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- usrerr("550 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- usrerr("451 4.7.1 Please try again later");
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-# endif /* _FFR_MILTER */
-
- define(macid("{rcpt_mailer}", NULL), NULL, e);
- define(macid("{rcpt_relay}", NULL), NULL, e);
- define(macid("{rcpt_addr}", NULL), NULL, e);
- define(macid("{dsn_notify}", NULL), NULL, e);
+ MILTER_REPLY("to");
+ }
+#endif /* MILTER */
+
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_mailer}"), NULL);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_relay}"), NULL);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{rcpt_addr}"), NULL);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_notify}"), NULL);
if (Errors > 0)
- break;
+ goto rcpt_done;
/* save in recipient list after ESMTP mods */
a = recipient(a, &e->e_sendqueue, 0, e);
if (Errors > 0)
- break;
+ goto rcpt_done;
/* no errors during parsing, but might be a duplicate */
e->e_to = a->q_paddr;
if (!QS_IS_BADADDR(a->q_state))
{
- if (e->e_queuedir == NOQDIR)
+ if (smtp.sm_nrcpts == 0)
initsys(e);
message("250 2.1.5 Recipient ok%s",
QS_IS_QUEUEUP(a->q_state) ?
" (will queue)" : "");
- nrcpts++;
+ smtp.sm_nrcpts++;
}
else
{
/* punt -- should keep message in ADDRESS.... */
usrerr("550 5.1.1 Addressee unknown");
}
+ rcpt_done:
+ if (Errors > 0)
+ ++n_badrcpts;
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /* An exception occurred while processing RCPT */
+ e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
+ ++n_badrcpts;
+ }
+ SM_END_TRY
break;
case CMDDATA: /* data -- text of mail */
- SmtpPhase = "server DATA";
- if (!gotmail)
- {
- usrerr("503 5.0.0 Need MAIL command");
- break;
- }
- else if (nrcpts <= 0)
- {
- usrerr("503 5.0.0 Need RCPT (recipient)");
- break;
- }
-
- /* put back discard bit */
- if (discard)
- e->e_flags |= EF_DISCARD;
-
- /* check to see if we need to re-expand aliases */
- /* also reset QS_BADADDR on already-diagnosted addrs */
- doublequeue = FALSE;
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (QS_IS_VERIFIED(a->q_state) &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- /* need to re-expand aliases */
- doublequeue = TRUE;
- }
- if (QS_IS_BADADDR(a->q_state))
- {
- /* make this "go away" */
- a->q_state = QS_DONTSEND;
- }
- }
-
- /* collect the text of the message */
- SmtpPhase = "collect";
- buffer_errors();
- collect(InChannel, TRUE, NULL, e);
-
-# if _FFR_MILTER
- if (milterize &&
- Errors <= 0 &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_data(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- usrerr("554 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- usrerr("451 4.7.1 Please try again later");
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-
- /* abort message filters that didn't get the body */
- if (milterize)
- milter_abort(e);
-# endif /* _FFR_MILTER */
-
- /* redefine message size */
- if ((q = macvalue(macid("{msg_size}", NULL), e))
- != NULL)
- sm_free(q);
- snprintf(inp, sizeof inp, "%ld", e->e_msgsize);
- define(macid("{msg_size}", NULL), newstr(inp), e);
- if (Errors > 0)
- {
- /* Log who the mail would have gone to */
- if (LogLevel > 8 &&
- e->e_message != NULL)
- {
- for (a = e->e_sendqueue;
- a != NULL;
- a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state))
- continue;
-
- e->e_to = a->q_paddr;
- logdelivery(NULL, NULL,
- a->q_status,
- e->e_message,
- NULL,
- (time_t) 0, e);
- }
- e->e_to = NULL;
- }
- flush_errors(TRUE);
- buffer_errors();
- goto abortmessage;
- }
-
- /* make sure we actually do delivery */
- e->e_flags &= ~EF_CLRQUEUE;
-
- /* from now on, we have to operate silently */
- buffer_errors();
- e->e_errormode = EM_MAIL;
-
- /*
- ** Arrange to send to everyone.
- ** If sending to multiple people, mail back
- ** errors rather than reporting directly.
- ** In any case, don't mail back errors for
- ** anything that has happened up to
- ** now (the other end will do this).
- ** Truncate our transcript -- the mail has gotten
- ** to us successfully, and if we have
- ** to mail this back, it will be easier
- ** on the reader.
- ** Then send to everyone.
- ** Finally give a reply code. If an error has
- ** already been given, don't mail a
- ** message back.
- ** We goose error returns by clearing error bit.
- */
-
- SmtpPhase = "delivery";
- (void) bftruncate(e->e_xfp);
- id = e->e_id;
-
- /*
- ** If a header/body check (header checks or milter)
- ** set EF_DISCARD, don't queueup the message --
- ** that would lose the EF_DISCARD bit and deliver
- ** the message.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- doublequeue = FALSE;
-
- if (doublequeue)
- {
- /* make sure it is in the queue */
- queueup(e, FALSE);
- }
- else
- {
- /* send to all recipients */
-# if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-# endif /* NAMED_BIND */
- sendall(e, SM_DEFAULT);
- }
- e->e_to = NULL;
-
- /* issue success message */
- message("250 2.0.0 %s Message accepted for delivery", id);
-
- /* if we just queued, poke it */
- if (doublequeue &&
- e->e_sendmode != SM_QUEUE &&
- e->e_sendmode != SM_DEFER)
- {
- CurrentLA = sm_getla(e);
-
- if (!shouldqueue(e->e_msgpriority, e->e_ctime))
- {
- /* close all the queue files */
- closexscript(e);
- if (e->e_dfp != NULL)
- (void) bfclose(e->e_dfp);
- e->e_dfp = NULL;
- unlockqueue(e);
-
- (void) dowork(e->e_queuedir, id,
- TRUE, TRUE, e);
- }
- }
-
- abortmessage:
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- /* if in a child, pop back to our parent */
- if (InChild)
- finis(TRUE, ExitStat);
-
- /* clean up a bit */
- gotmail = FALSE;
- dropenvelope(e, TRUE);
- CurEnv = e = newenvelope(e, CurEnv);
- e->e_flags = BlankEnvelope.e_flags;
+ DELAY_CONN("DATA");
+ smtp_data(&smtp, e);
break;
case CMDRSET: /* rset -- reset state */
-# if _FFR_MILTER
- /* abort milter filters */
- milter_abort(e);
-# endif /* _FFR_MILTER */
-
if (tTd(94, 100))
message("451 4.0.0 Test failure");
else
message("250 2.0.0 Reset state");
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- e->e_flags |= EF_CLRQUEUE;
-
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- if (InChild)
- finis(TRUE, ExitStat);
-
- /* clean up a bit */
- gotmail = FALSE;
- SuprErrs = TRUE;
- dropenvelope(e, TRUE);
- CurEnv = e = newenvelope(e, CurEnv);
+ 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 */
case CMDEXPN: /* expn -- expand address */
+ vrfy = c->cmd_code == CMDVRFY;
+ DELAY_CONN(vrfy ? "VRFY" : "EXPN");
if (tempfail)
{
if (LogLevel > 9)
sm_syslog(LOG_INFO, e->e_id,
"SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)",
- c->cmd_code == CMDVRFY ? "VRFY" : "EXPN",
+ vrfy ? "VRFY" : "EXPN",
p, CurSmtpClient);
+
+ /* RFC 821 doesn't allow 4xy reply code */
usrerr("550 5.7.1 Please try again later");
break;
}
- wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE,
- c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e);
+ wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
+ false, vrfy ? "VRFY" : "EXPN", e);
previous = curtime();
- vrfy = c->cmd_code == CMDVRFY;
if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
- PrivacyFlags))
+ PrivacyFlags))
{
if (vrfy)
message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
@@ -2050,18 +2363,15 @@ smtp(nullserver, d_flags, e)
usrerr("503 5.0.0 I demand that you introduce yourself first");
break;
}
- if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
- break;
if (Errors > 0)
- goto undo_subproc;
+ break;
if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: %s",
+ sm_syslog(LOG_INFO, e->e_id, "%.100s: %s",
CurSmtpClient,
shortenstring(inp, MAXSHORTSTR));
- if (setjmp(TopFrame) > 0)
- goto undo_subproc;
- QuickAbort = TRUE;
+ SM_TRY
+ {
+ QuickAbort = true;
vrfyqueue = NULL;
if (vrfy)
e->e_flags |= EF_VRFYONLY;
@@ -2075,9 +2385,10 @@ smtp(nullserver, d_flags, e)
{
/* do config file checking of the address */
if (rscheck(vrfy ? "check_vrfy" : "check_expn",
- p, NULL, e, TRUE, FALSE, 4, NULL)
- != EX_OK || Errors > 0)
- goto undo_subproc;
+ p, NULL, e, true, false, 3, NULL,
+ NOQID) != EX_OK ||
+ Errors > 0)
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
}
if (wt > 0)
@@ -2089,7 +2400,7 @@ smtp(nullserver, d_flags, e)
(void) sleep(t);
}
if (Errors > 0)
- goto undo_subproc;
+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
if (vrfyqueue == NULL)
{
usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
@@ -2110,13 +2421,26 @@ smtp(nullserver, d_flags, e)
printvrfyaddr(vrfyqueue, a == NULL, vrfy);
vrfyqueue = a;
}
- if (InChild)
- finis(TRUE, ExitStat);
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** An exception occurred while processing VRFY/EXPN
+ */
+
+ sm_exc_free(exc);
+ goto undo;
+ }
+ SM_END_TRY
break;
case CMDETRN: /* etrn -- force queue flush */
- if (bitset(PRIV_NOETRN, PrivacyFlags) ||
- bitnset(D_NOETRN, d_flags))
+ DELAY_CONN("ETRN");
+
+ /* Don't leak queue information via debug flags */
+ if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
+ (RealUid != 0 && RealUid != TrustedUid &&
+ OpMode == MD_SMTP))
{
/* different message for MSA ? */
message("502 5.7.0 Sorry, we do not allow this operation");
@@ -2133,7 +2457,7 @@ smtp(nullserver, d_flags, e)
sm_syslog(LOG_INFO, e->e_id,
"SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)",
p, CurSmtpClient);
- usrerr("451 4.7.1 Please try again later");
+ usrerr(MSG_TEMPFAIL);
break;
}
@@ -2144,80 +2468,119 @@ smtp(nullserver, d_flags, e)
}
/* crude way to avoid denial-of-service attacks */
- (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, TRUE,
+ (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, true,
"ETRN", e);
- /* do config file checking of the parameter */
- if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4,
- NULL) != EX_OK || Errors > 0)
+ /*
+ ** Do config file checking of the parameter.
+ ** Even though we have srv_features now, we still
+ ** need this ruleset because the former is called
+ ** when the connection has been established, while
+ ** this ruleset is called when the command is
+ ** actually issued and therefore has all information
+ ** available to make a decision.
+ */
+
+ if (rscheck("check_etrn", p, NULL, e, true, false, 3,
+ NULL, NOQID) != EX_OK || Errors > 0)
break;
if (LogLevel > 5)
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: ETRN %s",
- CurSmtpClient,
+ "%.100s: ETRN %s", CurSmtpClient,
shortenstring(p, MAXSHORTSTR));
id = p;
+ if (*id == '#')
+ {
+ int wgrp;
+
+ id++;
+ wgrp = name2qid(id);
+ if (!ISVALIDQGRP(wgrp))
+ {
+ usrerr("459 4.5.4 Queue %s unknown",
+ id);
+ break;
+ }
+ ok = run_work_group(wgrp, true, false,
+ false, true);
+ if (ok && Errors == 0)
+ message("250 2.0.0 Queuing for queue group %s started", id);
+ break;
+ }
+
if (*id == '@')
id++;
else
*--id = '@';
- new = (QUEUE_CHAR *)xalloc(sizeof(QUEUE_CHAR));
+ new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
+ if (new == NULL)
+ {
+ syserr("500 5.5.0 ETRN out of memory");
+ break;
+ }
new->queue_match = id;
+ new->queue_negate = false;
new->queue_next = NULL;
QueueLimitRecipient = new;
- ok = runqueue(TRUE, FALSE);
- sm_free(QueueLimitRecipient);
+ ok = runqueue(true, false, false, true);
+ sm_free(QueueLimitRecipient); /* XXX */
QueueLimitRecipient = NULL;
if (ok && Errors == 0)
message("250 2.0.0 Queuing for node %s started", p);
break;
case CMDHELP: /* help -- give user info */
+ DELAY_CONN("HELP");
help(p, e);
break;
case CMDNOOP: /* noop -- do nothing */
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ DELAY_CONN("NOOP");
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
"NOOP", e);
message("250 2.0.0 OK");
break;
case CMDQUIT: /* quit -- leave mail */
message("221 2.0.0 %s closing connection", MyHostName);
+#if PIPELINING
+ (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
+#endif /* PIPELINING */
+
+ if (smtp.sm_nrcpts > 0)
+ logundelrcpts(e, "aborted by sender", 9, false);
/* arrange to ignore any current send list */
e->e_sendqueue = NULL;
-# if STARTTLS
+#if STARTTLS
/* shutdown TLS connection */
if (tls_active)
{
(void) endtls(srv_ssl, "server");
- tls_active = FALSE;
+ tls_active = false;
}
-# endif /* STARTTLS */
-# if SASL
+#endif /* STARTTLS */
+#if SASL
if (authenticating == SASL_IS_AUTH)
{
sasl_dispose(&conn);
authenticating = SASL_NOT_AUTH;
+ /* XXX sasl_done(); this is a child */
}
-# endif /* SASL */
+#endif /* SASL */
doquit:
/* avoid future 050 messages */
disconnect(1, e);
-# if _FFR_MILTER
+#if MILTER
/* close out milter filters */
milter_quit(e);
-# endif /* _FFR_MILTER */
-
- if (InChild)
- ExitStat = EX_QUIT;
+#endif /* MILTER */
if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
logsender(e, NULL);
@@ -2227,49 +2590,48 @@ doquit:
{
char *d;
- d = macvalue(macid("{daemon_name}", NULL), e);
+ d = macvalue(macid("{daemon_name}"), e);
if (d == NULL)
d = "stdin";
- sm_syslog(LOG_INFO, NULL,
- "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
+
+ /*
+ ** even though this id is "bogus", it makes
+ ** it simpler to "grep" related events, e.g.,
+ ** timeouts for the same connection.
+ */
+
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
CurSmtpClient, d);
}
- finis(TRUE, ExitStat);
+#if PROFILING
+ return;
+#endif /* PROFILING */
+ finis(true, true, ExitStat);
/* NOTREACHED */
case CMDVERB: /* set verbose mode */
+ DELAY_CONN("VERB");
if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
+ !bitset(SRV_OFFER_VERB, features) ||
bitset(PRIV_NOVERB, PrivacyFlags))
{
/* this would give out the same info */
message("502 5.7.0 Verbose unavailable");
break;
}
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
+ (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
"VERB", e);
Verbose = 1;
set_delivery_mode(SM_DELIVER, e);
message("250 2.0.0 Verbose mode");
break;
- case CMDONEX: /* doing one transaction only */
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
- "ONEX", e);
- OneXact = TRUE;
- message("250 2.0.0 Only one transaction");
- break;
-
- case CMDXUSR: /* initial (user) submission */
- (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
- "XUSR", e);
- define(macid("{daemon_flags}", NULL), "c u", CurEnv);
- message("250 2.0.0 Initial submission");
- break;
-
-# if SMTPDEBUG
+#if SMTPDEBUG
case CMDDBGQSHOW: /* show queues */
- printf("Send Queue=");
- printaddr(e->e_sendqueue, TRUE);
+ (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
+ "Send Queue=");
+ printaddr(e->e_sendqueue, true);
break;
case CMDDBGDEBUG: /* set debug mode */
@@ -2278,11 +2640,12 @@ doquit:
message("200 2.0.0 Debug set");
break;
-# else /* SMTPDEBUG */
+#else /* SMTPDEBUG */
case CMDDBGQSHOW: /* show queues */
case CMDDBGDEBUG: /* set debug mode */
-# endif /* SMTPDEBUG */
+#endif /* SMTPDEBUG */
case CMDLOGBOGUS: /* bogus command */
+ DELAY_CONN("Bogus");
if (LogLevel > 0)
sm_syslog(LOG_CRIT, e->e_id,
"\"%s\" command from %.100s (%.100s)",
@@ -2291,7 +2654,8 @@ doquit:
/* FALLTHROUGH */
case CMDERROR: /* unknown command */
- if (++badcommands > MAXBADCOMMANDS)
+#if MAXBADCOMMANDS > 0
+ if (++n_badcmds > MAXBADCOMMANDS)
{
message("421 4.7.0 %s Too many bad commands; closing connection",
MyHostName);
@@ -2300,28 +2664,439 @@ doquit:
e->e_sendqueue = NULL;
goto doquit;
}
+#endif /* MAXBADCOMMANDS > 0 */
usrerr("500 5.5.1 Command unrecognized: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
case CMDUNIMPL:
+ DELAY_CONN("Unimpl");
usrerr("502 5.5.1 Command not implemented: \"%s\"",
shortenstring(inp, MAXSHORTSTR));
break;
default:
+ DELAY_CONN("default");
errno = 0;
syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
break;
}
-# if SASL
+#if SASL
}
-# endif /* SASL */
+#endif /* SASL */
+ }
+ SM_EXCEPT(exc, "[!F]*")
+ {
+ /*
+ ** The only possible exception is "E:mta.quickabort".
+ ** There is nothing to do except fall through and loop.
+ */
+ }
+ SM_END_TRY
}
+}
+/*
+** SMTP_DATA -- implement the SMTP DATA command.
+**
+** Parameters:
+** smtp -- status of SMTP connection.
+** e -- envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** possibly sends message.
+*/
+
+static void
+smtp_data(smtp, e)
+ SMTP_T *smtp;
+ ENVELOPE *e;
+{
+#if MILTER
+ bool milteraccept;
+#endif /* MILTER */
+ bool aborting;
+ bool doublequeue;
+ ADDRESS *a;
+ ENVELOPE *ee;
+ char *id;
+ char buf[32];
+
+ SmtpPhase = "server DATA";
+ if (!smtp->sm_gotmail)
+ {
+ usrerr("503 5.0.0 Need MAIL command");
+ return;
+ }
+ else if (smtp->sm_nrcpts <= 0)
+ {
+ usrerr("503 5.0.0 Need RCPT (recipient)");
+ return;
+ }
+ (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
+ if (rscheck("check_data", buf, NULL, e,
+ true, false, 3, NULL, e->e_id) != EX_OK)
+ return;
+
+ /* put back discard bit */
+ if (smtp->sm_discard)
+ e->e_flags |= EF_DISCARD;
+
+ /* check to see if we need to re-expand aliases */
+ /* also reset QS_BADADDR on already-diagnosted addrs */
+ doublequeue = false;
+ for (a = e->e_sendqueue; a != NULL; a = a->q_next)
+ {
+ if (QS_IS_VERIFIED(a->q_state) &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ /* need to re-expand aliases */
+ doublequeue = true;
+ }
+ if (QS_IS_BADADDR(a->q_state))
+ {
+ /* make this "go away" */
+ a->q_state = QS_DONTSEND;
+ }
+ }
+
+ /* collect the text of the message */
+ 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);
+
+ /* redefine message size */
+ (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
+ macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
+
+#if _FFR_CHECK_EOM
+ /* rscheck() will set Errors or EF_DISCARD if it trips */
+ (void) rscheck("check_eom", buf, NULL, e, false,
+ true, 3, NULL, e->e_id);
+#endif /* _FFR_CHECK_EOM */
+
+#if MILTER
+ milteraccept = true;
+ if (smtp->sm_milterlist && smtp->sm_milterize &&
+ Errors <= 0 &&
+ !bitset(EF_DISCARD, e->e_flags))
+ {
+ char state;
+ char *response;
+
+ response = milter_data(e, &state);
+ switch (state)
+ {
+ case SMFIR_REPLYCODE:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, reject=%s",
+ response);
+ milteraccept = false;
+ usrerr(response);
+ break;
+
+ case SMFIR_REJECT:
+ milteraccept = false;
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, reject=554 5.7.1 Command rejected");
+ usrerr("554 5.7.1 Command rejected");
+ break;
+
+ case SMFIR_DISCARD:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, discard");
+ milteraccept = false;
+ e->e_flags |= EF_DISCARD;
+ break;
+
+ case SMFIR_TEMPFAIL:
+ if (MilterLogLevel > 3)
+ sm_syslog(LOG_INFO, e->e_id,
+ "Milter: data, reject=%s",
+ MSG_TEMPFAIL);
+ milteraccept = false;
+ usrerr(MSG_TEMPFAIL);
+ break;
+ }
+ if (response != NULL)
+ sm_free(response);
+ }
+
+ /* Milter may have changed message size */
+ (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
+ macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
+
+ /* abort message filters that didn't get the body & log msg is OK */
+ if (smtp->sm_milterlist && smtp->sm_milterize)
+ {
+ milter_abort(e);
+ if (milteraccept && MilterLogLevel > 9)
+ sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
+ }
+#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)
+ ** set EF_DISCARD, don't queueup the message --
+ ** that would lose the EF_DISCARD bit and deliver
+ ** the message.
+ */
+
+ if (bitset(EF_DISCARD, e->e_flags))
+ doublequeue = false;
+
+ aborting = Errors > 0;
+ if (!aborting &&
+#if _FFR_QUARANTINE
+ (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
+#endif /* _FFR_QUARANTINE */
+ !split_by_recipient(e))
+ aborting = bitset(EF_FATALERRS, e->e_flags);
+
+ if (aborting)
+ {
+ /* Log who the mail would have gone to */
+ logundelrcpts(e, e->e_message, 8, false);
+ flush_errors(true);
+ buffer_errors();
+ goto abortmessage;
+ }
+
+ /* from now on, we have to operate silently */
+ buffer_errors();
+
+#if 0
+ /*
+ ** Clear message, it may contain an error from the SMTP dialogue.
+ ** This error must not show up in the queue.
+ ** Some error message should show up, e.g., alias database
+ ** not available, but others shouldn't, e.g., from check_rcpt.
+ */
+
+ e->e_message = NULL;
+#endif /* 0 */
+
+ /*
+ ** Arrange to send to everyone.
+ ** If sending to multiple people, mail back
+ ** errors rather than reporting directly.
+ ** In any case, don't mail back errors for
+ ** anything that has happened up to
+ ** now (the other end will do this).
+ ** Truncate our transcript -- the mail has gotten
+ ** to us successfully, and if we have
+ ** to mail this back, it will be easier
+ ** on the reader.
+ ** Then send to everyone.
+ ** Finally give a reply code. If an error has
+ ** already been given, don't mail a
+ ** message back.
+ ** We goose error returns by clearing error bit.
+ */
+
+ SmtpPhase = "delivery";
+ (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
+ id = e->e_id;
+
+#if NAMED_BIND
+ _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
+ _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
+#endif /* NAMED_BIND */
+
+ for (ee = e; ee != NULL; ee = ee->e_sibling)
+ {
+ /* make sure we actually do delivery */
+ ee->e_flags &= ~EF_CLRQUEUE;
+
+ /* from now on, operate silently */
+ ee->e_errormode = EM_MAIL;
+
+ if (doublequeue)
+ {
+ /* make sure it is in the queue */
+ queueup(ee, false, true);
+ }
+ else
+ {
+ /* send to all recipients */
+ sendall(ee, SM_DEFAULT);
+ }
+ ee->e_to = NULL;
+ }
+
+ /* issue success message */
+ message("250 2.0.0 %s Message accepted for delivery", id);
+
+ /* if we just queued, poke it */
+ if (doublequeue)
+ {
+ bool anything_to_send = false;
+
+ sm_getla();
+ for (ee = e; ee != NULL; ee = ee->e_sibling)
+ {
+ if (WILL_BE_QUEUED(ee->e_sendmode))
+ continue;
+ if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
+ {
+ 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 */
+ closexscript(ee);
+ if (ee->e_dfp != NULL)
+ {
+ (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
+ ee->e_dfp = NULL;
+ }
+ unlockqueue(ee);
+ }
+ if (anything_to_send)
+ {
+#if PIPELINING
+ /*
+ ** XXX if we don't do this, we get 250 twice
+ ** because it is also flushed in the child.
+ */
+
+ (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
+#endif /* PIPELINING */
+ (void) doworklist(e, true, true);
+ }
+ }
+
+ abortmessage:
+ if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
+ logsender(e, NULL);
+ e->e_flags &= ~EF_LOGSENDER;
+
+ /* clean up a bit */
+ smtp->sm_gotmail = false;
+
+ /*
+ ** Call dropenvelope if and only if the envelope is *not*
+ ** being processed by the child process forked by doworklist().
+ */
+
+ if (aborting || bitset(EF_DISCARD, e->e_flags))
+ dropenvelope(e, true, false);
+ else
+ {
+ for (ee = e; ee != NULL; ee = ee->e_sibling)
+ {
+#if _FFR_QUARANTINE
+ if (!doublequeue &&
+ QueueMode != QM_QUARANTINE &&
+ ee->e_quarmsg != NULL)
+ {
+ dropenvelope(ee, true, false);
+ continue;
+ }
+#endif /* _FFR_QUARANTINE */
+ if (WILL_BE_QUEUED(ee->e_sendmode))
+ dropenvelope(ee, true, false);
+ }
+ }
+ sm_rpool_free(e->e_rpool);
+
+ /*
+ ** At this point, e == &MainEnvelope, but if we did splitting,
+ ** then CurEnv may point to an envelope structure that was just
+ ** freed with the rpool. So reset CurEnv *before* calling
+ ** newenvelope.
+ */
+
+ CurEnv = 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)
+ {
+ 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 */
+}
+/*
+** LOGUNDELRCPTS -- log undelivered (or all) recipients.
+**
+** Parameters:
+** e -- envelope.
+** msg -- message for Stat=
+** level -- log level.
+** all -- log all recipients.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** logs undelivered (or all) recipients
+*/
+
+void
+logundelrcpts(e, msg, level, all)
+ ENVELOPE *e;
+ char *msg;
+ int level;
+ bool all;
+{
+ ADDRESS *a;
+
+ if (LogLevel <= level || msg == NULL || *msg == '\0')
+ return;
+
+ /* Clear $h so relay= doesn't get mislogged by logdelivery() */
+ macdefine(&e->e_macro, A_PERM, 'h', NULL);
+ /* Log who the mail would have gone to */
+ for (a = e->e_sendqueue; a != NULL; a = a->q_next)
+ {
+ if (!QS_IS_UNDELIVERED(a->q_state) && !all)
+ continue;
+ e->e_to = a->q_paddr;
+ logdelivery(NULL, NULL, a->q_status, msg, NULL,
+ (time_t) 0, e);
+ }
+ e->e_to = NULL;
}
- /*
+/*
** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
**
** Parameters:
@@ -2341,12 +3116,15 @@ doquit:
static time_t
checksmtpattack(pcounter, maxcount, waitnow, cname, e)
- volatile int *pcounter;
+ volatile unsigned int *pcounter;
int maxcount;
bool waitnow;
char *cname;
ENVELOPE *e;
{
+ if (maxcount <= 0) /* no limit */
+ return (time_t) 0;
+
if (++(*pcounter) >= maxcount)
{
time_t s;
@@ -2354,25 +3132,102 @@ checksmtpattack(pcounter, maxcount, waitnow, cname, e)
if (*pcounter == maxcount && LogLevel > 5)
{
sm_syslog(LOG_INFO, e->e_id,
- "%.100s: possible SMTP attack: command=%.40s, count=%d",
+ "%.100s: possible SMTP attack: command=%.40s, count=%u",
CurSmtpClient, cname, *pcounter);
}
s = 1 << (*pcounter - maxcount);
- if (s >= MAXTIMEOUT)
+ if (s >= MAXTIMEOUT || s <= 0)
s = MAXTIMEOUT;
+
/* sleep at least 1 second before returning */
(void) sleep(*pcounter / maxcount);
s -= *pcounter / maxcount;
if (waitnow)
{
(void) sleep(s);
- return(0);
+ return 0;
}
- return(s);
+ return s;
}
- return((time_t) 0);
+ return (time_t) 0;
}
- /*
+/*
+** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
+**
+** Parameters:
+** none.
+**
+** Returns:
+** nothing.
+**
+** Side Effects:
+** may change I/O fd.
+*/
+
+static void
+setup_smtpd_io()
+{
+ int inchfd, outchfd, outfd;
+
+ inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
+ outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
+ outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
+ if (outchfd != outfd)
+ {
+ /* arrange for debugging output to go to remote host */
+ (void) dup2(outchfd, outfd);
+ }
+
+ /*
+ ** if InChannel and OutChannel are stdin/stdout
+ ** and connected to ttys
+ ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
+ ** then "chain" them together.
+ */
+
+ if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
+ isatty(inchfd) && isatty(outchfd))
+ {
+ int inmode, outmode;
+
+ inmode = fcntl(inchfd, F_GETFL, 0);
+ if (inmode == -1)
+ {
+ if (LogLevel > 11)
+ sm_syslog(LOG_INFO, NOQID,
+ "fcntl(inchfd, F_GETFL) failed: %s",
+ sm_errstring(errno));
+ return;
+ }
+ outmode = fcntl(outchfd, F_GETFL, 0);
+ if (outmode == -1)
+ {
+ if (LogLevel > 11)
+ sm_syslog(LOG_INFO, NOQID,
+ "fcntl(outchfd, F_GETFL) failed: %s",
+ sm_errstring(errno));
+ return;
+ }
+ if (bitset(O_NONBLOCK, inmode) ||
+ bitset(O_NONBLOCK, outmode) ||
+ fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
+ return;
+ outmode = fcntl(outchfd, F_GETFL, 0);
+ if (outmode != -1 && bitset(O_NONBLOCK, outmode))
+ {
+ /* changing InChannel also changes OutChannel */
+ sm_io_automode(OutChannel, InChannel);
+ if (tTd(97, 4) && LogLevel > 9)
+ sm_syslog(LOG_INFO, NOQID,
+ "set automode for I (%d)/O (%d) in SMTP server",
+ inchfd, outchfd);
+ }
+
+ /* undo change of inchfd */
+ (void) fcntl(inchfd, F_SETFL, inmode);
+ }
+}
+/*
** SKIPWORD -- skip a fixed word.
**
** Parameters:
@@ -2396,8 +3251,7 @@ skipword(p, w)
char *firstp = p;
/* find beginning of word */
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
q = p;
/* find end of word */
@@ -2413,19 +3267,18 @@ skipword(p, w)
return NULL;
}
*p++ = '\0';
- while (isascii(*p) && isspace(*p))
- p++;
+ SKIP_SPACE(p);
if (*p == '\0')
goto syntax;
/* see if the input word matches desired word */
- if (strcasecmp(q, w))
+ if (sm_strcasecmp(q, w))
goto syntax;
return p;
}
- /*
+/*
** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
**
** Parameters:
@@ -2443,45 +3296,50 @@ mail_esmtp_args(kp, vp, e)
char *vp;
ENVELOPE *e;
{
- if (strcasecmp(kp, "size") == 0)
+ if (sm_strcasecmp(kp, "size") == 0)
{
if (vp == NULL)
{
usrerr("501 5.5.2 SIZE requires a value");
/* NOTREACHED */
}
- define(macid("{msg_size}", NULL), newstr(vp), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
+ errno = 0;
e->e_msgsize = strtol(vp, (char **) NULL, 10);
if (e->e_msgsize == LONG_MAX && errno == ERANGE)
{
usrerr("552 5.2.3 Message size exceeds maximum value");
/* NOTREACHED */
}
+ if (e->e_msgsize < 0)
+ {
+ usrerr("552 5.2.3 Message size invalid");
+ /* NOTREACHED */
+ }
}
- else if (strcasecmp(kp, "body") == 0)
+ else if (sm_strcasecmp(kp, "body") == 0)
{
if (vp == NULL)
{
usrerr("501 5.5.2 BODY requires a value");
/* NOTREACHED */
}
- else if (strcasecmp(vp, "8bitmime") == 0)
+ else if (sm_strcasecmp(vp, "8bitmime") == 0)
{
- SevenBitInput = FALSE;
+ SevenBitInput = false;
}
- else if (strcasecmp(vp, "7bit") == 0)
+ else if (sm_strcasecmp(vp, "7bit") == 0)
{
- SevenBitInput = TRUE;
+ SevenBitInput = true;
}
else
{
- usrerr("501 5.5.4 Unknown BODY type %s",
- vp);
+ usrerr("501 5.5.4 Unknown BODY type %s", vp);
/* NOTREACHED */
}
- e->e_bodytype = newstr(vp);
+ e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
}
- else if (strcasecmp(kp, "envid") == 0)
+ else if (sm_strcasecmp(kp, "envid") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
@@ -2503,10 +3361,11 @@ mail_esmtp_args(kp, vp, e)
usrerr("501 5.5.0 Duplicate ENVID parameter");
/* NOTREACHED */
}
- e->e_envid = newstr(vp);
- define(macid("{dsn_envid}", NULL), newstr(vp), e);
+ e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
+ macdefine(&e->e_macro, A_PERM,
+ macid("{dsn_envid}"), e->e_envid);
}
- else if (strcasecmp(kp, "ret") == 0)
+ else if (sm_strcasecmp(kp, "ret") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
@@ -2524,23 +3383,24 @@ mail_esmtp_args(kp, vp, e)
/* NOTREACHED */
}
e->e_flags |= EF_RET_PARAM;
- if (strcasecmp(vp, "hdrs") == 0)
+ if (sm_strcasecmp(vp, "hdrs") == 0)
e->e_flags |= EF_NO_BODY_RETN;
- else if (strcasecmp(vp, "full") != 0)
+ else if (sm_strcasecmp(vp, "full") != 0)
{
usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
/* NOTREACHED */
}
- define(macid("{dsn_ret}", NULL), newstr(vp), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
}
-# if SASL
- else if (strcasecmp(kp, "auth") == 0)
+#if SASL
+ else if (sm_strcasecmp(kp, "auth") == 0)
{
int len;
char *q;
char *auth_param; /* the value of the AUTH=x */
bool saveQuickAbort = QuickAbort;
bool saveSuprErrs = SuprErrs;
+ bool saveExitStat = ExitStat;
char pbuf[256];
if (vp == NULL)
@@ -2558,7 +3418,7 @@ mail_esmtp_args(kp, vp, e)
else
len = strlen(vp) + 1;
auth_param = xalloc(len);
- (void) strlcpy(auth_param, vp, len);
+ (void) sm_strlcpy(auth_param, vp, len);
if (!xtextok(auth_param))
{
usrerr("501 5.5.4 Syntax error in AUTH parameter value");
@@ -2567,11 +3427,11 @@ mail_esmtp_args(kp, vp, e)
}
/* XXX this might be cut off */
- snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param));
+ (void) sm_strlcpy(pbuf, xuntextify(auth_param), sizeof pbuf);
/* xalloc() the buffer instead? */
/* XXX define this always or only if trusted? */
- define(macid("{auth_author}", NULL), newstr(pbuf), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), pbuf);
/*
** call Strust_auth to find out whether
@@ -2580,42 +3440,123 @@ mail_esmtp_args(kp, vp, e)
** (required by RFC, leave it to ruleset?)
*/
- SuprErrs = TRUE;
- QuickAbort = FALSE;
+ SuprErrs = true;
+ QuickAbort = false;
if (strcmp(auth_param, "<>") != 0 &&
- (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10,
- NULL) != EX_OK || Errors > 0))
+ (rscheck("trust_auth", pbuf, NULL, e, true, false, 9,
+ NULL, NOQID) != EX_OK || Errors > 0))
{
if (tTd(95, 8))
{
q = e->e_auth_param;
- dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
+ sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
pbuf, (q == NULL) ? "" : q);
}
+
/* not trusted */
- e->e_auth_param = newstr("<>");
+ e->e_auth_param = "<>";
+# if _FFR_AUTH_PASSING
+ macdefine(&BlankEnvelope.e_macro, A_PERM,
+ macid("{auth_author}"), NULL);
+# endif /* _FFR_AUTH_PASSING */
}
else
{
if (tTd(95, 8))
- dprintf("auth=\"%.100s\" trusted\n", pbuf);
- e->e_auth_param = newstr(auth_param);
+ sm_dprintf("auth=\"%.100s\" trusted\n", pbuf);
+ e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
+ auth_param);
}
- sm_free(auth_param);
+ sm_free(auth_param); /* XXX */
/* reset values */
Errors = 0;
QuickAbort = saveQuickAbort;
SuprErrs = saveSuprErrs;
+ ExitStat = saveExitStat;
+ }
+#endif /* SASL */
+#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?')
+
+ /*
+ ** "by" is only accepted if DeliverByMin >= 0.
+ ** We maybe could add this to the list of server_features.
+ */
+
+ else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
+ {
+ char *s;
+
+ if (vp == NULL)
+ {
+ usrerr("501 5.5.2 BY= requires a value");
+ /* NOTREACHED */
+ }
+ errno = 0;
+ e->e_deliver_by = strtol(vp, &s, 10);
+ if (e->e_deliver_by == LONG_MIN ||
+ e->e_deliver_by == LONG_MAX ||
+ e->e_deliver_by > 999999999l ||
+ e->e_deliver_by < -999999999l)
+ {
+ usrerr("501 5.5.2 BY=%s out of range", vp);
+ /* NOTREACHED */
+ }
+ if (s == NULL || *s != ';')
+ {
+ usrerr("501 5.5.2 BY= missing ';'");
+ /* NOTREACHED */
+ }
+ e->e_dlvr_flag = 0;
+ ++s; /* XXX: spaces allowed? */
+ SKIP_SPACE(s);
+ switch (tolower(*s))
+ {
+ case 'n':
+ e->e_dlvr_flag = DLVR_NOTIFY;
+ break;
+ case 'r':
+ e->e_dlvr_flag = DLVR_RETURN;
+ if (e->e_deliver_by <= 0)
+ {
+ usrerr("501 5.5.4 mode R requires BY time > 0");
+ /* NOTREACHED */
+ }
+ if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
+ e->e_deliver_by < DeliverByMin)
+ {
+ usrerr("555 5.5.2 time %ld less than %ld",
+ e->e_deliver_by, (long) DeliverByMin);
+ /* NOTREACHED */
+ }
+ break;
+ default:
+ usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
+ /* NOTREACHED */
+ }
+ ++s; /* XXX: spaces allowed? */
+ SKIP_SPACE(s);
+ switch (tolower(*s))
+ {
+ case 't':
+ e->e_dlvr_flag |= DLVR_TRACE;
+ break;
+ case '\0':
+ break;
+ default:
+ usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
+ /* NOTREACHED */
+ }
+
+ /* XXX: check whether more characters follow? */
}
-# endif /* SASL */
else
{
usrerr("555 5.5.4 %s parameter unrecognized", kp);
/* NOTREACHED */
}
}
- /*
+/*
** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
**
** Parameters:
@@ -2635,7 +3576,7 @@ rcpt_esmtp_args(a, kp, vp, e)
char *vp;
ENVELOPE *e;
{
- if (strcasecmp(kp, "notify") == 0)
+ if (sm_strcasecmp(kp, "notify") == 0)
{
char *p;
@@ -2651,20 +3592,20 @@ rcpt_esmtp_args(a, kp, vp, e)
}
a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
a->q_flags |= QHASNOTIFY;
- define(macid("{dsn_notify}", NULL), newstr(vp), e);
+ macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
- if (strcasecmp(vp, "never") == 0)
+ if (sm_strcasecmp(vp, "never") == 0)
return;
for (p = vp; p != NULL; vp = p)
{
p = strchr(p, ',');
if (p != NULL)
*p++ = '\0';
- if (strcasecmp(vp, "success") == 0)
+ if (sm_strcasecmp(vp, "success") == 0)
a->q_flags |= QPINGONSUCCESS;
- else if (strcasecmp(vp, "failure") == 0)
+ else if (sm_strcasecmp(vp, "failure") == 0)
a->q_flags |= QPINGONFAILURE;
- else if (strcasecmp(vp, "delay") == 0)
+ else if (sm_strcasecmp(vp, "delay") == 0)
a->q_flags |= QPINGONDELAY;
else
{
@@ -2674,7 +3615,7 @@ rcpt_esmtp_args(a, kp, vp, e)
}
}
}
- else if (strcasecmp(kp, "orcpt") == 0)
+ else if (sm_strcasecmp(kp, "orcpt") == 0)
{
if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
{
@@ -2696,7 +3637,7 @@ rcpt_esmtp_args(a, kp, vp, e)
usrerr("501 5.5.0 Duplicate ORCPT parameter");
/* NOTREACHED */
}
- a->q_orcpt = newstr(vp);
+ a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
}
else
{
@@ -2704,11 +3645,11 @@ rcpt_esmtp_args(a, kp, vp, e)
/* NOTREACHED */
}
}
- /*
+/*
** PRINTVRFYADDR -- print an entry in the verify queue
**
** Parameters:
-** a -- the address to print
+** a -- the address to print.
** last -- set if this is the last one.
** vrfy -- set if this is a VRFY command.
**
@@ -2730,21 +3671,21 @@ printvrfyaddr(a, last, vrfy)
if (vrfy && a->q_mailer != NULL &&
!bitnset(M_VRFY250, a->q_mailer->m_flags))
- (void) strlcpy(fmtbuf, "252", sizeof fmtbuf);
+ (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
else
- (void) strlcpy(fmtbuf, "250", sizeof fmtbuf);
+ (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
fmtbuf[3] = last ? ' ' : '-';
- (void) strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
+ (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
if (a->q_fullname == NULL)
{
if ((a->q_mailer == NULL ||
a->q_mailer->m_addrtype == NULL ||
- strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+ sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
strchr(a->q_user, '@') == NULL)
- (void) strlcpy(&fmtbuf[OFFF], "<%s@%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
sizeof fmtbuf - OFFF);
else
- (void) strlcpy(&fmtbuf[OFFF], "<%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
sizeof fmtbuf - OFFF);
message(fmtbuf, a->q_user, MyHostName);
}
@@ -2752,127 +3693,27 @@ printvrfyaddr(a, last, vrfy)
{
if ((a->q_mailer == NULL ||
a->q_mailer->m_addrtype == NULL ||
- strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
+ sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
strchr(a->q_user, '@') == NULL)
- (void) strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
sizeof fmtbuf - OFFF);
else
- (void) strlcpy(&fmtbuf[OFFF], "%s <%s>",
+ (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
sizeof fmtbuf - OFFF);
message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
}
}
- /*
-** RUNINCHILD -- return twice -- once in the child, then in the parent again
-**
-** Parameters:
-** label -- a string used in error messages
-**
-** Returns:
-** RIC_INCHILD in the child
-** RIC_INPARENT in the parent
-** RIC_TEMPFAIL tempfail condition
-**
-** Side Effects:
-** none.
-*/
-
-static int
-runinchild(label, e)
- char *label;
- register ENVELOPE *e;
-{
- pid_t childpid;
-
- if (!OneXact)
- {
- extern int NumQueues;
- /*
- ** advance state of PRNG
- ** this is necessary because otherwise all child processes
- ** will produce the same PRN sequence and hence the selection
- ** of a queue directory is not "really" random.
- */
- if (NumQueues > 1)
- (void) get_random();
-
- /*
- ** Disable child process reaping, in case ETRN has preceded
- ** MAIL command, and then fork.
- */
-
- (void) blocksignal(SIGCHLD);
-
-
- childpid = dofork();
- if (childpid < 0)
- {
- syserr("451 4.3.0 %s: cannot fork", label);
- (void) releasesignal(SIGCHLD);
- return RIC_INPARENT;
- }
- if (childpid > 0)
- {
- auto int st;
-
- /* parent -- wait for child to complete */
- sm_setproctitle(TRUE, e, "server %s child wait",
- CurSmtpClient);
- st = waitfor(childpid);
- if (st == -1)
- syserr("451 4.3.0 %s: lost child", label);
- else if (!WIFEXITED(st))
- {
- syserr("451 4.3.0 %s: died on signal %d",
- label, st & 0177);
- return RIC_TEMPFAIL;
- }
-
- /* if exited on a QUIT command, complete the process */
- if (WEXITSTATUS(st) == EX_QUIT)
- {
- disconnect(1, e);
- finis(TRUE, ExitStat);
- }
-
- /* restore the child signal */
- (void) releasesignal(SIGCHLD);
-
- return RIC_INPARENT;
- }
- else
- {
- /* child */
- InChild = TRUE;
- QuickAbort = FALSE;
-
- /* Reset global flags */
- RestartRequest = NULL;
- ShutdownRequest = NULL;
- PendingSignal = 0;
-
- clearstats();
- clearenvelope(e, FALSE);
- assign_queueid(e);
- (void) setsignal(SIGCHLD, SIG_DFL);
- (void) releasesignal(SIGCHLD);
- }
- }
- return RIC_INCHILD;
-}
-
-# if SASL
-
- /*
+#if SASL
+/*
** SASLMECHS -- get list of possible AUTH mechanisms
**
** Parameters:
-** conn -- SASL connection info
-** mechlist -- output parameter for list of mechanisms
+** conn -- SASL connection info.
+** mechlist -- output parameter for list of mechanisms.
**
** Returns:
-** number of mechs
+** number of mechs.
*/
static int
@@ -2885,35 +3726,41 @@ saslmechs(conn, mechlist)
/* "user" is currently unused */
result = sasl_listmech(conn, "user", /* XXX */
"", " ", "", mechlist,
- (u_int *)&len, (u_int *)&num);
- if (result == SASL_OK && num > 0)
+ (unsigned int *)&len, (unsigned int *)&num);
+ if (result != SASL_OK)
+ {
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, NOQID,
+ "AUTH error: listmech=%d, num=%d",
+ result, num);
+ num = 0;
+ }
+ if (num > 0)
{
if (LogLevel > 11)
sm_syslog(LOG_INFO, NOQID,
- "SASL: available mech=%s, allowed mech=%s",
+ "AUTH: available mech=%s, allowed mech=%s",
*mechlist, AuthMechanisms);
- *mechlist = intersect(AuthMechanisms, *mechlist);
+ *mechlist = intersect(AuthMechanisms, *mechlist, NULL);
}
else
{
- if (LogLevel > 9)
+ *mechlist = NULL; /* be paranoid... */
+ if (result == SASL_OK && LogLevel > 9)
sm_syslog(LOG_WARNING, NOQID,
- "SASL error: listmech=%d, num=%d",
- result, num);
- num = 0;
+ "AUTH warning: no mechanisms");
}
return num;
}
-
- /*
+/*
** PROXY_POLICY -- define proxy policy for AUTH
**
** Parameters:
-** conntext -- unused
-** auth_identity -- authentication identity
-** requested_user -- authorization identity
-** user -- allowed user (output)
-** errstr -- possible error string (output)
+** context -- unused.
+** auth_identity -- authentication identity.
+** requested_user -- authorization identity.
+** user -- allowed user (output).
+** errstr -- possible error string (output).
**
** Returns:
** ok?
@@ -2932,1275 +3779,139 @@ proxy_policy(context, auth_identity, requested_user, user, errstr)
*user = newstr(auth_identity);
return SASL_OK;
}
+#endif /* SASL */
-# endif /* SASL */
-
-# if STARTTLS
-# if !TLS_NO_RSA
-RSA *rsa_tmp; /* temporary RSA key */
-static RSA * tmp_rsa_key __P((SSL *, int, int));
-# endif /* !TLS_NO_RSA */
-
-# if !NO_DH
-static DH *get_dh512 __P((void));
-
-static unsigned char dh512_p[] =
-{
- 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
- 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
- 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
- 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
- 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
- 0x47,0x74,0xE8,0x33
-};
-static unsigned char dh512_g[] =
-{
- 0x02
-};
-
-static DH *
-get_dh512()
-{
- DH *dh = NULL;
-
- if ((dh = DH_new()) == NULL)
- return(NULL);
- dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
- dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
- if ((dh->p == NULL) || (dh->g == NULL))
- return(NULL);
- return(dh);
-}
-# endif /* !NO_DH */
-
- /*
-** TLS_RAND_INIT -- initialize STARTTLS random generator
-**
-** Parameters:
-** randfile -- name of file with random data
-** logl -- loglevel
-**
-** Returns:
-** success/failure
-**
-** Side Effects:
-** initializes PRNG for tls library.
-*/
-
-#define MIN_RAND_BYTES 16 /* 128 bits */
-
-bool
-tls_rand_init(randfile, logl)
- char *randfile;
- int logl;
-{
-# ifndef HASURANDOMDEV
- /* not required if /dev/urandom exists, OpenSSL does it internally */
-#define RF_OK 0 /* randfile OK */
-#define RF_MISS 1 /* randfile == NULL || *randfile == '\0' */
-#define RF_UNKNOWN 2 /* unknown prefix for randfile */
-
-#define RI_NONE 0 /* no init yet */
-#define RI_SUCCESS 1 /* init was successful */
-#define RI_FAIL 2 /* init failed */
-
- bool ok;
- int randdef;
- static int done = RI_NONE;
-
- /*
- ** initialize PRNG
- */
-
- /* did we try this before? if yes: return old value */
- if (done != RI_NONE)
- return done == RI_SUCCESS;
-
- /* set default values */
- ok = FALSE;
- done = RI_FAIL;
- randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK;
-# if EGD
- if (randdef == RF_OK && strncasecmp(randfile, "egd:", 4) == 0)
- {
- randfile += 4;
- if (RAND_egd(randfile) < 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: RAND_egd(%s) failed: random number generator not seeded",
- randfile);
- }
- else
- ok = TRUE;
- }
- else
-# endif /* EGD */
- if (randdef == RF_OK && strncasecmp(randfile, "file:", 5) == 0)
- {
- int fd;
- long sff;
- struct stat st;
-
- randfile += 5;
- sff = SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES
- | SFF_NOGRFILES | SFF_NOWRFILES
- | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
- if ((fd = safeopen(randfile, O_RDONLY, 0, sff)) >= 0)
- {
- if (fstat(fd, &st) < 0)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "TLS: can't fstat(%s)",
- randfile);
- }
- else
- {
- bool use, problem;
-
- use = TRUE;
- problem = FALSE;
- if (st.st_mtime + 600 < curtime())
- {
- use = bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail);
- problem = TRUE;
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "TLS: RandFile %s too old: %s",
- randfile,
- use ? "unsafe" :
- "unusable");
- }
- if (use && st.st_size < MIN_RAND_BYTES)
- {
- use = bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail);
- problem = TRUE;
- if (LogLevel > logl)
- sm_syslog(LOG_ERR, NOQID,
- "TLS: size(%s) < %d: %s",
- randfile,
- MIN_RAND_BYTES,
- use ? "unsafe" :
- "unusable");
- }
- if (use)
- ok = RAND_load_file(randfile, -1) >=
- MIN_RAND_BYTES;
- if (use && !ok)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING,
- NOQID,
- "TLS: RAND_load_file(%s) failed: random number generator not seeded",
- randfile);
- }
- if (problem)
- ok = FALSE;
- }
- if (ok || bitnset(DBS_INSUFFICIENTENTROPY,
- DontBlameSendmail))
- {
- /* add this even if fstat() failed */
- RAND_seed((void *) &st, sizeof st);
- }
- (void) close(fd);
- }
- else
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Warning: safeopen(%s) failed",
- randfile);
- }
- }
- else if (randdef == RF_OK)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Error: no proper random file definition %s",
- randfile);
- randdef = RF_UNKNOWN;
- }
- if (randdef == RF_MISS)
- {
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Error: missing random file definition");
- }
- if (!ok && bitnset(DBS_INSUFFICIENTENTROPY, DontBlameSendmail))
- {
- int i;
- long r;
- unsigned char buf[MIN_RAND_BYTES];
-
- /* assert((MIN_RAND_BYTES % sizeof(long)) == 0); */
- for (i = 0; i <= sizeof(buf) - sizeof(long); i += sizeof(long))
- {
- r = get_random();
- (void) memcpy(buf + i, (void *) &r, sizeof(long));
- }
- RAND_seed(buf, sizeof buf);
- if (LogLevel > logl)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: Warning: random number generator not properly seeded");
- ok = TRUE;
- }
- done = ok ? RI_SUCCESS : RI_FAIL;
- return ok;
-# else /* !HASURANDOMDEV */
- return TRUE;
-# endif /* !HASURANDOMDEV */
-}
-
+#if STARTTLS
/*
-** status in initialization
-** these flags keep track of the status of the initialization
-** i.e., whether a file exists (_EX) and whether it can be used (_OK)
-** [due to permissions]
-*/
-#define TLS_S_NONE 0x00000000 /* none yet */
-#define TLS_S_CERT_EX 0x00000001 /* CERT file exists */
-#define TLS_S_CERT_OK 0x00000002 /* CERT file is ok */
-#define TLS_S_KEY_EX 0x00000004 /* KEY file exists */
-#define TLS_S_KEY_OK 0x00000008 /* KEY file is ok */
-#define TLS_S_CERTP_EX 0x00000010 /* CA CERT PATH exists */
-#define TLS_S_CERTP_OK 0x00000020 /* CA CERT PATH is ok */
-#define TLS_S_CERTF_EX 0x00000040 /* CA CERT FILE exists */
-#define TLS_S_CERTF_OK 0x00000080 /* CA CERT FILE is ok */
-
-# if _FFR_TLS_1
-#define TLS_S_CERT2_EX 0x00001000 /* 2nd CERT file exists */
-#define TLS_S_CERT2_OK 0x00002000 /* 2nd CERT file is ok */
-#define TLS_S_KEY2_EX 0x00004000 /* 2nd KEY file exists */
-#define TLS_S_KEY2_OK 0x00008000 /* 2nd KEY file is ok */
-# endif /* _FFR_TLS_1 */
-
-#define TLS_S_DH_OK 0x00200000 /* DH cert is ok */
-#define TLS_S_DHPAR_EX 0x00400000 /* DH param file exists */
-#define TLS_S_DHPAR_OK 0x00800000 /* DH param file is ok to use */
-
- /*
-** TLS_OK_F -- can var be an absolute filename?
-**
-** Parameters:
-** var -- filename
-** fn -- what is the filename used for?
-**
-** Returns:
-** ok?
-*/
-
-static bool
-tls_ok_f(var, fn)
- char *var;
- char *fn;
-{
- /* must be absolute pathname */
- if (var != NULL && *var == '/')
- return TRUE;
- if (LogLevel > 12)
- sm_syslog(LOG_WARNING, NOQID, "TLS: file %s missing", fn);
- return FALSE;
-}
-
- /*
-** TLS_SAFE_F -- is a file safe to use?
-**
-** Parameters:
-** var -- filename
-** sff -- flags for safefile()
-**
-** Returns:
-** ok?
-*/
-
-static bool
-tls_safe_f(var, sff)
- char *var;
- long sff;
-{
- int ret;
-
- if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff,
- S_IRUSR, NULL)) == 0)
- return TRUE;
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID, "TLS: file %s unsafe: %s",
- var, errstring(ret));
- return FALSE;
-}
-
-/*
-** TLS_OK_F -- macro to simplify calls to tls_ok_f
-**
-** Parameters:
-** var -- filename
-** fn -- what is the filename used for?
-** req -- is the file required?
-** st -- status bit to set if ok
-**
-** Side Effects:
-** uses r, ok; may change ok and status.
-**
-*/
-
-#define TLS_OK_F(var, fn, req, st) if (ok) \
- { \
- r = tls_ok_f(var, fn); \
- if (r) \
- status |= st; \
- else if (req) \
- ok = FALSE; \
- }
-
-/*
-** TLS_UNR -- macro to return whether a file should be unreadable
-**
-** Parameters:
-** bit -- flag to test
-** req -- flags
-**
-** Returns:
-** 0/SFF_NORFILES
-*/
-#define TLS_UNR(bit, req) (bitset(bit, req) ? SFF_NORFILES : 0)
-
-/*
-** TLS_SAFE_F -- macro to simplify calls to tls_safe_f
-**
-** Parameters:
-** var -- filename
-** sff -- flags for safefile()
-** req -- is the file required?
-** ex -- does the file exist?
-** st -- status bit to set if ok
-**
-** Side Effects:
-** uses r, ok, ex; may change ok and status.
-**
-*/
-
-#define TLS_SAFE_F(var, sff, req, ex, st) if (ex && ok) \
- { \
- r = tls_safe_f(var, sff); \
- if (r) \
- status |= st; \
- else if (req) \
- ok = FALSE; \
- }
- /*
-** INIT_TLS_LIBRARY -- calls functions which setup TLS library for global use
+** INITSRVTLS -- initialize server side TLS
**
** Parameters:
-** none.
+** tls_ok -- should tls initialization be done?
**
** Returns:
** succeeded?
**
** Side Effects:
-** Sets tls_ok_srv static, even when called from main()
+** sets tls_ok_srv which is a static variable in this module.
+** Do NOT remove assignments to it!
*/
bool
-init_tls_library()
+initsrvtls(tls_ok)
+ bool tls_ok;
{
- /*
- ** basic TLS initialization
- ** ignore result for now
- */
-
- SSL_library_init();
- SSL_load_error_strings();
-# if 0
- /* this is currently a macro for SSL_library_init */
- SSLeay_add_ssl_algorithms();
-# endif /* 0 */
-
- /* initialize PRNG */
- tls_ok_srv = tls_rand_init(RandFile, 7);
+ if (!tls_ok)
+ return false;
+ /* do NOT remove assignment */
+ tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCERTfile,
+ Srvkeyfile, CACERTpath, CACERTfile, DHParams);
return tls_ok_srv;
}
- /*
-** INITTLS -- initialize TLS
+#endif /* STARTTLS */
+/*
+** SRVFEATURES -- get features for SMTP server
**
** Parameters:
-** ctx -- pointer to context
-** req -- requirements for initialization (see sendmail.h)
-** srv -- server side?
-** certfile -- filename of certificate
-** keyfile -- filename of private key
-** cacertpath -- path to CAs
-** cacertfile -- file with CA
-** dhparam -- parameters for DH
+** e -- envelope (should be session context).
+** clientname -- name of client.
+** features -- default features for this invocation.
**
** Returns:
-** succeeded?
+** server features.
*/
-bool
-inittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
- SSL_CTX **ctx;
- u_long req;
- bool srv;
- char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam;
+/* table with options: it uses just one character, how about strings? */
+static struct
{
-# if !NO_DH
- static DH *dh = NULL;
-# endif /* !NO_DH */
- int r;
- bool ok;
- long sff, status;
- char *who;
-# if _FFR_TLS_1
- char *cf2, *kf2;
-# endif /* _FFR_TLS_1 */
-
- status = TLS_S_NONE;
- who = srv ? "srv" : "clt";
- if (ctx == NULL)
- syserr("TLS: %s:inittls: ctx == NULL", who);
-
- /* already initialized? (we could re-init...) */
- if (*ctx != NULL)
- return TRUE;
-
- /* PRNG seeded? */
- if (!tls_rand_init(RandFile, 10))
- return FALSE;
-
- /* let's start with the assumption it will work */
- ok = TRUE;
-
-# if _FFR_TLS_1
- /*
- ** look for a second filename: it must be separated by a ','
- ** no blanks allowed (they won't be skipped).
- ** we change a global variable here! this change will be undone
- ** before return from the function but only if it returns TRUE.
- ** this isn't a problem since in a failure case this function
- ** won't be called again with the same (overwritten) values.
- ** otherwise each return must be replaced with a goto endinittls.
- */
- cf2 = NULL;
- kf2 = NULL;
- if (certfile != NULL && (cf2 = strchr(certfile, ',')) != NULL)
- {
- *cf2++ = '\0';
- if (keyfile != NULL && (kf2 = strchr(keyfile, ',')) != NULL)
- *kf2++ = '\0';
- }
-# endif /* _FFR_TLS_1 */
-
- /*
- ** what do we require from the client?
- ** must it have CERTs?
- ** introduce an option and decide based on that
- */
-
- TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req),
- TLS_S_CERT_EX);
- TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req),
- TLS_S_KEY_EX);
- TLS_OK_F(cacertpath, "CACERTPath", bitset(TLS_I_CERTP_EX, req),
- TLS_S_CERTP_EX);
- TLS_OK_F(cacertfile, "CACERTFile", bitset(TLS_I_CERTF_EX, req),
- TLS_S_CERTF_EX);
-
-# if _FFR_TLS_1
- if (cf2 != NULL)
- {
- TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req),
- TLS_S_CERT2_EX);
- }
- if (kf2 != NULL)
- {
- TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req),
- TLS_S_KEY2_EX);
- }
-# endif /* _FFR_TLS_1 */
-
- /*
- ** valid values for dhparam are (only the first char is checked)
- ** none no parameters: don't use DH
- ** 512 generate 512 bit parameters (fixed)
- ** 1024 generate 1024 bit parameters
- ** /file/name read parameters from /file/name
- ** default is: 1024 for server, 512 for client (OK? XXX)
- */
- if (bitset(TLS_I_TRY_DH, req))
- {
- if (dhparam != NULL)
- {
- char c = *dhparam;
-
- if (c == '1')
- req |= TLS_I_DH1024;
- else if (c == '5')
- req |= TLS_I_DH512;
- else if (c != 'n' && c != 'N' && c != '/')
- {
- if (LogLevel > 12)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: illegal value '%s' for DHParam",
- dhparam);
- dhparam = NULL;
- }
- }
- if (dhparam == NULL)
- dhparam = srv ? "1" : "5";
- else if (*dhparam == '/')
- {
- TLS_OK_F(dhparam, "DHParameters",
- bitset(TLS_I_DHPAR_EX, req),
- TLS_S_DHPAR_EX);
- }
- }
- if (!ok)
- return ok;
-
- /* certfile etc. must be "safe". */
- sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
- | SFF_NOGWFILES | SFF_NOWWFILES
- | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
-
- TLS_SAFE_F(certfile, sff | TLS_UNR(TLS_I_CERT_UNR, req),
- bitset(TLS_I_CERT_EX, req),
- bitset(TLS_S_CERT_EX, status), TLS_S_CERT_OK);
- TLS_SAFE_F(keyfile, sff | TLS_UNR(TLS_I_KEY_UNR, req),
- bitset(TLS_I_KEY_EX, req),
- bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK);
- TLS_SAFE_F(cacertfile, sff | TLS_UNR(TLS_I_CERTF_UNR, req),
- bitset(TLS_I_CERTF_EX, req),
- bitset(TLS_S_CERTF_EX, status), TLS_S_CERTF_OK);
- TLS_SAFE_F(dhparam, sff | TLS_UNR(TLS_I_DHPAR_UNR, req),
- bitset(TLS_I_DHPAR_EX, req),
- bitset(TLS_S_DHPAR_EX, status), TLS_S_DHPAR_OK);
- if (!ok)
- return ok;
-# if _FFR_TLS_1
- if (cf2 != NULL)
- {
- TLS_SAFE_F(cf2, sff | TLS_UNR(TLS_I_CERT_UNR, req),
- bitset(TLS_I_CERT_EX, req),
- bitset(TLS_S_CERT2_EX, status), TLS_S_CERT2_OK);
- }
- if (kf2 != NULL)
- {
- TLS_SAFE_F(kf2, sff | TLS_UNR(TLS_I_KEY_UNR, req),
- bitset(TLS_I_KEY_EX, req),
- bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK);
- }
-# endif /* _FFR_TLS_1 */
-
- /* create a method and a new context */
- if (srv)
- {
- if ((*ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: SSL_CTX_new(SSLv23_server_method()) failed");
- if (LogLevel > 9)
- tlslogerr();
- return FALSE;
- }
- }
- else
- {
- if ((*ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: SSL_CTX_new(SSLv23_client_method()) failed");
- if (LogLevel > 9)
- tlslogerr();
- return FALSE;
- }
- }
-
-# if TLS_NO_RSA
- /* turn off backward compatibility, required for no-rsa */
- SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
-# endif /* TLS_NO_RSA */
-
+ char srvf_opt;
+ unsigned int srvf_flag;
+} srv_feat_table[] =
+{
+ { 'A', SRV_OFFER_AUTH },
+ { 'B', SRV_OFFER_VERB },
+ { 'D', SRV_OFFER_DSN },
+ { 'E', SRV_OFFER_ETRN },
+ { 'L', SRV_REQ_AUTH }, /* not documented in 8.12 */
+#if PIPELINING
+# if _FFR_NO_PIPE
+ { 'N', SRV_NO_PIPE },
+# endif /* _FFR_NO_PIPE */
+ { 'P', SRV_OFFER_PIPE },
+#endif /* PIPELINING */
+ { 'R', SRV_VRFY_CLT },
+ { 'S', SRV_OFFER_TLS },
+/* { 'T', SRV_TMP_FAIL }, */
+ { 'V', SRV_VRFY_CLT },
+ { 'X', SRV_OFFER_EXPN },
+/* { 'Y', SRV_OFFER_VRFY }, */
+ { '\0', SRV_NONE }
+};
-# if !TLS_NO_RSA
- /*
- ** Create a temporary RSA key
- ** XXX Maybe we shouldn't create this always (even though it
- ** is only at startup).
- ** It is a time-consuming operation and it is not always necessary.
- ** maybe we should do it only on demand...
- */
- if (bitset(TLS_I_RSA_TMP, req) &&
- (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL,
- NULL)) == NULL
- )
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: RSA_generate_key failed",
- who);
- if (LogLevel > 9)
- tlslogerr();
- }
- return FALSE;
- }
-# endif /* !TLS_NO_RSA */
+static unsigned int
+srvfeatures(e, clientname, features)
+ ENVELOPE *e;
+ char *clientname;
+ unsigned int features;
+{
+ int r, i, j;
+ char **pvp, c, opt;
+ char pvpbuf[PSBUFSIZE];
+
+ pvp = NULL;
+ r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
+ sizeof(pvpbuf));
+ if (r != EX_OK)
+ return features;
+ if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
+ return features;
+ if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
+ return SRV_TMP_FAIL;
/*
- ** load private key
- ** XXX change this for DSA-only version
+ ** General rule (see sendmail.h, d_flags):
+ ** lower case: required/offered, upper case: Not required/available
+ **
+ ** Since we can change some features per daemon, we have both
+ ** cases here: turn on/off a feature.
*/
- if (bitset(TLS_S_KEY_OK, status) &&
- SSL_CTX_use_PrivateKey_file(*ctx, keyfile,
- SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
- who, keyfile);
- if (LogLevel > 9)
- tlslogerr();
- }
- if (bitset(TLS_I_USE_KEY, req))
- return FALSE;
- }
-
- /* get the certificate file */
- if (bitset(TLS_S_CERT_OK, status) &&
- SSL_CTX_use_certificate_file(*ctx, certfile,
- SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
- who, certfile);
- if (LogLevel > 9)
- tlslogerr();
- }
- if (bitset(TLS_I_USE_CERT, req))
- return FALSE;
- }
-
- /* check the private key */
- if (bitset(TLS_S_KEY_OK, status) &&
- (r = SSL_CTX_check_private_key(*ctx)) <= 0)
- {
- /* Private key does not match the certificate public key */
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_check_private_key failed(%s): %d",
- who, keyfile, r);
- if (LogLevel > 9)
- tlslogerr();
- }
- if (bitset(TLS_I_USE_KEY, req))
- return FALSE;
- }
-# if _FFR_TLS_1
- /* XXX this code is pretty much duplicated from above! */
-
- /* load private key */
- if (bitset(TLS_S_KEY2_OK, status) &&
- SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0)
+ for (i = 1; pvp[i] != NULL; i++)
{
- if (LogLevel > 7)
+ c = pvp[i][0];
+ j = 0;
+ for (;;)
{
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
- who, kf2);
- if (LogLevel > 9)
- tlslogerr();
- }
- }
-
- /* get the certificate file */
- if (bitset(TLS_S_CERT2_OK, status) &&
- SSL_CTX_use_certificate_file(*ctx, cf2, SSL_FILETYPE_PEM) <= 0)
- {
- if (LogLevel > 7)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
- who, cf2);
- if (LogLevel > 9)
- tlslogerr();
- }
- }
-
- /* we should also check the private key: */
- if (bitset(TLS_S_KEY2_OK, status) &&
- (r = SSL_CTX_check_private_key(*ctx)) <= 0)
- {
- /* Private key does not match the certificate public key */
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_check_private_key 2 failed: %d",
- who, r);
- if (LogLevel > 9)
- tlslogerr();
- }
- }
-# endif /* _FFR_TLS_1 */
-
- /* SSL_CTX_set_quiet_shutdown(*ctx, 1); violation of standard? */
- SSL_CTX_set_options(*ctx, SSL_OP_ALL); /* XXX bug compatibility? */
-
-# if !NO_DH
- /* Diffie-Hellman initialization */
- if (bitset(TLS_I_TRY_DH, req))
- {
- if (bitset(TLS_S_DHPAR_OK, status))
- {
- BIO *bio;
-
- if ((bio = BIO_new_file(dhparam, "r")) != NULL)
+ if ((opt = srv_feat_table[j].srvf_opt) == '\0')
{
- dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
- BIO_free(bio);
- if (dh == NULL && LogLevel > 7)
- {
- u_long err;
-
- err = ERR_get_error();
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: cannot read DH parameters(%s): %s",
- who, dhparam,
- ERR_error_string(err, NULL));
- if (LogLevel > 9)
- tlslogerr();
- }
- }
- else
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: BIO_new_file(%s) failed",
- who, dhparam);
- if (LogLevel > 9)
- tlslogerr();
- }
- }
- }
- if (dh == NULL && bitset(TLS_I_DH1024, req))
- {
- DSA *dsa;
-
- /* this takes a while! (7-130s on a 450MHz AMD K6-2) */
- dsa = DSA_generate_parameters(1024, NULL, 0, NULL,
- NULL, 0, NULL);
- dh = DSA_dup_DH(dsa);
- DSA_free(dsa);
- }
- else
- if (dh == NULL && bitset(TLS_I_DH512, req))
- dh = get_dh512();
-
- if (dh == NULL)
- {
- if (LogLevel > 9)
- {
- u_long err;
-
- err = ERR_get_error();
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: cannot read or set DH parameters(%s): %s",
- who, dhparam,
- ERR_error_string(err, NULL));
+ if (LogLevel > 9)
+ sm_syslog(LOG_WARNING, e->e_id,
+ "srvfeatures: unknown feature %s",
+ pvp[i]);
+ break;
}
- if (bitset(TLS_I_REQ_DH, req))
- return FALSE;
- }
- else
- {
- SSL_CTX_set_tmp_dh(*ctx, dh);
-
- /* important to avoid small subgroup attacks */
- SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE);
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID,
- "TLS: %s: Diffie-Hellman init, key=%d bit (%c)",
- who, 8 * DH_size(dh), *dhparam);
- DH_free(dh);
- }
- }
-# endif /* !NO_DH */
-
-
- /* XXX do we need this cache here? */
- if (bitset(TLS_I_CACHE, req))
- SSL_CTX_sess_set_cache_size(*ctx, 128);
- /* timeout? SSL_CTX_set_timeout(*ctx, TimeOut...); */
-
- /* load certificate locations and default CA paths */
- if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status))
- {
- if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile,
- cacertpath)) == 1)
- {
-# if !TLS_NO_RSA
- if (bitset(TLS_I_RSA_TMP, req))
- SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key);
-# endif /* !TLS_NO_RSA */
-
- /* ask to verify the peer */
- SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL);
-
- /* install verify callback */
- SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb,
- NULL);
- SSL_CTX_set_client_CA_list(*ctx,
- SSL_load_client_CA_file(cacertfile));
- }
- else
- {
- /*
- ** can't load CA data; do we care?
- ** the data is necessary to authenticate the client,
- ** which in turn would be necessary
- ** if we want to allow relaying based on it.
- */
- if (LogLevel > 5)
+ if (c == opt)
{
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: %d load verify locs %s, %s",
- who, r, cacertpath, cacertfile);
- if (LogLevel > 9)
- tlslogerr();
+ features &= ~(srv_feat_table[j].srvf_flag);
+ break;
}
- if (bitset(TLS_I_VRFY_LOC, req))
- return FALSE;
- }
- }
-
- /* XXX: make this dependent on an option? */
- if (tTd(96, 9))
- SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb);
-
-# if _FFR_TLS_1
- /*
- ** XXX install our own cipher list: option?
- */
- if (CipherList != NULL && *CipherList != '\0')
- {
- if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0)
- {
- if (LogLevel > 7)
+ if (c == tolower(opt))
{
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored",
- who, CipherList);
-
- if (LogLevel > 9)
- tlslogerr();
+ features |= srv_feat_table[j].srvf_flag;
+ break;
}
- /* failure if setting to this list is required? */
+ ++j;
}
}
-# endif /* _FFR_TLS_1 */
- if (LogLevel > 12)
- sm_syslog(LOG_INFO, NOQID, "TLS: init(%s)=%d", who, ok);
-
-# if _FFR_TLS_1
-# if 0
- /*
- ** this label is required if we want to have a "clean" exit
- ** see the comments above at the initialization of cf2
- */
- endinittls:
-# endif /* 0 */
-
- /* undo damage to global variables */
- if (cf2 != NULL)
- *--cf2 = ',';
- if (kf2 != NULL)
- *--kf2 = ',';
-# endif /* _FFR_TLS_1 */
-
- return ok;
+ return features;
}
- /*
-** INITSRVTLS -- initialize server side TLS
-**
-** Parameters:
-** none.
-**
-** Returns:
-** succeeded?
-**
-** Side Effects:
-** sets tls_ok_srv static, even when called from main()
-*/
-
-bool
-initsrvtls()
-{
- tls_ok_srv = inittls(&srv_ctx, TLS_I_SRV, TRUE, SrvCERTfile,
- Srvkeyfile, CACERTpath, CACERTfile, DHParams);
- return tls_ok_srv;
-}
- /*
-** TLS_GET_INFO -- get information about TLS connection
-**
-** Parameters:
-** ssl -- SSL connection structure
-** e -- current envelope
-** srv -- server or client
-** host -- hostname of other side
-** log -- log connection information?
-**
-** Returns:
-** result of authentication.
-**
-** Side Effects:
-** sets ${cipher}, ${tls_version}, ${verify}, ${cipher_bits},
-** ${cert}
-*/
-
-int
-tls_get_info(ssl, e, srv, host, log)
- SSL *ssl;
- ENVELOPE *e;
- bool srv;
- char *host;
- bool log;
-{
- SSL_CIPHER *c;
- int b, r;
- char *s;
- char bitstr[16];
- X509 *cert;
-
- c = SSL_get_current_cipher(ssl);
- define(macid("{cipher}", NULL), newstr(SSL_CIPHER_get_name(c)), e);
- b = SSL_CIPHER_get_bits(c, &r);
- (void) snprintf(bitstr, sizeof bitstr, "%d", b);
- define(macid("{cipher_bits}", NULL), newstr(bitstr), e);
-# if _FFR_TLS_1
- (void) snprintf(bitstr, sizeof bitstr, "%d", r);
- define(macid("{alg_bits}", NULL), newstr(bitstr), e);
-# endif /* _FFR_TLS_1 */
- s = SSL_CIPHER_get_version(c);
- if (s == NULL)
- s = "UNKNOWN";
- define(macid("{tls_version}", NULL), newstr(s), e);
-
- cert = SSL_get_peer_certificate(ssl);
- if (log && LogLevel >= 14)
- sm_syslog(LOG_INFO, e->e_id,
- "TLS: get_verify in %s: %ld get_peer: 0x%lx",
- srv ? "srv" : "clt",
- SSL_get_verify_result(ssl), (u_long) cert);
- if (cert != NULL)
- {
- char buf[MAXNAME];
-
- X509_NAME_oneline(X509_get_subject_name(cert),
- buf, sizeof buf);
- define(macid("{cert_subject}", NULL),
- newstr(xtextify(buf, "<>\")")), e);
- X509_NAME_oneline(X509_get_issuer_name(cert),
- buf, sizeof buf);
- define(macid("{cert_issuer}", NULL),
- newstr(xtextify(buf, "<>\")")), e);
-# if _FFR_TLS_1
- X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
- NID_commonName, buf, sizeof buf);
- define(macid("{cn_subject}", NULL),
- newstr(xtextify(buf, "<>\")")), e);
- X509_NAME_get_text_by_NID(X509_get_issuer_name(cert),
- NID_commonName, buf, sizeof buf);
- define(macid("{cn_issuer}", NULL),
- newstr(xtextify(buf, "<>\")")), e);
-# endif /* _FFR_TLS_1 */
- }
- else
- {
- define(macid("{cert_subject}", NULL), "", e);
- define(macid("{cert_issuer}", NULL), "", e);
-# if _FFR_TLS_1
- define(macid("{cn_subject}", NULL), "", e);
- define(macid("{cn_issuer}", NULL), "", e);
-# endif /* _FFR_TLS_1 */
- }
- switch(SSL_get_verify_result(ssl))
- {
- case X509_V_OK:
- if (cert != NULL)
- {
- s = "OK";
- r = TLS_AUTH_OK;
- }
- else
- {
- s = "NO";
- r = TLS_AUTH_NO;
- }
- break;
- default:
- s = "FAIL";
- r = TLS_AUTH_FAIL;
- break;
- }
- define(macid("{verify}", NULL), newstr(s), e);
- if (cert != NULL)
- X509_free(cert);
-
- /* do some logging */
- if (log && LogLevel > 9)
- {
- char *vers, *s1, *s2, *bits;
-
- vers = macvalue(macid("{tls_version}", NULL), e);
- bits = macvalue(macid("{cipher_bits}", NULL), e);
- s1 = macvalue(macid("{verify}", NULL), e);
- s2 = macvalue(macid("{cipher}", NULL), e);
- sm_syslog(LOG_INFO, NOQID,
- "TLS: connection %s %.64s, version=%.16s, verify=%.16s, cipher=%.64s, bits=%.6s",
- srv ? "from" : "to",
- host == NULL ? "none" : host,
- vers == NULL ? "none" : vers,
- s1 == NULL ? "none" : s1,
- s2 == NULL ? "none" : s2,
- bits == NULL ? "0" : bits);
- if (LogLevel > 11)
- {
- /*
- ** maybe run xuntextify on the strings?
- ** that is easier to read but makes it maybe a bit
- ** more complicated to figure out the right values
- ** for the access map...
- */
- s1 = macvalue(macid("{cert_subject}", NULL), e);
- s2 = macvalue(macid("{cert_issuer}", NULL), e);
- sm_syslog(LOG_INFO, NOQID,
- "TLS: %s cert subject:%.128s, cert issuer=%.128s",
- srv ? "client" : "server",
- s1 == NULL ? "none" : s1,
- s2 == NULL ? "none" : s2);
- }
- }
-
- return r;
-}
-
-# if !TLS_NO_RSA
- /*
-** TMP_RSA_KEY -- return temporary RSA key
-**
-** Parameters:
-** s -- SSL connection structure
-** export --
-** keylength --
-**
-** Returns:
-** temporary RSA key.
-*/
-
-/* ARGUSED0 */
-static RSA *
-tmp_rsa_key(s, export, keylength)
- SSL *s;
- int export;
- int keylength;
-{
- return rsa_tmp;
-}
-# endif /* !TLS_NO_RSA */
- /*
-** APPS_SSL_INFO_CB -- info callback for TLS connections
-**
-** Parameters:
-** s -- SSL connection structure
-** where --
-** ret --
-**
-** Returns:
-** none.
-*/
-
-void
-apps_ssl_info_cb(s, where, ret)
- SSL *s;
- int where;
- int ret;
-{
- char *str;
- int w;
- BIO *bio_err = NULL;
-
- if (LogLevel > 14)
- sm_syslog(LOG_INFO, NOQID,
- "info_callback where 0x%x ret %d", where, ret);
-
- w = where & ~SSL_ST_MASK;
- if (bio_err == NULL)
- bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
-
- if (w & SSL_ST_CONNECT)
- str = "SSL_connect";
- else if (w & SSL_ST_ACCEPT)
- str = "SSL_accept";
- else
- str = "undefined";
-
- if (where & SSL_CB_LOOP)
- {
- if (LogLevel > 12)
- sm_syslog(LOG_NOTICE, NOQID,
- "%s:%s\n", str, SSL_state_string_long(s));
- }
- else if (where & SSL_CB_ALERT)
- {
- str = (where & SSL_CB_READ) ? "read" : "write";
- if (LogLevel > 12)
- sm_syslog(LOG_NOTICE, NOQID,
- "SSL3 alert %s:%s:%s\n",
- str, SSL_alert_type_string_long(ret),
- SSL_alert_desc_string_long(ret));
- }
- else if (where & SSL_CB_EXIT)
- {
- if (ret == 0)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "%s:failed in %s\n",
- str, SSL_state_string_long(s));
- }
- else if (ret < 0)
- {
- if (LogLevel > 7)
- sm_syslog(LOG_WARNING, NOQID,
- "%s:error in %s\n",
- str, SSL_state_string_long(s));
- }
- }
-}
- /*
-** TLS_VERIFY_LOG -- log verify error for TLS certificates
-**
-** Parameters:
-** ok -- verify ok?
-** ctx -- x509 context
-**
-** Returns:
-** 0 -- fatal error
-** 1 -- ok
-*/
-
-static int
-tls_verify_log(ok, ctx)
- int ok;
- X509_STORE_CTX *ctx;
-{
- SSL *ssl;
- X509 *cert;
- int reason, depth;
- char buf[512];
-
- cert = X509_STORE_CTX_get_current_cert(ctx);
- reason = X509_STORE_CTX_get_error(ctx);
- depth = X509_STORE_CTX_get_error_depth(ctx);
- ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx,
- SSL_get_ex_data_X509_STORE_CTX_idx());
-
- if (ssl == NULL)
- {
- /* internal error */
- sm_syslog(LOG_ERR, NOQID,
- "TLS: internal error: tls_verify_cb: ssl == NULL");
- return 0;
- }
-
- X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof buf);
- sm_syslog(LOG_INFO, NOQID,
- "TLS cert verify: depth=%d %s, state=%d, reason=%s\n",
- depth, buf, ok, X509_verify_cert_error_string(reason));
- return 1;
-}
-
- /*
-** TLS_VERIFY_CB -- verify callback for TLS certificates
-**
-** Parameters:
-** ctx -- x509 context
-**
-** Returns:
-** accept connection?
-** currently: always yes.
-*/
-
-static int
-tls_verify_cb(ctx)
- X509_STORE_CTX *ctx;
-{
- int ok;
-
- ok = X509_verify_cert(ctx);
- if (ok == 0)
- {
- if (LogLevel > 13)
- return tls_verify_log(ok, ctx);
- return 1; /* override it */
- }
- return ok;
-}
-
-
- /*
-** TLSLOGERR -- log the errors from the TLS error stack
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-*/
-
-void
-tlslogerr()
-{
- unsigned long l;
- int line, flags;
- unsigned long es;
- char *file, *data;
- char buf[256];
-#define CP (const char **)
-
- es = CRYPTO_thread_id();
- while ((l = ERR_get_error_line_data(CP &file, &line, CP &data, &flags))
- != 0)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "TLS: %lu:%s:%s:%d:%s\n", es, ERR_error_string(l, buf),
- file, line, (flags & ERR_TXT_STRING) ? data : "");
- }
-}
-
-# endif /* STARTTLS */
-#endif /* SMTP */
- /*
+/*
** HELP -- implement the HELP command.
**
** Parameters:
** topic -- the topic we want help for.
-** e -- envelope
+** e -- envelope.
**
** Returns:
** none.
@@ -4216,11 +3927,11 @@ help(topic, e)
char *topic;
ENVELOPE *e;
{
- register FILE *hf;
+ register SM_FILE_T *hf;
register char *p;
int len;
bool noinfo;
- bool first = TRUE;
+ bool first = true;
long sff = SFF_OPENASROOT|SFF_REGONLY;
char buf[MAXLINE];
char inp[MAXLINE];
@@ -4245,17 +3956,17 @@ help(topic, e)
if (topic == NULL || *topic == '\0')
{
topic = "smtp";
- noinfo = FALSE;
+ noinfo = false;
}
else
{
makelower(topic);
- noinfo = TRUE;
+ noinfo = true;
}
len = strlen(topic);
- while (fgets(buf, sizeof buf, hf) != NULL)
+ while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
{
if (buf[0] == '#')
{
@@ -4264,8 +3975,8 @@ help(topic, e)
{
int h;
- if (sscanf(buf + strlen(HELPVSTR), "%d",
- &h) == 1)
+ if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
+ &h) == 1)
foundvers = h;
}
continue;
@@ -4274,7 +3985,7 @@ help(topic, e)
{
if (first)
{
- first = FALSE;
+ first = false;
/* print version if no/old vers# in file */
if (foundvers < 2 && !noinfo)
@@ -4285,7 +3996,7 @@ help(topic, e)
p = buf + strlen(buf) - 1;
else
p++;
- fixcrlf(p, TRUE);
+ fixcrlf(p, true);
if (foundvers >= 2)
{
translate_dollars(p);
@@ -4293,7 +4004,7 @@ help(topic, e)
p = inp;
}
message("214-2.0.0 %s", p);
- noinfo = FALSE;
+ noinfo = false;
}
}
@@ -4313,5 +4024,5 @@ help(topic, e)
foundvers = 0;
}
- (void) fclose(hf);
+ (void) sm_io_close(hf, SM_TIME_DEFAULT);
}
OpenPOWER on IntegriCloud