diff options
author | Richard A Nelson (Rick) <cowboy@debian.org> | 2005-02-10 19:02:00 +0000 |
---|---|---|
committer | Andreas Beckmann <debian@abeckmann.de> | 2012-10-01 19:58:46 +0200 |
commit | a875402789ecb6c61ead9b214e258c83e4d135e6 (patch) | |
tree | 87bb1748277aec2368a892cee0963a84f96185cd /debian | |
parent | e6b88b4e82cb1d7b6750d610b11b5036a5f5ab2a (diff) | |
download | sendmail-a875402789ecb6c61ead9b214e258c83e4d135e6.tar.gz |
Imported Debian patch 8.13.3-7debian/8.13.3-7
Diffstat (limited to 'debian')
-rw-r--r-- | debian/Makefile.in | 24 | ||||
-rw-r--r-- | debian/build/debian/control.m4.in | 6 | ||||
-rw-r--r-- | debian/cf/Makefile.in | 4 | ||||
-rw-r--r-- | debian/changelog | 10 | ||||
-rw-r--r-- | debian/control | 6 | ||||
-rw-r--r-- | debian/examples/Makefile.in | 4 | ||||
-rw-r--r-- | debian/examples/db/access | 23 | ||||
-rw-r--r-- | debian/local/Makefile.in | 4 | ||||
-rw-r--r-- | debian/local/dynamic.in | 2 | ||||
-rw-r--r-- | debian/local/sendmail.in | 13 | ||||
-rw-r--r-- | debian/patches/8.13/srvrsmtp.c | 4702 | ||||
-rw-r--r-- | debian/patches/8.13/srvrsmtp.new | 4702 | ||||
-rw-r--r-- | debian/patches/8.13/srvrsmtp.patch | 120 | ||||
-rw-r--r-- | debian/sensible_mda/Makefile.in | 4 |
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. |