srvrsmtp.c revision 94334
138032Speter/* 290792Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1590792Sgshapiro#if MILTER 1690792Sgshapiro# include <libmilter/mfdef.h> 1790792Sgshapiro#endif /* MILTER */ 1864562Sgshapiro 1994334SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.819 2002/04/02 03:51:02 ca Exp $") 2064562Sgshapiro 2190792Sgshapiro#if SASL || STARTTLS 2290792Sgshapiro# include <sys/time.h> 2390792Sgshapiro# include "sfsasl.h" 2490792Sgshapiro#endif /* SASL || STARTTLS */ 2590792Sgshapiro#if SASL 2690792Sgshapiro# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1) 2764562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **)); 2890792Sgshapiro#endif /* SASL */ 2990792Sgshapiro#if STARTTLS 3090792Sgshapiro# include <sysexits.h> 3138032Speter 3290792Sgshapirostatic SSL_CTX *srv_ctx = NULL; /* TLS server context */ 3390792Sgshapirostatic SSL *srv_ssl = NULL; /* per connection context */ 3438032Speter 3590792Sgshapirostatic bool tls_ok_srv = false; 3690792Sgshapiro 3790792Sgshapiroextern void tls_set_verify __P((SSL_CTX *, SSL *, bool)); 3890792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \ 3990792Sgshapiro bitset(SRV_VRFY_CLT, features)) 4090792Sgshapiro#endif /* STARTTLS */ 4190792Sgshapiro 4290792Sgshapiro/* server features */ 4390792Sgshapiro#define SRV_NONE 0x0000 /* none... */ 4490792Sgshapiro#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */ 4590792Sgshapiro#define SRV_VRFY_CLT 0x0002 /* request a cert */ 4690792Sgshapiro#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */ 4790792Sgshapiro#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */ 4890792Sgshapiro#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */ 4990792Sgshapiro#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */ 5090792Sgshapiro#define SRV_OFFER_VERB 0x0040 /* offer VERB */ 5190792Sgshapiro#define SRV_OFFER_DSN 0x0080 /* offer DSN */ 5290792Sgshapiro#if PIPELINING 5390792Sgshapiro# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */ 5490792Sgshapiro# if _FFR_NO_PIPE 5590792Sgshapiro# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */ 5690792Sgshapiro# endif /* _FFR_NO_PIPE */ 5790792Sgshapiro#endif /* PIPELINING */ 5890792Sgshapiro#define SRV_REQ_AUTH 0x0400 /* require AUTH */ 5990792Sgshapiro#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */ 6090792Sgshapiro 6190792Sgshapirostatic unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int)); 6290792Sgshapiro 6390792Sgshapirostatic time_t checksmtpattack __P((volatile unsigned int *, int, bool, 6464562Sgshapiro char *, ENVELOPE *)); 6564562Sgshapirostatic void mail_esmtp_args __P((char *, char *, ENVELOPE *)); 6664562Sgshapirostatic void printvrfyaddr __P((ADDRESS *, bool, bool)); 6764562Sgshapirostatic void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); 6864562Sgshapirostatic char *skipword __P((char *volatile, char *)); 6990792Sgshapirostatic void setup_smtpd_io __P((void)); 7064562Sgshapiroextern ENVELOPE BlankEnvelope; 7138032Speter 7290792Sgshapiro#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \ 7390792Sgshapiro (s)++ 7490792Sgshapiro 7538032Speter/* 7638032Speter** SMTP -- run the SMTP protocol. 7738032Speter** 7838032Speter** Parameters: 7938032Speter** nullserver -- if non-NULL, rejection message for 8090792Sgshapiro** (almost) all SMTP commands. 8190792Sgshapiro** d_flags -- daemon flags 8238032Speter** e -- the envelope. 8338032Speter** 8438032Speter** Returns: 8538032Speter** never. 8638032Speter** 8738032Speter** Side Effects: 8890792Sgshapiro** Reads commands from the input channel and processes them. 8938032Speter*/ 9038032Speter 9190792Sgshapiro/* 9290792Sgshapiro** Notice: The smtp server doesn't have a session context like the client 9390792Sgshapiro** side has (mci). Therefore some data (session oriented) is allocated 9490792Sgshapiro** or assigned to the "wrong" structure (esp. STARTTLS, AUTH). 9590792Sgshapiro** This should be fixed in a successor version. 9690792Sgshapiro*/ 9790792Sgshapiro 9838032Speterstruct cmd 9938032Speter{ 10064562Sgshapiro char *cmd_name; /* command name */ 10164562Sgshapiro int cmd_code; /* internal code, see below */ 10238032Speter}; 10338032Speter 10464562Sgshapiro/* values for cmd_code */ 10590792Sgshapiro#define CMDERROR 0 /* bad command */ 10690792Sgshapiro#define CMDMAIL 1 /* mail -- designate sender */ 10790792Sgshapiro#define CMDRCPT 2 /* rcpt -- designate recipient */ 10890792Sgshapiro#define CMDDATA 3 /* data -- send message text */ 10990792Sgshapiro#define CMDRSET 4 /* rset -- reset state */ 11090792Sgshapiro#define CMDVRFY 5 /* vrfy -- verify address */ 11190792Sgshapiro#define CMDEXPN 6 /* expn -- expand address */ 11290792Sgshapiro#define CMDNOOP 7 /* noop -- do nothing */ 11390792Sgshapiro#define CMDQUIT 8 /* quit -- close connection and die */ 11490792Sgshapiro#define CMDHELO 9 /* helo -- be polite */ 11590792Sgshapiro#define CMDHELP 10 /* help -- give usage info */ 11690792Sgshapiro#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 11790792Sgshapiro#define CMDETRN 12 /* etrn -- flush queue */ 11890792Sgshapiro#if SASL 11990792Sgshapiro# define CMDAUTH 13 /* auth -- SASL authenticate */ 12090792Sgshapiro#endif /* SASL */ 12190792Sgshapiro#if STARTTLS 12290792Sgshapiro# define CMDSTLS 14 /* STARTTLS -- start TLS session */ 12390792Sgshapiro#endif /* STARTTLS */ 12438032Speter/* non-standard commands */ 12590792Sgshapiro#define CMDVERB 17 /* verb -- go into verbose mode */ 12664562Sgshapiro/* unimplemented commands from RFC 821 */ 12790792Sgshapiro#define CMDUNIMPL 19 /* unimplemented rfc821 commands */ 12838032Speter/* use this to catch and log "door handle" attempts on your system */ 12990792Sgshapiro#define CMDLOGBOGUS 23 /* bogus command that should be logged */ 13038032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */ 13190792Sgshapiro#define CMDDBGQSHOW 24 /* showq -- show send queue */ 13290792Sgshapiro#define CMDDBGDEBUG 25 /* debug -- set debug mode */ 13338032Speter 13466494Sgshapiro/* 13590792Sgshapiro** Note: If you change this list, remember to update 'helpfile' 13666494Sgshapiro*/ 13766494Sgshapiro 13838032Speterstatic struct cmd CmdTab[] = 13938032Speter{ 14038032Speter { "mail", CMDMAIL }, 14138032Speter { "rcpt", CMDRCPT }, 14238032Speter { "data", CMDDATA }, 14338032Speter { "rset", CMDRSET }, 14438032Speter { "vrfy", CMDVRFY }, 14538032Speter { "expn", CMDEXPN }, 14638032Speter { "help", CMDHELP }, 14738032Speter { "noop", CMDNOOP }, 14838032Speter { "quit", CMDQUIT }, 14938032Speter { "helo", CMDHELO }, 15038032Speter { "ehlo", CMDEHLO }, 15138032Speter { "etrn", CMDETRN }, 15238032Speter { "verb", CMDVERB }, 15364562Sgshapiro { "send", CMDUNIMPL }, 15464562Sgshapiro { "saml", CMDUNIMPL }, 15564562Sgshapiro { "soml", CMDUNIMPL }, 15664562Sgshapiro { "turn", CMDUNIMPL }, 15790792Sgshapiro#if SASL 15864562Sgshapiro { "auth", CMDAUTH, }, 15990792Sgshapiro#endif /* SASL */ 16090792Sgshapiro#if STARTTLS 16164562Sgshapiro { "starttls", CMDSTLS, }, 16290792Sgshapiro#endif /* STARTTLS */ 16338032Speter /* remaining commands are here only to trap and log attempts to use them */ 16438032Speter { "showq", CMDDBGQSHOW }, 16538032Speter { "debug", CMDDBGDEBUG }, 16638032Speter { "wiz", CMDLOGBOGUS }, 16738032Speter 16838032Speter { NULL, CMDERROR } 16938032Speter}; 17038032Speter 17164562Sgshapirostatic char *CurSmtpClient; /* who's at the other end of channel */ 17238032Speter 17390792Sgshapiro#ifndef MAXBADCOMMANDS 17490792Sgshapiro# define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 17594334Sgshapiro#endif /* ! MAXBADCOMMANDS */ 17690792Sgshapiro#ifndef MAXNOOPCOMMANDS 17790792Sgshapiro# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ 17894334Sgshapiro#endif /* ! MAXNOOPCOMMANDS */ 17990792Sgshapiro#ifndef MAXHELOCOMMANDS 18090792Sgshapiro# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ 18194334Sgshapiro#endif /* ! MAXHELOCOMMANDS */ 18290792Sgshapiro#ifndef MAXVRFYCOMMANDS 18390792Sgshapiro# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ 18494334Sgshapiro#endif /* ! MAXVRFYCOMMANDS */ 18590792Sgshapiro#ifndef MAXETRNCOMMANDS 18690792Sgshapiro# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ 18794334Sgshapiro#endif /* ! MAXETRNCOMMANDS */ 18890792Sgshapiro#ifndef MAXTIMEOUT 18990792Sgshapiro# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */ 19094334Sgshapiro#endif /* ! MAXTIMEOUT */ 19138032Speter 19290792Sgshapiro#if SM_HEAP_CHECK 19390792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp", 19490792Sgshapiro "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $"); 19590792Sgshapiro#endif /* SM_HEAP_CHECK */ 19638032Speter 19790792Sgshapirotypedef struct 19890792Sgshapiro{ 19990792Sgshapiro bool sm_gotmail; /* mail command received */ 20090792Sgshapiro unsigned int sm_nrcpts; /* number of successful RCPT commands */ 20190792Sgshapiro#if _FFR_ADAPTIVE_EOL 20290792SgshapiroWARNING: do NOT use this FFR, it is most likely broken 20390792Sgshapiro bool sm_crlf; /* input in CRLF form? */ 20490792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */ 20590792Sgshapiro bool sm_discard; 20690792Sgshapiro#if MILTER 20790792Sgshapiro bool sm_milterize; 20890792Sgshapiro bool sm_milterlist; /* any filters in the list? */ 20990792Sgshapiro#endif /* MILTER */ 21090792Sgshapiro#if _FFR_QUARANTINE 21190792Sgshapiro char *sm_quarmsg; /* carry quarantining across messages */ 21290792Sgshapiro#endif /* _FFR_QUARANTINE */ 21390792Sgshapiro} SMTP_T; 21490792Sgshapiro 21590792Sgshapirostatic void smtp_data __P((SMTP_T *, ENVELOPE *)); 21690792Sgshapiro 21790792Sgshapiro#define MSG_TEMPFAIL "451 4.7.1 Please try again later" 21890792Sgshapiro 21990792Sgshapiro#if MILTER 22090792Sgshapiro# define MILTER_ABORT(e) milter_abort((e)) 22190792Sgshapiro# define MILTER_REPLY(str) \ 22290792Sgshapiro { \ 22390792Sgshapiro int savelogusrerrs = LogUsrErrs; \ 22490792Sgshapiro \ 22590792Sgshapiro switch (state) \ 22690792Sgshapiro { \ 22790792Sgshapiro case SMFIR_REPLYCODE: \ 22890792Sgshapiro if (MilterLogLevel > 3) \ 22990792Sgshapiro { \ 23090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 23190792Sgshapiro "Milter: %s=%s, reject=%s", \ 23290792Sgshapiro str, addr, response); \ 23390792Sgshapiro LogUsrErrs = false; \ 23490792Sgshapiro } \ 23590792Sgshapiro usrerr(response); \ 23690792Sgshapiro break; \ 23790792Sgshapiro \ 23890792Sgshapiro case SMFIR_REJECT: \ 23990792Sgshapiro if (MilterLogLevel > 3) \ 24090792Sgshapiro { \ 24190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 24290792Sgshapiro "Milter: %s=%s, reject=550 5.7.1 Command rejected", \ 24390792Sgshapiro str, addr); \ 24490792Sgshapiro LogUsrErrs = false; \ 24590792Sgshapiro } \ 24690792Sgshapiro usrerr("550 5.7.1 Command rejected"); \ 24790792Sgshapiro break; \ 24890792Sgshapiro \ 24990792Sgshapiro case SMFIR_DISCARD: \ 25090792Sgshapiro if (MilterLogLevel > 3) \ 25190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 25290792Sgshapiro "Milter: %s=%s, discard", \ 25390792Sgshapiro str, addr); \ 25490792Sgshapiro e->e_flags |= EF_DISCARD; \ 25590792Sgshapiro break; \ 25690792Sgshapiro \ 25790792Sgshapiro case SMFIR_TEMPFAIL: \ 25890792Sgshapiro if (MilterLogLevel > 3) \ 25990792Sgshapiro { \ 26090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 26190792Sgshapiro "Milter: %s=%s, reject=%s", \ 26290792Sgshapiro str, addr, MSG_TEMPFAIL); \ 26390792Sgshapiro LogUsrErrs = false; \ 26490792Sgshapiro } \ 26590792Sgshapiro usrerr(MSG_TEMPFAIL); \ 26690792Sgshapiro break; \ 26790792Sgshapiro } \ 26890792Sgshapiro LogUsrErrs = savelogusrerrs; \ 26990792Sgshapiro if (response != NULL) \ 27090792Sgshapiro sm_free(response); /* XXX */ \ 27190792Sgshapiro } 27290792Sgshapiro 27390792Sgshapiro#else /* MILTER */ 27490792Sgshapiro# define MILTER_ABORT(e) 27590792Sgshapiro#endif /* MILTER */ 27690792Sgshapiro 27790792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */ 27890792Sgshapiro#define CLEAR_STATE(cmd) \ 27990792Sgshapiro{ \ 28090792Sgshapiro /* abort milter filters */ \ 28190792Sgshapiro MILTER_ABORT(e); \ 28290792Sgshapiro \ 28390792Sgshapiro if (smtp.sm_nrcpts > 0) \ 28490792Sgshapiro { \ 28590792Sgshapiro logundelrcpts(e, cmd, 10, false); \ 28690792Sgshapiro smtp.sm_nrcpts = 0; \ 28790792Sgshapiro macdefine(&e->e_macro, A_PERM, \ 28890792Sgshapiro macid("{nrcpts}"), "0"); \ 28990792Sgshapiro } \ 29090792Sgshapiro \ 29190792Sgshapiro e->e_sendqueue = NULL; \ 29290792Sgshapiro e->e_flags |= EF_CLRQUEUE; \ 29390792Sgshapiro \ 29490792Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \ 29590792Sgshapiro logsender(e, NULL); \ 29690792Sgshapiro e->e_flags &= ~EF_LOGSENDER; \ 29790792Sgshapiro \ 29890792Sgshapiro /* clean up a bit */ \ 29990792Sgshapiro smtp.sm_gotmail = false; \ 30090792Sgshapiro SuprErrs = true; \ 30190792Sgshapiro dropenvelope(e, true, false); \ 30290792Sgshapiro sm_rpool_free(e->e_rpool); \ 30390792Sgshapiro e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \ 30490792Sgshapiro CurEnv = e; \ 30590792Sgshapiro} 30690792Sgshapiro 30790792Sgshapiro/* sleep to flatten out connection load */ 30890792Sgshapiro#define MIN_DELAY_LOG 15 /* wait before logging this again */ 30990792Sgshapiro 31090792Sgshapiro/* is it worth setting the process title for 1s? */ 31190792Sgshapiro#define DELAY_CONN(cmd) \ 31290792Sgshapiro if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \ 31390792Sgshapiro { \ 31490792Sgshapiro time_t dnow; \ 31590792Sgshapiro \ 31690792Sgshapiro sm_setproctitle(true, e, \ 31790792Sgshapiro "%s: %s: delaying %s: load average: %d", \ 31890792Sgshapiro qid_printname(e), CurSmtpClient, \ 31990792Sgshapiro cmd, DelayLA); \ 32090792Sgshapiro if (LogLevel > 8 && (dnow = curtime()) > log_delay) \ 32190792Sgshapiro { \ 32290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 32390792Sgshapiro "delaying=%s, load average=%d >= %d", \ 32490792Sgshapiro cmd, CurrentLA, DelayLA); \ 32590792Sgshapiro log_delay = dnow + MIN_DELAY_LOG; \ 32690792Sgshapiro } \ 32790792Sgshapiro (void) sleep(1); \ 32890792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", \ 32990792Sgshapiro qid_printname(e), CurSmtpClient, inp); \ 33090792Sgshapiro } 33190792Sgshapiro 33290792Sgshapiro 33338032Spetervoid 33464562Sgshapirosmtp(nullserver, d_flags, e) 33564562Sgshapiro char *volatile nullserver; 33664562Sgshapiro BITMAP256 d_flags; 33738032Speter register ENVELOPE *volatile e; 33838032Speter{ 33938032Speter register char *volatile p; 34064562Sgshapiro register struct cmd *volatile c = NULL; 34138032Speter char *cmd; 34238032Speter auto ADDRESS *vrfyqueue; 34338032Speter ADDRESS *a; 34438032Speter volatile bool gothello; /* helo command received */ 34538032Speter bool vrfy; /* set if this is a vrfy command */ 34638032Speter char *volatile protocol; /* sending protocol */ 34738032Speter char *volatile sendinghost; /* sending hostname */ 34838032Speter char *volatile peerhostname; /* name of SMTP peer or "localhost" */ 34938032Speter auto char *delimptr; 35038032Speter char *id; 35190792Sgshapiro volatile unsigned int n_badcmds = 0; /* count of bad commands */ 35290792Sgshapiro volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */ 35390792Sgshapiro volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */ 35490792Sgshapiro volatile unsigned int n_etrn = 0; /* count of ETRN */ 35590792Sgshapiro volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */ 35690792Sgshapiro volatile unsigned int n_helo = 0; /* count of HELO/EHLO */ 35738032Speter bool ok; 35890792Sgshapiro#if _FFR_ADAPTIVE_EOL 35990792Sgshapiro volatile bool first; 36090792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */ 36190792Sgshapiro volatile bool tempfail = false; 36264562Sgshapiro volatile time_t wt; /* timeout after too many commands */ 36364562Sgshapiro volatile time_t previous; /* time after checksmtpattack() */ 36490792Sgshapiro volatile bool lognullconnection = true; 36538032Speter register char *q; 36690792Sgshapiro SMTP_T smtp; 36764562Sgshapiro char *addr; 36864562Sgshapiro char *greetcode = "220"; 36990792Sgshapiro char *hostname; /* my hostname ($j) */ 37038032Speter QUEUE_CHAR *new; 37164562Sgshapiro int argno; 37264562Sgshapiro char *args[MAXSMTPARGS]; 37338032Speter char inp[MAXLINE]; 37438032Speter char cmdbuf[MAXLINE]; 37590792Sgshapiro#if SASL 37664562Sgshapiro sasl_conn_t *conn; 37764562Sgshapiro volatile bool sasl_ok; 37890792Sgshapiro volatile unsigned int n_auth = 0; /* count of AUTH commands */ 37964562Sgshapiro bool ismore; 38064562Sgshapiro int result; 38164562Sgshapiro volatile int authenticating; 38264562Sgshapiro char *user; 38364562Sgshapiro char *in, *out, *out2; 38464562Sgshapiro const char *errstr; 38590792Sgshapiro unsigned int inlen, out2len; 38664562Sgshapiro unsigned int outlen; 38764562Sgshapiro char *volatile auth_type; 38864562Sgshapiro char *mechlist; 38990792Sgshapiro volatile unsigned int n_mechs; 39090792Sgshapiro unsigned int len; 39164562Sgshapiro sasl_security_properties_t ssp; 39264562Sgshapiro sasl_external_properties_t ext_ssf; 39364562Sgshapiro sasl_ssf_t *ssf; 39490792Sgshapiro#endif /* SASL */ 39590792Sgshapiro#if STARTTLS 39664562Sgshapiro int r; 39766494Sgshapiro int rfd, wfd; 39890792Sgshapiro volatile bool tls_active = false; 39990792Sgshapiro# if _FFR_SMTP_SSL 40090792Sgshapiro volatile bool smtps = false; 40190792Sgshapiro# endif /* _FFR_SMTP_SSL */ 40264562Sgshapiro bool saveQuickAbort; 40364562Sgshapiro bool saveSuprErrs; 40490792Sgshapiro time_t tlsstart; 40590792Sgshapiro#endif /* STARTTLS */ 40690792Sgshapiro volatile unsigned int features; 40790792Sgshapiro#if PIPELINING 40890792Sgshapiro# if _FFR_NO_PIPE 40990792Sgshapiro int np_log = 0; 41090792Sgshapiro# endif /* _FFR_NO_PIPE */ 41190792Sgshapiro#endif /* PIPELINING */ 41290792Sgshapiro volatile time_t log_delay = (time_t) 0; 41338032Speter 41490792Sgshapiro smtp.sm_nrcpts = 0; 41590792Sgshapiro#if MILTER 41690792Sgshapiro smtp.sm_milterize = (nullserver == NULL); 41790792Sgshapiro smtp.sm_milterlist = false; 41890792Sgshapiro#endif /* MILTER */ 41990792Sgshapiro 42090792Sgshapiro /* setup I/O fd correctly for the SMTP server */ 42190792Sgshapiro setup_smtpd_io(); 42290792Sgshapiro 42390792Sgshapiro#if SM_HEAP_CHECK 42490792Sgshapiro if (sm_debug_active(&DebugLeakSmtp, 1)) 42538032Speter { 42690792Sgshapiro sm_heap_newgroup(); 42790792Sgshapiro sm_dprintf("smtp() heap group #%d\n", sm_heap_group()); 42838032Speter } 42990792Sgshapiro#endif /* SM_HEAP_CHECK */ 43064562Sgshapiro 43190792Sgshapiro /* XXX the rpool should be set when e is initialized in main() */ 43290792Sgshapiro e->e_rpool = sm_rpool_new_x(NULL); 43390792Sgshapiro e->e_macro.mac_rpool = e->e_rpool; 43490792Sgshapiro 43538032Speter settime(e); 43690792Sgshapiro sm_getla(); 43738032Speter peerhostname = RealHostName; 43838032Speter if (peerhostname == NULL) 43938032Speter peerhostname = "localhost"; 44038032Speter CurHostName = peerhostname; 44138032Speter CurSmtpClient = macvalue('_', e); 44238032Speter if (CurSmtpClient == NULL) 44338032Speter CurSmtpClient = CurHostName; 44438032Speter 44538032Speter /* check_relay may have set discard bit, save for later */ 44690792Sgshapiro smtp.sm_discard = bitset(EF_DISCARD, e->e_flags); 44738032Speter 44890792Sgshapiro#if PIPELINING 44990792Sgshapiro /* auto-flush output when reading input */ 45090792Sgshapiro (void) sm_io_autoflush(InChannel, OutChannel); 45190792Sgshapiro#endif /* PIPELINING */ 45264562Sgshapiro 45390792Sgshapiro sm_setproctitle(true, e, "server %s startup", CurSmtpClient); 45490792Sgshapiro 45590792Sgshapiro /* Set default features for server. */ 45690792Sgshapiro features = ((bitset(PRIV_NOETRN, PrivacyFlags) || 45790792Sgshapiro bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN) 45890792Sgshapiro | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE) 45990792Sgshapiro | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE 46090792Sgshapiro : (SRV_OFFER_EXPN 46190792Sgshapiro | (bitset(PRIV_NOVERB, PrivacyFlags) 46290792Sgshapiro ? SRV_NONE : SRV_OFFER_VERB))) 46390792Sgshapiro | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE 46490792Sgshapiro : SRV_OFFER_DSN) 46590792Sgshapiro#if SASL 46690792Sgshapiro | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH) 46790792Sgshapiro#endif /* SASL */ 46890792Sgshapiro#if PIPELINING 46990792Sgshapiro | SRV_OFFER_PIPE 47090792Sgshapiro#endif /* PIPELINING */ 47190792Sgshapiro#if STARTTLS 47290792Sgshapiro | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS) 47390792Sgshapiro | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE 47490792Sgshapiro : SRV_VRFY_CLT) 47590792Sgshapiro#endif /* STARTTLS */ 47690792Sgshapiro ; 47790792Sgshapiro if (nullserver == NULL) 47890792Sgshapiro { 47990792Sgshapiro features = srvfeatures(e, CurSmtpClient, features); 48090792Sgshapiro if (bitset(SRV_TMP_FAIL, features)) 48190792Sgshapiro { 48290792Sgshapiro if (LogLevel > 4) 48390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 48490792Sgshapiro "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled", 48590792Sgshapiro CurSmtpClient); 48690792Sgshapiro nullserver = "450 4.3.0 Please try again later."; 48790792Sgshapiro } 48890792Sgshapiro#if PIPELINING 48990792Sgshapiro# if _FFR_NO_PIPE 49090792Sgshapiro else if (bitset(SRV_NO_PIPE, features)) 49190792Sgshapiro { 49290792Sgshapiro /* for consistency */ 49390792Sgshapiro features &= ~SRV_OFFER_PIPE; 49490792Sgshapiro } 49590792Sgshapiro# endif /* _FFR_NO_PIPE */ 49690792Sgshapiro#endif /* PIPELINING */ 49790792Sgshapiro } 49890792Sgshapiro 49990792Sgshapiro hostname = macvalue('j', e); 50090792Sgshapiro 50190792Sgshapiro 50290792Sgshapiro#if SASL 50390792Sgshapiro sasl_ok = bitset(SRV_OFFER_AUTH, features); 50464562Sgshapiro n_mechs = 0; 50590792Sgshapiro authenticating = SASL_NOT_AUTH; 50664562Sgshapiro 50764562Sgshapiro /* SASL server new connection */ 50890792Sgshapiro if (sasl_ok) 50938032Speter { 51090792Sgshapiro# if SASL > 10505 51190792Sgshapiro /* use empty realm: only works in SASL > 1.5.5 */ 51290792Sgshapiro result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn); 51390792Sgshapiro# else /* SASL > 10505 */ 51490792Sgshapiro /* use no realm -> realm is set to hostname by SASL lib */ 51590792Sgshapiro result = sasl_server_new("smtp", hostname, NULL, NULL, 0, 51690792Sgshapiro &conn); 51790792Sgshapiro# endif /* SASL > 10505 */ 51890792Sgshapiro sasl_ok = result == SASL_OK; 51990792Sgshapiro if (!sasl_ok) 52090792Sgshapiro { 52190792Sgshapiro if (LogLevel > 9) 52290792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 52390792Sgshapiro "AUTH error: sasl_server_new failed=%d", 52490792Sgshapiro result); 52590792Sgshapiro } 52690792Sgshapiro } 52790792Sgshapiro if (sasl_ok) 52890792Sgshapiro { 52964562Sgshapiro /* 53064562Sgshapiro ** SASL set properties for sasl 53164562Sgshapiro ** set local/remote IP 53264562Sgshapiro ** XXX only IPv4: Cyrus SASL doesn't support anything else 53364562Sgshapiro ** 53464562Sgshapiro ** XXX where exactly are these used/required? 53564562Sgshapiro ** Kerberos_v4 53664562Sgshapiro */ 53764562Sgshapiro 53894334Sgshapiro# if NETINET 53990792Sgshapiro in = macvalue(macid("{daemon_family}"), e); 54064562Sgshapiro if (in != NULL && strcmp(in, "inet") == 0) 54164562Sgshapiro { 54264562Sgshapiro SOCKADDR_LEN_T addrsize; 54364562Sgshapiro struct sockaddr_in saddr_l; 54464562Sgshapiro struct sockaddr_in saddr_r; 54564562Sgshapiro 54664562Sgshapiro addrsize = sizeof(struct sockaddr_in); 54790792Sgshapiro if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 54890792Sgshapiro NULL), 54964562Sgshapiro (struct sockaddr *)&saddr_r, 55064562Sgshapiro &addrsize) == 0) 55164562Sgshapiro { 55264562Sgshapiro sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r); 55364562Sgshapiro addrsize = sizeof(struct sockaddr_in); 55490792Sgshapiro if (getsockname(sm_io_getinfo(InChannel, 55590792Sgshapiro SM_IO_WHAT_FD, 55690792Sgshapiro NULL), 55764562Sgshapiro (struct sockaddr *)&saddr_l, 55864562Sgshapiro &addrsize) == 0) 55964562Sgshapiro sasl_setprop(conn, SASL_IP_LOCAL, 56064562Sgshapiro &saddr_l); 56164562Sgshapiro } 56264562Sgshapiro } 56394334Sgshapiro# endif /* NETINET */ 56464562Sgshapiro 56564562Sgshapiro auth_type = NULL; 56664562Sgshapiro mechlist = NULL; 56764562Sgshapiro user = NULL; 56890792Sgshapiro# if 0 56990792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 57090792Sgshapiro macid("{auth_author}"), NULL); 57190792Sgshapiro# endif /* 0 */ 57264562Sgshapiro 57364562Sgshapiro /* set properties */ 57464562Sgshapiro (void) memset(&ssp, '\0', sizeof ssp); 57590792Sgshapiro 57664562Sgshapiro /* XXX should these be options settable via .cf ? */ 57764562Sgshapiro /* ssp.min_ssf = 0; is default due to memset() */ 57894334Sgshapiro# if STARTTLS 57994334Sgshapiro# endif /* STARTTLS */ 58064562Sgshapiro { 58190792Sgshapiro ssp.max_ssf = MaxSLBits; 58264562Sgshapiro ssp.maxbufsize = MAXOUTLEN; 58364562Sgshapiro } 58464562Sgshapiro ssp.security_flags = SASLOpts & SASL_SEC_MASK; 58564562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK; 58664562Sgshapiro 58764562Sgshapiro if (sasl_ok) 58864562Sgshapiro { 58964562Sgshapiro /* 59064562Sgshapiro ** external security strength factor; 59190792Sgshapiro ** currently we have none so zero 59264562Sgshapiro */ 59390792Sgshapiro 59464562Sgshapiro ext_ssf.ssf = 0; 59564562Sgshapiro ext_ssf.auth_id = NULL; 59664562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, 59764562Sgshapiro &ext_ssf) == SASL_OK; 59864562Sgshapiro } 59964562Sgshapiro if (sasl_ok) 60064562Sgshapiro n_mechs = saslmechs(conn, &mechlist); 60138032Speter } 60290792Sgshapiro#endif /* SASL */ 60338032Speter 60490792Sgshapiro#if MILTER 60590792Sgshapiro if (smtp.sm_milterize) 60664562Sgshapiro { 60764562Sgshapiro char state; 60864562Sgshapiro 60964562Sgshapiro /* initialize mail filter connection */ 61090792Sgshapiro smtp.sm_milterlist = milter_init(e, &state); 61164562Sgshapiro switch (state) 61264562Sgshapiro { 61364562Sgshapiro case SMFIR_REJECT: 61490792Sgshapiro if (MilterLogLevel > 3) 61590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 61694334Sgshapiro "Milter: initialization failed, rejecting commands"); 61764562Sgshapiro greetcode = "554"; 61864562Sgshapiro nullserver = "Command rejected"; 61990792Sgshapiro smtp.sm_milterize = false; 62064562Sgshapiro break; 62164562Sgshapiro 62264562Sgshapiro case SMFIR_TEMPFAIL: 62390792Sgshapiro if (MilterLogLevel > 3) 62490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 62594334Sgshapiro "Milter: initialization failed, temp failing commands"); 62690792Sgshapiro tempfail = true; 62790792Sgshapiro smtp.sm_milterize = false; 62864562Sgshapiro break; 62964562Sgshapiro } 63064562Sgshapiro } 63164562Sgshapiro 63290792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 63390792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 63464562Sgshapiro { 63564562Sgshapiro char state; 63690792Sgshapiro char *response; 63764562Sgshapiro 63890792Sgshapiro response = milter_connect(peerhostname, RealHostAddr, 63990792Sgshapiro e, &state); 64064562Sgshapiro switch (state) 64164562Sgshapiro { 64264562Sgshapiro case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */ 64364562Sgshapiro case SMFIR_REJECT: 64490792Sgshapiro if (MilterLogLevel > 3) 64590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 64690792Sgshapiro "Milter: connect: host=%s, addr=%s, rejecting commands", 64790792Sgshapiro peerhostname, 64890792Sgshapiro anynet_ntoa(&RealHostAddr)); 64964562Sgshapiro greetcode = "554"; 65064562Sgshapiro nullserver = "Command rejected"; 65190792Sgshapiro smtp.sm_milterize = false; 65264562Sgshapiro break; 65364562Sgshapiro 65464562Sgshapiro case SMFIR_TEMPFAIL: 65590792Sgshapiro if (MilterLogLevel > 3) 65690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 65790792Sgshapiro "Milter: connect: host=%s, addr=%s, temp failing commands", 65890792Sgshapiro peerhostname, 65990792Sgshapiro anynet_ntoa(&RealHostAddr)); 66090792Sgshapiro tempfail = true; 66190792Sgshapiro smtp.sm_milterize = false; 66264562Sgshapiro break; 66364562Sgshapiro } 66490792Sgshapiro if (response != NULL) 66590792Sgshapiro 66690792Sgshapiro sm_free(response); /* XXX */ 66764562Sgshapiro } 66890792Sgshapiro#endif /* MILTER */ 66964562Sgshapiro 67090792Sgshapiro#if STARTTLS 67190792Sgshapiro# if _FFR_SMTP_SSL 67290792Sgshapiro /* If this an smtps connection, start TLS now */ 67390792Sgshapiro smtps = bitnset(D_SMTPS, d_flags); 67490792Sgshapiro if (smtps) 67590792Sgshapiro goto starttls; 67690792Sgshapiro 67790792Sgshapiro greeting: 67890792Sgshapiro 67990792Sgshapiro# endif /* _FFR_SMTP_SSL */ 68090792Sgshapiro#endif /* STARTTLS */ 68190792Sgshapiro 68238032Speter /* output the first line, inserting "ESMTP" as second word */ 68390792Sgshapiro if (*greetcode == '5') 68490792Sgshapiro (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages", 68590792Sgshapiro hostname); 68690792Sgshapiro else 68790792Sgshapiro expand(SmtpGreeting, inp, sizeof inp, e); 68890792Sgshapiro 68938032Speter p = strchr(inp, '\n'); 69038032Speter if (p != NULL) 69138032Speter *p++ = '\0'; 69238032Speter id = strchr(inp, ' '); 69338032Speter if (id == NULL) 69438032Speter id = &inp[strlen(inp)]; 69564562Sgshapiro if (p == NULL) 69690792Sgshapiro (void) sm_snprintf(cmdbuf, sizeof cmdbuf, 69764562Sgshapiro "%s %%.*s ESMTP%%s", greetcode); 69864562Sgshapiro else 69990792Sgshapiro (void) sm_snprintf(cmdbuf, sizeof cmdbuf, 70064562Sgshapiro "%s-%%.*s ESMTP%%s", greetcode); 70166494Sgshapiro message(cmdbuf, (int) (id - inp), inp, id); 70238032Speter 70338032Speter /* output remaining lines */ 70438032Speter while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 70538032Speter { 70638032Speter *p++ = '\0'; 70738032Speter if (isascii(*id) && isspace(*id)) 70838032Speter id++; 70990792Sgshapiro (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s"); 71064562Sgshapiro message(cmdbuf, id); 71138032Speter } 71238032Speter if (id != NULL) 71338032Speter { 71438032Speter if (isascii(*id) && isspace(*id)) 71538032Speter id++; 71690792Sgshapiro (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s"); 71764562Sgshapiro message(cmdbuf, id); 71838032Speter } 71938032Speter 72038032Speter protocol = NULL; 72138032Speter sendinghost = macvalue('s', e); 72290792Sgshapiro 72390792Sgshapiro#if _FFR_QUARANTINE 72490792Sgshapiro /* If quarantining by a connect/ehlo action, save between messages */ 72590792Sgshapiro if (e->e_quarmsg == NULL) 72690792Sgshapiro smtp.sm_quarmsg = NULL; 72790792Sgshapiro else 72890792Sgshapiro smtp.sm_quarmsg = newstr(e->e_quarmsg); 72990792Sgshapiro#endif /* _FFR_QUARANTINE */ 73090792Sgshapiro 73190792Sgshapiro /* sendinghost's storage must outlive the current envelope */ 73290792Sgshapiro if (sendinghost != NULL) 73390792Sgshapiro sendinghost = sm_strdup_x(sendinghost); 73490792Sgshapiro#if _FFR_ADAPTIVE_EOL 73590792Sgshapiro first = true; 73690792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */ 73790792Sgshapiro gothello = false; 73890792Sgshapiro smtp.sm_gotmail = false; 73938032Speter for (;;) 74038032Speter { 74190792Sgshapiro SM_TRY 74290792Sgshapiro { 74390792Sgshapiro QuickAbort = false; 74490792Sgshapiro HoldErrs = false; 74590792Sgshapiro SuprErrs = false; 74690792Sgshapiro LogUsrErrs = false; 74790792Sgshapiro OnlyOneError = true; 74838032Speter e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 74938032Speter 75038032Speter /* setup for the read */ 75138032Speter e->e_to = NULL; 75238032Speter Errors = 0; 75364562Sgshapiro FileName = NULL; 75490792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 75538032Speter 75638032Speter /* read the input line */ 75738032Speter SmtpPhase = "server cmd read"; 75890792Sgshapiro sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient); 75990792Sgshapiro#if SASL 76064562Sgshapiro /* 76190792Sgshapiro ** XXX SMTP AUTH requires accepting any length, 76290792Sgshapiro ** at least for challenge/response 76364562Sgshapiro */ 76490792Sgshapiro#endif /* SASL */ 76538032Speter 76638032Speter /* handle errors */ 76790792Sgshapiro if (sm_io_error(OutChannel) || 76864562Sgshapiro (p = sfgets(inp, sizeof inp, InChannel, 76964562Sgshapiro TimeOuts.to_nextcommand, SmtpPhase)) == NULL) 77038032Speter { 77164562Sgshapiro char *d; 77264562Sgshapiro 77390792Sgshapiro d = macvalue(macid("{daemon_name}"), e); 77464562Sgshapiro if (d == NULL) 77564562Sgshapiro d = "stdin"; 77638032Speter /* end of file, just die */ 77738032Speter disconnect(1, e); 77864562Sgshapiro 77990792Sgshapiro#if MILTER 78064562Sgshapiro /* close out milter filters */ 78164562Sgshapiro milter_quit(e); 78290792Sgshapiro#endif /* MILTER */ 78364562Sgshapiro 78464562Sgshapiro message("421 4.4.1 %s Lost input channel from %s", 78538032Speter MyHostName, CurSmtpClient); 78690792Sgshapiro if (LogLevel > (smtp.sm_gotmail ? 1 : 19)) 78738032Speter sm_syslog(LOG_NOTICE, e->e_id, 78864562Sgshapiro "lost input channel from %.100s to %s after %s", 78964562Sgshapiro CurSmtpClient, d, 79064562Sgshapiro (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name); 79138032Speter /* 79242575Speter ** If have not accepted mail (DATA), do not bounce 79342575Speter ** bad addresses back to sender. 79438032Speter */ 79564562Sgshapiro 79638032Speter if (bitset(EF_CLRQUEUE, e->e_flags)) 79738032Speter e->e_sendqueue = NULL; 79864562Sgshapiro goto doquit; 79938032Speter } 80038032Speter 80190792Sgshapiro#if _FFR_ADAPTIVE_EOL 80290792Sgshapiro if (first) 80390792Sgshapiro { 80490792Sgshapiro char *p; 80590792Sgshapiro 80690792Sgshapiro smtp.sm_crlf = true; 80790792Sgshapiro p = strchr(inp, '\n'); 80890792Sgshapiro if (p == NULL || p <= inp || p[-1] != '\r') 80990792Sgshapiro { 81090792Sgshapiro smtp.sm_crlf = false; 81190792Sgshapiro if (tTd(66, 1) && LogLevel > 8) 81290792Sgshapiro { 81390792Sgshapiro /* how many bad guys are there? */ 81490792Sgshapiro sm_syslog(LOG_INFO, NOQID, 81590792Sgshapiro "%.100s did not use CRLF", 81690792Sgshapiro CurSmtpClient); 81790792Sgshapiro } 81890792Sgshapiro } 81990792Sgshapiro first = false; 82090792Sgshapiro } 82190792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */ 82290792Sgshapiro 82338032Speter /* clean up end of line */ 82490792Sgshapiro fixcrlf(inp, true); 82538032Speter 82690792Sgshapiro#if PIPELINING 82790792Sgshapiro# if _FFR_NO_PIPE 82890792Sgshapiro /* 82990792Sgshapiro ** if there is more input and pipelining is disabled: 83090792Sgshapiro ** delay ... (and maybe discard the input?) 83190792Sgshapiro ** XXX this doesn't really work, at least in tests using 83290792Sgshapiro ** telnet SM_IO_IS_READABLE only returns 1 if there were 83390792Sgshapiro ** more than 2 input lines available. 83490792Sgshapiro */ 83590792Sgshapiro 83690792Sgshapiro if (bitset(SRV_NO_PIPE, features) && 83790792Sgshapiro sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL)) 83890792Sgshapiro { 83990792Sgshapiro if (++np_log < 3) 84090792Sgshapiro sm_syslog(LOG_INFO, NOQID, 84190792Sgshapiro "unauthorized PIPELINING, sleeping"); 84290792Sgshapiro sleep(1); 84390792Sgshapiro } 84490792Sgshapiro 84590792Sgshapiro# endif /* _FFR_NO_PIPE */ 84690792Sgshapiro#endif /* PIPELINING */ 84790792Sgshapiro 84890792Sgshapiro#if SASL 84964562Sgshapiro if (authenticating == SASL_PROC_AUTH) 85064562Sgshapiro { 85190792Sgshapiro# if 0 85264562Sgshapiro if (*inp == '\0') 85364562Sgshapiro { 85464562Sgshapiro authenticating = SASL_NOT_AUTH; 85564562Sgshapiro message("501 5.5.2 missing input"); 85664562Sgshapiro continue; 85764562Sgshapiro } 85890792Sgshapiro# endif /* 0 */ 85964562Sgshapiro if (*inp == '*' && *(inp + 1) == '\0') 86064562Sgshapiro { 86164562Sgshapiro authenticating = SASL_NOT_AUTH; 86264562Sgshapiro 86364562Sgshapiro /* rfc 2254 4. */ 86464562Sgshapiro message("501 5.0.0 AUTH aborted"); 86564562Sgshapiro continue; 86664562Sgshapiro } 86764562Sgshapiro 86864562Sgshapiro /* could this be shorter? XXX */ 86964562Sgshapiro out = xalloc(strlen(inp)); 87064562Sgshapiro result = sasl_decode64(inp, strlen(inp), out, &outlen); 87164562Sgshapiro if (result != SASL_OK) 87264562Sgshapiro { 87364562Sgshapiro authenticating = SASL_NOT_AUTH; 87464562Sgshapiro 87564562Sgshapiro /* rfc 2254 4. */ 87664562Sgshapiro message("501 5.5.4 cannot decode AUTH parameter %s", 87764562Sgshapiro inp); 87864562Sgshapiro continue; 87964562Sgshapiro } 88064562Sgshapiro 88164562Sgshapiro result = sasl_server_step(conn, out, outlen, 88264562Sgshapiro &out, &outlen, &errstr); 88364562Sgshapiro 88464562Sgshapiro /* get an OK if we're done */ 88564562Sgshapiro if (result == SASL_OK) 88664562Sgshapiro { 88764562Sgshapiro authenticated: 88864562Sgshapiro message("235 2.0.0 OK Authenticated"); 88964562Sgshapiro authenticating = SASL_IS_AUTH; 89090792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 89190792Sgshapiro macid("{auth_type}"), auth_type); 89264562Sgshapiro 89364562Sgshapiro result = sasl_getprop(conn, SASL_USERNAME, 89464562Sgshapiro (void **)&user); 89564562Sgshapiro if (result != SASL_OK) 89664562Sgshapiro { 89764562Sgshapiro user = ""; 89890792Sgshapiro macdefine(&BlankEnvelope.e_macro, 89990792Sgshapiro A_PERM, 90090792Sgshapiro macid("{auth_authen}"), NULL); 90164562Sgshapiro } 90264562Sgshapiro else 90364562Sgshapiro { 90490792Sgshapiro macdefine(&BlankEnvelope.e_macro, 90590792Sgshapiro A_TEMP, 90690792Sgshapiro macid("{auth_authen}"), user); 90764562Sgshapiro } 90864562Sgshapiro 90990792Sgshapiro# if 0 91064562Sgshapiro /* get realm? */ 91164562Sgshapiro sasl_getprop(conn, SASL_REALM, (void **) &data); 91290792Sgshapiro# endif /* 0 */ 91364562Sgshapiro 91464562Sgshapiro /* get security strength (features) */ 91564562Sgshapiro result = sasl_getprop(conn, SASL_SSF, 91664562Sgshapiro (void **) &ssf); 91764562Sgshapiro if (result != SASL_OK) 91864562Sgshapiro { 91990792Sgshapiro macdefine(&BlankEnvelope.e_macro, 92090792Sgshapiro A_PERM, 92190792Sgshapiro macid("{auth_ssf}"), "0"); 92264562Sgshapiro ssf = NULL; 92364562Sgshapiro } 92464562Sgshapiro else 92564562Sgshapiro { 92664562Sgshapiro char pbuf[8]; 92764562Sgshapiro 92890792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, 92990792Sgshapiro "%u", *ssf); 93090792Sgshapiro macdefine(&BlankEnvelope.e_macro, 93190792Sgshapiro A_TEMP, 93290792Sgshapiro macid("{auth_ssf}"), pbuf); 93364562Sgshapiro if (tTd(95, 8)) 93490792Sgshapiro sm_dprintf("AUTH auth_ssf: %u\n", 93590792Sgshapiro *ssf); 93664562Sgshapiro } 93790792Sgshapiro 93864562Sgshapiro /* 93990792Sgshapiro ** Only switch to encrypted connection 94064562Sgshapiro ** if a security layer has been negotiated 94164562Sgshapiro */ 94290792Sgshapiro 94364562Sgshapiro if (ssf != NULL && *ssf > 0) 94464562Sgshapiro { 94564562Sgshapiro /* 94690792Sgshapiro ** Convert I/O layer to use SASL. 94790792Sgshapiro ** If the call fails, the connection 94890792Sgshapiro ** is aborted. 94964562Sgshapiro */ 95090792Sgshapiro 95190792Sgshapiro if (sfdcsasl(&InChannel, &OutChannel, 95290792Sgshapiro conn) == 0) 95364562Sgshapiro { 95464562Sgshapiro /* restart dialogue */ 95564562Sgshapiro n_helo = 0; 95694334Sgshapiro# if PIPELINING 95790792Sgshapiro (void) sm_io_autoflush(InChannel, 95890792Sgshapiro OutChannel); 95994334Sgshapiro# endif /* PIPELINING */ 96064562Sgshapiro } 96164562Sgshapiro else 96264562Sgshapiro syserr("503 5.3.3 SASL TLS failed"); 96364562Sgshapiro } 96490792Sgshapiro 96590792Sgshapiro /* NULL pointer ok since it's our function */ 96690792Sgshapiro if (LogLevel > 8) 96764562Sgshapiro sm_syslog(LOG_INFO, NOQID, 96890792Sgshapiro "AUTH=server, relay=%.100s, authid=%.128s, mech=%.16s, bits=%d", 96990792Sgshapiro CurSmtpClient, 97090792Sgshapiro shortenstring(user, 128), 97190792Sgshapiro auth_type, *ssf); 97264562Sgshapiro } 97364562Sgshapiro else if (result == SASL_CONTINUE) 97464562Sgshapiro { 97564562Sgshapiro len = ENC64LEN(outlen); 97664562Sgshapiro out2 = xalloc(len); 97764562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 97890792Sgshapiro &out2len); 97964562Sgshapiro if (result != SASL_OK) 98064562Sgshapiro { 98164562Sgshapiro /* correct code? XXX */ 98264562Sgshapiro /* 454 Temp. authentication failure */ 98364562Sgshapiro message("454 4.5.4 Internal error: unable to encode64"); 98464562Sgshapiro if (LogLevel > 5) 98564562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 98690792Sgshapiro "AUTH encode64 error [%d for \"%s\"]", 98764562Sgshapiro result, out); 98864562Sgshapiro /* start over? */ 98964562Sgshapiro authenticating = SASL_NOT_AUTH; 99064562Sgshapiro } 99164562Sgshapiro else 99264562Sgshapiro { 99364562Sgshapiro message("334 %s", out2); 99464562Sgshapiro if (tTd(95, 2)) 99590792Sgshapiro sm_dprintf("AUTH continue: msg='%s' len=%u\n", 99690792Sgshapiro out2, out2len); 99764562Sgshapiro } 99864562Sgshapiro } 99964562Sgshapiro else 100064562Sgshapiro { 100164562Sgshapiro /* not SASL_OK or SASL_CONT */ 100264562Sgshapiro message("500 5.7.0 authentication failed"); 100364562Sgshapiro if (LogLevel > 9) 100464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 100590792Sgshapiro "AUTH failure (%s): %s (%d) %s", 100664562Sgshapiro auth_type, 100764562Sgshapiro sasl_errstring(result, NULL, 100864562Sgshapiro NULL), 100990792Sgshapiro result, 101090792Sgshapiro errstr == NULL ? "" : errstr); 101164562Sgshapiro authenticating = SASL_NOT_AUTH; 101264562Sgshapiro } 101364562Sgshapiro } 101464562Sgshapiro else 101564562Sgshapiro { 101664562Sgshapiro /* don't want to do any of this if authenticating */ 101790792Sgshapiro#endif /* SASL */ 101864562Sgshapiro 101938032Speter /* echo command to transcript */ 102038032Speter if (e->e_xfp != NULL) 102190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 102290792Sgshapiro "<<< %s\n", inp); 102338032Speter 102490792Sgshapiro if (LogLevel > 14) 102590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp); 102638032Speter 102738032Speter /* break off command */ 102838032Speter for (p = inp; isascii(*p) && isspace(*p); p++) 102938032Speter continue; 103038032Speter cmd = cmdbuf; 103138032Speter while (*p != '\0' && 103238032Speter !(isascii(*p) && isspace(*p)) && 103338032Speter cmd < &cmdbuf[sizeof cmdbuf - 2]) 103438032Speter *cmd++ = *p++; 103538032Speter *cmd = '\0'; 103638032Speter 103738032Speter /* throw away leading whitespace */ 103890792Sgshapiro SKIP_SPACE(p); 103938032Speter 104038032Speter /* decode command */ 104164562Sgshapiro for (c = CmdTab; c->cmd_name != NULL; c++) 104238032Speter { 104390792Sgshapiro if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) 104438032Speter break; 104538032Speter } 104638032Speter 104738032Speter /* reset errors */ 104838032Speter errno = 0; 104938032Speter 105090792Sgshapiro /* check whether a "non-null" command has been used */ 105190792Sgshapiro switch (c->cmd_code) 105290792Sgshapiro { 105390792Sgshapiro#if SASL 105490792Sgshapiro case CMDAUTH: 105590792Sgshapiro /* avoid information leak; take first two words? */ 105690792Sgshapiro q = "AUTH"; 105790792Sgshapiro break; 105890792Sgshapiro#endif /* SASL */ 105990792Sgshapiro 106090792Sgshapiro case CMDMAIL: 106190792Sgshapiro case CMDEXPN: 106290792Sgshapiro case CMDVRFY: 106390792Sgshapiro case CMDETRN: 106490792Sgshapiro lognullconnection = false; 106590792Sgshapiro /* FALLTHROUGH */ 106690792Sgshapiro default: 106790792Sgshapiro q = inp; 106890792Sgshapiro break; 106990792Sgshapiro } 107090792Sgshapiro 107190792Sgshapiro if (e->e_id == NULL) 107290792Sgshapiro sm_setproctitle(true, e, "%s: %.80s", 107390792Sgshapiro CurSmtpClient, q); 107490792Sgshapiro else 107590792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", 107690792Sgshapiro qid_printname(e), 107790792Sgshapiro CurSmtpClient, q); 107890792Sgshapiro 107938032Speter /* 108038032Speter ** Process command. 108138032Speter ** 108238032Speter ** If we are running as a null server, return 550 108390792Sgshapiro ** to almost everything. 108438032Speter */ 108538032Speter 108664562Sgshapiro if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags)) 108738032Speter { 108864562Sgshapiro switch (c->cmd_code) 108938032Speter { 109038032Speter case CMDQUIT: 109138032Speter case CMDHELO: 109238032Speter case CMDEHLO: 109338032Speter case CMDNOOP: 109464562Sgshapiro case CMDRSET: 109538032Speter /* process normally */ 109638032Speter break; 109738032Speter 109864562Sgshapiro case CMDETRN: 109964562Sgshapiro if (bitnset(D_ETRNONLY, d_flags) && 110064562Sgshapiro nullserver == NULL) 110164562Sgshapiro break; 110290792Sgshapiro DELAY_CONN("ETRN"); 110380785Sgshapiro /* FALLTHROUGH */ 110464562Sgshapiro 110538032Speter default: 110690792Sgshapiro#if MAXBADCOMMANDS > 0 110790792Sgshapiro /* theoretically this could overflow */ 110890792Sgshapiro if (nullserver != NULL && 110990792Sgshapiro ++n_badcmds > MAXBADCOMMANDS) 111064562Sgshapiro { 111190792Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 111290792Sgshapiro MyHostName); 111390792Sgshapiro 111490792Sgshapiro /* arrange to ignore send list */ 111590792Sgshapiro e->e_sendqueue = NULL; 111690792Sgshapiro goto doquit; 111764562Sgshapiro } 111890792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 111964562Sgshapiro if (nullserver != NULL) 112064562Sgshapiro { 112164562Sgshapiro if (ISSMTPREPLY(nullserver)) 112264562Sgshapiro usrerr(nullserver); 112364562Sgshapiro else 112490792Sgshapiro usrerr("550 5.0.0 %s", 112590792Sgshapiro nullserver); 112664562Sgshapiro } 112764562Sgshapiro else 112864562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 112938032Speter continue; 113038032Speter } 113138032Speter } 113238032Speter 113364562Sgshapiro switch (c->cmd_code) 113438032Speter { 113590792Sgshapiro#if SASL 113664562Sgshapiro case CMDAUTH: /* sasl */ 113790792Sgshapiro DELAY_CONN("AUTH"); 113890792Sgshapiro if (!sasl_ok || n_mechs <= 0) 113964562Sgshapiro { 114064562Sgshapiro message("503 5.3.3 AUTH not available"); 114164562Sgshapiro break; 114264562Sgshapiro } 114364562Sgshapiro if (authenticating == SASL_IS_AUTH) 114464562Sgshapiro { 114564562Sgshapiro message("503 5.5.0 Already Authenticated"); 114664562Sgshapiro break; 114764562Sgshapiro } 114890792Sgshapiro if (smtp.sm_gotmail) 114964562Sgshapiro { 115064562Sgshapiro message("503 5.5.0 AUTH not permitted during a mail transaction"); 115164562Sgshapiro break; 115264562Sgshapiro } 115364562Sgshapiro if (tempfail) 115464562Sgshapiro { 115564562Sgshapiro if (LogLevel > 9) 115664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 115764562Sgshapiro "SMTP AUTH command (%.100s) from %.100s tempfailed (due to previous checks)", 115864562Sgshapiro p, CurSmtpClient); 115964562Sgshapiro usrerr("454 4.7.1 Please try again later"); 116064562Sgshapiro break; 116164562Sgshapiro } 116264562Sgshapiro 116390792Sgshapiro ismore = false; 116464562Sgshapiro 116564562Sgshapiro /* crude way to avoid crack attempts */ 116690792Sgshapiro (void) checksmtpattack(&n_auth, n_mechs + 1, true, 116764562Sgshapiro "AUTH", e); 116864562Sgshapiro 116990792Sgshapiro /* make sure mechanism (p) is a valid string */ 117064562Sgshapiro for (q = p; *q != '\0' && isascii(*q); q++) 117164562Sgshapiro { 117264562Sgshapiro if (isspace(*q)) 117364562Sgshapiro { 117464562Sgshapiro *q = '\0'; 117564562Sgshapiro while (*++q != '\0' && 117664562Sgshapiro isascii(*q) && isspace(*q)) 117764562Sgshapiro continue; 117864562Sgshapiro *(q - 1) = '\0'; 117964562Sgshapiro ismore = (*q != '\0'); 118064562Sgshapiro break; 118164562Sgshapiro } 118264562Sgshapiro } 118364562Sgshapiro 118464562Sgshapiro /* check whether mechanism is available */ 118564562Sgshapiro if (iteminlist(p, mechlist, " ") == NULL) 118664562Sgshapiro { 118790792Sgshapiro message("503 5.3.3 AUTH mechanism %.32s not available", 118864562Sgshapiro p); 118964562Sgshapiro break; 119064562Sgshapiro } 119164562Sgshapiro 119264562Sgshapiro if (ismore) 119364562Sgshapiro { 119464562Sgshapiro /* could this be shorter? XXX */ 119590792Sgshapiro in = sm_rpool_malloc(e->e_rpool, strlen(q)); 119664562Sgshapiro result = sasl_decode64(q, strlen(q), in, 119790792Sgshapiro &inlen); 119864562Sgshapiro if (result != SASL_OK) 119964562Sgshapiro { 120064562Sgshapiro message("501 5.5.4 cannot BASE64 decode '%s'", 120164562Sgshapiro q); 120264562Sgshapiro if (LogLevel > 5) 120364562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 120490792Sgshapiro "AUTH decode64 error [%d for \"%s\"]", 120564562Sgshapiro result, q); 120664562Sgshapiro /* start over? */ 120764562Sgshapiro authenticating = SASL_NOT_AUTH; 120864562Sgshapiro in = NULL; 120964562Sgshapiro inlen = 0; 121064562Sgshapiro break; 121164562Sgshapiro } 121264562Sgshapiro } 121364562Sgshapiro else 121464562Sgshapiro { 121564562Sgshapiro in = NULL; 121664562Sgshapiro inlen = 0; 121764562Sgshapiro } 121864562Sgshapiro 121964562Sgshapiro /* see if that auth type exists */ 122064562Sgshapiro result = sasl_server_start(conn, p, in, inlen, 122164562Sgshapiro &out, &outlen, &errstr); 122264562Sgshapiro 122364562Sgshapiro if (result != SASL_OK && result != SASL_CONTINUE) 122464562Sgshapiro { 122564562Sgshapiro message("500 5.7.0 authentication failed"); 122664562Sgshapiro if (LogLevel > 9) 122764562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 122890792Sgshapiro "AUTH failure (%s): %s (%d) %s", 122964562Sgshapiro p, 123064562Sgshapiro sasl_errstring(result, NULL, 123164562Sgshapiro NULL), 123290792Sgshapiro result, 123390792Sgshapiro errstr); 123464562Sgshapiro break; 123564562Sgshapiro } 123664562Sgshapiro auth_type = newstr(p); 123764562Sgshapiro 123864562Sgshapiro if (result == SASL_OK) 123964562Sgshapiro { 124064562Sgshapiro /* ugly, but same code */ 124164562Sgshapiro goto authenticated; 124264562Sgshapiro /* authenticated by the initial response */ 124364562Sgshapiro } 124464562Sgshapiro 124564562Sgshapiro /* len is at least 2 */ 124664562Sgshapiro len = ENC64LEN(outlen); 124764562Sgshapiro out2 = xalloc(len); 124864562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 124990792Sgshapiro &out2len); 125064562Sgshapiro 125164562Sgshapiro if (result != SASL_OK) 125264562Sgshapiro { 125364562Sgshapiro message("454 4.5.4 Temporary authentication failure"); 125464562Sgshapiro if (LogLevel > 5) 125564562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 125690792Sgshapiro "AUTH encode64 error [%d for \"%s\"]", 125764562Sgshapiro result, out); 125864562Sgshapiro 125964562Sgshapiro /* start over? */ 126064562Sgshapiro authenticating = SASL_NOT_AUTH; 126164562Sgshapiro } 126264562Sgshapiro else 126364562Sgshapiro { 126464562Sgshapiro message("334 %s", out2); 126564562Sgshapiro authenticating = SASL_PROC_AUTH; 126664562Sgshapiro } 126764562Sgshapiro break; 126890792Sgshapiro#endif /* SASL */ 126964562Sgshapiro 127090792Sgshapiro#if STARTTLS 127164562Sgshapiro case CMDSTLS: /* starttls */ 127290792Sgshapiro DELAY_CONN("STARTTLS"); 127364562Sgshapiro if (*p != '\0') 127464562Sgshapiro { 127564562Sgshapiro message("501 5.5.2 Syntax error (no parameters allowed)"); 127664562Sgshapiro break; 127764562Sgshapiro } 127890792Sgshapiro if (!bitset(SRV_OFFER_TLS, features)) 127964562Sgshapiro { 128064562Sgshapiro message("503 5.5.0 TLS not available"); 128164562Sgshapiro break; 128264562Sgshapiro } 128377349Sgshapiro if (!tls_ok_srv) 128464562Sgshapiro { 128564562Sgshapiro message("454 4.3.3 TLS not available after start"); 128664562Sgshapiro break; 128764562Sgshapiro } 128890792Sgshapiro if (smtp.sm_gotmail) 128964562Sgshapiro { 129064562Sgshapiro message("503 5.5.0 TLS not permitted during a mail transaction"); 129164562Sgshapiro break; 129264562Sgshapiro } 129364562Sgshapiro if (tempfail) 129464562Sgshapiro { 129564562Sgshapiro if (LogLevel > 9) 129664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 129764562Sgshapiro "SMTP STARTTLS command (%.100s) from %.100s tempfailed (due to previous checks)", 129864562Sgshapiro p, CurSmtpClient); 129964562Sgshapiro usrerr("454 4.7.1 Please try again later"); 130064562Sgshapiro break; 130164562Sgshapiro } 130290792Sgshapiro# if _FFR_SMTP_SSL 130390792Sgshapiro starttls: 130490792Sgshapiro# endif /* _FFR_SMTP_SSL */ 130564562Sgshapiro# if TLS_NO_RSA 130664562Sgshapiro /* 130764562Sgshapiro ** XXX do we need a temp key ? 130864562Sgshapiro */ 130964562Sgshapiro# else /* TLS_NO_RSA */ 131064562Sgshapiro# endif /* TLS_NO_RSA */ 131190792Sgshapiro 131290792Sgshapiro# if TLS_VRFY_PER_CTX 131390792Sgshapiro /* 131490792Sgshapiro ** Note: this sets the verification globally 131590792Sgshapiro ** (per SSL_CTX) 131690792Sgshapiro ** it's ok since it applies only to one transaction 131790792Sgshapiro */ 131890792Sgshapiro 131990792Sgshapiro TLS_VERIFY_CLIENT(); 132090792Sgshapiro# endif /* TLS_VRFY_PER_CTX */ 132190792Sgshapiro 132264562Sgshapiro if (srv_ssl != NULL) 132364562Sgshapiro SSL_clear(srv_ssl); 132464562Sgshapiro else if ((srv_ssl = SSL_new(srv_ctx)) == NULL) 132564562Sgshapiro { 132664562Sgshapiro message("454 4.3.3 TLS not available: error generating SSL handle"); 132790792Sgshapiro# if _FFR_SMTP_SSL 132890792Sgshapiro goto tls_done; 132990792Sgshapiro# else /* _FFR_SMTP_SSL */ 133064562Sgshapiro break; 133190792Sgshapiro# endif /* _FFR_SMTP_SSL */ 133264562Sgshapiro } 133390792Sgshapiro 133490792Sgshapiro# if !TLS_VRFY_PER_CTX 133590792Sgshapiro /* 133690792Sgshapiro ** this could be used if it were possible to set 133790792Sgshapiro ** verification per SSL (connection) 133890792Sgshapiro ** not just per SSL_CTX (global) 133990792Sgshapiro */ 134090792Sgshapiro 134190792Sgshapiro TLS_VERIFY_CLIENT(); 134290792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */ 134390792Sgshapiro 134490792Sgshapiro rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 134590792Sgshapiro wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); 134690792Sgshapiro 134766494Sgshapiro if (rfd < 0 || wfd < 0 || 134866494Sgshapiro SSL_set_rfd(srv_ssl, rfd) <= 0 || 134966494Sgshapiro SSL_set_wfd(srv_ssl, wfd) <= 0) 135064562Sgshapiro { 135164562Sgshapiro message("454 4.3.3 TLS not available: error set fd"); 135264562Sgshapiro SSL_free(srv_ssl); 135364562Sgshapiro srv_ssl = NULL; 135490792Sgshapiro# if _FFR_SMTP_SSL 135590792Sgshapiro goto tls_done; 135690792Sgshapiro# else /* _FFR_SMTP_SSL */ 135764562Sgshapiro break; 135890792Sgshapiro# endif /* _FFR_SMTP_SSL */ 135964562Sgshapiro } 136090792Sgshapiro# if _FFR_SMTP_SSL 136190792Sgshapiro if (!smtps) 136290792Sgshapiro# endif /* _FFR_SMTP_SSL */ 136390792Sgshapiro message("220 2.0.0 Ready to start TLS"); 136490792Sgshapiro# if PIPELINING 136590792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 136690792Sgshapiro# endif /* PIPELINING */ 136790792Sgshapiro 136864562Sgshapiro SSL_set_accept_state(srv_ssl); 136964562Sgshapiro 137064562Sgshapiro# define SSL_ACC(s) SSL_accept(s) 137190792Sgshapiro 137290792Sgshapiro tlsstart = curtime(); 137390792Sgshapiro ssl_retry: 137464562Sgshapiro if ((r = SSL_ACC(srv_ssl)) <= 0) 137564562Sgshapiro { 137664562Sgshapiro int i; 137790792Sgshapiro bool timedout; 137890792Sgshapiro time_t left; 137990792Sgshapiro time_t now = curtime(); 138090792Sgshapiro struct timeval tv; 138164562Sgshapiro 138264562Sgshapiro /* what to do in this case? */ 138364562Sgshapiro i = SSL_get_error(srv_ssl, r); 138490792Sgshapiro 138590792Sgshapiro /* 138690792Sgshapiro ** For SSL_ERROR_WANT_{READ,WRITE}: 138790792Sgshapiro ** There is no SSL record available yet 138890792Sgshapiro ** or there is only a partial SSL record 138990792Sgshapiro ** removed from the network (socket) buffer 139090792Sgshapiro ** into the SSL buffer. The SSL_accept will 139190792Sgshapiro ** only succeed when a full SSL record is 139290792Sgshapiro ** available (assuming a "real" error 139390792Sgshapiro ** doesn't happen). To handle when a "real" 139490792Sgshapiro ** error does happen the select is set for 139590792Sgshapiro ** exceptions too. 139690792Sgshapiro ** The connection may be re-negotiated 139790792Sgshapiro ** during this time so both read and write 139890792Sgshapiro ** "want errors" need to be handled. 139990792Sgshapiro ** A select() exception loops back so that 140090792Sgshapiro ** a proper SSL error message can be gotten. 140190792Sgshapiro */ 140290792Sgshapiro 140390792Sgshapiro left = TimeOuts.to_starttls - (now - tlsstart); 140490792Sgshapiro timedout = left <= 0; 140590792Sgshapiro if (!timedout) 140690792Sgshapiro { 140790792Sgshapiro tv.tv_sec = left; 140890792Sgshapiro tv.tv_usec = 0; 140990792Sgshapiro } 141090792Sgshapiro 141190792Sgshapiro /* XXX what about SSL_pending() ? */ 141290792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_READ) 141390792Sgshapiro { 141490792Sgshapiro fd_set ssl_maskr, ssl_maskx; 141590792Sgshapiro 141690792Sgshapiro FD_ZERO(&ssl_maskr); 141790792Sgshapiro FD_SET(rfd, &ssl_maskr); 141890792Sgshapiro FD_ZERO(&ssl_maskx); 141990792Sgshapiro FD_SET(rfd, &ssl_maskx); 142090792Sgshapiro if (select(rfd + 1, &ssl_maskr, NULL, 142190792Sgshapiro &ssl_maskx, &tv) > 0) 142290792Sgshapiro goto ssl_retry; 142390792Sgshapiro } 142490792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_WRITE) 142590792Sgshapiro { 142690792Sgshapiro fd_set ssl_maskw, ssl_maskx; 142790792Sgshapiro 142890792Sgshapiro FD_ZERO(&ssl_maskw); 142990792Sgshapiro FD_SET(wfd, &ssl_maskw); 143090792Sgshapiro FD_ZERO(&ssl_maskx); 143190792Sgshapiro FD_SET(rfd, &ssl_maskx); 143290792Sgshapiro if (select(wfd + 1, NULL, &ssl_maskw, 143390792Sgshapiro &ssl_maskx, &tv) > 0) 143490792Sgshapiro goto ssl_retry; 143590792Sgshapiro } 143664562Sgshapiro if (LogLevel > 5) 143764562Sgshapiro { 143890792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 143990792Sgshapiro "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d", 144090792Sgshapiro r, i, (int) timedout); 144190792Sgshapiro if (LogLevel > 8) 144290792Sgshapiro tlslogerr("server"); 144364562Sgshapiro } 144490792Sgshapiro tls_ok_srv = false; 144564562Sgshapiro SSL_free(srv_ssl); 144664562Sgshapiro srv_ssl = NULL; 144764562Sgshapiro 144864562Sgshapiro /* 144964562Sgshapiro ** according to the next draft of 145064562Sgshapiro ** RFC 2487 the connection should be dropped 145164562Sgshapiro */ 145264562Sgshapiro 145364562Sgshapiro /* arrange to ignore any current send list */ 145464562Sgshapiro e->e_sendqueue = NULL; 145564562Sgshapiro goto doquit; 145664562Sgshapiro } 145764562Sgshapiro 145864562Sgshapiro /* ignore return code for now, it's in {verify} */ 145990792Sgshapiro (void) tls_get_info(srv_ssl, true, 146090792Sgshapiro CurSmtpClient, 146190792Sgshapiro &BlankEnvelope.e_macro, 146290792Sgshapiro bitset(SRV_VRFY_CLT, features)); 146364562Sgshapiro 146464562Sgshapiro /* 146564562Sgshapiro ** call Stls_client to find out whether 146664562Sgshapiro ** to accept the connection from the client 146764562Sgshapiro */ 146864562Sgshapiro 146964562Sgshapiro saveQuickAbort = QuickAbort; 147064562Sgshapiro saveSuprErrs = SuprErrs; 147190792Sgshapiro SuprErrs = true; 147290792Sgshapiro QuickAbort = false; 147364562Sgshapiro if (rscheck("tls_client", 147490792Sgshapiro macvalue(macid("{verify}"), e), 147590792Sgshapiro "STARTTLS", e, true, true, 5, 147690792Sgshapiro NULL, NOQID) != EX_OK || 147790792Sgshapiro Errors > 0) 147864562Sgshapiro { 147964562Sgshapiro extern char MsgBuf[]; 148064562Sgshapiro 148164562Sgshapiro if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf)) 148264562Sgshapiro nullserver = newstr(MsgBuf); 148364562Sgshapiro else 148464562Sgshapiro nullserver = "503 5.7.0 Authentication required."; 148564562Sgshapiro } 148664562Sgshapiro QuickAbort = saveQuickAbort; 148764562Sgshapiro SuprErrs = saveSuprErrs; 148864562Sgshapiro 148990792Sgshapiro tls_ok_srv = false; /* don't offer STARTTLS again */ 149064562Sgshapiro n_helo = 0; 149190792Sgshapiro# if SASL 149264562Sgshapiro if (sasl_ok) 149364562Sgshapiro { 149464562Sgshapiro char *s; 149564562Sgshapiro 149690792Sgshapiro s = macvalue(macid("{cipher_bits}"), e); 149790792Sgshapiro if (s != NULL && (ext_ssf.ssf = atoi(s)) > 0) 149864562Sgshapiro { 149990792Sgshapiro ext_ssf.auth_id = macvalue(macid("{cert_subject}"), 150064562Sgshapiro e); 150164562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, 150264562Sgshapiro &ext_ssf) == SASL_OK; 150364562Sgshapiro mechlist = NULL; 150464562Sgshapiro if (sasl_ok) 150564562Sgshapiro n_mechs = saslmechs(conn, 150664562Sgshapiro &mechlist); 150764562Sgshapiro } 150864562Sgshapiro } 150990792Sgshapiro# endif /* SASL */ 151064562Sgshapiro 151164562Sgshapiro /* switch to secure connection */ 151290792Sgshapiro if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0) 151390792Sgshapiro { 151490792Sgshapiro tls_active = true; 151590792Sgshapiro# if PIPELINING 151690792Sgshapiro (void) sm_io_autoflush(InChannel, OutChannel); 151790792Sgshapiro# endif /* PIPELINING */ 151890792Sgshapiro } 151964562Sgshapiro else 152064562Sgshapiro { 152164562Sgshapiro /* 152264562Sgshapiro ** XXX this is an internal error 152364562Sgshapiro ** how to deal with it? 152464562Sgshapiro ** we can't generate an error message 152564562Sgshapiro ** since the other side switched to an 152664562Sgshapiro ** encrypted layer, but we could not... 152764562Sgshapiro ** just "hang up"? 152864562Sgshapiro */ 152990792Sgshapiro 153064562Sgshapiro nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer"; 153190792Sgshapiro syserr("STARTTLS: can't switch to encrypted layer"); 153264562Sgshapiro } 153390792Sgshapiro# if _FFR_SMTP_SSL 153490792Sgshapiro tls_done: 153590792Sgshapiro if (smtps) 153690792Sgshapiro { 153790792Sgshapiro if (tls_active) 153890792Sgshapiro goto greeting; 153990792Sgshapiro else 154090792Sgshapiro goto doquit; 154190792Sgshapiro } 154290792Sgshapiro# endif /* _FFR_SMTP_SSL */ 154364562Sgshapiro break; 154490792Sgshapiro#endif /* STARTTLS */ 154564562Sgshapiro 154638032Speter case CMDHELO: /* hello -- introduce yourself */ 154738032Speter case CMDEHLO: /* extended hello */ 154890792Sgshapiro DELAY_CONN("EHLO"); 154964562Sgshapiro if (c->cmd_code == CMDEHLO) 155038032Speter { 155138032Speter protocol = "ESMTP"; 155238032Speter SmtpPhase = "server EHLO"; 155338032Speter } 155438032Speter else 155538032Speter { 155638032Speter protocol = "SMTP"; 155738032Speter SmtpPhase = "server HELO"; 155838032Speter } 155938032Speter 156038032Speter /* avoid denial-of-service */ 156190792Sgshapiro (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, true, 156264562Sgshapiro "HELO/EHLO", e); 156338032Speter 156490792Sgshapiro#if 0 156590792Sgshapiro /* RFC2821 4.1.4 allows duplicate HELO/EHLO */ 156638032Speter /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ 156738032Speter if (gothello) 156838032Speter { 156938032Speter usrerr("503 %s Duplicate HELO/EHLO", 157073188Sgshapiro MyHostName); 157138032Speter break; 157238032Speter } 157390792Sgshapiro#endif /* 0 */ 157438032Speter 157538032Speter /* check for valid domain name (re 1123 5.2.5) */ 157638032Speter if (*p == '\0' && !AllowBogusHELO) 157738032Speter { 157838032Speter usrerr("501 %s requires domain address", 157938032Speter cmdbuf); 158038032Speter break; 158138032Speter } 158238032Speter 158338032Speter /* check for long domain name (hides Received: info) */ 158438032Speter if (strlen(p) > MAXNAME) 158538032Speter { 158638032Speter usrerr("501 Invalid domain name"); 158764562Sgshapiro if (LogLevel > 9) 158864562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 158964562Sgshapiro "invalid domain name (too long) from %.100s", 159064562Sgshapiro CurSmtpClient); 159138032Speter break; 159238032Speter } 159338032Speter 159438032Speter for (q = p; *q != '\0'; q++) 159538032Speter { 159638032Speter if (!isascii(*q)) 159738032Speter break; 159838032Speter if (isalnum(*q)) 159938032Speter continue; 160038032Speter if (isspace(*q)) 160138032Speter { 160238032Speter *q = '\0'; 160338032Speter break; 160438032Speter } 160538032Speter if (strchr("[].-_#", *q) == NULL) 160638032Speter break; 160738032Speter } 160864562Sgshapiro 160938032Speter if (*q == '\0') 161038032Speter { 161138032Speter q = "pleased to meet you"; 161290792Sgshapiro sendinghost = sm_strdup_x(p); 161338032Speter } 161438032Speter else if (!AllowBogusHELO) 161538032Speter { 161638032Speter usrerr("501 Invalid domain name"); 161764562Sgshapiro if (LogLevel > 9) 161864562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 161964562Sgshapiro "invalid domain name (%.100s) from %.100s", 162064562Sgshapiro p, CurSmtpClient); 162138032Speter break; 162238032Speter } 162338032Speter else 162438032Speter { 162538032Speter q = "accepting invalid domain name"; 162638032Speter } 162738032Speter 162890792Sgshapiro if (gothello) 162990792Sgshapiro { 163090792Sgshapiro CLEAR_STATE(cmdbuf); 163164562Sgshapiro 163290792Sgshapiro#if _FFR_QUARANTINE 163390792Sgshapiro /* restore connection quarantining */ 163490792Sgshapiro if (smtp.sm_quarmsg == NULL) 163590792Sgshapiro { 163690792Sgshapiro e->e_quarmsg = NULL; 163790792Sgshapiro macdefine(&e->e_macro, A_PERM, 163890792Sgshapiro macid("{quarantine}"), ""); 163990792Sgshapiro } 164090792Sgshapiro else 164190792Sgshapiro { 164290792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 164390792Sgshapiro smtp.sm_quarmsg); 164490792Sgshapiro macdefine(&e->e_macro, A_PERM, 164590792Sgshapiro macid("{quarantine}"), 164690792Sgshapiro e->e_quarmsg); 164790792Sgshapiro } 164890792Sgshapiro#endif /* _FFR_QUARANTINE */ 164990792Sgshapiro } 165090792Sgshapiro 165190792Sgshapiro#if MILTER 165290792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 165390792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 165464562Sgshapiro { 165564562Sgshapiro char state; 165664562Sgshapiro char *response; 165764562Sgshapiro 165864562Sgshapiro response = milter_helo(p, e, &state); 165964562Sgshapiro switch (state) 166064562Sgshapiro { 166164562Sgshapiro case SMFIR_REPLYCODE: 166290792Sgshapiro if (MilterLogLevel > 3) 166390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 166490792Sgshapiro "Milter: helo=%s, reject=%s", 166590792Sgshapiro p, response); 166690792Sgshapiro nullserver = newstr(response); 166790792Sgshapiro smtp.sm_milterize = false; 166864562Sgshapiro break; 166964562Sgshapiro 167064562Sgshapiro case SMFIR_REJECT: 167190792Sgshapiro if (MilterLogLevel > 3) 167290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 167390792Sgshapiro "Milter: helo=%s, reject=Command rejected", 167490792Sgshapiro p); 167564562Sgshapiro nullserver = "Command rejected"; 167690792Sgshapiro smtp.sm_milterize = false; 167764562Sgshapiro break; 167864562Sgshapiro 167964562Sgshapiro case SMFIR_TEMPFAIL: 168090792Sgshapiro if (MilterLogLevel > 3) 168190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 168290792Sgshapiro "Milter: helo=%s, reject=%s", 168390792Sgshapiro p, MSG_TEMPFAIL); 168490792Sgshapiro tempfail = true; 168590792Sgshapiro smtp.sm_milterize = false; 168664562Sgshapiro break; 168764562Sgshapiro } 168890792Sgshapiro if (response != NULL) 168990792Sgshapiro sm_free(response); 169090792Sgshapiro 169194334Sgshapiro# if _FFR_QUARANTINE 169290792Sgshapiro /* 169390792Sgshapiro ** If quarantining by a connect/ehlo action, 169490792Sgshapiro ** save between messages 169590792Sgshapiro */ 169690792Sgshapiro 169790792Sgshapiro if (smtp.sm_quarmsg == NULL && 169890792Sgshapiro e->e_quarmsg != NULL) 169990792Sgshapiro smtp.sm_quarmsg = newstr(e->e_quarmsg); 170094334Sgshapiro# endif /* _FFR_QUARANTINE */ 170164562Sgshapiro } 170290792Sgshapiro#endif /* MILTER */ 170390792Sgshapiro gothello = true; 170464562Sgshapiro 170538032Speter /* print HELO response message */ 170664562Sgshapiro if (c->cmd_code != CMDEHLO) 170738032Speter { 170838032Speter message("250 %s Hello %s, %s", 170938032Speter MyHostName, CurSmtpClient, q); 171038032Speter break; 171138032Speter } 171238032Speter 171338032Speter message("250-%s Hello %s, %s", 171438032Speter MyHostName, CurSmtpClient, q); 171538032Speter 171664562Sgshapiro /* offer ENHSC even for nullserver */ 171764562Sgshapiro if (nullserver != NULL) 171864562Sgshapiro { 171964562Sgshapiro message("250 ENHANCEDSTATUSCODES"); 172064562Sgshapiro break; 172164562Sgshapiro } 172264562Sgshapiro 172366494Sgshapiro /* 172466494Sgshapiro ** print EHLO features list 172566494Sgshapiro ** 172666494Sgshapiro ** Note: If you change this list, 172790792Sgshapiro ** remember to update 'helpfile' 172866494Sgshapiro */ 172966494Sgshapiro 173064562Sgshapiro message("250-ENHANCEDSTATUSCODES"); 173190792Sgshapiro#if PIPELINING 173290792Sgshapiro if (bitset(SRV_OFFER_PIPE, features)) 173390792Sgshapiro message("250-PIPELINING"); 173490792Sgshapiro#endif /* PIPELINING */ 173590792Sgshapiro if (bitset(SRV_OFFER_EXPN, features)) 173638032Speter { 173738032Speter message("250-EXPN"); 173890792Sgshapiro if (bitset(SRV_OFFER_VERB, features)) 173938032Speter message("250-VERB"); 174038032Speter } 174190792Sgshapiro#if MIME8TO7 174238032Speter message("250-8BITMIME"); 174390792Sgshapiro#endif /* MIME8TO7 */ 174438032Speter if (MaxMessageSize > 0) 174538032Speter message("250-SIZE %ld", MaxMessageSize); 174638032Speter else 174738032Speter message("250-SIZE"); 174890792Sgshapiro#if DSN 174990792Sgshapiro if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features)) 175038032Speter message("250-DSN"); 175190792Sgshapiro#endif /* DSN */ 175290792Sgshapiro if (bitset(SRV_OFFER_ETRN, features)) 175338032Speter message("250-ETRN"); 175490792Sgshapiro#if SASL 175564562Sgshapiro if (sasl_ok && mechlist != NULL && *mechlist != '\0') 175664562Sgshapiro message("250-AUTH %s", mechlist); 175790792Sgshapiro#endif /* SASL */ 175890792Sgshapiro#if STARTTLS 175990792Sgshapiro if (tls_ok_srv && bitset(SRV_OFFER_TLS, features)) 176064562Sgshapiro message("250-STARTTLS"); 176190792Sgshapiro#endif /* STARTTLS */ 176290792Sgshapiro if (DeliverByMin > 0) 176390792Sgshapiro message("250-DELIVERBY %ld", 176490792Sgshapiro (long) DeliverByMin); 176590792Sgshapiro else if (DeliverByMin == 0) 176690792Sgshapiro message("250-DELIVERBY"); 176790792Sgshapiro 176890792Sgshapiro /* < 0: no deliver-by */ 176990792Sgshapiro 177038032Speter message("250 HELP"); 177138032Speter break; 177238032Speter 177338032Speter case CMDMAIL: /* mail -- designate sender */ 177438032Speter SmtpPhase = "server MAIL"; 177590792Sgshapiro DELAY_CONN("MAIL"); 177638032Speter 177738032Speter /* check for validity of this command */ 177838032Speter if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 177938032Speter { 178064562Sgshapiro usrerr("503 5.0.0 Polite people say HELO first"); 178138032Speter break; 178238032Speter } 178390792Sgshapiro if (smtp.sm_gotmail) 178438032Speter { 178564562Sgshapiro usrerr("503 5.5.0 Sender already specified"); 178638032Speter break; 178738032Speter } 178890792Sgshapiro#if SASL 178990792Sgshapiro if (bitset(SRV_REQ_AUTH, features) && 179064562Sgshapiro authenticating != SASL_IS_AUTH) 179164562Sgshapiro { 179264562Sgshapiro usrerr("530 5.7.0 Authentication required"); 179364562Sgshapiro break; 179464562Sgshapiro } 179590792Sgshapiro#endif /* SASL */ 179638032Speter 179764562Sgshapiro p = skipword(p, "from"); 179864562Sgshapiro if (p == NULL) 179964562Sgshapiro break; 180064562Sgshapiro if (tempfail) 180164562Sgshapiro { 180264562Sgshapiro if (LogLevel > 9) 180364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 180464562Sgshapiro "SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)", 180564562Sgshapiro p, CurSmtpClient); 180690792Sgshapiro usrerr(MSG_TEMPFAIL); 180764562Sgshapiro break; 180864562Sgshapiro } 180964562Sgshapiro 181038032Speter /* make sure we know who the sending host is */ 181138032Speter if (sendinghost == NULL) 181238032Speter sendinghost = peerhostname; 181338032Speter 181438032Speter 181590792Sgshapiro#if SM_HEAP_CHECK 181690792Sgshapiro if (sm_debug_active(&DebugLeakSmtp, 1)) 181790792Sgshapiro { 181890792Sgshapiro sm_heap_newgroup(); 181990792Sgshapiro sm_dprintf("smtp() heap group #%d\n", 182090792Sgshapiro sm_heap_group()); 182190792Sgshapiro } 182290792Sgshapiro#endif /* SM_HEAP_CHECK */ 182364562Sgshapiro 182438032Speter if (Errors > 0) 182590792Sgshapiro goto undo_no_pm; 182638032Speter if (!gothello) 182738032Speter { 182890792Sgshapiro auth_warning(e, "%s didn't use HELO protocol", 182990792Sgshapiro CurSmtpClient); 183038032Speter } 183190792Sgshapiro#ifdef PICKY_HELO_CHECK 183290792Sgshapiro if (sm_strcasecmp(sendinghost, peerhostname) != 0 && 183390792Sgshapiro (sm_strcasecmp(peerhostname, "localhost") != 0 || 183490792Sgshapiro sm_strcasecmp(sendinghost, MyHostName) != 0)) 183538032Speter { 183638032Speter auth_warning(e, "Host %s claimed to be %s", 183790792Sgshapiro CurSmtpClient, sendinghost); 183838032Speter } 183990792Sgshapiro#endif /* PICKY_HELO_CHECK */ 184038032Speter 184138032Speter if (protocol == NULL) 184238032Speter protocol = "SMTP"; 184390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'r', protocol); 184490792Sgshapiro macdefine(&e->e_macro, A_PERM, 's', sendinghost); 184564562Sgshapiro 184638032Speter if (Errors > 0) 184790792Sgshapiro goto undo_no_pm; 184890792Sgshapiro smtp.sm_nrcpts = 0; 184990792Sgshapiro n_badrcpts = 0; 185090792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0"); 185190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0"); 185264562Sgshapiro e->e_flags |= EF_CLRQUEUE; 185390792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", 185464562Sgshapiro qid_printname(e), 185564562Sgshapiro CurSmtpClient, inp); 185638032Speter 185790792Sgshapiro /* do the processing */ 185890792Sgshapiro SM_TRY 185990792Sgshapiro { 186094334Sgshapiro extern char *FullName; 186194334Sgshapiro 186290792Sgshapiro QuickAbort = true; 186394334Sgshapiro SM_FREE_CLR(FullName); 186464562Sgshapiro 186538032Speter /* must parse sender first */ 186638032Speter delimptr = NULL; 186790792Sgshapiro setsender(p, e, &delimptr, ' ', false); 186838032Speter if (delimptr != NULL && *delimptr != '\0') 186938032Speter *delimptr++ = '\0'; 187038032Speter if (Errors > 0) 187190792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 187238032Speter 187364562Sgshapiro /* Successfully set e_from, allow logging */ 187464562Sgshapiro e->e_flags |= EF_LOGSENDER; 187538032Speter 187664562Sgshapiro /* put resulting triple from parseaddr() into macros */ 187764562Sgshapiro if (e->e_from.q_mailer != NULL) 187890792Sgshapiro macdefine(&e->e_macro, A_PERM, 187990792Sgshapiro macid("{mail_mailer}"), 188090792Sgshapiro e->e_from.q_mailer->m_name); 188164562Sgshapiro else 188290792Sgshapiro macdefine(&e->e_macro, A_PERM, 188390792Sgshapiro macid("{mail_mailer}"), NULL); 188464562Sgshapiro if (e->e_from.q_host != NULL) 188590792Sgshapiro macdefine(&e->e_macro, A_PERM, 188690792Sgshapiro macid("{mail_host}"), 188790792Sgshapiro e->e_from.q_host); 188864562Sgshapiro else 188990792Sgshapiro macdefine(&e->e_macro, A_PERM, 189090792Sgshapiro macid("{mail_host}"), "localhost"); 189164562Sgshapiro if (e->e_from.q_user != NULL) 189290792Sgshapiro macdefine(&e->e_macro, A_PERM, 189390792Sgshapiro macid("{mail_addr}"), 189490792Sgshapiro e->e_from.q_user); 189564562Sgshapiro else 189690792Sgshapiro macdefine(&e->e_macro, A_PERM, 189790792Sgshapiro macid("{mail_addr}"), NULL); 189864562Sgshapiro if (Errors > 0) 189990792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 190064562Sgshapiro 190138032Speter /* check for possible spoofing */ 190238032Speter if (RealUid != 0 && OpMode == MD_SMTP && 190338032Speter !wordinclass(RealUserName, 't') && 190464562Sgshapiro (!bitnset(M_LOCALMAILER, 190564562Sgshapiro e->e_from.q_mailer->m_flags) || 190664562Sgshapiro strcmp(e->e_from.q_user, RealUserName) != 0)) 190738032Speter { 190838032Speter auth_warning(e, "%s owned process doing -bs", 190938032Speter RealUserName); 191038032Speter } 191138032Speter 191238032Speter /* now parse ESMTP arguments */ 191338032Speter e->e_msgsize = 0; 191464562Sgshapiro addr = p; 191564562Sgshapiro argno = 0; 191664562Sgshapiro args[argno++] = p; 191738032Speter p = delimptr; 191838032Speter while (p != NULL && *p != '\0') 191938032Speter { 192038032Speter char *kp; 192138032Speter char *vp = NULL; 192264562Sgshapiro char *equal = NULL; 192338032Speter 192438032Speter /* locate the beginning of the keyword */ 192590792Sgshapiro SKIP_SPACE(p); 192638032Speter if (*p == '\0') 192738032Speter break; 192838032Speter kp = p; 192938032Speter 193038032Speter /* skip to the value portion */ 193138032Speter while ((isascii(*p) && isalnum(*p)) || *p == '-') 193238032Speter p++; 193338032Speter if (*p == '=') 193438032Speter { 193564562Sgshapiro equal = p; 193638032Speter *p++ = '\0'; 193738032Speter vp = p; 193838032Speter 193938032Speter /* skip to the end of the value */ 194038032Speter while (*p != '\0' && *p != ' ' && 194138032Speter !(isascii(*p) && iscntrl(*p)) && 194238032Speter *p != '=') 194338032Speter p++; 194438032Speter } 194538032Speter 194638032Speter if (*p != '\0') 194738032Speter *p++ = '\0'; 194838032Speter 194938032Speter if (tTd(19, 1)) 195090792Sgshapiro sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp, 195138032Speter vp == NULL ? "<null>" : vp); 195238032Speter 195338032Speter mail_esmtp_args(kp, vp, e); 195464562Sgshapiro if (equal != NULL) 195564562Sgshapiro *equal = '='; 195664562Sgshapiro args[argno++] = kp; 195764562Sgshapiro if (argno >= MAXSMTPARGS - 1) 195864562Sgshapiro usrerr("501 5.5.4 Too many parameters"); 195938032Speter if (Errors > 0) 196090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 196138032Speter } 196264562Sgshapiro args[argno] = NULL; 196338032Speter if (Errors > 0) 196490792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 196538032Speter 196690792Sgshapiro#if SASL 196790792Sgshapiro# if _FFR_AUTH_PASSING 196890792Sgshapiro /* set the default AUTH= if the sender didn't */ 196990792Sgshapiro if (e->e_auth_param == NULL) 197090792Sgshapiro { 197190792Sgshapiro /* XXX only do this for an MSA? */ 197290792Sgshapiro e->e_auth_param = macvalue(macid("{auth_authen}"), 197390792Sgshapiro e); 197490792Sgshapiro if (e->e_auth_param == NULL) 197590792Sgshapiro e->e_auth_param = "<>"; 197690792Sgshapiro 197790792Sgshapiro /* 197890792Sgshapiro ** XXX should we invoke Strust_auth now? 197990792Sgshapiro ** authorizing as the client that just 198090792Sgshapiro ** authenticated, so we'll trust implicitly 198190792Sgshapiro */ 198290792Sgshapiro } 198390792Sgshapiro# endif /* _FFR_AUTH_PASSING */ 198490792Sgshapiro#endif /* SASL */ 198590792Sgshapiro 198664562Sgshapiro /* do config file checking of the sender */ 198790792Sgshapiro macdefine(&e->e_macro, A_PERM, 198890792Sgshapiro macid("{addr_type}"), "e s"); 198990792Sgshapiro#if _FFR_MAIL_MACRO 199090792Sgshapiro /* make the "real" sender address available */ 199190792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"), 199290792Sgshapiro e->e_from.q_paddr); 199390792Sgshapiro#endif /* _FFR_MAIL_MACRO */ 199464562Sgshapiro if (rscheck("check_mail", addr, 199590792Sgshapiro NULL, e, true, true, 3, NULL, 199690792Sgshapiro e->e_id) != EX_OK || 199764562Sgshapiro Errors > 0) 199890792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 199990792Sgshapiro macdefine(&e->e_macro, A_PERM, 200090792Sgshapiro macid("{addr_type}"), NULL); 200164562Sgshapiro 200266494Sgshapiro if (MaxMessageSize > 0 && 200377349Sgshapiro (e->e_msgsize > MaxMessageSize || 200477349Sgshapiro e->e_msgsize < 0)) 200538032Speter { 200664562Sgshapiro usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)", 200738032Speter MaxMessageSize); 200890792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 200938032Speter } 201064562Sgshapiro 201190792Sgshapiro /* 201290792Sgshapiro ** XXX always check whether there is at least one fs 201390792Sgshapiro ** with enough space? 201490792Sgshapiro ** However, this may not help much: the queue group 201590792Sgshapiro ** selection may later on select a FS that hasn't 201690792Sgshapiro ** enough space. 201790792Sgshapiro */ 201890792Sgshapiro 201990792Sgshapiro if ((NumFileSys == 1 || NumQueue == 1) && 202090792Sgshapiro !enoughdiskspace(e->e_msgsize, e) 202190792Sgshapiro#if _FFR_ANY_FREE_FS 202290792Sgshapiro && !filesys_free(e->e_msgsize) 202390792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 202490792Sgshapiro ) 202538032Speter { 202690792Sgshapiro /* 202790792Sgshapiro ** We perform this test again when the 202890792Sgshapiro ** queue directory is selected, in collect. 202990792Sgshapiro */ 203090792Sgshapiro 203164562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 203290792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 203338032Speter } 203438032Speter if (Errors > 0) 203590792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 203664562Sgshapiro 203790792Sgshapiro LogUsrErrs = true; 203890792Sgshapiro#if MILTER 203990792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 204090792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 204164562Sgshapiro { 204264562Sgshapiro char state; 204364562Sgshapiro char *response; 204464562Sgshapiro 204564562Sgshapiro response = milter_envfrom(args, e, &state); 204690792Sgshapiro MILTER_REPLY("from"); 204764562Sgshapiro } 204890792Sgshapiro#endif /* MILTER */ 204964562Sgshapiro if (Errors > 0) 205090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 205164562Sgshapiro 205264562Sgshapiro message("250 2.1.0 Sender ok"); 205390792Sgshapiro smtp.sm_gotmail = true; 205490792Sgshapiro } 205590792Sgshapiro SM_EXCEPT(exc, "[!F]*") 205690792Sgshapiro { 205790792Sgshapiro /* 205890792Sgshapiro ** An error occurred while processing a MAIL command. 205990792Sgshapiro ** Jump to the common error handling code. 206090792Sgshapiro */ 206190792Sgshapiro 206290792Sgshapiro sm_exc_free(exc); 206390792Sgshapiro goto undo_no_pm; 206490792Sgshapiro } 206590792Sgshapiro SM_END_TRY 206638032Speter break; 206738032Speter 206890792Sgshapiro undo_no_pm: 206990792Sgshapiro e->e_flags &= ~EF_PM_NOTIFY; 207090792Sgshapiro undo: 207190792Sgshapiro break; 207290792Sgshapiro 207338032Speter case CMDRCPT: /* rcpt -- designate recipient */ 207490792Sgshapiro DELAY_CONN("RCPT"); 207590792Sgshapiro if (!smtp.sm_gotmail) 207638032Speter { 207764562Sgshapiro usrerr("503 5.0.0 Need MAIL before RCPT"); 207838032Speter break; 207938032Speter } 208038032Speter SmtpPhase = "server RCPT"; 208190792Sgshapiro SM_TRY 208290792Sgshapiro { 208390792Sgshapiro QuickAbort = true; 208490792Sgshapiro LogUsrErrs = true; 208538032Speter 208638032Speter /* limit flooding of our machine */ 208790792Sgshapiro if (MaxRcptPerMsg > 0 && 208890792Sgshapiro smtp.sm_nrcpts >= MaxRcptPerMsg) 208938032Speter { 209090792Sgshapiro /* sleep(1); / * slow down? */ 209164562Sgshapiro usrerr("452 4.5.3 Too many recipients"); 209290792Sgshapiro goto rcpt_done; 209338032Speter } 209438032Speter 209538032Speter if (e->e_sendmode != SM_DELIVER) 209638032Speter e->e_flags |= EF_VRFYONLY; 209738032Speter 209890792Sgshapiro#if MILTER 209964562Sgshapiro /* 210064562Sgshapiro ** If the filter will be deleting recipients, 210164562Sgshapiro ** don't expand them at RCPT time (in the call 210264562Sgshapiro ** to recipient()). If they are expanded, it 210364562Sgshapiro ** is impossible for removefromlist() to figure 210464562Sgshapiro ** out the expanded members of the original 210564562Sgshapiro ** recipient and mark them as QS_DONTSEND. 210664562Sgshapiro */ 210764562Sgshapiro 210864562Sgshapiro if (milter_can_delrcpts()) 210964562Sgshapiro e->e_flags |= EF_VRFYONLY; 211090792Sgshapiro#endif /* MILTER */ 211164562Sgshapiro 211238032Speter p = skipword(p, "to"); 211338032Speter if (p == NULL) 211490792Sgshapiro goto rcpt_done; 211590792Sgshapiro macdefine(&e->e_macro, A_PERM, 211690792Sgshapiro macid("{addr_type}"), "e r"); 211790792Sgshapiro a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, 211890792Sgshapiro e, true); 211990792Sgshapiro macdefine(&e->e_macro, A_PERM, 212090792Sgshapiro macid("{addr_type}"), NULL); 212190792Sgshapiro if (BadRcptThrottle > 0 && 212290792Sgshapiro n_badrcpts >= BadRcptThrottle) 212390792Sgshapiro { 212490792Sgshapiro if (LogLevel > 5 && 212590792Sgshapiro n_badrcpts == BadRcptThrottle) 212690792Sgshapiro { 212790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 212890792Sgshapiro "%.100s: Possible SMTP RCPT flood, throttling.", 212990792Sgshapiro CurSmtpClient); 213090792Sgshapiro 213190792Sgshapiro /* To avoid duplicated message */ 213290792Sgshapiro n_badrcpts++; 213390792Sgshapiro } 213490792Sgshapiro 213590792Sgshapiro /* 213690792Sgshapiro ** Don't use exponential backoff for now. 213790792Sgshapiro ** Some servers will open more connections 213890792Sgshapiro ** and actually overload the receiver even 213990792Sgshapiro ** more. 214090792Sgshapiro */ 214190792Sgshapiro 214290792Sgshapiro (void) sleep(1); 214390792Sgshapiro } 214442575Speter if (Errors > 0) 214590792Sgshapiro goto rcpt_done; 214642575Speter if (a == NULL) 214742575Speter { 214864562Sgshapiro usrerr("501 5.0.0 Missing recipient"); 214990792Sgshapiro goto rcpt_done; 215042575Speter } 215142575Speter 215238032Speter if (delimptr != NULL && *delimptr != '\0') 215338032Speter *delimptr++ = '\0'; 215438032Speter 215564562Sgshapiro /* put resulting triple from parseaddr() into macros */ 215664562Sgshapiro if (a->q_mailer != NULL) 215790792Sgshapiro macdefine(&e->e_macro, A_PERM, 215890792Sgshapiro macid("{rcpt_mailer}"), 215990792Sgshapiro a->q_mailer->m_name); 216064562Sgshapiro else 216190792Sgshapiro macdefine(&e->e_macro, A_PERM, 216290792Sgshapiro macid("{rcpt_mailer}"), NULL); 216364562Sgshapiro if (a->q_host != NULL) 216490792Sgshapiro macdefine(&e->e_macro, A_PERM, 216590792Sgshapiro macid("{rcpt_host}"), a->q_host); 216664562Sgshapiro else 216790792Sgshapiro macdefine(&e->e_macro, A_PERM, 216890792Sgshapiro macid("{rcpt_host}"), "localhost"); 216964562Sgshapiro if (a->q_user != NULL) 217090792Sgshapiro macdefine(&e->e_macro, A_PERM, 217190792Sgshapiro macid("{rcpt_addr}"), a->q_user); 217264562Sgshapiro else 217390792Sgshapiro macdefine(&e->e_macro, A_PERM, 217490792Sgshapiro macid("{rcpt_addr}"), NULL); 217564562Sgshapiro if (Errors > 0) 217690792Sgshapiro goto rcpt_done; 217738032Speter 217838032Speter /* now parse ESMTP arguments */ 217964562Sgshapiro addr = p; 218064562Sgshapiro argno = 0; 218164562Sgshapiro args[argno++] = p; 218238032Speter p = delimptr; 218338032Speter while (p != NULL && *p != '\0') 218438032Speter { 218538032Speter char *kp; 218638032Speter char *vp = NULL; 218764562Sgshapiro char *equal = NULL; 218838032Speter 218938032Speter /* locate the beginning of the keyword */ 219090792Sgshapiro SKIP_SPACE(p); 219138032Speter if (*p == '\0') 219238032Speter break; 219338032Speter kp = p; 219438032Speter 219538032Speter /* skip to the value portion */ 219638032Speter while ((isascii(*p) && isalnum(*p)) || *p == '-') 219738032Speter p++; 219838032Speter if (*p == '=') 219938032Speter { 220064562Sgshapiro equal = p; 220138032Speter *p++ = '\0'; 220238032Speter vp = p; 220338032Speter 220438032Speter /* skip to the end of the value */ 220538032Speter while (*p != '\0' && *p != ' ' && 220638032Speter !(isascii(*p) && iscntrl(*p)) && 220738032Speter *p != '=') 220838032Speter p++; 220938032Speter } 221038032Speter 221138032Speter if (*p != '\0') 221238032Speter *p++ = '\0'; 221338032Speter 221438032Speter if (tTd(19, 1)) 221590792Sgshapiro sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp, 221638032Speter vp == NULL ? "<null>" : vp); 221738032Speter 221838032Speter rcpt_esmtp_args(a, kp, vp, e); 221964562Sgshapiro if (equal != NULL) 222064562Sgshapiro *equal = '='; 222164562Sgshapiro args[argno++] = kp; 222264562Sgshapiro if (argno >= MAXSMTPARGS - 1) 222364562Sgshapiro usrerr("501 5.5.4 Too many parameters"); 222438032Speter if (Errors > 0) 222538032Speter break; 222638032Speter } 222764562Sgshapiro args[argno] = NULL; 222838032Speter if (Errors > 0) 222990792Sgshapiro goto rcpt_done; 223038032Speter 223164562Sgshapiro /* do config file checking of the recipient */ 223290792Sgshapiro macdefine(&e->e_macro, A_PERM, 223390792Sgshapiro macid("{addr_type}"), "e r"); 223464562Sgshapiro if (rscheck("check_rcpt", addr, 223590792Sgshapiro NULL, e, true, true, 3, NULL, 223690792Sgshapiro e->e_id) != EX_OK || 223764562Sgshapiro Errors > 0) 223890792Sgshapiro goto rcpt_done; 223990792Sgshapiro macdefine(&e->e_macro, A_PERM, 224090792Sgshapiro macid("{addr_type}"), NULL); 224164562Sgshapiro 224290792Sgshapiro#if MILTER 224390792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 224490792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 224564562Sgshapiro { 224664562Sgshapiro char state; 224764562Sgshapiro char *response; 224864562Sgshapiro 224964562Sgshapiro response = milter_envrcpt(args, e, &state); 225090792Sgshapiro MILTER_REPLY("to"); 225164562Sgshapiro } 225290792Sgshapiro#endif /* MILTER */ 225364562Sgshapiro 225490792Sgshapiro macdefine(&e->e_macro, A_PERM, 225590792Sgshapiro macid("{rcpt_mailer}"), NULL); 225690792Sgshapiro macdefine(&e->e_macro, A_PERM, 225790792Sgshapiro macid("{rcpt_relay}"), NULL); 225890792Sgshapiro macdefine(&e->e_macro, A_PERM, 225990792Sgshapiro macid("{rcpt_addr}"), NULL); 226090792Sgshapiro macdefine(&e->e_macro, A_PERM, 226190792Sgshapiro macid("{dsn_notify}"), NULL); 226264562Sgshapiro if (Errors > 0) 226390792Sgshapiro goto rcpt_done; 226464562Sgshapiro 226538032Speter /* save in recipient list after ESMTP mods */ 226638032Speter a = recipient(a, &e->e_sendqueue, 0, e); 226738032Speter if (Errors > 0) 226890792Sgshapiro goto rcpt_done; 226938032Speter 227038032Speter /* no errors during parsing, but might be a duplicate */ 227138032Speter e->e_to = a->q_paddr; 227264562Sgshapiro if (!QS_IS_BADADDR(a->q_state)) 227338032Speter { 227490792Sgshapiro if (smtp.sm_nrcpts == 0) 227564562Sgshapiro initsys(e); 227664562Sgshapiro message("250 2.1.5 Recipient ok%s", 227764562Sgshapiro QS_IS_QUEUEUP(a->q_state) ? 227838032Speter " (will queue)" : ""); 227990792Sgshapiro smtp.sm_nrcpts++; 228038032Speter } 228138032Speter else 228238032Speter { 228338032Speter /* punt -- should keep message in ADDRESS.... */ 228464562Sgshapiro usrerr("550 5.1.1 Addressee unknown"); 228538032Speter } 228690792Sgshapiro rcpt_done: 228790792Sgshapiro if (Errors > 0) 228890792Sgshapiro ++n_badrcpts; 228990792Sgshapiro } 229090792Sgshapiro SM_EXCEPT(exc, "[!F]*") 229190792Sgshapiro { 229290792Sgshapiro /* An exception occurred while processing RCPT */ 229390792Sgshapiro e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY); 229490792Sgshapiro ++n_badrcpts; 229590792Sgshapiro } 229690792Sgshapiro SM_END_TRY 229738032Speter break; 229838032Speter 229938032Speter case CMDDATA: /* data -- text of mail */ 230090792Sgshapiro DELAY_CONN("DATA"); 230190792Sgshapiro smtp_data(&smtp, e); 230238032Speter break; 230338032Speter 230438032Speter case CMDRSET: /* rset -- reset state */ 230538032Speter if (tTd(94, 100)) 230664562Sgshapiro message("451 4.0.0 Test failure"); 230738032Speter else 230864562Sgshapiro message("250 2.0.0 Reset state"); 230990792Sgshapiro CLEAR_STATE(cmdbuf); 231090792Sgshapiro#if _FFR_QUARANTINE 231190792Sgshapiro /* restore connection quarantining */ 231290792Sgshapiro if (smtp.sm_quarmsg == NULL) 231390792Sgshapiro { 231490792Sgshapiro e->e_quarmsg = NULL; 231590792Sgshapiro macdefine(&e->e_macro, A_PERM, 231690792Sgshapiro macid("{quarantine}"), ""); 231790792Sgshapiro } 231890792Sgshapiro else 231990792Sgshapiro { 232090792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, 232190792Sgshapiro smtp.sm_quarmsg); 232290792Sgshapiro macdefine(&e->e_macro, A_PERM, 232390792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 232490792Sgshapiro } 232590792Sgshapiro#endif /* _FFR_QUARANTINE */ 232638032Speter break; 232738032Speter 232838032Speter case CMDVRFY: /* vrfy -- verify address */ 232938032Speter case CMDEXPN: /* expn -- expand address */ 233090792Sgshapiro vrfy = c->cmd_code == CMDVRFY; 233190792Sgshapiro DELAY_CONN(vrfy ? "VRFY" : "EXPN"); 233264562Sgshapiro if (tempfail) 233364562Sgshapiro { 233464562Sgshapiro if (LogLevel > 9) 233564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 233664562Sgshapiro "SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)", 233790792Sgshapiro vrfy ? "VRFY" : "EXPN", 233864562Sgshapiro p, CurSmtpClient); 233990792Sgshapiro 234090792Sgshapiro /* RFC 821 doesn't allow 4xy reply code */ 234164562Sgshapiro usrerr("550 5.7.1 Please try again later"); 234264562Sgshapiro break; 234364562Sgshapiro } 234490792Sgshapiro wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS, 234590792Sgshapiro false, vrfy ? "VRFY" : "EXPN", e); 234664562Sgshapiro previous = curtime(); 234738032Speter if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 234890792Sgshapiro PrivacyFlags)) 234938032Speter { 235038032Speter if (vrfy) 235164562Sgshapiro message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 235238032Speter else 235364562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 235438032Speter if (LogLevel > 5) 235538032Speter sm_syslog(LOG_INFO, e->e_id, 235664562Sgshapiro "%.100s: %s [rejected]", 235764562Sgshapiro CurSmtpClient, 235864562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 235938032Speter break; 236038032Speter } 236138032Speter else if (!gothello && 236238032Speter bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 236338032Speter PrivacyFlags)) 236438032Speter { 236564562Sgshapiro usrerr("503 5.0.0 I demand that you introduce yourself first"); 236638032Speter break; 236738032Speter } 236890792Sgshapiro if (Errors > 0) 236938032Speter break; 237038032Speter if (LogLevel > 5) 237190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "%.100s: %s", 237264562Sgshapiro CurSmtpClient, 237364562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 237490792Sgshapiro SM_TRY 237590792Sgshapiro { 237690792Sgshapiro QuickAbort = true; 237738032Speter vrfyqueue = NULL; 237838032Speter if (vrfy) 237938032Speter e->e_flags |= EF_VRFYONLY; 238038032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 238138032Speter p++; 238238032Speter if (*p == '\0') 238338032Speter { 238464562Sgshapiro usrerr("501 5.5.2 Argument required"); 238538032Speter } 238638032Speter else 238738032Speter { 238864562Sgshapiro /* do config file checking of the address */ 238964562Sgshapiro if (rscheck(vrfy ? "check_vrfy" : "check_expn", 239090792Sgshapiro p, NULL, e, true, false, 3, NULL, 239190792Sgshapiro NOQID) != EX_OK || 239290792Sgshapiro Errors > 0) 239390792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 239438032Speter (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 239538032Speter } 239664562Sgshapiro if (wt > 0) 239771345Sgshapiro { 239871345Sgshapiro time_t t; 239971345Sgshapiro 240071345Sgshapiro t = wt - (curtime() - previous); 240171345Sgshapiro if (t > 0) 240271345Sgshapiro (void) sleep(t); 240371345Sgshapiro } 240438032Speter if (Errors > 0) 240590792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 240638032Speter if (vrfyqueue == NULL) 240738032Speter { 240864562Sgshapiro usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 240938032Speter } 241038032Speter while (vrfyqueue != NULL) 241138032Speter { 241264562Sgshapiro if (!QS_IS_UNDELIVERED(vrfyqueue->q_state)) 241364562Sgshapiro { 241464562Sgshapiro vrfyqueue = vrfyqueue->q_next; 241564562Sgshapiro continue; 241664562Sgshapiro } 241738032Speter 241864562Sgshapiro /* see if there is more in the vrfy list */ 241938032Speter a = vrfyqueue; 242038032Speter while ((a = a->q_next) != NULL && 242166494Sgshapiro (!QS_IS_UNDELIVERED(a->q_state))) 242238032Speter continue; 242364562Sgshapiro printvrfyaddr(vrfyqueue, a == NULL, vrfy); 242464562Sgshapiro vrfyqueue = a; 242538032Speter } 242690792Sgshapiro } 242790792Sgshapiro SM_EXCEPT(exc, "[!F]*") 242890792Sgshapiro { 242990792Sgshapiro /* 243090792Sgshapiro ** An exception occurred while processing VRFY/EXPN 243190792Sgshapiro */ 243290792Sgshapiro 243390792Sgshapiro sm_exc_free(exc); 243490792Sgshapiro goto undo; 243590792Sgshapiro } 243690792Sgshapiro SM_END_TRY 243738032Speter break; 243838032Speter 243938032Speter case CMDETRN: /* etrn -- force queue flush */ 244090792Sgshapiro DELAY_CONN("ETRN"); 244190792Sgshapiro 244290792Sgshapiro /* Don't leak queue information via debug flags */ 244390792Sgshapiro if (!bitset(SRV_OFFER_ETRN, features) || UseMSP || 244490792Sgshapiro (RealUid != 0 && RealUid != TrustedUid && 244590792Sgshapiro OpMode == MD_SMTP)) 244638032Speter { 244764562Sgshapiro /* different message for MSA ? */ 244864562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 244938032Speter if (LogLevel > 5) 245038032Speter sm_syslog(LOG_INFO, e->e_id, 245164562Sgshapiro "%.100s: %s [rejected]", 245264562Sgshapiro CurSmtpClient, 245364562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 245438032Speter break; 245538032Speter } 245664562Sgshapiro if (tempfail) 245764562Sgshapiro { 245864562Sgshapiro if (LogLevel > 9) 245964562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 246064562Sgshapiro "SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)", 246164562Sgshapiro p, CurSmtpClient); 246290792Sgshapiro usrerr(MSG_TEMPFAIL); 246364562Sgshapiro break; 246464562Sgshapiro } 246538032Speter 246638032Speter if (strlen(p) <= 0) 246738032Speter { 246864562Sgshapiro usrerr("500 5.5.2 Parameter required"); 246938032Speter break; 247038032Speter } 247138032Speter 247238032Speter /* crude way to avoid denial-of-service attacks */ 247390792Sgshapiro (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, true, 247464562Sgshapiro "ETRN", e); 247538032Speter 247690792Sgshapiro /* 247790792Sgshapiro ** Do config file checking of the parameter. 247890792Sgshapiro ** Even though we have srv_features now, we still 247990792Sgshapiro ** need this ruleset because the former is called 248090792Sgshapiro ** when the connection has been established, while 248190792Sgshapiro ** this ruleset is called when the command is 248290792Sgshapiro ** actually issued and therefore has all information 248390792Sgshapiro ** available to make a decision. 248490792Sgshapiro */ 248590792Sgshapiro 248690792Sgshapiro if (rscheck("check_etrn", p, NULL, e, true, false, 3, 248790792Sgshapiro NULL, NOQID) != EX_OK || Errors > 0) 248864562Sgshapiro break; 248964562Sgshapiro 249038032Speter if (LogLevel > 5) 249138032Speter sm_syslog(LOG_INFO, e->e_id, 249290792Sgshapiro "%.100s: ETRN %s", CurSmtpClient, 249364562Sgshapiro shortenstring(p, MAXSHORTSTR)); 249438032Speter 249538032Speter id = p; 249690792Sgshapiro if (*id == '#') 249790792Sgshapiro { 249890792Sgshapiro int wgrp; 249990792Sgshapiro 250090792Sgshapiro id++; 250190792Sgshapiro wgrp = name2qid(id); 250290792Sgshapiro if (!ISVALIDQGRP(wgrp)) 250390792Sgshapiro { 250490792Sgshapiro usrerr("459 4.5.4 Queue %s unknown", 250590792Sgshapiro id); 250690792Sgshapiro break; 250790792Sgshapiro } 250890792Sgshapiro ok = run_work_group(wgrp, true, false, 250990792Sgshapiro false, true); 251090792Sgshapiro if (ok && Errors == 0) 251190792Sgshapiro message("250 2.0.0 Queuing for queue group %s started", id); 251290792Sgshapiro break; 251390792Sgshapiro } 251490792Sgshapiro 251538032Speter if (*id == '@') 251638032Speter id++; 251738032Speter else 251838032Speter *--id = '@'; 251964562Sgshapiro 252090792Sgshapiro new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR)); 252190792Sgshapiro if (new == NULL) 252290792Sgshapiro { 252390792Sgshapiro syserr("500 5.5.0 ETRN out of memory"); 252490792Sgshapiro break; 252590792Sgshapiro } 252638032Speter new->queue_match = id; 252790792Sgshapiro new->queue_negate = false; 252838032Speter new->queue_next = NULL; 252938032Speter QueueLimitRecipient = new; 253090792Sgshapiro ok = runqueue(true, false, false, true); 253190792Sgshapiro sm_free(QueueLimitRecipient); /* XXX */ 253238032Speter QueueLimitRecipient = NULL; 253338032Speter if (ok && Errors == 0) 253464562Sgshapiro message("250 2.0.0 Queuing for node %s started", p); 253538032Speter break; 253638032Speter 253738032Speter case CMDHELP: /* help -- give user info */ 253890792Sgshapiro DELAY_CONN("HELP"); 253964562Sgshapiro help(p, e); 254038032Speter break; 254138032Speter 254238032Speter case CMDNOOP: /* noop -- do nothing */ 254390792Sgshapiro DELAY_CONN("NOOP"); 254490792Sgshapiro (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true, 254564562Sgshapiro "NOOP", e); 254664562Sgshapiro message("250 2.0.0 OK"); 254738032Speter break; 254838032Speter 254938032Speter case CMDQUIT: /* quit -- leave mail */ 255064562Sgshapiro message("221 2.0.0 %s closing connection", MyHostName); 255190792Sgshapiro#if PIPELINING 255290792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 255390792Sgshapiro#endif /* PIPELINING */ 255438032Speter 255590792Sgshapiro if (smtp.sm_nrcpts > 0) 255690792Sgshapiro logundelrcpts(e, "aborted by sender", 9, false); 255790792Sgshapiro 255838032Speter /* arrange to ignore any current send list */ 255938032Speter e->e_sendqueue = NULL; 256038032Speter 256190792Sgshapiro#if STARTTLS 256264562Sgshapiro /* shutdown TLS connection */ 256364562Sgshapiro if (tls_active) 256464562Sgshapiro { 256564562Sgshapiro (void) endtls(srv_ssl, "server"); 256690792Sgshapiro tls_active = false; 256764562Sgshapiro } 256890792Sgshapiro#endif /* STARTTLS */ 256990792Sgshapiro#if SASL 257064562Sgshapiro if (authenticating == SASL_IS_AUTH) 257164562Sgshapiro { 257264562Sgshapiro sasl_dispose(&conn); 257364562Sgshapiro authenticating = SASL_NOT_AUTH; 257490792Sgshapiro /* XXX sasl_done(); this is a child */ 257564562Sgshapiro } 257690792Sgshapiro#endif /* SASL */ 257764562Sgshapiro 257864562Sgshapirodoquit: 257938032Speter /* avoid future 050 messages */ 258038032Speter disconnect(1, e); 258138032Speter 258290792Sgshapiro#if MILTER 258364562Sgshapiro /* close out milter filters */ 258464562Sgshapiro milter_quit(e); 258590792Sgshapiro#endif /* MILTER */ 258664562Sgshapiro 258764562Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 258864562Sgshapiro logsender(e, NULL); 258964562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 259064562Sgshapiro 259138032Speter if (lognullconnection && LogLevel > 5) 259264562Sgshapiro { 259364562Sgshapiro char *d; 259464562Sgshapiro 259590792Sgshapiro d = macvalue(macid("{daemon_name}"), e); 259664562Sgshapiro if (d == NULL) 259764562Sgshapiro d = "stdin"; 259890792Sgshapiro 259990792Sgshapiro /* 260090792Sgshapiro ** even though this id is "bogus", it makes 260190792Sgshapiro ** it simpler to "grep" related events, e.g., 260290792Sgshapiro ** timeouts for the same connection. 260390792Sgshapiro */ 260490792Sgshapiro 260590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 260690792Sgshapiro "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s", 260764562Sgshapiro CurSmtpClient, d); 260864562Sgshapiro } 260990792Sgshapiro#if PROFILING 261090792Sgshapiro return; 261190792Sgshapiro#endif /* PROFILING */ 261290792Sgshapiro finis(true, true, ExitStat); 261364562Sgshapiro /* NOTREACHED */ 261438032Speter 261538032Speter case CMDVERB: /* set verbose mode */ 261690792Sgshapiro DELAY_CONN("VERB"); 261738032Speter if (bitset(PRIV_NOEXPN, PrivacyFlags) || 261890792Sgshapiro !bitset(SRV_OFFER_VERB, features) || 261938032Speter bitset(PRIV_NOVERB, PrivacyFlags)) 262038032Speter { 262138032Speter /* this would give out the same info */ 262264562Sgshapiro message("502 5.7.0 Verbose unavailable"); 262338032Speter break; 262438032Speter } 262590792Sgshapiro (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true, 262664562Sgshapiro "VERB", e); 262738032Speter Verbose = 1; 262864562Sgshapiro set_delivery_mode(SM_DELIVER, e); 262964562Sgshapiro message("250 2.0.0 Verbose mode"); 263038032Speter break; 263138032Speter 263290792Sgshapiro#if SMTPDEBUG 263338032Speter case CMDDBGQSHOW: /* show queues */ 263490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 263590792Sgshapiro "Send Queue="); 263690792Sgshapiro printaddr(e->e_sendqueue, true); 263738032Speter break; 263838032Speter 263938032Speter case CMDDBGDEBUG: /* set debug mode */ 264038032Speter tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 264138032Speter tTflag(p); 264264562Sgshapiro message("200 2.0.0 Debug set"); 264338032Speter break; 264438032Speter 264590792Sgshapiro#else /* SMTPDEBUG */ 264638032Speter case CMDDBGQSHOW: /* show queues */ 264738032Speter case CMDDBGDEBUG: /* set debug mode */ 264890792Sgshapiro#endif /* SMTPDEBUG */ 264938032Speter case CMDLOGBOGUS: /* bogus command */ 265090792Sgshapiro DELAY_CONN("Bogus"); 265138032Speter if (LogLevel > 0) 265238032Speter sm_syslog(LOG_CRIT, e->e_id, 265364562Sgshapiro "\"%s\" command from %.100s (%.100s)", 265464562Sgshapiro c->cmd_name, CurSmtpClient, 265564562Sgshapiro anynet_ntoa(&RealHostAddr)); 265664562Sgshapiro /* FALLTHROUGH */ 265738032Speter 265838032Speter case CMDERROR: /* unknown command */ 265990792Sgshapiro#if MAXBADCOMMANDS > 0 266090792Sgshapiro if (++n_badcmds > MAXBADCOMMANDS) 266138032Speter { 266264562Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 266338032Speter MyHostName); 266464562Sgshapiro 266564562Sgshapiro /* arrange to ignore any current send list */ 266664562Sgshapiro e->e_sendqueue = NULL; 266738032Speter goto doquit; 266838032Speter } 266990792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 267038032Speter 267164562Sgshapiro usrerr("500 5.5.1 Command unrecognized: \"%s\"", 267264562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 267338032Speter break; 267438032Speter 267564562Sgshapiro case CMDUNIMPL: 267690792Sgshapiro DELAY_CONN("Unimpl"); 267764562Sgshapiro usrerr("502 5.5.1 Command not implemented: \"%s\"", 267864562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 267964562Sgshapiro break; 268064562Sgshapiro 268138032Speter default: 268290792Sgshapiro DELAY_CONN("default"); 268338032Speter errno = 0; 268464562Sgshapiro syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code); 268538032Speter break; 268638032Speter } 268790792Sgshapiro#if SASL 268864562Sgshapiro } 268990792Sgshapiro#endif /* SASL */ 269090792Sgshapiro } 269190792Sgshapiro SM_EXCEPT(exc, "[!F]*") 269290792Sgshapiro { 269390792Sgshapiro /* 269490792Sgshapiro ** The only possible exception is "E:mta.quickabort". 269590792Sgshapiro ** There is nothing to do except fall through and loop. 269690792Sgshapiro */ 269790792Sgshapiro } 269890792Sgshapiro SM_END_TRY 269938032Speter } 270090792Sgshapiro} 270190792Sgshapiro/* 270290792Sgshapiro** SMTP_DATA -- implement the SMTP DATA command. 270390792Sgshapiro** 270490792Sgshapiro** Parameters: 270590792Sgshapiro** smtp -- status of SMTP connection. 270690792Sgshapiro** e -- envelope. 270790792Sgshapiro** 270890792Sgshapiro** Returns: 270990792Sgshapiro** none. 271090792Sgshapiro** 271190792Sgshapiro** Side Effects: 271290792Sgshapiro** possibly sends message. 271390792Sgshapiro*/ 271464562Sgshapiro 271590792Sgshapirostatic void 271690792Sgshapirosmtp_data(smtp, e) 271790792Sgshapiro SMTP_T *smtp; 271890792Sgshapiro ENVELOPE *e; 271990792Sgshapiro{ 272090792Sgshapiro#if MILTER 272190792Sgshapiro bool milteraccept; 272290792Sgshapiro#endif /* MILTER */ 272390792Sgshapiro bool aborting; 272490792Sgshapiro bool doublequeue; 272590792Sgshapiro ADDRESS *a; 272690792Sgshapiro ENVELOPE *ee; 272790792Sgshapiro char *id; 272890792Sgshapiro char buf[32]; 272990792Sgshapiro 273090792Sgshapiro SmtpPhase = "server DATA"; 273190792Sgshapiro if (!smtp->sm_gotmail) 273290792Sgshapiro { 273390792Sgshapiro usrerr("503 5.0.0 Need MAIL command"); 273490792Sgshapiro return; 273590792Sgshapiro } 273690792Sgshapiro else if (smtp->sm_nrcpts <= 0) 273790792Sgshapiro { 273890792Sgshapiro usrerr("503 5.0.0 Need RCPT (recipient)"); 273990792Sgshapiro return; 274090792Sgshapiro } 274190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts); 274290792Sgshapiro if (rscheck("check_data", buf, NULL, e, 274390792Sgshapiro true, false, 3, NULL, e->e_id) != EX_OK) 274490792Sgshapiro return; 274590792Sgshapiro 274690792Sgshapiro /* put back discard bit */ 274790792Sgshapiro if (smtp->sm_discard) 274890792Sgshapiro e->e_flags |= EF_DISCARD; 274990792Sgshapiro 275090792Sgshapiro /* check to see if we need to re-expand aliases */ 275190792Sgshapiro /* also reset QS_BADADDR on already-diagnosted addrs */ 275290792Sgshapiro doublequeue = false; 275390792Sgshapiro for (a = e->e_sendqueue; a != NULL; a = a->q_next) 275490792Sgshapiro { 275590792Sgshapiro if (QS_IS_VERIFIED(a->q_state) && 275690792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 275790792Sgshapiro { 275890792Sgshapiro /* need to re-expand aliases */ 275990792Sgshapiro doublequeue = true; 276090792Sgshapiro } 276190792Sgshapiro if (QS_IS_BADADDR(a->q_state)) 276290792Sgshapiro { 276390792Sgshapiro /* make this "go away" */ 276490792Sgshapiro a->q_state = QS_DONTSEND; 276590792Sgshapiro } 276690792Sgshapiro } 276790792Sgshapiro 276890792Sgshapiro /* collect the text of the message */ 276990792Sgshapiro SmtpPhase = "collect"; 277090792Sgshapiro buffer_errors(); 277190792Sgshapiro 277290792Sgshapiro#if _FFR_ADAPTIVE_EOL 277390792Sgshapiro /* triggers error in collect, disabled for now */ 277490792Sgshapiro if (smtp->sm_crlf) 277590792Sgshapiro e->e_flags |= EF_NL_NOT_EOL; 277690792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */ 277790792Sgshapiro 277890792Sgshapiro collect(InChannel, true, NULL, e); 277990792Sgshapiro 278090792Sgshapiro /* redefine message size */ 278190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize); 278290792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); 278390792Sgshapiro 278490792Sgshapiro#if _FFR_CHECK_EOM 278590792Sgshapiro /* rscheck() will set Errors or EF_DISCARD if it trips */ 278690792Sgshapiro (void) rscheck("check_eom", buf, NULL, e, false, 278790792Sgshapiro true, 3, NULL, e->e_id); 278890792Sgshapiro#endif /* _FFR_CHECK_EOM */ 278990792Sgshapiro 279090792Sgshapiro#if MILTER 279190792Sgshapiro milteraccept = true; 279290792Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize && 279390792Sgshapiro Errors <= 0 && 279490792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 279590792Sgshapiro { 279690792Sgshapiro char state; 279790792Sgshapiro char *response; 279890792Sgshapiro 279990792Sgshapiro response = milter_data(e, &state); 280090792Sgshapiro switch (state) 280190792Sgshapiro { 280290792Sgshapiro case SMFIR_REPLYCODE: 280390792Sgshapiro if (MilterLogLevel > 3) 280490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 280590792Sgshapiro "Milter: data, reject=%s", 280690792Sgshapiro response); 280790792Sgshapiro milteraccept = false; 280890792Sgshapiro usrerr(response); 280990792Sgshapiro break; 281090792Sgshapiro 281190792Sgshapiro case SMFIR_REJECT: 281290792Sgshapiro milteraccept = false; 281390792Sgshapiro if (MilterLogLevel > 3) 281490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 281590792Sgshapiro "Milter: data, reject=554 5.7.1 Command rejected"); 281690792Sgshapiro usrerr("554 5.7.1 Command rejected"); 281790792Sgshapiro break; 281890792Sgshapiro 281990792Sgshapiro case SMFIR_DISCARD: 282090792Sgshapiro if (MilterLogLevel > 3) 282190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 282290792Sgshapiro "Milter: data, discard"); 282390792Sgshapiro milteraccept = false; 282490792Sgshapiro e->e_flags |= EF_DISCARD; 282590792Sgshapiro break; 282690792Sgshapiro 282790792Sgshapiro case SMFIR_TEMPFAIL: 282890792Sgshapiro if (MilterLogLevel > 3) 282990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 283090792Sgshapiro "Milter: data, reject=%s", 283190792Sgshapiro MSG_TEMPFAIL); 283290792Sgshapiro milteraccept = false; 283390792Sgshapiro usrerr(MSG_TEMPFAIL); 283490792Sgshapiro break; 283590792Sgshapiro } 283690792Sgshapiro if (response != NULL) 283790792Sgshapiro sm_free(response); 283890792Sgshapiro } 283990792Sgshapiro 284090792Sgshapiro /* Milter may have changed message size */ 284190792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize); 284290792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); 284390792Sgshapiro 284490792Sgshapiro /* abort message filters that didn't get the body & log msg is OK */ 284590792Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize) 284690792Sgshapiro { 284790792Sgshapiro milter_abort(e); 284890792Sgshapiro if (milteraccept && MilterLogLevel > 9) 284990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter accept: message"); 285090792Sgshapiro } 285190792Sgshapiro#endif /* MILTER */ 285290792Sgshapiro 285390792Sgshapiro#if _FFR_QUARANTINE 285490792Sgshapiro /* Check if quarantining stats should be updated */ 285590792Sgshapiro if (e->e_quarmsg != NULL) 285690792Sgshapiro markstats(e, NULL, STATS_QUARANTINE); 285790792Sgshapiro#endif /* _FFR_QUARANTINE */ 285890792Sgshapiro 285990792Sgshapiro /* 286090792Sgshapiro ** If a header/body check (header checks or milter) 286190792Sgshapiro ** set EF_DISCARD, don't queueup the message -- 286290792Sgshapiro ** that would lose the EF_DISCARD bit and deliver 286390792Sgshapiro ** the message. 286490792Sgshapiro */ 286590792Sgshapiro 286690792Sgshapiro if (bitset(EF_DISCARD, e->e_flags)) 286790792Sgshapiro doublequeue = false; 286890792Sgshapiro 286990792Sgshapiro aborting = Errors > 0; 287090792Sgshapiro if (!aborting && 287190792Sgshapiro#if _FFR_QUARANTINE 287290792Sgshapiro (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) && 287390792Sgshapiro#endif /* _FFR_QUARANTINE */ 287490792Sgshapiro !split_by_recipient(e)) 287590792Sgshapiro aborting = bitset(EF_FATALERRS, e->e_flags); 287690792Sgshapiro 287790792Sgshapiro if (aborting) 287890792Sgshapiro { 287990792Sgshapiro /* Log who the mail would have gone to */ 288090792Sgshapiro logundelrcpts(e, e->e_message, 8, false); 288190792Sgshapiro flush_errors(true); 288290792Sgshapiro buffer_errors(); 288390792Sgshapiro goto abortmessage; 288490792Sgshapiro } 288590792Sgshapiro 288690792Sgshapiro /* from now on, we have to operate silently */ 288790792Sgshapiro buffer_errors(); 288890792Sgshapiro 288990792Sgshapiro#if 0 289090792Sgshapiro /* 289190792Sgshapiro ** Clear message, it may contain an error from the SMTP dialogue. 289290792Sgshapiro ** This error must not show up in the queue. 289390792Sgshapiro ** Some error message should show up, e.g., alias database 289490792Sgshapiro ** not available, but others shouldn't, e.g., from check_rcpt. 289590792Sgshapiro */ 289690792Sgshapiro 289790792Sgshapiro e->e_message = NULL; 289890792Sgshapiro#endif /* 0 */ 289990792Sgshapiro 290090792Sgshapiro /* 290190792Sgshapiro ** Arrange to send to everyone. 290290792Sgshapiro ** If sending to multiple people, mail back 290390792Sgshapiro ** errors rather than reporting directly. 290490792Sgshapiro ** In any case, don't mail back errors for 290590792Sgshapiro ** anything that has happened up to 290690792Sgshapiro ** now (the other end will do this). 290790792Sgshapiro ** Truncate our transcript -- the mail has gotten 290890792Sgshapiro ** to us successfully, and if we have 290990792Sgshapiro ** to mail this back, it will be easier 291090792Sgshapiro ** on the reader. 291190792Sgshapiro ** Then send to everyone. 291290792Sgshapiro ** Finally give a reply code. If an error has 291390792Sgshapiro ** already been given, don't mail a 291490792Sgshapiro ** message back. 291590792Sgshapiro ** We goose error returns by clearing error bit. 291690792Sgshapiro */ 291790792Sgshapiro 291890792Sgshapiro SmtpPhase = "delivery"; 291990792Sgshapiro (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL); 292090792Sgshapiro id = e->e_id; 292190792Sgshapiro 292290792Sgshapiro#if NAMED_BIND 292390792Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 292490792Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 292590792Sgshapiro#endif /* NAMED_BIND */ 292690792Sgshapiro 292790792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 292890792Sgshapiro { 292990792Sgshapiro /* make sure we actually do delivery */ 293090792Sgshapiro ee->e_flags &= ~EF_CLRQUEUE; 293190792Sgshapiro 293290792Sgshapiro /* from now on, operate silently */ 293390792Sgshapiro ee->e_errormode = EM_MAIL; 293490792Sgshapiro 293590792Sgshapiro if (doublequeue) 293690792Sgshapiro { 293790792Sgshapiro /* make sure it is in the queue */ 293890792Sgshapiro queueup(ee, false, true); 293990792Sgshapiro } 294090792Sgshapiro else 294190792Sgshapiro { 294290792Sgshapiro /* send to all recipients */ 294390792Sgshapiro sendall(ee, SM_DEFAULT); 294490792Sgshapiro } 294590792Sgshapiro ee->e_to = NULL; 294690792Sgshapiro } 294790792Sgshapiro 294890792Sgshapiro /* issue success message */ 294990792Sgshapiro message("250 2.0.0 %s Message accepted for delivery", id); 295090792Sgshapiro 295190792Sgshapiro /* if we just queued, poke it */ 295290792Sgshapiro if (doublequeue) 295390792Sgshapiro { 295490792Sgshapiro bool anything_to_send = false; 295590792Sgshapiro 295690792Sgshapiro sm_getla(); 295790792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 295890792Sgshapiro { 295990792Sgshapiro if (WILL_BE_QUEUED(ee->e_sendmode)) 296090792Sgshapiro continue; 296190792Sgshapiro if (shouldqueue(ee->e_msgpriority, ee->e_ctime)) 296290792Sgshapiro { 296390792Sgshapiro ee->e_sendmode = SM_QUEUE; 296490792Sgshapiro continue; 296590792Sgshapiro } 296690792Sgshapiro#if _FFR_QUARANTINE 296790792Sgshapiro else if (QueueMode != QM_QUARANTINE && 296890792Sgshapiro ee->e_quarmsg != NULL) 296990792Sgshapiro { 297090792Sgshapiro ee->e_sendmode = SM_QUEUE; 297190792Sgshapiro continue; 297290792Sgshapiro } 297390792Sgshapiro#endif /* _FFR_QUARANTINE */ 297490792Sgshapiro anything_to_send = true; 297590792Sgshapiro 297690792Sgshapiro /* close all the queue files */ 297790792Sgshapiro closexscript(ee); 297890792Sgshapiro if (ee->e_dfp != NULL) 297990792Sgshapiro { 298090792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 298190792Sgshapiro ee->e_dfp = NULL; 298290792Sgshapiro } 298390792Sgshapiro unlockqueue(ee); 298490792Sgshapiro } 298590792Sgshapiro if (anything_to_send) 298690792Sgshapiro { 298790792Sgshapiro#if PIPELINING 298890792Sgshapiro /* 298990792Sgshapiro ** XXX if we don't do this, we get 250 twice 299090792Sgshapiro ** because it is also flushed in the child. 299190792Sgshapiro */ 299290792Sgshapiro 299390792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 299490792Sgshapiro#endif /* PIPELINING */ 299590792Sgshapiro (void) doworklist(e, true, true); 299690792Sgshapiro } 299790792Sgshapiro } 299890792Sgshapiro 299990792Sgshapiro abortmessage: 300090792Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 300190792Sgshapiro logsender(e, NULL); 300290792Sgshapiro e->e_flags &= ~EF_LOGSENDER; 300390792Sgshapiro 300490792Sgshapiro /* clean up a bit */ 300590792Sgshapiro smtp->sm_gotmail = false; 300690792Sgshapiro 300790792Sgshapiro /* 300890792Sgshapiro ** Call dropenvelope if and only if the envelope is *not* 300990792Sgshapiro ** being processed by the child process forked by doworklist(). 301090792Sgshapiro */ 301190792Sgshapiro 301290792Sgshapiro if (aborting || bitset(EF_DISCARD, e->e_flags)) 301390792Sgshapiro dropenvelope(e, true, false); 301490792Sgshapiro else 301590792Sgshapiro { 301690792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 301790792Sgshapiro { 301890792Sgshapiro#if _FFR_QUARANTINE 301990792Sgshapiro if (!doublequeue && 302090792Sgshapiro QueueMode != QM_QUARANTINE && 302190792Sgshapiro ee->e_quarmsg != NULL) 302290792Sgshapiro { 302390792Sgshapiro dropenvelope(ee, true, false); 302490792Sgshapiro continue; 302590792Sgshapiro } 302690792Sgshapiro#endif /* _FFR_QUARANTINE */ 302790792Sgshapiro if (WILL_BE_QUEUED(ee->e_sendmode)) 302890792Sgshapiro dropenvelope(ee, true, false); 302990792Sgshapiro } 303090792Sgshapiro } 303190792Sgshapiro sm_rpool_free(e->e_rpool); 303290792Sgshapiro 303390792Sgshapiro /* 303490792Sgshapiro ** At this point, e == &MainEnvelope, but if we did splitting, 303590792Sgshapiro ** then CurEnv may point to an envelope structure that was just 303690792Sgshapiro ** freed with the rpool. So reset CurEnv *before* calling 303790792Sgshapiro ** newenvelope. 303890792Sgshapiro */ 303990792Sgshapiro 304090792Sgshapiro CurEnv = e; 304190792Sgshapiro newenvelope(e, e, sm_rpool_new_x(NULL)); 304290792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 304390792Sgshapiro 304490792Sgshapiro#if _FFR_QUARANTINE 304590792Sgshapiro /* restore connection quarantining */ 304690792Sgshapiro if (smtp->sm_quarmsg == NULL) 304790792Sgshapiro { 304890792Sgshapiro e->e_quarmsg = NULL; 304990792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), ""); 305090792Sgshapiro } 305190792Sgshapiro else 305290792Sgshapiro { 305390792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg); 305490792Sgshapiro macdefine(&e->e_macro, A_PERM, 305590792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 305690792Sgshapiro } 305790792Sgshapiro#endif /* _FFR_QUARANTINE */ 305838032Speter} 305990792Sgshapiro/* 306090792Sgshapiro** LOGUNDELRCPTS -- log undelivered (or all) recipients. 306190792Sgshapiro** 306290792Sgshapiro** Parameters: 306390792Sgshapiro** e -- envelope. 306490792Sgshapiro** msg -- message for Stat= 306590792Sgshapiro** level -- log level. 306690792Sgshapiro** all -- log all recipients. 306790792Sgshapiro** 306890792Sgshapiro** Returns: 306990792Sgshapiro** none. 307090792Sgshapiro** 307190792Sgshapiro** Side Effects: 307290792Sgshapiro** logs undelivered (or all) recipients 307390792Sgshapiro*/ 307490792Sgshapiro 307590792Sgshapirovoid 307690792Sgshapirologundelrcpts(e, msg, level, all) 307790792Sgshapiro ENVELOPE *e; 307890792Sgshapiro char *msg; 307990792Sgshapiro int level; 308090792Sgshapiro bool all; 308190792Sgshapiro{ 308290792Sgshapiro ADDRESS *a; 308390792Sgshapiro 308490792Sgshapiro if (LogLevel <= level || msg == NULL || *msg == '\0') 308590792Sgshapiro return; 308690792Sgshapiro 308790792Sgshapiro /* Clear $h so relay= doesn't get mislogged by logdelivery() */ 308890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', NULL); 308990792Sgshapiro 309090792Sgshapiro /* Log who the mail would have gone to */ 309190792Sgshapiro for (a = e->e_sendqueue; a != NULL; a = a->q_next) 309290792Sgshapiro { 309390792Sgshapiro if (!QS_IS_UNDELIVERED(a->q_state) && !all) 309490792Sgshapiro continue; 309590792Sgshapiro e->e_to = a->q_paddr; 309690792Sgshapiro logdelivery(NULL, NULL, a->q_status, msg, NULL, 309790792Sgshapiro (time_t) 0, e); 309890792Sgshapiro } 309990792Sgshapiro e->e_to = NULL; 310090792Sgshapiro} 310190792Sgshapiro/* 310238032Speter** CHECKSMTPATTACK -- check for denial-of-service attack by repetition 310338032Speter** 310438032Speter** Parameters: 310538032Speter** pcounter -- pointer to a counter for this command. 310638032Speter** maxcount -- maximum value for this counter before we 310738032Speter** slow down. 310864562Sgshapiro** waitnow -- sleep now (in this routine)? 310938032Speter** cname -- command name for logging. 311038032Speter** e -- the current envelope. 311138032Speter** 311238032Speter** Returns: 311371345Sgshapiro** time to wait. 311438032Speter** 311538032Speter** Side Effects: 311638032Speter** Slows down if we seem to be under attack. 311738032Speter*/ 311838032Speter 311964562Sgshapirostatic time_t 312064562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e) 312190792Sgshapiro volatile unsigned int *pcounter; 312238032Speter int maxcount; 312364562Sgshapiro bool waitnow; 312438032Speter char *cname; 312538032Speter ENVELOPE *e; 312638032Speter{ 312790792Sgshapiro if (maxcount <= 0) /* no limit */ 312890792Sgshapiro return (time_t) 0; 312990792Sgshapiro 313038032Speter if (++(*pcounter) >= maxcount) 313138032Speter { 313264562Sgshapiro time_t s; 313364562Sgshapiro 313438032Speter if (*pcounter == maxcount && LogLevel > 5) 313538032Speter { 313638032Speter sm_syslog(LOG_INFO, e->e_id, 313790792Sgshapiro "%.100s: possible SMTP attack: command=%.40s, count=%u", 313877349Sgshapiro CurSmtpClient, cname, *pcounter); 313938032Speter } 314064562Sgshapiro s = 1 << (*pcounter - maxcount); 314190792Sgshapiro if (s >= MAXTIMEOUT || s <= 0) 314264562Sgshapiro s = MAXTIMEOUT; 314390792Sgshapiro 314464562Sgshapiro /* sleep at least 1 second before returning */ 314564562Sgshapiro (void) sleep(*pcounter / maxcount); 314664562Sgshapiro s -= *pcounter / maxcount; 314764562Sgshapiro if (waitnow) 314864562Sgshapiro { 314964562Sgshapiro (void) sleep(s); 315090792Sgshapiro return 0; 315164562Sgshapiro } 315290792Sgshapiro return s; 315338032Speter } 315490792Sgshapiro return (time_t) 0; 315538032Speter} 315690792Sgshapiro/* 315790792Sgshapiro** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server 315890792Sgshapiro** 315990792Sgshapiro** Parameters: 316090792Sgshapiro** none. 316190792Sgshapiro** 316290792Sgshapiro** Returns: 316390792Sgshapiro** nothing. 316490792Sgshapiro** 316590792Sgshapiro** Side Effects: 316690792Sgshapiro** may change I/O fd. 316790792Sgshapiro*/ 316890792Sgshapiro 316990792Sgshapirostatic void 317090792Sgshapirosetup_smtpd_io() 317190792Sgshapiro{ 317290792Sgshapiro int inchfd, outchfd, outfd; 317390792Sgshapiro 317490792Sgshapiro inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 317590792Sgshapiro outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); 317690792Sgshapiro outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL); 317790792Sgshapiro if (outchfd != outfd) 317890792Sgshapiro { 317990792Sgshapiro /* arrange for debugging output to go to remote host */ 318090792Sgshapiro (void) dup2(outchfd, outfd); 318190792Sgshapiro } 318290792Sgshapiro 318390792Sgshapiro /* 318490792Sgshapiro ** if InChannel and OutChannel are stdin/stdout 318590792Sgshapiro ** and connected to ttys 318690792Sgshapiro ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT, 318790792Sgshapiro ** then "chain" them together. 318890792Sgshapiro */ 318990792Sgshapiro 319090792Sgshapiro if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO && 319190792Sgshapiro isatty(inchfd) && isatty(outchfd)) 319290792Sgshapiro { 319390792Sgshapiro int inmode, outmode; 319490792Sgshapiro 319590792Sgshapiro inmode = fcntl(inchfd, F_GETFL, 0); 319690792Sgshapiro if (inmode == -1) 319790792Sgshapiro { 319890792Sgshapiro if (LogLevel > 11) 319990792Sgshapiro sm_syslog(LOG_INFO, NOQID, 320090792Sgshapiro "fcntl(inchfd, F_GETFL) failed: %s", 320190792Sgshapiro sm_errstring(errno)); 320290792Sgshapiro return; 320390792Sgshapiro } 320490792Sgshapiro outmode = fcntl(outchfd, F_GETFL, 0); 320590792Sgshapiro if (outmode == -1) 320690792Sgshapiro { 320790792Sgshapiro if (LogLevel > 11) 320890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 320990792Sgshapiro "fcntl(outchfd, F_GETFL) failed: %s", 321090792Sgshapiro sm_errstring(errno)); 321190792Sgshapiro return; 321290792Sgshapiro } 321390792Sgshapiro if (bitset(O_NONBLOCK, inmode) || 321490792Sgshapiro bitset(O_NONBLOCK, outmode) || 321590792Sgshapiro fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1) 321690792Sgshapiro return; 321790792Sgshapiro outmode = fcntl(outchfd, F_GETFL, 0); 321890792Sgshapiro if (outmode != -1 && bitset(O_NONBLOCK, outmode)) 321990792Sgshapiro { 322090792Sgshapiro /* changing InChannel also changes OutChannel */ 322190792Sgshapiro sm_io_automode(OutChannel, InChannel); 322290792Sgshapiro if (tTd(97, 4) && LogLevel > 9) 322390792Sgshapiro sm_syslog(LOG_INFO, NOQID, 322490792Sgshapiro "set automode for I (%d)/O (%d) in SMTP server", 322590792Sgshapiro inchfd, outchfd); 322690792Sgshapiro } 322790792Sgshapiro 322890792Sgshapiro /* undo change of inchfd */ 322990792Sgshapiro (void) fcntl(inchfd, F_SETFL, inmode); 323090792Sgshapiro } 323190792Sgshapiro} 323290792Sgshapiro/* 323338032Speter** SKIPWORD -- skip a fixed word. 323438032Speter** 323538032Speter** Parameters: 323638032Speter** p -- place to start looking. 323738032Speter** w -- word to skip. 323838032Speter** 323938032Speter** Returns: 324038032Speter** p following w. 324138032Speter** NULL on error. 324238032Speter** 324338032Speter** Side Effects: 324438032Speter** clobbers the p data area. 324538032Speter*/ 324638032Speter 324738032Speterstatic char * 324838032Speterskipword(p, w) 324938032Speter register char *volatile p; 325038032Speter char *w; 325138032Speter{ 325238032Speter register char *q; 325338032Speter char *firstp = p; 325438032Speter 325538032Speter /* find beginning of word */ 325690792Sgshapiro SKIP_SPACE(p); 325738032Speter q = p; 325838032Speter 325938032Speter /* find end of word */ 326038032Speter while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 326138032Speter p++; 326238032Speter while (isascii(*p) && isspace(*p)) 326338032Speter *p++ = '\0'; 326438032Speter if (*p != ':') 326538032Speter { 326638032Speter syntax: 326764562Sgshapiro usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"", 326838032Speter shortenstring(firstp, MAXSHORTSTR)); 326964562Sgshapiro return NULL; 327038032Speter } 327138032Speter *p++ = '\0'; 327290792Sgshapiro SKIP_SPACE(p); 327338032Speter 327438032Speter if (*p == '\0') 327538032Speter goto syntax; 327638032Speter 327738032Speter /* see if the input word matches desired word */ 327890792Sgshapiro if (sm_strcasecmp(q, w)) 327938032Speter goto syntax; 328038032Speter 328164562Sgshapiro return p; 328238032Speter} 328390792Sgshapiro/* 328438032Speter** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 328538032Speter** 328638032Speter** Parameters: 328738032Speter** kp -- the parameter key. 328838032Speter** vp -- the value of that parameter. 328938032Speter** e -- the envelope. 329038032Speter** 329138032Speter** Returns: 329238032Speter** none. 329338032Speter*/ 329438032Speter 329564562Sgshapirostatic void 329638032Spetermail_esmtp_args(kp, vp, e) 329738032Speter char *kp; 329838032Speter char *vp; 329938032Speter ENVELOPE *e; 330038032Speter{ 330190792Sgshapiro if (sm_strcasecmp(kp, "size") == 0) 330238032Speter { 330338032Speter if (vp == NULL) 330438032Speter { 330564562Sgshapiro usrerr("501 5.5.2 SIZE requires a value"); 330638032Speter /* NOTREACHED */ 330738032Speter } 330890792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp); 330990792Sgshapiro errno = 0; 331071345Sgshapiro e->e_msgsize = strtol(vp, (char **) NULL, 10); 331166494Sgshapiro if (e->e_msgsize == LONG_MAX && errno == ERANGE) 331266494Sgshapiro { 331366494Sgshapiro usrerr("552 5.2.3 Message size exceeds maximum value"); 331466494Sgshapiro /* NOTREACHED */ 331566494Sgshapiro } 331690792Sgshapiro if (e->e_msgsize < 0) 331790792Sgshapiro { 331890792Sgshapiro usrerr("552 5.2.3 Message size invalid"); 331990792Sgshapiro /* NOTREACHED */ 332090792Sgshapiro } 332138032Speter } 332290792Sgshapiro else if (sm_strcasecmp(kp, "body") == 0) 332338032Speter { 332438032Speter if (vp == NULL) 332538032Speter { 332664562Sgshapiro usrerr("501 5.5.2 BODY requires a value"); 332738032Speter /* NOTREACHED */ 332838032Speter } 332990792Sgshapiro else if (sm_strcasecmp(vp, "8bitmime") == 0) 333038032Speter { 333190792Sgshapiro SevenBitInput = false; 333238032Speter } 333390792Sgshapiro else if (sm_strcasecmp(vp, "7bit") == 0) 333438032Speter { 333590792Sgshapiro SevenBitInput = true; 333638032Speter } 333738032Speter else 333838032Speter { 333990792Sgshapiro usrerr("501 5.5.4 Unknown BODY type %s", vp); 334038032Speter /* NOTREACHED */ 334138032Speter } 334290792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp); 334338032Speter } 334490792Sgshapiro else if (sm_strcasecmp(kp, "envid") == 0) 334538032Speter { 334664562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 334764562Sgshapiro { 334864562Sgshapiro usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN"); 334964562Sgshapiro /* NOTREACHED */ 335064562Sgshapiro } 335138032Speter if (vp == NULL) 335238032Speter { 335364562Sgshapiro usrerr("501 5.5.2 ENVID requires a value"); 335438032Speter /* NOTREACHED */ 335538032Speter } 335638032Speter if (!xtextok(vp)) 335738032Speter { 335864562Sgshapiro usrerr("501 5.5.4 Syntax error in ENVID parameter value"); 335938032Speter /* NOTREACHED */ 336038032Speter } 336138032Speter if (e->e_envid != NULL) 336238032Speter { 336364562Sgshapiro usrerr("501 5.5.0 Duplicate ENVID parameter"); 336438032Speter /* NOTREACHED */ 336538032Speter } 336690792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp); 336790792Sgshapiro macdefine(&e->e_macro, A_PERM, 336890792Sgshapiro macid("{dsn_envid}"), e->e_envid); 336938032Speter } 337090792Sgshapiro else if (sm_strcasecmp(kp, "ret") == 0) 337138032Speter { 337264562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 337364562Sgshapiro { 337464562Sgshapiro usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN"); 337564562Sgshapiro /* NOTREACHED */ 337664562Sgshapiro } 337738032Speter if (vp == NULL) 337838032Speter { 337964562Sgshapiro usrerr("501 5.5.2 RET requires a value"); 338038032Speter /* NOTREACHED */ 338138032Speter } 338238032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 338338032Speter { 338464562Sgshapiro usrerr("501 5.5.0 Duplicate RET parameter"); 338538032Speter /* NOTREACHED */ 338638032Speter } 338738032Speter e->e_flags |= EF_RET_PARAM; 338890792Sgshapiro if (sm_strcasecmp(vp, "hdrs") == 0) 338938032Speter e->e_flags |= EF_NO_BODY_RETN; 339090792Sgshapiro else if (sm_strcasecmp(vp, "full") != 0) 339138032Speter { 339264562Sgshapiro usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp); 339338032Speter /* NOTREACHED */ 339438032Speter } 339590792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp); 339638032Speter } 339790792Sgshapiro#if SASL 339890792Sgshapiro else if (sm_strcasecmp(kp, "auth") == 0) 339964562Sgshapiro { 340064562Sgshapiro int len; 340164562Sgshapiro char *q; 340264562Sgshapiro char *auth_param; /* the value of the AUTH=x */ 340364562Sgshapiro bool saveQuickAbort = QuickAbort; 340464562Sgshapiro bool saveSuprErrs = SuprErrs; 340590792Sgshapiro bool saveExitStat = ExitStat; 340664562Sgshapiro char pbuf[256]; 340764562Sgshapiro 340864562Sgshapiro if (vp == NULL) 340964562Sgshapiro { 341064562Sgshapiro usrerr("501 5.5.2 AUTH= requires a value"); 341164562Sgshapiro /* NOTREACHED */ 341264562Sgshapiro } 341364562Sgshapiro if (e->e_auth_param != NULL) 341464562Sgshapiro { 341564562Sgshapiro usrerr("501 5.5.0 Duplicate AUTH parameter"); 341664562Sgshapiro /* NOTREACHED */ 341764562Sgshapiro } 341864562Sgshapiro if ((q = strchr(vp, ' ')) != NULL) 341964562Sgshapiro len = q - vp + 1; 342064562Sgshapiro else 342164562Sgshapiro len = strlen(vp) + 1; 342264562Sgshapiro auth_param = xalloc(len); 342390792Sgshapiro (void) sm_strlcpy(auth_param, vp, len); 342464562Sgshapiro if (!xtextok(auth_param)) 342564562Sgshapiro { 342664562Sgshapiro usrerr("501 5.5.4 Syntax error in AUTH parameter value"); 342764562Sgshapiro /* just a warning? */ 342864562Sgshapiro /* NOTREACHED */ 342964562Sgshapiro } 343064562Sgshapiro 343164562Sgshapiro /* XXX this might be cut off */ 343290792Sgshapiro (void) sm_strlcpy(pbuf, xuntextify(auth_param), sizeof pbuf); 343364562Sgshapiro /* xalloc() the buffer instead? */ 343464562Sgshapiro 343564562Sgshapiro /* XXX define this always or only if trusted? */ 343690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), pbuf); 343764562Sgshapiro 343864562Sgshapiro /* 343964562Sgshapiro ** call Strust_auth to find out whether 344064562Sgshapiro ** auth_param is acceptable (trusted) 344164562Sgshapiro ** we shouldn't trust it if not authenticated 344264562Sgshapiro ** (required by RFC, leave it to ruleset?) 344364562Sgshapiro */ 344464562Sgshapiro 344590792Sgshapiro SuprErrs = true; 344690792Sgshapiro QuickAbort = false; 344764562Sgshapiro if (strcmp(auth_param, "<>") != 0 && 344890792Sgshapiro (rscheck("trust_auth", pbuf, NULL, e, true, false, 9, 344990792Sgshapiro NULL, NOQID) != EX_OK || Errors > 0)) 345064562Sgshapiro { 345164562Sgshapiro if (tTd(95, 8)) 345264562Sgshapiro { 345364562Sgshapiro q = e->e_auth_param; 345490792Sgshapiro sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n", 345564562Sgshapiro pbuf, (q == NULL) ? "" : q); 345664562Sgshapiro } 345790792Sgshapiro 345864562Sgshapiro /* not trusted */ 345990792Sgshapiro e->e_auth_param = "<>"; 346090792Sgshapiro# if _FFR_AUTH_PASSING 346190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 346290792Sgshapiro macid("{auth_author}"), NULL); 346390792Sgshapiro# endif /* _FFR_AUTH_PASSING */ 346464562Sgshapiro } 346564562Sgshapiro else 346664562Sgshapiro { 346764562Sgshapiro if (tTd(95, 8)) 346890792Sgshapiro sm_dprintf("auth=\"%.100s\" trusted\n", pbuf); 346990792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, 347090792Sgshapiro auth_param); 347164562Sgshapiro } 347290792Sgshapiro sm_free(auth_param); /* XXX */ 347377349Sgshapiro 347464562Sgshapiro /* reset values */ 347564562Sgshapiro Errors = 0; 347664562Sgshapiro QuickAbort = saveQuickAbort; 347764562Sgshapiro SuprErrs = saveSuprErrs; 347890792Sgshapiro ExitStat = saveExitStat; 347964562Sgshapiro } 348090792Sgshapiro#endif /* SASL */ 348190792Sgshapiro#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?') 348290792Sgshapiro 348390792Sgshapiro /* 348490792Sgshapiro ** "by" is only accepted if DeliverByMin >= 0. 348590792Sgshapiro ** We maybe could add this to the list of server_features. 348690792Sgshapiro */ 348790792Sgshapiro 348890792Sgshapiro else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0) 348990792Sgshapiro { 349090792Sgshapiro char *s; 349190792Sgshapiro 349290792Sgshapiro if (vp == NULL) 349390792Sgshapiro { 349490792Sgshapiro usrerr("501 5.5.2 BY= requires a value"); 349590792Sgshapiro /* NOTREACHED */ 349690792Sgshapiro } 349790792Sgshapiro errno = 0; 349890792Sgshapiro e->e_deliver_by = strtol(vp, &s, 10); 349990792Sgshapiro if (e->e_deliver_by == LONG_MIN || 350090792Sgshapiro e->e_deliver_by == LONG_MAX || 350190792Sgshapiro e->e_deliver_by > 999999999l || 350290792Sgshapiro e->e_deliver_by < -999999999l) 350390792Sgshapiro { 350490792Sgshapiro usrerr("501 5.5.2 BY=%s out of range", vp); 350590792Sgshapiro /* NOTREACHED */ 350690792Sgshapiro } 350790792Sgshapiro if (s == NULL || *s != ';') 350890792Sgshapiro { 350990792Sgshapiro usrerr("501 5.5.2 BY= missing ';'"); 351090792Sgshapiro /* NOTREACHED */ 351190792Sgshapiro } 351290792Sgshapiro e->e_dlvr_flag = 0; 351390792Sgshapiro ++s; /* XXX: spaces allowed? */ 351490792Sgshapiro SKIP_SPACE(s); 351590792Sgshapiro switch (tolower(*s)) 351690792Sgshapiro { 351790792Sgshapiro case 'n': 351890792Sgshapiro e->e_dlvr_flag = DLVR_NOTIFY; 351990792Sgshapiro break; 352090792Sgshapiro case 'r': 352190792Sgshapiro e->e_dlvr_flag = DLVR_RETURN; 352290792Sgshapiro if (e->e_deliver_by <= 0) 352390792Sgshapiro { 352490792Sgshapiro usrerr("501 5.5.4 mode R requires BY time > 0"); 352590792Sgshapiro /* NOTREACHED */ 352690792Sgshapiro } 352790792Sgshapiro if (DeliverByMin > 0 && e->e_deliver_by > 0 && 352890792Sgshapiro e->e_deliver_by < DeliverByMin) 352990792Sgshapiro { 353090792Sgshapiro usrerr("555 5.5.2 time %ld less than %ld", 353190792Sgshapiro e->e_deliver_by, (long) DeliverByMin); 353290792Sgshapiro /* NOTREACHED */ 353390792Sgshapiro } 353490792Sgshapiro break; 353590792Sgshapiro default: 353690792Sgshapiro usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s)); 353790792Sgshapiro /* NOTREACHED */ 353890792Sgshapiro } 353990792Sgshapiro ++s; /* XXX: spaces allowed? */ 354090792Sgshapiro SKIP_SPACE(s); 354190792Sgshapiro switch (tolower(*s)) 354290792Sgshapiro { 354390792Sgshapiro case 't': 354490792Sgshapiro e->e_dlvr_flag |= DLVR_TRACE; 354590792Sgshapiro break; 354690792Sgshapiro case '\0': 354790792Sgshapiro break; 354890792Sgshapiro default: 354990792Sgshapiro usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s)); 355090792Sgshapiro /* NOTREACHED */ 355190792Sgshapiro } 355290792Sgshapiro 355390792Sgshapiro /* XXX: check whether more characters follow? */ 355490792Sgshapiro } 355538032Speter else 355638032Speter { 355766494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 355838032Speter /* NOTREACHED */ 355938032Speter } 356038032Speter} 356190792Sgshapiro/* 356238032Speter** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 356338032Speter** 356438032Speter** Parameters: 356538032Speter** a -- the address corresponding to the To: parameter. 356638032Speter** kp -- the parameter key. 356738032Speter** vp -- the value of that parameter. 356838032Speter** e -- the envelope. 356938032Speter** 357038032Speter** Returns: 357138032Speter** none. 357238032Speter*/ 357338032Speter 357464562Sgshapirostatic void 357538032Speterrcpt_esmtp_args(a, kp, vp, e) 357638032Speter ADDRESS *a; 357738032Speter char *kp; 357838032Speter char *vp; 357938032Speter ENVELOPE *e; 358038032Speter{ 358190792Sgshapiro if (sm_strcasecmp(kp, "notify") == 0) 358238032Speter { 358338032Speter char *p; 358438032Speter 358564562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 358664562Sgshapiro { 358764562Sgshapiro usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN"); 358864562Sgshapiro /* NOTREACHED */ 358964562Sgshapiro } 359038032Speter if (vp == NULL) 359138032Speter { 359264562Sgshapiro usrerr("501 5.5.2 NOTIFY requires a value"); 359338032Speter /* NOTREACHED */ 359438032Speter } 359538032Speter a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 359638032Speter a->q_flags |= QHASNOTIFY; 359790792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp); 359864562Sgshapiro 359990792Sgshapiro if (sm_strcasecmp(vp, "never") == 0) 360038032Speter return; 360138032Speter for (p = vp; p != NULL; vp = p) 360238032Speter { 360338032Speter p = strchr(p, ','); 360438032Speter if (p != NULL) 360538032Speter *p++ = '\0'; 360690792Sgshapiro if (sm_strcasecmp(vp, "success") == 0) 360738032Speter a->q_flags |= QPINGONSUCCESS; 360890792Sgshapiro else if (sm_strcasecmp(vp, "failure") == 0) 360938032Speter a->q_flags |= QPINGONFAILURE; 361090792Sgshapiro else if (sm_strcasecmp(vp, "delay") == 0) 361138032Speter a->q_flags |= QPINGONDELAY; 361238032Speter else 361338032Speter { 361464562Sgshapiro usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY", 361538032Speter vp); 361638032Speter /* NOTREACHED */ 361738032Speter } 361838032Speter } 361938032Speter } 362090792Sgshapiro else if (sm_strcasecmp(kp, "orcpt") == 0) 362138032Speter { 362264562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 362364562Sgshapiro { 362464562Sgshapiro usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN"); 362564562Sgshapiro /* NOTREACHED */ 362664562Sgshapiro } 362738032Speter if (vp == NULL) 362838032Speter { 362964562Sgshapiro usrerr("501 5.5.2 ORCPT requires a value"); 363038032Speter /* NOTREACHED */ 363138032Speter } 363238032Speter if (strchr(vp, ';') == NULL || !xtextok(vp)) 363338032Speter { 363464562Sgshapiro usrerr("501 5.5.4 Syntax error in ORCPT parameter value"); 363538032Speter /* NOTREACHED */ 363638032Speter } 363738032Speter if (a->q_orcpt != NULL) 363838032Speter { 363964562Sgshapiro usrerr("501 5.5.0 Duplicate ORCPT parameter"); 364038032Speter /* NOTREACHED */ 364138032Speter } 364290792Sgshapiro a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp); 364338032Speter } 364438032Speter else 364538032Speter { 364666494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 364738032Speter /* NOTREACHED */ 364838032Speter } 364938032Speter} 365090792Sgshapiro/* 365138032Speter** PRINTVRFYADDR -- print an entry in the verify queue 365238032Speter** 365338032Speter** Parameters: 365490792Sgshapiro** a -- the address to print. 365538032Speter** last -- set if this is the last one. 365638032Speter** vrfy -- set if this is a VRFY command. 365738032Speter** 365838032Speter** Returns: 365938032Speter** none. 366038032Speter** 366138032Speter** Side Effects: 366238032Speter** Prints the appropriate 250 codes. 366338032Speter*/ 366464562Sgshapiro#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */ 366538032Speter 366664562Sgshapirostatic void 366738032Speterprintvrfyaddr(a, last, vrfy) 366838032Speter register ADDRESS *a; 366938032Speter bool last; 367038032Speter bool vrfy; 367138032Speter{ 367264562Sgshapiro char fmtbuf[30]; 367338032Speter 367438032Speter if (vrfy && a->q_mailer != NULL && 367538032Speter !bitnset(M_VRFY250, a->q_mailer->m_flags)) 367690792Sgshapiro (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf); 367738032Speter else 367890792Sgshapiro (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf); 367938032Speter fmtbuf[3] = last ? ' ' : '-'; 368090792Sgshapiro (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4); 368138032Speter if (a->q_fullname == NULL) 368238032Speter { 368364562Sgshapiro if ((a->q_mailer == NULL || 368464562Sgshapiro a->q_mailer->m_addrtype == NULL || 368590792Sgshapiro sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 368664562Sgshapiro strchr(a->q_user, '@') == NULL) 368790792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>", 368864562Sgshapiro sizeof fmtbuf - OFFF); 368938032Speter else 369090792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>", 369164562Sgshapiro sizeof fmtbuf - OFFF); 369238032Speter message(fmtbuf, a->q_user, MyHostName); 369338032Speter } 369438032Speter else 369538032Speter { 369664562Sgshapiro if ((a->q_mailer == NULL || 369764562Sgshapiro a->q_mailer->m_addrtype == NULL || 369890792Sgshapiro sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 369964562Sgshapiro strchr(a->q_user, '@') == NULL) 370090792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>", 370164562Sgshapiro sizeof fmtbuf - OFFF); 370238032Speter else 370390792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>", 370464562Sgshapiro sizeof fmtbuf - OFFF); 370538032Speter message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 370638032Speter } 370738032Speter} 370838032Speter 370990792Sgshapiro#if SASL 371090792Sgshapiro/* 371164562Sgshapiro** SASLMECHS -- get list of possible AUTH mechanisms 371264562Sgshapiro** 371364562Sgshapiro** Parameters: 371490792Sgshapiro** conn -- SASL connection info. 371590792Sgshapiro** mechlist -- output parameter for list of mechanisms. 371664562Sgshapiro** 371764562Sgshapiro** Returns: 371890792Sgshapiro** number of mechs. 371964562Sgshapiro*/ 372064562Sgshapiro 372164562Sgshapirostatic int 372264562Sgshapirosaslmechs(conn, mechlist) 372364562Sgshapiro sasl_conn_t *conn; 372464562Sgshapiro char **mechlist; 372564562Sgshapiro{ 372664562Sgshapiro int len, num, result; 372764562Sgshapiro 372864562Sgshapiro /* "user" is currently unused */ 372964562Sgshapiro result = sasl_listmech(conn, "user", /* XXX */ 373064562Sgshapiro "", " ", "", mechlist, 373190792Sgshapiro (unsigned int *)&len, (unsigned int *)&num); 373290792Sgshapiro if (result != SASL_OK) 373364562Sgshapiro { 373490792Sgshapiro if (LogLevel > 9) 373590792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 373690792Sgshapiro "AUTH error: listmech=%d, num=%d", 373790792Sgshapiro result, num); 373890792Sgshapiro num = 0; 373990792Sgshapiro } 374090792Sgshapiro if (num > 0) 374190792Sgshapiro { 374264562Sgshapiro if (LogLevel > 11) 374364562Sgshapiro sm_syslog(LOG_INFO, NOQID, 374490792Sgshapiro "AUTH: available mech=%s, allowed mech=%s", 374564562Sgshapiro *mechlist, AuthMechanisms); 374690792Sgshapiro *mechlist = intersect(AuthMechanisms, *mechlist, NULL); 374764562Sgshapiro } 374864562Sgshapiro else 374964562Sgshapiro { 375090792Sgshapiro *mechlist = NULL; /* be paranoid... */ 375190792Sgshapiro if (result == SASL_OK && LogLevel > 9) 375264562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 375390792Sgshapiro "AUTH warning: no mechanisms"); 375464562Sgshapiro } 375564562Sgshapiro return num; 375664562Sgshapiro} 375790792Sgshapiro/* 375864562Sgshapiro** PROXY_POLICY -- define proxy policy for AUTH 375964562Sgshapiro** 376064562Sgshapiro** Parameters: 376190792Sgshapiro** context -- unused. 376290792Sgshapiro** auth_identity -- authentication identity. 376390792Sgshapiro** requested_user -- authorization identity. 376490792Sgshapiro** user -- allowed user (output). 376590792Sgshapiro** errstr -- possible error string (output). 376664562Sgshapiro** 376764562Sgshapiro** Returns: 376864562Sgshapiro** ok? 376964562Sgshapiro*/ 377064562Sgshapiro 377164562Sgshapiroint 377264562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr) 377364562Sgshapiro void *context; 377464562Sgshapiro const char *auth_identity; 377564562Sgshapiro const char *requested_user; 377664562Sgshapiro const char **user; 377764562Sgshapiro const char **errstr; 377864562Sgshapiro{ 377964562Sgshapiro if (user == NULL || auth_identity == NULL) 378064562Sgshapiro return SASL_FAIL; 378164562Sgshapiro *user = newstr(auth_identity); 378264562Sgshapiro return SASL_OK; 378364562Sgshapiro} 378490792Sgshapiro#endif /* SASL */ 378564562Sgshapiro 378690792Sgshapiro#if STARTTLS 378790792Sgshapiro/* 378890792Sgshapiro** INITSRVTLS -- initialize server side TLS 378964562Sgshapiro** 379064562Sgshapiro** Parameters: 379190792Sgshapiro** tls_ok -- should tls initialization be done? 379264562Sgshapiro** 379364562Sgshapiro** Returns: 379490792Sgshapiro** succeeded? 379564562Sgshapiro** 379664562Sgshapiro** Side Effects: 379790792Sgshapiro** sets tls_ok_srv which is a static variable in this module. 379890792Sgshapiro** Do NOT remove assignments to it! 379964562Sgshapiro*/ 380064562Sgshapiro 380166494Sgshapirobool 380290792Sgshapiroinitsrvtls(tls_ok) 380390792Sgshapiro bool tls_ok; 380464562Sgshapiro{ 380590792Sgshapiro if (!tls_ok) 380690792Sgshapiro return false; 380764562Sgshapiro 380890792Sgshapiro /* do NOT remove assignment */ 380990792Sgshapiro tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCERTfile, 381090792Sgshapiro Srvkeyfile, CACERTpath, CACERTfile, DHParams); 381190792Sgshapiro return tls_ok_srv; 381264562Sgshapiro} 381390792Sgshapiro#endif /* STARTTLS */ 381464562Sgshapiro/* 381590792Sgshapiro** SRVFEATURES -- get features for SMTP server 381664562Sgshapiro** 381764562Sgshapiro** Parameters: 381890792Sgshapiro** e -- envelope (should be session context). 381990792Sgshapiro** clientname -- name of client. 382090792Sgshapiro** features -- default features for this invocation. 382164562Sgshapiro** 382264562Sgshapiro** Returns: 382390792Sgshapiro** server features. 382464562Sgshapiro*/ 382564562Sgshapiro 382690792Sgshapiro/* table with options: it uses just one character, how about strings? */ 382790792Sgshapirostatic struct 382864562Sgshapiro{ 382990792Sgshapiro char srvf_opt; 383090792Sgshapiro unsigned int srvf_flag; 383190792Sgshapiro} srv_feat_table[] = 383264562Sgshapiro{ 383390792Sgshapiro { 'A', SRV_OFFER_AUTH }, 383490792Sgshapiro { 'B', SRV_OFFER_VERB }, 383590792Sgshapiro { 'D', SRV_OFFER_DSN }, 383690792Sgshapiro { 'E', SRV_OFFER_ETRN }, 383790792Sgshapiro { 'L', SRV_REQ_AUTH }, /* not documented in 8.12 */ 383890792Sgshapiro#if PIPELINING 383990792Sgshapiro# if _FFR_NO_PIPE 384090792Sgshapiro { 'N', SRV_NO_PIPE }, 384190792Sgshapiro# endif /* _FFR_NO_PIPE */ 384290792Sgshapiro { 'P', SRV_OFFER_PIPE }, 384390792Sgshapiro#endif /* PIPELINING */ 384490792Sgshapiro { 'R', SRV_VRFY_CLT }, 384590792Sgshapiro { 'S', SRV_OFFER_TLS }, 384690792Sgshapiro/* { 'T', SRV_TMP_FAIL }, */ 384790792Sgshapiro { 'V', SRV_VRFY_CLT }, 384890792Sgshapiro { 'X', SRV_OFFER_EXPN }, 384990792Sgshapiro/* { 'Y', SRV_OFFER_VRFY }, */ 385090792Sgshapiro { '\0', SRV_NONE } 385190792Sgshapiro}; 385264562Sgshapiro 385390792Sgshapirostatic unsigned int 385490792Sgshapirosrvfeatures(e, clientname, features) 385590792Sgshapiro ENVELOPE *e; 385690792Sgshapiro char *clientname; 385790792Sgshapiro unsigned int features; 385877349Sgshapiro{ 385990792Sgshapiro int r, i, j; 386090792Sgshapiro char **pvp, c, opt; 386190792Sgshapiro char pvpbuf[PSBUFSIZE]; 386277349Sgshapiro 386390792Sgshapiro pvp = NULL; 386490792Sgshapiro r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf, 386590792Sgshapiro sizeof(pvpbuf)); 386690792Sgshapiro if (r != EX_OK) 386790792Sgshapiro return features; 386890792Sgshapiro if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 386990792Sgshapiro return features; 387090792Sgshapiro if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0) 387190792Sgshapiro return SRV_TMP_FAIL; 387277349Sgshapiro 387364562Sgshapiro /* 387490792Sgshapiro ** General rule (see sendmail.h, d_flags): 387590792Sgshapiro ** lower case: required/offered, upper case: Not required/available 387690792Sgshapiro ** 387790792Sgshapiro ** Since we can change some features per daemon, we have both 387890792Sgshapiro ** cases here: turn on/off a feature. 387964562Sgshapiro */ 388064562Sgshapiro 388190792Sgshapiro for (i = 1; pvp[i] != NULL; i++) 388264562Sgshapiro { 388390792Sgshapiro c = pvp[i][0]; 388490792Sgshapiro j = 0; 388590792Sgshapiro for (;;) 388664562Sgshapiro { 388790792Sgshapiro if ((opt = srv_feat_table[j].srvf_opt) == '\0') 388864562Sgshapiro { 388990792Sgshapiro if (LogLevel > 9) 389090792Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 389190792Sgshapiro "srvfeatures: unknown feature %s", 389290792Sgshapiro pvp[i]); 389390792Sgshapiro break; 389464562Sgshapiro } 389590792Sgshapiro if (c == opt) 389664562Sgshapiro { 389790792Sgshapiro features &= ~(srv_feat_table[j].srvf_flag); 389890792Sgshapiro break; 389964562Sgshapiro } 390090792Sgshapiro if (c == tolower(opt)) 390164562Sgshapiro { 390290792Sgshapiro features |= srv_feat_table[j].srvf_flag; 390390792Sgshapiro break; 390464562Sgshapiro } 390590792Sgshapiro ++j; 390664562Sgshapiro } 390764562Sgshapiro } 390890792Sgshapiro return features; 390964562Sgshapiro} 391064562Sgshapiro 391190792Sgshapiro/* 391238032Speter** HELP -- implement the HELP command. 391338032Speter** 391438032Speter** Parameters: 391538032Speter** topic -- the topic we want help for. 391690792Sgshapiro** e -- envelope. 391738032Speter** 391838032Speter** Returns: 391938032Speter** none. 392038032Speter** 392138032Speter** Side Effects: 392238032Speter** outputs the help file to message output. 392338032Speter*/ 392464562Sgshapiro#define HELPVSTR "#vers " 392564562Sgshapiro#define HELPVERSION 2 392638032Speter 392738032Spetervoid 392864562Sgshapirohelp(topic, e) 392938032Speter char *topic; 393064562Sgshapiro ENVELOPE *e; 393138032Speter{ 393290792Sgshapiro register SM_FILE_T *hf; 393364562Sgshapiro register char *p; 393438032Speter int len; 393538032Speter bool noinfo; 393690792Sgshapiro bool first = true; 393764562Sgshapiro long sff = SFF_OPENASROOT|SFF_REGONLY; 393838032Speter char buf[MAXLINE]; 393964562Sgshapiro char inp[MAXLINE]; 394064562Sgshapiro static int foundvers = -1; 394138032Speter extern char Version[]; 394238032Speter 394338032Speter if (DontLockReadFiles) 394438032Speter sff |= SFF_NOLOCK; 394564562Sgshapiro if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail)) 394638032Speter sff |= SFF_SAFEDIRPATH; 394738032Speter 394838032Speter if (HelpFile == NULL || 394938032Speter (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL) 395038032Speter { 395138032Speter /* no help */ 395238032Speter errno = 0; 395364562Sgshapiro message("502 5.3.0 Sendmail %s -- HELP not implemented", 395464562Sgshapiro Version); 395538032Speter return; 395638032Speter } 395738032Speter 395838032Speter if (topic == NULL || *topic == '\0') 395938032Speter { 396038032Speter topic = "smtp"; 396190792Sgshapiro noinfo = false; 396238032Speter } 396338032Speter else 396438032Speter { 396538032Speter makelower(topic); 396690792Sgshapiro noinfo = true; 396738032Speter } 396838032Speter 396938032Speter len = strlen(topic); 397038032Speter 397190792Sgshapiro while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 397238032Speter { 397364562Sgshapiro if (buf[0] == '#') 397464562Sgshapiro { 397564562Sgshapiro if (foundvers < 0 && 397664562Sgshapiro strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0) 397764562Sgshapiro { 397864562Sgshapiro int h; 397964562Sgshapiro 398090792Sgshapiro if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d", 398190792Sgshapiro &h) == 1) 398264562Sgshapiro foundvers = h; 398364562Sgshapiro } 398464562Sgshapiro continue; 398564562Sgshapiro } 398638032Speter if (strncmp(buf, topic, len) == 0) 398738032Speter { 398864562Sgshapiro if (first) 398964562Sgshapiro { 399090792Sgshapiro first = false; 399138032Speter 399264562Sgshapiro /* print version if no/old vers# in file */ 399364562Sgshapiro if (foundvers < 2 && !noinfo) 399464562Sgshapiro message("214-2.0.0 This is Sendmail version %s", Version); 399564562Sgshapiro } 399664562Sgshapiro p = strpbrk(buf, " \t"); 399738032Speter if (p == NULL) 399864562Sgshapiro p = buf + strlen(buf) - 1; 399938032Speter else 400038032Speter p++; 400190792Sgshapiro fixcrlf(p, true); 400264562Sgshapiro if (foundvers >= 2) 400364562Sgshapiro { 400464562Sgshapiro translate_dollars(p); 400564562Sgshapiro expand(p, inp, sizeof inp, e); 400664562Sgshapiro p = inp; 400764562Sgshapiro } 400864562Sgshapiro message("214-2.0.0 %s", p); 400990792Sgshapiro noinfo = false; 401038032Speter } 401138032Speter } 401238032Speter 401338032Speter if (noinfo) 401464562Sgshapiro message("504 5.3.0 HELP topic \"%.10s\" unknown", topic); 401538032Speter else 401664562Sgshapiro message("214 2.0.0 End of HELP info"); 401764562Sgshapiro 401864562Sgshapiro if (foundvers != 0 && foundvers < HELPVERSION) 401964562Sgshapiro { 402064562Sgshapiro if (LogLevel > 1) 402164562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 402264562Sgshapiro "%s too old (require version %d)", 402364562Sgshapiro HelpFile, HELPVERSION); 402464562Sgshapiro 402564562Sgshapiro /* avoid log next time */ 402664562Sgshapiro foundvers = 0; 402764562Sgshapiro } 402864562Sgshapiro 402990792Sgshapiro (void) sm_io_close(hf, SM_TIME_DEFAULT); 403038032Speter} 4031