1N/A/*
1N/A * Copyright (c) 1998-2010 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
1N/A * Copyright (c) 1988, 1993
1N/A * The Regents of the University of California. All rights reserved.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A *
1N/A */
1N/A
1N/A#include <sendmail.h>
1N/A#if MILTER
1N/A# include <libmilter/mfapi.h>
1N/A# include <libmilter/mfdef.h>
1N/A#endif /* MILTER */
1N/A
1N/ASM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1008 2011/01/12 23:52:59 ca Exp $")
1N/A
1N/A#include <sm/time.h>
1N/A#include <sm/fdset.h>
1N/A
1N/A#if SASL || STARTTLS
1N/A# include "sfsasl.h"
1N/A#endif /* SASL || STARTTLS */
1N/A#if SASL
1N/A# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
1N/Astatic int saslmechs __P((sasl_conn_t *, char **));
1N/A#endif /* SASL */
1N/A#if STARTTLS
1N/A# include <sysexits.h>
1N/A
1N/Astatic SSL_CTX *srv_ctx = NULL; /* TLS server context */
1N/Astatic SSL *srv_ssl = NULL; /* per connection context */
1N/A
1N/Astatic bool tls_ok_srv = false;
1N/A
1N/A# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
1N/A bitset(SRV_VRFY_CLT, features))
1N/A#endif /* STARTTLS */
1N/A
1N/A#if _FFR_DM_ONE
1N/Astatic bool NotFirstDelivery = false;
1N/A#endif /* _FFR_DM_ONE */
1N/A
1N/A/* server features */
1N/A#define SRV_NONE 0x0000 /* none... */
1N/A#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
1N/A#define SRV_VRFY_CLT 0x0002 /* request a cert */
1N/A#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
1N/A#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
1N/A#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
1N/A#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
1N/A#define SRV_OFFER_VERB 0x0040 /* offer VERB */
1N/A#define SRV_OFFER_DSN 0x0080 /* offer DSN */
1N/A#if PIPELINING
1N/A# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
1N/A# if _FFR_NO_PIPE
1N/A# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
1N/A# endif /* _FFR_NO_PIPE */
1N/A#endif /* PIPELINING */
1N/A#define SRV_REQ_AUTH 0x0400 /* require AUTH */
1N/A#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */
1N/A#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
1N/A
1N/Astatic unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
1N/A
1N/A#define STOP_ATTACK ((time_t) -1)
1N/Astatic time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
1N/A bool, char *, ENVELOPE *));
1N/Astatic void printvrfyaddr __P((ADDRESS *, bool, bool));
1N/Astatic char *skipword __P((char *volatile, char *));
1N/Astatic void setup_smtpd_io __P((void));
1N/A
1N/A#if SASL
1N/A# if SASL >= 20000
1N/Astatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
1N/A char *_remoteip, char *_localip,
1N/A char *_auth_id, sasl_ssf_t *_ext_ssf));
1N/A
1N/A# define RESET_SASLCONN \
1N/A do \
1N/A { \
1N/A result = reset_saslconn(&conn, AuthRealm, remoteip, \
1N/A localip, auth_id, &ext_ssf); \
1N/A if (result != SASL_OK) \
1N/A sasl_ok = false; \
1N/A } while (0)
1N/A
1N/A# else /* SASL >= 20000 */
1N/Astatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
1N/A struct sockaddr_in *_saddr_r,
1N/A struct sockaddr_in *_saddr_l,
1N/A sasl_external_properties_t *_ext_ssf));
1N/A# define RESET_SASLCONN \
1N/A do \
1N/A { \
1N/A result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
1N/A &saddr_l, &ext_ssf); \
1N/A if (result != SASL_OK) \
1N/A sasl_ok = false; \
1N/A } while (0)
1N/A
1N/A# endif /* SASL >= 20000 */
1N/A#endif /* SASL */
1N/A
1N/Aextern ENVELOPE BlankEnvelope;
1N/A
1N/A#define NBADRCPTS \
1N/A do \
1N/A { \
1N/A char buf[16]; \
1N/A (void) sm_snprintf(buf, sizeof(buf), "%d", \
1N/A BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
1N/A ? n_badrcpts - 1 : n_badrcpts); \
1N/A macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
1N/A } while (0)
1N/A
1N/A#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
1N/A (s)++
1N/A
1N/A/*
1N/A** PARSE_ESMTP_ARGS -- parse EMSTP arguments (for MAIL, RCPT)
1N/A**
1N/A** Parameters:
1N/A** e -- the envelope
1N/A** addr_st -- address (RCPT only)
1N/A** p -- read buffer
1N/A** delimptr -- current position in read buffer
1N/A** which -- MAIL/RCPT
1N/A** args -- arguments (output)
1N/A** esmtp_args -- function to process a single ESMTP argument
1N/A**
1N/A** Returns:
1N/A** none
1N/A*/
1N/A
1N/Avoid
1N/Aparse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
1N/A ENVELOPE *e;
1N/A ADDRESS *addr_st;
1N/A char *p;
1N/A char *delimptr;
1N/A char *which;
1N/A char *args[];
1N/A esmtp_args_F esmtp_args;
1N/A{
1N/A int argno;
1N/A
1N/A argno = 0;
1N/A if (args != NULL)
1N/A args[argno++] = p;
1N/A p = delimptr;
1N/A while (p != NULL && *p != '\0')
1N/A {
1N/A char *kp;
1N/A char *vp = NULL;
1N/A char *equal = NULL;
1N/A
1N/A /* locate the beginning of the keyword */
1N/A SKIP_SPACE(p);
1N/A if (*p == '\0')
1N/A break;
1N/A kp = p;
1N/A
1N/A /* skip to the value portion */
1N/A while ((isascii(*p) && isalnum(*p)) || *p == '-')
1N/A p++;
1N/A if (*p == '=')
1N/A {
1N/A equal = p;
1N/A *p++ = '\0';
1N/A vp = p;
1N/A
1N/A /* skip to the end of the value */
1N/A while (*p != '\0' && *p != ' ' &&
1N/A !(isascii(*p) && iscntrl(*p)) &&
1N/A *p != '=')
1N/A p++;
1N/A }
1N/A
1N/A if (*p != '\0')
1N/A *p++ = '\0';
1N/A
1N/A if (tTd(19, 1))
1N/A sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp,
1N/A vp == NULL ? "<null>" : vp);
1N/A
1N/A esmtp_args(addr_st, kp, vp, e);
1N/A if (equal != NULL)
1N/A *equal = '=';
1N/A if (args != NULL)
1N/A args[argno] = kp;
1N/A argno++;
1N/A if (argno >= MAXSMTPARGS - 1)
1N/A usrerr("501 5.5.4 Too many parameters");
1N/A if (Errors > 0)
1N/A break;
1N/A }
1N/A if (args != NULL)
1N/A args[argno] = NULL;
1N/A}
1N/A
1N/A/*
1N/A** SMTP -- run the SMTP protocol.
1N/A**
1N/A** Parameters:
1N/A** nullserver -- if non-NULL, rejection message for
1N/A** (almost) all SMTP commands.
1N/A** d_flags -- daemon flags
1N/A** e -- the envelope.
1N/A**
1N/A** Returns:
1N/A** never.
1N/A**
1N/A** Side Effects:
1N/A** Reads commands from the input channel and processes them.
1N/A*/
1N/A
1N/A/*
1N/A** Notice: The smtp server doesn't have a session context like the client
1N/A** side has (mci). Therefore some data (session oriented) is allocated
1N/A** or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
1N/A** This should be fixed in a successor version.
1N/A*/
1N/A
1N/Astruct cmd
1N/A{
1N/A char *cmd_name; /* command name */
1N/A int cmd_code; /* internal code, see below */
1N/A};
1N/A
1N/A/* values for cmd_code */
1N/A#define CMDERROR 0 /* bad command */
1N/A#define CMDMAIL 1 /* mail -- designate sender */
1N/A#define CMDRCPT 2 /* rcpt -- designate recipient */
1N/A#define CMDDATA 3 /* data -- send message text */
1N/A#define CMDRSET 4 /* rset -- reset state */
1N/A#define CMDVRFY 5 /* vrfy -- verify address */
1N/A#define CMDEXPN 6 /* expn -- expand address */
1N/A#define CMDNOOP 7 /* noop -- do nothing */
1N/A#define CMDQUIT 8 /* quit -- close connection and die */
1N/A#define CMDHELO 9 /* helo -- be polite */
1N/A#define CMDHELP 10 /* help -- give usage info */
1N/A#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
1N/A#define CMDETRN 12 /* etrn -- flush queue */
1N/A#if SASL
1N/A# define CMDAUTH 13 /* auth -- SASL authenticate */
1N/A#endif /* SASL */
1N/A#if STARTTLS
1N/A# define CMDSTLS 14 /* STARTTLS -- start TLS session */
1N/A#endif /* STARTTLS */
1N/A/* non-standard commands */
1N/A#define CMDVERB 17 /* verb -- go into verbose mode */
1N/A/* unimplemented commands from RFC 821 */
1N/A#define CMDUNIMPL 19 /* unimplemented rfc821 commands */
1N/A/* use this to catch and log "door handle" attempts on your system */
1N/A#define CMDLOGBOGUS 23 /* bogus command that should be logged */
1N/A/* debugging-only commands, only enabled if SMTPDEBUG is defined */
1N/A#define CMDDBGQSHOW 24 /* showq -- show send queue */
1N/A#define CMDDBGDEBUG 25 /* debug -- set debug mode */
1N/A
1N/A/*
1N/A** Note: If you change this list, remember to update 'helpfile'
1N/A*/
1N/A
1N/Astatic struct cmd CmdTab[] =
1N/A{
1N/A { "mail", CMDMAIL },
1N/A { "rcpt", CMDRCPT },
1N/A { "data", CMDDATA },
1N/A { "rset", CMDRSET },
1N/A { "vrfy", CMDVRFY },
1N/A { "expn", CMDEXPN },
1N/A { "help", CMDHELP },
1N/A { "noop", CMDNOOP },
1N/A { "quit", CMDQUIT },
1N/A { "helo", CMDHELO },
1N/A { "ehlo", CMDEHLO },
1N/A { "etrn", CMDETRN },
1N/A { "verb", CMDVERB },
1N/A { "send", CMDUNIMPL },
1N/A { "saml", CMDUNIMPL },
1N/A { "soml", CMDUNIMPL },
1N/A { "turn", CMDUNIMPL },
1N/A#if SASL
1N/A { "auth", CMDAUTH, },
1N/A#endif /* SASL */
1N/A#if STARTTLS
1N/A { "starttls", CMDSTLS, },
1N/A#endif /* STARTTLS */
1N/A /* remaining commands are here only to trap and log attempts to use them */
1N/A { "showq", CMDDBGQSHOW },
1N/A { "debug", CMDDBGDEBUG },
1N/A { "wiz", CMDLOGBOGUS },
1N/A
1N/A { NULL, CMDERROR }
1N/A};
1N/A
1N/Astatic char *CurSmtpClient; /* who's at the other end of channel */
1N/A
1N/A#ifndef MAXBADCOMMANDS
1N/A# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
1N/A#endif /* ! MAXBADCOMMANDS */
1N/A#ifndef MAXHELOCOMMANDS
1N/A# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
1N/A#endif /* ! MAXHELOCOMMANDS */
1N/A#ifndef MAXVRFYCOMMANDS
1N/A# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
1N/A#endif /* ! MAXVRFYCOMMANDS */
1N/A#ifndef MAXETRNCOMMANDS
1N/A# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
1N/A#endif /* ! MAXETRNCOMMANDS */
1N/A#ifndef MAXTIMEOUT
1N/A# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
1N/A#endif /* ! MAXTIMEOUT */
1N/A
1N/A/*
1N/A** Maximum shift value to compute timeout for bad commands.
1N/A** This introduces an upper limit of 2^MAXSHIFT for the timeout.
1N/A*/
1N/A
1N/A#ifndef MAXSHIFT
1N/A# define MAXSHIFT 8
1N/A#endif /* ! MAXSHIFT */
1N/A#if MAXSHIFT > 31
1N/A ERROR _MAXSHIFT > 31 is invalid
1N/A#endif /* MAXSHIFT */
1N/A
1N/A
1N/A#if MAXBADCOMMANDS > 0
1N/A# define STOP_IF_ATTACK(r) do \
1N/A { \
1N/A if ((r) == STOP_ATTACK) \
1N/A goto stopattack; \
1N/A } while (0)
1N/A
1N/A#else /* MAXBADCOMMANDS > 0 */
1N/A# define STOP_IF_ATTACK(r) r
1N/A#endif /* MAXBADCOMMANDS > 0 */
1N/A
1N/A
1N/A#if SM_HEAP_CHECK
1N/Astatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
1N/A "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
1N/A#endif /* SM_HEAP_CHECK */
1N/A
1N/Atypedef struct
1N/A{
1N/A bool sm_gotmail; /* mail command received */
1N/A unsigned int sm_nrcpts; /* number of successful RCPT commands */
1N/A bool sm_discard;
1N/A#if MILTER
1N/A bool sm_milterize;
1N/A bool sm_milterlist; /* any filters in the list? */
1N/A milters_T sm_milters;
1N/A
1N/A /* e_nrcpts from envelope before recipient() call */
1N/A unsigned int sm_e_nrcpts_orig;
1N/A#endif /* MILTER */
1N/A char *sm_quarmsg; /* carry quarantining across messages */
1N/A} SMTP_T;
1N/A
1N/Astatic bool smtp_data __P((SMTP_T *, ENVELOPE *));
1N/A
1N/A#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
1N/A
1N/A#if MILTER
1N/A# define MILTER_ABORT(e) milter_abort((e))
1N/A
1N/A# define MILTER_REPLY(str) \
1N/A { \
1N/A int savelogusrerrs = LogUsrErrs; \
1N/A \
1N/A milter_cmd_fail = true; \
1N/A switch (state) \
1N/A { \
1N/A case SMFIR_SHUTDOWN: \
1N/A if (MilterLogLevel > 3) \
1N/A { \
1N/A sm_syslog(LOG_INFO, e->e_id, \
1N/A "Milter: %s=%s, reject=421, errormode=4", \
1N/A str, addr); \
1N/A LogUsrErrs = false; \
1N/A } \
1N/A { \
1N/A bool tsave = QuickAbort; \
1N/A \
1N/A QuickAbort = false; \
1N/A usrerr("421 4.3.0 closing connection"); \
1N/A QuickAbort = tsave; \
1N/A e->e_sendqueue = NULL; \
1N/A goto doquit; \
1N/A } \
1N/A break; \
1N/A case SMFIR_REPLYCODE: \
1N/A if (MilterLogLevel > 3) \
1N/A { \
1N/A sm_syslog(LOG_INFO, e->e_id, \
1N/A "Milter: %s=%s, reject=%s", \
1N/A str, addr, response); \
1N/A LogUsrErrs = false; \
1N/A } \
1N/A if (strncmp(response, "421 ", 4) == 0 \
1N/A || strncmp(response, "421-", 4) == 0) \
1N/A { \
1N/A bool tsave = QuickAbort; \
1N/A \
1N/A QuickAbort = false; \
1N/A usrerr(response); \
1N/A QuickAbort = tsave; \
1N/A e->e_sendqueue = NULL; \
1N/A goto doquit; \
1N/A } \
1N/A else \
1N/A usrerr(response); \
1N/A break; \
1N/A \
1N/A case SMFIR_REJECT: \
1N/A if (MilterLogLevel > 3) \
1N/A { \
1N/A sm_syslog(LOG_INFO, e->e_id, \
1N/A "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
1N/A str, addr); \
1N/A LogUsrErrs = false; \
1N/A } \
1N/A usrerr("550 5.7.1 Command rejected"); \
1N/A break; \
1N/A \
1N/A case SMFIR_DISCARD: \
1N/A if (MilterLogLevel > 3) \
1N/A sm_syslog(LOG_INFO, e->e_id, \
1N/A "Milter: %s=%s, discard", \
1N/A str, addr); \
1N/A e->e_flags |= EF_DISCARD; \
1N/A milter_cmd_fail = false; \
1N/A break; \
1N/A \
1N/A case SMFIR_TEMPFAIL: \
1N/A if (MilterLogLevel > 3) \
1N/A { \
1N/A sm_syslog(LOG_INFO, e->e_id, \
1N/A "Milter: %s=%s, reject=%s", \
1N/A str, addr, MSG_TEMPFAIL); \
1N/A LogUsrErrs = false; \
1N/A } \
1N/A usrerr(MSG_TEMPFAIL); \
1N/A break; \
1N/A default: \
1N/A milter_cmd_fail = false; \
1N/A break; \
1N/A } \
1N/A LogUsrErrs = savelogusrerrs; \
1N/A if (response != NULL) \
1N/A sm_free(response); /* XXX */ \
1N/A }
1N/A
1N/A#else /* MILTER */
1N/A# define MILTER_ABORT(e)
1N/A#endif /* MILTER */
1N/A
1N/A/* clear all SMTP state (for HELO/EHLO/RSET) */
1N/A#define CLEAR_STATE(cmd) \
1N/Ado \
1N/A{ \
1N/A /* abort milter filters */ \
1N/A MILTER_ABORT(e); \
1N/A \
1N/A if (smtp.sm_nrcpts > 0) \
1N/A { \
1N/A logundelrcpts(e, cmd, 10, false); \
1N/A smtp.sm_nrcpts = 0; \
1N/A macdefine(&e->e_macro, A_PERM, \
1N/A macid("{nrcpts}"), "0"); \
1N/A } \
1N/A \
1N/A e->e_sendqueue = NULL; \
1N/A e->e_flags |= EF_CLRQUEUE; \
1N/A \
1N/A if (tTd(92, 2)) \
1N/A sm_dprintf("CLEAR_STATE: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",\
1N/A e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);\
1N/A if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \
1N/A logsender(e, NULL); \
1N/A e->e_flags &= ~EF_LOGSENDER; \
1N/A \
1N/A /* clean up a bit */ \
1N/A smtp.sm_gotmail = false; \
1N/A SuprErrs = true; \
1N/A (void) dropenvelope(e, true, false); \
1N/A sm_rpool_free(e->e_rpool); \
1N/A e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
1N/A CurEnv = e; \
1N/A e->e_features = features; \
1N/A \
1N/A /* put back discard bit */ \
1N/A if (smtp.sm_discard) \
1N/A e->e_flags |= EF_DISCARD; \
1N/A \
1N/A /* restore connection quarantining */ \
1N/A if (smtp.sm_quarmsg == NULL) \
1N/A { \
1N/A e->e_quarmsg = NULL; \
1N/A macdefine(&e->e_macro, A_PERM, \
1N/A macid("{quarantine}"), ""); \
1N/A } \
1N/A else \
1N/A { \
1N/A e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
1N/A smtp.sm_quarmsg); \
1N/A macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
1N/A e->e_quarmsg); \
1N/A } \
1N/A} while (0)
1N/A
1N/A/* sleep to flatten out connection load */
1N/A#define MIN_DELAY_LOG 15 /* wait before logging this again */
1N/A
1N/A/* is it worth setting the process title for 1s? */
1N/A#define DELAY_CONN(cmd) \
1N/A if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \
1N/A { \
1N/A time_t dnow; \
1N/A \
1N/A sm_setproctitle(true, e, \
1N/A "%s: %s: delaying %s: load average: %d", \
1N/A qid_printname(e), CurSmtpClient, \
1N/A cmd, DelayLA); \
1N/A if (LogLevel > 8 && (dnow = curtime()) > log_delay) \
1N/A { \
1N/A sm_syslog(LOG_INFO, e->e_id, \
1N/A "delaying=%s, load average=%d >= %d", \
1N/A cmd, CurrentLA, DelayLA); \
1N/A log_delay = dnow + MIN_DELAY_LOG; \
1N/A } \
1N/A (void) sleep(1); \
1N/A sm_setproctitle(true, e, "%s %s: %.80s", \
1N/A qid_printname(e), CurSmtpClient, inp); \
1N/A }
1N/A
1N/Astatic bool SevenBitInput_Saved; /* saved version of SevenBitInput */
1N/A
1N/Avoid
1N/Asmtp(nullserver, d_flags, e)
1N/A char *volatile nullserver;
1N/A BITMAP256 d_flags;
1N/A register ENVELOPE *volatile e;
1N/A{
1N/A register char *volatile p;
1N/A register struct cmd *volatile c = NULL;
1N/A char *cmd;
1N/A auto ADDRESS *vrfyqueue;
1N/A ADDRESS *a;
1N/A volatile bool gothello; /* helo command received */
1N/A bool vrfy; /* set if this is a vrfy command */
1N/A char *volatile protocol; /* sending protocol */
1N/A char *volatile sendinghost; /* sending hostname */
1N/A char *volatile peerhostname; /* name of SMTP peer or "localhost" */
1N/A auto char *delimptr;
1N/A char *id;
1N/A volatile unsigned int n_badcmds = 0; /* count of bad commands */
1N/A volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */
1N/A volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */
1N/A volatile unsigned int n_etrn = 0; /* count of ETRN */
1N/A volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */
1N/A volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
1N/A bool ok;
1N/A volatile bool first;
1N/A volatile bool tempfail = false;
1N/A volatile time_t wt; /* timeout after too many commands */
1N/A volatile time_t previous; /* time after checksmtpattack() */
1N/A volatile bool lognullconnection = true;
1N/A register char *q;
1N/A SMTP_T smtp;
1N/A char *addr;
1N/A char *greetcode = "220";
1N/A char *hostname; /* my hostname ($j) */
1N/A QUEUE_CHAR *new;
1N/A char *args[MAXSMTPARGS];
1N/A char inp[MAXINPLINE];
1N/A#if MAXINPLINE < MAXLINE
1N/A ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
1N/A#endif /* MAXINPLINE < MAXLINE */
1N/A char cmdbuf[MAXLINE];
1N/A#if SASL
1N/A sasl_conn_t *conn;
1N/A volatile bool sasl_ok;
1N/A volatile unsigned int n_auth = 0; /* count of AUTH commands */
1N/A bool ismore;
1N/A int result;
1N/A volatile int authenticating;
1N/A char *user;
1N/A char *in, *out2;
1N/A# if SASL >= 20000
1N/A char *auth_id = NULL;
1N/A const char *out;
1N/A sasl_ssf_t ext_ssf;
1N/A char localip[60], remoteip[60];
1N/A# else /* SASL >= 20000 */
1N/A char *out;
1N/A const char *errstr;
1N/A sasl_external_properties_t ext_ssf;
1N/A struct sockaddr_in saddr_l;
1N/A struct sockaddr_in saddr_r;
1N/A# endif /* SASL >= 20000 */
1N/A sasl_security_properties_t ssp;
1N/A sasl_ssf_t *ssf;
1N/A unsigned int inlen, out2len;
1N/A unsigned int outlen;
1N/A char *volatile auth_type;
1N/A char *mechlist;
1N/A volatile unsigned int n_mechs;
1N/A unsigned int len;
1N/A#else /* SASL */
1N/A#endif /* SASL */
1N/A int r;
1N/A#if STARTTLS
1N/A int rfd, wfd;
1N/A volatile bool tls_active = false;
1N/A volatile bool smtps = bitnset(D_SMTPS, d_flags);
1N/A bool saveQuickAbort;
1N/A bool saveSuprErrs;
1N/A time_t tlsstart;
1N/A#endif /* STARTTLS */
1N/A volatile unsigned int features;
1N/A#if PIPELINING
1N/A# if _FFR_NO_PIPE
1N/A int np_log = 0;
1N/A# endif /* _FFR_NO_PIPE */
1N/A#endif /* PIPELINING */
1N/A volatile time_t log_delay = (time_t) 0;
1N/A#if MILTER
1N/A volatile bool milter_cmd_done, milter_cmd_safe;
1N/A volatile bool milter_rcpt_added, milter_cmd_fail;
1N/A ADDRESS addr_st;
1N/A# define p_addr_st &addr_st
1N/A#else /* MILTER */
1N/A# define p_addr_st NULL
1N/A#endif /* MILTER */
1N/A size_t inplen;
1N/A#if _FFR_BADRCPT_SHUTDOWN
1N/A int n_badrcpts_adj;
1N/A#endif /* _FFR_BADRCPT_SHUTDOWN */
1N/A
1N/A SevenBitInput_Saved = SevenBitInput;
1N/A smtp.sm_nrcpts = 0;
1N/A#if MILTER
1N/A smtp.sm_milterize = (nullserver == NULL);
1N/A smtp.sm_milterlist = false;
1N/A addr = NULL;
1N/A#endif /* MILTER */
1N/A
1N/A /* setup I/O fd correctly for the SMTP server */
1N/A setup_smtpd_io();
1N/A
1N/A#if SM_HEAP_CHECK
1N/A if (sm_debug_active(&DebugLeakSmtp, 1))
1N/A {
1N/A sm_heap_newgroup();
1N/A sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
1N/A }
1N/A#endif /* SM_HEAP_CHECK */
1N/A
1N/A /* XXX the rpool should be set when e is initialized in main() */
1N/A e->e_rpool = sm_rpool_new_x(NULL);
1N/A e->e_macro.mac_rpool = e->e_rpool;
1N/A
1N/A settime(e);
1N/A sm_getla();
1N/A peerhostname = RealHostName;
1N/A if (peerhostname == NULL)
1N/A peerhostname = "localhost";
1N/A CurHostName = peerhostname;
1N/A CurSmtpClient = macvalue('_', e);
1N/A if (CurSmtpClient == NULL)
1N/A CurSmtpClient = CurHostName;
1N/A
1N/A /* check_relay may have set discard bit, save for later */
1N/A smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
1N/A
1N/A#if PIPELINING
1N/A /* auto-flush output when reading input */
1N/A (void) sm_io_autoflush(InChannel, OutChannel);
1N/A#endif /* PIPELINING */
1N/A
1N/A sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
1N/A
1N/A /* Set default features for server. */
1N/A features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
1N/A bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
1N/A | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
1N/A | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
1N/A : (SRV_OFFER_EXPN
1N/A | (bitset(PRIV_NOVERB, PrivacyFlags)
1N/A ? SRV_NONE : SRV_OFFER_VERB)))
1N/A | ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors)
1N/A ? SRV_NONE : SRV_OFFER_DSN)
1N/A#if SASL
1N/A | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
1N/A | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
1N/A : SRV_NONE)
1N/A#endif /* SASL */
1N/A#if PIPELINING
1N/A | SRV_OFFER_PIPE
1N/A#endif /* PIPELINING */
1N/A#if STARTTLS
1N/A | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
1N/A | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
1N/A : SRV_VRFY_CLT)
1N/A#endif /* STARTTLS */
1N/A ;
1N/A if (nullserver == NULL)
1N/A {
1N/A features = srvfeatures(e, CurSmtpClient, features);
1N/A if (bitset(SRV_TMP_FAIL, features))
1N/A {
1N/A if (LogLevel > 4)
1N/A sm_syslog(LOG_ERR, NOQID,
1N/A "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
1N/A CurSmtpClient);
1N/A nullserver = "450 4.3.0 Please try again later.";
1N/A }
1N/A else
1N/A {
1N/A#if PIPELINING
1N/A# if _FFR_NO_PIPE
1N/A if (bitset(SRV_NO_PIPE, features))
1N/A {
1N/A /* for consistency */
1N/A features &= ~SRV_OFFER_PIPE;
1N/A }
1N/A# endif /* _FFR_NO_PIPE */
1N/A#endif /* PIPELINING */
1N/A#if SASL
1N/A if (bitset(SRV_REQ_SEC, features))
1N/A SASLOpts |= SASL_SEC_NOPLAINTEXT;
1N/A else
1N/A SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
1N/A#endif /* SASL */
1N/A }
1N/A }
1N/A else if (strncmp(nullserver, "421 ", 4) == 0)
1N/A {
1N/A message(nullserver);
1N/A goto doquit;
1N/A }
1N/A
1N/A e->e_features = features;
1N/A hostname = macvalue('j', e);
1N/A#if SASL
1N/A if (AuthRealm == NULL)
1N/A AuthRealm = hostname;
1N/A sasl_ok = bitset(SRV_OFFER_AUTH, features);
1N/A n_mechs = 0;
1N/A authenticating = SASL_NOT_AUTH;
1N/A
1N/A /* SASL server new connection */
1N/A if (sasl_ok)
1N/A {
1N/A# if SASL >= 20000
1N/A result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
1N/A NULL, 0, &conn);
1N/A# elif SASL > 10505
1N/A /* use empty realm: only works in SASL > 1.5.5 */
1N/A result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
1N/A# else /* SASL >= 20000 */
1N/A /* use no realm -> realm is set to hostname by SASL lib */
1N/A result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
1N/A &conn);
1N/A# endif /* SASL >= 20000 */
1N/A sasl_ok = result == SASL_OK;
1N/A if (!sasl_ok)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "AUTH error: sasl_server_new failed=%d",
1N/A result);
1N/A }
1N/A }
1N/A if (sasl_ok)
1N/A {
1N/A /*
1N/A ** SASL set properties for sasl
1N/A ** set local/remote IP
1N/A ** XXX Cyrus SASL v1 only supports IPv4
1N/A **
1N/A ** XXX where exactly are these used/required?
1N/A ** Kerberos_v4
1N/A */
1N/A
1N/A# if SASL >= 20000
1N/A localip[0] = remoteip[0] = '\0';
1N/A# if NETINET || NETINET6
1N/A in = macvalue(macid("{daemon_family}"), e);
1N/A if (in != NULL && (
1N/A# if NETINET6
1N/A strcmp(in, "inet6") == 0 ||
1N/A# endif /* NETINET6 */
1N/A strcmp(in, "inet") == 0))
1N/A {
1N/A SOCKADDR_LEN_T addrsize;
1N/A SOCKADDR saddr_l;
1N/A SOCKADDR saddr_r;
1N/A
1N/A addrsize = sizeof(saddr_r);
1N/A if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
1N/A NULL),
1N/A (struct sockaddr *) &saddr_r,
1N/A &addrsize) == 0)
1N/A {
1N/A if (iptostring(&saddr_r, addrsize,
1N/A remoteip, sizeof(remoteip)))
1N/A {
1N/A sasl_setprop(conn, SASL_IPREMOTEPORT,
1N/A remoteip);
1N/A }
1N/A addrsize = sizeof(saddr_l);
1N/A if (getsockname(sm_io_getinfo(InChannel,
1N/A SM_IO_WHAT_FD,
1N/A NULL),
1N/A (struct sockaddr *) &saddr_l,
1N/A &addrsize) == 0)
1N/A {
1N/A if (iptostring(&saddr_l, addrsize,
1N/A localip,
1N/A sizeof(localip)))
1N/A {
1N/A sasl_setprop(conn,
1N/A SASL_IPLOCALPORT,
1N/A localip);
1N/A }
1N/A }
1N/A }
1N/A }
1N/A# endif /* NETINET || NETINET6 */
1N/A# else /* SASL >= 20000 */
1N/A# if NETINET
1N/A in = macvalue(macid("{daemon_family}"), e);
1N/A if (in != NULL && strcmp(in, "inet") == 0)
1N/A {
1N/A SOCKADDR_LEN_T addrsize;
1N/A
1N/A addrsize = sizeof(struct sockaddr_in);
1N/A if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
1N/A NULL),
1N/A (struct sockaddr *)&saddr_r,
1N/A &addrsize) == 0)
1N/A {
1N/A sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
1N/A addrsize = sizeof(struct sockaddr_in);
1N/A if (getsockname(sm_io_getinfo(InChannel,
1N/A SM_IO_WHAT_FD,
1N/A NULL),
1N/A (struct sockaddr *)&saddr_l,
1N/A &addrsize) == 0)
1N/A sasl_setprop(conn, SASL_IP_LOCAL,
1N/A &saddr_l);
1N/A }
1N/A }
1N/A# endif /* NETINET */
1N/A# endif /* SASL >= 20000 */
1N/A
1N/A auth_type = NULL;
1N/A mechlist = NULL;
1N/A user = NULL;
1N/A# if 0
1N/A macdefine(&BlankEnvelope.e_macro, A_PERM,
1N/A macid("{auth_author}"), NULL);
1N/A# endif /* 0 */
1N/A
1N/A /* set properties */
1N/A (void) memset(&ssp, '\0', sizeof(ssp));
1N/A
1N/A /* XXX should these be options settable via .cf ? */
1N/A /* ssp.min_ssf = 0; is default due to memset() */
1N/A ssp.max_ssf = MaxSLBits;
1N/A ssp.maxbufsize = MAXOUTLEN;
1N/A ssp.security_flags = SASLOpts & SASL_SEC_MASK;
1N/A sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
1N/A
1N/A if (sasl_ok)
1N/A {
1N/A /*
1N/A ** external security strength factor;
1N/A ** currently we have none so zero
1N/A */
1N/A
1N/A# if SASL >= 20000
1N/A ext_ssf = 0;
1N/A auth_id = NULL;
1N/A sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
1N/A &ext_ssf) == SASL_OK) &&
1N/A (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
1N/A auth_id) == SASL_OK));
1N/A# else /* SASL >= 20000 */
1N/A ext_ssf.ssf = 0;
1N/A ext_ssf.auth_id = NULL;
1N/A sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
1N/A &ext_ssf) == SASL_OK;
1N/A# endif /* SASL >= 20000 */
1N/A }
1N/A if (sasl_ok)
1N/A n_mechs = saslmechs(conn, &mechlist);
1N/A }
1N/A#endif /* SASL */
1N/A
1N/A#if STARTTLS
1N/A
1N/A
1N/A set_tls_rd_tmo(TimeOuts.to_nextcommand);
1N/A#endif /* STARTTLS */
1N/A
1N/A#if MILTER
1N/A if (smtp.sm_milterize)
1N/A {
1N/A char state;
1N/A
1N/A /* initialize mail filter connection */
1N/A smtp.sm_milterlist = milter_init(e, &state, &smtp.sm_milters);
1N/A switch (state)
1N/A {
1N/A case SMFIR_REJECT:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: initialization failed, rejecting commands");
1N/A greetcode = "554";
1N/A nullserver = "Command rejected";
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A
1N/A case SMFIR_TEMPFAIL:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: initialization failed, temp failing commands");
1N/A tempfail = true;
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A
1N/A case SMFIR_SHUTDOWN:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: initialization failed, closing connection");
1N/A tempfail = true;
1N/A smtp.sm_milterize = false;
1N/A message("421 4.7.0 %s closing connection",
1N/A MyHostName);
1N/A
1N/A /* arrange to ignore send list */
1N/A e->e_sendqueue = NULL;
1N/A lognullconnection = false;
1N/A goto doquit;
1N/A }
1N/A }
1N/A
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A q = macvalue(macid("{client_name}"), e);
1N/A SM_ASSERT(q != NULL || OpMode == MD_SMTP);
1N/A if (q == NULL)
1N/A q = "localhost";
1N/A response = milter_connect(q, RealHostAddr, e, &state);
1N/A switch (state)
1N/A {
1N/A case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
1N/A case SMFIR_REJECT:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: connect: host=%s, addr=%s, rejecting commands",
1N/A peerhostname,
1N/A anynet_ntoa(&RealHostAddr));
1N/A greetcode = "554";
1N/A nullserver = "Command rejected";
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A
1N/A case SMFIR_TEMPFAIL:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: connect: host=%s, addr=%s, temp failing commands",
1N/A peerhostname,
1N/A anynet_ntoa(&RealHostAddr));
1N/A tempfail = true;
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A
1N/A case SMFIR_SHUTDOWN:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: connect: host=%s, addr=%s, shutdown",
1N/A peerhostname,
1N/A anynet_ntoa(&RealHostAddr));
1N/A tempfail = true;
1N/A smtp.sm_milterize = false;
1N/A message("421 4.7.0 %s closing connection",
1N/A MyHostName);
1N/A
1N/A /* arrange to ignore send list */
1N/A e->e_sendqueue = NULL;
1N/A goto doquit;
1N/A }
1N/A if (response != NULL)
1N/A sm_free(response); /* XXX */
1N/A }
1N/A#endif /* MILTER */
1N/A
1N/A /*
1N/A ** Broken proxies and SMTP slammers
1N/A ** push data without waiting, catch them
1N/A */
1N/A
1N/A if (
1N/A#if STARTTLS
1N/A !smtps &&
1N/A#endif /* STARTTLS */
1N/A *greetcode == '2' && nullserver == NULL)
1N/A {
1N/A time_t msecs = 0;
1N/A char **pvp;
1N/A char pvpbuf[PSBUFSIZE];
1N/A
1N/A /* Ask the rulesets how long to pause */
1N/A pvp = NULL;
1N/A r = rscap("greet_pause", peerhostname,
1N/A anynet_ntoa(&RealHostAddr), e,
1N/A &pvp, pvpbuf, sizeof(pvpbuf));
1N/A if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
1N/A (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
1N/A {
1N/A msecs = strtol(pvp[1], NULL, 10);
1N/A }
1N/A
1N/A if (msecs > 0)
1N/A {
1N/A int fd;
1N/A fd_set readfds;
1N/A struct timeval timeout;
1N/A struct timeval bp, ep, tp; /* {begin,end,total}pause */
1N/A int eoftest;
1N/A
1N/A /* pause for a moment */
1N/A timeout.tv_sec = msecs / 1000;
1N/A timeout.tv_usec = (msecs % 1000) * 1000;
1N/A
1N/A /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
1N/A if (timeout.tv_sec >= 300)
1N/A {
1N/A timeout.tv_sec = 300;
1N/A timeout.tv_usec = 0;
1N/A }
1N/A
1N/A /* check if data is on the socket during the pause */
1N/A fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1N/A FD_ZERO(&readfds);
1N/A SM_FD_SET(fd, &readfds);
1N/A gettimeofday(&bp, NULL);
1N/A if (select(fd + 1, FDSET_CAST &readfds,
1N/A NULL, NULL, &timeout) > 0 &&
1N/A FD_ISSET(fd, &readfds) &&
1N/A (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT))
1N/A != SM_IO_EOF)
1N/A {
1N/A sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
1N/A eoftest);
1N/A gettimeofday(&ep, NULL);
1N/A timersub(&ep, &bp, &tp);
1N/A greetcode = "554";
1N/A nullserver = "Command rejected";
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
1N/A peerhostname,
1N/A anynet_ntoa(&RealHostAddr),
1N/A (int) tp.tv_sec +
1N/A (tp.tv_usec >= 500000 ? 1 : 0)
1N/A );
1N/A }
1N/A }
1N/A }
1N/A
1N/A#if STARTTLS
1N/A /* If this an smtps connection, start TLS now */
1N/A if (smtps)
1N/A {
1N/A Errors = 0;
1N/A goto starttls;
1N/A }
1N/A
1N/A greeting:
1N/A
1N/A#endif /* STARTTLS */
1N/A
1N/A /* output the first line, inserting "ESMTP" as second word */
1N/A if (*greetcode == '5')
1N/A (void) sm_snprintf(inp, sizeof(inp),
1N/A "%s not accepting messages", hostname);
1N/A else
1N/A expand(SmtpGreeting, inp, sizeof(inp), e);
1N/A
1N/A p = strchr(inp, '\n');
1N/A if (p != NULL)
1N/A *p++ = '\0';
1N/A id = strchr(inp, ' ');
1N/A if (id == NULL)
1N/A id = &inp[strlen(inp)];
1N/A if (p == NULL)
1N/A (void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
1N/A "%s %%.*s ESMTP%%s", greetcode);
1N/A else
1N/A (void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
1N/A "%s-%%.*s ESMTP%%s", greetcode);
1N/A message(cmdbuf, (int) (id - inp), inp, id);
1N/A
1N/A /* output remaining lines */
1N/A while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
1N/A {
1N/A *p++ = '\0';
1N/A if (isascii(*id) && isspace(*id))
1N/A id++;
1N/A (void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
1N/A message(cmdbuf, id);
1N/A }
1N/A if (id != NULL)
1N/A {
1N/A if (isascii(*id) && isspace(*id))
1N/A id++;
1N/A (void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
1N/A message(cmdbuf, id);
1N/A }
1N/A
1N/A protocol = NULL;
1N/A sendinghost = macvalue('s', e);
1N/A
1N/A /* If quarantining by a connect/ehlo action, save between messages */
1N/A if (e->e_quarmsg == NULL)
1N/A smtp.sm_quarmsg = NULL;
1N/A else
1N/A smtp.sm_quarmsg = newstr(e->e_quarmsg);
1N/A
1N/A /* sendinghost's storage must outlive the current envelope */
1N/A if (sendinghost != NULL)
1N/A sendinghost = sm_strdup_x(sendinghost);
1N/A first = true;
1N/A gothello = false;
1N/A smtp.sm_gotmail = false;
1N/A for (;;)
1N/A {
1N/A SM_TRY
1N/A {
1N/A QuickAbort = false;
1N/A HoldErrs = false;
1N/A SuprErrs = false;
1N/A LogUsrErrs = false;
1N/A OnlyOneError = true;
1N/A e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
1N/A#if MILTER
1N/A milter_cmd_fail = false;
1N/A#endif /* MILTER */
1N/A
1N/A /* setup for the read */
1N/A e->e_to = NULL;
1N/A Errors = 0;
1N/A FileName = NULL;
1N/A (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
1N/A
1N/A /* read the input line */
1N/A SmtpPhase = "server cmd read";
1N/A sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
1N/A
1N/A /* handle errors */
1N/A if (sm_io_error(OutChannel) ||
1N/A (p = sfgets(inp, sizeof(inp), InChannel,
1N/A TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
1N/A {
1N/A char *d;
1N/A
1N/A d = macvalue(macid("{daemon_name}"), e);
1N/A if (d == NULL)
1N/A d = "stdin";
1N/A /* end of file, just die */
1N/A disconnect(1, e);
1N/A
1N/A#if MILTER
1N/A /* close out milter filters */
1N/A milter_quit(e);
1N/A#endif /* MILTER */
1N/A
1N/A message("421 4.4.1 %s Lost input channel from %s",
1N/A MyHostName, CurSmtpClient);
1N/A if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
1N/A sm_syslog(LOG_NOTICE, e->e_id,
1N/A "lost input channel from %s to %s after %s",
1N/A CurSmtpClient, d,
1N/A (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
1N/A /*
1N/A ** If have not accepted mail (DATA), do not bounce
1N/A ** bad addresses back to sender.
1N/A */
1N/A
1N/A if (bitset(EF_CLRQUEUE, e->e_flags))
1N/A e->e_sendqueue = NULL;
1N/A goto doquit;
1N/A }
1N/A
1N/A /* also used by "proxy" check below */
1N/A inplen = strlen(inp);
1N/A#if SASL
1N/A /*
1N/A ** SMTP AUTH requires accepting any length,
1N/A ** at least for challenge/response. However, not imposing
1N/A ** a limit is a bad idea (denial of service).
1N/A */
1N/A
1N/A if (authenticating != SASL_PROC_AUTH
1N/A && sm_strncasecmp(inp, "AUTH ", 5) != 0
1N/A && inplen > MAXLINE)
1N/A {
1N/A message("421 4.7.0 %s Command too long, possible attack %s",
1N/A MyHostName, CurSmtpClient);
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: SMTP violation, input too long: %lu",
1N/A CurSmtpClient, (unsigned long) inplen);
1N/A goto doquit;
1N/A }
1N/A#endif /* SASL */
1N/A
1N/A if (first)
1N/A {
1N/A size_t cmdlen;
1N/A int idx;
1N/A char *http_cmd;
1N/A static char *http_cmds[] = { "GET", "POST",
1N/A "CONNECT", "USER", NULL };
1N/A
1N/A for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
1N/A idx++)
1N/A {
1N/A cmdlen = strlen(http_cmd);
1N/A if (cmdlen < inplen &&
1N/A sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
1N/A isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
1N/A {
1N/A /* Open proxy, drop it */
1N/A message("421 4.7.0 %s Rejecting open proxy %s",
1N/A MyHostName, CurSmtpClient);
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: probable open proxy: command=%.40s",
1N/A CurSmtpClient, inp);
1N/A goto doquit;
1N/A }
1N/A }
1N/A first = false;
1N/A }
1N/A
1N/A /* clean up end of line */
1N/A fixcrlf(inp, true);
1N/A
1N/A#if PIPELINING
1N/A# if _FFR_NO_PIPE
1N/A /*
1N/A ** if there is more input and pipelining is disabled:
1N/A ** delay ... (and maybe discard the input?)
1N/A ** XXX this doesn't really work, at least in tests using
1N/A ** telnet SM_IO_IS_READABLE only returns 1 if there were
1N/A ** more than 2 input lines available.
1N/A */
1N/A
1N/A if (bitset(SRV_NO_PIPE, features) &&
1N/A sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
1N/A {
1N/A if (++np_log < 3)
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "unauthorized PIPELINING, sleeping, relay=%.100s",
1N/A CurSmtpClient);
1N/A sleep(1);
1N/A }
1N/A
1N/A# endif /* _FFR_NO_PIPE */
1N/A#endif /* PIPELINING */
1N/A
1N/A#if SASL
1N/A if (authenticating == SASL_PROC_AUTH)
1N/A {
1N/A# if 0
1N/A if (*inp == '\0')
1N/A {
1N/A authenticating = SASL_NOT_AUTH;
1N/A message("501 5.5.2 missing input");
1N/A RESET_SASLCONN;
1N/A continue;
1N/A }
1N/A# endif /* 0 */
1N/A if (*inp == '*' && *(inp + 1) == '\0')
1N/A {
1N/A authenticating = SASL_NOT_AUTH;
1N/A
1N/A /* RFC 2554 4. */
1N/A message("501 5.0.0 AUTH aborted");
1N/A RESET_SASLCONN;
1N/A continue;
1N/A }
1N/A
1N/A /* could this be shorter? XXX */
1N/A# if SASL >= 20000
1N/A in = xalloc(strlen(inp) + 1);
1N/A result = sasl_decode64(inp, strlen(inp), in,
1N/A strlen(inp), &inlen);
1N/A# else /* SASL >= 20000 */
1N/A out = xalloc(strlen(inp));
1N/A result = sasl_decode64(inp, strlen(inp), out, &outlen);
1N/A# endif /* SASL >= 20000 */
1N/A if (result != SASL_OK)
1N/A {
1N/A authenticating = SASL_NOT_AUTH;
1N/A
1N/A /* RFC 2554 4. */
1N/A message("501 5.5.4 cannot decode AUTH parameter %s",
1N/A inp);
1N/A# if SASL >= 20000
1N/A sm_free(in);
1N/A# endif /* SASL >= 20000 */
1N/A RESET_SASLCONN;
1N/A continue;
1N/A }
1N/A
1N/A# if SASL >= 20000
1N/A result = sasl_server_step(conn, in, inlen,
1N/A &out, &outlen);
1N/A sm_free(in);
1N/A# else /* SASL >= 20000 */
1N/A result = sasl_server_step(conn, out, outlen,
1N/A &out, &outlen, &errstr);
1N/A# endif /* SASL >= 20000 */
1N/A
1N/A /* get an OK if we're done */
1N/A if (result == SASL_OK)
1N/A {
1N/A authenticated:
1N/A message("235 2.0.0 OK Authenticated");
1N/A authenticating = SASL_IS_AUTH;
1N/A macdefine(&BlankEnvelope.e_macro, A_TEMP,
1N/A macid("{auth_type}"), auth_type);
1N/A
1N/A# if SASL >= 20000
1N/A user = macvalue(macid("{auth_authen}"), e);
1N/A
1N/A /* get security strength (features) */
1N/A result = sasl_getprop(conn, SASL_SSF,
1N/A (const void **) &ssf);
1N/A# else /* SASL >= 20000 */
1N/A result = sasl_getprop(conn, SASL_USERNAME,
1N/A (void **)&user);
1N/A if (result != SASL_OK)
1N/A {
1N/A user = "";
1N/A macdefine(&BlankEnvelope.e_macro,
1N/A A_PERM,
1N/A macid("{auth_authen}"), NULL);
1N/A }
1N/A else
1N/A {
1N/A macdefine(&BlankEnvelope.e_macro,
1N/A A_TEMP,
1N/A macid("{auth_authen}"),
1N/A xtextify(user, "<>\")"));
1N/A }
1N/A
1N/A# if 0
1N/A /* get realm? */
1N/A sasl_getprop(conn, SASL_REALM, (void **) &data);
1N/A# endif /* 0 */
1N/A
1N/A /* get security strength (features) */
1N/A result = sasl_getprop(conn, SASL_SSF,
1N/A (void **) &ssf);
1N/A# endif /* SASL >= 20000 */
1N/A if (result != SASL_OK)
1N/A {
1N/A macdefine(&BlankEnvelope.e_macro,
1N/A A_PERM,
1N/A macid("{auth_ssf}"), "0");
1N/A ssf = NULL;
1N/A }
1N/A else
1N/A {
1N/A char pbuf[8];
1N/A
1N/A (void) sm_snprintf(pbuf, sizeof(pbuf),
1N/A "%u", *ssf);
1N/A macdefine(&BlankEnvelope.e_macro,
1N/A A_TEMP,
1N/A macid("{auth_ssf}"), pbuf);
1N/A if (tTd(95, 8))
1N/A sm_dprintf("AUTH auth_ssf: %u\n",
1N/A *ssf);
1N/A }
1N/A
1N/A /*
1N/A ** Only switch to encrypted connection
1N/A ** if a security layer has been negotiated
1N/A */
1N/A
1N/A if (ssf != NULL && *ssf > 0)
1N/A {
1N/A int tmo;
1N/A
1N/A /*
1N/A ** Convert I/O layer to use SASL.
1N/A ** If the call fails, the connection
1N/A ** is aborted.
1N/A */
1N/A
1N/A tmo = TimeOuts.to_datablock * 1000;
1N/A if (sfdcsasl(&InChannel, &OutChannel,
1N/A conn, tmo) == 0)
1N/A {
1N/A /* restart dialogue */
1N/A n_helo = 0;
1N/A# if PIPELINING
1N/A (void) sm_io_autoflush(InChannel,
1N/A OutChannel);
1N/A# endif /* PIPELINING */
1N/A }
1N/A else
1N/A syserr("503 5.3.3 SASL TLS failed");
1N/A }
1N/A
1N/A /* NULL pointer ok since it's our function */
1N/A if (LogLevel > 8)
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
1N/A CurSmtpClient,
1N/A shortenstring(user, 128),
1N/A auth_type, *ssf);
1N/A }
1N/A else if (result == SASL_CONTINUE)
1N/A {
1N/A len = ENC64LEN(outlen);
1N/A out2 = xalloc(len);
1N/A result = sasl_encode64(out, outlen, out2, len,
1N/A &out2len);
1N/A if (result != SASL_OK)
1N/A {
1N/A /* correct code? XXX */
1N/A /* 454 Temp. authentication failure */
1N/A message("454 4.5.4 Internal error: unable to encode64");
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_WARNING, e->e_id,
1N/A "AUTH encode64 error [%d for \"%s\"], relay=%.100s",
1N/A result, out,
1N/A CurSmtpClient);
1N/A /* start over? */
1N/A authenticating = SASL_NOT_AUTH;
1N/A }
1N/A else
1N/A {
1N/A message("334 %s", out2);
1N/A if (tTd(95, 2))
1N/A sm_dprintf("AUTH continue: msg='%s' len=%u\n",
1N/A out2, out2len);
1N/A }
1N/A# if SASL >= 20000
1N/A sm_free(out2);
1N/A# endif /* SASL >= 20000 */
1N/A }
1N/A else
1N/A {
1N/A /* not SASL_OK or SASL_CONT */
1N/A message("535 5.7.0 authentication failed");
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_WARNING, e->e_id,
1N/A "AUTH failure (%s): %s (%d) %s, relay=%.100s",
1N/A auth_type,
1N/A sasl_errstring(result, NULL,
1N/A NULL),
1N/A result,
1N/A# if SASL >= 20000
1N/A sasl_errdetail(conn),
1N/A# else /* SASL >= 20000 */
1N/A errstr == NULL ? "" : errstr,
1N/A# endif /* SASL >= 20000 */
1N/A CurSmtpClient);
1N/A RESET_SASLCONN;
1N/A authenticating = SASL_NOT_AUTH;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A /* don't want to do any of this if authenticating */
1N/A#endif /* SASL */
1N/A
1N/A /* echo command to transcript */
1N/A if (e->e_xfp != NULL)
1N/A (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
1N/A "<<< %s\n", inp);
1N/A
1N/A if (LogLevel > 14)
1N/A sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
1N/A
1N/A /* break off command */
1N/A for (p = inp; isascii(*p) && isspace(*p); p++)
1N/A continue;
1N/A cmd = cmdbuf;
1N/A while (*p != '\0' &&
1N/A !(isascii(*p) && isspace(*p)) &&
1N/A cmd < &cmdbuf[sizeof(cmdbuf) - 2])
1N/A *cmd++ = *p++;
1N/A *cmd = '\0';
1N/A
1N/A /* throw away leading whitespace */
1N/A SKIP_SPACE(p);
1N/A
1N/A /* decode command */
1N/A for (c = CmdTab; c->cmd_name != NULL; c++)
1N/A {
1N/A if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
1N/A break;
1N/A }
1N/A
1N/A /* reset errors */
1N/A errno = 0;
1N/A
1N/A /* check whether a "non-null" command has been used */
1N/A switch (c->cmd_code)
1N/A {
1N/A#if SASL
1N/A case CMDAUTH:
1N/A /* avoid information leak; take first two words? */
1N/A q = "AUTH";
1N/A break;
1N/A#endif /* SASL */
1N/A
1N/A case CMDMAIL:
1N/A case CMDEXPN:
1N/A case CMDVRFY:
1N/A case CMDETRN:
1N/A lognullconnection = false;
1N/A /* FALLTHROUGH */
1N/A default:
1N/A q = inp;
1N/A break;
1N/A }
1N/A
1N/A if (e->e_id == NULL)
1N/A sm_setproctitle(true, e, "%s: %.80s",
1N/A CurSmtpClient, q);
1N/A else
1N/A sm_setproctitle(true, e, "%s %s: %.80s",
1N/A qid_printname(e),
1N/A CurSmtpClient, q);
1N/A
1N/A /*
1N/A ** Process command.
1N/A **
1N/A ** If we are running as a null server, return 550
1N/A ** to almost everything.
1N/A */
1N/A
1N/A if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
1N/A {
1N/A switch (c->cmd_code)
1N/A {
1N/A case CMDQUIT:
1N/A case CMDHELO:
1N/A case CMDEHLO:
1N/A case CMDNOOP:
1N/A case CMDRSET:
1N/A case CMDERROR:
1N/A /* process normally */
1N/A break;
1N/A
1N/A case CMDETRN:
1N/A if (bitnset(D_ETRNONLY, d_flags) &&
1N/A nullserver == NULL)
1N/A break;
1N/A DELAY_CONN("ETRN");
1N/A /* FALLTHROUGH */
1N/A
1N/A default:
1N/A#if MAXBADCOMMANDS > 0
1N/A /* theoretically this could overflow */
1N/A if (nullserver != NULL &&
1N/A ++n_badcmds > MAXBADCOMMANDS)
1N/A {
1N/A message("421 4.7.0 %s Too many bad commands; closing connection",
1N/A MyHostName);
1N/A
1N/A /* arrange to ignore send list */
1N/A e->e_sendqueue = NULL;
1N/A goto doquit;
1N/A }
1N/A#endif /* MAXBADCOMMANDS > 0 */
1N/A if (nullserver != NULL)
1N/A {
1N/A if (ISSMTPREPLY(nullserver))
1N/A usrerr(nullserver);
1N/A else
1N/A usrerr("550 5.0.0 %s",
1N/A nullserver);
1N/A }
1N/A else
1N/A usrerr("452 4.4.5 Insufficient disk space; try again later");
1N/A continue;
1N/A }
1N/A }
1N/A
1N/A switch (c->cmd_code)
1N/A {
1N/A#if SASL
1N/A case CMDAUTH: /* sasl */
1N/A DELAY_CONN("AUTH");
1N/A if (!sasl_ok || n_mechs <= 0)
1N/A {
1N/A message("503 5.3.3 AUTH not available");
1N/A break;
1N/A }
1N/A if (authenticating == SASL_IS_AUTH)
1N/A {
1N/A message("503 5.5.0 Already Authenticated");
1N/A break;
1N/A }
1N/A if (smtp.sm_gotmail)
1N/A {
1N/A message("503 5.5.0 AUTH not permitted during a mail transaction");
1N/A break;
1N/A }
1N/A if (tempfail)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
1N/A p, CurSmtpClient);
1N/A usrerr("454 4.3.0 Please try again later");
1N/A break;
1N/A }
1N/A
1N/A ismore = false;
1N/A
1N/A /* crude way to avoid crack attempts */
1N/A STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
1N/A true, "AUTH", e));
1N/A
1N/A /* make sure mechanism (p) is a valid string */
1N/A for (q = p; *q != '\0' && isascii(*q); q++)
1N/A {
1N/A if (isspace(*q))
1N/A {
1N/A *q = '\0';
1N/A while (*++q != '\0' &&
1N/A isascii(*q) && isspace(*q))
1N/A continue;
1N/A *(q - 1) = '\0';
1N/A ismore = (*q != '\0');
1N/A break;
1N/A }
1N/A }
1N/A
1N/A if (*p == '\0')
1N/A {
1N/A message("501 5.5.2 AUTH mechanism must be specified");
1N/A break;
1N/A }
1N/A
1N/A /* check whether mechanism is available */
1N/A if (iteminlist(p, mechlist, " ") == NULL)
1N/A {
1N/A message("504 5.3.3 AUTH mechanism %.32s not available",
1N/A p);
1N/A break;
1N/A }
1N/A
1N/A /*
1N/A ** RFC 2554 4.
1N/A ** Unlike a zero-length client answer to a
1N/A ** 334 reply, a zero- length initial response
1N/A ** is sent as a single equals sign ("=").
1N/A */
1N/A
1N/A if (ismore && *q == '=' && *(q + 1) == '\0')
1N/A {
1N/A /* will be free()d, don't use in=""; */
1N/A in = xalloc(1);
1N/A *in = '\0';
1N/A inlen = 0;
1N/A }
1N/A else if (ismore)
1N/A {
1N/A /* could this be shorter? XXX */
1N/A# if SASL >= 20000
1N/A in = xalloc(strlen(q) + 1);
1N/A result = sasl_decode64(q, strlen(q), in,
1N/A strlen(q), &inlen);
1N/A# else /* SASL >= 20000 */
1N/A in = sm_rpool_malloc(e->e_rpool, strlen(q));
1N/A result = sasl_decode64(q, strlen(q), in,
1N/A &inlen);
1N/A# endif /* SASL >= 20000 */
1N/A if (result != SASL_OK)
1N/A {
1N/A message("501 5.5.4 cannot BASE64 decode '%s'",
1N/A q);
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_WARNING, e->e_id,
1N/A "AUTH decode64 error [%d for \"%s\"], relay=%.100s",
1N/A result, q,
1N/A CurSmtpClient);
1N/A /* start over? */
1N/A authenticating = SASL_NOT_AUTH;
1N/A# if SASL >= 20000
1N/A sm_free(in);
1N/A# endif /* SASL >= 20000 */
1N/A in = NULL;
1N/A inlen = 0;
1N/A break;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A in = NULL;
1N/A inlen = 0;
1N/A }
1N/A
1N/A /* see if that auth type exists */
1N/A# if SASL >= 20000
1N/A result = sasl_server_start(conn, p, in, inlen,
1N/A &out, &outlen);
1N/A if (in != NULL)
1N/A sm_free(in);
1N/A# else /* SASL >= 20000 */
1N/A result = sasl_server_start(conn, p, in, inlen,
1N/A &out, &outlen, &errstr);
1N/A# endif /* SASL >= 20000 */
1N/A
1N/A if (result != SASL_OK && result != SASL_CONTINUE)
1N/A {
1N/A message("535 5.7.0 authentication failed");
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_ERR, e->e_id,
1N/A "AUTH failure (%s): %s (%d) %s, relay=%.100s",
1N/A p,
1N/A sasl_errstring(result, NULL,
1N/A NULL),
1N/A result,
1N/A# if SASL >= 20000
1N/A sasl_errdetail(conn),
1N/A# else /* SASL >= 20000 */
1N/A errstr,
1N/A# endif /* SASL >= 20000 */
1N/A CurSmtpClient);
1N/A RESET_SASLCONN;
1N/A break;
1N/A }
1N/A auth_type = newstr(p);
1N/A
1N/A if (result == SASL_OK)
1N/A {
1N/A /* ugly, but same code */
1N/A goto authenticated;
1N/A /* authenticated by the initial response */
1N/A }
1N/A
1N/A /* len is at least 2 */
1N/A len = ENC64LEN(outlen);
1N/A out2 = xalloc(len);
1N/A result = sasl_encode64(out, outlen, out2, len,
1N/A &out2len);
1N/A
1N/A if (result != SASL_OK)
1N/A {
1N/A message("454 4.5.4 Temporary authentication failure");
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_WARNING, e->e_id,
1N/A "AUTH encode64 error [%d for \"%s\"]",
1N/A result, out);
1N/A
1N/A /* start over? */
1N/A authenticating = SASL_NOT_AUTH;
1N/A RESET_SASLCONN;
1N/A }
1N/A else
1N/A {
1N/A message("334 %s", out2);
1N/A authenticating = SASL_PROC_AUTH;
1N/A }
1N/A# if SASL >= 20000
1N/A sm_free(out2);
1N/A# endif /* SASL >= 20000 */
1N/A break;
1N/A#endif /* SASL */
1N/A
1N/A#if STARTTLS
1N/A case CMDSTLS: /* starttls */
1N/A DELAY_CONN("STARTTLS");
1N/A if (*p != '\0')
1N/A {
1N/A message("501 5.5.2 Syntax error (no parameters allowed)");
1N/A break;
1N/A }
1N/A if (!bitset(SRV_OFFER_TLS, features))
1N/A {
1N/A message("503 5.5.0 TLS not available");
1N/A break;
1N/A }
1N/A if (!tls_ok_srv)
1N/A {
1N/A message("454 4.3.3 TLS not available after start");
1N/A break;
1N/A }
1N/A if (smtp.sm_gotmail)
1N/A {
1N/A message("503 5.5.0 TLS not permitted during a mail transaction");
1N/A break;
1N/A }
1N/A if (tempfail)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
1N/A p, CurSmtpClient);
1N/A usrerr("454 4.7.0 Please try again later");
1N/A break;
1N/A }
1N/A starttls:
1N/A# if USE_OPENSSL_ENGINE
1N/A if (!SSLEngineInitialized)
1N/A {
1N/A if (!SSL_set_engine(NULL))
1N/A {
1N/A sm_syslog(LOG_ERR, NOQID,
1N/A "STARTTLS=server, SSL_set_engine=failed");
1N/A tls_ok_srv = false;
1N/A message("454 4.3.3 TLS not available right now");
1N/A break;
1N/A }
1N/A else
1N/A SSLEngineInitialized = true;
1N/A }
1N/A# endif /* USE_OPENSSL_ENGINE */
1N/A# if TLS_NO_RSA
1N/A /*
1N/A ** XXX do we need a temp key ?
1N/A */
1N/A# else /* TLS_NO_RSA */
1N/A# endif /* TLS_NO_RSA */
1N/A
1N/A# if TLS_VRFY_PER_CTX
1N/A /*
1N/A ** Note: this sets the verification globally
1N/A ** (per SSL_CTX)
1N/A ** it's ok since it applies only to one transaction
1N/A */
1N/A
1N/A TLS_VERIFY_CLIENT();
1N/A# endif /* TLS_VRFY_PER_CTX */
1N/A
1N/A if (srv_ssl != NULL)
1N/A SSL_clear(srv_ssl);
1N/A else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
1N/A {
1N/A message("454 4.3.3 TLS not available: error generating SSL handle");
1N/A if (LogLevel > 8)
1N/A tlslogerr("server");
1N/A goto tls_done;
1N/A }
1N/A
1N/A# if !TLS_VRFY_PER_CTX
1N/A /*
1N/A ** this could be used if it were possible to set
1N/A ** verification per SSL (connection)
1N/A ** not just per SSL_CTX (global)
1N/A */
1N/A
1N/A TLS_VERIFY_CLIENT();
1N/A# endif /* !TLS_VRFY_PER_CTX */
1N/A
1N/A rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1N/A wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
1N/A
1N/A if (rfd < 0 || wfd < 0 ||
1N/A SSL_set_rfd(srv_ssl, rfd) <= 0 ||
1N/A SSL_set_wfd(srv_ssl, wfd) <= 0)
1N/A {
1N/A message("454 4.3.3 TLS not available: error set fd");
1N/A SSL_free(srv_ssl);
1N/A srv_ssl = NULL;
1N/A goto tls_done;
1N/A }
1N/A if (!smtps)
1N/A message("220 2.0.0 Ready to start TLS");
1N/A# if PIPELINING
1N/A (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
1N/A# endif /* PIPELINING */
1N/A
1N/A SSL_set_accept_state(srv_ssl);
1N/A
1N/A# define SSL_ACC(s) SSL_accept(s)
1N/A
1N/A tlsstart = curtime();
1N/A ssl_retry:
1N/A if ((r = SSL_ACC(srv_ssl)) <= 0)
1N/A {
1N/A int i, ssl_err;
1N/A
1N/A ssl_err = SSL_get_error(srv_ssl, r);
1N/A i = tls_retry(srv_ssl, rfd, wfd, tlsstart,
1N/A TimeOuts.to_starttls, ssl_err,
1N/A "server");
1N/A if (i > 0)
1N/A goto ssl_retry;
1N/A
1N/A if (LogLevel > 5)
1N/A {
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "STARTTLS=server, error: accept failed=%d, SSL_error=%d, errno=%d, retry=%d, relay=%.100s",
1N/A r, ssl_err, errno, i,
1N/A CurSmtpClient);
1N/A if (LogLevel > 8)
1N/A tlslogerr("server");
1N/A }
1N/A tls_ok_srv = false;
1N/A SSL_free(srv_ssl);
1N/A srv_ssl = NULL;
1N/A
1N/A /*
1N/A ** according to the next draft of
1N/A ** RFC 2487 the connection should be dropped
1N/A */
1N/A
1N/A /* arrange to ignore any current send list */
1N/A e->e_sendqueue = NULL;
1N/A goto doquit;
1N/A }
1N/A
1N/A /* ignore return code for now, it's in {verify} */
1N/A (void) tls_get_info(srv_ssl, true,
1N/A CurSmtpClient,
1N/A &BlankEnvelope.e_macro,
1N/A bitset(SRV_VRFY_CLT, features));
1N/A
1N/A /*
1N/A ** call Stls_client to find out whether
1N/A ** to accept the connection from the client
1N/A */
1N/A
1N/A saveQuickAbort = QuickAbort;
1N/A saveSuprErrs = SuprErrs;
1N/A SuprErrs = true;
1N/A QuickAbort = false;
1N/A if (rscheck("tls_client",
1N/A macvalue(macid("{verify}"), e),
1N/A "STARTTLS", e,
1N/A RSF_RMCOMM|RSF_COUNT,
1N/A 5, NULL, NOQID, NULL) != EX_OK ||
1N/A Errors > 0)
1N/A {
1N/A extern char MsgBuf[];
1N/A
1N/A if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
1N/A nullserver = newstr(MsgBuf);
1N/A else
1N/A nullserver = "503 5.7.0 Authentication required.";
1N/A }
1N/A QuickAbort = saveQuickAbort;
1N/A SuprErrs = saveSuprErrs;
1N/A
1N/A tls_ok_srv = false; /* don't offer STARTTLS again */
1N/A n_helo = 0;
1N/A# if SASL
1N/A if (sasl_ok)
1N/A {
1N/A int cipher_bits;
1N/A bool verified;
1N/A char *s, *v, *c;
1N/A
1N/A s = macvalue(macid("{cipher_bits}"), e);
1N/A v = macvalue(macid("{verify}"), e);
1N/A c = macvalue(macid("{cert_subject}"), e);
1N/A verified = (v != NULL && strcmp(v, "OK") == 0);
1N/A if (s != NULL && (cipher_bits = atoi(s)) > 0)
1N/A {
1N/A# if SASL >= 20000
1N/A ext_ssf = cipher_bits;
1N/A auth_id = verified ? c : NULL;
1N/A sasl_ok = ((sasl_setprop(conn,
1N/A SASL_SSF_EXTERNAL,
1N/A &ext_ssf) == SASL_OK) &&
1N/A (sasl_setprop(conn,
1N/A SASL_AUTH_EXTERNAL,
1N/A auth_id) == SASL_OK));
1N/A# else /* SASL >= 20000 */
1N/A ext_ssf.ssf = cipher_bits;
1N/A ext_ssf.auth_id = verified ? c : NULL;
1N/A sasl_ok = sasl_setprop(conn,
1N/A SASL_SSF_EXTERNAL,
1N/A &ext_ssf) == SASL_OK;
1N/A# endif /* SASL >= 20000 */
1N/A mechlist = NULL;
1N/A if (sasl_ok)
1N/A n_mechs = saslmechs(conn,
1N/A &mechlist);
1N/A }
1N/A }
1N/A# endif /* SASL */
1N/A
1N/A /* switch to secure connection */
1N/A if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
1N/A {
1N/A tls_active = true;
1N/A# if PIPELINING
1N/A (void) sm_io_autoflush(InChannel, OutChannel);
1N/A# endif /* PIPELINING */
1N/A }
1N/A else
1N/A {
1N/A /*
1N/A ** XXX this is an internal error
1N/A ** how to deal with it?
1N/A ** we can't generate an error message
1N/A ** since the other side switched to an
1N/A ** encrypted layer, but we could not...
1N/A ** just "hang up"?
1N/A */
1N/A
1N/A nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
1N/A syserr("STARTTLS: can't switch to encrypted layer");
1N/A }
1N/A tls_done:
1N/A if (smtps)
1N/A {
1N/A if (tls_active)
1N/A goto greeting;
1N/A else
1N/A goto doquit;
1N/A }
1N/A break;
1N/A#endif /* STARTTLS */
1N/A
1N/A case CMDHELO: /* hello -- introduce yourself */
1N/A case CMDEHLO: /* extended hello */
1N/A DELAY_CONN("EHLO");
1N/A if (c->cmd_code == CMDEHLO)
1N/A {
1N/A protocol = "ESMTP";
1N/A SmtpPhase = "server EHLO";
1N/A }
1N/A else
1N/A {
1N/A protocol = "SMTP";
1N/A SmtpPhase = "server HELO";
1N/A }
1N/A
1N/A /* avoid denial-of-service */
1N/A STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
1N/A true, "HELO/EHLO", e));
1N/A
1N/A#if 0
1N/A /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
1N/A /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
1N/A if (gothello)
1N/A {
1N/A usrerr("503 %s Duplicate HELO/EHLO",
1N/A MyHostName);
1N/A break;
1N/A }
1N/A#endif /* 0 */
1N/A
1N/A /* check for valid domain name (re 1123 5.2.5) */
1N/A if (*p == '\0' && !AllowBogusHELO)
1N/A {
1N/A usrerr("501 %s requires domain address",
1N/A cmdbuf);
1N/A break;
1N/A }
1N/A
1N/A /* check for long domain name (hides Received: info) */
1N/A if (strlen(p) > MAXNAME)
1N/A {
1N/A usrerr("501 Invalid domain name");
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, CurEnv->e_id,
1N/A "invalid domain name (too long) from %s",
1N/A CurSmtpClient);
1N/A break;
1N/A }
1N/A
1N/A ok = true;
1N/A for (q = p; *q != '\0'; q++)
1N/A {
1N/A if (!isascii(*q))
1N/A break;
1N/A if (isalnum(*q))
1N/A continue;
1N/A if (isspace(*q))
1N/A {
1N/A *q = '\0';
1N/A
1N/A /* only complain if strict check */
1N/A ok = AllowBogusHELO;
1N/A
1N/A /* allow trailing whitespace */
1N/A while (!ok && *++q != '\0' &&
1N/A isspace(*q))
1N/A ;
1N/A if (*q == '\0')
1N/A ok = true;
1N/A break;
1N/A }
1N/A if (strchr("[].-_#:", *q) == NULL)
1N/A break;
1N/A }
1N/A
1N/A if (*q == '\0' && ok)
1N/A {
1N/A q = "pleased to meet you";
1N/A sendinghost = sm_strdup_x(p);
1N/A }
1N/A else if (!AllowBogusHELO)
1N/A {
1N/A usrerr("501 Invalid domain name");
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, CurEnv->e_id,
1N/A "invalid domain name (%s) from %.100s",
1N/A p, CurSmtpClient);
1N/A break;
1N/A }
1N/A else
1N/A {
1N/A q = "accepting invalid domain name";
1N/A }
1N/A
1N/A if (gothello || smtp.sm_gotmail)
1N/A CLEAR_STATE(cmdbuf);
1N/A
1N/A#if MILTER
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A response = milter_helo(p, e, &state);
1N/A switch (state)
1N/A {
1N/A case SMFIR_REJECT:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: helo=%s, reject=Command rejected",
1N/A p);
1N/A nullserver = "Command rejected";
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A
1N/A case SMFIR_TEMPFAIL:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: helo=%s, reject=%s",
1N/A p, MSG_TEMPFAIL);
1N/A tempfail = true;
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A
1N/A case SMFIR_REPLYCODE:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: helo=%s, reject=%s",
1N/A p, response);
1N/A if (strncmp(response, "421 ", 4) != 0
1N/A && strncmp(response, "421-", 4) != 0)
1N/A {
1N/A nullserver = newstr(response);
1N/A smtp.sm_milterize = false;
1N/A break;
1N/A }
1N/A /* FALLTHROUGH */
1N/A
1N/A case SMFIR_SHUTDOWN:
1N/A if (MilterLogLevel > 3 &&
1N/A response == NULL)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: helo=%s, reject=421 4.7.0 %s closing connection",
1N/A p, MyHostName);
1N/A tempfail = true;
1N/A smtp.sm_milterize = false;
1N/A if (response != NULL)
1N/A usrerr(response);
1N/A else
1N/A message("421 4.7.0 %s closing connection",
1N/A MyHostName);
1N/A /* arrange to ignore send list */
1N/A e->e_sendqueue = NULL;
1N/A lognullconnection = false;
1N/A goto doquit;
1N/A }
1N/A if (response != NULL)
1N/A sm_free(response);
1N/A
1N/A /*
1N/A ** If quarantining by a connect/ehlo action,
1N/A ** save between messages
1N/A */
1N/A
1N/A if (smtp.sm_quarmsg == NULL &&
1N/A e->e_quarmsg != NULL)
1N/A smtp.sm_quarmsg = newstr(e->e_quarmsg);
1N/A }
1N/A#endif /* MILTER */
1N/A gothello = true;
1N/A
1N/A /* print HELO response message */
1N/A if (c->cmd_code != CMDEHLO)
1N/A {
1N/A message("250 %s Hello %s, %s",
1N/A MyHostName, CurSmtpClient, q);
1N/A break;
1N/A }
1N/A
1N/A message("250-%s Hello %s, %s",
1N/A MyHostName, CurSmtpClient, q);
1N/A
1N/A /* offer ENHSC even for nullserver */
1N/A if (nullserver != NULL)
1N/A {
1N/A message("250 ENHANCEDSTATUSCODES");
1N/A break;
1N/A }
1N/A
1N/A /*
1N/A ** print EHLO features list
1N/A **
1N/A ** Note: If you change this list,
1N/A ** remember to update 'helpfile'
1N/A */
1N/A
1N/A message("250-ENHANCEDSTATUSCODES");
1N/A#if PIPELINING
1N/A if (bitset(SRV_OFFER_PIPE, features))
1N/A message("250-PIPELINING");
1N/A#endif /* PIPELINING */
1N/A if (bitset(SRV_OFFER_EXPN, features))
1N/A {
1N/A message("250-EXPN");
1N/A if (bitset(SRV_OFFER_VERB, features))
1N/A message("250-VERB");
1N/A }
1N/A#if MIME8TO7
1N/A message("250-8BITMIME");
1N/A#endif /* MIME8TO7 */
1N/A if (MaxMessageSize > 0)
1N/A message("250-SIZE %ld", MaxMessageSize);
1N/A else
1N/A message("250-SIZE");
1N/A#if DSN
1N/A if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
1N/A message("250-DSN");
1N/A#endif /* DSN */
1N/A if (bitset(SRV_OFFER_ETRN, features))
1N/A message("250-ETRN");
1N/A#if SASL
1N/A if (sasl_ok && mechlist != NULL && *mechlist != '\0')
1N/A message("250-AUTH %s", mechlist);
1N/A#endif /* SASL */
1N/A#if STARTTLS
1N/A if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
1N/A message("250-STARTTLS");
1N/A#endif /* STARTTLS */
1N/A if (DeliverByMin > 0)
1N/A message("250-DELIVERBY %ld",
1N/A (long) DeliverByMin);
1N/A else if (DeliverByMin == 0)
1N/A message("250-DELIVERBY");
1N/A
1N/A /* < 0: no deliver-by */
1N/A
1N/A message("250 HELP");
1N/A break;
1N/A
1N/A case CMDMAIL: /* mail -- designate sender */
1N/A SmtpPhase = "server MAIL";
1N/A DELAY_CONN("MAIL");
1N/A
1N/A /* check for validity of this command */
1N/A if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
1N/A {
1N/A usrerr("503 5.0.0 Polite people say HELO first");
1N/A break;
1N/A }
1N/A if (smtp.sm_gotmail)
1N/A {
1N/A usrerr("503 5.5.0 Sender already specified");
1N/A break;
1N/A }
1N/A#if SASL
1N/A if (bitset(SRV_REQ_AUTH, features) &&
1N/A authenticating != SASL_IS_AUTH)
1N/A {
1N/A usrerr("530 5.7.0 Authentication required");
1N/A break;
1N/A }
1N/A#endif /* SASL */
1N/A
1N/A p = skipword(p, "from");
1N/A if (p == NULL)
1N/A break;
1N/A if (tempfail)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
1N/A p, CurSmtpClient);
1N/A usrerr(MSG_TEMPFAIL);
1N/A break;
1N/A }
1N/A
1N/A /* make sure we know who the sending host is */
1N/A if (sendinghost == NULL)
1N/A sendinghost = peerhostname;
1N/A
1N/A
1N/A#if SM_HEAP_CHECK
1N/A if (sm_debug_active(&DebugLeakSmtp, 1))
1N/A {
1N/A sm_heap_newgroup();
1N/A sm_dprintf("smtp() heap group #%d\n",
1N/A sm_heap_group());
1N/A }
1N/A#endif /* SM_HEAP_CHECK */
1N/A
1N/A if (Errors > 0)
1N/A goto undo_no_pm;
1N/A if (!gothello)
1N/A {
1N/A auth_warning(e, "%s didn't use HELO protocol",
1N/A CurSmtpClient);
1N/A }
1N/A#ifdef PICKY_HELO_CHECK
1N/A if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
1N/A (sm_strcasecmp(peerhostname, "localhost") != 0 ||
1N/A sm_strcasecmp(sendinghost, MyHostName) != 0))
1N/A {
1N/A auth_warning(e, "Host %s claimed to be %s",
1N/A CurSmtpClient, sendinghost);
1N/A }
1N/A#endif /* PICKY_HELO_CHECK */
1N/A
1N/A if (protocol == NULL)
1N/A protocol = "SMTP";
1N/A macdefine(&e->e_macro, A_PERM, 'r', protocol);
1N/A macdefine(&e->e_macro, A_PERM, 's', sendinghost);
1N/A
1N/A if (Errors > 0)
1N/A goto undo_no_pm;
1N/A smtp.sm_nrcpts = 0;
1N/A n_badrcpts = 0;
1N/A macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
1N/A macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
1N/A macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
1N/A "0");
1N/A e->e_flags |= EF_CLRQUEUE;
1N/A sm_setproctitle(true, e, "%s %s: %.80s",
1N/A qid_printname(e),
1N/A CurSmtpClient, inp);
1N/A
1N/A /* do the processing */
1N/A SM_TRY
1N/A {
1N/A extern char *FullName;
1N/A
1N/A QuickAbort = true;
1N/A SM_FREE_CLR(FullName);
1N/A
1N/A /* must parse sender first */
1N/A delimptr = NULL;
1N/A setsender(p, e, &delimptr, ' ', false);
1N/A if (delimptr != NULL && *delimptr != '\0')
1N/A *delimptr++ = '\0';
1N/A if (Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A
1N/A /* Successfully set e_from, allow logging */
1N/A e->e_flags |= EF_LOGSENDER;
1N/A
1N/A /* put resulting triple from parseaddr() into macros */
1N/A if (e->e_from.q_mailer != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{mail_mailer}"),
1N/A e->e_from.q_mailer->m_name);
1N/A else
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{mail_mailer}"), NULL);
1N/A if (e->e_from.q_host != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{mail_host}"),
1N/A e->e_from.q_host);
1N/A else
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{mail_host}"), "localhost");
1N/A if (e->e_from.q_user != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{mail_addr}"),
1N/A e->e_from.q_user);
1N/A else
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{mail_addr}"), NULL);
1N/A if (Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A
1N/A /* check for possible spoofing */
1N/A if (RealUid != 0 && OpMode == MD_SMTP &&
1N/A !wordinclass(RealUserName, 't') &&
1N/A (!bitnset(M_LOCALMAILER,
1N/A e->e_from.q_mailer->m_flags) ||
1N/A strcmp(e->e_from.q_user, RealUserName) != 0))
1N/A {
1N/A auth_warning(e, "%s owned process doing -bs",
1N/A RealUserName);
1N/A }
1N/A
1N/A /* reset to default value */
1N/A SevenBitInput = SevenBitInput_Saved;
1N/A
1N/A /* now parse ESMTP arguments */
1N/A e->e_msgsize = 0;
1N/A addr = p;
1N/A parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args,
1N/A mail_esmtp_args);
1N/A if (Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A
1N/A#if SASL
1N/A# if _FFR_AUTH_PASSING
1N/A /* set the default AUTH= if the sender didn't */
1N/A if (e->e_auth_param == NULL)
1N/A {
1N/A /* XXX only do this for an MSA? */
1N/A e->e_auth_param = macvalue(macid("{auth_authen}"),
1N/A e);
1N/A if (e->e_auth_param == NULL)
1N/A e->e_auth_param = "<>";
1N/A
1N/A /*
1N/A ** XXX should we invoke Strust_auth now?
1N/A ** authorizing as the client that just
1N/A ** authenticated, so we'll trust implicitly
1N/A */
1N/A }
1N/A# endif /* _FFR_AUTH_PASSING */
1N/A#endif /* SASL */
1N/A
1N/A /* do config file checking of the sender */
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{addr_type}"), "e s");
1N/A#if _FFR_MAIL_MACRO
1N/A /* make the "real" sender address available */
1N/A macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
1N/A e->e_from.q_paddr);
1N/A#endif /* _FFR_MAIL_MACRO */
1N/A if (rscheck("check_mail", addr,
1N/A NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
1N/A NULL, e->e_id, NULL) != EX_OK ||
1N/A Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{addr_type}"), NULL);
1N/A
1N/A if (MaxMessageSize > 0 &&
1N/A (e->e_msgsize > MaxMessageSize ||
1N/A e->e_msgsize < 0))
1N/A {
1N/A usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
1N/A MaxMessageSize);
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A }
1N/A
1N/A /*
1N/A ** XXX always check whether there is at least one fs
1N/A ** with enough space?
1N/A ** However, this may not help much: the queue group
1N/A ** selection may later on select a FS that hasn't
1N/A ** enough space.
1N/A */
1N/A
1N/A if ((NumFileSys == 1 || NumQueue == 1) &&
1N/A !enoughdiskspace(e->e_msgsize, e)
1N/A#if _FFR_ANY_FREE_FS
1N/A && !filesys_free(e->e_msgsize)
1N/A#endif /* _FFR_ANY_FREE_FS */
1N/A )
1N/A {
1N/A /*
1N/A ** We perform this test again when the
1N/A ** queue directory is selected, in collect.
1N/A */
1N/A
1N/A usrerr("452 4.4.5 Insufficient disk space; try again later");
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A }
1N/A if (Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A
1N/A LogUsrErrs = true;
1N/A#if MILTER
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A response = milter_envfrom(args, e, &state);
1N/A MILTER_REPLY("from");
1N/A }
1N/A#endif /* MILTER */
1N/A if (Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A
1N/A message("250 2.1.0 Sender ok");
1N/A smtp.sm_gotmail = true;
1N/A }
1N/A SM_EXCEPT(exc, "[!F]*")
1N/A {
1N/A /*
1N/A ** An error occurred while processing a MAIL command.
1N/A ** Jump to the common error handling code.
1N/A */
1N/A
1N/A sm_exc_free(exc);
1N/A goto undo_no_pm;
1N/A }
1N/A SM_END_TRY
1N/A break;
1N/A
1N/A undo_no_pm:
1N/A e->e_flags &= ~EF_PM_NOTIFY;
1N/A undo:
1N/A break;
1N/A
1N/A case CMDRCPT: /* rcpt -- designate recipient */
1N/A DELAY_CONN("RCPT");
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"), NULL);
1N/A#if MILTER
1N/A (void) memset(&addr_st, '\0', sizeof(addr_st));
1N/A a = NULL;
1N/A milter_rcpt_added = false;
1N/A smtp.sm_e_nrcpts_orig = e->e_nrcpts;
1N/A#endif
1N/A#if _FFR_BADRCPT_SHUTDOWN
1N/A /*
1N/A ** hack to deal with hack, see below:
1N/A ** n_badrcpts is increased if limit is reached.
1N/A */
1N/A
1N/A n_badrcpts_adj = (BadRcptThrottle > 0 &&
1N/A n_badrcpts > BadRcptThrottle &&
1N/A LogLevel > 5)
1N/A ? n_badrcpts - 1 : n_badrcpts;
1N/A if (BadRcptShutdown > 0 &&
1N/A n_badrcpts_adj >= BadRcptShutdown &&
1N/A (BadRcptShutdownGood == 0 ||
1N/A smtp.sm_nrcpts == 0 ||
1N/A (n_badrcpts_adj * 100 /
1N/A (smtp.sm_nrcpts + n_badrcpts) >=
1N/A BadRcptShutdownGood)))
1N/A {
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: Possible SMTP RCPT flood, shutting down connection.",
1N/A CurSmtpClient);
1N/A message("421 4.7.0 %s Too many bad recipients; closing connection",
1N/A MyHostName);
1N/A
1N/A /* arrange to ignore any current send list */
1N/A e->e_sendqueue = NULL;
1N/A goto doquit;
1N/A }
1N/A#endif /* _FFR_BADRCPT_SHUTDOWN */
1N/A if (BadRcptThrottle > 0 &&
1N/A n_badrcpts >= BadRcptThrottle)
1N/A {
1N/A if (LogLevel > 5 &&
1N/A n_badrcpts == BadRcptThrottle)
1N/A {
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: Possible SMTP RCPT flood, throttling.",
1N/A CurSmtpClient);
1N/A
1N/A /* To avoid duplicated message */
1N/A n_badrcpts++;
1N/A }
1N/A NBADRCPTS;
1N/A
1N/A /*
1N/A ** Don't use exponential backoff for now.
1N/A ** Some systems will open more connections
1N/A ** and actually overload the receiver even
1N/A ** more.
1N/A */
1N/A
1N/A (void) sleep(BadRcptThrottleDelay);
1N/A }
1N/A if (!smtp.sm_gotmail)
1N/A {
1N/A usrerr("503 5.0.0 Need MAIL before RCPT");
1N/A break;
1N/A }
1N/A SmtpPhase = "server RCPT";
1N/A SM_TRY
1N/A {
1N/A QuickAbort = true;
1N/A LogUsrErrs = true;
1N/A
1N/A /* limit flooding of our machine */
1N/A if (MaxRcptPerMsg > 0 &&
1N/A smtp.sm_nrcpts >= MaxRcptPerMsg)
1N/A {
1N/A /* sleep(1); / * slow down? */
1N/A usrerr("452 4.5.3 Too many recipients");
1N/A goto rcpt_done;
1N/A }
1N/A
1N/A if (!SM_IS_INTERACTIVE(e->e_sendmode)
1N/A#if _FFR_DM_ONE
1N/A && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
1N/A#endif /* _FFR_DM_ONE */
1N/A )
1N/A e->e_flags |= EF_VRFYONLY;
1N/A
1N/A#if MILTER
1N/A /*
1N/A ** Do not expand recipients at RCPT time (in the call
1N/A ** to recipient()) if a milter can delete or reject
1N/A ** a RCPT. If they are expanded, it is impossible
1N/A ** for removefromlist() to figure out the expanded
1N/A ** members of the original recipient and mark them
1N/A ** as QS_DONTSEND.
1N/A */
1N/A
1N/A if (!(smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags)) &&
1N/A (smtp.sm_milters.mis_flags &
1N/A (MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0)
1N/A e->e_flags |= EF_VRFYONLY;
1N/A milter_cmd_done = false;
1N/A milter_cmd_safe = false;
1N/A#endif /* MILTER */
1N/A
1N/A p = skipword(p, "to");
1N/A if (p == NULL)
1N/A goto rcpt_done;
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{addr_type}"), "e r");
1N/A a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
1N/A e, true);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{addr_type}"), NULL);
1N/A if (Errors > 0)
1N/A goto rcpt_done;
1N/A if (a == NULL)
1N/A {
1N/A usrerr("501 5.0.0 Missing recipient");
1N/A goto rcpt_done;
1N/A }
1N/A
1N/A if (delimptr != NULL && *delimptr != '\0')
1N/A *delimptr++ = '\0';
1N/A
1N/A /* put resulting triple from parseaddr() into macros */
1N/A if (a->q_mailer != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"),
1N/A a->q_mailer->m_name);
1N/A else
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"), NULL);
1N/A if (a->q_host != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"), a->q_host);
1N/A else
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"), "localhost");
1N/A if (a->q_user != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"), a->q_user);
1N/A else
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"), NULL);
1N/A if (Errors > 0)
1N/A goto rcpt_done;
1N/A
1N/A /* now parse ESMTP arguments */
1N/A addr = p;
1N/A parse_esmtp_args(e, a, p, delimptr, "RCPT", args,
1N/A rcpt_esmtp_args);
1N/A if (Errors > 0)
1N/A goto rcpt_done;
1N/A
1N/A#if MILTER
1N/A /*
1N/A ** rscheck() can trigger an "exception"
1N/A ** in which case the execution continues at
1N/A ** SM_EXCEPT(exc, "[!F]*")
1N/A ** This means milter_cmd_safe is not set
1N/A ** and hence milter is not invoked.
1N/A ** Would it be "safe" to change that, i.e., use
1N/A ** milter_cmd_safe = true;
1N/A ** here so a milter is informed (if requested)
1N/A ** about RCPTs that are rejected by check_rcpt?
1N/A */
1N/A# if _FFR_MILTER_CHECK_REJECTIONS_TOO
1N/A milter_cmd_safe = true;
1N/A# endif
1N/A#endif
1N/A
1N/A /* do config file checking of the recipient */
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{addr_type}"), "e r");
1N/A if (rscheck("check_rcpt", addr,
1N/A NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
1N/A NULL, e->e_id, p_addr_st) != EX_OK ||
1N/A Errors > 0)
1N/A goto rcpt_done;
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{addr_type}"), NULL);
1N/A
1N/A /* If discarding, don't bother to verify user */
1N/A if (bitset(EF_DISCARD, e->e_flags))
1N/A a->q_state = QS_VERIFIED;
1N/A#if MILTER
1N/A milter_cmd_safe = true;
1N/A#endif
1N/A
1N/A /* save in recipient list after ESMTP mods */
1N/A a = recipient(a, &e->e_sendqueue, 0, e);
1N/A /* may trigger exception... */
1N/A
1N/A#if MILTER
1N/A milter_rcpt_added = true;
1N/A#endif
1N/A
1N/A if(!(Errors > 0) && QS_IS_BADADDR(a->q_state))
1N/A {
1N/A /* punt -- should keep message in ADDRESS.... */
1N/A usrerr("550 5.1.1 Addressee unknown");
1N/A }
1N/A
1N/A#if MILTER
1N/A rcpt_done:
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A /* how to get the error codes? */
1N/A if (Errors > 0)
1N/A {
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"),
1N/A "error");
1N/A if (a != NULL &&
1N/A a->q_status != NULL &&
1N/A a->q_rstatus != NULL)
1N/A {
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"),
1N/A a->q_status);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"),
1N/A a->q_rstatus);
1N/A }
1N/A else
1N/A {
1N/A if (addr_st.q_host != NULL)
1N/A macdefine(&e->e_macro,
1N/A A_PERM,
1N/A macid("{rcpt_host}"),
1N/A addr_st.q_host);
1N/A if (addr_st.q_user != NULL)
1N/A macdefine(&e->e_macro,
1N/A A_PERM,
1N/A macid("{rcpt_addr}"),
1N/A addr_st.q_user);
1N/A }
1N/A }
1N/A
1N/A response = milter_envrcpt(args, e, &state,
1N/A Errors > 0);
1N/A milter_cmd_done = true;
1N/A MILTER_REPLY("to");
1N/A }
1N/A#endif /* MILTER */
1N/A
1N/A /* no errors during parsing, but might be a duplicate */
1N/A e->e_to = a->q_paddr;
1N/A if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state))
1N/A {
1N/A if (smtp.sm_nrcpts == 0)
1N/A initsys(e);
1N/A message("250 2.1.5 Recipient ok%s",
1N/A QS_IS_QUEUEUP(a->q_state) ?
1N/A " (will queue)" : "");
1N/A smtp.sm_nrcpts++;
1N/A }
1N/A
1N/A /* Is this needed? */
1N/A#if !MILTER
1N/A rcpt_done:
1N/A#endif /* !MILTER */
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{dsn_notify}"), NULL);
1N/A
1N/A if (Errors > 0)
1N/A {
1N/A ++n_badrcpts;
1N/A NBADRCPTS;
1N/A }
1N/A }
1N/A SM_EXCEPT(exc, "[!F]*")
1N/A {
1N/A /* An exception occurred while processing RCPT */
1N/A e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
1N/A ++n_badrcpts;
1N/A NBADRCPTS;
1N/A#if MILTER
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags) &&
1N/A !milter_cmd_done && milter_cmd_safe)
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"), "error");
1N/A
1N/A /* how to get the error codes? */
1N/A if (addr_st.q_host != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"),
1N/A addr_st.q_host);
1N/A else if (a != NULL && a->q_status != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"),
1N/A a->q_status);
1N/A
1N/A if (addr_st.q_user != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"),
1N/A addr_st.q_user);
1N/A else if (a != NULL && a->q_rstatus != NULL)
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"),
1N/A a->q_rstatus);
1N/A
1N/A response = milter_envrcpt(args, e, &state,
1N/A true);
1N/A milter_cmd_done = true;
1N/A MILTER_REPLY("to");
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_mailer}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_host}"), NULL);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{rcpt_addr}"), NULL);
1N/A }
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A milter_rcpt_added && milter_cmd_done &&
1N/A milter_cmd_fail)
1N/A {
1N/A (void) removefromlist(addr, &e->e_sendqueue, e);
1N/A milter_cmd_fail = false;
1N/A if (smtp.sm_e_nrcpts_orig < e->e_nrcpts)
1N/A e->e_nrcpts = smtp.sm_e_nrcpts_orig;
1N/A }
1N/A#endif /* MILTER */
1N/A }
1N/A SM_END_TRY
1N/A break;
1N/A
1N/A case CMDDATA: /* data -- text of mail */
1N/A DELAY_CONN("DATA");
1N/A if (!smtp_data(&smtp, e))
1N/A goto doquit;
1N/A break;
1N/A
1N/A case CMDRSET: /* rset -- reset state */
1N/A if (tTd(94, 100))
1N/A message("451 4.0.0 Test failure");
1N/A else
1N/A message("250 2.0.0 Reset state");
1N/A CLEAR_STATE(cmdbuf);
1N/A break;
1N/A
1N/A case CMDVRFY: /* vrfy -- verify address */
1N/A case CMDEXPN: /* expn -- expand address */
1N/A vrfy = c->cmd_code == CMDVRFY;
1N/A DELAY_CONN(vrfy ? "VRFY" : "EXPN");
1N/A if (tempfail)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
1N/A vrfy ? "VRFY" : "EXPN",
1N/A p, CurSmtpClient);
1N/A
1N/A /* RFC 821 doesn't allow 4xy reply code */
1N/A usrerr("550 5.7.1 Please try again later");
1N/A break;
1N/A }
1N/A wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
1N/A false, vrfy ? "VRFY" : "EXPN", e);
1N/A STOP_IF_ATTACK(wt);
1N/A previous = curtime();
1N/A if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
1N/A (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
1N/A {
1N/A if (vrfy)
1N/A message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
1N/A else
1N/A message("502 5.7.0 Sorry, we do not allow this operation");
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: %s [rejected]",
1N/A CurSmtpClient,
1N/A shortenstring(inp, MAXSHORTSTR));
1N/A break;
1N/A }
1N/A else if (!gothello &&
1N/A bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
1N/A PrivacyFlags))
1N/A {
1N/A usrerr("503 5.0.0 I demand that you introduce yourself first");
1N/A break;
1N/A }
1N/A if (Errors > 0)
1N/A break;
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_INFO, e->e_id, "%s: %s",
1N/A CurSmtpClient,
1N/A shortenstring(inp, MAXSHORTSTR));
1N/A SM_TRY
1N/A {
1N/A QuickAbort = true;
1N/A vrfyqueue = NULL;
1N/A if (vrfy)
1N/A e->e_flags |= EF_VRFYONLY;
1N/A while (*p != '\0' && isascii(*p) && isspace(*p))
1N/A p++;
1N/A if (*p == '\0')
1N/A {
1N/A usrerr("501 5.5.2 Argument required");
1N/A }
1N/A else
1N/A {
1N/A /* do config file checking of the address */
1N/A if (rscheck(vrfy ? "check_vrfy" : "check_expn",
1N/A p, NULL, e, RSF_RMCOMM,
1N/A 3, NULL, NOQID, NULL) != EX_OK ||
1N/A Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
1N/A }
1N/A if (wt > 0)
1N/A {
1N/A time_t t;
1N/A
1N/A t = wt - (curtime() - previous);
1N/A if (t > 0)
1N/A (void) sleep(t);
1N/A }
1N/A if (Errors > 0)
1N/A sm_exc_raisenew_x(&EtypeQuickAbort, 1);
1N/A if (vrfyqueue == NULL)
1N/A {
1N/A usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
1N/A }
1N/A while (vrfyqueue != NULL)
1N/A {
1N/A if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
1N/A {
1N/A vrfyqueue = vrfyqueue->q_next;
1N/A continue;
1N/A }
1N/A
1N/A /* see if there is more in the vrfy list */
1N/A a = vrfyqueue;
1N/A while ((a = a->q_next) != NULL &&
1N/A (!QS_IS_UNDELIVERED(a->q_state)))
1N/A continue;
1N/A printvrfyaddr(vrfyqueue, a == NULL, vrfy);
1N/A vrfyqueue = a;
1N/A }
1N/A }
1N/A SM_EXCEPT(exc, "[!F]*")
1N/A {
1N/A /*
1N/A ** An exception occurred while processing VRFY/EXPN
1N/A */
1N/A
1N/A sm_exc_free(exc);
1N/A goto undo;
1N/A }
1N/A SM_END_TRY
1N/A break;
1N/A
1N/A case CMDETRN: /* etrn -- force queue flush */
1N/A DELAY_CONN("ETRN");
1N/A
1N/A /* Don't leak queue information via debug flags */
1N/A if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
1N/A (RealUid != 0 && RealUid != TrustedUid &&
1N/A OpMode == MD_SMTP))
1N/A {
1N/A /* different message for MSA ? */
1N/A message("502 5.7.0 Sorry, we do not allow this operation");
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: %s [rejected]",
1N/A CurSmtpClient,
1N/A shortenstring(inp, MAXSHORTSTR));
1N/A break;
1N/A }
1N/A if (tempfail)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
1N/A p, CurSmtpClient);
1N/A usrerr(MSG_TEMPFAIL);
1N/A break;
1N/A }
1N/A
1N/A if (strlen(p) <= 0)
1N/A {
1N/A usrerr("500 5.5.2 Parameter required");
1N/A break;
1N/A }
1N/A
1N/A /* crude way to avoid denial-of-service attacks */
1N/A STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
1N/A true, "ETRN", e));
1N/A
1N/A /*
1N/A ** Do config file checking of the parameter.
1N/A ** Even though we have srv_features now, we still
1N/A ** need this ruleset because the former is called
1N/A ** when the connection has been established, while
1N/A ** this ruleset is called when the command is
1N/A ** actually issued and therefore has all information
1N/A ** available to make a decision.
1N/A */
1N/A
1N/A if (rscheck("check_etrn", p, NULL, e,
1N/A RSF_RMCOMM, 3, NULL, NOQID, NULL)
1N/A != EX_OK ||
1N/A Errors > 0)
1N/A break;
1N/A
1N/A if (LogLevel > 5)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: ETRN %s", CurSmtpClient,
1N/A shortenstring(p, MAXSHORTSTR));
1N/A
1N/A id = p;
1N/A if (*id == '#')
1N/A {
1N/A int i, qgrp;
1N/A
1N/A id++;
1N/A qgrp = name2qid(id);
1N/A if (!ISVALIDQGRP(qgrp))
1N/A {
1N/A usrerr("459 4.5.4 Queue %s unknown",
1N/A id);
1N/A break;
1N/A }
1N/A for (i = 0; i < NumQueue && Queue[i] != NULL;
1N/A i++)
1N/A Queue[i]->qg_nextrun = (time_t) -1;
1N/A Queue[qgrp]->qg_nextrun = 0;
1N/A ok = run_work_group(Queue[qgrp]->qg_wgrp,
1N/A RWG_FORK|RWG_FORCE);
1N/A if (ok && Errors == 0)
1N/A message("250 2.0.0 Queuing for queue group %s started", id);
1N/A break;
1N/A }
1N/A
1N/A if (*id == '@')
1N/A id++;
1N/A else
1N/A *--id = '@';
1N/A
1N/A new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
1N/A if (new == NULL)
1N/A {
1N/A syserr("500 5.5.0 ETRN out of memory");
1N/A break;
1N/A }
1N/A new->queue_match = id;
1N/A new->queue_negate = false;
1N/A new->queue_next = NULL;
1N/A QueueLimitRecipient = new;
1N/A ok = runqueue(true, false, false, true);
1N/A sm_free(QueueLimitRecipient); /* XXX */
1N/A QueueLimitRecipient = NULL;
1N/A if (ok && Errors == 0)
1N/A message("250 2.0.0 Queuing for node %s started", p);
1N/A break;
1N/A
1N/A case CMDHELP: /* help -- give user info */
1N/A DELAY_CONN("HELP");
1N/A help(p, e);
1N/A break;
1N/A
1N/A case CMDNOOP: /* noop -- do nothing */
1N/A DELAY_CONN("NOOP");
1N/A STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
1N/A true, "NOOP", e));
1N/A message("250 2.0.0 OK");
1N/A break;
1N/A
1N/A case CMDQUIT: /* quit -- leave mail */
1N/A message("221 2.0.0 %s closing connection", MyHostName);
1N/A#if PIPELINING
1N/A (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
1N/A#endif /* PIPELINING */
1N/A
1N/A if (smtp.sm_nrcpts > 0)
1N/A logundelrcpts(e, "aborted by sender", 9, false);
1N/A
1N/A /* arrange to ignore any current send list */
1N/A e->e_sendqueue = NULL;
1N/A
1N/A#if STARTTLS
1N/A /* shutdown TLS connection */
1N/A if (tls_active)
1N/A {
1N/A (void) endtls(srv_ssl, "server");
1N/A tls_active = false;
1N/A }
1N/A#endif /* STARTTLS */
1N/A#if SASL
1N/A if (authenticating == SASL_IS_AUTH)
1N/A {
1N/A sasl_dispose(&conn);
1N/A authenticating = SASL_NOT_AUTH;
1N/A /* XXX sasl_done(); this is a child */
1N/A }
1N/A#endif /* SASL */
1N/A
1N/Adoquit:
1N/A /* avoid future 050 messages */
1N/A disconnect(1, e);
1N/A
1N/A#if MILTER
1N/A /* close out milter filters */
1N/A milter_quit(e);
1N/A#endif /* MILTER */
1N/A
1N/A if (tTd(92, 2))
1N/A sm_dprintf("QUIT: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
1N/A e->e_id,
1N/A bitset(EF_LOGSENDER, e->e_flags),
1N/A LogLevel);
1N/A if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
1N/A logsender(e, NULL);
1N/A e->e_flags &= ~EF_LOGSENDER;
1N/A
1N/A if (lognullconnection && LogLevel > 5 &&
1N/A nullserver == NULL)
1N/A {
1N/A char *d;
1N/A
1N/A d = macvalue(macid("{daemon_name}"), e);
1N/A if (d == NULL)
1N/A d = "stdin";
1N/A
1N/A /*
1N/A ** even though this id is "bogus", it makes
1N/A ** it simpler to "grep" related events, e.g.,
1N/A ** timeouts for the same connection.
1N/A */
1N/A
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
1N/A CurSmtpClient, d);
1N/A }
1N/A if (tTd(93, 100))
1N/A {
1N/A /* return to handle next connection */
1N/A return;
1N/A }
1N/A finis(true, true, ExitStat);
1N/A /* NOTREACHED */
1N/A
1N/A /* just to avoid bogus warning from some compilers */
1N/A exit(EX_OSERR);
1N/A
1N/A case CMDVERB: /* set verbose mode */
1N/A DELAY_CONN("VERB");
1N/A if (!bitset(SRV_OFFER_EXPN, features) ||
1N/A !bitset(SRV_OFFER_VERB, features))
1N/A {
1N/A /* this would give out the same info */
1N/A message("502 5.7.0 Verbose unavailable");
1N/A break;
1N/A }
1N/A STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
1N/A true, "VERB", e));
1N/A Verbose = 1;
1N/A set_delivery_mode(SM_DELIVER, e);
1N/A message("250 2.0.0 Verbose mode");
1N/A break;
1N/A
1N/A#if SMTPDEBUG
1N/A case CMDDBGQSHOW: /* show queues */
1N/A (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1N/A "Send Queue=");
1N/A printaddr(smioout, e->e_sendqueue, true);
1N/A break;
1N/A
1N/A case CMDDBGDEBUG: /* set debug mode */
1N/A tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
1N/A tTflag(p);
1N/A message("200 2.0.0 Debug set");
1N/A break;
1N/A
1N/A#else /* SMTPDEBUG */
1N/A case CMDDBGQSHOW: /* show queues */
1N/A case CMDDBGDEBUG: /* set debug mode */
1N/A#endif /* SMTPDEBUG */
1N/A case CMDLOGBOGUS: /* bogus command */
1N/A DELAY_CONN("Bogus");
1N/A if (LogLevel > 0)
1N/A sm_syslog(LOG_CRIT, e->e_id,
1N/A "\"%s\" command from %s (%.100s)",
1N/A c->cmd_name, CurSmtpClient,
1N/A anynet_ntoa(&RealHostAddr));
1N/A /* FALLTHROUGH */
1N/A
1N/A case CMDERROR: /* unknown command */
1N/A#if MAXBADCOMMANDS > 0
1N/A if (++n_badcmds > MAXBADCOMMANDS)
1N/A {
1N/A stopattack:
1N/A message("421 4.7.0 %s Too many bad commands; closing connection",
1N/A MyHostName);
1N/A
1N/A /* arrange to ignore any current send list */
1N/A e->e_sendqueue = NULL;
1N/A goto doquit;
1N/A }
1N/A#endif /* MAXBADCOMMANDS > 0 */
1N/A
1N/A#if MILTER && SMFI_VERSION > 2
1N/A if (smtp.sm_milterlist && smtp.sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A if (MilterLogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Sending \"%s\" to Milter", inp);
1N/A response = milter_unknown(inp, e, &state);
1N/A MILTER_REPLY("unknown");
1N/A if (state == SMFIR_REPLYCODE ||
1N/A state == SMFIR_REJECT ||
1N/A state == SMFIR_TEMPFAIL ||
1N/A state == SMFIR_SHUTDOWN)
1N/A {
1N/A /* MILTER_REPLY already gave an error */
1N/A break;
1N/A }
1N/A }
1N/A#endif /* MILTER && SMFI_VERSION > 2 */
1N/A
1N/A usrerr("500 5.5.1 Command unrecognized: \"%s\"",
1N/A shortenstring(inp, MAXSHORTSTR));
1N/A break;
1N/A
1N/A case CMDUNIMPL:
1N/A DELAY_CONN("Unimpl");
1N/A usrerr("502 5.5.1 Command not implemented: \"%s\"",
1N/A shortenstring(inp, MAXSHORTSTR));
1N/A break;
1N/A
1N/A default:
1N/A DELAY_CONN("default");
1N/A errno = 0;
1N/A syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
1N/A break;
1N/A }
1N/A#if SASL
1N/A }
1N/A#endif /* SASL */
1N/A }
1N/A SM_EXCEPT(exc, "[!F]*")
1N/A {
1N/A /*
1N/A ** The only possible exception is "E:mta.quickabort".
1N/A ** There is nothing to do except fall through and loop.
1N/A */
1N/A }
1N/A SM_END_TRY
1N/A }
1N/A}
1N/A/*
1N/A** SMTP_DATA -- implement the SMTP DATA command.
1N/A**
1N/A** Parameters:
1N/A** smtp -- status of SMTP connection.
1N/A** e -- envelope.
1N/A**
1N/A** Returns:
1N/A** true iff SMTP session can continue.
1N/A**
1N/A** Side Effects:
1N/A** possibly sends message.
1N/A*/
1N/A
1N/Astatic bool
1N/Asmtp_data(smtp, e)
1N/A SMTP_T *smtp;
1N/A ENVELOPE *e;
1N/A{
1N/A#if MILTER
1N/A bool milteraccept;
1N/A#endif /* MILTER */
1N/A bool aborting;
1N/A bool doublequeue;
1N/A bool rv = true;
1N/A ADDRESS *a;
1N/A ENVELOPE *ee;
1N/A char *id;
1N/A char *oldid;
1N/A unsigned int features;
1N/A char buf[32];
1N/A
1N/A SmtpPhase = "server DATA";
1N/A if (!smtp->sm_gotmail)
1N/A {
1N/A usrerr("503 5.0.0 Need MAIL command");
1N/A return true;
1N/A }
1N/A else if (smtp->sm_nrcpts <= 0)
1N/A {
1N/A usrerr("503 5.0.0 Need RCPT (recipient)");
1N/A return true;
1N/A }
1N/A (void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
1N/A if (rscheck("check_data", buf, NULL, e,
1N/A RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
1N/A e->e_id, NULL) != EX_OK)
1N/A return true;
1N/A
1N/A#if MILTER && SMFI_VERSION > 3
1N/A if (smtp->sm_milterlist && smtp->sm_milterize &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A int savelogusrerrs = LogUsrErrs;
1N/A
1N/A response = milter_data_cmd(e, &state);
1N/A switch (state)
1N/A {
1N/A case SMFIR_REPLYCODE:
1N/A if (MilterLogLevel > 3)
1N/A {
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: cmd=data, reject=%s",
1N/A response);
1N/A LogUsrErrs = false;
1N/A }
1N/A#if _FFR_MILTER_ENHSC
1N/A if (ISSMTPCODE(response))
1N/A (void) extenhsc(response + 4, ' ', e->e_enhsc);
1N/A#endif /* _FFR_MILTER_ENHSC */
1N/A
1N/A usrerr(response);
1N/A if (strncmp(response, "421 ", 4) == 0
1N/A || strncmp(response, "421-", 4) == 0)
1N/A {
1N/A e->e_sendqueue = NULL;
1N/A return false;
1N/A }
1N/A return true;
1N/A
1N/A case SMFIR_REJECT:
1N/A if (MilterLogLevel > 3)
1N/A {
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: cmd=data, reject=550 5.7.1 Command rejected");
1N/A LogUsrErrs = false;
1N/A }
1N/A#if _FFR_MILTER_ENHSC
1N/A (void) sm_strlcpy(e->e_enhsc, "5.7.1",
1N/A sizeof(e->e_enhsc));
1N/A#endif /* _FFR_MILTER_ENHSC */
1N/A usrerr("550 5.7.1 Command rejected");
1N/A return true;
1N/A
1N/A case SMFIR_DISCARD:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: cmd=data, discard");
1N/A e->e_flags |= EF_DISCARD;
1N/A break;
1N/A
1N/A case SMFIR_TEMPFAIL:
1N/A if (MilterLogLevel > 3)
1N/A {
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: cmd=data, reject=%s",
1N/A MSG_TEMPFAIL);
1N/A LogUsrErrs = false;
1N/A }
1N/A#if _FFR_MILTER_ENHSC
1N/A (void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
1N/A#endif /* _FFR_MILTER_ENHSC */
1N/A usrerr(MSG_TEMPFAIL);
1N/A return true;
1N/A
1N/A case SMFIR_SHUTDOWN:
1N/A if (MilterLogLevel > 3)
1N/A {
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: cmd=data, reject=421 4.7.0 %s closing connection",
1N/A MyHostName);
1N/A LogUsrErrs = false;
1N/A }
1N/A usrerr("421 4.7.0 %s closing connection", MyHostName);
1N/A e->e_sendqueue = NULL;
1N/A return false;
1N/A }
1N/A LogUsrErrs = savelogusrerrs;
1N/A if (response != NULL)
1N/A sm_free(response); /* XXX */
1N/A }
1N/A#endif /* MILTER && SMFI_VERSION > 3 */
1N/A
1N/A /* put back discard bit */
1N/A if (smtp->sm_discard)
1N/A e->e_flags |= EF_DISCARD;
1N/A
1N/A /* check to see if we need to re-expand aliases */
1N/A /* also reset QS_BADADDR on already-diagnosted addrs */
1N/A doublequeue = false;
1N/A for (a = e->e_sendqueue; a != NULL; a = a->q_next)
1N/A {
1N/A if (QS_IS_VERIFIED(a->q_state) &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A /* need to re-expand aliases */
1N/A doublequeue = true;
1N/A }
1N/A if (QS_IS_BADADDR(a->q_state))
1N/A {
1N/A /* make this "go away" */
1N/A a->q_state = QS_DONTSEND;
1N/A }
1N/A }
1N/A
1N/A /* collect the text of the message */
1N/A SmtpPhase = "collect";
1N/A buffer_errors();
1N/A
1N/A collect(InChannel, true, NULL, e, true);
1N/A
1N/A /* redefine message size */
1N/A (void) sm_snprintf(buf, sizeof(buf), "%ld", e->e_msgsize);
1N/A macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
1N/A
1N/A /* rscheck() will set Errors or EF_DISCARD if it trips */
1N/A (void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
1N/A 3, NULL, e->e_id, NULL);
1N/A
1N/A#if MILTER
1N/A milteraccept = true;
1N/A if (smtp->sm_milterlist && smtp->sm_milterize &&
1N/A Errors <= 0 &&
1N/A !bitset(EF_DISCARD, e->e_flags))
1N/A {
1N/A char state;
1N/A char *response;
1N/A
1N/A response = milter_data(e, &state);
1N/A switch (state)
1N/A {
1N/A case SMFIR_REPLYCODE:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: data, reject=%s",
1N/A response);
1N/A milteraccept = false;
1N/A#if _FFR_MILTER_ENHSC
1N/A if (ISSMTPCODE(response))
1N/A (void) extenhsc(response + 4, ' ', e->e_enhsc);
1N/A#endif /* _FFR_MILTER_ENHSC */
1N/A usrerr(response);
1N/A if (strncmp(response, "421 ", 4) == 0
1N/A || strncmp(response, "421-", 4) == 0)
1N/A rv = false;
1N/A break;
1N/A
1N/A case SMFIR_REJECT:
1N/A milteraccept = false;
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: data, reject=554 5.7.1 Command rejected");
1N/A usrerr("554 5.7.1 Command rejected");
1N/A break;
1N/A
1N/A case SMFIR_DISCARD:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: data, discard");
1N/A milteraccept = false;
1N/A e->e_flags |= EF_DISCARD;
1N/A break;
1N/A
1N/A case SMFIR_TEMPFAIL:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: data, reject=%s",
1N/A MSG_TEMPFAIL);
1N/A milteraccept = false;
1N/A#if _FFR_MILTER_ENHSC
1N/A (void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
1N/A#endif /* _FFR_MILTER_ENHSC */
1N/A usrerr(MSG_TEMPFAIL);
1N/A break;
1N/A
1N/A case SMFIR_SHUTDOWN:
1N/A if (MilterLogLevel > 3)
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "Milter: data, reject=421 4.7.0 %s closing connection",
1N/A MyHostName);
1N/A milteraccept = false;
1N/A usrerr("421 4.7.0 %s closing connection", MyHostName);
1N/A rv = false;
1N/A break;
1N/A }
1N/A if (response != NULL)
1N/A sm_free(response);
1N/A }
1N/A
1N/A /* Milter may have changed message size */
1N/A (void) sm_snprintf(buf, sizeof(buf), "%ld", e->e_msgsize);
1N/A macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
1N/A
1N/A /* abort message filters that didn't get the body & log msg is OK */
1N/A if (smtp->sm_milterlist && smtp->sm_milterize)
1N/A {
1N/A milter_abort(e);
1N/A if (milteraccept && MilterLogLevel > 9)
1N/A sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
1N/A }
1N/A
1N/A /*
1N/A ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
1N/A ** milter accepted message, sync it now
1N/A **
1N/A ** XXX This is almost a copy of the code in collect(): put it into
1N/A ** a function that is called from both places?
1N/A */
1N/A
1N/A if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
1N/A {
1N/A int afd;
1N/A SM_FILE_T *volatile df;
1N/A char *dfname;
1N/A
1N/A df = e->e_dfp;
1N/A dfname = queuename(e, DATAFL_LETTER);
1N/A if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
1N/A && errno != EINVAL)
1N/A {
1N/A int save_errno;
1N/A
1N/A save_errno = errno;
1N/A if (save_errno == EEXIST)
1N/A {
1N/A struct stat st;
1N/A int dfd;
1N/A
1N/A if (stat(dfname, &st) < 0)
1N/A st.st_size = -1;
1N/A errno = EEXIST;
1N/A syserr("@collect: bfcommit(%s): already on disk, size=%ld",
1N/A dfname, (long) st.st_size);
1N/A dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
1N/A if (dfd >= 0)
1N/A dumpfd(dfd, true, true);
1N/A }
1N/A errno = save_errno;
1N/A dferror(df, "bfcommit", e);
1N/A flush_errors(true);
1N/A finis(save_errno != EEXIST, true, ExitStat);
1N/A }
1N/A else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
1N/A {
1N/A dferror(df, "sm_io_getinfo", e);
1N/A flush_errors(true);
1N/A finis(true, true, ExitStat);
1N/A /* NOTREACHED */
1N/A }
1N/A else if (fsync(afd) < 0)
1N/A {
1N/A dferror(df, "fsync", e);
1N/A flush_errors(true);
1N/A finis(true, true, ExitStat);
1N/A /* NOTREACHED */
1N/A }
1N/A else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
1N/A {
1N/A dferror(df, "sm_io_close", e);
1N/A flush_errors(true);
1N/A finis(true, true, ExitStat);
1N/A /* NOTREACHED */
1N/A }
1N/A
1N/A /* Now reopen the df file */
1N/A e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
1N/A SM_IO_RDONLY, NULL);
1N/A if (e->e_dfp == NULL)
1N/A {
1N/A /* we haven't acked receipt yet, so just chuck this */
1N/A syserr("@Cannot reopen %s", dfname);
1N/A finis(true, true, ExitStat);
1N/A /* NOTREACHED */
1N/A }
1N/A }
1N/A#endif /* MILTER */
1N/A
1N/A /* Check if quarantining stats should be updated */
1N/A if (e->e_quarmsg != NULL)
1N/A markstats(e, NULL, STATS_QUARANTINE);
1N/A
1N/A /*
1N/A ** If a header/body check (header checks or milter)
1N/A ** set EF_DISCARD, don't queueup the message --
1N/A ** that would lose the EF_DISCARD bit and deliver
1N/A ** the message.
1N/A */
1N/A
1N/A if (bitset(EF_DISCARD, e->e_flags))
1N/A doublequeue = false;
1N/A
1N/A aborting = Errors > 0;
1N/A if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
1N/A (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
1N/A !split_by_recipient(e))
1N/A aborting = bitset(EF_FATALERRS, e->e_flags);
1N/A
1N/A if (aborting)
1N/A {
1N/A ADDRESS *q;
1N/A
1N/A /* Log who the mail would have gone to */
1N/A logundelrcpts(e, e->e_message, 8, false);
1N/A
1N/A /*
1N/A ** If something above refused the message, we still haven't
1N/A ** accepted responsibility for it. Don't send DSNs.
1N/A */
1N/A
1N/A for (q = e->e_sendqueue; q != NULL; q = q->q_next)
1N/A q->q_flags &= ~Q_PINGFLAGS;
1N/A
1N/A flush_errors(true);
1N/A buffer_errors();
1N/A goto abortmessage;
1N/A }
1N/A
1N/A /* from now on, we have to operate silently */
1N/A buffer_errors();
1N/A
1N/A#if 0
1N/A /*
1N/A ** Clear message, it may contain an error from the SMTP dialogue.
1N/A ** This error must not show up in the queue.
1N/A ** Some error message should show up, e.g., alias database
1N/A ** not available, but others shouldn't, e.g., from check_rcpt.
1N/A */
1N/A
1N/A e->e_message = NULL;
1N/A#endif /* 0 */
1N/A
1N/A /*
1N/A ** Arrange to send to everyone.
1N/A ** If sending to multiple people, mail back
1N/A ** errors rather than reporting directly.
1N/A ** In any case, don't mail back errors for
1N/A ** anything that has happened up to
1N/A ** now (the other end will do this).
1N/A ** Truncate our transcript -- the mail has gotten
1N/A ** to us successfully, and if we have
1N/A ** to mail this back, it will be easier
1N/A ** on the reader.
1N/A ** Then send to everyone.
1N/A ** Finally give a reply code. If an error has
1N/A ** already been given, don't mail a
1N/A ** message back.
1N/A ** We goose error returns by clearing error bit.
1N/A */
1N/A
1N/A SmtpPhase = "delivery";
1N/A (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
1N/A id = e->e_id;
1N/A
1N/A#if NAMED_BIND
1N/A _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
1N/A _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
1N/A#endif /* NAMED_BIND */
1N/A
1N/A
1N/A for (ee = e; ee != NULL; ee = ee->e_sibling)
1N/A {
1N/A /* make sure we actually do delivery */
1N/A ee->e_flags &= ~EF_CLRQUEUE;
1N/A
1N/A /* from now on, operate silently */
1N/A ee->e_errormode = EM_MAIL;
1N/A
1N/A if (doublequeue)
1N/A {
1N/A /* make sure it is in the queue */
1N/A queueup(ee, false, true);
1N/A }
1N/A else
1N/A {
1N/A int mode;
1N/A
1N/A /* send to all recipients */
1N/A mode = SM_DEFAULT;
1N/A#if _FFR_DM_ONE
1N/A if (SM_DM_ONE == e->e_sendmode)
1N/A {
1N/A if (NotFirstDelivery)
1N/A {
1N/A mode = SM_QUEUE;
1N/A e->e_sendmode = SM_QUEUE;
1N/A }
1N/A else
1N/A {
1N/A mode = SM_FORK;
1N/A NotFirstDelivery = true;
1N/A }
1N/A }
1N/A#endif /* _FFR_DM_ONE */
1N/A sendall(ee, mode);
1N/A }
1N/A ee->e_to = NULL;
1N/A }
1N/A
1N/A /* put back id for SMTP logging in putoutmsg() */
1N/A oldid = CurEnv->e_id;
1N/A CurEnv->e_id = id;
1N/A
1N/A /* issue success message */
1N/A#if _FFR_MSG_ACCEPT
1N/A if (MessageAccept != NULL && *MessageAccept != '\0')
1N/A {
1N/A char msg[MAXLINE];
1N/A
1N/A expand(MessageAccept, msg, sizeof(msg), e);
1N/A message("250 2.0.0 %s", msg);
1N/A }
1N/A else
1N/A#endif /* _FFR_MSG_ACCEPT */
1N/A message("250 2.0.0 %s Message accepted for delivery", id);
1N/A CurEnv->e_id = oldid;
1N/A
1N/A /* if we just queued, poke it */
1N/A if (doublequeue)
1N/A {
1N/A bool anything_to_send = false;
1N/A
1N/A sm_getla();
1N/A for (ee = e; ee != NULL; ee = ee->e_sibling)
1N/A {
1N/A if (WILL_BE_QUEUED(ee->e_sendmode))
1N/A continue;
1N/A if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
1N/A {
1N/A ee->e_sendmode = SM_QUEUE;
1N/A continue;
1N/A }
1N/A else if (QueueMode != QM_QUARANTINE &&
1N/A ee->e_quarmsg != NULL)
1N/A {
1N/A ee->e_sendmode = SM_QUEUE;
1N/A continue;
1N/A }
1N/A anything_to_send = true;
1N/A
1N/A /* close all the queue files */
1N/A closexscript(ee);
1N/A if (ee->e_dfp != NULL)
1N/A {
1N/A (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
1N/A ee->e_dfp = NULL;
1N/A }
1N/A unlockqueue(ee);
1N/A }
1N/A if (anything_to_send)
1N/A {
1N/A#if PIPELINING
1N/A /*
1N/A ** XXX if we don't do this, we get 250 twice
1N/A ** because it is also flushed in the child.
1N/A */
1N/A
1N/A (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
1N/A#endif /* PIPELINING */
1N/A (void) doworklist(e, true, true);
1N/A }
1N/A }
1N/A
1N/A abortmessage:
1N/A if (tTd(92, 2))
1N/A sm_dprintf("abortmessage: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
1N/A e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);
1N/A if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
1N/A logsender(e, NULL);
1N/A e->e_flags &= ~EF_LOGSENDER;
1N/A
1N/A /* clean up a bit */
1N/A smtp->sm_gotmail = false;
1N/A
1N/A /*
1N/A ** Call dropenvelope if and only if the envelope is *not*
1N/A ** being processed by the child process forked by doworklist().
1N/A */
1N/A
1N/A if (aborting || bitset(EF_DISCARD, e->e_flags))
1N/A (void) dropenvelope(e, true, false);
1N/A else
1N/A {
1N/A for (ee = e; ee != NULL; ee = ee->e_sibling)
1N/A {
1N/A if (!doublequeue &&
1N/A QueueMode != QM_QUARANTINE &&
1N/A ee->e_quarmsg != NULL)
1N/A {
1N/A (void) dropenvelope(ee, true, false);
1N/A continue;
1N/A }
1N/A if (WILL_BE_QUEUED(ee->e_sendmode))
1N/A (void) dropenvelope(ee, true, false);
1N/A }
1N/A }
1N/A
1N/A CurEnv = e;
1N/A features = e->e_features;
1N/A sm_rpool_free(e->e_rpool);
1N/A newenvelope(e, e, sm_rpool_new_x(NULL));
1N/A e->e_flags = BlankEnvelope.e_flags;
1N/A e->e_features = features;
1N/A
1N/A /* restore connection quarantining */
1N/A if (smtp->sm_quarmsg == NULL)
1N/A {
1N/A e->e_quarmsg = NULL;
1N/A macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
1N/A }
1N/A else
1N/A {
1N/A e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{quarantine}"), e->e_quarmsg);
1N/A }
1N/A return rv;
1N/A}
1N/A/*
1N/A** LOGUNDELRCPTS -- log undelivered (or all) recipients.
1N/A**
1N/A** Parameters:
1N/A** e -- envelope.
1N/A** msg -- message for Stat=
1N/A** level -- log level.
1N/A** all -- log all recipients.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A**
1N/A** Side Effects:
1N/A** logs undelivered (or all) recipients
1N/A*/
1N/A
1N/Avoid
1N/Alogundelrcpts(e, msg, level, all)
1N/A ENVELOPE *e;
1N/A char *msg;
1N/A int level;
1N/A bool all;
1N/A{
1N/A ADDRESS *a;
1N/A
1N/A if (LogLevel <= level || msg == NULL || *msg == '\0')
1N/A return;
1N/A
1N/A /* Clear $h so relay= doesn't get mislogged by logdelivery() */
1N/A macdefine(&e->e_macro, A_PERM, 'h', NULL);
1N/A
1N/A /* Log who the mail would have gone to */
1N/A for (a = e->e_sendqueue; a != NULL; a = a->q_next)
1N/A {
1N/A if (!QS_IS_UNDELIVERED(a->q_state) && !all)
1N/A continue;
1N/A e->e_to = a->q_paddr;
1N/A logdelivery(NULL, NULL,
1N/A#if _FFR_MILTER_ENHSC
1N/A (a->q_status == NULL && e->e_enhsc[0] != '\0')
1N/A ? e->e_enhsc :
1N/A#endif /* _FFR_MILTER_ENHSC */
1N/A a->q_status,
1N/A msg, NULL, (time_t) 0, e);
1N/A }
1N/A e->e_to = NULL;
1N/A}
1N/A/*
1N/A** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
1N/A**
1N/A** Parameters:
1N/A** pcounter -- pointer to a counter for this command.
1N/A** maxcount -- maximum value for this counter before we
1N/A** slow down.
1N/A** waitnow -- sleep now (in this routine)?
1N/A** cname -- command name for logging.
1N/A** e -- the current envelope.
1N/A**
1N/A** Returns:
1N/A** time to wait,
1N/A** STOP_ATTACK if twice as many commands as allowed and
1N/A** MaxChildren > 0.
1N/A**
1N/A** Side Effects:
1N/A** Slows down if we seem to be under attack.
1N/A*/
1N/A
1N/Astatic time_t
1N/Achecksmtpattack(pcounter, maxcount, waitnow, cname, e)
1N/A volatile unsigned int *pcounter;
1N/A unsigned int maxcount;
1N/A bool waitnow;
1N/A char *cname;
1N/A ENVELOPE *e;
1N/A{
1N/A if (maxcount <= 0) /* no limit */
1N/A return (time_t) 0;
1N/A
1N/A if (++(*pcounter) >= maxcount)
1N/A {
1N/A unsigned int shift;
1N/A time_t s;
1N/A
1N/A if (*pcounter == maxcount && LogLevel > 5)
1N/A {
1N/A sm_syslog(LOG_INFO, e->e_id,
1N/A "%s: possible SMTP attack: command=%.40s, count=%u",
1N/A CurSmtpClient, cname, *pcounter);
1N/A }
1N/A shift = *pcounter - maxcount;
1N/A s = 1 << shift;
1N/A if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
1N/A s = MAXTIMEOUT;
1N/A
1N/A#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \
1N/A ? STOP_ATTACK : (time_t) s)
1N/A
1N/A /* sleep at least 1 second before returning */
1N/A (void) sleep(*pcounter / maxcount);
1N/A s -= *pcounter / maxcount;
1N/A if (s >= MAXTIMEOUT || s < 0)
1N/A s = MAXTIMEOUT;
1N/A if (waitnow && s > 0)
1N/A {
1N/A (void) sleep(s);
1N/A return IS_ATTACK(0);
1N/A }
1N/A return IS_ATTACK(s);
1N/A }
1N/A return (time_t) 0;
1N/A}
1N/A/*
1N/A** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
1N/A**
1N/A** Parameters:
1N/A** none.
1N/A**
1N/A** Returns:
1N/A** nothing.
1N/A**
1N/A** Side Effects:
1N/A** may change I/O fd.
1N/A*/
1N/A
1N/Astatic void
1N/Asetup_smtpd_io()
1N/A{
1N/A int inchfd, outchfd, outfd;
1N/A
1N/A inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1N/A outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
1N/A outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
1N/A if (outchfd != outfd)
1N/A {
1N/A /* arrange for debugging output to go to remote host */
1N/A (void) dup2(outchfd, outfd);
1N/A }
1N/A
1N/A /*
1N/A ** if InChannel and OutChannel are stdin/stdout
1N/A ** and connected to ttys
1N/A ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
1N/A ** then "chain" them together.
1N/A */
1N/A
1N/A if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
1N/A isatty(inchfd) && isatty(outchfd))
1N/A {
1N/A int inmode, outmode;
1N/A
1N/A inmode = fcntl(inchfd, F_GETFL, 0);
1N/A if (inmode == -1)
1N/A {
1N/A if (LogLevel > 11)
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "fcntl(inchfd, F_GETFL) failed: %s",
1N/A sm_errstring(errno));
1N/A return;
1N/A }
1N/A outmode = fcntl(outchfd, F_GETFL, 0);
1N/A if (outmode == -1)
1N/A {
1N/A if (LogLevel > 11)
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "fcntl(outchfd, F_GETFL) failed: %s",
1N/A sm_errstring(errno));
1N/A return;
1N/A }
1N/A if (bitset(O_NONBLOCK, inmode) ||
1N/A bitset(O_NONBLOCK, outmode) ||
1N/A fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
1N/A return;
1N/A outmode = fcntl(outchfd, F_GETFL, 0);
1N/A if (outmode != -1 && bitset(O_NONBLOCK, outmode))
1N/A {
1N/A /* changing InChannel also changes OutChannel */
1N/A sm_io_automode(OutChannel, InChannel);
1N/A if (tTd(97, 4) && LogLevel > 9)
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "set automode for I (%d)/O (%d) in SMTP server",
1N/A inchfd, outchfd);
1N/A }
1N/A
1N/A /* undo change of inchfd */
1N/A (void) fcntl(inchfd, F_SETFL, inmode);
1N/A }
1N/A}
1N/A/*
1N/A** SKIPWORD -- skip a fixed word.
1N/A**
1N/A** Parameters:
1N/A** p -- place to start looking.
1N/A** w -- word to skip.
1N/A**
1N/A** Returns:
1N/A** p following w.
1N/A** NULL on error.
1N/A**
1N/A** Side Effects:
1N/A** clobbers the p data area.
1N/A*/
1N/A
1N/Astatic char *
1N/Askipword(p, w)
1N/A register char *volatile p;
1N/A char *w;
1N/A{
1N/A register char *q;
1N/A char *firstp = p;
1N/A
1N/A /* find beginning of word */
1N/A SKIP_SPACE(p);
1N/A q = p;
1N/A
1N/A /* find end of word */
1N/A while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
1N/A p++;
1N/A while (isascii(*p) && isspace(*p))
1N/A *p++ = '\0';
1N/A if (*p != ':')
1N/A {
1N/A syntax:
1N/A usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
1N/A shortenstring(firstp, MAXSHORTSTR));
1N/A return NULL;
1N/A }
1N/A *p++ = '\0';
1N/A SKIP_SPACE(p);
1N/A
1N/A if (*p == '\0')
1N/A goto syntax;
1N/A
1N/A /* see if the input word matches desired word */
1N/A if (sm_strcasecmp(q, w))
1N/A goto syntax;
1N/A
1N/A return p;
1N/A}
1N/A
1N/A/*
1N/A** RESET_MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
1N/A**
1N/A** Parameters:
1N/A** e -- the envelope.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A*/
1N/A
1N/Avoid
1N/Areset_mail_esmtp_args(e)
1N/A ENVELOPE *e;
1N/A{
1N/A /* "size": no reset */
1N/A
1N/A /* "body" */
1N/A SevenBitInput = SevenBitInput_Saved;
1N/A e->e_bodytype = NULL;
1N/A
1N/A /* "envid" */
1N/A e->e_envid = NULL;
1N/A macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL);
1N/A
1N/A /* "ret" */
1N/A e->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1N/A macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL);
1N/A
1N/A#if SASL
1N/A /* "auth" */
1N/A macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL);
1N/A e->e_auth_param = "";
1N/A# if _FFR_AUTH_PASSING
1N/A macdefine(&BlankEnvelope.e_macro, A_PERM,
1N/A macid("{auth_author}"), NULL);
1N/A# endif /* _FFR_AUTH_PASSING */
1N/A#endif /* SASL */
1N/A
1N/A /* "by" */
1N/A e->e_deliver_by = 0;
1N/A e->e_dlvr_flag = 0;
1N/A}
1N/A
1N/A/*
1N/A** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
1N/A**
1N/A** Parameters:
1N/A** a -- address (unused, for compatibility with rcpt_esmtp_args)
1N/A** kp -- the parameter key.
1N/A** vp -- the value of that parameter.
1N/A** e -- the envelope.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A*/
1N/A
1N/Avoid
1N/Amail_esmtp_args(a, kp, vp, e)
1N/A ADDRESS *a;
1N/A char *kp;
1N/A char *vp;
1N/A ENVELOPE *e;
1N/A{
1N/A if (sm_strcasecmp(kp, "size") == 0)
1N/A {
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 SIZE requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
1N/A errno = 0;
1N/A e->e_msgsize = strtol(vp, (char **) NULL, 10);
1N/A if (e->e_msgsize == LONG_MAX && errno == ERANGE)
1N/A {
1N/A usrerr("552 5.2.3 Message size exceeds maximum value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (e->e_msgsize < 0)
1N/A {
1N/A usrerr("552 5.2.3 Message size invalid");
1N/A /* NOTREACHED */
1N/A }
1N/A }
1N/A else if (sm_strcasecmp(kp, "body") == 0)
1N/A {
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 BODY requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A else if (sm_strcasecmp(vp, "8bitmime") == 0)
1N/A {
1N/A SevenBitInput = false;
1N/A }
1N/A else if (sm_strcasecmp(vp, "7bit") == 0)
1N/A {
1N/A SevenBitInput = true;
1N/A }
1N/A else
1N/A {
1N/A usrerr("501 5.5.4 Unknown BODY type %s", vp);
1N/A /* NOTREACHED */
1N/A }
1N/A e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
1N/A }
1N/A else if (sm_strcasecmp(kp, "envid") == 0)
1N/A {
1N/A if (!bitset(SRV_OFFER_DSN, e->e_features))
1N/A {
1N/A usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
1N/A /* NOTREACHED */
1N/A }
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 ENVID requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (!xtextok(vp))
1N/A {
1N/A usrerr("501 5.5.4 Syntax error in ENVID parameter value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (e->e_envid != NULL)
1N/A {
1N/A usrerr("501 5.5.0 Duplicate ENVID parameter");
1N/A /* NOTREACHED */
1N/A }
1N/A e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
1N/A macdefine(&e->e_macro, A_PERM,
1N/A macid("{dsn_envid}"), e->e_envid);
1N/A }
1N/A else if (sm_strcasecmp(kp, "ret") == 0)
1N/A {
1N/A if (!bitset(SRV_OFFER_DSN, e->e_features))
1N/A {
1N/A usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
1N/A /* NOTREACHED */
1N/A }
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 RET requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (bitset(EF_RET_PARAM, e->e_flags))
1N/A {
1N/A usrerr("501 5.5.0 Duplicate RET parameter");
1N/A /* NOTREACHED */
1N/A }
1N/A e->e_flags |= EF_RET_PARAM;
1N/A if (sm_strcasecmp(vp, "hdrs") == 0)
1N/A e->e_flags |= EF_NO_BODY_RETN;
1N/A else if (sm_strcasecmp(vp, "full") != 0)
1N/A {
1N/A usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
1N/A /* NOTREACHED */
1N/A }
1N/A macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
1N/A }
1N/A#if SASL
1N/A else if (sm_strcasecmp(kp, "auth") == 0)
1N/A {
1N/A int len;
1N/A char *q;
1N/A char *auth_param; /* the value of the AUTH=x */
1N/A bool saveQuickAbort = QuickAbort;
1N/A bool saveSuprErrs = SuprErrs;
1N/A bool saveExitStat = ExitStat;
1N/A
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 AUTH= requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (e->e_auth_param != NULL)
1N/A {
1N/A usrerr("501 5.5.0 Duplicate AUTH parameter");
1N/A /* NOTREACHED */
1N/A }
1N/A if ((q = strchr(vp, ' ')) != NULL)
1N/A len = q - vp + 1;
1N/A else
1N/A len = strlen(vp) + 1;
1N/A auth_param = xalloc(len);
1N/A (void) sm_strlcpy(auth_param, vp, len);
1N/A if (!xtextok(auth_param))
1N/A {
1N/A usrerr("501 5.5.4 Syntax error in AUTH parameter value");
1N/A /* just a warning? */
1N/A /* NOTREACHED */
1N/A }
1N/A
1N/A /* XXX define this always or only if trusted? */
1N/A macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
1N/A auth_param);
1N/A
1N/A /*
1N/A ** call Strust_auth to find out whether
1N/A ** auth_param is acceptable (trusted)
1N/A ** we shouldn't trust it if not authenticated
1N/A ** (required by RFC, leave it to ruleset?)
1N/A */
1N/A
1N/A SuprErrs = true;
1N/A QuickAbort = false;
1N/A if (strcmp(auth_param, "<>") != 0 &&
1N/A (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
1N/A 9, NULL, NOQID, NULL) != EX_OK || Errors > 0))
1N/A {
1N/A if (tTd(95, 8))
1N/A {
1N/A q = e->e_auth_param;
1N/A sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
1N/A auth_param, (q == NULL) ? "" : q);
1N/A }
1N/A
1N/A /* not trusted */
1N/A e->e_auth_param = "<>";
1N/A# if _FFR_AUTH_PASSING
1N/A macdefine(&BlankEnvelope.e_macro, A_PERM,
1N/A macid("{auth_author}"), NULL);
1N/A# endif /* _FFR_AUTH_PASSING */
1N/A }
1N/A else
1N/A {
1N/A if (tTd(95, 8))
1N/A sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
1N/A e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
1N/A auth_param);
1N/A }
1N/A sm_free(auth_param); /* XXX */
1N/A
1N/A /* reset values */
1N/A Errors = 0;
1N/A QuickAbort = saveQuickAbort;
1N/A SuprErrs = saveSuprErrs;
1N/A ExitStat = saveExitStat;
1N/A }
1N/A#endif /* SASL */
1N/A#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?')
1N/A
1N/A /*
1N/A ** "by" is only accepted if DeliverByMin >= 0.
1N/A ** We maybe could add this to the list of server_features.
1N/A */
1N/A
1N/A else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
1N/A {
1N/A char *s;
1N/A
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 BY= requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A errno = 0;
1N/A e->e_deliver_by = strtol(vp, &s, 10);
1N/A if (e->e_deliver_by == LONG_MIN ||
1N/A e->e_deliver_by == LONG_MAX ||
1N/A e->e_deliver_by > 999999999l ||
1N/A e->e_deliver_by < -999999999l)
1N/A {
1N/A usrerr("501 5.5.2 BY=%s out of range", vp);
1N/A /* NOTREACHED */
1N/A }
1N/A if (s == NULL || *s != ';')
1N/A {
1N/A usrerr("501 5.5.2 BY= missing ';'");
1N/A /* NOTREACHED */
1N/A }
1N/A e->e_dlvr_flag = 0;
1N/A ++s; /* XXX: spaces allowed? */
1N/A SKIP_SPACE(s);
1N/A switch (tolower(*s))
1N/A {
1N/A case 'n':
1N/A e->e_dlvr_flag = DLVR_NOTIFY;
1N/A break;
1N/A case 'r':
1N/A e->e_dlvr_flag = DLVR_RETURN;
1N/A if (e->e_deliver_by <= 0)
1N/A {
1N/A usrerr("501 5.5.4 mode R requires BY time > 0");
1N/A /* NOTREACHED */
1N/A }
1N/A if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
1N/A e->e_deliver_by < DeliverByMin)
1N/A {
1N/A usrerr("555 5.5.2 time %ld less than %ld",
1N/A e->e_deliver_by, (long) DeliverByMin);
1N/A /* NOTREACHED */
1N/A }
1N/A break;
1N/A default:
1N/A usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
1N/A /* NOTREACHED */
1N/A }
1N/A ++s; /* XXX: spaces allowed? */
1N/A SKIP_SPACE(s);
1N/A switch (tolower(*s))
1N/A {
1N/A case 't':
1N/A e->e_dlvr_flag |= DLVR_TRACE;
1N/A break;
1N/A case '\0':
1N/A break;
1N/A default:
1N/A usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
1N/A /* NOTREACHED */
1N/A }
1N/A
1N/A /* XXX: check whether more characters follow? */
1N/A }
1N/A else
1N/A {
1N/A usrerr("555 5.5.4 %s parameter unrecognized", kp);
1N/A /* NOTREACHED */
1N/A }
1N/A}
1N/A
1N/A/*
1N/A** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
1N/A**
1N/A** Parameters:
1N/A** a -- the address corresponding to the To: parameter.
1N/A** kp -- the parameter key.
1N/A** vp -- the value of that parameter.
1N/A** e -- the envelope.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A*/
1N/A
1N/Avoid
1N/Arcpt_esmtp_args(a, kp, vp, e)
1N/A ADDRESS *a;
1N/A char *kp;
1N/A char *vp;
1N/A ENVELOPE *e;
1N/A{
1N/A if (sm_strcasecmp(kp, "notify") == 0)
1N/A {
1N/A char *p;
1N/A
1N/A if (!bitset(SRV_OFFER_DSN, e->e_features))
1N/A {
1N/A usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
1N/A /* NOTREACHED */
1N/A }
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 NOTIFY requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1N/A a->q_flags |= QHASNOTIFY;
1N/A macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
1N/A
1N/A if (sm_strcasecmp(vp, "never") == 0)
1N/A return;
1N/A for (p = vp; p != NULL; vp = p)
1N/A {
1N/A char *s;
1N/A
1N/A s = p = strchr(p, ',');
1N/A if (p != NULL)
1N/A *p++ = '\0';
1N/A if (sm_strcasecmp(vp, "success") == 0)
1N/A a->q_flags |= QPINGONSUCCESS;
1N/A else if (sm_strcasecmp(vp, "failure") == 0)
1N/A a->q_flags |= QPINGONFAILURE;
1N/A else if (sm_strcasecmp(vp, "delay") == 0)
1N/A a->q_flags |= QPINGONDELAY;
1N/A else
1N/A {
1N/A usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY",
1N/A vp);
1N/A /* NOTREACHED */
1N/A }
1N/A if (s != NULL)
1N/A *s = ',';
1N/A }
1N/A }
1N/A else if (sm_strcasecmp(kp, "orcpt") == 0)
1N/A {
1N/A if (!bitset(SRV_OFFER_DSN, e->e_features))
1N/A {
1N/A usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
1N/A /* NOTREACHED */
1N/A }
1N/A if (vp == NULL)
1N/A {
1N/A usrerr("501 5.5.2 ORCPT requires a value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (strchr(vp, ';') == NULL || !xtextok(vp))
1N/A {
1N/A usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
1N/A /* NOTREACHED */
1N/A }
1N/A if (a->q_orcpt != NULL)
1N/A {
1N/A usrerr("501 5.5.0 Duplicate ORCPT parameter");
1N/A /* NOTREACHED */
1N/A }
1N/A a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
1N/A }
1N/A else
1N/A {
1N/A usrerr("555 5.5.4 %s parameter unrecognized", kp);
1N/A /* NOTREACHED */
1N/A }
1N/A}
1N/A/*
1N/A** PRINTVRFYADDR -- print an entry in the verify queue
1N/A**
1N/A** Parameters:
1N/A** a -- the address to print.
1N/A** last -- set if this is the last one.
1N/A** vrfy -- set if this is a VRFY command.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A**
1N/A** Side Effects:
1N/A** Prints the appropriate 250 codes.
1N/A*/
1N/A#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */
1N/A
1N/Astatic void
1N/Aprintvrfyaddr(a, last, vrfy)
1N/A register ADDRESS *a;
1N/A bool last;
1N/A bool vrfy;
1N/A{
1N/A char fmtbuf[30];
1N/A
1N/A if (vrfy && a->q_mailer != NULL &&
1N/A !bitnset(M_VRFY250, a->q_mailer->m_flags))
1N/A (void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf));
1N/A else
1N/A (void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf));
1N/A fmtbuf[3] = last ? ' ' : '-';
1N/A (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4);
1N/A if (a->q_fullname == NULL)
1N/A {
1N/A if ((a->q_mailer == NULL ||
1N/A a->q_mailer->m_addrtype == NULL ||
1N/A sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
1N/A strchr(a->q_user, '@') == NULL)
1N/A (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
1N/A sizeof(fmtbuf) - OFFF);
1N/A else
1N/A (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
1N/A sizeof(fmtbuf) - OFFF);
1N/A message(fmtbuf, a->q_user, MyHostName);
1N/A }
1N/A else
1N/A {
1N/A if ((a->q_mailer == NULL ||
1N/A a->q_mailer->m_addrtype == NULL ||
1N/A sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
1N/A strchr(a->q_user, '@') == NULL)
1N/A (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
1N/A sizeof(fmtbuf) - OFFF);
1N/A else
1N/A (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
1N/A sizeof(fmtbuf) - OFFF);
1N/A message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1N/A }
1N/A}
1N/A
1N/A#if SASL
1N/A/*
1N/A** SASLMECHS -- get list of possible AUTH mechanisms
1N/A**
1N/A** Parameters:
1N/A** conn -- SASL connection info.
1N/A** mechlist -- output parameter for list of mechanisms.
1N/A**
1N/A** Returns:
1N/A** number of mechs.
1N/A*/
1N/A
1N/Astatic int
1N/Asaslmechs(conn, mechlist)
1N/A sasl_conn_t *conn;
1N/A char **mechlist;
1N/A{
1N/A int len, num, result;
1N/A
1N/A /* "user" is currently unused */
1N/A# if SASL >= 20000
1N/A result = sasl_listmech(conn, NULL,
1N/A "", " ", "", (const char **) mechlist,
1N/A (unsigned int *)&len, &num);
1N/A# else /* SASL >= 20000 */
1N/A result = sasl_listmech(conn, "user", /* XXX */
1N/A "", " ", "", mechlist,
1N/A (unsigned int *)&len, (unsigned int *)&num);
1N/A# endif /* SASL >= 20000 */
1N/A if (result != SASL_OK)
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "AUTH error: listmech=%d, num=%d",
1N/A result, num);
1N/A num = 0;
1N/A }
1N/A if (num > 0)
1N/A {
1N/A if (LogLevel > 11)
1N/A sm_syslog(LOG_INFO, NOQID,
1N/A "AUTH: available mech=%s, allowed mech=%s",
1N/A *mechlist, AuthMechanisms);
1N/A *mechlist = intersect(AuthMechanisms, *mechlist, NULL);
1N/A }
1N/A else
1N/A {
1N/A *mechlist = NULL; /* be paranoid... */
1N/A if (result == SASL_OK && LogLevel > 9)
1N/A sm_syslog(LOG_WARNING, NOQID,
1N/A "AUTH warning: no mechanisms");
1N/A }
1N/A return num;
1N/A}
1N/A
1N/A# if SASL >= 20000
1N/A/*
1N/A** PROXY_POLICY -- define proxy policy for AUTH
1N/A**
1N/A** Parameters:
1N/A** conn -- unused.
1N/A** context -- unused.
1N/A** requested_user -- authorization identity.
1N/A** rlen -- authorization identity length.
1N/A** auth_identity -- authentication identity.
1N/A** alen -- authentication identity length.
1N/A** def_realm -- default user realm.
1N/A** urlen -- user realm length.
1N/A** propctx -- unused.
1N/A**
1N/A** Returns:
1N/A** ok?
1N/A**
1N/A** Side Effects:
1N/A** sets {auth_authen} macro.
1N/A*/
1N/A
1N/Aint
1N/Aproxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
1N/A def_realm, urlen, propctx)
1N/A sasl_conn_t *conn;
1N/A void *context;
1N/A const char *requested_user;
1N/A unsigned rlen;
1N/A const char *auth_identity;
1N/A unsigned alen;
1N/A const char *def_realm;
1N/A unsigned urlen;
1N/A struct propctx *propctx;
1N/A{
1N/A if (auth_identity == NULL)
1N/A return SASL_FAIL;
1N/A
1N/A macdefine(&BlankEnvelope.e_macro, A_TEMP,
1N/A macid("{auth_authen}"),
1N/A xtextify((char *) auth_identity, "=<>\")"));
1N/A
1N/A return SASL_OK;
1N/A}
1N/A# else /* SASL >= 20000 */
1N/A
1N/A/*
1N/A** PROXY_POLICY -- define proxy policy for AUTH
1N/A**
1N/A** Parameters:
1N/A** context -- unused.
1N/A** auth_identity -- authentication identity.
1N/A** requested_user -- authorization identity.
1N/A** user -- allowed user (output).
1N/A** errstr -- possible error string (output).
1N/A**
1N/A** Returns:
1N/A** ok?
1N/A*/
1N/A
1N/Aint
1N/Aproxy_policy(context, auth_identity, requested_user, user, errstr)
1N/A void *context;
1N/A const char *auth_identity;
1N/A const char *requested_user;
1N/A const char **user;
1N/A const char **errstr;
1N/A{
1N/A if (user == NULL || auth_identity == NULL)
1N/A return SASL_FAIL;
1N/A *user = newstr(auth_identity);
1N/A return SASL_OK;
1N/A}
1N/A# endif /* SASL >= 20000 */
1N/A#endif /* SASL */
1N/A
1N/A#if STARTTLS
1N/A/*
1N/A** INITSRVTLS -- initialize server side TLS
1N/A**
1N/A** Parameters:
1N/A** tls_ok -- should tls initialization be done?
1N/A**
1N/A** Returns:
1N/A** succeeded?
1N/A**
1N/A** Side Effects:
1N/A** sets tls_ok_srv which is a static variable in this module.
1N/A** Do NOT remove assignments to it!
1N/A*/
1N/A
1N/Abool
1N/Ainitsrvtls(tls_ok)
1N/A bool tls_ok;
1N/A{
1N/A if (!tls_ok)
1N/A return false;
1N/A
1N/A /* do NOT remove assignment */
1N/A tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, Srv_SSL_Options, true,
1N/A SrvCertFile, SrvKeyFile,
1N/A CACertPath, CACertFile, DHParams);
1N/A return tls_ok_srv;
1N/A}
1N/A#endif /* STARTTLS */
1N/A/*
1N/A** SRVFEATURES -- get features for SMTP server
1N/A**
1N/A** Parameters:
1N/A** e -- envelope (should be session context).
1N/A** clientname -- name of client.
1N/A** features -- default features for this invocation.
1N/A**
1N/A** Returns:
1N/A** server features.
1N/A*/
1N/A
1N/A/* table with options: it uses just one character, how about strings? */
1N/Astatic struct
1N/A{
1N/A char srvf_opt;
1N/A unsigned int srvf_flag;
1N/A} srv_feat_table[] =
1N/A{
1N/A { 'A', SRV_OFFER_AUTH },
1N/A { 'B', SRV_OFFER_VERB },
1N/A { 'C', SRV_REQ_SEC },
1N/A { 'D', SRV_OFFER_DSN },
1N/A { 'E', SRV_OFFER_ETRN },
1N/A { 'L', SRV_REQ_AUTH },
1N/A#if PIPELINING
1N/A# if _FFR_NO_PIPE
1N/A { 'N', SRV_NO_PIPE },
1N/A# endif /* _FFR_NO_PIPE */
1N/A { 'P', SRV_OFFER_PIPE },
1N/A#endif /* PIPELINING */
1N/A { 'R', SRV_VRFY_CLT }, /* same as V; not documented */
1N/A { 'S', SRV_OFFER_TLS },
1N/A/* { 'T', SRV_TMP_FAIL }, */
1N/A { 'V', SRV_VRFY_CLT },
1N/A { 'X', SRV_OFFER_EXPN },
1N/A/* { 'Y', SRV_OFFER_VRFY }, */
1N/A { '\0', SRV_NONE }
1N/A};
1N/A
1N/Astatic unsigned int
1N/Asrvfeatures(e, clientname, features)
1N/A ENVELOPE *e;
1N/A char *clientname;
1N/A unsigned int features;
1N/A{
1N/A int r, i, j;
1N/A char **pvp, c, opt;
1N/A char pvpbuf[PSBUFSIZE];
1N/A
1N/A pvp = NULL;
1N/A r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
1N/A sizeof(pvpbuf));
1N/A if (r != EX_OK)
1N/A return features;
1N/A if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
1N/A return features;
1N/A if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
1N/A return SRV_TMP_FAIL;
1N/A
1N/A /*
1N/A ** General rule (see sendmail.h, d_flags):
1N/A ** lower case: required/offered, upper case: Not required/available
1N/A **
1N/A ** Since we can change some features per daemon, we have both
1N/A ** cases here: turn on/off a feature.
1N/A */
1N/A
1N/A for (i = 1; pvp[i] != NULL; i++)
1N/A {
1N/A c = pvp[i][0];
1N/A j = 0;
1N/A for (;;)
1N/A {
1N/A if ((opt = srv_feat_table[j].srvf_opt) == '\0')
1N/A {
1N/A if (LogLevel > 9)
1N/A sm_syslog(LOG_WARNING, e->e_id,
1N/A "srvfeatures: unknown feature %s",
1N/A pvp[i]);
1N/A break;
1N/A }
1N/A if (c == opt)
1N/A {
1N/A features &= ~(srv_feat_table[j].srvf_flag);
1N/A break;
1N/A }
1N/A if (c == tolower(opt))
1N/A {
1N/A features |= srv_feat_table[j].srvf_flag;
1N/A break;
1N/A }
1N/A ++j;
1N/A }
1N/A }
1N/A return features;
1N/A}
1N/A
1N/A/*
1N/A** HELP -- implement the HELP command.
1N/A**
1N/A** Parameters:
1N/A** topic -- the topic we want help for.
1N/A** e -- envelope.
1N/A**
1N/A** Returns:
1N/A** none.
1N/A**
1N/A** Side Effects:
1N/A** outputs the help file to message output.
1N/A*/
1N/A#define HELPVSTR "#vers "
1N/A#define HELPVERSION 2
1N/A
1N/Avoid
1N/Ahelp(topic, e)
1N/A char *topic;
1N/A ENVELOPE *e;
1N/A{
1N/A register SM_FILE_T *hf;
1N/A register char *p;
1N/A int len;
1N/A bool noinfo;
1N/A bool first = true;
1N/A long sff = SFF_OPENASROOT|SFF_REGONLY;
1N/A char buf[MAXLINE];
1N/A char inp[MAXLINE];
1N/A static int foundvers = -1;
1N/A extern char Version[];
1N/A
1N/A if (DontLockReadFiles)
1N/A sff |= SFF_NOLOCK;
1N/A if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
1N/A sff |= SFF_SAFEDIRPATH;
1N/A
1N/A if (HelpFile == NULL ||
1N/A (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
1N/A {
1N/A /* no help */
1N/A errno = 0;
1N/A message("502 5.3.0 Sendmail %s -- HELP not implemented",
1N/A Version);
1N/A return;
1N/A }
1N/A
1N/A if (topic == NULL || *topic == '\0')
1N/A {
1N/A topic = "smtp";
1N/A noinfo = false;
1N/A }
1N/A else
1N/A {
1N/A makelower(topic);
1N/A noinfo = true;
1N/A }
1N/A
1N/A len = strlen(topic);
1N/A
1N/A while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
1N/A {
1N/A if (buf[0] == '#')
1N/A {
1N/A if (foundvers < 0 &&
1N/A strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
1N/A {
1N/A int h;
1N/A
1N/A if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
1N/A &h) == 1)
1N/A foundvers = h;
1N/A }
1N/A continue;
1N/A }
1N/A if (strncmp(buf, topic, len) == 0)
1N/A {
1N/A if (first)
1N/A {
1N/A first = false;
1N/A
1N/A /* print version if no/old vers# in file */
1N/A if (foundvers < 2 && !noinfo)
1N/A message("214-2.0.0 This is Sendmail version %s", Version);
1N/A }
1N/A p = strpbrk(buf, " \t");
1N/A if (p == NULL)
1N/A p = buf + strlen(buf) - 1;
1N/A else
1N/A p++;
1N/A fixcrlf(p, true);
1N/A if (foundvers >= 2)
1N/A {
1N/A char *lbp;
1N/A int lbs = sizeof(buf) - (p - buf);
1N/A
1N/A lbp = translate_dollars(p, p, &lbs);
1N/A expand(lbp, inp, sizeof(inp), e);
1N/A if (p != lbp)
1N/A sm_free(lbp);
1N/A p = inp;
1N/A }
1N/A message("214-2.0.0 %s", p);
1N/A noinfo = false;
1N/A }
1N/A }
1N/A
1N/A if (noinfo)
1N/A message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
1N/A else
1N/A message("214 2.0.0 End of HELP info");
1N/A
1N/A if (foundvers != 0 && foundvers < HELPVERSION)
1N/A {
1N/A if (LogLevel > 1)
1N/A sm_syslog(LOG_WARNING, e->e_id,
1N/A "%s too old (require version %d)",
1N/A HelpFile, HELPVERSION);
1N/A
1N/A /* avoid log next time */
1N/A foundvers = 0;
1N/A }
1N/A
1N/A (void) sm_io_close(hf, SM_TIME_DEFAULT);
1N/A}
1N/A
1N/A#if SASL
1N/A/*
1N/A** RESET_SASLCONN -- reset SASL connection data
1N/A**
1N/A** Parameters:
1N/A** conn -- SASL connection context
1N/A** hostname -- host name
1N/A** various connection data
1N/A**
1N/A** Returns:
1N/A** SASL result
1N/A*/
1N/A
1N/Astatic int
1N/Areset_saslconn(sasl_conn_t **conn, char *hostname,
1N/A# if SASL >= 20000
1N/A char *remoteip, char *localip,
1N/A char *auth_id, sasl_ssf_t * ext_ssf)
1N/A# else /* SASL >= 20000 */
1N/A struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
1N/A sasl_external_properties_t * ext_ssf)
1N/A# endif /* SASL >= 20000 */
1N/A{
1N/A int result;
1N/A
1N/A sasl_dispose(conn);
1N/A# if SASL >= 20000
1N/A result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
1N/A NULL, 0, conn);
1N/A# elif SASL > 10505
1N/A /* use empty realm: only works in SASL > 1.5.5 */
1N/A result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
1N/A# else /* SASL >= 20000 */
1N/A /* use no realm -> realm is set to hostname by SASL lib */
1N/A result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
1N/A conn);
1N/A# endif /* SASL >= 20000 */
1N/A if (result != SASL_OK)
1N/A return result;
1N/A
1N/A# if SASL >= 20000
1N/A# if NETINET || NETINET6
1N/A if (remoteip != NULL && *remoteip != '\0')
1N/A result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A
1N/A if (localip != NULL && *localip != '\0')
1N/A result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A# endif /* NETINET || NETINET6 */
1N/A
1N/A result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A
1N/A result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A# else /* SASL >= 20000 */
1N/A# if NETINET
1N/A if (saddr_r != NULL)
1N/A result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A
1N/A if (saddr_l != NULL)
1N/A result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A# endif /* NETINET */
1N/A
1N/A result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
1N/A if (result != SASL_OK)
1N/A return result;
1N/A# endif /* SASL >= 20000 */
1N/A return SASL_OK;
1N/A}
1N/A#endif /* SASL */