summaryrefslogtreecommitdiff
path: root/debian
diff options
context:
space:
mode:
authorRichard A Nelson (Rick) <cowboy@debian.org>2005-02-10 19:02:00 +0000
committerAndreas Beckmann <debian@abeckmann.de>2012-10-01 19:58:46 +0200
commita875402789ecb6c61ead9b214e258c83e4d135e6 (patch)
tree87bb1748277aec2368a892cee0963a84f96185cd /debian
parente6b88b4e82cb1d7b6750d610b11b5036a5f5ab2a (diff)
downloadsendmail-a875402789ecb6c61ead9b214e258c83e4d135e6.tar.gz
Imported Debian patch 8.13.3-7debian/8.13.3-7
Diffstat (limited to 'debian')
-rw-r--r--debian/Makefile.in24
-rw-r--r--debian/build/debian/control.m4.in6
-rw-r--r--debian/cf/Makefile.in4
-rw-r--r--debian/changelog10
-rw-r--r--debian/control6
-rw-r--r--debian/examples/Makefile.in4
-rw-r--r--debian/examples/db/access23
-rw-r--r--debian/local/Makefile.in4
-rw-r--r--debian/local/dynamic.in2
-rw-r--r--debian/local/sendmail.in13
-rw-r--r--debian/patches/8.13/srvrsmtp.c4702
-rw-r--r--debian/patches/8.13/srvrsmtp.new4702
-rw-r--r--debian/patches/8.13/srvrsmtp.patch120
-rw-r--r--debian/sensible_mda/Makefile.in4
14 files changed, 54 insertions, 9570 deletions
diff --git a/debian/Makefile.in b/debian/Makefile.in
index 9a648aa..61893c6 100644
--- a/debian/Makefile.in
+++ b/debian/Makefile.in
@@ -1,8 +1,8 @@
-# Makefile.in generated by automake 1.9.4 from Makefile.am.
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004 Free Software Foundation, Inc.
+# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -512,7 +512,13 @@ uninstall-info-am:
# (which will cause the Makefiles to be regenerated when you run `make');
# (2) otherwise, pass the desired values on the `make' command line.
$(RECURSIVE_TARGETS):
- @set fnord $$MAKEFLAGS; amf=$$2; \
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
@@ -524,7 +530,7 @@ $(RECURSIVE_TARGETS):
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+ || eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
@@ -532,7 +538,13 @@ $(RECURSIVE_TARGETS):
mostlyclean-recursive clean-recursive distclean-recursive \
maintainer-clean-recursive:
- @set fnord $$MAKEFLAGS; amf=$$2; \
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
dot_seen=no; \
case "$@" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
@@ -553,7 +565,7 @@ maintainer-clean-recursive:
local_target="$$target"; \
fi; \
(cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+ || eval $$failcom; \
done && test -z "$$fail"
tags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
diff --git a/debian/build/debian/control.m4.in b/debian/build/debian/control.m4.in
index 47e0fca..8cb43e2 100644
--- a/debian/build/debian/control.m4.in
+++ b/debian/build/debian/control.m4.in
@@ -24,8 +24,8 @@ Section: mail
Maintainer: Richard A Nelson (Rick) <cowboy@debian.org>
Standards-Version: 3.6.10.0
Build-Depends-Indep: groff-base, bsdmainutils
-Build-Depends: make (>> 3.79.1-14), m4, patch, debhelper (>= 4.1.68)@sm_badepends@
-Build-Conflicts: libbind-dev, bind-dev]]dnl
+Build-Depends: make (>> 3.79.1-14), m4, patch, debhelper (>= 4.1.68) @sm_badepends@
+Build-Conflicts: libbind-dev, bind-dev, fakeroot (<< 0.4.5)]]dnl
dnl #
dnl # The arch=any package: sendmail-bin
dnl # Always built
@@ -206,7 +206,7 @@ dnl #
Package: sendmail-base
Architecture: all
Section: mail
-Depends: m4, make, adduser, ${perl:Depends}
+Depends: m4, make, adduser, netbase (>= 4.0.8), ${perl:Depends}
Recommends: resolvconf
Suggests: sendmail-doc, ca-certificates, logcheck, logrotate
Conflicts: sendmail (<= 8.13.1-7)
diff --git a/debian/cf/Makefile.in b/debian/cf/Makefile.in
index 97d0ad9..aae944f 100644
--- a/debian/cf/Makefile.in
+++ b/debian/cf/Makefile.in
@@ -1,8 +1,8 @@
-# Makefile.in generated by automake 1.9.4 from Makefile.am.
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004 Free Software Foundation, Inc.
+# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/debian/changelog b/debian/changelog
index b80628b..c13b6ba 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+sendmail (8.13.3-7) unstable; urgency=high
+ * Build conflicts on older version of fakeroot (or sudo) (FTBFS)
+ * Log (some) sendmail startup commands iff LOG_CMDS=Yes
+ * Depends netbase (>= 4.0.8) for submission closes: #295132
+ * Correct multiple IF in dynamic closes: #296475
+ * Drop invalid ip's from sample access closes: #296491
+ A nice idea, but would require constant updates :(
+
+ -- Richard A Nelson (Rick) <cowboy@debian.org> Thu, 10 Feb 2005 19:02:00 -0000
+
sendmail (8.13.3-6) unstable; urgency=high
* Correct m4 quoting in control file (FTBFS)
* Only call dh_shlibdeps for build-arch targets
diff --git a/debian/control b/debian/control
index e470cd6..78778c2 100644
--- a/debian/control
+++ b/debian/control
@@ -4,8 +4,8 @@ Section: mail
Maintainer: Richard A Nelson (Rick) <cowboy@debian.org>
Standards-Version: 3.6.10.0
Build-Depends-Indep: groff-base, bsdmainutils
-Build-Depends: make (>> 3.79.1-14), m4, patch, debhelper (>= 4.1.68), libdb4.2-dev, libldap2-dev, liblockfile-dev, libsasl2-dev, libssl-dev
-Build-Conflicts: libbind-dev, bind-dev
+Build-Depends: make (>> 3.79.1-14), m4, patch, debhelper (>= 4.1.68) , libdb4.2-dev, libldap2-dev, liblockfile-dev, libsasl2-dev, libssl-dev
+Build-Conflicts: libbind-dev, bind-dev, fakeroot (<< 0.4.5)
Package: sendmail-bin
Architecture: any
@@ -146,7 +146,7 @@ Description: powerful, efficient, and scalable Mail Transport Agent
Package: sendmail-base
Architecture: all
Section: mail
-Depends: m4, make, adduser, ${perl:Depends}
+Depends: m4, make, adduser, netbase (>= 4.0.8), ${perl:Depends}
Recommends: resolvconf
Suggests: sendmail-doc, ca-certificates, logcheck, logrotate
Conflicts: sendmail (<= 8.13.1-7)
diff --git a/debian/examples/Makefile.in b/debian/examples/Makefile.in
index 08f4d46..290511c 100644
--- a/debian/examples/Makefile.in
+++ b/debian/examples/Makefile.in
@@ -1,8 +1,8 @@
-# Makefile.in generated by automake 1.9.4 from Makefile.am.
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004 Free Software Foundation, Inc.
+# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/debian/examples/db/access b/debian/examples/db/access
index 8bfab0b..af43fef 100644
--- a/debian/examples/db/access
+++ b/debian/examples/db/access
@@ -133,30 +133,7 @@ reject@ REJECT
# Block invalid IPs
#
Connect:0 REJECT
-Connect:2 REJECT
-Connect:5 REJECT
-Connect:7 REJECT
-Connect:8 REJECT
-Connect:23 REJECT
-Connect:27 REJECT
-Connect:31 REJECT
-Connect:36 REJECT
-Connect:37 REJECT
-Connect:39 REJECT
-Connect:41 REJECT
-Connect:42 REJECT
-Connect:50 REJECT
-Connect:71 REJECT
-Connect:72 REJECT
-Connect:73 REJECT
-Connect:74 REJECT
-Connect:75 REJECT
-Connect:76 REJECT
-Connect:77 REJECT
-Connect:78 REJECT
-Connect:79 REJECT
Connect:169.254 REJECT
Connect:192.0.2 REJECT
-Connect:197 REJECT
Connect:224 REJECT
Connect:255 REJECT
diff --git a/debian/local/Makefile.in b/debian/local/Makefile.in
index 59a51c5..5c86719 100644
--- a/debian/local/Makefile.in
+++ b/debian/local/Makefile.in
@@ -1,8 +1,8 @@
-# Makefile.in generated by automake 1.9.4 from Makefile.am.
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004 Free Software Foundation, Inc.
+# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/debian/local/dynamic.in b/debian/local/dynamic.in
index ce96cef..9598e96 100644
--- a/debian/local/dynamic.in
+++ b/debian/local/dynamic.in
@@ -61,7 +61,7 @@ update_interface() {
fi;
# Check to see if we care about this interface
- for if in "$DAEMON_NETIF"; do
+ for if in $DAEMON_NETIF; do
if [ "$if" = "$SM_interface" ]; then
SM_ignore=0;
break;
diff --git a/debian/local/sendmail.in b/debian/local/sendmail.in
index 234b5ed..c29b712 100644
--- a/debian/local/sendmail.in
+++ b/debian/local/sendmail.in
@@ -37,7 +37,7 @@ Get_Parameters () {
MSP_PARMS="";
MISC_PARMS='';
CRON_PARMS='';
- LOG_CMDS='Yes';
+ LOG_CMDS='No';
# Secondary (non-documented) parameters in /etc/mail/sendmail.conf
# Caveat Emptor: change these at your own risk - they impact several
@@ -190,7 +190,11 @@ start_mta () {
#
# cd to a safe place to stash core files...
cd $MTA_ROOT;
- $START_MTAL_CMD -- $MTAL_PARMS &
+ CMD="$START_MTAL_CMD -- $MTAL_PARMS";
+ if [ "$LOG_CMDS" = "Yes" ]; then
+ logger -i -p mail.debug -- "$0 $CMD";
+ fi;
+ $CMD &
#
# Update permissions on smsocket
sleep 2;
@@ -200,6 +204,11 @@ start_mta () {
#
# Check for split daemon mode (separate listener/queue runner)
if [ "$SPLIT_DAEMON" -eq 1 ]; then
+ CMD="$START_MTAQ_CMD -- $MTAQ_PARMS";
+ if [ "$LOG_CMDS" = "Yes" ]; then
+ logger -i -p mail.debug -- "$0 $CMD";
+ fi;
+ $CMD &
$START_MTAQ_CMD -- $MTAQ_PARMS &
sleep 2;
qp=`expr "${MTAQ_PARMS}" : '.*\(-qp[0-9]*[smhdw]\)'` || true;
diff --git a/debian/patches/8.13/srvrsmtp.c b/debian/patches/8.13/srvrsmtp.c
deleted file mode 100644
index 728a5fc..0000000
--- a/debian/patches/8.13/srvrsmtp.c
+++ /dev/null
@@ -1,4702 +0,0 @@
-/*
- * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-#if MILTER
-# include <libmilter/mfapi.h>
-# include <libmilter/mfdef.h>
-#endif /* MILTER */
-
-SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.900 2004/07/08 23:29:33 ca Exp $")
-
-#include <sys/time.h>
-#include <sm/fdset.h>
-
-#if SASL || STARTTLS
-# include "sfsasl.h"
-#endif /* SASL || STARTTLS */
-#if SASL
-# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
-static int saslmechs __P((sasl_conn_t *, char **));
-#endif /* SASL */
-#if STARTTLS
-# include <sysexits.h>
-
-static SSL_CTX *srv_ctx = NULL; /* TLS server context */
-static SSL *srv_ssl = NULL; /* per connection context */
-
-static bool tls_ok_srv = false;
-
-extern void tls_set_verify __P((SSL_CTX *, SSL *, bool));
-# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
- bitset(SRV_VRFY_CLT, features))
-#endif /* STARTTLS */
-
-/* server features */
-#define SRV_NONE 0x0000 /* none... */
-#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
-#define SRV_VRFY_CLT 0x0002 /* request a cert */
-#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
-#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
-#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
-#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
-#define SRV_OFFER_VERB 0x0040 /* offer VERB */
-#define SRV_OFFER_DSN 0x0080 /* offer DSN */
-#if PIPELINING
-# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
-# if _FFR_NO_PIPE
-# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-#define SRV_REQ_AUTH 0x0400 /* require AUTH */
-#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */
-#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
-
-static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
-
-#define STOP_ATTACK ((time_t) -1)
-static time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
- bool, char *, ENVELOPE *));
-static void mail_esmtp_args __P((char *, char *, ENVELOPE *));
-static void printvrfyaddr __P((ADDRESS *, bool, bool));
-static void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
-static char *skipword __P((char *volatile, char *));
-static void setup_smtpd_io __P((void));
-
-#if SASL
-# if SASL >= 20000
-static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
- char *_remoteip, char *_localip,
- char *_auth_id, sasl_ssf_t *_ext_ssf));
-
-# define RESET_SASLCONN \
- result = reset_saslconn(&conn, AuthRealm, remoteip, localip, auth_id, \
- &ext_ssf); \
- if (result != SASL_OK) \
- { \
- /* This is pretty fatal */ \
- goto doquit; \
- }
-
-# else /* SASL >= 20000 */
-static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
- struct sockaddr_in *_saddr_r,
- struct sockaddr_in *_saddr_l,
- sasl_external_properties_t *_ext_ssf));
-# define RESET_SASLCONN \
- result = reset_saslconn(&conn, AuthRealm, &saddr_r, &saddr_l, &ext_ssf); \
- if (result != SASL_OK) \
- { \
- /* This is pretty fatal */ \
- goto doquit; \
- }
-
-# endif /* SASL >= 20000 */
-#endif /* SASL */
-
-extern ENVELOPE BlankEnvelope;
-
-#define NBADRCPTS \
- do \
- { \
- char buf[16]; \
- (void) sm_snprintf(buf, sizeof buf, "%d", \
- BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
- ? n_badrcpts - 1 : n_badrcpts); \
- macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
- } while (0)
-
-#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
- (s)++
-
-/*
-** SMTP -- run the SMTP protocol.
-**
-** Parameters:
-** nullserver -- if non-NULL, rejection message for
-** (almost) all SMTP commands.
-** d_flags -- daemon flags
-** e -- the envelope.
-**
-** Returns:
-** never.
-**
-** Side Effects:
-** Reads commands from the input channel and processes them.
-*/
-
-/*
-** Notice: The smtp server doesn't have a session context like the client
-** side has (mci). Therefore some data (session oriented) is allocated
-** or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
-** This should be fixed in a successor version.
-*/
-
-struct cmd
-{
- char *cmd_name; /* command name */
- int cmd_code; /* internal code, see below */
-};
-
-/* values for cmd_code */
-#define CMDERROR 0 /* bad command */
-#define CMDMAIL 1 /* mail -- designate sender */
-#define CMDRCPT 2 /* rcpt -- designate recipient */
-#define CMDDATA 3 /* data -- send message text */
-#define CMDRSET 4 /* rset -- reset state */
-#define CMDVRFY 5 /* vrfy -- verify address */
-#define CMDEXPN 6 /* expn -- expand address */
-#define CMDNOOP 7 /* noop -- do nothing */
-#define CMDQUIT 8 /* quit -- close connection and die */
-#define CMDHELO 9 /* helo -- be polite */
-#define CMDHELP 10 /* help -- give usage info */
-#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
-#define CMDETRN 12 /* etrn -- flush queue */
-#if SASL
-# define CMDAUTH 13 /* auth -- SASL authenticate */
-#endif /* SASL */
-#if STARTTLS
-# define CMDSTLS 14 /* STARTTLS -- start TLS session */
-#endif /* STARTTLS */
-/* non-standard commands */
-#define CMDVERB 17 /* verb -- go into verbose mode */
-/* unimplemented commands from RFC 821 */
-#define CMDUNIMPL 19 /* unimplemented rfc821 commands */
-/* use this to catch and log "door handle" attempts on your system */
-#define CMDLOGBOGUS 23 /* bogus command that should be logged */
-/* debugging-only commands, only enabled if SMTPDEBUG is defined */
-#define CMDDBGQSHOW 24 /* showq -- show send queue */
-#define CMDDBGDEBUG 25 /* debug -- set debug mode */
-
-/*
-** Note: If you change this list, remember to update 'helpfile'
-*/
-
-static struct cmd CmdTab[] =
-{
- { "mail", CMDMAIL },
- { "rcpt", CMDRCPT },
- { "data", CMDDATA },
- { "rset", CMDRSET },
- { "vrfy", CMDVRFY },
- { "expn", CMDEXPN },
- { "help", CMDHELP },
- { "noop", CMDNOOP },
- { "quit", CMDQUIT },
- { "helo", CMDHELO },
- { "ehlo", CMDEHLO },
- { "etrn", CMDETRN },
- { "verb", CMDVERB },
- { "send", CMDUNIMPL },
- { "saml", CMDUNIMPL },
- { "soml", CMDUNIMPL },
- { "turn", CMDUNIMPL },
-#if SASL
- { "auth", CMDAUTH, },
-#endif /* SASL */
-#if STARTTLS
- { "starttls", CMDSTLS, },
-#endif /* STARTTLS */
- /* remaining commands are here only to trap and log attempts to use them */
- { "showq", CMDDBGQSHOW },
- { "debug", CMDDBGDEBUG },
- { "wiz", CMDLOGBOGUS },
-
- { NULL, CMDERROR }
-};
-
-static char *CurSmtpClient; /* who's at the other end of channel */
-
-#ifndef MAXBADCOMMANDS
-# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
-#endif /* ! MAXBADCOMMANDS */
-#ifndef MAXNOOPCOMMANDS
-# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
-#endif /* ! MAXNOOPCOMMANDS */
-#ifndef MAXHELOCOMMANDS
-# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
-#endif /* ! MAXHELOCOMMANDS */
-#ifndef MAXVRFYCOMMANDS
-# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
-#endif /* ! MAXVRFYCOMMANDS */
-#ifndef MAXETRNCOMMANDS
-# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
-#endif /* ! MAXETRNCOMMANDS */
-#ifndef MAXTIMEOUT
-# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
-#endif /* ! MAXTIMEOUT */
-
-/*
-** Maximum shift value to compute timeout for bad commands.
-** This introduces an upper limit of 2^MAXSHIFT for the timeout.
-*/
-
-#ifndef MAXSHIFT
-# define MAXSHIFT 8
-#endif /* ! MAXSHIFT */
-#if MAXSHIFT > 31
- ERROR _MAXSHIFT > 31 is invalid
-#endif /* MAXSHIFT */
-
-
-#if MAXBADCOMMANDS > 0
-# define STOP_IF_ATTACK(r) do \
- { \
- if ((r) == STOP_ATTACK) \
- goto stopattack; \
- } while (0)
-
-#else /* MAXBADCOMMANDS > 0 */
-# define STOP_IF_ATTACK(r) r
-#endif /* MAXBADCOMMANDS > 0 */
-
-
-#if SM_HEAP_CHECK
-static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
- "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
-#endif /* SM_HEAP_CHECK */
-
-typedef struct
-{
- bool sm_gotmail; /* mail command received */
- unsigned int sm_nrcpts; /* number of successful RCPT commands */
- bool sm_discard;
-#if MILTER
- bool sm_milterize;
- bool sm_milterlist; /* any filters in the list? */
-#endif /* MILTER */
- char *sm_quarmsg; /* carry quarantining across messages */
-} SMTP_T;
-
-static bool smtp_data __P((SMTP_T *, ENVELOPE *));
-
-#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
-
-#if MILTER
-# define MILTER_ABORT(e) milter_abort((e))
-
-# define MILTER_REPLY(str) \
- { \
- int savelogusrerrs = LogUsrErrs; \
- \
- switch (state) \
- { \
- case SMFIR_REPLYCODE: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=%s", \
- str, addr, response); \
- LogUsrErrs = false; \
- } \
- if (strncmp(response, "421 ", 4) == 0) \
- { \
- bool tsave = QuickAbort; \
- \
- QuickAbort = false; \
- usrerr(response); \
- QuickAbort = tsave; \
- e->e_sendqueue = NULL; \
- goto doquit; \
- } \
- else \
- usrerr(response); \
- break; \
- \
- case SMFIR_REJECT: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
- str, addr); \
- LogUsrErrs = false; \
- } \
- usrerr("550 5.7.1 Command rejected"); \
- break; \
- \
- case SMFIR_DISCARD: \
- if (MilterLogLevel > 3) \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, discard", \
- str, addr); \
- e->e_flags |= EF_DISCARD; \
- break; \
- \
- case SMFIR_TEMPFAIL: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=%s", \
- str, addr, MSG_TEMPFAIL); \
- LogUsrErrs = false; \
- } \
- usrerr(MSG_TEMPFAIL); \
- break; \
- } \
- LogUsrErrs = savelogusrerrs; \
- if (response != NULL) \
- sm_free(response); /* XXX */ \
- }
-
-#else /* MILTER */
-# define MILTER_ABORT(e)
-#endif /* MILTER */
-
-/* clear all SMTP state (for HELO/EHLO/RSET) */
-#define CLEAR_STATE(cmd) \
-do \
-{ \
- /* abort milter filters */ \
- MILTER_ABORT(e); \
- \
- if (smtp.sm_nrcpts > 0) \
- { \
- logundelrcpts(e, cmd, 10, false); \
- smtp.sm_nrcpts = 0; \
- macdefine(&e->e_macro, A_PERM, \
- macid("{nrcpts}"), "0"); \
- } \
- \
- e->e_sendqueue = NULL; \
- e->e_flags |= EF_CLRQUEUE; \
- \
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \
- logsender(e, NULL); \
- e->e_flags &= ~EF_LOGSENDER; \
- \
- /* clean up a bit */ \
- smtp.sm_gotmail = false; \
- SuprErrs = true; \
- dropenvelope(e, true, false); \
- sm_rpool_free(e->e_rpool); \
- e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
- CurEnv = e; \
- \
- /* put back discard bit */ \
- if (smtp.sm_discard) \
- e->e_flags |= EF_DISCARD; \
- \
- /* restore connection quarantining */ \
- if (smtp.sm_quarmsg == NULL) \
- { \
- e->e_quarmsg = NULL; \
- macdefine(&e->e_macro, A_PERM, \
- macid("{quarantine}"), ""); \
- } \
- else \
- { \
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
- smtp.sm_quarmsg); \
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
- e->e_quarmsg); \
- } \
-} while (0)
-
-/* sleep to flatten out connection load */
-#define MIN_DELAY_LOG 15 /* wait before logging this again */
-
-/* is it worth setting the process title for 1s? */
-#define DELAY_CONN(cmd) \
- if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \
- { \
- time_t dnow; \
- \
- sm_setproctitle(true, e, \
- "%s: %s: delaying %s: load average: %d", \
- qid_printname(e), CurSmtpClient, \
- cmd, DelayLA); \
- if (LogLevel > 8 && (dnow = curtime()) > log_delay) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "delaying=%s, load average=%d >= %d", \
- cmd, CurrentLA, DelayLA); \
- log_delay = dnow + MIN_DELAY_LOG; \
- } \
- (void) sleep(1); \
- sm_setproctitle(true, e, "%s %s: %.80s", \
- qid_printname(e), CurSmtpClient, inp); \
- }
-
-
-void
-smtp(nullserver, d_flags, e)
- char *volatile nullserver;
- BITMAP256 d_flags;
- register ENVELOPE *volatile e;
-{
- register char *volatile p;
- register struct cmd *volatile c = NULL;
- char *cmd;
- auto ADDRESS *vrfyqueue;
- ADDRESS *a;
- volatile bool gothello; /* helo command received */
- bool vrfy; /* set if this is a vrfy command */
- char *volatile protocol; /* sending protocol */
- char *volatile sendinghost; /* sending hostname */
- char *volatile peerhostname; /* name of SMTP peer or "localhost" */
- char *volatile hello_name; /* client_hello string */
- bool hello_acceptd = false; /* helo/ehlo command accepted */
- auto char *delimptr;
- char *id;
- volatile unsigned int n_badcmds = 0; /* count of bad commands */
- volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */
- volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */
- volatile unsigned int n_etrn = 0; /* count of ETRN */
- volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */
- volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
- volatile int save_sevenbitinput;
- bool ok;
-#if _FFR_BLOCK_PROXIES
- volatile bool first;
-#endif /* _FFR_BLOCK_PROXIES */
- volatile bool tempfail = false;
- volatile time_t wt; /* timeout after too many commands */
- volatile time_t previous; /* time after checksmtpattack() */
- volatile bool lognullconnection = true;
- register char *q;
- SMTP_T smtp;
- char *addr;
- char *greetcode = "220";
- char *hostname; /* my hostname ($j) */
- QUEUE_CHAR *new;
- int argno;
- char *args[MAXSMTPARGS];
- char inp[MAXLINE];
- char cmdbuf[MAXLINE];
-#if SASL
- sasl_conn_t *conn;
- volatile bool sasl_ok;
- volatile unsigned int n_auth = 0; /* count of AUTH commands */
- bool ismore;
- int result;
- volatile int authenticating;
- char *user;
- char *in, *out2;
-# if SASL >= 20000
- char *auth_id;
- const char *out;
- sasl_ssf_t ext_ssf;
- char localip[60], remoteip[60];
-# else /* SASL >= 20000 */
- char *out;
- const char *errstr;
- sasl_external_properties_t ext_ssf;
- struct sockaddr_in saddr_l;
- struct sockaddr_in saddr_r;
-# endif /* SASL >= 20000 */
- sasl_security_properties_t ssp;
- sasl_ssf_t *ssf;
- unsigned int inlen, out2len;
- unsigned int outlen;
- char *volatile auth_type;
- char *mechlist;
- volatile unsigned int n_mechs;
- unsigned int len;
-#endif /* SASL */
- int r;
-#if STARTTLS
- int fdfl;
- int rfd, wfd;
- volatile bool tls_active = false;
- volatile bool smtps = bitnset(D_SMTPS, d_flags);
- bool saveQuickAbort;
- bool saveSuprErrs;
- time_t tlsstart;
-#endif /* STARTTLS */
- volatile unsigned int features;
-#if PIPELINING
-# if _FFR_NO_PIPE
- int np_log = 0;
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
- volatile time_t log_delay = (time_t) 0;
-
- save_sevenbitinput = SevenBitInput;
- smtp.sm_nrcpts = 0;
-#if MILTER
- smtp.sm_milterize = (nullserver == NULL);
- smtp.sm_milterlist = false;
-#endif /* MILTER */
-
- /* setup I/O fd correctly for the SMTP server */
- setup_smtpd_io();
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakSmtp, 1))
- {
- sm_heap_newgroup();
- sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- /* XXX the rpool should be set when e is initialized in main() */
- e->e_rpool = sm_rpool_new_x(NULL);
- e->e_macro.mac_rpool = e->e_rpool;
-
- settime(e);
- sm_getla();
- peerhostname = RealHostName;
- if (peerhostname == NULL)
- peerhostname = "localhost";
- CurHostName = peerhostname;
- CurSmtpClient = macvalue('_', e);
- if (CurSmtpClient == NULL)
- CurSmtpClient = CurHostName;
-
- /* check_relay may have set discard bit, save for later */
- smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
-
-#if PIPELINING
- /* auto-flush output when reading input */
- (void) sm_io_autoflush(InChannel, OutChannel);
-#endif /* PIPELINING */
-
- sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
-
- /* Set default features for server. */
- features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
- bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
- | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
- | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
- : (SRV_OFFER_EXPN
- | (bitset(PRIV_NOVERB, PrivacyFlags)
- ? SRV_NONE : SRV_OFFER_VERB)))
- | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
- : SRV_OFFER_DSN)
-#if SASL
- | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
- | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
- : SRV_NONE)
-#endif /* SASL */
-#if PIPELINING
- | SRV_OFFER_PIPE
-#endif /* PIPELINING */
-#if STARTTLS
- | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
- | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
- : SRV_VRFY_CLT)
-#endif /* STARTTLS */
- ;
- if (nullserver == NULL)
- {
- features = srvfeatures(e, CurSmtpClient, features);
- if (bitset(SRV_TMP_FAIL, features))
- {
- if (LogLevel > 4)
- sm_syslog(LOG_ERR, NOQID,
- "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
- CurSmtpClient);
- nullserver = "450 4.3.0 Please try again later.";
- }
- else
- {
-#if PIPELINING
-# if _FFR_NO_PIPE
- if (bitset(SRV_NO_PIPE, features))
- {
- /* for consistency */
- features &= ~SRV_OFFER_PIPE;
- }
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-#if SASL
- if (bitset(SRV_REQ_SEC, features))
- SASLOpts |= SASL_SEC_NOPLAINTEXT;
- else
- SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
-#endif /* SASL */
- }
- }
- else if (strncmp(nullserver, "421 ", 4) == 0)
- {
- message(nullserver);
- goto doquit;
- }
-
- hostname = macvalue('j', e);
-#if SASL
- if (AuthRealm == NULL)
- AuthRealm = hostname;
- sasl_ok = bitset(SRV_OFFER_AUTH, features);
- n_mechs = 0;
- authenticating = SASL_NOT_AUTH;
-
- /* SASL server new connection */
- if (sasl_ok)
- {
-# if SASL >= 20000
- result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
- NULL, 0, &conn);
-# elif SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
-# else /* SASL >= 20000 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
- &conn);
-# endif /* SASL >= 20000 */
- sasl_ok = result == SASL_OK;
- if (!sasl_ok)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH error: sasl_server_new failed=%d",
- result);
- }
- }
- if (sasl_ok)
- {
- /*
- ** SASL set properties for sasl
- ** set local/remote IP
- ** XXX Cyrus SASL v1 only supports IPv4
- **
- ** XXX where exactly are these used/required?
- ** Kerberos_v4
- */
-
-# if SASL >= 20000
-# if NETINET || NETINET6
- in = macvalue(macid("{daemon_family}"), e);
- if (in != NULL && (
-# if NETINET6
- strcmp(in, "inet6") == 0 ||
-# endif /* NETINET6 */
- strcmp(in, "inet") == 0))
- {
- SOCKADDR_LEN_T addrsize;
- SOCKADDR saddr_l;
- SOCKADDR saddr_r;
-
- addrsize = sizeof(saddr_r);
- if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_r,
- &addrsize) == 0)
- {
- if (iptostring(&saddr_r, addrsize,
- remoteip, sizeof remoteip))
- {
- sasl_setprop(conn, SASL_IPREMOTEPORT,
- remoteip);
- }
- addrsize = sizeof(saddr_l);
- if (getsockname(sm_io_getinfo(InChannel,
- SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_l,
- &addrsize) == 0)
- {
- if (iptostring(&saddr_l, addrsize,
- localip,
- sizeof localip))
- {
- sasl_setprop(conn,
- SASL_IPLOCALPORT,
- localip);
- }
- }
- }
- }
-# endif /* NETINET || NETINET6 */
-# else /* SASL >= 20000 */
-# if NETINET
- in = macvalue(macid("{daemon_family}"), e);
- if (in != NULL && strcmp(in, "inet") == 0)
- {
- SOCKADDR_LEN_T addrsize;
-
- addrsize = sizeof(struct sockaddr_in);
- if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *)&saddr_r,
- &addrsize) == 0)
- {
- sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
- addrsize = sizeof(struct sockaddr_in);
- if (getsockname(sm_io_getinfo(InChannel,
- SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *)&saddr_l,
- &addrsize) == 0)
- sasl_setprop(conn, SASL_IP_LOCAL,
- &saddr_l);
- }
- }
-# endif /* NETINET */
-# endif /* SASL >= 20000 */
-
- auth_type = NULL;
- mechlist = NULL;
- user = NULL;
-# if 0
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* 0 */
-
- /* set properties */
- (void) memset(&ssp, '\0', sizeof ssp);
-
- /* XXX should these be options settable via .cf ? */
- /* ssp.min_ssf = 0; is default due to memset() */
-# if STARTTLS
-# endif /* STARTTLS */
- {
- ssp.max_ssf = MaxSLBits;
- ssp.maxbufsize = MAXOUTLEN;
- }
- ssp.security_flags = SASLOpts & SASL_SEC_MASK;
- sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
-
- if (sasl_ok)
- {
- /*
- ** external security strength factor;
- ** currently we have none so zero
- */
-
-# if SASL >= 20000
- ext_ssf = 0;
- auth_id = NULL;
- sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
-# else /* SASL >= 20000 */
- ext_ssf.ssf = 0;
- ext_ssf.auth_id = NULL;
- sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
-# endif /* SASL >= 20000 */
- }
- if (sasl_ok)
- n_mechs = saslmechs(conn, &mechlist);
- }
-#endif /* SASL */
-
-#if STARTTLS
-#endif /* STARTTLS */
-
-#if MILTER
- if (smtp.sm_milterize)
- {
- char state;
-
- /* initialize mail filter connection */
- smtp.sm_milterlist = milter_init(e, &state);
- switch (state)
- {
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, rejecting commands");
- greetcode = "554";
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, temp failing commands");
- tempfail = true;
- smtp.sm_milterize = false;
- break;
- }
- }
-
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_connect(peerhostname, RealHostAddr,
- e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, rejecting commands",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- greetcode = "554";
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, temp failing commands",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- tempfail = true;
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, shutdown",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- tempfail = true;
- smtp.sm_milterize = false;
- message("421 4.7.0 %s closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
- if (response != NULL)
- sm_free(response); /* XXX */
- }
-#endif /* MILTER */
-
- /*
- ** Broken proxies and SMTP slammers
- ** push data without waiting, catch them
- */
-
- if (
-#if STARTTLS
- !smtps &&
-#endif /* STARTTLS */
- *greetcode == '2')
- {
- time_t msecs = 0;
- char **pvp;
- char pvpbuf[PSBUFSIZE];
-
- /* Ask the rulesets how long to pause */
- pvp = NULL;
- r = rscap("greet_pause", peerhostname,
- anynet_ntoa(&RealHostAddr), e,
- &pvp, pvpbuf, sizeof(pvpbuf));
- if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
- (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
- {
- msecs = strtol(pvp[1], NULL, 10);
- }
-
- if (msecs > 0)
- {
- int fd;
- fd_set readfds;
- struct timeval timeout;
-
- /* pause for a moment */
- timeout.tv_sec = msecs / 1000;
- timeout.tv_usec = (msecs % 1000) * 1000;
-
- /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
- if (timeout.tv_sec >= 300)
- {
- timeout.tv_sec = 300;
- timeout.tv_usec = 0;
- }
-
- /* check if data is on the socket during the pause */
- fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- FD_ZERO(&readfds);
- SM_FD_SET(fd, &readfds);
- if (select(fd + 1, FDSET_CAST &readfds,
- NULL, NULL, &timeout) > 0 &&
- FD_ISSET(fd, &readfds))
- {
- greetcode = "554";
- nullserver = "Command rejected";
- sm_syslog(LOG_INFO, e->e_id,
- "rejecting commands from %s [%s] due to pre-greeting traffic",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- }
- }
- }
-
-#if STARTTLS
- /* If this an smtps connection, start TLS now */
- if (smtps)
- {
- Errors = 0;
- goto starttls;
- }
-
- greeting:
-
-#endif /* STARTTLS */
-
- /* output the first line, inserting "ESMTP" as second word */
- if (*greetcode == '5')
- (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
- hostname);
- else
- expand(SmtpGreeting, inp, sizeof inp, e);
-
- p = strchr(inp, '\n');
- if (p != NULL)
- *p++ = '\0';
- id = strchr(inp, ' ');
- if (id == NULL)
- id = &inp[strlen(inp)];
- if (p == NULL)
- (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
- "%s %%.*s ESMTP%%s", greetcode);
- else
- (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
- "%s-%%.*s ESMTP%%s", greetcode);
- message(cmdbuf, (int) (id - inp), inp, id);
-
- /* output remaining lines */
- while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
- {
- *p++ = '\0';
- if (isascii(*id) && isspace(*id))
- id++;
- (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
- message(cmdbuf, id);
- }
- if (id != NULL)
- {
- if (isascii(*id) && isspace(*id))
- id++;
- (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
- message(cmdbuf, id);
- }
-
- protocol = NULL;
- sendinghost = macvalue('s', e);
-
- /* If quarantining by a connect/ehlo action, save between messages */
- if (e->e_quarmsg == NULL)
- smtp.sm_quarmsg = NULL;
- else
- smtp.sm_quarmsg = newstr(e->e_quarmsg);
-
- /* sendinghost's storage must outlive the current envelope */
- if (sendinghost != NULL)
- sendinghost = sm_strdup_x(sendinghost);
-#if _FFR_BLOCK_PROXIES
- first = true;
-#endif /* _FFR_BLOCK_PROXIES */
- gothello = false;
- smtp.sm_gotmail = false;
- for (;;)
- {
- SM_TRY
- {
- QuickAbort = false;
- HoldErrs = false;
- SuprErrs = false;
- LogUsrErrs = false;
- OnlyOneError = true;
- e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
-
- /* setup for the read */
- e->e_to = NULL;
- Errors = 0;
- FileName = NULL;
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
-
- /* read the input line */
- SmtpPhase = "server cmd read";
- sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
-#if SASL
- /*
- ** XXX SMTP AUTH requires accepting any length,
- ** at least for challenge/response
- */
-#endif /* SASL */
-
- /* handle errors */
- if (sm_io_error(OutChannel) ||
- (p = sfgets(inp, sizeof inp, InChannel,
- TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
- {
- char *d;
-
- d = macvalue(macid("{daemon_name}"), e);
- if (d == NULL)
- d = "stdin";
- /* end of file, just die */
- disconnect(1, e);
-
-#if MILTER
- /* close out milter filters */
- milter_quit(e);
-#endif /* MILTER */
-
- message("421 4.4.1 %s Lost input channel from %s",
- MyHostName, CurSmtpClient);
- if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
- sm_syslog(LOG_NOTICE, e->e_id,
- "lost input channel from %s to %s after %s",
- CurSmtpClient, d,
- (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
- /*
- ** If have not accepted mail (DATA), do not bounce
- ** bad addresses back to sender.
- */
-
- if (bitset(EF_CLRQUEUE, e->e_flags))
- e->e_sendqueue = NULL;
- goto doquit;
- }
-
-#if _FFR_BLOCK_PROXIES
- if (first)
- {
- size_t inplen, cmdlen;
- int idx;
- char *http_cmd;
- static char *http_cmds[] = { "GET", "POST",
- "CONNECT", "USER", NULL };
-
- inplen = strlen(inp);
- for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
- idx++)
- {
- cmdlen = strlen(http_cmd);
- if (cmdlen < inplen &&
- sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
- isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
- {
- /* Open proxy, drop it */
- message("421 4.7.0 %s Rejecting open proxy %s",
- MyHostName, CurSmtpClient);
- sm_syslog(LOG_INFO, e->e_id,
- "%s: probable open proxy: command=%.40s",
- CurSmtpClient, inp);
- goto doquit;
- }
- }
- first = false;
- }
-#endif /* _FFR_BLOCK_PROXIES */
-
- /* clean up end of line */
- fixcrlf(inp, true);
-
-#if PIPELINING
-# if _FFR_NO_PIPE
- /*
- ** if there is more input and pipelining is disabled:
- ** delay ... (and maybe discard the input?)
- ** XXX this doesn't really work, at least in tests using
- ** telnet SM_IO_IS_READABLE only returns 1 if there were
- ** more than 2 input lines available.
- */
-
- if (bitset(SRV_NO_PIPE, features) &&
- sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
- {
- if (++np_log < 3)
- sm_syslog(LOG_INFO, NOQID,
- "unauthorized PIPELINING, sleeping");
- sleep(1);
- }
-
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-
-#if SASL
- if (authenticating == SASL_PROC_AUTH)
- {
-# if 0
- if (*inp == '\0')
- {
- authenticating = SASL_NOT_AUTH;
- message("501 5.5.2 missing input");
- RESET_SASLCONN;
- continue;
- }
-# endif /* 0 */
- if (*inp == '*' && *(inp + 1) == '\0')
- {
- authenticating = SASL_NOT_AUTH;
-
- /* rfc 2254 4. */
- message("501 5.0.0 AUTH aborted");
- RESET_SASLCONN;
- continue;
- }
-
- /* could this be shorter? XXX */
-# if SASL >= 20000
- in = xalloc(strlen(inp) + 1);
- result = sasl_decode64(inp, strlen(inp), in,
- strlen(inp), &inlen);
-# else /* SASL >= 20000 */
- out = xalloc(strlen(inp));
- result = sasl_decode64(inp, strlen(inp), out, &outlen);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- authenticating = SASL_NOT_AUTH;
-
- /* rfc 2254 4. */
- message("501 5.5.4 cannot decode AUTH parameter %s",
- inp);
-# if SASL >= 20000
- sm_free(in);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- continue;
- }
-
-# if SASL >= 20000
- result = sasl_server_step(conn, in, inlen,
- &out, &outlen);
- sm_free(in);
-# else /* SASL >= 20000 */
- result = sasl_server_step(conn, out, outlen,
- &out, &outlen, &errstr);
-# endif /* SASL >= 20000 */
-
- /* get an OK if we're done */
- if (result == SASL_OK)
- {
- authenticated:
- message("235 2.0.0 OK Authenticated");
- authenticating = SASL_IS_AUTH;
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{auth_type}"), auth_type);
-
-# if SASL >= 20000
- user = macvalue(macid("{auth_authen}"), e);
-
- /* get security strength (features) */
- result = sasl_getprop(conn, SASL_SSF,
- (const void **) &ssf);
-# else /* SASL >= 20000 */
- result = sasl_getprop(conn, SASL_USERNAME,
- (void **)&user);
- if (result != SASL_OK)
- {
- user = "";
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{auth_authen}"), NULL);
- }
- else
- {
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{auth_authen}"),
- xtextify(user, "<>\")"));
- }
-
-# if 0
- /* get realm? */
- sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif /* 0 */
-
- /* get security strength (features) */
- result = sasl_getprop(conn, SASL_SSF,
- (void **) &ssf);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{auth_ssf}"), "0");
- ssf = NULL;
- }
- else
- {
- char pbuf[8];
-
- (void) sm_snprintf(pbuf, sizeof pbuf,
- "%u", *ssf);
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{auth_ssf}"), pbuf);
- if (tTd(95, 8))
- sm_dprintf("AUTH auth_ssf: %u\n",
- *ssf);
- }
-
- /*
- ** Only switch to encrypted connection
- ** if a security layer has been negotiated
- */
-
- if (ssf != NULL && *ssf > 0)
- {
- /*
- ** Convert I/O layer to use SASL.
- ** If the call fails, the connection
- ** is aborted.
- */
-
- if (sfdcsasl(&InChannel, &OutChannel,
- conn) == 0)
- {
- /* restart dialogue */
- n_helo = 0;
-# if PIPELINING
- (void) sm_io_autoflush(InChannel,
- OutChannel);
-# endif /* PIPELINING */
- }
- else
- syserr("503 5.3.3 SASL TLS failed");
- }
-
- /* NULL pointer ok since it's our function */
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
- CurSmtpClient,
- shortenstring(user, 128),
- auth_type, *ssf);
- }
- else if (result == SASL_CONTINUE)
- {
- len = ENC64LEN(outlen);
- out2 = xalloc(len);
- result = sasl_encode64(out, outlen, out2, len,
- &out2len);
- if (result != SASL_OK)
- {
- /* correct code? XXX */
- /* 454 Temp. authentication failure */
- message("454 4.5.4 Internal error: unable to encode64");
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH encode64 error [%d for \"%s\"]",
- result, out);
- /* start over? */
- authenticating = SASL_NOT_AUTH;
- }
- else
- {
- message("334 %s", out2);
- if (tTd(95, 2))
- sm_dprintf("AUTH continue: msg='%s' len=%u\n",
- out2, out2len);
- }
-# if SASL >= 20000
- sm_free(out2);
-# endif /* SASL >= 20000 */
- }
- else
- {
- /* not SASL_OK or SASL_CONT */
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH failure (%s): %s (%d) %s",
- auth_type,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn));
-# else /* SASL >= 20000 */
- errstr == NULL ? "" : errstr);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- authenticating = SASL_NOT_AUTH;
- }
- }
- else
- {
- /* don't want to do any of this if authenticating */
-#endif /* SASL */
-
- /* echo command to transcript */
- if (e->e_xfp != NULL)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "<<< %s\n", inp);
-
- if (LogLevel > 14)
- sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
-
- /* break off command */
- for (p = inp; isascii(*p) && isspace(*p); p++)
- continue;
- cmd = cmdbuf;
- while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)) &&
- cmd < &cmdbuf[sizeof cmdbuf - 2])
- *cmd++ = *p++;
- *cmd = '\0';
-
- /* throw away leading whitespace */
- SKIP_SPACE(p);
-
- /* decode command */
- for (c = CmdTab; c->cmd_name != NULL; c++)
- {
- if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
- break;
- }
-
- /* reset errors */
- errno = 0;
-
- /* check whether a "non-null" command has been used */
- switch (c->cmd_code)
- {
-#if SASL
- case CMDAUTH:
- /* avoid information leak; take first two words? */
- q = "AUTH";
- break;
-#endif /* SASL */
-
- case CMDMAIL:
- case CMDEXPN:
- case CMDVRFY:
- case CMDETRN:
- lognullconnection = false;
- /* FALLTHROUGH */
- default:
- q = inp;
- break;
- }
-
- if (e->e_id == NULL)
- sm_setproctitle(true, e, "%s: %.80s",
- CurSmtpClient, q);
- else
- sm_setproctitle(true, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, q);
-
- /*
- ** Process command.
- **
- ** If we are running as a null server, return 550
- ** to almost everything.
- */
-
- if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
- {
- switch (c->cmd_code)
- {
- case CMDQUIT:
- case CMDHELO:
- case CMDEHLO:
- case CMDNOOP:
- case CMDRSET:
- case CMDERROR:
- /* process normally */
- break;
-
- case CMDETRN:
- if (bitnset(D_ETRNONLY, d_flags) &&
- nullserver == NULL)
- break;
- DELAY_CONN("ETRN");
- /* FALLTHROUGH */
-
- default:
-#if MAXBADCOMMANDS > 0
- /* theoretically this could overflow */
- if (nullserver != NULL &&
- ++n_badcmds > MAXBADCOMMANDS)
- {
- message("421 4.7.0 %s Too many bad commands; closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-#endif /* MAXBADCOMMANDS > 0 */
- if (nullserver != NULL)
- {
- if (ISSMTPREPLY(nullserver))
- usrerr(nullserver);
- else
- usrerr("550 5.0.0 %s",
- nullserver);
- }
- else
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- continue;
- }
- }
-
- switch (c->cmd_code)
- {
-#if SASL
- case CMDAUTH: /* sasl */
- DELAY_CONN("AUTH");
- if (!sasl_ok || n_mechs <= 0)
- {
- message("503 5.3.3 AUTH not available");
- break;
- }
- if (authenticating == SASL_IS_AUTH)
- {
- message("503 5.5.0 Already Authenticated");
- break;
- }
- if (smtp.sm_gotmail)
- {
- message("503 5.5.0 AUTH not permitted during a mail transaction");
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr("454 4.3.0 Please try again later");
- break;
- }
-
- ismore = false;
-
- /* crude way to avoid crack attempts */
- STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
- true, "AUTH", e));
-
- /* make sure mechanism (p) is a valid string */
- for (q = p; *q != '\0' && isascii(*q); q++)
- {
- if (isspace(*q))
- {
- *q = '\0';
- while (*++q != '\0' &&
- isascii(*q) && isspace(*q))
- continue;
- *(q - 1) = '\0';
- ismore = (*q != '\0');
- break;
- }
- }
-
- if (*p == '\0')
- {
- message("501 5.5.2 AUTH mechanism must be specified");
- break;
- }
-
- /* check whether mechanism is available */
- if (iteminlist(p, mechlist, " ") == NULL)
- {
- message("504 5.3.3 AUTH mechanism %.32s not available",
- p);
- break;
- }
-
- if (ismore)
- {
- /* could this be shorter? XXX */
-# if SASL >= 20000
- in = xalloc(strlen(q) + 1);
- result = sasl_decode64(q, strlen(q), in,
- strlen(q), &inlen);
-# else /* SASL >= 20000 */
- in = sm_rpool_malloc(e->e_rpool, strlen(q));
- result = sasl_decode64(q, strlen(q), in,
- &inlen);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- message("501 5.5.4 cannot BASE64 decode '%s'",
- q);
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH decode64 error [%d for \"%s\"]",
- result, q);
- /* start over? */
- authenticating = SASL_NOT_AUTH;
-# if SASL >= 20000
- sm_free(in);
-# endif /* SASL >= 20000 */
- in = NULL;
- inlen = 0;
- break;
- }
- }
- else
- {
- in = NULL;
- inlen = 0;
- }
-
- /* see if that auth type exists */
-# if SASL >= 20000
- result = sasl_server_start(conn, p, in, inlen,
- &out, &outlen);
- if (in != NULL)
- sm_free(in);
-# else /* SASL >= 20000 */
- result = sasl_server_start(conn, p, in, inlen,
- &out, &outlen, &errstr);
-# endif /* SASL >= 20000 */
-
- if (result != SASL_OK && result != SASL_CONTINUE)
- {
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_ERR, e->e_id,
- "AUTH failure (%s): %s (%d) %s",
- p,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn));
-# else /* SASL >= 20000 */
- errstr);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- break;
- }
- auth_type = newstr(p);
-
- if (result == SASL_OK)
- {
- /* ugly, but same code */
- goto authenticated;
- /* authenticated by the initial response */
- }
-
- /* len is at least 2 */
- len = ENC64LEN(outlen);
- out2 = xalloc(len);
- result = sasl_encode64(out, outlen, out2, len,
- &out2len);
-
- if (result != SASL_OK)
- {
- message("454 4.5.4 Temporary authentication failure");
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH encode64 error [%d for \"%s\"]",
- result, out);
-
- /* start over? */
- authenticating = SASL_NOT_AUTH;
- RESET_SASLCONN;
- }
- else
- {
- message("334 %s", out2);
- authenticating = SASL_PROC_AUTH;
- }
-# if SASL >= 20000
- sm_free(out2);
-# endif /* SASL >= 20000 */
- break;
-#endif /* SASL */
-
-#if STARTTLS
- case CMDSTLS: /* starttls */
- DELAY_CONN("STARTTLS");
- if (*p != '\0')
- {
- message("501 5.5.2 Syntax error (no parameters allowed)");
- break;
- }
- if (!bitset(SRV_OFFER_TLS, features))
- {
- message("503 5.5.0 TLS not available");
- break;
- }
- if (!tls_ok_srv)
- {
- message("454 4.3.3 TLS not available after start");
- break;
- }
- if (smtp.sm_gotmail)
- {
- message("503 5.5.0 TLS not permitted during a mail transaction");
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr("454 4.7.0 Please try again later");
- break;
- }
- starttls:
-# if TLS_NO_RSA
- /*
- ** XXX do we need a temp key ?
- */
-# else /* TLS_NO_RSA */
-# endif /* TLS_NO_RSA */
-
-# if TLS_VRFY_PER_CTX
- /*
- ** Note: this sets the verification globally
- ** (per SSL_CTX)
- ** it's ok since it applies only to one transaction
- */
-
- TLS_VERIFY_CLIENT();
-# endif /* TLS_VRFY_PER_CTX */
-
- if (srv_ssl != NULL)
- SSL_clear(srv_ssl);
- else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
- {
- message("454 4.3.3 TLS not available: error generating SSL handle");
- if (LogLevel > 8)
- tlslogerr("server");
- goto tls_done;
- }
-
-# if !TLS_VRFY_PER_CTX
- /*
- ** this could be used if it were possible to set
- ** verification per SSL (connection)
- ** not just per SSL_CTX (global)
- */
-
- TLS_VERIFY_CLIENT();
-# endif /* !TLS_VRFY_PER_CTX */
-
- rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
-
- if (rfd < 0 || wfd < 0 ||
- SSL_set_rfd(srv_ssl, rfd) <= 0 ||
- SSL_set_wfd(srv_ssl, wfd) <= 0)
- {
- message("454 4.3.3 TLS not available: error set fd");
- SSL_free(srv_ssl);
- srv_ssl = NULL;
- goto tls_done;
- }
- if (!smtps)
- message("220 2.0.0 Ready to start TLS");
-# if PIPELINING
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-# endif /* PIPELINING */
-
- SSL_set_accept_state(srv_ssl);
-
-# define SSL_ACC(s) SSL_accept(s)
-
- tlsstart = curtime();
- fdfl = fcntl(rfd, F_GETFL);
- if (fdfl != -1)
- fcntl(rfd, F_SETFL, fdfl|O_NONBLOCK);
- ssl_retry:
- if ((r = SSL_ACC(srv_ssl)) <= 0)
- {
- int i;
- bool timedout;
- time_t left;
- time_t now = curtime();
- struct timeval tv;
-
- /* what to do in this case? */
- i = SSL_get_error(srv_ssl, r);
-
- /*
- ** For SSL_ERROR_WANT_{READ,WRITE}:
- ** There is no SSL record available yet
- ** or there is only a partial SSL record
- ** removed from the network (socket) buffer
- ** into the SSL buffer. The SSL_accept will
- ** only succeed when a full SSL record is
- ** available (assuming a "real" error
- ** doesn't happen). To handle when a "real"
- ** error does happen the select is set for
- ** exceptions too.
- ** The connection may be re-negotiated
- ** during this time so both read and write
- ** "want errors" need to be handled.
- ** A select() exception loops back so that
- ** a proper SSL error message can be gotten.
- */
-
- left = TimeOuts.to_starttls - (now - tlsstart);
- timedout = left <= 0;
- if (!timedout)
- {
- tv.tv_sec = left;
- tv.tv_usec = 0;
- }
-
- if (!timedout && FD_SETSIZE > 0 &&
- (rfd >= FD_SETSIZE ||
- (i == SSL_ERROR_WANT_WRITE &&
- wfd >= FD_SETSIZE)))
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=server, error: fd %d/%d too large",
- rfd, wfd);
- if (LogLevel > 8)
- tlslogerr("server");
- }
- goto tlsfail;
- }
-
- /* XXX what about SSL_pending() ? */
- if (!timedout && i == SSL_ERROR_WANT_READ)
- {
- fd_set ssl_maskr, ssl_maskx;
-
- FD_ZERO(&ssl_maskr);
- FD_SET(rfd, &ssl_maskr);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- if (select(rfd + 1, &ssl_maskr, NULL,
- &ssl_maskx, &tv) > 0)
- goto ssl_retry;
- }
- if (!timedout && i == SSL_ERROR_WANT_WRITE)
- {
- fd_set ssl_maskw, ssl_maskx;
-
- FD_ZERO(&ssl_maskw);
- FD_SET(wfd, &ssl_maskw);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- if (select(wfd + 1, NULL, &ssl_maskw,
- &ssl_maskx, &tv) > 0)
- goto ssl_retry;
- }
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d, errno=%d",
- r, i, (int) timedout, errno);
- if (LogLevel > 8)
- tlslogerr("server");
- }
-tlsfail:
- tls_ok_srv = false;
- SSL_free(srv_ssl);
- srv_ssl = NULL;
-
- /*
- ** according to the next draft of
- ** RFC 2487 the connection should be dropped
- */
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-
- if (fdfl != -1)
- fcntl(rfd, F_SETFL, fdfl);
-
- /* ignore return code for now, it's in {verify} */
- (void) tls_get_info(srv_ssl, true,
- CurSmtpClient,
- &BlankEnvelope.e_macro,
- bitset(SRV_VRFY_CLT, features));
-
- /*
- ** call Stls_client to find out whether
- ** to accept the connection from the client
- */
-
- saveQuickAbort = QuickAbort;
- saveSuprErrs = SuprErrs;
- SuprErrs = true;
- QuickAbort = false;
- if (rscheck("tls_client",
- macvalue(macid("{verify}"), e),
- "STARTTLS", e,
- RSF_RMCOMM|RSF_COUNT,
- 5, NULL, NOQID) != EX_OK ||
- Errors > 0)
- {
- extern char MsgBuf[];
-
- if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
- nullserver = newstr(MsgBuf);
- else
- nullserver = "503 5.7.0 Authentication required.";
- }
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
-
- tls_ok_srv = false; /* don't offer STARTTLS again */
- n_helo = 0;
-# if SASL
- if (sasl_ok)
- {
- int cipher_bits;
- bool verified;
- char *s, *v, *c;
-
- s = macvalue(macid("{cipher_bits}"), e);
- v = macvalue(macid("{verify}"), e);
- c = macvalue(macid("{cert_subject}"), e);
- verified = (v != NULL && strcmp(v, "OK") == 0);
- if (s != NULL && (cipher_bits = atoi(s)) > 0)
- {
-# if SASL >= 20000
- ext_ssf = cipher_bits;
- auth_id = verified ? c : NULL;
- sasl_ok = ((sasl_setprop(conn,
- SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn,
- SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
-# else /* SASL >= 20000 */
- ext_ssf.ssf = cipher_bits;
- ext_ssf.auth_id = verified ? c : NULL;
- sasl_ok = sasl_setprop(conn,
- SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
-# endif /* SASL >= 20000 */
- mechlist = NULL;
- if (sasl_ok)
- n_mechs = saslmechs(conn,
- &mechlist);
- }
- }
-# endif /* SASL */
-
- /* switch to secure connection */
- if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
- {
- tls_active = true;
-# if PIPELINING
- (void) sm_io_autoflush(InChannel, OutChannel);
-# endif /* PIPELINING */
- }
- else
- {
- /*
- ** XXX this is an internal error
- ** how to deal with it?
- ** we can't generate an error message
- ** since the other side switched to an
- ** encrypted layer, but we could not...
- ** just "hang up"?
- */
-
- nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
- syserr("STARTTLS: can't switch to encrypted layer");
- }
- tls_done:
- if (smtps)
- {
- if (tls_active)
- goto greeting;
- else
- goto doquit;
- }
- break;
-#endif /* STARTTLS */
-
- case CMDHELO: /* hello -- introduce yourself */
- case CMDEHLO: /* extended hello */
- DELAY_CONN("EHLO");
- if (c->cmd_code == CMDEHLO)
- {
- protocol = "ESMTP";
- SmtpPhase = "server EHLO";
- }
- else
- {
- protocol = "SMTP";
- SmtpPhase = "server HELO";
- }
-
- /* avoid denial-of-service */
- STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
- true, "HELO/EHLO", e));
-
-#if 0
- /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
- /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
- if (gothello)
- {
- usrerr("503 %s Duplicate HELO/EHLO",
- MyHostName);
- break;
- }
-#endif /* 0 */
-
- /* check for valid domain name (re 1123 5.2.5) */
- if (*p == '\0' && !AllowBogusHELO)
- {
- usrerr("501 %s requires domain address",
- cmdbuf);
- break;
- }
-
- /* check for long domain name (hides Received: info) */
- if (strlen(p) > MAXNAME)
- {
- usrerr("501 Invalid domain name");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "invalid domain name (too long) from %s",
- CurSmtpClient);
- break;
- }
-
- ok = true;
- for (q = p; *q != '\0'; q++)
- {
- if (!isascii(*q))
- break;
- if (isalnum(*q))
- continue;
- if (isspace(*q))
- {
- *q = '\0';
-
- /* only complain if strict check */
- ok = AllowBogusHELO;
- break;
- }
- if (strchr("[].-_#:", *q) == NULL)
- break;
- }
-
- if (*q == '\0' && ok)
- {
- q = "pleased to meet you";
- sendinghost = sm_strdup_x(p);
- hello_accept = true;
- hello_name = sm_strdup_x(p);
- macdefine(&e->e_macro, A_PERM, macid("{client_hello}"),
- hello_name);
- }
- else if (!AllowBogusHELO)
- {
- usrerr("501 Invalid domain name");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "invalid domain name (%s) from %.100s",
- p, CurSmtpClient);
- break;
- }
- else
- {
- q = "accepting invalid domain name";
- hello_accept = true;
- hello_name = sm_strdup_x(p);
- macdefine(&e->e_macro, A_PERM, macid("{client_hello}"),
- hello_name);
- }
-
- if (gothello)
- {
- CLEAR_STATE(cmdbuf);
- }
-
- if (hello_accept) {
- if (rscheck("check_hello", hello_name,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- if (MaxMessageSize > 0 &&
- (e->e_msgsize > MaxMessageSize ||
- e->e_msgsize < 0))
- {
- usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
- MaxMessageSize);
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- }
-
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_helo(p, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=%s",
- p, response);
- nullserver = newstr(response);
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=Command rejected",
- p);
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=%s",
- p, MSG_TEMPFAIL);
- tempfail = true;
- smtp.sm_milterize = false;
- break;
- }
- if (response != NULL)
- sm_free(response);
-
- /*
- ** If quarantining by a connect/ehlo action,
- ** save between messages
- */
-
- if (smtp.sm_quarmsg == NULL &&
- e->e_quarmsg != NULL)
- smtp.sm_quarmsg = newstr(e->e_quarmsg);
- }
-#endif /* MILTER */
- gothello = true;
-
- /* print HELO response message */
- if (c->cmd_code != CMDEHLO)
- {
- message("250 %s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
- break;
- }
-
- message("250-%s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
-
- /* offer ENHSC even for nullserver */
- if (nullserver != NULL)
- {
- message("250 ENHANCEDSTATUSCODES");
- break;
- }
-
- /*
- ** print EHLO features list
- **
- ** Note: If you change this list,
- ** remember to update 'helpfile'
- */
-
- message("250-ENHANCEDSTATUSCODES");
-#if PIPELINING
- if (bitset(SRV_OFFER_PIPE, features))
- message("250-PIPELINING");
-#endif /* PIPELINING */
- if (bitset(SRV_OFFER_EXPN, features))
- {
- message("250-EXPN");
- if (bitset(SRV_OFFER_VERB, features))
- message("250-VERB");
- }
-#if MIME8TO7
- message("250-8BITMIME");
-#endif /* MIME8TO7 */
- if (MaxMessageSize > 0)
- message("250-SIZE %ld", MaxMessageSize);
- else
- message("250-SIZE");
-#if DSN
- if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
- message("250-DSN");
-#endif /* DSN */
- if (bitset(SRV_OFFER_ETRN, features))
- message("250-ETRN");
-#if SASL
- if (sasl_ok && mechlist != NULL && *mechlist != '\0')
- message("250-AUTH %s", mechlist);
-#endif /* SASL */
-#if STARTTLS
- if (tls_ok_srv &&
- bitset(SRV_OFFER_TLS, features))
- message("250-STARTTLS");
-#endif /* STARTTLS */
- if (DeliverByMin > 0)
- message("250-DELIVERBY %ld",
- (long) DeliverByMin);
- else if (DeliverByMin == 0)
- message("250-DELIVERBY");
-
- /* < 0: no deliver-by */
-
- message("250 HELP");
- break;
-
- case CMDMAIL: /* mail -- designate sender */
- SmtpPhase = "server MAIL";
- DELAY_CONN("MAIL");
-
- /* check for validity of this command */
- if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
- {
- usrerr("503 5.0.0 Polite people say HELO first");
- break;
- }
- if (smtp.sm_gotmail)
- {
- usrerr("503 5.5.0 Sender already specified");
- break;
- }
-#if SASL
- if (bitset(SRV_REQ_AUTH, features) &&
- authenticating != SASL_IS_AUTH)
- {
- usrerr("530 5.7.0 Authentication required");
- break;
- }
-#endif /* SASL */
-
- p = skipword(p, "from");
- if (p == NULL)
- break;
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr(MSG_TEMPFAIL);
- break;
- }
-
- /* make sure we know who the sending host is */
- if (sendinghost == NULL)
- sendinghost = peerhostname;
-
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakSmtp, 1))
- {
- sm_heap_newgroup();
- sm_dprintf("smtp() heap group #%d\n",
- sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- if (Errors > 0)
- goto undo_no_pm;
- if (!gothello)
- {
- auth_warning(e, "%s didn't use HELO protocol",
- CurSmtpClient);
- }
-#ifdef PICKY_HELO_CHECK
- if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
- (sm_strcasecmp(peerhostname, "localhost") != 0 ||
- sm_strcasecmp(sendinghost, MyHostName) != 0))
- {
- auth_warning(e, "Host %s claimed to be %s",
- CurSmtpClient, sendinghost);
- }
-#endif /* PICKY_HELO_CHECK */
-
- if (protocol == NULL)
- protocol = "SMTP";
- macdefine(&e->e_macro, A_PERM, 'r', protocol);
- macdefine(&e->e_macro, A_PERM, 's', sendinghost);
-
- if (Errors > 0)
- goto undo_no_pm;
- smtp.sm_nrcpts = 0;
- n_badrcpts = 0;
- macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
- macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
- macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
- "0");
- e->e_flags |= EF_CLRQUEUE;
- sm_setproctitle(true, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, inp);
-
- /* do the processing */
- SM_TRY
- {
- extern char *FullName;
-
- QuickAbort = true;
- SM_FREE_CLR(FullName);
-
- /* must parse sender first */
- delimptr = NULL;
- setsender(p, e, &delimptr, ' ', false);
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- /* Successfully set e_from, allow logging */
- e->e_flags |= EF_LOGSENDER;
-
- /* put resulting triple from parseaddr() into macros */
- if (e->e_from.q_mailer != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_mailer}"),
- e->e_from.q_mailer->m_name);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_mailer}"), NULL);
- if (e->e_from.q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_host}"),
- e->e_from.q_host);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_host}"), "localhost");
- if (e->e_from.q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_addr}"),
- e->e_from.q_user);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_addr}"), NULL);
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- /* check for possible spoofing */
- if (RealUid != 0 && OpMode == MD_SMTP &&
- !wordinclass(RealUserName, 't') &&
- (!bitnset(M_LOCALMAILER,
- e->e_from.q_mailer->m_flags) ||
- strcmp(e->e_from.q_user, RealUserName) != 0))
- {
- auth_warning(e, "%s owned process doing -bs",
- RealUserName);
- }
-
- /* reset to default value */
- SevenBitInput = save_sevenbitinput;
-
- /* now parse ESMTP arguments */
- e->e_msgsize = 0;
- addr = p;
- argno = 0;
- args[argno++] = p;
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- char *equal = NULL;
-
- /* locate the beginning of the keyword */
- SKIP_SPACE(p);
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- equal = p;
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
- vp == NULL ? "<null>" : vp);
-
- mail_esmtp_args(kp, vp, e);
- if (equal != NULL)
- *equal = '=';
- args[argno++] = kp;
- if (argno >= MAXSMTPARGS - 1)
- usrerr("501 5.5.4 Too many parameters");
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- args[argno] = NULL;
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
-#if SASL
-# if _FFR_AUTH_PASSING
- /* set the default AUTH= if the sender didn't */
- if (e->e_auth_param == NULL)
- {
- /* XXX only do this for an MSA? */
- e->e_auth_param = macvalue(macid("{auth_authen}"),
- e);
- if (e->e_auth_param == NULL)
- e->e_auth_param = "<>";
-
- /*
- ** XXX should we invoke Strust_auth now?
- ** authorizing as the client that just
- ** authenticated, so we'll trust implicitly
- */
- }
-# endif /* _FFR_AUTH_PASSING */
-#endif /* SASL */
-
- /* do config file checking of the sender */
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e s");
-#if _FFR_MAIL_MACRO
- /* make the "real" sender address available */
- macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
- e->e_from.q_paddr);
-#endif /* _FFR_MAIL_MACRO */
- if (rscheck("check_mail", addr,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
-
- if (MaxMessageSize > 0 &&
- (e->e_msgsize > MaxMessageSize ||
- e->e_msgsize < 0))
- {
- usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
- MaxMessageSize);
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
-
- /*
- ** XXX always check whether there is at least one fs
- ** with enough space?
- ** However, this may not help much: the queue group
- ** selection may later on select a FS that hasn't
- ** enough space.
- */
-
- if ((NumFileSys == 1 || NumQueue == 1) &&
- !enoughdiskspace(e->e_msgsize, e)
-#if _FFR_ANY_FREE_FS
- && !filesys_free(e->e_msgsize)
-#endif /* _FFR_ANY_FREE_FS */
- )
- {
- /*
- ** We perform this test again when the
- ** queue directory is selected, in collect.
- */
-
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- LogUsrErrs = true;
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_envfrom(args, e, &state);
- MILTER_REPLY("from");
- }
-#endif /* MILTER */
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- message("250 2.1.0 Sender ok");
- smtp.sm_gotmail = true;
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** An error occurred while processing a MAIL command.
- ** Jump to the common error handling code.
- */
-
- sm_exc_free(exc);
- goto undo_no_pm;
- }
- SM_END_TRY
- break;
-
- undo_no_pm:
- e->e_flags &= ~EF_PM_NOTIFY;
- undo:
- break;
-
- case CMDRCPT: /* rcpt -- designate recipient */
- DELAY_CONN("RCPT");
- if (BadRcptThrottle > 0 &&
- n_badrcpts >= BadRcptThrottle)
- {
- if (LogLevel > 5 &&
- n_badrcpts == BadRcptThrottle)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%s: Possible SMTP RCPT flood, throttling.",
- CurSmtpClient);
-
- /* To avoid duplicated message */
- n_badrcpts++;
- }
- NBADRCPTS;
-
- /*
- ** Don't use exponential backoff for now.
- ** Some servers will open more connections
- ** and actually overload the receiver even
- ** more.
- */
-
- (void) sleep(1);
- }
- if (!smtp.sm_gotmail)
- {
- usrerr("503 5.0.0 Need MAIL before RCPT");
- break;
- }
- SmtpPhase = "server RCPT";
- SM_TRY
- {
- QuickAbort = true;
- LogUsrErrs = true;
-
- /* limit flooding of our machine */
- if (MaxRcptPerMsg > 0 &&
- smtp.sm_nrcpts >= MaxRcptPerMsg)
- {
- /* sleep(1); / * slow down? */
- usrerr("452 4.5.3 Too many recipients");
- goto rcpt_done;
- }
-
- if (e->e_sendmode != SM_DELIVER)
- e->e_flags |= EF_VRFYONLY;
-
-#if MILTER
- /*
- ** If the filter will be deleting recipients,
- ** don't expand them at RCPT time (in the call
- ** to recipient()). If they are expanded, it
- ** is impossible for removefromlist() to figure
- ** out the expanded members of the original
- ** recipient and mark them as QS_DONTSEND.
- */
-
- if (milter_can_delrcpts())
- e->e_flags |= EF_VRFYONLY;
-#endif /* MILTER */
-
- p = skipword(p, "to");
- if (p == NULL)
- goto rcpt_done;
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e r");
- a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
- e, true);
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
- if (a == NULL)
- {
- usrerr("501 5.0.0 Missing recipient");
- goto rcpt_done;
- }
-
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
-
- /* put resulting triple from parseaddr() into macros */
- if (a->q_mailer != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"),
- a->q_mailer->m_name);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- if (a->q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), a->q_host);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), "localhost");
- if (a->q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), a->q_user);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
-
- /* now parse ESMTP arguments */
- addr = p;
- argno = 0;
- args[argno++] = p;
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- char *equal = NULL;
-
- /* locate the beginning of the keyword */
- SKIP_SPACE(p);
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- equal = p;
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
- vp == NULL ? "<null>" : vp);
-
- rcpt_esmtp_args(a, kp, vp, e);
- if (equal != NULL)
- *equal = '=';
- args[argno++] = kp;
- if (argno >= MAXSMTPARGS - 1)
- usrerr("501 5.5.4 Too many parameters");
- if (Errors > 0)
- break;
- }
- args[argno] = NULL;
- if (Errors > 0)
- goto rcpt_done;
-
- /* do config file checking of the recipient */
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e r");
- if (rscheck("check_rcpt", addr,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id) != EX_OK ||
- Errors > 0)
- goto rcpt_done;
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
-
- /* If discarding, don't bother to verify user */
- if (bitset(EF_DISCARD, e->e_flags))
- a->q_state = QS_VERIFIED;
-
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_envrcpt(args, e, &state);
- MILTER_REPLY("to");
- }
-#endif /* MILTER */
-
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_notify}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
-
- /* save in recipient list after ESMTP mods */
- a = recipient(a, &e->e_sendqueue, 0, e);
- if (Errors > 0)
- goto rcpt_done;
-
- /* no errors during parsing, but might be a duplicate */
- e->e_to = a->q_paddr;
- if (!QS_IS_BADADDR(a->q_state))
- {
- if (smtp.sm_nrcpts == 0)
- initsys(e);
- message("250 2.1.5 Recipient ok%s",
- QS_IS_QUEUEUP(a->q_state) ?
- " (will queue)" : "");
- smtp.sm_nrcpts++;
- }
- else
- {
- /* punt -- should keep message in ADDRESS.... */
- usrerr("550 5.1.1 Addressee unknown");
- }
- rcpt_done:
- if (Errors > 0)
- {
- ++n_badrcpts;
- NBADRCPTS;
- }
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /* An exception occurred while processing RCPT */
- e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
- ++n_badrcpts;
- NBADRCPTS;
- }
- SM_END_TRY
- break;
-
- case CMDDATA: /* data -- text of mail */
- DELAY_CONN("DATA");
- if (!smtp_data(&smtp, e))
- goto doquit;
- break;
-
- case CMDRSET: /* rset -- reset state */
- if (tTd(94, 100))
- message("451 4.0.0 Test failure");
- else
- message("250 2.0.0 Reset state");
- CLEAR_STATE(cmdbuf);
- break;
-
- case CMDVRFY: /* vrfy -- verify address */
- case CMDEXPN: /* expn -- expand address */
- vrfy = c->cmd_code == CMDVRFY;
- DELAY_CONN(vrfy ? "VRFY" : "EXPN");
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
- vrfy ? "VRFY" : "EXPN",
- p, CurSmtpClient);
-
- /* RFC 821 doesn't allow 4xy reply code */
- usrerr("550 5.7.1 Please try again later");
- break;
- }
- wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
- false, vrfy ? "VRFY" : "EXPN", e);
- STOP_IF_ATTACK(wt);
- previous = curtime();
- if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
- (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
- {
- if (vrfy)
- message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
- else
- message("502 5.7.0 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- else if (!gothello &&
- bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
- PrivacyFlags))
- {
- usrerr("503 5.0.0 I demand that you introduce yourself first");
- break;
- }
- if (Errors > 0)
- break;
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id, "%s: %s",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- SM_TRY
- {
- QuickAbort = true;
- vrfyqueue = NULL;
- if (vrfy)
- e->e_flags |= EF_VRFYONLY;
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- {
- usrerr("501 5.5.2 Argument required");
- }
- else
- {
- /* do config file checking of the address */
- if (rscheck(vrfy ? "check_vrfy" : "check_expn",
- p, NULL, e, RSF_RMCOMM,
- 3, NULL, NOQID) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
- }
- if (wt > 0)
- {
- time_t t;
-
- t = wt - (curtime() - previous);
- if (t > 0)
- (void) sleep(t);
- }
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- if (vrfyqueue == NULL)
- {
- usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
- }
- while (vrfyqueue != NULL)
- {
- if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
- {
- vrfyqueue = vrfyqueue->q_next;
- continue;
- }
-
- /* see if there is more in the vrfy list */
- a = vrfyqueue;
- while ((a = a->q_next) != NULL &&
- (!QS_IS_UNDELIVERED(a->q_state)))
- continue;
- printvrfyaddr(vrfyqueue, a == NULL, vrfy);
- vrfyqueue = a;
- }
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** An exception occurred while processing VRFY/EXPN
- */
-
- sm_exc_free(exc);
- goto undo;
- }
- SM_END_TRY
- break;
-
- case CMDETRN: /* etrn -- force queue flush */
- DELAY_CONN("ETRN");
-
- /* Don't leak queue information via debug flags */
- if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
- (RealUid != 0 && RealUid != TrustedUid &&
- OpMode == MD_SMTP))
- {
- /* different message for MSA ? */
- message("502 5.7.0 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr(MSG_TEMPFAIL);
- break;
- }
-
- if (strlen(p) <= 0)
- {
- usrerr("500 5.5.2 Parameter required");
- break;
- }
-
- /* crude way to avoid denial-of-service attacks */
- STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
- true, "ETRN", e));
-
- /*
- ** Do config file checking of the parameter.
- ** Even though we have srv_features now, we still
- ** need this ruleset because the former is called
- ** when the connection has been established, while
- ** this ruleset is called when the command is
- ** actually issued and therefore has all information
- ** available to make a decision.
- */
-
- if (rscheck("check_etrn", p, NULL, e,
- RSF_RMCOMM, 3, NULL, NOQID) != EX_OK ||
- Errors > 0)
- break;
-
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: ETRN %s", CurSmtpClient,
- shortenstring(p, MAXSHORTSTR));
-
- id = p;
- if (*id == '#')
- {
- int i, qgrp;
-
- id++;
- qgrp = name2qid(id);
- if (!ISVALIDQGRP(qgrp))
- {
- usrerr("459 4.5.4 Queue %s unknown",
- id);
- break;
- }
- for (i = 0; i < NumQueue && Queue[i] != NULL;
- i++)
- Queue[i]->qg_nextrun = (time_t) -1;
- Queue[qgrp]->qg_nextrun = 0;
- ok = run_work_group(Queue[qgrp]->qg_wgrp,
- RWG_FORK|RWG_FORCE);
- if (ok && Errors == 0)
- message("250 2.0.0 Queuing for queue group %s started", id);
- break;
- }
-
- if (*id == '@')
- id++;
- else
- *--id = '@';
-
- new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
- if (new == NULL)
- {
- syserr("500 5.5.0 ETRN out of memory");
- break;
- }
- new->queue_match = id;
- new->queue_negate = false;
- new->queue_next = NULL;
- QueueLimitRecipient = new;
- ok = runqueue(true, false, false, true);
- sm_free(QueueLimitRecipient); /* XXX */
- QueueLimitRecipient = NULL;
- if (ok && Errors == 0)
- message("250 2.0.0 Queuing for node %s started", p);
- break;
-
- case CMDHELP: /* help -- give user info */
- DELAY_CONN("HELP");
- help(p, e);
- break;
-
- case CMDNOOP: /* noop -- do nothing */
- DELAY_CONN("NOOP");
- STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
- true, "NOOP", e));
- message("250 2.0.0 OK");
- break;
-
- case CMDQUIT: /* quit -- leave mail */
- message("221 2.0.0 %s closing connection", MyHostName);
-#if PIPELINING
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
-
- if (smtp.sm_nrcpts > 0)
- logundelrcpts(e, "aborted by sender", 9, false);
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
-
-#if STARTTLS
- /* shutdown TLS connection */
- if (tls_active)
- {
- (void) endtls(srv_ssl, "server");
- tls_active = false;
- }
-#endif /* STARTTLS */
-#if SASL
- if (authenticating == SASL_IS_AUTH)
- {
- sasl_dispose(&conn);
- authenticating = SASL_NOT_AUTH;
- /* XXX sasl_done(); this is a child */
- }
-#endif /* SASL */
-
-doquit:
- /* avoid future 050 messages */
- disconnect(1, e);
-
-#if MILTER
- /* close out milter filters */
- milter_quit(e);
-#endif /* MILTER */
-
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- if (lognullconnection && LogLevel > 5 &&
- nullserver == NULL)
- {
- char *d;
-
- d = macvalue(macid("{daemon_name}"), e);
- if (d == NULL)
- d = "stdin";
-
- /*
- ** even though this id is "bogus", it makes
- ** it simpler to "grep" related events, e.g.,
- ** timeouts for the same connection.
- */
-
- sm_syslog(LOG_INFO, e->e_id,
- "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
- CurSmtpClient, d);
- }
- if (tTd(93, 100))
- {
- /* return to handle next connection */
- return;
- }
- finis(true, true, ExitStat);
- /* NOTREACHED */
-
- case CMDVERB: /* set verbose mode */
- DELAY_CONN("VERB");
- if (!bitset(SRV_OFFER_EXPN, features) ||
- !bitset(SRV_OFFER_VERB, features))
- {
- /* this would give out the same info */
- message("502 5.7.0 Verbose unavailable");
- break;
- }
- STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
- true, "VERB", e));
- Verbose = 1;
- set_delivery_mode(SM_DELIVER, e);
- message("250 2.0.0 Verbose mode");
- break;
-
-#if SMTPDEBUG
- case CMDDBGQSHOW: /* show queues */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Send Queue=");
- printaddr(smioout, e->e_sendqueue, true);
- break;
-
- case CMDDBGDEBUG: /* set debug mode */
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
- tTflag(p);
- message("200 2.0.0 Debug set");
- break;
-
-#else /* SMTPDEBUG */
- case CMDDBGQSHOW: /* show queues */
- case CMDDBGDEBUG: /* set debug mode */
-#endif /* SMTPDEBUG */
- case CMDLOGBOGUS: /* bogus command */
- DELAY_CONN("Bogus");
- if (LogLevel > 0)
- sm_syslog(LOG_CRIT, e->e_id,
- "\"%s\" command from %s (%.100s)",
- c->cmd_name, CurSmtpClient,
- anynet_ntoa(&RealHostAddr));
- /* FALLTHROUGH */
-
- case CMDERROR: /* unknown command */
-#if MAXBADCOMMANDS > 0
- if (++n_badcmds > MAXBADCOMMANDS)
- {
- stopattack:
- message("421 4.7.0 %s Too many bad commands; closing connection",
- MyHostName);
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-#endif /* MAXBADCOMMANDS > 0 */
-
-#if MILTER && SMFI_VERSION > 2
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- if (MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "Sending \"%s\" to Milter", inp);
- response = milter_unknown(inp, e, &state);
- MILTER_REPLY("unknown");
- if (state == SMFIR_REPLYCODE ||
- state == SMFIR_REJECT ||
- state == SMFIR_TEMPFAIL)
- {
- /* MILTER_REPLY already gave an error */
- break;
- }
- }
-#endif /* MILTER && SMFI_VERSION > 2 */
-
- usrerr("500 5.5.1 Command unrecognized: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- case CMDUNIMPL:
- DELAY_CONN("Unimpl");
- usrerr("502 5.5.1 Command not implemented: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- default:
- DELAY_CONN("default");
- errno = 0;
- syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
- break;
- }
-#if SASL
- }
-#endif /* SASL */
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** The only possible exception is "E:mta.quickabort".
- ** There is nothing to do except fall through and loop.
- */
- }
- SM_END_TRY
- }
-}
-/*
-** SMTP_DATA -- implement the SMTP DATA command.
-**
-** Parameters:
-** smtp -- status of SMTP connection.
-** e -- envelope.
-**
-** Returns:
-** true iff SMTP session can continue.
-**
-** Side Effects:
-** possibly sends message.
-*/
-
-static bool
-smtp_data(smtp, e)
- SMTP_T *smtp;
- ENVELOPE *e;
-{
-#if MILTER
- bool milteraccept;
-#endif /* MILTER */
- bool aborting;
- bool doublequeue;
- ADDRESS *a;
- ENVELOPE *ee;
- char *id;
- char *oldid;
- char buf[32];
-
- SmtpPhase = "server DATA";
- if (!smtp->sm_gotmail)
- {
- usrerr("503 5.0.0 Need MAIL command");
- return true;
- }
- else if (smtp->sm_nrcpts <= 0)
- {
- usrerr("503 5.0.0 Need RCPT (recipient)");
- return true;
- }
- (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
- if (rscheck("check_data", buf, NULL, e,
- RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
- e->e_id) != EX_OK)
- return true;
-
-#if MILTER && SMFI_VERSION > 3
- if (smtp->sm_milterlist && smtp->sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
- int savelogusrerrs = LogUsrErrs;
-
- response = milter_data_cmd(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=%s",
- response);
- LogUsrErrs = false;
- }
- usrerr(response);
- if (strncmp(response, "421 ", 4) == 0)
- {
- e->e_sendqueue = NULL;
- return false;
- }
- return true;
-
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=550 5.7.1 Command rejected");
- LogUsrErrs = false;
- }
- usrerr("550 5.7.1 Command rejected");
- return true;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, discard");
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=%s",
- MSG_TEMPFAIL);
- LogUsrErrs = false;
- }
- usrerr(MSG_TEMPFAIL);
- return true;
- }
- LogUsrErrs = savelogusrerrs;
- if (response != NULL)
- sm_free(response); /* XXX */
- }
-#endif /* MILTER && SMFI_VERSION > 3 */
-
- /* put back discard bit */
- if (smtp->sm_discard)
- e->e_flags |= EF_DISCARD;
-
- /* check to see if we need to re-expand aliases */
- /* also reset QS_BADADDR on already-diagnosted addrs */
- doublequeue = false;
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (QS_IS_VERIFIED(a->q_state) &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- /* need to re-expand aliases */
- doublequeue = true;
- }
- if (QS_IS_BADADDR(a->q_state))
- {
- /* make this "go away" */
- a->q_state = QS_DONTSEND;
- }
- }
-
- /* collect the text of the message */
- SmtpPhase = "collect";
- buffer_errors();
-
- collect(InChannel, true, NULL, e, true);
-
- /* redefine message size */
- (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
-
-#if _FFR_CHECK_EOM
- /* rscheck() will set Errors or EF_DISCARD if it trips */
- (void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
- 3, NULL, e->e_id);
-#endif /* _FFR_CHECK_EOM */
-
-#if MILTER
- milteraccept = true;
- if (smtp->sm_milterlist && smtp->sm_milterize &&
- Errors <= 0 &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_data(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=%s",
- response);
- milteraccept = false;
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- milteraccept = false;
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=554 5.7.1 Command rejected");
- usrerr("554 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, discard");
- milteraccept = false;
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=%s",
- MSG_TEMPFAIL);
- milteraccept = false;
- usrerr(MSG_TEMPFAIL);
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-
- /* Milter may have changed message size */
- (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
-
- /* abort message filters that didn't get the body & log msg is OK */
- if (smtp->sm_milterlist && smtp->sm_milterize)
- {
- milter_abort(e);
- if (milteraccept && MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
- }
-
- /*
- ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
- ** milter accepted message, sync it now
- **
- ** XXX This is almost a copy of the code in collect(): put it into
- ** a function that is called from both places?
- */
-
- if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
- {
- int afd;
- SM_FILE_T *volatile df;
- char *dfname;
-
- df = e->e_dfp;
- dfname = queuename(e, DATAFL_LETTER);
- if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
- && errno != EINVAL)
- {
- int save_errno;
-
- save_errno = errno;
- if (save_errno == EEXIST)
- {
- struct stat st;
- int dfd;
-
- if (stat(dfname, &st) < 0)
- st.st_size = -1;
- errno = EEXIST;
- syserr("@collect: bfcommit(%s): already on disk, size=%ld",
- dfname, (long) st.st_size);
- dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
- if (dfd >= 0)
- dumpfd(dfd, true, true);
- }
- errno = save_errno;
- dferror(df, "bfcommit", e);
- flush_errors(true);
- finis(save_errno != EEXIST, true, ExitStat);
- }
- else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
- {
- dferror(df, "sm_io_getinfo", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (fsync(afd) < 0)
- {
- dferror(df, "fsync", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
- {
- dferror(df, "sm_io_close", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
-
- /* Now reopen the df file */
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_RDONLY, NULL);
- if (e->e_dfp == NULL)
- {
- /* we haven't acked receipt yet, so just chuck this */
- syserr("@Cannot reopen %s", dfname);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- }
-#endif /* MILTER */
-
- /* Check if quarantining stats should be updated */
- if (e->e_quarmsg != NULL)
- markstats(e, NULL, STATS_QUARANTINE);
-
- /*
- ** If a header/body check (header checks or milter)
- ** set EF_DISCARD, don't queueup the message --
- ** that would lose the EF_DISCARD bit and deliver
- ** the message.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- doublequeue = false;
-
- aborting = Errors > 0;
- if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
- (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
- !split_by_recipient(e))
- aborting = bitset(EF_FATALERRS, e->e_flags);
-
- if (aborting)
- {
- /* Log who the mail would have gone to */
- logundelrcpts(e, e->e_message, 8, false);
- flush_errors(true);
- buffer_errors();
- goto abortmessage;
- }
-
- /* from now on, we have to operate silently */
- buffer_errors();
-
-#if 0
- /*
- ** Clear message, it may contain an error from the SMTP dialogue.
- ** This error must not show up in the queue.
- ** Some error message should show up, e.g., alias database
- ** not available, but others shouldn't, e.g., from check_rcpt.
- */
-
- e->e_message = NULL;
-#endif /* 0 */
-
- /*
- ** Arrange to send to everyone.
- ** If sending to multiple people, mail back
- ** errors rather than reporting directly.
- ** In any case, don't mail back errors for
- ** anything that has happened up to
- ** now (the other end will do this).
- ** Truncate our transcript -- the mail has gotten
- ** to us successfully, and if we have
- ** to mail this back, it will be easier
- ** on the reader.
- ** Then send to everyone.
- ** Finally give a reply code. If an error has
- ** already been given, don't mail a
- ** message back.
- ** We goose error returns by clearing error bit.
- */
-
- SmtpPhase = "delivery";
- (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
- id = e->e_id;
-
-#if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-#endif /* NAMED_BIND */
-
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- /* make sure we actually do delivery */
- ee->e_flags &= ~EF_CLRQUEUE;
-
- /* from now on, operate silently */
- ee->e_errormode = EM_MAIL;
-
- if (doublequeue)
- {
- /* make sure it is in the queue */
- queueup(ee, false, true);
- }
- else
- {
- /* send to all recipients */
- sendall(ee, SM_DEFAULT);
- }
- ee->e_to = NULL;
- }
-
- /* put back id for SMTP logging in putoutmsg() */
- oldid = CurEnv->e_id;
- CurEnv->e_id = id;
-
- /* issue success message */
- message("250 2.0.0 %s Message accepted for delivery", id);
- CurEnv->e_id = oldid;
-
- /* if we just queued, poke it */
- if (doublequeue)
- {
- bool anything_to_send = false;
-
- sm_getla();
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- if (WILL_BE_QUEUED(ee->e_sendmode))
- continue;
- if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
- {
- ee->e_sendmode = SM_QUEUE;
- continue;
- }
- else if (QueueMode != QM_QUARANTINE &&
- ee->e_quarmsg != NULL)
- {
- ee->e_sendmode = SM_QUEUE;
- continue;
- }
- anything_to_send = true;
-
- /* close all the queue files */
- closexscript(ee);
- if (ee->e_dfp != NULL)
- {
- (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
- ee->e_dfp = NULL;
- }
- unlockqueue(ee);
- }
- if (anything_to_send)
- {
-#if PIPELINING
- /*
- ** XXX if we don't do this, we get 250 twice
- ** because it is also flushed in the child.
- */
-
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
- (void) doworklist(e, true, true);
- }
- }
-
- abortmessage:
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- /* clean up a bit */
- smtp->sm_gotmail = false;
-
- /*
- ** Call dropenvelope if and only if the envelope is *not*
- ** being processed by the child process forked by doworklist().
- */
-
- if (aborting || bitset(EF_DISCARD, e->e_flags))
- dropenvelope(e, true, false);
- else
- {
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- if (!doublequeue &&
- QueueMode != QM_QUARANTINE &&
- ee->e_quarmsg != NULL)
- {
- dropenvelope(ee, true, false);
- continue;
- }
- if (WILL_BE_QUEUED(ee->e_sendmode))
- dropenvelope(ee, true, false);
- }
- }
- sm_rpool_free(e->e_rpool);
-
- /*
- ** At this point, e == &MainEnvelope, but if we did splitting,
- ** then CurEnv may point to an envelope structure that was just
- ** freed with the rpool. So reset CurEnv *before* calling
- ** newenvelope.
- */
-
- CurEnv = e;
- newenvelope(e, e, sm_rpool_new_x(NULL));
- e->e_flags = BlankEnvelope.e_flags;
-
- /* restore connection quarantining */
- if (smtp->sm_quarmsg == NULL)
- {
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
- }
- else
- {
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- }
- return true;
-}
-/*
-** LOGUNDELRCPTS -- log undelivered (or all) recipients.
-**
-** Parameters:
-** e -- envelope.
-** msg -- message for Stat=
-** level -- log level.
-** all -- log all recipients.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** logs undelivered (or all) recipients
-*/
-
-void
-logundelrcpts(e, msg, level, all)
- ENVELOPE *e;
- char *msg;
- int level;
- bool all;
-{
- ADDRESS *a;
-
- if (LogLevel <= level || msg == NULL || *msg == '\0')
- return;
-
- /* Clear $h so relay= doesn't get mislogged by logdelivery() */
- macdefine(&e->e_macro, A_PERM, 'h', NULL);
-
- /* Log who the mail would have gone to */
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state) && !all)
- continue;
- e->e_to = a->q_paddr;
- logdelivery(NULL, NULL, a->q_status, msg, NULL,
- (time_t) 0, e);
- }
- e->e_to = NULL;
-}
-/*
-** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
-**
-** Parameters:
-** pcounter -- pointer to a counter for this command.
-** maxcount -- maximum value for this counter before we
-** slow down.
-** waitnow -- sleep now (in this routine)?
-** cname -- command name for logging.
-** e -- the current envelope.
-**
-** Returns:
-** time to wait,
-** STOP_ATTACK if twice as many commands as allowed and
-** MaxChildren > 0.
-**
-** Side Effects:
-** Slows down if we seem to be under attack.
-*/
-
-static time_t
-checksmtpattack(pcounter, maxcount, waitnow, cname, e)
- volatile unsigned int *pcounter;
- unsigned int maxcount;
- bool waitnow;
- char *cname;
- ENVELOPE *e;
-{
- if (maxcount <= 0) /* no limit */
- return (time_t) 0;
-
- if (++(*pcounter) >= maxcount)
- {
- unsigned int shift;
- time_t s;
-
- if (*pcounter == maxcount && LogLevel > 5)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%s: possible SMTP attack: command=%.40s, count=%u",
- CurSmtpClient, cname, *pcounter);
- }
- shift = *pcounter - maxcount;
- s = 1 << shift;
- if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
- s = MAXTIMEOUT;
-
-#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \
- ? STOP_ATTACK : (time_t) s)
-
- /* sleep at least 1 second before returning */
- (void) sleep(*pcounter / maxcount);
- s -= *pcounter / maxcount;
- if (s >= MAXTIMEOUT || s < 0)
- s = MAXTIMEOUT;
- if (waitnow && s > 0)
- {
- (void) sleep(s);
- return IS_ATTACK(0);
- }
- return IS_ATTACK(s);
- }
- return (time_t) 0;
-}
-/*
-** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
-**
-** Parameters:
-** none.
-**
-** Returns:
-** nothing.
-**
-** Side Effects:
-** may change I/O fd.
-*/
-
-static void
-setup_smtpd_io()
-{
- int inchfd, outchfd, outfd;
-
- inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
- outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
- if (outchfd != outfd)
- {
- /* arrange for debugging output to go to remote host */
- (void) dup2(outchfd, outfd);
- }
-
- /*
- ** if InChannel and OutChannel are stdin/stdout
- ** and connected to ttys
- ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
- ** then "chain" them together.
- */
-
- if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
- isatty(inchfd) && isatty(outchfd))
- {
- int inmode, outmode;
-
- inmode = fcntl(inchfd, F_GETFL, 0);
- if (inmode == -1)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "fcntl(inchfd, F_GETFL) failed: %s",
- sm_errstring(errno));
- return;
- }
- outmode = fcntl(outchfd, F_GETFL, 0);
- if (outmode == -1)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "fcntl(outchfd, F_GETFL) failed: %s",
- sm_errstring(errno));
- return;
- }
- if (bitset(O_NONBLOCK, inmode) ||
- bitset(O_NONBLOCK, outmode) ||
- fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
- return;
- outmode = fcntl(outchfd, F_GETFL, 0);
- if (outmode != -1 && bitset(O_NONBLOCK, outmode))
- {
- /* changing InChannel also changes OutChannel */
- sm_io_automode(OutChannel, InChannel);
- if (tTd(97, 4) && LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "set automode for I (%d)/O (%d) in SMTP server",
- inchfd, outchfd);
- }
-
- /* undo change of inchfd */
- (void) fcntl(inchfd, F_SETFL, inmode);
- }
-}
-/*
-** SKIPWORD -- skip a fixed word.
-**
-** Parameters:
-** p -- place to start looking.
-** w -- word to skip.
-**
-** Returns:
-** p following w.
-** NULL on error.
-**
-** Side Effects:
-** clobbers the p data area.
-*/
-
-static char *
-skipword(p, w)
- register char *volatile p;
- char *w;
-{
- register char *q;
- char *firstp = p;
-
- /* find beginning of word */
- SKIP_SPACE(p);
- q = p;
-
- /* find end of word */
- while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
- p++;
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- if (*p != ':')
- {
- syntax:
- usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
- shortenstring(firstp, MAXSHORTSTR));
- return NULL;
- }
- *p++ = '\0';
- SKIP_SPACE(p);
-
- if (*p == '\0')
- goto syntax;
-
- /* see if the input word matches desired word */
- if (sm_strcasecmp(q, w))
- goto syntax;
-
- return p;
-}
-/*
-** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
-**
-** Parameters:
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-static void
-mail_esmtp_args(kp, vp, e)
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (sm_strcasecmp(kp, "size") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 5.5.2 SIZE requires a value");
- /* NOTREACHED */
- }
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
- errno = 0;
- e->e_msgsize = strtol(vp, (char **) NULL, 10);
- if (e->e_msgsize == LONG_MAX && errno == ERANGE)
- {
- usrerr("552 5.2.3 Message size exceeds maximum value");
- /* NOTREACHED */
- }
- if (e->e_msgsize < 0)
- {
- usrerr("552 5.2.3 Message size invalid");
- /* NOTREACHED */
- }
- }
- else if (sm_strcasecmp(kp, "body") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 5.5.2 BODY requires a value");
- /* NOTREACHED */
- }
- else if (sm_strcasecmp(vp, "8bitmime") == 0)
- {
- SevenBitInput = false;
- }
- else if (sm_strcasecmp(vp, "7bit") == 0)
- {
- SevenBitInput = true;
- }
- else
- {
- usrerr("501 5.5.4 Unknown BODY type %s", vp);
- /* NOTREACHED */
- }
- e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
- }
- else if (sm_strcasecmp(kp, "envid") == 0)
- {
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 ENVID requires a value");
- /* NOTREACHED */
- }
- if (!xtextok(vp))
- {
- usrerr("501 5.5.4 Syntax error in ENVID parameter value");
- /* NOTREACHED */
- }
- if (e->e_envid != NULL)
- {
- usrerr("501 5.5.0 Duplicate ENVID parameter");
- /* NOTREACHED */
- }
- e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_envid}"), e->e_envid);
- }
- else if (sm_strcasecmp(kp, "ret") == 0)
- {
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 RET requires a value");
- /* NOTREACHED */
- }
- if (bitset(EF_RET_PARAM, e->e_flags))
- {
- usrerr("501 5.5.0 Duplicate RET parameter");
- /* NOTREACHED */
- }
- e->e_flags |= EF_RET_PARAM;
- if (sm_strcasecmp(vp, "hdrs") == 0)
- e->e_flags |= EF_NO_BODY_RETN;
- else if (sm_strcasecmp(vp, "full") != 0)
- {
- usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
- /* NOTREACHED */
- }
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
- }
-#if SASL
- else if (sm_strcasecmp(kp, "auth") == 0)
- {
- int len;
- char *q;
- char *auth_param; /* the value of the AUTH=x */
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- bool saveExitStat = ExitStat;
-
- if (vp == NULL)
- {
- usrerr("501 5.5.2 AUTH= requires a value");
- /* NOTREACHED */
- }
- if (e->e_auth_param != NULL)
- {
- usrerr("501 5.5.0 Duplicate AUTH parameter");
- /* NOTREACHED */
- }
- if ((q = strchr(vp, ' ')) != NULL)
- len = q - vp + 1;
- else
- len = strlen(vp) + 1;
- auth_param = xalloc(len);
- (void) sm_strlcpy(auth_param, vp, len);
- if (!xtextok(auth_param))
- {
- usrerr("501 5.5.4 Syntax error in AUTH parameter value");
- /* just a warning? */
- /* NOTREACHED */
- }
-
- /* XXX define this always or only if trusted? */
- macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
- auth_param);
-
- /*
- ** call Strust_auth to find out whether
- ** auth_param is acceptable (trusted)
- ** we shouldn't trust it if not authenticated
- ** (required by RFC, leave it to ruleset?)
- */
-
- SuprErrs = true;
- QuickAbort = false;
- if (strcmp(auth_param, "<>") != 0 &&
- (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
- 9, NULL, NOQID) != EX_OK || Errors > 0))
- {
- if (tTd(95, 8))
- {
- q = e->e_auth_param;
- sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
- auth_param, (q == NULL) ? "" : q);
- }
-
- /* not trusted */
- e->e_auth_param = "<>";
-# if _FFR_AUTH_PASSING
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* _FFR_AUTH_PASSING */
- }
- else
- {
- if (tTd(95, 8))
- sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
- e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
- auth_param);
- }
- sm_free(auth_param); /* XXX */
-
- /* reset values */
- Errors = 0;
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
- ExitStat = saveExitStat;
- }
-#endif /* SASL */
-#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?')
-
- /*
- ** "by" is only accepted if DeliverByMin >= 0.
- ** We maybe could add this to the list of server_features.
- */
-
- else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
- {
- char *s;
-
- if (vp == NULL)
- {
- usrerr("501 5.5.2 BY= requires a value");
- /* NOTREACHED */
- }
- errno = 0;
- e->e_deliver_by = strtol(vp, &s, 10);
- if (e->e_deliver_by == LONG_MIN ||
- e->e_deliver_by == LONG_MAX ||
- e->e_deliver_by > 999999999l ||
- e->e_deliver_by < -999999999l)
- {
- usrerr("501 5.5.2 BY=%s out of range", vp);
- /* NOTREACHED */
- }
- if (s == NULL || *s != ';')
- {
- usrerr("501 5.5.2 BY= missing ';'");
- /* NOTREACHED */
- }
- e->e_dlvr_flag = 0;
- ++s; /* XXX: spaces allowed? */
- SKIP_SPACE(s);
- switch (tolower(*s))
- {
- case 'n':
- e->e_dlvr_flag = DLVR_NOTIFY;
- break;
- case 'r':
- e->e_dlvr_flag = DLVR_RETURN;
- if (e->e_deliver_by <= 0)
- {
- usrerr("501 5.5.4 mode R requires BY time > 0");
- /* NOTREACHED */
- }
- if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
- e->e_deliver_by < DeliverByMin)
- {
- usrerr("555 5.5.2 time %ld less than %ld",
- e->e_deliver_by, (long) DeliverByMin);
- /* NOTREACHED */
- }
- break;
- default:
- usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
- /* NOTREACHED */
- }
- ++s; /* XXX: spaces allowed? */
- SKIP_SPACE(s);
- switch (tolower(*s))
- {
- case 't':
- e->e_dlvr_flag |= DLVR_TRACE;
- break;
- case '\0':
- break;
- default:
- usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
- /* NOTREACHED */
- }
-
- /* XXX: check whether more characters follow? */
- }
- else
- {
- usrerr("555 5.5.4 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
-/*
-** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
-**
-** Parameters:
-** a -- the address corresponding to the To: parameter.
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-static void
-rcpt_esmtp_args(a, kp, vp, e)
- ADDRESS *a;
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (sm_strcasecmp(kp, "notify") == 0)
- {
- char *p;
-
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 NOTIFY requires a value");
- /* NOTREACHED */
- }
- a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
- a->q_flags |= QHASNOTIFY;
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
-
- if (sm_strcasecmp(vp, "never") == 0)
- return;
- for (p = vp; p != NULL; vp = p)
- {
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- if (sm_strcasecmp(vp, "success") == 0)
- a->q_flags |= QPINGONSUCCESS;
- else if (sm_strcasecmp(vp, "failure") == 0)
- a->q_flags |= QPINGONFAILURE;
- else if (sm_strcasecmp(vp, "delay") == 0)
- a->q_flags |= QPINGONDELAY;
- else
- {
- usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY",
- vp);
- /* NOTREACHED */
- }
- }
- }
- else if (sm_strcasecmp(kp, "orcpt") == 0)
- {
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 ORCPT requires a value");
- /* NOTREACHED */
- }
- if (strchr(vp, ';') == NULL || !xtextok(vp))
- {
- usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
- /* NOTREACHED */
- }
- if (a->q_orcpt != NULL)
- {
- usrerr("501 5.5.0 Duplicate ORCPT parameter");
- /* NOTREACHED */
- }
- a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
- }
- else
- {
- usrerr("555 5.5.4 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
-/*
-** PRINTVRFYADDR -- print an entry in the verify queue
-**
-** Parameters:
-** a -- the address to print.
-** last -- set if this is the last one.
-** vrfy -- set if this is a VRFY command.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Prints the appropriate 250 codes.
-*/
-#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */
-
-static void
-printvrfyaddr(a, last, vrfy)
- register ADDRESS *a;
- bool last;
- bool vrfy;
-{
- char fmtbuf[30];
-
- if (vrfy && a->q_mailer != NULL &&
- !bitnset(M_VRFY250, a->q_mailer->m_flags))
- (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
- else
- (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
- fmtbuf[3] = last ? ' ' : '-';
- (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
- if (a->q_fullname == NULL)
- {
- if ((a->q_mailer == NULL ||
- a->q_mailer->m_addrtype == NULL ||
- sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
- strchr(a->q_user, '@') == NULL)
- (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
- sizeof fmtbuf - OFFF);
- else
- (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
- sizeof fmtbuf - OFFF);
- message(fmtbuf, a->q_user, MyHostName);
- }
- else
- {
- if ((a->q_mailer == NULL ||
- a->q_mailer->m_addrtype == NULL ||
- sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
- strchr(a->q_user, '@') == NULL)
- (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
- sizeof fmtbuf - OFFF);
- else
- (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
- sizeof fmtbuf - OFFF);
- message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
- }
-}
-
-#if SASL
-/*
-** SASLMECHS -- get list of possible AUTH mechanisms
-**
-** Parameters:
-** conn -- SASL connection info.
-** mechlist -- output parameter for list of mechanisms.
-**
-** Returns:
-** number of mechs.
-*/
-
-static int
-saslmechs(conn, mechlist)
- sasl_conn_t *conn;
- char **mechlist;
-{
- int len, num, result;
-
- /* "user" is currently unused */
-# if SASL >= 20000
- result = sasl_listmech(conn, NULL,
- "", " ", "", (const char **) mechlist,
- (unsigned int *)&len, &num);
-# else /* SASL >= 20000 */
- result = sasl_listmech(conn, "user", /* XXX */
- "", " ", "", mechlist,
- (unsigned int *)&len, (unsigned int *)&num);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH error: listmech=%d, num=%d",
- result, num);
- num = 0;
- }
- if (num > 0)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH: available mech=%s, allowed mech=%s",
- *mechlist, AuthMechanisms);
- *mechlist = intersect(AuthMechanisms, *mechlist, NULL);
- }
- else
- {
- *mechlist = NULL; /* be paranoid... */
- if (result == SASL_OK && LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH warning: no mechanisms");
- }
- return num;
-}
-
-# if SASL >= 20000
-/*
-** PROXY_POLICY -- define proxy policy for AUTH
-**
-** Parameters:
-** conn -- unused.
-** context -- unused.
-** requested_user -- authorization identity.
-** rlen -- authorization identity length.
-** auth_identity -- authentication identity.
-** alen -- authentication identity length.
-** def_realm -- default user realm.
-** urlen -- user realm length.
-** propctx -- unused.
-**
-** Returns:
-** ok?
-**
-** Side Effects:
-** sets {auth_authen} macro.
-*/
-
-int
-proxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
- def_realm, urlen, propctx)
- sasl_conn_t *conn;
- void *context;
- const char *requested_user;
- unsigned rlen;
- const char *auth_identity;
- unsigned alen;
- const char *def_realm;
- unsigned urlen;
- struct propctx *propctx;
-{
- if (auth_identity == NULL)
- return SASL_FAIL;
-
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{auth_authen}"), (char *) auth_identity);
-
- return SASL_OK;
-}
-# else /* SASL >= 20000 */
-
-/*
-** PROXY_POLICY -- define proxy policy for AUTH
-**
-** Parameters:
-** context -- unused.
-** auth_identity -- authentication identity.
-** requested_user -- authorization identity.
-** user -- allowed user (output).
-** errstr -- possible error string (output).
-**
-** Returns:
-** ok?
-*/
-
-int
-proxy_policy(context, auth_identity, requested_user, user, errstr)
- void *context;
- const char *auth_identity;
- const char *requested_user;
- const char **user;
- const char **errstr;
-{
- if (user == NULL || auth_identity == NULL)
- return SASL_FAIL;
- *user = newstr(auth_identity);
- return SASL_OK;
-}
-# endif /* SASL >= 20000 */
-#endif /* SASL */
-
-#if STARTTLS
-/*
-** INITSRVTLS -- initialize server side TLS
-**
-** Parameters:
-** tls_ok -- should tls initialization be done?
-**
-** Returns:
-** succeeded?
-**
-** Side Effects:
-** sets tls_ok_srv which is a static variable in this module.
-** Do NOT remove assignments to it!
-*/
-
-bool
-initsrvtls(tls_ok)
- bool tls_ok;
-{
- if (!tls_ok)
- return false;
-
- /* do NOT remove assignment */
- tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile,
- SrvKeyFile, CACertPath, CACertFile, DHParams);
- return tls_ok_srv;
-}
-#endif /* STARTTLS */
-/*
-** SRVFEATURES -- get features for SMTP server
-**
-** Parameters:
-** e -- envelope (should be session context).
-** clientname -- name of client.
-** features -- default features for this invocation.
-**
-** Returns:
-** server features.
-*/
-
-/* table with options: it uses just one character, how about strings? */
-static struct
-{
- char srvf_opt;
- unsigned int srvf_flag;
-} srv_feat_table[] =
-{
- { 'A', SRV_OFFER_AUTH },
- { 'B', SRV_OFFER_VERB },
- { 'C', SRV_REQ_SEC },
- { 'D', SRV_OFFER_DSN },
- { 'E', SRV_OFFER_ETRN },
- { 'L', SRV_REQ_AUTH },
-#if PIPELINING
-# if _FFR_NO_PIPE
- { 'N', SRV_NO_PIPE },
-# endif /* _FFR_NO_PIPE */
- { 'P', SRV_OFFER_PIPE },
-#endif /* PIPELINING */
- { 'R', SRV_VRFY_CLT }, /* same as V; not documented */
- { 'S', SRV_OFFER_TLS },
-/* { 'T', SRV_TMP_FAIL }, */
- { 'V', SRV_VRFY_CLT },
- { 'X', SRV_OFFER_EXPN },
-/* { 'Y', SRV_OFFER_VRFY }, */
- { '\0', SRV_NONE }
-};
-
-static unsigned int
-srvfeatures(e, clientname, features)
- ENVELOPE *e;
- char *clientname;
- unsigned int features;
-{
- int r, i, j;
- char **pvp, c, opt;
- char pvpbuf[PSBUFSIZE];
-
- pvp = NULL;
- r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
- sizeof(pvpbuf));
- if (r != EX_OK)
- return features;
- if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
- return features;
- if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
- return SRV_TMP_FAIL;
-
- /*
- ** General rule (see sendmail.h, d_flags):
- ** lower case: required/offered, upper case: Not required/available
- **
- ** Since we can change some features per daemon, we have both
- ** cases here: turn on/off a feature.
- */
-
- for (i = 1; pvp[i] != NULL; i++)
- {
- c = pvp[i][0];
- j = 0;
- for (;;)
- {
- if ((opt = srv_feat_table[j].srvf_opt) == '\0')
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "srvfeatures: unknown feature %s",
- pvp[i]);
- break;
- }
- if (c == opt)
- {
- features &= ~(srv_feat_table[j].srvf_flag);
- break;
- }
- if (c == tolower(opt))
- {
- features |= srv_feat_table[j].srvf_flag;
- break;
- }
- ++j;
- }
- }
- return features;
-}
-
-/*
-** HELP -- implement the HELP command.
-**
-** Parameters:
-** topic -- the topic we want help for.
-** e -- envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** outputs the help file to message output.
-*/
-#define HELPVSTR "#vers "
-#define HELPVERSION 2
-
-void
-help(topic, e)
- char *topic;
- ENVELOPE *e;
-{
- register SM_FILE_T *hf;
- register char *p;
- int len;
- bool noinfo;
- bool first = true;
- long sff = SFF_OPENASROOT|SFF_REGONLY;
- char buf[MAXLINE];
- char inp[MAXLINE];
- static int foundvers = -1;
- extern char Version[];
-
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
-
- if (HelpFile == NULL ||
- (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
- {
- /* no help */
- errno = 0;
- message("502 5.3.0 Sendmail %s -- HELP not implemented",
- Version);
- return;
- }
-
- if (topic == NULL || *topic == '\0')
- {
- topic = "smtp";
- noinfo = false;
- }
- else
- {
- makelower(topic);
- noinfo = true;
- }
-
- len = strlen(topic);
-
- while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
- {
- if (buf[0] == '#')
- {
- if (foundvers < 0 &&
- strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
- {
- int h;
-
- if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
- &h) == 1)
- foundvers = h;
- }
- continue;
- }
- if (strncmp(buf, topic, len) == 0)
- {
- if (first)
- {
- first = false;
-
- /* print version if no/old vers# in file */
- if (foundvers < 2 && !noinfo)
- message("214-2.0.0 This is Sendmail version %s", Version);
- }
- p = strpbrk(buf, " \t");
- if (p == NULL)
- p = buf + strlen(buf) - 1;
- else
- p++;
- fixcrlf(p, true);
- if (foundvers >= 2)
- {
- translate_dollars(p);
- expand(p, inp, sizeof inp, e);
- p = inp;
- }
- message("214-2.0.0 %s", p);
- noinfo = false;
- }
- }
-
- if (noinfo)
- message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
- else
- message("214 2.0.0 End of HELP info");
-
- if (foundvers != 0 && foundvers < HELPVERSION)
- {
- if (LogLevel > 1)
- sm_syslog(LOG_WARNING, e->e_id,
- "%s too old (require version %d)",
- HelpFile, HELPVERSION);
-
- /* avoid log next time */
- foundvers = 0;
- }
-
- (void) sm_io_close(hf, SM_TIME_DEFAULT);
-}
-
-#if SASL
-/*
-** RESET_SASLCONN -- reset SASL connection data
-**
-** Parameters:
-** conn -- SASL connection context
-** hostname -- host name
-** various connection data
-**
-** Returns:
-** SASL result
-*/
-
-static int
-reset_saslconn(sasl_conn_t ** conn, char *hostname,
-# if SASL >= 20000
- char *remoteip, char *localip,
- char *auth_id, sasl_ssf_t * ext_ssf)
-# else /* SASL >= 20000 */
- struct sockaddr_in * saddr_r, struct sockaddr_in * saddr_l,
- sasl_external_properties_t * ext_ssf)
-# endif /* SASL >= 20000 */
-{
- int result;
-
- sasl_dispose(conn);
-# if SASL >= 20000
- result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
- NULL, 0, conn);
-# elif SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
-# else /* SASL >= 20000 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
- conn);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- return result;
-
-# if SASL >= 20000
-# if NETINET || NETINET6
- if (remoteip != NULL)
- result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
- if (result != SASL_OK)
- return result;
-
- if (localip != NULL)
- result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
- if (result != SASL_OK)
- return result;
-# endif /* NETINET || NETINET6 */
-
- result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
- if (result != SASL_OK)
- return result;
-
- result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
- if (result != SASL_OK)
- return result;
-# else /* SASL >= 20000 */
-# if NETINET
- if (saddr_r != NULL)
- result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
- if (result != SASL_OK)
- return result;
-
- if (saddr_l != NULL)
- result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
- if (result != SASL_OK)
- return result;
-# endif /* NETINET */
-
- result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
- if (result != SASL_OK)
- return result;
-# endif /* SASL >= 20000 */
- return SASL_OK;
-}
-#endif /* SASL */
diff --git a/debian/patches/8.13/srvrsmtp.new b/debian/patches/8.13/srvrsmtp.new
deleted file mode 100644
index f8ac1be..0000000
--- a/debian/patches/8.13/srvrsmtp.new
+++ /dev/null
@@ -1,4702 +0,0 @@
-/*
- * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
- * All rights reserved.
- * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * By using this file, you agree to the terms and conditions set
- * forth in the LICENSE file which can be found at the top level of
- * the sendmail distribution.
- *
- */
-
-#include <sendmail.h>
-#if MILTER
-# include <libmilter/mfapi.h>
-# include <libmilter/mfdef.h>
-#endif /* MILTER */
-
-SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.900 2004/07/08 23:29:33 ca Exp $")
-
-#include <sys/time.h>
-#include <sm/fdset.h>
-
-#if SASL || STARTTLS
-# include "sfsasl.h"
-#endif /* SASL || STARTTLS */
-#if SASL
-# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1)
-static int saslmechs __P((sasl_conn_t *, char **));
-#endif /* SASL */
-#if STARTTLS
-# include <sysexits.h>
-
-static SSL_CTX *srv_ctx = NULL; /* TLS server context */
-static SSL *srv_ssl = NULL; /* per connection context */
-
-static bool tls_ok_srv = false;
-
-extern void tls_set_verify __P((SSL_CTX *, SSL *, bool));
-# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
- bitset(SRV_VRFY_CLT, features))
-#endif /* STARTTLS */
-
-/* server features */
-#define SRV_NONE 0x0000 /* none... */
-#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */
-#define SRV_VRFY_CLT 0x0002 /* request a cert */
-#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */
-#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */
-#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */
-#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */
-#define SRV_OFFER_VERB 0x0040 /* offer VERB */
-#define SRV_OFFER_DSN 0x0080 /* offer DSN */
-#if PIPELINING
-# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */
-# if _FFR_NO_PIPE
-# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-#define SRV_REQ_AUTH 0x0400 /* require AUTH */
-#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */
-#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */
-
-static unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int));
-
-#define STOP_ATTACK ((time_t) -1)
-static time_t checksmtpattack __P((volatile unsigned int *, unsigned int,
- bool, char *, ENVELOPE *));
-static void mail_esmtp_args __P((char *, char *, ENVELOPE *));
-static void printvrfyaddr __P((ADDRESS *, bool, bool));
-static void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
-static char *skipword __P((char *volatile, char *));
-static void setup_smtpd_io __P((void));
-
-#if SASL
-# if SASL >= 20000
-static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
- char *_remoteip, char *_localip,
- char *_auth_id, sasl_ssf_t *_ext_ssf));
-
-# define RESET_SASLCONN \
- result = reset_saslconn(&conn, AuthRealm, remoteip, localip, auth_id, \
- &ext_ssf); \
- if (result != SASL_OK) \
- { \
- /* This is pretty fatal */ \
- goto doquit; \
- }
-
-# else /* SASL >= 20000 */
-static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
- struct sockaddr_in *_saddr_r,
- struct sockaddr_in *_saddr_l,
- sasl_external_properties_t *_ext_ssf));
-# define RESET_SASLCONN \
- result = reset_saslconn(&conn, AuthRealm, &saddr_r, &saddr_l, &ext_ssf); \
- if (result != SASL_OK) \
- { \
- /* This is pretty fatal */ \
- goto doquit; \
- }
-
-# endif /* SASL >= 20000 */
-#endif /* SASL */
-
-extern ENVELOPE BlankEnvelope;
-
-#define NBADRCPTS \
- do \
- { \
- char buf[16]; \
- (void) sm_snprintf(buf, sizeof buf, "%d", \
- BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
- ? n_badrcpts - 1 : n_badrcpts); \
- macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
- } while (0)
-
-#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \
- (s)++
-
-/*
-** SMTP -- run the SMTP protocol.
-**
-** Parameters:
-** nullserver -- if non-NULL, rejection message for
-** (almost) all SMTP commands.
-** d_flags -- daemon flags
-** e -- the envelope.
-**
-** Returns:
-** never.
-**
-** Side Effects:
-** Reads commands from the input channel and processes them.
-*/
-
-/*
-** Notice: The smtp server doesn't have a session context like the client
-** side has (mci). Therefore some data (session oriented) is allocated
-** or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
-** This should be fixed in a successor version.
-*/
-
-struct cmd
-{
- char *cmd_name; /* command name */
- int cmd_code; /* internal code, see below */
-};
-
-/* values for cmd_code */
-#define CMDERROR 0 /* bad command */
-#define CMDMAIL 1 /* mail -- designate sender */
-#define CMDRCPT 2 /* rcpt -- designate recipient */
-#define CMDDATA 3 /* data -- send message text */
-#define CMDRSET 4 /* rset -- reset state */
-#define CMDVRFY 5 /* vrfy -- verify address */
-#define CMDEXPN 6 /* expn -- expand address */
-#define CMDNOOP 7 /* noop -- do nothing */
-#define CMDQUIT 8 /* quit -- close connection and die */
-#define CMDHELO 9 /* helo -- be polite */
-#define CMDHELP 10 /* help -- give usage info */
-#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */
-#define CMDETRN 12 /* etrn -- flush queue */
-#if SASL
-# define CMDAUTH 13 /* auth -- SASL authenticate */
-#endif /* SASL */
-#if STARTTLS
-# define CMDSTLS 14 /* STARTTLS -- start TLS session */
-#endif /* STARTTLS */
-/* non-standard commands */
-#define CMDVERB 17 /* verb -- go into verbose mode */
-/* unimplemented commands from RFC 821 */
-#define CMDUNIMPL 19 /* unimplemented rfc821 commands */
-/* use this to catch and log "door handle" attempts on your system */
-#define CMDLOGBOGUS 23 /* bogus command that should be logged */
-/* debugging-only commands, only enabled if SMTPDEBUG is defined */
-#define CMDDBGQSHOW 24 /* showq -- show send queue */
-#define CMDDBGDEBUG 25 /* debug -- set debug mode */
-
-/*
-** Note: If you change this list, remember to update 'helpfile'
-*/
-
-static struct cmd CmdTab[] =
-{
- { "mail", CMDMAIL },
- { "rcpt", CMDRCPT },
- { "data", CMDDATA },
- { "rset", CMDRSET },
- { "vrfy", CMDVRFY },
- { "expn", CMDEXPN },
- { "help", CMDHELP },
- { "noop", CMDNOOP },
- { "quit", CMDQUIT },
- { "helo", CMDHELO },
- { "ehlo", CMDEHLO },
- { "etrn", CMDETRN },
- { "verb", CMDVERB },
- { "send", CMDUNIMPL },
- { "saml", CMDUNIMPL },
- { "soml", CMDUNIMPL },
- { "turn", CMDUNIMPL },
-#if SASL
- { "auth", CMDAUTH, },
-#endif /* SASL */
-#if STARTTLS
- { "starttls", CMDSTLS, },
-#endif /* STARTTLS */
- /* remaining commands are here only to trap and log attempts to use them */
- { "showq", CMDDBGQSHOW },
- { "debug", CMDDBGDEBUG },
- { "wiz", CMDLOGBOGUS },
-
- { NULL, CMDERROR }
-};
-
-static char *CurSmtpClient; /* who's at the other end of channel */
-
-#ifndef MAXBADCOMMANDS
-# define MAXBADCOMMANDS 25 /* maximum number of bad commands */
-#endif /* ! MAXBADCOMMANDS */
-#ifndef MAXNOOPCOMMANDS
-# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */
-#endif /* ! MAXNOOPCOMMANDS */
-#ifndef MAXHELOCOMMANDS
-# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */
-#endif /* ! MAXHELOCOMMANDS */
-#ifndef MAXVRFYCOMMANDS
-# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */
-#endif /* ! MAXVRFYCOMMANDS */
-#ifndef MAXETRNCOMMANDS
-# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */
-#endif /* ! MAXETRNCOMMANDS */
-#ifndef MAXTIMEOUT
-# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */
-#endif /* ! MAXTIMEOUT */
-
-/*
-** Maximum shift value to compute timeout for bad commands.
-** This introduces an upper limit of 2^MAXSHIFT for the timeout.
-*/
-
-#ifndef MAXSHIFT
-# define MAXSHIFT 8
-#endif /* ! MAXSHIFT */
-#if MAXSHIFT > 31
- ERROR _MAXSHIFT > 31 is invalid
-#endif /* MAXSHIFT */
-
-
-#if MAXBADCOMMANDS > 0
-# define STOP_IF_ATTACK(r) do \
- { \
- if ((r) == STOP_ATTACK) \
- goto stopattack; \
- } while (0)
-
-#else /* MAXBADCOMMANDS > 0 */
-# define STOP_IF_ATTACK(r) r
-#endif /* MAXBADCOMMANDS > 0 */
-
-
-#if SM_HEAP_CHECK
-static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
- "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
-#endif /* SM_HEAP_CHECK */
-
-typedef struct
-{
- bool sm_gotmail; /* mail command received */
- unsigned int sm_nrcpts; /* number of successful RCPT commands */
- bool sm_discard;
-#if MILTER
- bool sm_milterize;
- bool sm_milterlist; /* any filters in the list? */
-#endif /* MILTER */
- char *sm_quarmsg; /* carry quarantining across messages */
-} SMTP_T;
-
-static bool smtp_data __P((SMTP_T *, ENVELOPE *));
-
-#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
-
-#if MILTER
-# define MILTER_ABORT(e) milter_abort((e))
-
-# define MILTER_REPLY(str) \
- { \
- int savelogusrerrs = LogUsrErrs; \
- \
- switch (state) \
- { \
- case SMFIR_REPLYCODE: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=%s", \
- str, addr, response); \
- LogUsrErrs = false; \
- } \
- if (strncmp(response, "421 ", 4) == 0) \
- { \
- bool tsave = QuickAbort; \
- \
- QuickAbort = false; \
- usrerr(response); \
- QuickAbort = tsave; \
- e->e_sendqueue = NULL; \
- goto doquit; \
- } \
- else \
- usrerr(response); \
- break; \
- \
- case SMFIR_REJECT: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
- str, addr); \
- LogUsrErrs = false; \
- } \
- usrerr("550 5.7.1 Command rejected"); \
- break; \
- \
- case SMFIR_DISCARD: \
- if (MilterLogLevel > 3) \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, discard", \
- str, addr); \
- e->e_flags |= EF_DISCARD; \
- break; \
- \
- case SMFIR_TEMPFAIL: \
- if (MilterLogLevel > 3) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "Milter: %s=%s, reject=%s", \
- str, addr, MSG_TEMPFAIL); \
- LogUsrErrs = false; \
- } \
- usrerr(MSG_TEMPFAIL); \
- break; \
- } \
- LogUsrErrs = savelogusrerrs; \
- if (response != NULL) \
- sm_free(response); /* XXX */ \
- }
-
-#else /* MILTER */
-# define MILTER_ABORT(e)
-#endif /* MILTER */
-
-/* clear all SMTP state (for HELO/EHLO/RSET) */
-#define CLEAR_STATE(cmd) \
-do \
-{ \
- /* abort milter filters */ \
- MILTER_ABORT(e); \
- \
- if (smtp.sm_nrcpts > 0) \
- { \
- logundelrcpts(e, cmd, 10, false); \
- smtp.sm_nrcpts = 0; \
- macdefine(&e->e_macro, A_PERM, \
- macid("{nrcpts}"), "0"); \
- } \
- \
- e->e_sendqueue = NULL; \
- e->e_flags |= EF_CLRQUEUE; \
- \
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \
- logsender(e, NULL); \
- e->e_flags &= ~EF_LOGSENDER; \
- \
- /* clean up a bit */ \
- smtp.sm_gotmail = false; \
- SuprErrs = true; \
- dropenvelope(e, true, false); \
- sm_rpool_free(e->e_rpool); \
- e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \
- CurEnv = e; \
- \
- /* put back discard bit */ \
- if (smtp.sm_discard) \
- e->e_flags |= EF_DISCARD; \
- \
- /* restore connection quarantining */ \
- if (smtp.sm_quarmsg == NULL) \
- { \
- e->e_quarmsg = NULL; \
- macdefine(&e->e_macro, A_PERM, \
- macid("{quarantine}"), ""); \
- } \
- else \
- { \
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \
- smtp.sm_quarmsg); \
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \
- e->e_quarmsg); \
- } \
-} while (0)
-
-/* sleep to flatten out connection load */
-#define MIN_DELAY_LOG 15 /* wait before logging this again */
-
-/* is it worth setting the process title for 1s? */
-#define DELAY_CONN(cmd) \
- if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \
- { \
- time_t dnow; \
- \
- sm_setproctitle(true, e, \
- "%s: %s: delaying %s: load average: %d", \
- qid_printname(e), CurSmtpClient, \
- cmd, DelayLA); \
- if (LogLevel > 8 && (dnow = curtime()) > log_delay) \
- { \
- sm_syslog(LOG_INFO, e->e_id, \
- "delaying=%s, load average=%d >= %d", \
- cmd, CurrentLA, DelayLA); \
- log_delay = dnow + MIN_DELAY_LOG; \
- } \
- (void) sleep(1); \
- sm_setproctitle(true, e, "%s %s: %.80s", \
- qid_printname(e), CurSmtpClient, inp); \
- }
-
-
-void
-smtp(nullserver, d_flags, e)
- char *volatile nullserver;
- BITMAP256 d_flags;
- register ENVELOPE *volatile e;
-{
- register char *volatile p;
- register struct cmd *volatile c = NULL;
- char *cmd;
- auto ADDRESS *vrfyqueue;
- ADDRESS *a;
- volatile bool gothello; /* helo command received */
- bool vrfy; /* set if this is a vrfy command */
- char *volatile protocol; /* sending protocol */
- char *volatile sendinghost; /* sending hostname */
- char *volatile peerhostname; /* name of SMTP peer or "localhost" */
- char *volatile hello_name; /* client_hello string */
- bool hello_accept = false; /* helo/ehlo command accepted */
- auto char *delimptr;
- char *id;
- volatile unsigned int n_badcmds = 0; /* count of bad commands */
- volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */
- volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */
- volatile unsigned int n_etrn = 0; /* count of ETRN */
- volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */
- volatile unsigned int n_helo = 0; /* count of HELO/EHLO */
- volatile int save_sevenbitinput;
- bool ok;
-#if _FFR_BLOCK_PROXIES
- volatile bool first;
-#endif /* _FFR_BLOCK_PROXIES */
- volatile bool tempfail = false;
- volatile time_t wt; /* timeout after too many commands */
- volatile time_t previous; /* time after checksmtpattack() */
- volatile bool lognullconnection = true;
- register char *q;
- SMTP_T smtp;
- char *addr;
- char *greetcode = "220";
- char *hostname; /* my hostname ($j) */
- QUEUE_CHAR *new;
- int argno;
- char *args[MAXSMTPARGS];
- char inp[MAXLINE];
- char cmdbuf[MAXLINE];
-#if SASL
- sasl_conn_t *conn;
- volatile bool sasl_ok;
- volatile unsigned int n_auth = 0; /* count of AUTH commands */
- bool ismore;
- int result;
- volatile int authenticating;
- char *user;
- char *in, *out2;
-# if SASL >= 20000
- char *auth_id;
- const char *out;
- sasl_ssf_t ext_ssf;
- char localip[60], remoteip[60];
-# else /* SASL >= 20000 */
- char *out;
- const char *errstr;
- sasl_external_properties_t ext_ssf;
- struct sockaddr_in saddr_l;
- struct sockaddr_in saddr_r;
-# endif /* SASL >= 20000 */
- sasl_security_properties_t ssp;
- sasl_ssf_t *ssf;
- unsigned int inlen, out2len;
- unsigned int outlen;
- char *volatile auth_type;
- char *mechlist;
- volatile unsigned int n_mechs;
- unsigned int len;
-#endif /* SASL */
- int r;
-#if STARTTLS
- int fdfl;
- int rfd, wfd;
- volatile bool tls_active = false;
- volatile bool smtps = bitnset(D_SMTPS, d_flags);
- bool saveQuickAbort;
- bool saveSuprErrs;
- time_t tlsstart;
-#endif /* STARTTLS */
- volatile unsigned int features;
-#if PIPELINING
-# if _FFR_NO_PIPE
- int np_log = 0;
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
- volatile time_t log_delay = (time_t) 0;
-
- save_sevenbitinput = SevenBitInput;
- smtp.sm_nrcpts = 0;
-#if MILTER
- smtp.sm_milterize = (nullserver == NULL);
- smtp.sm_milterlist = false;
-#endif /* MILTER */
-
- /* setup I/O fd correctly for the SMTP server */
- setup_smtpd_io();
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakSmtp, 1))
- {
- sm_heap_newgroup();
- sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- /* XXX the rpool should be set when e is initialized in main() */
- e->e_rpool = sm_rpool_new_x(NULL);
- e->e_macro.mac_rpool = e->e_rpool;
-
- settime(e);
- sm_getla();
- peerhostname = RealHostName;
- if (peerhostname == NULL)
- peerhostname = "localhost";
- CurHostName = peerhostname;
- CurSmtpClient = macvalue('_', e);
- if (CurSmtpClient == NULL)
- CurSmtpClient = CurHostName;
-
- /* check_relay may have set discard bit, save for later */
- smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
-
-#if PIPELINING
- /* auto-flush output when reading input */
- (void) sm_io_autoflush(InChannel, OutChannel);
-#endif /* PIPELINING */
-
- sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
-
- /* Set default features for server. */
- features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
- bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
- | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
- | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
- : (SRV_OFFER_EXPN
- | (bitset(PRIV_NOVERB, PrivacyFlags)
- ? SRV_NONE : SRV_OFFER_VERB)))
- | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
- : SRV_OFFER_DSN)
-#if SASL
- | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
- | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
- : SRV_NONE)
-#endif /* SASL */
-#if PIPELINING
- | SRV_OFFER_PIPE
-#endif /* PIPELINING */
-#if STARTTLS
- | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
- | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
- : SRV_VRFY_CLT)
-#endif /* STARTTLS */
- ;
- if (nullserver == NULL)
- {
- features = srvfeatures(e, CurSmtpClient, features);
- if (bitset(SRV_TMP_FAIL, features))
- {
- if (LogLevel > 4)
- sm_syslog(LOG_ERR, NOQID,
- "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
- CurSmtpClient);
- nullserver = "450 4.3.0 Please try again later.";
- }
- else
- {
-#if PIPELINING
-# if _FFR_NO_PIPE
- if (bitset(SRV_NO_PIPE, features))
- {
- /* for consistency */
- features &= ~SRV_OFFER_PIPE;
- }
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-#if SASL
- if (bitset(SRV_REQ_SEC, features))
- SASLOpts |= SASL_SEC_NOPLAINTEXT;
- else
- SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
-#endif /* SASL */
- }
- }
- else if (strncmp(nullserver, "421 ", 4) == 0)
- {
- message(nullserver);
- goto doquit;
- }
-
- hostname = macvalue('j', e);
-#if SASL
- if (AuthRealm == NULL)
- AuthRealm = hostname;
- sasl_ok = bitset(SRV_OFFER_AUTH, features);
- n_mechs = 0;
- authenticating = SASL_NOT_AUTH;
-
- /* SASL server new connection */
- if (sasl_ok)
- {
-# if SASL >= 20000
- result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
- NULL, 0, &conn);
-# elif SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
-# else /* SASL >= 20000 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
- &conn);
-# endif /* SASL >= 20000 */
- sasl_ok = result == SASL_OK;
- if (!sasl_ok)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH error: sasl_server_new failed=%d",
- result);
- }
- }
- if (sasl_ok)
- {
- /*
- ** SASL set properties for sasl
- ** set local/remote IP
- ** XXX Cyrus SASL v1 only supports IPv4
- **
- ** XXX where exactly are these used/required?
- ** Kerberos_v4
- */
-
-# if SASL >= 20000
-# if NETINET || NETINET6
- in = macvalue(macid("{daemon_family}"), e);
- if (in != NULL && (
-# if NETINET6
- strcmp(in, "inet6") == 0 ||
-# endif /* NETINET6 */
- strcmp(in, "inet") == 0))
- {
- SOCKADDR_LEN_T addrsize;
- SOCKADDR saddr_l;
- SOCKADDR saddr_r;
-
- addrsize = sizeof(saddr_r);
- if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_r,
- &addrsize) == 0)
- {
- if (iptostring(&saddr_r, addrsize,
- remoteip, sizeof remoteip))
- {
- sasl_setprop(conn, SASL_IPREMOTEPORT,
- remoteip);
- }
- addrsize = sizeof(saddr_l);
- if (getsockname(sm_io_getinfo(InChannel,
- SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *) &saddr_l,
- &addrsize) == 0)
- {
- if (iptostring(&saddr_l, addrsize,
- localip,
- sizeof localip))
- {
- sasl_setprop(conn,
- SASL_IPLOCALPORT,
- localip);
- }
- }
- }
- }
-# endif /* NETINET || NETINET6 */
-# else /* SASL >= 20000 */
-# if NETINET
- in = macvalue(macid("{daemon_family}"), e);
- if (in != NULL && strcmp(in, "inet") == 0)
- {
- SOCKADDR_LEN_T addrsize;
-
- addrsize = sizeof(struct sockaddr_in);
- if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *)&saddr_r,
- &addrsize) == 0)
- {
- sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
- addrsize = sizeof(struct sockaddr_in);
- if (getsockname(sm_io_getinfo(InChannel,
- SM_IO_WHAT_FD,
- NULL),
- (struct sockaddr *)&saddr_l,
- &addrsize) == 0)
- sasl_setprop(conn, SASL_IP_LOCAL,
- &saddr_l);
- }
- }
-# endif /* NETINET */
-# endif /* SASL >= 20000 */
-
- auth_type = NULL;
- mechlist = NULL;
- user = NULL;
-# if 0
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* 0 */
-
- /* set properties */
- (void) memset(&ssp, '\0', sizeof ssp);
-
- /* XXX should these be options settable via .cf ? */
- /* ssp.min_ssf = 0; is default due to memset() */
-# if STARTTLS
-# endif /* STARTTLS */
- {
- ssp.max_ssf = MaxSLBits;
- ssp.maxbufsize = MAXOUTLEN;
- }
- ssp.security_flags = SASLOpts & SASL_SEC_MASK;
- sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
-
- if (sasl_ok)
- {
- /*
- ** external security strength factor;
- ** currently we have none so zero
- */
-
-# if SASL >= 20000
- ext_ssf = 0;
- auth_id = NULL;
- sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
-# else /* SASL >= 20000 */
- ext_ssf.ssf = 0;
- ext_ssf.auth_id = NULL;
- sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
-# endif /* SASL >= 20000 */
- }
- if (sasl_ok)
- n_mechs = saslmechs(conn, &mechlist);
- }
-#endif /* SASL */
-
-#if STARTTLS
-#endif /* STARTTLS */
-
-#if MILTER
- if (smtp.sm_milterize)
- {
- char state;
-
- /* initialize mail filter connection */
- smtp.sm_milterlist = milter_init(e, &state);
- switch (state)
- {
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, rejecting commands");
- greetcode = "554";
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: initialization failed, temp failing commands");
- tempfail = true;
- smtp.sm_milterize = false;
- break;
- }
- }
-
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_connect(peerhostname, RealHostAddr,
- e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, rejecting commands",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- greetcode = "554";
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, temp failing commands",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- tempfail = true;
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_SHUTDOWN:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: connect: host=%s, addr=%s, shutdown",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- tempfail = true;
- smtp.sm_milterize = false;
- message("421 4.7.0 %s closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
- if (response != NULL)
- sm_free(response); /* XXX */
- }
-#endif /* MILTER */
-
- /*
- ** Broken proxies and SMTP slammers
- ** push data without waiting, catch them
- */
-
- if (
-#if STARTTLS
- !smtps &&
-#endif /* STARTTLS */
- *greetcode == '2')
- {
- time_t msecs = 0;
- char **pvp;
- char pvpbuf[PSBUFSIZE];
-
- /* Ask the rulesets how long to pause */
- pvp = NULL;
- r = rscap("greet_pause", peerhostname,
- anynet_ntoa(&RealHostAddr), e,
- &pvp, pvpbuf, sizeof(pvpbuf));
- if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
- (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
- {
- msecs = strtol(pvp[1], NULL, 10);
- }
-
- if (msecs > 0)
- {
- int fd;
- fd_set readfds;
- struct timeval timeout;
-
- /* pause for a moment */
- timeout.tv_sec = msecs / 1000;
- timeout.tv_usec = (msecs % 1000) * 1000;
-
- /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
- if (timeout.tv_sec >= 300)
- {
- timeout.tv_sec = 300;
- timeout.tv_usec = 0;
- }
-
- /* check if data is on the socket during the pause */
- fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- FD_ZERO(&readfds);
- SM_FD_SET(fd, &readfds);
- if (select(fd + 1, FDSET_CAST &readfds,
- NULL, NULL, &timeout) > 0 &&
- FD_ISSET(fd, &readfds))
- {
- greetcode = "554";
- nullserver = "Command rejected";
- sm_syslog(LOG_INFO, e->e_id,
- "rejecting commands from %s [%s] due to pre-greeting traffic",
- peerhostname,
- anynet_ntoa(&RealHostAddr));
- }
- }
- }
-
-#if STARTTLS
- /* If this an smtps connection, start TLS now */
- if (smtps)
- {
- Errors = 0;
- goto starttls;
- }
-
- greeting:
-
-#endif /* STARTTLS */
-
- /* output the first line, inserting "ESMTP" as second word */
- if (*greetcode == '5')
- (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
- hostname);
- else
- expand(SmtpGreeting, inp, sizeof inp, e);
-
- p = strchr(inp, '\n');
- if (p != NULL)
- *p++ = '\0';
- id = strchr(inp, ' ');
- if (id == NULL)
- id = &inp[strlen(inp)];
- if (p == NULL)
- (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
- "%s %%.*s ESMTP%%s", greetcode);
- else
- (void) sm_snprintf(cmdbuf, sizeof cmdbuf,
- "%s-%%.*s ESMTP%%s", greetcode);
- message(cmdbuf, (int) (id - inp), inp, id);
-
- /* output remaining lines */
- while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
- {
- *p++ = '\0';
- if (isascii(*id) && isspace(*id))
- id++;
- (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
- message(cmdbuf, id);
- }
- if (id != NULL)
- {
- if (isascii(*id) && isspace(*id))
- id++;
- (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
- message(cmdbuf, id);
- }
-
- protocol = NULL;
- sendinghost = macvalue('s', e);
-
- /* If quarantining by a connect/ehlo action, save between messages */
- if (e->e_quarmsg == NULL)
- smtp.sm_quarmsg = NULL;
- else
- smtp.sm_quarmsg = newstr(e->e_quarmsg);
-
- /* sendinghost's storage must outlive the current envelope */
- if (sendinghost != NULL)
- sendinghost = sm_strdup_x(sendinghost);
-#if _FFR_BLOCK_PROXIES
- first = true;
-#endif /* _FFR_BLOCK_PROXIES */
- gothello = false;
- smtp.sm_gotmail = false;
- for (;;)
- {
- SM_TRY
- {
- QuickAbort = false;
- HoldErrs = false;
- SuprErrs = false;
- LogUsrErrs = false;
- OnlyOneError = true;
- e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
-
- /* setup for the read */
- e->e_to = NULL;
- Errors = 0;
- FileName = NULL;
- (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
-
- /* read the input line */
- SmtpPhase = "server cmd read";
- sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
-#if SASL
- /*
- ** XXX SMTP AUTH requires accepting any length,
- ** at least for challenge/response
- */
-#endif /* SASL */
-
- /* handle errors */
- if (sm_io_error(OutChannel) ||
- (p = sfgets(inp, sizeof inp, InChannel,
- TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
- {
- char *d;
-
- d = macvalue(macid("{daemon_name}"), e);
- if (d == NULL)
- d = "stdin";
- /* end of file, just die */
- disconnect(1, e);
-
-#if MILTER
- /* close out milter filters */
- milter_quit(e);
-#endif /* MILTER */
-
- message("421 4.4.1 %s Lost input channel from %s",
- MyHostName, CurSmtpClient);
- if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
- sm_syslog(LOG_NOTICE, e->e_id,
- "lost input channel from %s to %s after %s",
- CurSmtpClient, d,
- (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
- /*
- ** If have not accepted mail (DATA), do not bounce
- ** bad addresses back to sender.
- */
-
- if (bitset(EF_CLRQUEUE, e->e_flags))
- e->e_sendqueue = NULL;
- goto doquit;
- }
-
-#if _FFR_BLOCK_PROXIES
- if (first)
- {
- size_t inplen, cmdlen;
- int idx;
- char *http_cmd;
- static char *http_cmds[] = { "GET", "POST",
- "CONNECT", "USER", NULL };
-
- inplen = strlen(inp);
- for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
- idx++)
- {
- cmdlen = strlen(http_cmd);
- if (cmdlen < inplen &&
- sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
- isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
- {
- /* Open proxy, drop it */
- message("421 4.7.0 %s Rejecting open proxy %s",
- MyHostName, CurSmtpClient);
- sm_syslog(LOG_INFO, e->e_id,
- "%s: probable open proxy: command=%.40s",
- CurSmtpClient, inp);
- goto doquit;
- }
- }
- first = false;
- }
-#endif /* _FFR_BLOCK_PROXIES */
-
- /* clean up end of line */
- fixcrlf(inp, true);
-
-#if PIPELINING
-# if _FFR_NO_PIPE
- /*
- ** if there is more input and pipelining is disabled:
- ** delay ... (and maybe discard the input?)
- ** XXX this doesn't really work, at least in tests using
- ** telnet SM_IO_IS_READABLE only returns 1 if there were
- ** more than 2 input lines available.
- */
-
- if (bitset(SRV_NO_PIPE, features) &&
- sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
- {
- if (++np_log < 3)
- sm_syslog(LOG_INFO, NOQID,
- "unauthorized PIPELINING, sleeping");
- sleep(1);
- }
-
-# endif /* _FFR_NO_PIPE */
-#endif /* PIPELINING */
-
-#if SASL
- if (authenticating == SASL_PROC_AUTH)
- {
-# if 0
- if (*inp == '\0')
- {
- authenticating = SASL_NOT_AUTH;
- message("501 5.5.2 missing input");
- RESET_SASLCONN;
- continue;
- }
-# endif /* 0 */
- if (*inp == '*' && *(inp + 1) == '\0')
- {
- authenticating = SASL_NOT_AUTH;
-
- /* rfc 2254 4. */
- message("501 5.0.0 AUTH aborted");
- RESET_SASLCONN;
- continue;
- }
-
- /* could this be shorter? XXX */
-# if SASL >= 20000
- in = xalloc(strlen(inp) + 1);
- result = sasl_decode64(inp, strlen(inp), in,
- strlen(inp), &inlen);
-# else /* SASL >= 20000 */
- out = xalloc(strlen(inp));
- result = sasl_decode64(inp, strlen(inp), out, &outlen);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- authenticating = SASL_NOT_AUTH;
-
- /* rfc 2254 4. */
- message("501 5.5.4 cannot decode AUTH parameter %s",
- inp);
-# if SASL >= 20000
- sm_free(in);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- continue;
- }
-
-# if SASL >= 20000
- result = sasl_server_step(conn, in, inlen,
- &out, &outlen);
- sm_free(in);
-# else /* SASL >= 20000 */
- result = sasl_server_step(conn, out, outlen,
- &out, &outlen, &errstr);
-# endif /* SASL >= 20000 */
-
- /* get an OK if we're done */
- if (result == SASL_OK)
- {
- authenticated:
- message("235 2.0.0 OK Authenticated");
- authenticating = SASL_IS_AUTH;
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{auth_type}"), auth_type);
-
-# if SASL >= 20000
- user = macvalue(macid("{auth_authen}"), e);
-
- /* get security strength (features) */
- result = sasl_getprop(conn, SASL_SSF,
- (const void **) &ssf);
-# else /* SASL >= 20000 */
- result = sasl_getprop(conn, SASL_USERNAME,
- (void **)&user);
- if (result != SASL_OK)
- {
- user = "";
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{auth_authen}"), NULL);
- }
- else
- {
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{auth_authen}"),
- xtextify(user, "<>\")"));
- }
-
-# if 0
- /* get realm? */
- sasl_getprop(conn, SASL_REALM, (void **) &data);
-# endif /* 0 */
-
- /* get security strength (features) */
- result = sasl_getprop(conn, SASL_SSF,
- (void **) &ssf);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- macdefine(&BlankEnvelope.e_macro,
- A_PERM,
- macid("{auth_ssf}"), "0");
- ssf = NULL;
- }
- else
- {
- char pbuf[8];
-
- (void) sm_snprintf(pbuf, sizeof pbuf,
- "%u", *ssf);
- macdefine(&BlankEnvelope.e_macro,
- A_TEMP,
- macid("{auth_ssf}"), pbuf);
- if (tTd(95, 8))
- sm_dprintf("AUTH auth_ssf: %u\n",
- *ssf);
- }
-
- /*
- ** Only switch to encrypted connection
- ** if a security layer has been negotiated
- */
-
- if (ssf != NULL && *ssf > 0)
- {
- /*
- ** Convert I/O layer to use SASL.
- ** If the call fails, the connection
- ** is aborted.
- */
-
- if (sfdcsasl(&InChannel, &OutChannel,
- conn) == 0)
- {
- /* restart dialogue */
- n_helo = 0;
-# if PIPELINING
- (void) sm_io_autoflush(InChannel,
- OutChannel);
-# endif /* PIPELINING */
- }
- else
- syserr("503 5.3.3 SASL TLS failed");
- }
-
- /* NULL pointer ok since it's our function */
- if (LogLevel > 8)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
- CurSmtpClient,
- shortenstring(user, 128),
- auth_type, *ssf);
- }
- else if (result == SASL_CONTINUE)
- {
- len = ENC64LEN(outlen);
- out2 = xalloc(len);
- result = sasl_encode64(out, outlen, out2, len,
- &out2len);
- if (result != SASL_OK)
- {
- /* correct code? XXX */
- /* 454 Temp. authentication failure */
- message("454 4.5.4 Internal error: unable to encode64");
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH encode64 error [%d for \"%s\"]",
- result, out);
- /* start over? */
- authenticating = SASL_NOT_AUTH;
- }
- else
- {
- message("334 %s", out2);
- if (tTd(95, 2))
- sm_dprintf("AUTH continue: msg='%s' len=%u\n",
- out2, out2len);
- }
-# if SASL >= 20000
- sm_free(out2);
-# endif /* SASL >= 20000 */
- }
- else
- {
- /* not SASL_OK or SASL_CONT */
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH failure (%s): %s (%d) %s",
- auth_type,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn));
-# else /* SASL >= 20000 */
- errstr == NULL ? "" : errstr);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- authenticating = SASL_NOT_AUTH;
- }
- }
- else
- {
- /* don't want to do any of this if authenticating */
-#endif /* SASL */
-
- /* echo command to transcript */
- if (e->e_xfp != NULL)
- (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
- "<<< %s\n", inp);
-
- if (LogLevel > 14)
- sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
-
- /* break off command */
- for (p = inp; isascii(*p) && isspace(*p); p++)
- continue;
- cmd = cmdbuf;
- while (*p != '\0' &&
- !(isascii(*p) && isspace(*p)) &&
- cmd < &cmdbuf[sizeof cmdbuf - 2])
- *cmd++ = *p++;
- *cmd = '\0';
-
- /* throw away leading whitespace */
- SKIP_SPACE(p);
-
- /* decode command */
- for (c = CmdTab; c->cmd_name != NULL; c++)
- {
- if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
- break;
- }
-
- /* reset errors */
- errno = 0;
-
- /* check whether a "non-null" command has been used */
- switch (c->cmd_code)
- {
-#if SASL
- case CMDAUTH:
- /* avoid information leak; take first two words? */
- q = "AUTH";
- break;
-#endif /* SASL */
-
- case CMDMAIL:
- case CMDEXPN:
- case CMDVRFY:
- case CMDETRN:
- lognullconnection = false;
- /* FALLTHROUGH */
- default:
- q = inp;
- break;
- }
-
- if (e->e_id == NULL)
- sm_setproctitle(true, e, "%s: %.80s",
- CurSmtpClient, q);
- else
- sm_setproctitle(true, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, q);
-
- /*
- ** Process command.
- **
- ** If we are running as a null server, return 550
- ** to almost everything.
- */
-
- if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
- {
- switch (c->cmd_code)
- {
- case CMDQUIT:
- case CMDHELO:
- case CMDEHLO:
- case CMDNOOP:
- case CMDRSET:
- case CMDERROR:
- /* process normally */
- break;
-
- case CMDETRN:
- if (bitnset(D_ETRNONLY, d_flags) &&
- nullserver == NULL)
- break;
- DELAY_CONN("ETRN");
- /* FALLTHROUGH */
-
- default:
-#if MAXBADCOMMANDS > 0
- /* theoretically this could overflow */
- if (nullserver != NULL &&
- ++n_badcmds > MAXBADCOMMANDS)
- {
- message("421 4.7.0 %s Too many bad commands; closing connection",
- MyHostName);
-
- /* arrange to ignore send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-#endif /* MAXBADCOMMANDS > 0 */
- if (nullserver != NULL)
- {
- if (ISSMTPREPLY(nullserver))
- usrerr(nullserver);
- else
- usrerr("550 5.0.0 %s",
- nullserver);
- }
- else
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- continue;
- }
- }
-
- switch (c->cmd_code)
- {
-#if SASL
- case CMDAUTH: /* sasl */
- DELAY_CONN("AUTH");
- if (!sasl_ok || n_mechs <= 0)
- {
- message("503 5.3.3 AUTH not available");
- break;
- }
- if (authenticating == SASL_IS_AUTH)
- {
- message("503 5.5.0 Already Authenticated");
- break;
- }
- if (smtp.sm_gotmail)
- {
- message("503 5.5.0 AUTH not permitted during a mail transaction");
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr("454 4.3.0 Please try again later");
- break;
- }
-
- ismore = false;
-
- /* crude way to avoid crack attempts */
- STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
- true, "AUTH", e));
-
- /* make sure mechanism (p) is a valid string */
- for (q = p; *q != '\0' && isascii(*q); q++)
- {
- if (isspace(*q))
- {
- *q = '\0';
- while (*++q != '\0' &&
- isascii(*q) && isspace(*q))
- continue;
- *(q - 1) = '\0';
- ismore = (*q != '\0');
- break;
- }
- }
-
- if (*p == '\0')
- {
- message("501 5.5.2 AUTH mechanism must be specified");
- break;
- }
-
- /* check whether mechanism is available */
- if (iteminlist(p, mechlist, " ") == NULL)
- {
- message("504 5.3.3 AUTH mechanism %.32s not available",
- p);
- break;
- }
-
- if (ismore)
- {
- /* could this be shorter? XXX */
-# if SASL >= 20000
- in = xalloc(strlen(q) + 1);
- result = sasl_decode64(q, strlen(q), in,
- strlen(q), &inlen);
-# else /* SASL >= 20000 */
- in = sm_rpool_malloc(e->e_rpool, strlen(q));
- result = sasl_decode64(q, strlen(q), in,
- &inlen);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- message("501 5.5.4 cannot BASE64 decode '%s'",
- q);
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH decode64 error [%d for \"%s\"]",
- result, q);
- /* start over? */
- authenticating = SASL_NOT_AUTH;
-# if SASL >= 20000
- sm_free(in);
-# endif /* SASL >= 20000 */
- in = NULL;
- inlen = 0;
- break;
- }
- }
- else
- {
- in = NULL;
- inlen = 0;
- }
-
- /* see if that auth type exists */
-# if SASL >= 20000
- result = sasl_server_start(conn, p, in, inlen,
- &out, &outlen);
- if (in != NULL)
- sm_free(in);
-# else /* SASL >= 20000 */
- result = sasl_server_start(conn, p, in, inlen,
- &out, &outlen, &errstr);
-# endif /* SASL >= 20000 */
-
- if (result != SASL_OK && result != SASL_CONTINUE)
- {
- message("535 5.7.0 authentication failed");
- if (LogLevel > 9)
- sm_syslog(LOG_ERR, e->e_id,
- "AUTH failure (%s): %s (%d) %s",
- p,
- sasl_errstring(result, NULL,
- NULL),
- result,
-# if SASL >= 20000
- sasl_errdetail(conn));
-# else /* SASL >= 20000 */
- errstr);
-# endif /* SASL >= 20000 */
- RESET_SASLCONN;
- break;
- }
- auth_type = newstr(p);
-
- if (result == SASL_OK)
- {
- /* ugly, but same code */
- goto authenticated;
- /* authenticated by the initial response */
- }
-
- /* len is at least 2 */
- len = ENC64LEN(outlen);
- out2 = xalloc(len);
- result = sasl_encode64(out, outlen, out2, len,
- &out2len);
-
- if (result != SASL_OK)
- {
- message("454 4.5.4 Temporary authentication failure");
- if (LogLevel > 5)
- sm_syslog(LOG_WARNING, e->e_id,
- "AUTH encode64 error [%d for \"%s\"]",
- result, out);
-
- /* start over? */
- authenticating = SASL_NOT_AUTH;
- RESET_SASLCONN;
- }
- else
- {
- message("334 %s", out2);
- authenticating = SASL_PROC_AUTH;
- }
-# if SASL >= 20000
- sm_free(out2);
-# endif /* SASL >= 20000 */
- break;
-#endif /* SASL */
-
-#if STARTTLS
- case CMDSTLS: /* starttls */
- DELAY_CONN("STARTTLS");
- if (*p != '\0')
- {
- message("501 5.5.2 Syntax error (no parameters allowed)");
- break;
- }
- if (!bitset(SRV_OFFER_TLS, features))
- {
- message("503 5.5.0 TLS not available");
- break;
- }
- if (!tls_ok_srv)
- {
- message("454 4.3.3 TLS not available after start");
- break;
- }
- if (smtp.sm_gotmail)
- {
- message("503 5.5.0 TLS not permitted during a mail transaction");
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr("454 4.7.0 Please try again later");
- break;
- }
- starttls:
-# if TLS_NO_RSA
- /*
- ** XXX do we need a temp key ?
- */
-# else /* TLS_NO_RSA */
-# endif /* TLS_NO_RSA */
-
-# if TLS_VRFY_PER_CTX
- /*
- ** Note: this sets the verification globally
- ** (per SSL_CTX)
- ** it's ok since it applies only to one transaction
- */
-
- TLS_VERIFY_CLIENT();
-# endif /* TLS_VRFY_PER_CTX */
-
- if (srv_ssl != NULL)
- SSL_clear(srv_ssl);
- else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
- {
- message("454 4.3.3 TLS not available: error generating SSL handle");
- if (LogLevel > 8)
- tlslogerr("server");
- goto tls_done;
- }
-
-# if !TLS_VRFY_PER_CTX
- /*
- ** this could be used if it were possible to set
- ** verification per SSL (connection)
- ** not just per SSL_CTX (global)
- */
-
- TLS_VERIFY_CLIENT();
-# endif /* !TLS_VRFY_PER_CTX */
-
- rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
-
- if (rfd < 0 || wfd < 0 ||
- SSL_set_rfd(srv_ssl, rfd) <= 0 ||
- SSL_set_wfd(srv_ssl, wfd) <= 0)
- {
- message("454 4.3.3 TLS not available: error set fd");
- SSL_free(srv_ssl);
- srv_ssl = NULL;
- goto tls_done;
- }
- if (!smtps)
- message("220 2.0.0 Ready to start TLS");
-# if PIPELINING
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-# endif /* PIPELINING */
-
- SSL_set_accept_state(srv_ssl);
-
-# define SSL_ACC(s) SSL_accept(s)
-
- tlsstart = curtime();
- fdfl = fcntl(rfd, F_GETFL);
- if (fdfl != -1)
- fcntl(rfd, F_SETFL, fdfl|O_NONBLOCK);
- ssl_retry:
- if ((r = SSL_ACC(srv_ssl)) <= 0)
- {
- int i;
- bool timedout;
- time_t left;
- time_t now = curtime();
- struct timeval tv;
-
- /* what to do in this case? */
- i = SSL_get_error(srv_ssl, r);
-
- /*
- ** For SSL_ERROR_WANT_{READ,WRITE}:
- ** There is no SSL record available yet
- ** or there is only a partial SSL record
- ** removed from the network (socket) buffer
- ** into the SSL buffer. The SSL_accept will
- ** only succeed when a full SSL record is
- ** available (assuming a "real" error
- ** doesn't happen). To handle when a "real"
- ** error does happen the select is set for
- ** exceptions too.
- ** The connection may be re-negotiated
- ** during this time so both read and write
- ** "want errors" need to be handled.
- ** A select() exception loops back so that
- ** a proper SSL error message can be gotten.
- */
-
- left = TimeOuts.to_starttls - (now - tlsstart);
- timedout = left <= 0;
- if (!timedout)
- {
- tv.tv_sec = left;
- tv.tv_usec = 0;
- }
-
- if (!timedout && FD_SETSIZE > 0 &&
- (rfd >= FD_SETSIZE ||
- (i == SSL_ERROR_WANT_WRITE &&
- wfd >= FD_SETSIZE)))
- {
- if (LogLevel > 5)
- {
- sm_syslog(LOG_ERR, NOQID,
- "STARTTLS=server, error: fd %d/%d too large",
- rfd, wfd);
- if (LogLevel > 8)
- tlslogerr("server");
- }
- goto tlsfail;
- }
-
- /* XXX what about SSL_pending() ? */
- if (!timedout && i == SSL_ERROR_WANT_READ)
- {
- fd_set ssl_maskr, ssl_maskx;
-
- FD_ZERO(&ssl_maskr);
- FD_SET(rfd, &ssl_maskr);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- if (select(rfd + 1, &ssl_maskr, NULL,
- &ssl_maskx, &tv) > 0)
- goto ssl_retry;
- }
- if (!timedout && i == SSL_ERROR_WANT_WRITE)
- {
- fd_set ssl_maskw, ssl_maskx;
-
- FD_ZERO(&ssl_maskw);
- FD_SET(wfd, &ssl_maskw);
- FD_ZERO(&ssl_maskx);
- FD_SET(rfd, &ssl_maskx);
- if (select(wfd + 1, NULL, &ssl_maskw,
- &ssl_maskx, &tv) > 0)
- goto ssl_retry;
- }
- if (LogLevel > 5)
- {
- sm_syslog(LOG_WARNING, NOQID,
- "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d, errno=%d",
- r, i, (int) timedout, errno);
- if (LogLevel > 8)
- tlslogerr("server");
- }
-tlsfail:
- tls_ok_srv = false;
- SSL_free(srv_ssl);
- srv_ssl = NULL;
-
- /*
- ** according to the next draft of
- ** RFC 2487 the connection should be dropped
- */
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-
- if (fdfl != -1)
- fcntl(rfd, F_SETFL, fdfl);
-
- /* ignore return code for now, it's in {verify} */
- (void) tls_get_info(srv_ssl, true,
- CurSmtpClient,
- &BlankEnvelope.e_macro,
- bitset(SRV_VRFY_CLT, features));
-
- /*
- ** call Stls_client to find out whether
- ** to accept the connection from the client
- */
-
- saveQuickAbort = QuickAbort;
- saveSuprErrs = SuprErrs;
- SuprErrs = true;
- QuickAbort = false;
- if (rscheck("tls_client",
- macvalue(macid("{verify}"), e),
- "STARTTLS", e,
- RSF_RMCOMM|RSF_COUNT,
- 5, NULL, NOQID) != EX_OK ||
- Errors > 0)
- {
- extern char MsgBuf[];
-
- if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
- nullserver = newstr(MsgBuf);
- else
- nullserver = "503 5.7.0 Authentication required.";
- }
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
-
- tls_ok_srv = false; /* don't offer STARTTLS again */
- n_helo = 0;
-# if SASL
- if (sasl_ok)
- {
- int cipher_bits;
- bool verified;
- char *s, *v, *c;
-
- s = macvalue(macid("{cipher_bits}"), e);
- v = macvalue(macid("{verify}"), e);
- c = macvalue(macid("{cert_subject}"), e);
- verified = (v != NULL && strcmp(v, "OK") == 0);
- if (s != NULL && (cipher_bits = atoi(s)) > 0)
- {
-# if SASL >= 20000
- ext_ssf = cipher_bits;
- auth_id = verified ? c : NULL;
- sasl_ok = ((sasl_setprop(conn,
- SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK) &&
- (sasl_setprop(conn,
- SASL_AUTH_EXTERNAL,
- auth_id) == SASL_OK));
-# else /* SASL >= 20000 */
- ext_ssf.ssf = cipher_bits;
- ext_ssf.auth_id = verified ? c : NULL;
- sasl_ok = sasl_setprop(conn,
- SASL_SSF_EXTERNAL,
- &ext_ssf) == SASL_OK;
-# endif /* SASL >= 20000 */
- mechlist = NULL;
- if (sasl_ok)
- n_mechs = saslmechs(conn,
- &mechlist);
- }
- }
-# endif /* SASL */
-
- /* switch to secure connection */
- if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
- {
- tls_active = true;
-# if PIPELINING
- (void) sm_io_autoflush(InChannel, OutChannel);
-# endif /* PIPELINING */
- }
- else
- {
- /*
- ** XXX this is an internal error
- ** how to deal with it?
- ** we can't generate an error message
- ** since the other side switched to an
- ** encrypted layer, but we could not...
- ** just "hang up"?
- */
-
- nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
- syserr("STARTTLS: can't switch to encrypted layer");
- }
- tls_done:
- if (smtps)
- {
- if (tls_active)
- goto greeting;
- else
- goto doquit;
- }
- break;
-#endif /* STARTTLS */
-
- case CMDHELO: /* hello -- introduce yourself */
- case CMDEHLO: /* extended hello */
- DELAY_CONN("EHLO");
- if (c->cmd_code == CMDEHLO)
- {
- protocol = "ESMTP";
- SmtpPhase = "server EHLO";
- }
- else
- {
- protocol = "SMTP";
- SmtpPhase = "server HELO";
- }
-
- /* avoid denial-of-service */
- STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
- true, "HELO/EHLO", e));
-
-#if 0
- /* RFC2821 4.1.4 allows duplicate HELO/EHLO */
- /* check for duplicate HELO/EHLO per RFC 1651 4.2 */
- if (gothello)
- {
- usrerr("503 %s Duplicate HELO/EHLO",
- MyHostName);
- break;
- }
-#endif /* 0 */
-
- /* check for valid domain name (re 1123 5.2.5) */
- if (*p == '\0' && !AllowBogusHELO)
- {
- usrerr("501 %s requires domain address",
- cmdbuf);
- break;
- }
-
- /* check for long domain name (hides Received: info) */
- if (strlen(p) > MAXNAME)
- {
- usrerr("501 Invalid domain name");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "invalid domain name (too long) from %s",
- CurSmtpClient);
- break;
- }
-
- ok = true;
- for (q = p; *q != '\0'; q++)
- {
- if (!isascii(*q))
- break;
- if (isalnum(*q))
- continue;
- if (isspace(*q))
- {
- *q = '\0';
-
- /* only complain if strict check */
- ok = AllowBogusHELO;
- break;
- }
- if (strchr("[].-_#:", *q) == NULL)
- break;
- }
-
- if (*q == '\0' && ok)
- {
- q = "pleased to meet you";
- sendinghost = sm_strdup_x(p);
- hello_accept = true;
- hello_name = sm_strdup_x(p);
- macdefine(&e->e_macro, A_PERM, macid("{client_hello}"),
- hello_name);
- }
- else if (!AllowBogusHELO)
- {
- usrerr("501 Invalid domain name");
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, CurEnv->e_id,
- "invalid domain name (%s) from %.100s",
- p, CurSmtpClient);
- break;
- }
- else
- {
- q = "accepting invalid domain name";
- hello_accept = true;
- hello_name = sm_strdup_x(p);
- macdefine(&e->e_macro, A_PERM, macid("{client_hello}"),
- hello_name);
- }
-
- if (gothello)
- {
- CLEAR_STATE(cmdbuf);
- }
-
- if (hello_accept) {
- if (rscheck("check_hello", hello_name,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- if (MaxMessageSize > 0 &&
- (e->e_msgsize > MaxMessageSize ||
- e->e_msgsize < 0))
- {
- usrerr("552 5.2.3 Message size exceeds fixed maximum message siz
- MaxMessageSize);
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- };
-
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_helo(p, e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=%s",
- p, response);
- nullserver = newstr(response);
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=Command rejected",
- p);
- nullserver = "Command rejected";
- smtp.sm_milterize = false;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: helo=%s, reject=%s",
- p, MSG_TEMPFAIL);
- tempfail = true;
- smtp.sm_milterize = false;
- break;
- }
- if (response != NULL)
- sm_free(response);
-
- /*
- ** If quarantining by a connect/ehlo action,
- ** save between messages
- */
-
- if (smtp.sm_quarmsg == NULL &&
- e->e_quarmsg != NULL)
- smtp.sm_quarmsg = newstr(e->e_quarmsg);
- }
-#endif /* MILTER */
- gothello = true;
-
- /* print HELO response message */
- if (c->cmd_code != CMDEHLO)
- {
- message("250 %s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
- break;
- }
-
- message("250-%s Hello %s, %s",
- MyHostName, CurSmtpClient, q);
-
- /* offer ENHSC even for nullserver */
- if (nullserver != NULL)
- {
- message("250 ENHANCEDSTATUSCODES");
- break;
- }
-
- /*
- ** print EHLO features list
- **
- ** Note: If you change this list,
- ** remember to update 'helpfile'
- */
-
- message("250-ENHANCEDSTATUSCODES");
-#if PIPELINING
- if (bitset(SRV_OFFER_PIPE, features))
- message("250-PIPELINING");
-#endif /* PIPELINING */
- if (bitset(SRV_OFFER_EXPN, features))
- {
- message("250-EXPN");
- if (bitset(SRV_OFFER_VERB, features))
- message("250-VERB");
- }
-#if MIME8TO7
- message("250-8BITMIME");
-#endif /* MIME8TO7 */
- if (MaxMessageSize > 0)
- message("250-SIZE %ld", MaxMessageSize);
- else
- message("250-SIZE");
-#if DSN
- if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
- message("250-DSN");
-#endif /* DSN */
- if (bitset(SRV_OFFER_ETRN, features))
- message("250-ETRN");
-#if SASL
- if (sasl_ok && mechlist != NULL && *mechlist != '\0')
- message("250-AUTH %s", mechlist);
-#endif /* SASL */
-#if STARTTLS
- if (tls_ok_srv &&
- bitset(SRV_OFFER_TLS, features))
- message("250-STARTTLS");
-#endif /* STARTTLS */
- if (DeliverByMin > 0)
- message("250-DELIVERBY %ld",
- (long) DeliverByMin);
- else if (DeliverByMin == 0)
- message("250-DELIVERBY");
-
- /* < 0: no deliver-by */
-
- message("250 HELP");
- break;
-
- case CMDMAIL: /* mail -- designate sender */
- SmtpPhase = "server MAIL";
- DELAY_CONN("MAIL");
-
- /* check for validity of this command */
- if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
- {
- usrerr("503 5.0.0 Polite people say HELO first");
- break;
- }
- if (smtp.sm_gotmail)
- {
- usrerr("503 5.5.0 Sender already specified");
- break;
- }
-#if SASL
- if (bitset(SRV_REQ_AUTH, features) &&
- authenticating != SASL_IS_AUTH)
- {
- usrerr("530 5.7.0 Authentication required");
- break;
- }
-#endif /* SASL */
-
- p = skipword(p, "from");
- if (p == NULL)
- break;
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr(MSG_TEMPFAIL);
- break;
- }
-
- /* make sure we know who the sending host is */
- if (sendinghost == NULL)
- sendinghost = peerhostname;
-
-
-#if SM_HEAP_CHECK
- if (sm_debug_active(&DebugLeakSmtp, 1))
- {
- sm_heap_newgroup();
- sm_dprintf("smtp() heap group #%d\n",
- sm_heap_group());
- }
-#endif /* SM_HEAP_CHECK */
-
- if (Errors > 0)
- goto undo_no_pm;
- if (!gothello)
- {
- auth_warning(e, "%s didn't use HELO protocol",
- CurSmtpClient);
- }
-#ifdef PICKY_HELO_CHECK
- if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
- (sm_strcasecmp(peerhostname, "localhost") != 0 ||
- sm_strcasecmp(sendinghost, MyHostName) != 0))
- {
- auth_warning(e, "Host %s claimed to be %s",
- CurSmtpClient, sendinghost);
- }
-#endif /* PICKY_HELO_CHECK */
-
- if (protocol == NULL)
- protocol = "SMTP";
- macdefine(&e->e_macro, A_PERM, 'r', protocol);
- macdefine(&e->e_macro, A_PERM, 's', sendinghost);
-
- if (Errors > 0)
- goto undo_no_pm;
- smtp.sm_nrcpts = 0;
- n_badrcpts = 0;
- macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
- macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
- macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
- "0");
- e->e_flags |= EF_CLRQUEUE;
- sm_setproctitle(true, e, "%s %s: %.80s",
- qid_printname(e),
- CurSmtpClient, inp);
-
- /* do the processing */
- SM_TRY
- {
- extern char *FullName;
-
- QuickAbort = true;
- SM_FREE_CLR(FullName);
-
- /* must parse sender first */
- delimptr = NULL;
- setsender(p, e, &delimptr, ' ', false);
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- /* Successfully set e_from, allow logging */
- e->e_flags |= EF_LOGSENDER;
-
- /* put resulting triple from parseaddr() into macros */
- if (e->e_from.q_mailer != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_mailer}"),
- e->e_from.q_mailer->m_name);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_mailer}"), NULL);
- if (e->e_from.q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_host}"),
- e->e_from.q_host);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_host}"), "localhost");
- if (e->e_from.q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_addr}"),
- e->e_from.q_user);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{mail_addr}"), NULL);
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- /* check for possible spoofing */
- if (RealUid != 0 && OpMode == MD_SMTP &&
- !wordinclass(RealUserName, 't') &&
- (!bitnset(M_LOCALMAILER,
- e->e_from.q_mailer->m_flags) ||
- strcmp(e->e_from.q_user, RealUserName) != 0))
- {
- auth_warning(e, "%s owned process doing -bs",
- RealUserName);
- }
-
- /* reset to default value */
- SevenBitInput = save_sevenbitinput;
-
- /* now parse ESMTP arguments */
- e->e_msgsize = 0;
- addr = p;
- argno = 0;
- args[argno++] = p;
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- char *equal = NULL;
-
- /* locate the beginning of the keyword */
- SKIP_SPACE(p);
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- equal = p;
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
- vp == NULL ? "<null>" : vp);
-
- mail_esmtp_args(kp, vp, e);
- if (equal != NULL)
- *equal = '=';
- args[argno++] = kp;
- if (argno >= MAXSMTPARGS - 1)
- usrerr("501 5.5.4 Too many parameters");
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- args[argno] = NULL;
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
-#if SASL
-# if _FFR_AUTH_PASSING
- /* set the default AUTH= if the sender didn't */
- if (e->e_auth_param == NULL)
- {
- /* XXX only do this for an MSA? */
- e->e_auth_param = macvalue(macid("{auth_authen}"),
- e);
- if (e->e_auth_param == NULL)
- e->e_auth_param = "<>";
-
- /*
- ** XXX should we invoke Strust_auth now?
- ** authorizing as the client that just
- ** authenticated, so we'll trust implicitly
- */
- }
-# endif /* _FFR_AUTH_PASSING */
-#endif /* SASL */
-
- /* do config file checking of the sender */
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e s");
-#if _FFR_MAIL_MACRO
- /* make the "real" sender address available */
- macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
- e->e_from.q_paddr);
-#endif /* _FFR_MAIL_MACRO */
- if (rscheck("check_mail", addr,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
-
- if (MaxMessageSize > 0 &&
- (e->e_msgsize > MaxMessageSize ||
- e->e_msgsize < 0))
- {
- usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
- MaxMessageSize);
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
-
- /*
- ** XXX always check whether there is at least one fs
- ** with enough space?
- ** However, this may not help much: the queue group
- ** selection may later on select a FS that hasn't
- ** enough space.
- */
-
- if ((NumFileSys == 1 || NumQueue == 1) &&
- !enoughdiskspace(e->e_msgsize, e)
-#if _FFR_ANY_FREE_FS
- && !filesys_free(e->e_msgsize)
-#endif /* _FFR_ANY_FREE_FS */
- )
- {
- /*
- ** We perform this test again when the
- ** queue directory is selected, in collect.
- */
-
- usrerr("452 4.4.5 Insufficient disk space; try again later");
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- }
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- LogUsrErrs = true;
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_envfrom(args, e, &state);
- MILTER_REPLY("from");
- }
-#endif /* MILTER */
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-
- message("250 2.1.0 Sender ok");
- smtp.sm_gotmail = true;
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** An error occurred while processing a MAIL command.
- ** Jump to the common error handling code.
- */
-
- sm_exc_free(exc);
- goto undo_no_pm;
- }
- SM_END_TRY
- break;
-
- undo_no_pm:
- e->e_flags &= ~EF_PM_NOTIFY;
- undo:
- break;
-
- case CMDRCPT: /* rcpt -- designate recipient */
- DELAY_CONN("RCPT");
- if (BadRcptThrottle > 0 &&
- n_badrcpts >= BadRcptThrottle)
- {
- if (LogLevel > 5 &&
- n_badrcpts == BadRcptThrottle)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%s: Possible SMTP RCPT flood, throttling.",
- CurSmtpClient);
-
- /* To avoid duplicated message */
- n_badrcpts++;
- }
- NBADRCPTS;
-
- /*
- ** Don't use exponential backoff for now.
- ** Some servers will open more connections
- ** and actually overload the receiver even
- ** more.
- */
-
- (void) sleep(1);
- }
- if (!smtp.sm_gotmail)
- {
- usrerr("503 5.0.0 Need MAIL before RCPT");
- break;
- }
- SmtpPhase = "server RCPT";
- SM_TRY
- {
- QuickAbort = true;
- LogUsrErrs = true;
-
- /* limit flooding of our machine */
- if (MaxRcptPerMsg > 0 &&
- smtp.sm_nrcpts >= MaxRcptPerMsg)
- {
- /* sleep(1); / * slow down? */
- usrerr("452 4.5.3 Too many recipients");
- goto rcpt_done;
- }
-
- if (e->e_sendmode != SM_DELIVER)
- e->e_flags |= EF_VRFYONLY;
-
-#if MILTER
- /*
- ** If the filter will be deleting recipients,
- ** don't expand them at RCPT time (in the call
- ** to recipient()). If they are expanded, it
- ** is impossible for removefromlist() to figure
- ** out the expanded members of the original
- ** recipient and mark them as QS_DONTSEND.
- */
-
- if (milter_can_delrcpts())
- e->e_flags |= EF_VRFYONLY;
-#endif /* MILTER */
-
- p = skipword(p, "to");
- if (p == NULL)
- goto rcpt_done;
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e r");
- a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
- e, true);
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
- if (a == NULL)
- {
- usrerr("501 5.0.0 Missing recipient");
- goto rcpt_done;
- }
-
- if (delimptr != NULL && *delimptr != '\0')
- *delimptr++ = '\0';
-
- /* put resulting triple from parseaddr() into macros */
- if (a->q_mailer != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"),
- a->q_mailer->m_name);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- if (a->q_host != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), a->q_host);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), "localhost");
- if (a->q_user != NULL)
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), a->q_user);
- else
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
-
- /* now parse ESMTP arguments */
- addr = p;
- argno = 0;
- args[argno++] = p;
- p = delimptr;
- while (p != NULL && *p != '\0')
- {
- char *kp;
- char *vp = NULL;
- char *equal = NULL;
-
- /* locate the beginning of the keyword */
- SKIP_SPACE(p);
- if (*p == '\0')
- break;
- kp = p;
-
- /* skip to the value portion */
- while ((isascii(*p) && isalnum(*p)) || *p == '-')
- p++;
- if (*p == '=')
- {
- equal = p;
- *p++ = '\0';
- vp = p;
-
- /* skip to the end of the value */
- while (*p != '\0' && *p != ' ' &&
- !(isascii(*p) && iscntrl(*p)) &&
- *p != '=')
- p++;
- }
-
- if (*p != '\0')
- *p++ = '\0';
-
- if (tTd(19, 1))
- sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
- vp == NULL ? "<null>" : vp);
-
- rcpt_esmtp_args(a, kp, vp, e);
- if (equal != NULL)
- *equal = '=';
- args[argno++] = kp;
- if (argno >= MAXSMTPARGS - 1)
- usrerr("501 5.5.4 Too many parameters");
- if (Errors > 0)
- break;
- }
- args[argno] = NULL;
- if (Errors > 0)
- goto rcpt_done;
-
- /* do config file checking of the recipient */
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), "e r");
- if (rscheck("check_rcpt", addr,
- NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
- NULL, e->e_id) != EX_OK ||
- Errors > 0)
- goto rcpt_done;
- macdefine(&e->e_macro, A_PERM,
- macid("{addr_type}"), NULL);
-
- /* If discarding, don't bother to verify user */
- if (bitset(EF_DISCARD, e->e_flags))
- a->q_state = QS_VERIFIED;
-
-#if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_envrcpt(args, e, &state);
- MILTER_REPLY("to");
- }
-#endif /* MILTER */
-
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_mailer}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_host}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{rcpt_addr}"), NULL);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_notify}"), NULL);
- if (Errors > 0)
- goto rcpt_done;
-
- /* save in recipient list after ESMTP mods */
- a = recipient(a, &e->e_sendqueue, 0, e);
- if (Errors > 0)
- goto rcpt_done;
-
- /* no errors during parsing, but might be a duplicate */
- e->e_to = a->q_paddr;
- if (!QS_IS_BADADDR(a->q_state))
- {
- if (smtp.sm_nrcpts == 0)
- initsys(e);
- message("250 2.1.5 Recipient ok%s",
- QS_IS_QUEUEUP(a->q_state) ?
- " (will queue)" : "");
- smtp.sm_nrcpts++;
- }
- else
- {
- /* punt -- should keep message in ADDRESS.... */
- usrerr("550 5.1.1 Addressee unknown");
- }
- rcpt_done:
- if (Errors > 0)
- {
- ++n_badrcpts;
- NBADRCPTS;
- }
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /* An exception occurred while processing RCPT */
- e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
- ++n_badrcpts;
- NBADRCPTS;
- }
- SM_END_TRY
- break;
-
- case CMDDATA: /* data -- text of mail */
- DELAY_CONN("DATA");
- if (!smtp_data(&smtp, e))
- goto doquit;
- break;
-
- case CMDRSET: /* rset -- reset state */
- if (tTd(94, 100))
- message("451 4.0.0 Test failure");
- else
- message("250 2.0.0 Reset state");
- CLEAR_STATE(cmdbuf);
- break;
-
- case CMDVRFY: /* vrfy -- verify address */
- case CMDEXPN: /* expn -- expand address */
- vrfy = c->cmd_code == CMDVRFY;
- DELAY_CONN(vrfy ? "VRFY" : "EXPN");
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
- vrfy ? "VRFY" : "EXPN",
- p, CurSmtpClient);
-
- /* RFC 821 doesn't allow 4xy reply code */
- usrerr("550 5.7.1 Please try again later");
- break;
- }
- wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
- false, vrfy ? "VRFY" : "EXPN", e);
- STOP_IF_ATTACK(wt);
- previous = curtime();
- if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
- (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
- {
- if (vrfy)
- message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
- else
- message("502 5.7.0 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- else if (!gothello &&
- bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
- PrivacyFlags))
- {
- usrerr("503 5.0.0 I demand that you introduce yourself first");
- break;
- }
- if (Errors > 0)
- break;
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id, "%s: %s",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- SM_TRY
- {
- QuickAbort = true;
- vrfyqueue = NULL;
- if (vrfy)
- e->e_flags |= EF_VRFYONLY;
- while (*p != '\0' && isascii(*p) && isspace(*p))
- p++;
- if (*p == '\0')
- {
- usrerr("501 5.5.2 Argument required");
- }
- else
- {
- /* do config file checking of the address */
- if (rscheck(vrfy ? "check_vrfy" : "check_expn",
- p, NULL, e, RSF_RMCOMM,
- 3, NULL, NOQID) != EX_OK ||
- Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
- }
- if (wt > 0)
- {
- time_t t;
-
- t = wt - (curtime() - previous);
- if (t > 0)
- (void) sleep(t);
- }
- if (Errors > 0)
- sm_exc_raisenew_x(&EtypeQuickAbort, 1);
- if (vrfyqueue == NULL)
- {
- usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
- }
- while (vrfyqueue != NULL)
- {
- if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
- {
- vrfyqueue = vrfyqueue->q_next;
- continue;
- }
-
- /* see if there is more in the vrfy list */
- a = vrfyqueue;
- while ((a = a->q_next) != NULL &&
- (!QS_IS_UNDELIVERED(a->q_state)))
- continue;
- printvrfyaddr(vrfyqueue, a == NULL, vrfy);
- vrfyqueue = a;
- }
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** An exception occurred while processing VRFY/EXPN
- */
-
- sm_exc_free(exc);
- goto undo;
- }
- SM_END_TRY
- break;
-
- case CMDETRN: /* etrn -- force queue flush */
- DELAY_CONN("ETRN");
-
- /* Don't leak queue information via debug flags */
- if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
- (RealUid != 0 && RealUid != TrustedUid &&
- OpMode == MD_SMTP))
- {
- /* different message for MSA ? */
- message("502 5.7.0 Sorry, we do not allow this operation");
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: %s [rejected]",
- CurSmtpClient,
- shortenstring(inp, MAXSHORTSTR));
- break;
- }
- if (tempfail)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
- p, CurSmtpClient);
- usrerr(MSG_TEMPFAIL);
- break;
- }
-
- if (strlen(p) <= 0)
- {
- usrerr("500 5.5.2 Parameter required");
- break;
- }
-
- /* crude way to avoid denial-of-service attacks */
- STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
- true, "ETRN", e));
-
- /*
- ** Do config file checking of the parameter.
- ** Even though we have srv_features now, we still
- ** need this ruleset because the former is called
- ** when the connection has been established, while
- ** this ruleset is called when the command is
- ** actually issued and therefore has all information
- ** available to make a decision.
- */
-
- if (rscheck("check_etrn", p, NULL, e,
- RSF_RMCOMM, 3, NULL, NOQID) != EX_OK ||
- Errors > 0)
- break;
-
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%s: ETRN %s", CurSmtpClient,
- shortenstring(p, MAXSHORTSTR));
-
- id = p;
- if (*id == '#')
- {
- int i, qgrp;
-
- id++;
- qgrp = name2qid(id);
- if (!ISVALIDQGRP(qgrp))
- {
- usrerr("459 4.5.4 Queue %s unknown",
- id);
- break;
- }
- for (i = 0; i < NumQueue && Queue[i] != NULL;
- i++)
- Queue[i]->qg_nextrun = (time_t) -1;
- Queue[qgrp]->qg_nextrun = 0;
- ok = run_work_group(Queue[qgrp]->qg_wgrp,
- RWG_FORK|RWG_FORCE);
- if (ok && Errors == 0)
- message("250 2.0.0 Queuing for queue group %s started", id);
- break;
- }
-
- if (*id == '@')
- id++;
- else
- *--id = '@';
-
- new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
- if (new == NULL)
- {
- syserr("500 5.5.0 ETRN out of memory");
- break;
- }
- new->queue_match = id;
- new->queue_negate = false;
- new->queue_next = NULL;
- QueueLimitRecipient = new;
- ok = runqueue(true, false, false, true);
- sm_free(QueueLimitRecipient); /* XXX */
- QueueLimitRecipient = NULL;
- if (ok && Errors == 0)
- message("250 2.0.0 Queuing for node %s started", p);
- break;
-
- case CMDHELP: /* help -- give user info */
- DELAY_CONN("HELP");
- help(p, e);
- break;
-
- case CMDNOOP: /* noop -- do nothing */
- DELAY_CONN("NOOP");
- STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
- true, "NOOP", e));
- message("250 2.0.0 OK");
- break;
-
- case CMDQUIT: /* quit -- leave mail */
- message("221 2.0.0 %s closing connection", MyHostName);
-#if PIPELINING
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
-
- if (smtp.sm_nrcpts > 0)
- logundelrcpts(e, "aborted by sender", 9, false);
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
-
-#if STARTTLS
- /* shutdown TLS connection */
- if (tls_active)
- {
- (void) endtls(srv_ssl, "server");
- tls_active = false;
- }
-#endif /* STARTTLS */
-#if SASL
- if (authenticating == SASL_IS_AUTH)
- {
- sasl_dispose(&conn);
- authenticating = SASL_NOT_AUTH;
- /* XXX sasl_done(); this is a child */
- }
-#endif /* SASL */
-
-doquit:
- /* avoid future 050 messages */
- disconnect(1, e);
-
-#if MILTER
- /* close out milter filters */
- milter_quit(e);
-#endif /* MILTER */
-
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- if (lognullconnection && LogLevel > 5 &&
- nullserver == NULL)
- {
- char *d;
-
- d = macvalue(macid("{daemon_name}"), e);
- if (d == NULL)
- d = "stdin";
-
- /*
- ** even though this id is "bogus", it makes
- ** it simpler to "grep" related events, e.g.,
- ** timeouts for the same connection.
- */
-
- sm_syslog(LOG_INFO, e->e_id,
- "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
- CurSmtpClient, d);
- }
- if (tTd(93, 100))
- {
- /* return to handle next connection */
- return;
- }
- finis(true, true, ExitStat);
- /* NOTREACHED */
-
- case CMDVERB: /* set verbose mode */
- DELAY_CONN("VERB");
- if (!bitset(SRV_OFFER_EXPN, features) ||
- !bitset(SRV_OFFER_VERB, features))
- {
- /* this would give out the same info */
- message("502 5.7.0 Verbose unavailable");
- break;
- }
- STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
- true, "VERB", e));
- Verbose = 1;
- set_delivery_mode(SM_DELIVER, e);
- message("250 2.0.0 Verbose mode");
- break;
-
-#if SMTPDEBUG
- case CMDDBGQSHOW: /* show queues */
- (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
- "Send Queue=");
- printaddr(smioout, e->e_sendqueue, true);
- break;
-
- case CMDDBGDEBUG: /* set debug mode */
- tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
- tTflag(p);
- message("200 2.0.0 Debug set");
- break;
-
-#else /* SMTPDEBUG */
- case CMDDBGQSHOW: /* show queues */
- case CMDDBGDEBUG: /* set debug mode */
-#endif /* SMTPDEBUG */
- case CMDLOGBOGUS: /* bogus command */
- DELAY_CONN("Bogus");
- if (LogLevel > 0)
- sm_syslog(LOG_CRIT, e->e_id,
- "\"%s\" command from %s (%.100s)",
- c->cmd_name, CurSmtpClient,
- anynet_ntoa(&RealHostAddr));
- /* FALLTHROUGH */
-
- case CMDERROR: /* unknown command */
-#if MAXBADCOMMANDS > 0
- if (++n_badcmds > MAXBADCOMMANDS)
- {
- stopattack:
- message("421 4.7.0 %s Too many bad commands; closing connection",
- MyHostName);
-
- /* arrange to ignore any current send list */
- e->e_sendqueue = NULL;
- goto doquit;
- }
-#endif /* MAXBADCOMMANDS > 0 */
-
-#if MILTER && SMFI_VERSION > 2
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- if (MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id,
- "Sending \"%s\" to Milter", inp);
- response = milter_unknown(inp, e, &state);
- MILTER_REPLY("unknown");
- if (state == SMFIR_REPLYCODE ||
- state == SMFIR_REJECT ||
- state == SMFIR_TEMPFAIL)
- {
- /* MILTER_REPLY already gave an error */
- break;
- }
- }
-#endif /* MILTER && SMFI_VERSION > 2 */
-
- usrerr("500 5.5.1 Command unrecognized: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- case CMDUNIMPL:
- DELAY_CONN("Unimpl");
- usrerr("502 5.5.1 Command not implemented: \"%s\"",
- shortenstring(inp, MAXSHORTSTR));
- break;
-
- default:
- DELAY_CONN("default");
- errno = 0;
- syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
- break;
- }
-#if SASL
- }
-#endif /* SASL */
- }
- SM_EXCEPT(exc, "[!F]*")
- {
- /*
- ** The only possible exception is "E:mta.quickabort".
- ** There is nothing to do except fall through and loop.
- */
- }
- SM_END_TRY
- }
-}
-/*
-** SMTP_DATA -- implement the SMTP DATA command.
-**
-** Parameters:
-** smtp -- status of SMTP connection.
-** e -- envelope.
-**
-** Returns:
-** true iff SMTP session can continue.
-**
-** Side Effects:
-** possibly sends message.
-*/
-
-static bool
-smtp_data(smtp, e)
- SMTP_T *smtp;
- ENVELOPE *e;
-{
-#if MILTER
- bool milteraccept;
-#endif /* MILTER */
- bool aborting;
- bool doublequeue;
- ADDRESS *a;
- ENVELOPE *ee;
- char *id;
- char *oldid;
- char buf[32];
-
- SmtpPhase = "server DATA";
- if (!smtp->sm_gotmail)
- {
- usrerr("503 5.0.0 Need MAIL command");
- return true;
- }
- else if (smtp->sm_nrcpts <= 0)
- {
- usrerr("503 5.0.0 Need RCPT (recipient)");
- return true;
- }
- (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
- if (rscheck("check_data", buf, NULL, e,
- RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
- e->e_id) != EX_OK)
- return true;
-
-#if MILTER && SMFI_VERSION > 3
- if (smtp->sm_milterlist && smtp->sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
- int savelogusrerrs = LogUsrErrs;
-
- response = milter_data_cmd(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=%s",
- response);
- LogUsrErrs = false;
- }
- usrerr(response);
- if (strncmp(response, "421 ", 4) == 0)
- {
- e->e_sendqueue = NULL;
- return false;
- }
- return true;
-
- case SMFIR_REJECT:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=550 5.7.1 Command rejected");
- LogUsrErrs = false;
- }
- usrerr("550 5.7.1 Command rejected");
- return true;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, discard");
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: cmd=data, reject=%s",
- MSG_TEMPFAIL);
- LogUsrErrs = false;
- }
- usrerr(MSG_TEMPFAIL);
- return true;
- }
- LogUsrErrs = savelogusrerrs;
- if (response != NULL)
- sm_free(response); /* XXX */
- }
-#endif /* MILTER && SMFI_VERSION > 3 */
-
- /* put back discard bit */
- if (smtp->sm_discard)
- e->e_flags |= EF_DISCARD;
-
- /* check to see if we need to re-expand aliases */
- /* also reset QS_BADADDR on already-diagnosted addrs */
- doublequeue = false;
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (QS_IS_VERIFIED(a->q_state) &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- /* need to re-expand aliases */
- doublequeue = true;
- }
- if (QS_IS_BADADDR(a->q_state))
- {
- /* make this "go away" */
- a->q_state = QS_DONTSEND;
- }
- }
-
- /* collect the text of the message */
- SmtpPhase = "collect";
- buffer_errors();
-
- collect(InChannel, true, NULL, e, true);
-
- /* redefine message size */
- (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
-
-#if _FFR_CHECK_EOM
- /* rscheck() will set Errors or EF_DISCARD if it trips */
- (void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
- 3, NULL, e->e_id);
-#endif /* _FFR_CHECK_EOM */
-
-#if MILTER
- milteraccept = true;
- if (smtp->sm_milterlist && smtp->sm_milterize &&
- Errors <= 0 &&
- !bitset(EF_DISCARD, e->e_flags))
- {
- char state;
- char *response;
-
- response = milter_data(e, &state);
- switch (state)
- {
- case SMFIR_REPLYCODE:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=%s",
- response);
- milteraccept = false;
- usrerr(response);
- break;
-
- case SMFIR_REJECT:
- milteraccept = false;
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=554 5.7.1 Command rejected");
- usrerr("554 5.7.1 Command rejected");
- break;
-
- case SMFIR_DISCARD:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, discard");
- milteraccept = false;
- e->e_flags |= EF_DISCARD;
- break;
-
- case SMFIR_TEMPFAIL:
- if (MilterLogLevel > 3)
- sm_syslog(LOG_INFO, e->e_id,
- "Milter: data, reject=%s",
- MSG_TEMPFAIL);
- milteraccept = false;
- usrerr(MSG_TEMPFAIL);
- break;
- }
- if (response != NULL)
- sm_free(response);
- }
-
- /* Milter may have changed message size */
- (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
-
- /* abort message filters that didn't get the body & log msg is OK */
- if (smtp->sm_milterlist && smtp->sm_milterize)
- {
- milter_abort(e);
- if (milteraccept && MilterLogLevel > 9)
- sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
- }
-
- /*
- ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
- ** milter accepted message, sync it now
- **
- ** XXX This is almost a copy of the code in collect(): put it into
- ** a function that is called from both places?
- */
-
- if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
- {
- int afd;
- SM_FILE_T *volatile df;
- char *dfname;
-
- df = e->e_dfp;
- dfname = queuename(e, DATAFL_LETTER);
- if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
- && errno != EINVAL)
- {
- int save_errno;
-
- save_errno = errno;
- if (save_errno == EEXIST)
- {
- struct stat st;
- int dfd;
-
- if (stat(dfname, &st) < 0)
- st.st_size = -1;
- errno = EEXIST;
- syserr("@collect: bfcommit(%s): already on disk, size=%ld",
- dfname, (long) st.st_size);
- dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
- if (dfd >= 0)
- dumpfd(dfd, true, true);
- }
- errno = save_errno;
- dferror(df, "bfcommit", e);
- flush_errors(true);
- finis(save_errno != EEXIST, true, ExitStat);
- }
- else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
- {
- dferror(df, "sm_io_getinfo", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (fsync(afd) < 0)
- {
- dferror(df, "fsync", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
- {
- dferror(df, "sm_io_close", e);
- flush_errors(true);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
-
- /* Now reopen the df file */
- e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
- SM_IO_RDONLY, NULL);
- if (e->e_dfp == NULL)
- {
- /* we haven't acked receipt yet, so just chuck this */
- syserr("@Cannot reopen %s", dfname);
- finis(true, true, ExitStat);
- /* NOTREACHED */
- }
- }
-#endif /* MILTER */
-
- /* Check if quarantining stats should be updated */
- if (e->e_quarmsg != NULL)
- markstats(e, NULL, STATS_QUARANTINE);
-
- /*
- ** If a header/body check (header checks or milter)
- ** set EF_DISCARD, don't queueup the message --
- ** that would lose the EF_DISCARD bit and deliver
- ** the message.
- */
-
- if (bitset(EF_DISCARD, e->e_flags))
- doublequeue = false;
-
- aborting = Errors > 0;
- if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
- (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
- !split_by_recipient(e))
- aborting = bitset(EF_FATALERRS, e->e_flags);
-
- if (aborting)
- {
- /* Log who the mail would have gone to */
- logundelrcpts(e, e->e_message, 8, false);
- flush_errors(true);
- buffer_errors();
- goto abortmessage;
- }
-
- /* from now on, we have to operate silently */
- buffer_errors();
-
-#if 0
- /*
- ** Clear message, it may contain an error from the SMTP dialogue.
- ** This error must not show up in the queue.
- ** Some error message should show up, e.g., alias database
- ** not available, but others shouldn't, e.g., from check_rcpt.
- */
-
- e->e_message = NULL;
-#endif /* 0 */
-
- /*
- ** Arrange to send to everyone.
- ** If sending to multiple people, mail back
- ** errors rather than reporting directly.
- ** In any case, don't mail back errors for
- ** anything that has happened up to
- ** now (the other end will do this).
- ** Truncate our transcript -- the mail has gotten
- ** to us successfully, and if we have
- ** to mail this back, it will be easier
- ** on the reader.
- ** Then send to everyone.
- ** Finally give a reply code. If an error has
- ** already been given, don't mail a
- ** message back.
- ** We goose error returns by clearing error bit.
- */
-
- SmtpPhase = "delivery";
- (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
- id = e->e_id;
-
-#if NAMED_BIND
- _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
- _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
-#endif /* NAMED_BIND */
-
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- /* make sure we actually do delivery */
- ee->e_flags &= ~EF_CLRQUEUE;
-
- /* from now on, operate silently */
- ee->e_errormode = EM_MAIL;
-
- if (doublequeue)
- {
- /* make sure it is in the queue */
- queueup(ee, false, true);
- }
- else
- {
- /* send to all recipients */
- sendall(ee, SM_DEFAULT);
- }
- ee->e_to = NULL;
- }
-
- /* put back id for SMTP logging in putoutmsg() */
- oldid = CurEnv->e_id;
- CurEnv->e_id = id;
-
- /* issue success message */
- message("250 2.0.0 %s Message accepted for delivery", id);
- CurEnv->e_id = oldid;
-
- /* if we just queued, poke it */
- if (doublequeue)
- {
- bool anything_to_send = false;
-
- sm_getla();
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- if (WILL_BE_QUEUED(ee->e_sendmode))
- continue;
- if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
- {
- ee->e_sendmode = SM_QUEUE;
- continue;
- }
- else if (QueueMode != QM_QUARANTINE &&
- ee->e_quarmsg != NULL)
- {
- ee->e_sendmode = SM_QUEUE;
- continue;
- }
- anything_to_send = true;
-
- /* close all the queue files */
- closexscript(ee);
- if (ee->e_dfp != NULL)
- {
- (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
- ee->e_dfp = NULL;
- }
- unlockqueue(ee);
- }
- if (anything_to_send)
- {
-#if PIPELINING
- /*
- ** XXX if we don't do this, we get 250 twice
- ** because it is also flushed in the child.
- */
-
- (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
-#endif /* PIPELINING */
- (void) doworklist(e, true, true);
- }
- }
-
- abortmessage:
- if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
- logsender(e, NULL);
- e->e_flags &= ~EF_LOGSENDER;
-
- /* clean up a bit */
- smtp->sm_gotmail = false;
-
- /*
- ** Call dropenvelope if and only if the envelope is *not*
- ** being processed by the child process forked by doworklist().
- */
-
- if (aborting || bitset(EF_DISCARD, e->e_flags))
- dropenvelope(e, true, false);
- else
- {
- for (ee = e; ee != NULL; ee = ee->e_sibling)
- {
- if (!doublequeue &&
- QueueMode != QM_QUARANTINE &&
- ee->e_quarmsg != NULL)
- {
- dropenvelope(ee, true, false);
- continue;
- }
- if (WILL_BE_QUEUED(ee->e_sendmode))
- dropenvelope(ee, true, false);
- }
- }
- sm_rpool_free(e->e_rpool);
-
- /*
- ** At this point, e == &MainEnvelope, but if we did splitting,
- ** then CurEnv may point to an envelope structure that was just
- ** freed with the rpool. So reset CurEnv *before* calling
- ** newenvelope.
- */
-
- CurEnv = e;
- newenvelope(e, e, sm_rpool_new_x(NULL));
- e->e_flags = BlankEnvelope.e_flags;
-
- /* restore connection quarantining */
- if (smtp->sm_quarmsg == NULL)
- {
- e->e_quarmsg = NULL;
- macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
- }
- else
- {
- e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
- macdefine(&e->e_macro, A_PERM,
- macid("{quarantine}"), e->e_quarmsg);
- }
- return true;
-}
-/*
-** LOGUNDELRCPTS -- log undelivered (or all) recipients.
-**
-** Parameters:
-** e -- envelope.
-** msg -- message for Stat=
-** level -- log level.
-** all -- log all recipients.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** logs undelivered (or all) recipients
-*/
-
-void
-logundelrcpts(e, msg, level, all)
- ENVELOPE *e;
- char *msg;
- int level;
- bool all;
-{
- ADDRESS *a;
-
- if (LogLevel <= level || msg == NULL || *msg == '\0')
- return;
-
- /* Clear $h so relay= doesn't get mislogged by logdelivery() */
- macdefine(&e->e_macro, A_PERM, 'h', NULL);
-
- /* Log who the mail would have gone to */
- for (a = e->e_sendqueue; a != NULL; a = a->q_next)
- {
- if (!QS_IS_UNDELIVERED(a->q_state) && !all)
- continue;
- e->e_to = a->q_paddr;
- logdelivery(NULL, NULL, a->q_status, msg, NULL,
- (time_t) 0, e);
- }
- e->e_to = NULL;
-}
-/*
-** CHECKSMTPATTACK -- check for denial-of-service attack by repetition
-**
-** Parameters:
-** pcounter -- pointer to a counter for this command.
-** maxcount -- maximum value for this counter before we
-** slow down.
-** waitnow -- sleep now (in this routine)?
-** cname -- command name for logging.
-** e -- the current envelope.
-**
-** Returns:
-** time to wait,
-** STOP_ATTACK if twice as many commands as allowed and
-** MaxChildren > 0.
-**
-** Side Effects:
-** Slows down if we seem to be under attack.
-*/
-
-static time_t
-checksmtpattack(pcounter, maxcount, waitnow, cname, e)
- volatile unsigned int *pcounter;
- unsigned int maxcount;
- bool waitnow;
- char *cname;
- ENVELOPE *e;
-{
- if (maxcount <= 0) /* no limit */
- return (time_t) 0;
-
- if (++(*pcounter) >= maxcount)
- {
- unsigned int shift;
- time_t s;
-
- if (*pcounter == maxcount && LogLevel > 5)
- {
- sm_syslog(LOG_INFO, e->e_id,
- "%s: possible SMTP attack: command=%.40s, count=%u",
- CurSmtpClient, cname, *pcounter);
- }
- shift = *pcounter - maxcount;
- s = 1 << shift;
- if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
- s = MAXTIMEOUT;
-
-#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \
- ? STOP_ATTACK : (time_t) s)
-
- /* sleep at least 1 second before returning */
- (void) sleep(*pcounter / maxcount);
- s -= *pcounter / maxcount;
- if (s >= MAXTIMEOUT || s < 0)
- s = MAXTIMEOUT;
- if (waitnow && s > 0)
- {
- (void) sleep(s);
- return IS_ATTACK(0);
- }
- return IS_ATTACK(s);
- }
- return (time_t) 0;
-}
-/*
-** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
-**
-** Parameters:
-** none.
-**
-** Returns:
-** nothing.
-**
-** Side Effects:
-** may change I/O fd.
-*/
-
-static void
-setup_smtpd_io()
-{
- int inchfd, outchfd, outfd;
-
- inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
- outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
- outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
- if (outchfd != outfd)
- {
- /* arrange for debugging output to go to remote host */
- (void) dup2(outchfd, outfd);
- }
-
- /*
- ** if InChannel and OutChannel are stdin/stdout
- ** and connected to ttys
- ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
- ** then "chain" them together.
- */
-
- if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
- isatty(inchfd) && isatty(outchfd))
- {
- int inmode, outmode;
-
- inmode = fcntl(inchfd, F_GETFL, 0);
- if (inmode == -1)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "fcntl(inchfd, F_GETFL) failed: %s",
- sm_errstring(errno));
- return;
- }
- outmode = fcntl(outchfd, F_GETFL, 0);
- if (outmode == -1)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "fcntl(outchfd, F_GETFL) failed: %s",
- sm_errstring(errno));
- return;
- }
- if (bitset(O_NONBLOCK, inmode) ||
- bitset(O_NONBLOCK, outmode) ||
- fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
- return;
- outmode = fcntl(outchfd, F_GETFL, 0);
- if (outmode != -1 && bitset(O_NONBLOCK, outmode))
- {
- /* changing InChannel also changes OutChannel */
- sm_io_automode(OutChannel, InChannel);
- if (tTd(97, 4) && LogLevel > 9)
- sm_syslog(LOG_INFO, NOQID,
- "set automode for I (%d)/O (%d) in SMTP server",
- inchfd, outchfd);
- }
-
- /* undo change of inchfd */
- (void) fcntl(inchfd, F_SETFL, inmode);
- }
-}
-/*
-** SKIPWORD -- skip a fixed word.
-**
-** Parameters:
-** p -- place to start looking.
-** w -- word to skip.
-**
-** Returns:
-** p following w.
-** NULL on error.
-**
-** Side Effects:
-** clobbers the p data area.
-*/
-
-static char *
-skipword(p, w)
- register char *volatile p;
- char *w;
-{
- register char *q;
- char *firstp = p;
-
- /* find beginning of word */
- SKIP_SPACE(p);
- q = p;
-
- /* find end of word */
- while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
- p++;
- while (isascii(*p) && isspace(*p))
- *p++ = '\0';
- if (*p != ':')
- {
- syntax:
- usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
- shortenstring(firstp, MAXSHORTSTR));
- return NULL;
- }
- *p++ = '\0';
- SKIP_SPACE(p);
-
- if (*p == '\0')
- goto syntax;
-
- /* see if the input word matches desired word */
- if (sm_strcasecmp(q, w))
- goto syntax;
-
- return p;
-}
-/*
-** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
-**
-** Parameters:
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-static void
-mail_esmtp_args(kp, vp, e)
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (sm_strcasecmp(kp, "size") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 5.5.2 SIZE requires a value");
- /* NOTREACHED */
- }
- macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
- errno = 0;
- e->e_msgsize = strtol(vp, (char **) NULL, 10);
- if (e->e_msgsize == LONG_MAX && errno == ERANGE)
- {
- usrerr("552 5.2.3 Message size exceeds maximum value");
- /* NOTREACHED */
- }
- if (e->e_msgsize < 0)
- {
- usrerr("552 5.2.3 Message size invalid");
- /* NOTREACHED */
- }
- }
- else if (sm_strcasecmp(kp, "body") == 0)
- {
- if (vp == NULL)
- {
- usrerr("501 5.5.2 BODY requires a value");
- /* NOTREACHED */
- }
- else if (sm_strcasecmp(vp, "8bitmime") == 0)
- {
- SevenBitInput = false;
- }
- else if (sm_strcasecmp(vp, "7bit") == 0)
- {
- SevenBitInput = true;
- }
- else
- {
- usrerr("501 5.5.4 Unknown BODY type %s", vp);
- /* NOTREACHED */
- }
- e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
- }
- else if (sm_strcasecmp(kp, "envid") == 0)
- {
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 ENVID requires a value");
- /* NOTREACHED */
- }
- if (!xtextok(vp))
- {
- usrerr("501 5.5.4 Syntax error in ENVID parameter value");
- /* NOTREACHED */
- }
- if (e->e_envid != NULL)
- {
- usrerr("501 5.5.0 Duplicate ENVID parameter");
- /* NOTREACHED */
- }
- e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
- macdefine(&e->e_macro, A_PERM,
- macid("{dsn_envid}"), e->e_envid);
- }
- else if (sm_strcasecmp(kp, "ret") == 0)
- {
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 RET requires a value");
- /* NOTREACHED */
- }
- if (bitset(EF_RET_PARAM, e->e_flags))
- {
- usrerr("501 5.5.0 Duplicate RET parameter");
- /* NOTREACHED */
- }
- e->e_flags |= EF_RET_PARAM;
- if (sm_strcasecmp(vp, "hdrs") == 0)
- e->e_flags |= EF_NO_BODY_RETN;
- else if (sm_strcasecmp(vp, "full") != 0)
- {
- usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
- /* NOTREACHED */
- }
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
- }
-#if SASL
- else if (sm_strcasecmp(kp, "auth") == 0)
- {
- int len;
- char *q;
- char *auth_param; /* the value of the AUTH=x */
- bool saveQuickAbort = QuickAbort;
- bool saveSuprErrs = SuprErrs;
- bool saveExitStat = ExitStat;
-
- if (vp == NULL)
- {
- usrerr("501 5.5.2 AUTH= requires a value");
- /* NOTREACHED */
- }
- if (e->e_auth_param != NULL)
- {
- usrerr("501 5.5.0 Duplicate AUTH parameter");
- /* NOTREACHED */
- }
- if ((q = strchr(vp, ' ')) != NULL)
- len = q - vp + 1;
- else
- len = strlen(vp) + 1;
- auth_param = xalloc(len);
- (void) sm_strlcpy(auth_param, vp, len);
- if (!xtextok(auth_param))
- {
- usrerr("501 5.5.4 Syntax error in AUTH parameter value");
- /* just a warning? */
- /* NOTREACHED */
- }
-
- /* XXX define this always or only if trusted? */
- macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
- auth_param);
-
- /*
- ** call Strust_auth to find out whether
- ** auth_param is acceptable (trusted)
- ** we shouldn't trust it if not authenticated
- ** (required by RFC, leave it to ruleset?)
- */
-
- SuprErrs = true;
- QuickAbort = false;
- if (strcmp(auth_param, "<>") != 0 &&
- (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
- 9, NULL, NOQID) != EX_OK || Errors > 0))
- {
- if (tTd(95, 8))
- {
- q = e->e_auth_param;
- sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
- auth_param, (q == NULL) ? "" : q);
- }
-
- /* not trusted */
- e->e_auth_param = "<>";
-# if _FFR_AUTH_PASSING
- macdefine(&BlankEnvelope.e_macro, A_PERM,
- macid("{auth_author}"), NULL);
-# endif /* _FFR_AUTH_PASSING */
- }
- else
- {
- if (tTd(95, 8))
- sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
- e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
- auth_param);
- }
- sm_free(auth_param); /* XXX */
-
- /* reset values */
- Errors = 0;
- QuickAbort = saveQuickAbort;
- SuprErrs = saveSuprErrs;
- ExitStat = saveExitStat;
- }
-#endif /* SASL */
-#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?')
-
- /*
- ** "by" is only accepted if DeliverByMin >= 0.
- ** We maybe could add this to the list of server_features.
- */
-
- else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
- {
- char *s;
-
- if (vp == NULL)
- {
- usrerr("501 5.5.2 BY= requires a value");
- /* NOTREACHED */
- }
- errno = 0;
- e->e_deliver_by = strtol(vp, &s, 10);
- if (e->e_deliver_by == LONG_MIN ||
- e->e_deliver_by == LONG_MAX ||
- e->e_deliver_by > 999999999l ||
- e->e_deliver_by < -999999999l)
- {
- usrerr("501 5.5.2 BY=%s out of range", vp);
- /* NOTREACHED */
- }
- if (s == NULL || *s != ';')
- {
- usrerr("501 5.5.2 BY= missing ';'");
- /* NOTREACHED */
- }
- e->e_dlvr_flag = 0;
- ++s; /* XXX: spaces allowed? */
- SKIP_SPACE(s);
- switch (tolower(*s))
- {
- case 'n':
- e->e_dlvr_flag = DLVR_NOTIFY;
- break;
- case 'r':
- e->e_dlvr_flag = DLVR_RETURN;
- if (e->e_deliver_by <= 0)
- {
- usrerr("501 5.5.4 mode R requires BY time > 0");
- /* NOTREACHED */
- }
- if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
- e->e_deliver_by < DeliverByMin)
- {
- usrerr("555 5.5.2 time %ld less than %ld",
- e->e_deliver_by, (long) DeliverByMin);
- /* NOTREACHED */
- }
- break;
- default:
- usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
- /* NOTREACHED */
- }
- ++s; /* XXX: spaces allowed? */
- SKIP_SPACE(s);
- switch (tolower(*s))
- {
- case 't':
- e->e_dlvr_flag |= DLVR_TRACE;
- break;
- case '\0':
- break;
- default:
- usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
- /* NOTREACHED */
- }
-
- /* XXX: check whether more characters follow? */
- }
- else
- {
- usrerr("555 5.5.4 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
-/*
-** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
-**
-** Parameters:
-** a -- the address corresponding to the To: parameter.
-** kp -- the parameter key.
-** vp -- the value of that parameter.
-** e -- the envelope.
-**
-** Returns:
-** none.
-*/
-
-static void
-rcpt_esmtp_args(a, kp, vp, e)
- ADDRESS *a;
- char *kp;
- char *vp;
- ENVELOPE *e;
-{
- if (sm_strcasecmp(kp, "notify") == 0)
- {
- char *p;
-
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 NOTIFY requires a value");
- /* NOTREACHED */
- }
- a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
- a->q_flags |= QHASNOTIFY;
- macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
-
- if (sm_strcasecmp(vp, "never") == 0)
- return;
- for (p = vp; p != NULL; vp = p)
- {
- p = strchr(p, ',');
- if (p != NULL)
- *p++ = '\0';
- if (sm_strcasecmp(vp, "success") == 0)
- a->q_flags |= QPINGONSUCCESS;
- else if (sm_strcasecmp(vp, "failure") == 0)
- a->q_flags |= QPINGONFAILURE;
- else if (sm_strcasecmp(vp, "delay") == 0)
- a->q_flags |= QPINGONDELAY;
- else
- {
- usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY",
- vp);
- /* NOTREACHED */
- }
- }
- }
- else if (sm_strcasecmp(kp, "orcpt") == 0)
- {
- if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
- {
- usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
- /* NOTREACHED */
- }
- if (vp == NULL)
- {
- usrerr("501 5.5.2 ORCPT requires a value");
- /* NOTREACHED */
- }
- if (strchr(vp, ';') == NULL || !xtextok(vp))
- {
- usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
- /* NOTREACHED */
- }
- if (a->q_orcpt != NULL)
- {
- usrerr("501 5.5.0 Duplicate ORCPT parameter");
- /* NOTREACHED */
- }
- a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
- }
- else
- {
- usrerr("555 5.5.4 %s parameter unrecognized", kp);
- /* NOTREACHED */
- }
-}
-/*
-** PRINTVRFYADDR -- print an entry in the verify queue
-**
-** Parameters:
-** a -- the address to print.
-** last -- set if this is the last one.
-** vrfy -- set if this is a VRFY command.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Prints the appropriate 250 codes.
-*/
-#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */
-
-static void
-printvrfyaddr(a, last, vrfy)
- register ADDRESS *a;
- bool last;
- bool vrfy;
-{
- char fmtbuf[30];
-
- if (vrfy && a->q_mailer != NULL &&
- !bitnset(M_VRFY250, a->q_mailer->m_flags))
- (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
- else
- (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
- fmtbuf[3] = last ? ' ' : '-';
- (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
- if (a->q_fullname == NULL)
- {
- if ((a->q_mailer == NULL ||
- a->q_mailer->m_addrtype == NULL ||
- sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
- strchr(a->q_user, '@') == NULL)
- (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
- sizeof fmtbuf - OFFF);
- else
- (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
- sizeof fmtbuf - OFFF);
- message(fmtbuf, a->q_user, MyHostName);
- }
- else
- {
- if ((a->q_mailer == NULL ||
- a->q_mailer->m_addrtype == NULL ||
- sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
- strchr(a->q_user, '@') == NULL)
- (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
- sizeof fmtbuf - OFFF);
- else
- (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
- sizeof fmtbuf - OFFF);
- message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
- }
-}
-
-#if SASL
-/*
-** SASLMECHS -- get list of possible AUTH mechanisms
-**
-** Parameters:
-** conn -- SASL connection info.
-** mechlist -- output parameter for list of mechanisms.
-**
-** Returns:
-** number of mechs.
-*/
-
-static int
-saslmechs(conn, mechlist)
- sasl_conn_t *conn;
- char **mechlist;
-{
- int len, num, result;
-
- /* "user" is currently unused */
-# if SASL >= 20000
- result = sasl_listmech(conn, NULL,
- "", " ", "", (const char **) mechlist,
- (unsigned int *)&len, &num);
-# else /* SASL >= 20000 */
- result = sasl_listmech(conn, "user", /* XXX */
- "", " ", "", mechlist,
- (unsigned int *)&len, (unsigned int *)&num);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH error: listmech=%d, num=%d",
- result, num);
- num = 0;
- }
- if (num > 0)
- {
- if (LogLevel > 11)
- sm_syslog(LOG_INFO, NOQID,
- "AUTH: available mech=%s, allowed mech=%s",
- *mechlist, AuthMechanisms);
- *mechlist = intersect(AuthMechanisms, *mechlist, NULL);
- }
- else
- {
- *mechlist = NULL; /* be paranoid... */
- if (result == SASL_OK && LogLevel > 9)
- sm_syslog(LOG_WARNING, NOQID,
- "AUTH warning: no mechanisms");
- }
- return num;
-}
-
-# if SASL >= 20000
-/*
-** PROXY_POLICY -- define proxy policy for AUTH
-**
-** Parameters:
-** conn -- unused.
-** context -- unused.
-** requested_user -- authorization identity.
-** rlen -- authorization identity length.
-** auth_identity -- authentication identity.
-** alen -- authentication identity length.
-** def_realm -- default user realm.
-** urlen -- user realm length.
-** propctx -- unused.
-**
-** Returns:
-** ok?
-**
-** Side Effects:
-** sets {auth_authen} macro.
-*/
-
-int
-proxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
- def_realm, urlen, propctx)
- sasl_conn_t *conn;
- void *context;
- const char *requested_user;
- unsigned rlen;
- const char *auth_identity;
- unsigned alen;
- const char *def_realm;
- unsigned urlen;
- struct propctx *propctx;
-{
- if (auth_identity == NULL)
- return SASL_FAIL;
-
- macdefine(&BlankEnvelope.e_macro, A_TEMP,
- macid("{auth_authen}"), (char *) auth_identity);
-
- return SASL_OK;
-}
-# else /* SASL >= 20000 */
-
-/*
-** PROXY_POLICY -- define proxy policy for AUTH
-**
-** Parameters:
-** context -- unused.
-** auth_identity -- authentication identity.
-** requested_user -- authorization identity.
-** user -- allowed user (output).
-** errstr -- possible error string (output).
-**
-** Returns:
-** ok?
-*/
-
-int
-proxy_policy(context, auth_identity, requested_user, user, errstr)
- void *context;
- const char *auth_identity;
- const char *requested_user;
- const char **user;
- const char **errstr;
-{
- if (user == NULL || auth_identity == NULL)
- return SASL_FAIL;
- *user = newstr(auth_identity);
- return SASL_OK;
-}
-# endif /* SASL >= 20000 */
-#endif /* SASL */
-
-#if STARTTLS
-/*
-** INITSRVTLS -- initialize server side TLS
-**
-** Parameters:
-** tls_ok -- should tls initialization be done?
-**
-** Returns:
-** succeeded?
-**
-** Side Effects:
-** sets tls_ok_srv which is a static variable in this module.
-** Do NOT remove assignments to it!
-*/
-
-bool
-initsrvtls(tls_ok)
- bool tls_ok;
-{
- if (!tls_ok)
- return false;
-
- /* do NOT remove assignment */
- tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile,
- SrvKeyFile, CACertPath, CACertFile, DHParams);
- return tls_ok_srv;
-}
-#endif /* STARTTLS */
-/*
-** SRVFEATURES -- get features for SMTP server
-**
-** Parameters:
-** e -- envelope (should be session context).
-** clientname -- name of client.
-** features -- default features for this invocation.
-**
-** Returns:
-** server features.
-*/
-
-/* table with options: it uses just one character, how about strings? */
-static struct
-{
- char srvf_opt;
- unsigned int srvf_flag;
-} srv_feat_table[] =
-{
- { 'A', SRV_OFFER_AUTH },
- { 'B', SRV_OFFER_VERB },
- { 'C', SRV_REQ_SEC },
- { 'D', SRV_OFFER_DSN },
- { 'E', SRV_OFFER_ETRN },
- { 'L', SRV_REQ_AUTH },
-#if PIPELINING
-# if _FFR_NO_PIPE
- { 'N', SRV_NO_PIPE },
-# endif /* _FFR_NO_PIPE */
- { 'P', SRV_OFFER_PIPE },
-#endif /* PIPELINING */
- { 'R', SRV_VRFY_CLT }, /* same as V; not documented */
- { 'S', SRV_OFFER_TLS },
-/* { 'T', SRV_TMP_FAIL }, */
- { 'V', SRV_VRFY_CLT },
- { 'X', SRV_OFFER_EXPN },
-/* { 'Y', SRV_OFFER_VRFY }, */
- { '\0', SRV_NONE }
-};
-
-static unsigned int
-srvfeatures(e, clientname, features)
- ENVELOPE *e;
- char *clientname;
- unsigned int features;
-{
- int r, i, j;
- char **pvp, c, opt;
- char pvpbuf[PSBUFSIZE];
-
- pvp = NULL;
- r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
- sizeof(pvpbuf));
- if (r != EX_OK)
- return features;
- if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
- return features;
- if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
- return SRV_TMP_FAIL;
-
- /*
- ** General rule (see sendmail.h, d_flags):
- ** lower case: required/offered, upper case: Not required/available
- **
- ** Since we can change some features per daemon, we have both
- ** cases here: turn on/off a feature.
- */
-
- for (i = 1; pvp[i] != NULL; i++)
- {
- c = pvp[i][0];
- j = 0;
- for (;;)
- {
- if ((opt = srv_feat_table[j].srvf_opt) == '\0')
- {
- if (LogLevel > 9)
- sm_syslog(LOG_WARNING, e->e_id,
- "srvfeatures: unknown feature %s",
- pvp[i]);
- break;
- }
- if (c == opt)
- {
- features &= ~(srv_feat_table[j].srvf_flag);
- break;
- }
- if (c == tolower(opt))
- {
- features |= srv_feat_table[j].srvf_flag;
- break;
- }
- ++j;
- }
- }
- return features;
-}
-
-/*
-** HELP -- implement the HELP command.
-**
-** Parameters:
-** topic -- the topic we want help for.
-** e -- envelope.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** outputs the help file to message output.
-*/
-#define HELPVSTR "#vers "
-#define HELPVERSION 2
-
-void
-help(topic, e)
- char *topic;
- ENVELOPE *e;
-{
- register SM_FILE_T *hf;
- register char *p;
- int len;
- bool noinfo;
- bool first = true;
- long sff = SFF_OPENASROOT|SFF_REGONLY;
- char buf[MAXLINE];
- char inp[MAXLINE];
- static int foundvers = -1;
- extern char Version[];
-
- if (DontLockReadFiles)
- sff |= SFF_NOLOCK;
- if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
- sff |= SFF_SAFEDIRPATH;
-
- if (HelpFile == NULL ||
- (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
- {
- /* no help */
- errno = 0;
- message("502 5.3.0 Sendmail %s -- HELP not implemented",
- Version);
- return;
- }
-
- if (topic == NULL || *topic == '\0')
- {
- topic = "smtp";
- noinfo = false;
- }
- else
- {
- makelower(topic);
- noinfo = true;
- }
-
- len = strlen(topic);
-
- while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
- {
- if (buf[0] == '#')
- {
- if (foundvers < 0 &&
- strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
- {
- int h;
-
- if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
- &h) == 1)
- foundvers = h;
- }
- continue;
- }
- if (strncmp(buf, topic, len) == 0)
- {
- if (first)
- {
- first = false;
-
- /* print version if no/old vers# in file */
- if (foundvers < 2 && !noinfo)
- message("214-2.0.0 This is Sendmail version %s", Version);
- }
- p = strpbrk(buf, " \t");
- if (p == NULL)
- p = buf + strlen(buf) - 1;
- else
- p++;
- fixcrlf(p, true);
- if (foundvers >= 2)
- {
- translate_dollars(p);
- expand(p, inp, sizeof inp, e);
- p = inp;
- }
- message("214-2.0.0 %s", p);
- noinfo = false;
- }
- }
-
- if (noinfo)
- message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
- else
- message("214 2.0.0 End of HELP info");
-
- if (foundvers != 0 && foundvers < HELPVERSION)
- {
- if (LogLevel > 1)
- sm_syslog(LOG_WARNING, e->e_id,
- "%s too old (require version %d)",
- HelpFile, HELPVERSION);
-
- /* avoid log next time */
- foundvers = 0;
- }
-
- (void) sm_io_close(hf, SM_TIME_DEFAULT);
-}
-
-#if SASL
-/*
-** RESET_SASLCONN -- reset SASL connection data
-**
-** Parameters:
-** conn -- SASL connection context
-** hostname -- host name
-** various connection data
-**
-** Returns:
-** SASL result
-*/
-
-static int
-reset_saslconn(sasl_conn_t ** conn, char *hostname,
-# if SASL >= 20000
- char *remoteip, char *localip,
- char *auth_id, sasl_ssf_t * ext_ssf)
-# else /* SASL >= 20000 */
- struct sockaddr_in * saddr_r, struct sockaddr_in * saddr_l,
- sasl_external_properties_t * ext_ssf)
-# endif /* SASL >= 20000 */
-{
- int result;
-
- sasl_dispose(conn);
-# if SASL >= 20000
- result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
- NULL, 0, conn);
-# elif SASL > 10505
- /* use empty realm: only works in SASL > 1.5.5 */
- result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
-# else /* SASL >= 20000 */
- /* use no realm -> realm is set to hostname by SASL lib */
- result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
- conn);
-# endif /* SASL >= 20000 */
- if (result != SASL_OK)
- return result;
-
-# if SASL >= 20000
-# if NETINET || NETINET6
- if (remoteip != NULL)
- result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
- if (result != SASL_OK)
- return result;
-
- if (localip != NULL)
- result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
- if (result != SASL_OK)
- return result;
-# endif /* NETINET || NETINET6 */
-
- result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
- if (result != SASL_OK)
- return result;
-
- result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
- if (result != SASL_OK)
- return result;
-# else /* SASL >= 20000 */
-# if NETINET
- if (saddr_r != NULL)
- result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
- if (result != SASL_OK)
- return result;
-
- if (saddr_l != NULL)
- result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
- if (result != SASL_OK)
- return result;
-# endif /* NETINET */
-
- result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
- if (result != SASL_OK)
- return result;
-# endif /* SASL >= 20000 */
- return SASL_OK;
-}
-#endif /* SASL */
diff --git a/debian/patches/8.13/srvrsmtp.patch b/debian/patches/8.13/srvrsmtp.patch
deleted file mode 100644
index 419b4a3..0000000
--- a/debian/patches/8.13/srvrsmtp.patch
+++ /dev/null
@@ -1,120 +0,0 @@
-#
-# To apply this patch:
-# STEP 1: Chdir to the source directory.
-# STEP 2: Run the 'applypatch' program with this patch file as input.
-#
-# If you do not have 'applypatch', it is part of the 'makepatch' package
-# that you can fetch from the Comprehensive Perl Archive Network:
-# http://www.perl.com/CPAN/authors/Johan_Vromans/makepatch-x.y.tar.gz
-# In the above URL, 'x' should be 2 or higher.
-#
-# To apply this patch without the use of 'applypatch':
-# STEP 1: Chdir to the source directory.
-# STEP 2: Run the 'patch' program with this file as input.
-#
-#### End of Preamble ####
-
-#### Patch data follows ####
-diff -c 'srvrsmtp.c.orig' 'srvrsmtp.c'
-Index: ./srvrsmtp.c.orig
-Prereq: 8.900
-*** ./srvrsmtp.c.orig Tue Aug 10 23:48:17 2004
---- ./srvrsmtp.c Wed Aug 11 00:00:09 2004
-***************
-*** 444,449 ****
---- 444,451 ----
- char *volatile protocol; /* sending protocol */
- char *volatile sendinghost; /* sending hostname */
- char *volatile peerhostname; /* name of SMTP peer or "localhost" */
-+ char *volatile hello_name; /* client_hello string */
-+ bool hello_acceptd = false; /* helo/ehlo command accepted */
- auto char *delimptr;
- char *id;
- volatile unsigned int n_badcmds = 0; /* count of bad commands */
-***************
-*** 1976,1981 ****
---- 1978,1987 ----
- {
- q = "pleased to meet you";
- sendinghost = sm_strdup_x(p);
-+ hello_accept = true;
-+ hello_name = sm_strdup_x(p);
-+ macdefine(&e->e_macro, A_PERM, macid("{client_hello}"),
-+ hello_name);
- }
- else if (!AllowBogusHELO)
- {
-***************
-*** 1989,1994 ****
---- 1995,2004 ----
- else
- {
- q = "accepting invalid domain name";
-+ hello_accept = true;
-+ hello_name = sm_strdup_x(p);
-+ macdefine(&e->e_macro, A_PERM, macid("{client_hello}"),
-+ hello_name);
- }
-
- if (gothello)
-***************
-*** 1996,2001 ****
---- 2006,2028 ----
- CLEAR_STATE(cmdbuf);
- }
-
-+ if (hello_accept) {
-+ if (rscheck("check_hello", hello_name,
-+ NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
-+ NULL, e->e_id) != EX_OK ||
-+ Errors > 0)
-+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-+
-+ if (MaxMessageSize > 0 &&
-+ (e->e_msgsize > MaxMessageSize ||
-+ e->e_msgsize < 0))
-+ {
-+ usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
-+ MaxMessageSize);
-+ sm_exc_raisenew_x(&EtypeQuickAbort, 1);
-+ }
-+ }
-+
- #if MILTER
- if (smtp.sm_milterlist && smtp.sm_milterize &&
- !bitset(EF_DISCARD, e->e_flags))
-#### End of Patch data ####
-
-#### ApplyPatch data follows ####
-# Data version : 1.0
-# Date generated : Wed Aug 11 00:01:01 2004
-# Generated by : makepatch 2.00_07*
-# Recurse directories : Yes
-# Excluded files : (\A|/).*\~\Z
-# (\A|/).*\.a\Z
-# (\A|/).*\.bak\Z
-# (\A|/).*\.BAK\Z
-# (\A|/).*\.elc\Z
-# (\A|/).*\.exe\Z
-# (\A|/).*\.gz\Z
-# (\A|/).*\.ln\Z
-# (\A|/).*\.o\Z
-# (\A|/).*\.obj\Z
-# (\A|/).*\.olb\Z
-# (\A|/).*\.old\Z
-# (\A|/).*\.orig\Z
-# (\A|/).*\.rej\Z
-# (\A|/).*\.so\Z
-# (\A|/).*\.Z\Z
-# (\A|/)\.del\-.*\Z
-# (\A|/)\.make\.state\Z
-# (\A|/)\.nse_depinfo\Z
-# (\A|/)core\Z
-# (\A|/)tags\Z
-# (\A|/)TAGS\Z
-# p './srvrsmtp.c.orig' 111749
-#### End of ApplyPatch data ####
-
-#### End of Patch kit [created: Wed Aug 11 00:01:01 2004] ####
-#### Patch checksum: 102 3167 7100 ####
-#### Checksum: 119 3788 58534 ####
diff --git a/debian/sensible_mda/Makefile.in b/debian/sensible_mda/Makefile.in
index 85aa871..bf7d14b 100644
--- a/debian/sensible_mda/Makefile.in
+++ b/debian/sensible_mda/Makefile.in
@@ -1,8 +1,8 @@
-# Makefile.in generated by automake 1.9.4 from Makefile.am.
+# Makefile.in generated by automake 1.9.5 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004 Free Software Foundation, Inc.
+# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.