srvrsmtp.c revision 132943
138032Speter/* 2132943Sgshapiro * Copyright (c) 1998-2004 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 16132943Sgshapiro# include <libmilter/mfapi.h> 1790792Sgshapiro# include <libmilter/mfdef.h> 1890792Sgshapiro#endif /* MILTER */ 1964562Sgshapiro 20132943SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.900 2004/07/08 23:29:33 ca Exp $") 2164562Sgshapiro 22132943Sgshapiro#include <sys/time.h> 23132943Sgshapiro#include <sm/fdset.h> 24132943Sgshapiro 2590792Sgshapiro#if SASL || STARTTLS 2690792Sgshapiro# include "sfsasl.h" 2790792Sgshapiro#endif /* SASL || STARTTLS */ 2890792Sgshapiro#if SASL 2990792Sgshapiro# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1) 3064562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **)); 3190792Sgshapiro#endif /* SASL */ 3290792Sgshapiro#if STARTTLS 3390792Sgshapiro# include <sysexits.h> 3438032Speter 3590792Sgshapirostatic SSL_CTX *srv_ctx = NULL; /* TLS server context */ 3690792Sgshapirostatic SSL *srv_ssl = NULL; /* per connection context */ 3738032Speter 3890792Sgshapirostatic bool tls_ok_srv = false; 3990792Sgshapiro 4090792Sgshapiroextern void tls_set_verify __P((SSL_CTX *, SSL *, bool)); 4190792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \ 4290792Sgshapiro bitset(SRV_VRFY_CLT, features)) 4390792Sgshapiro#endif /* STARTTLS */ 4490792Sgshapiro 4590792Sgshapiro/* server features */ 4690792Sgshapiro#define SRV_NONE 0x0000 /* none... */ 4790792Sgshapiro#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */ 4890792Sgshapiro#define SRV_VRFY_CLT 0x0002 /* request a cert */ 4990792Sgshapiro#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */ 5090792Sgshapiro#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */ 5190792Sgshapiro#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */ 5290792Sgshapiro#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */ 5390792Sgshapiro#define SRV_OFFER_VERB 0x0040 /* offer VERB */ 5490792Sgshapiro#define SRV_OFFER_DSN 0x0080 /* offer DSN */ 5590792Sgshapiro#if PIPELINING 5690792Sgshapiro# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */ 5790792Sgshapiro# if _FFR_NO_PIPE 5890792Sgshapiro# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */ 5990792Sgshapiro# endif /* _FFR_NO_PIPE */ 6090792Sgshapiro#endif /* PIPELINING */ 6190792Sgshapiro#define SRV_REQ_AUTH 0x0400 /* require AUTH */ 62132943Sgshapiro#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */ 6390792Sgshapiro#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */ 6490792Sgshapiro 6590792Sgshapirostatic unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int)); 6690792Sgshapiro 67132943Sgshapiro#define STOP_ATTACK ((time_t) -1) 68132943Sgshapirostatic time_t checksmtpattack __P((volatile unsigned int *, unsigned int, 69132943Sgshapiro bool, char *, ENVELOPE *)); 7064562Sgshapirostatic void mail_esmtp_args __P((char *, char *, ENVELOPE *)); 7164562Sgshapirostatic void printvrfyaddr __P((ADDRESS *, bool, bool)); 7264562Sgshapirostatic void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); 7364562Sgshapirostatic char *skipword __P((char *volatile, char *)); 7490792Sgshapirostatic void setup_smtpd_io __P((void)); 75120256Sgshapiro 76120256Sgshapiro#if SASL 77120256Sgshapiro# if SASL >= 20000 78120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname, 79120256Sgshapiro char *_remoteip, char *_localip, 80120256Sgshapiro char *_auth_id, sasl_ssf_t *_ext_ssf)); 81120256Sgshapiro 82120256Sgshapiro# define RESET_SASLCONN \ 83132943Sgshapiro result = reset_saslconn(&conn, AuthRealm, remoteip, localip, auth_id, \ 84120256Sgshapiro &ext_ssf); \ 85120256Sgshapiro if (result != SASL_OK) \ 86120256Sgshapiro { \ 87120256Sgshapiro /* This is pretty fatal */ \ 88120256Sgshapiro goto doquit; \ 89120256Sgshapiro } 90120256Sgshapiro 91120256Sgshapiro# else /* SASL >= 20000 */ 92120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname, 93120256Sgshapiro struct sockaddr_in *_saddr_r, 94120256Sgshapiro struct sockaddr_in *_saddr_l, 95120256Sgshapiro sasl_external_properties_t *_ext_ssf)); 96120256Sgshapiro# define RESET_SASLCONN \ 97132943Sgshapiro result = reset_saslconn(&conn, AuthRealm, &saddr_r, &saddr_l, &ext_ssf); \ 98120256Sgshapiro if (result != SASL_OK) \ 99120256Sgshapiro { \ 100120256Sgshapiro /* This is pretty fatal */ \ 101120256Sgshapiro goto doquit; \ 102120256Sgshapiro } 103120256Sgshapiro 104120256Sgshapiro# endif /* SASL >= 20000 */ 105120256Sgshapiro#endif /* SASL */ 106120256Sgshapiro 10764562Sgshapiroextern ENVELOPE BlankEnvelope; 10838032Speter 109132943Sgshapiro#define NBADRCPTS \ 110132943Sgshapiro do \ 111132943Sgshapiro { \ 112132943Sgshapiro char buf[16]; \ 113132943Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%d", \ 114132943Sgshapiro BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \ 115132943Sgshapiro ? n_badrcpts - 1 : n_badrcpts); \ 116132943Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \ 117132943Sgshapiro } while (0) 118132943Sgshapiro 11990792Sgshapiro#define SKIP_SPACE(s) while (isascii(*s) && isspace(*s)) \ 12090792Sgshapiro (s)++ 12190792Sgshapiro 12238032Speter/* 12338032Speter** SMTP -- run the SMTP protocol. 12438032Speter** 12538032Speter** Parameters: 12638032Speter** nullserver -- if non-NULL, rejection message for 12790792Sgshapiro** (almost) all SMTP commands. 12890792Sgshapiro** d_flags -- daemon flags 12938032Speter** e -- the envelope. 13038032Speter** 13138032Speter** Returns: 13238032Speter** never. 13338032Speter** 13438032Speter** Side Effects: 13590792Sgshapiro** Reads commands from the input channel and processes them. 13638032Speter*/ 13738032Speter 13890792Sgshapiro/* 13990792Sgshapiro** Notice: The smtp server doesn't have a session context like the client 14090792Sgshapiro** side has (mci). Therefore some data (session oriented) is allocated 14190792Sgshapiro** or assigned to the "wrong" structure (esp. STARTTLS, AUTH). 14290792Sgshapiro** This should be fixed in a successor version. 14390792Sgshapiro*/ 14490792Sgshapiro 14538032Speterstruct cmd 14638032Speter{ 14764562Sgshapiro char *cmd_name; /* command name */ 14864562Sgshapiro int cmd_code; /* internal code, see below */ 14938032Speter}; 15038032Speter 15164562Sgshapiro/* values for cmd_code */ 15290792Sgshapiro#define CMDERROR 0 /* bad command */ 15390792Sgshapiro#define CMDMAIL 1 /* mail -- designate sender */ 15490792Sgshapiro#define CMDRCPT 2 /* rcpt -- designate recipient */ 15590792Sgshapiro#define CMDDATA 3 /* data -- send message text */ 15690792Sgshapiro#define CMDRSET 4 /* rset -- reset state */ 15790792Sgshapiro#define CMDVRFY 5 /* vrfy -- verify address */ 15890792Sgshapiro#define CMDEXPN 6 /* expn -- expand address */ 15990792Sgshapiro#define CMDNOOP 7 /* noop -- do nothing */ 16090792Sgshapiro#define CMDQUIT 8 /* quit -- close connection and die */ 16190792Sgshapiro#define CMDHELO 9 /* helo -- be polite */ 16290792Sgshapiro#define CMDHELP 10 /* help -- give usage info */ 16390792Sgshapiro#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 16490792Sgshapiro#define CMDETRN 12 /* etrn -- flush queue */ 16590792Sgshapiro#if SASL 16690792Sgshapiro# define CMDAUTH 13 /* auth -- SASL authenticate */ 16790792Sgshapiro#endif /* SASL */ 16890792Sgshapiro#if STARTTLS 16990792Sgshapiro# define CMDSTLS 14 /* STARTTLS -- start TLS session */ 17090792Sgshapiro#endif /* STARTTLS */ 17138032Speter/* non-standard commands */ 17290792Sgshapiro#define CMDVERB 17 /* verb -- go into verbose mode */ 17364562Sgshapiro/* unimplemented commands from RFC 821 */ 17490792Sgshapiro#define CMDUNIMPL 19 /* unimplemented rfc821 commands */ 17538032Speter/* use this to catch and log "door handle" attempts on your system */ 17690792Sgshapiro#define CMDLOGBOGUS 23 /* bogus command that should be logged */ 17738032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */ 17890792Sgshapiro#define CMDDBGQSHOW 24 /* showq -- show send queue */ 17990792Sgshapiro#define CMDDBGDEBUG 25 /* debug -- set debug mode */ 18038032Speter 18166494Sgshapiro/* 18290792Sgshapiro** Note: If you change this list, remember to update 'helpfile' 18366494Sgshapiro*/ 18466494Sgshapiro 18538032Speterstatic struct cmd CmdTab[] = 18638032Speter{ 18738032Speter { "mail", CMDMAIL }, 18838032Speter { "rcpt", CMDRCPT }, 18938032Speter { "data", CMDDATA }, 19038032Speter { "rset", CMDRSET }, 19138032Speter { "vrfy", CMDVRFY }, 19238032Speter { "expn", CMDEXPN }, 19338032Speter { "help", CMDHELP }, 19438032Speter { "noop", CMDNOOP }, 19538032Speter { "quit", CMDQUIT }, 19638032Speter { "helo", CMDHELO }, 19738032Speter { "ehlo", CMDEHLO }, 19838032Speter { "etrn", CMDETRN }, 19938032Speter { "verb", CMDVERB }, 20064562Sgshapiro { "send", CMDUNIMPL }, 20164562Sgshapiro { "saml", CMDUNIMPL }, 20264562Sgshapiro { "soml", CMDUNIMPL }, 20364562Sgshapiro { "turn", CMDUNIMPL }, 20490792Sgshapiro#if SASL 20564562Sgshapiro { "auth", CMDAUTH, }, 20690792Sgshapiro#endif /* SASL */ 20790792Sgshapiro#if STARTTLS 20864562Sgshapiro { "starttls", CMDSTLS, }, 20990792Sgshapiro#endif /* STARTTLS */ 21038032Speter /* remaining commands are here only to trap and log attempts to use them */ 21138032Speter { "showq", CMDDBGQSHOW }, 21238032Speter { "debug", CMDDBGDEBUG }, 21338032Speter { "wiz", CMDLOGBOGUS }, 21438032Speter 21538032Speter { NULL, CMDERROR } 21638032Speter}; 21738032Speter 21864562Sgshapirostatic char *CurSmtpClient; /* who's at the other end of channel */ 21938032Speter 22090792Sgshapiro#ifndef MAXBADCOMMANDS 22190792Sgshapiro# define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 22294334Sgshapiro#endif /* ! MAXBADCOMMANDS */ 22390792Sgshapiro#ifndef MAXNOOPCOMMANDS 22490792Sgshapiro# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ 22594334Sgshapiro#endif /* ! MAXNOOPCOMMANDS */ 22690792Sgshapiro#ifndef MAXHELOCOMMANDS 22790792Sgshapiro# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ 22894334Sgshapiro#endif /* ! MAXHELOCOMMANDS */ 22990792Sgshapiro#ifndef MAXVRFYCOMMANDS 23090792Sgshapiro# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ 23194334Sgshapiro#endif /* ! MAXVRFYCOMMANDS */ 23290792Sgshapiro#ifndef MAXETRNCOMMANDS 23390792Sgshapiro# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ 23494334Sgshapiro#endif /* ! MAXETRNCOMMANDS */ 23590792Sgshapiro#ifndef MAXTIMEOUT 23690792Sgshapiro# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */ 23794334Sgshapiro#endif /* ! MAXTIMEOUT */ 23838032Speter 239132943Sgshapiro/* 240132943Sgshapiro** Maximum shift value to compute timeout for bad commands. 241132943Sgshapiro** This introduces an upper limit of 2^MAXSHIFT for the timeout. 242132943Sgshapiro*/ 243132943Sgshapiro 244132943Sgshapiro#ifndef MAXSHIFT 245132943Sgshapiro# define MAXSHIFT 8 246132943Sgshapiro#endif /* ! MAXSHIFT */ 247132943Sgshapiro#if MAXSHIFT > 31 248132943Sgshapiro ERROR _MAXSHIFT > 31 is invalid 249132943Sgshapiro#endif /* MAXSHIFT */ 250132943Sgshapiro 251132943Sgshapiro 252132943Sgshapiro#if MAXBADCOMMANDS > 0 253132943Sgshapiro# define STOP_IF_ATTACK(r) do \ 254132943Sgshapiro { \ 255132943Sgshapiro if ((r) == STOP_ATTACK) \ 256132943Sgshapiro goto stopattack; \ 257132943Sgshapiro } while (0) 258132943Sgshapiro 259132943Sgshapiro#else /* MAXBADCOMMANDS > 0 */ 260132943Sgshapiro# define STOP_IF_ATTACK(r) r 261132943Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 262132943Sgshapiro 263132943Sgshapiro 26490792Sgshapiro#if SM_HEAP_CHECK 26590792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp", 26690792Sgshapiro "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $"); 26790792Sgshapiro#endif /* SM_HEAP_CHECK */ 26838032Speter 26990792Sgshapirotypedef struct 27090792Sgshapiro{ 27190792Sgshapiro bool sm_gotmail; /* mail command received */ 27290792Sgshapiro unsigned int sm_nrcpts; /* number of successful RCPT commands */ 27390792Sgshapiro bool sm_discard; 27490792Sgshapiro#if MILTER 27590792Sgshapiro bool sm_milterize; 27690792Sgshapiro bool sm_milterlist; /* any filters in the list? */ 27790792Sgshapiro#endif /* MILTER */ 27890792Sgshapiro char *sm_quarmsg; /* carry quarantining across messages */ 27990792Sgshapiro} SMTP_T; 28090792Sgshapiro 281132943Sgshapirostatic bool smtp_data __P((SMTP_T *, ENVELOPE *)); 28290792Sgshapiro 283132943Sgshapiro#define MSG_TEMPFAIL "451 4.3.2 Please try again later" 28490792Sgshapiro 28590792Sgshapiro#if MILTER 28690792Sgshapiro# define MILTER_ABORT(e) milter_abort((e)) 287110560Sgshapiro 28890792Sgshapiro# define MILTER_REPLY(str) \ 28990792Sgshapiro { \ 29090792Sgshapiro int savelogusrerrs = LogUsrErrs; \ 29190792Sgshapiro \ 29290792Sgshapiro switch (state) \ 29390792Sgshapiro { \ 29490792Sgshapiro case SMFIR_REPLYCODE: \ 29590792Sgshapiro if (MilterLogLevel > 3) \ 29690792Sgshapiro { \ 29790792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 29890792Sgshapiro "Milter: %s=%s, reject=%s", \ 29990792Sgshapiro str, addr, response); \ 30090792Sgshapiro LogUsrErrs = false; \ 30190792Sgshapiro } \ 302132943Sgshapiro if (strncmp(response, "421 ", 4) == 0) \ 303132943Sgshapiro { \ 304132943Sgshapiro bool tsave = QuickAbort; \ 305132943Sgshapiro \ 306132943Sgshapiro QuickAbort = false; \ 307132943Sgshapiro usrerr(response); \ 308132943Sgshapiro QuickAbort = tsave; \ 309132943Sgshapiro e->e_sendqueue = NULL; \ 310132943Sgshapiro goto doquit; \ 311132943Sgshapiro } \ 312132943Sgshapiro else \ 313132943Sgshapiro usrerr(response); \ 31490792Sgshapiro break; \ 31590792Sgshapiro \ 31690792Sgshapiro case SMFIR_REJECT: \ 31790792Sgshapiro if (MilterLogLevel > 3) \ 31890792Sgshapiro { \ 31990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 32090792Sgshapiro "Milter: %s=%s, reject=550 5.7.1 Command rejected", \ 32190792Sgshapiro str, addr); \ 32290792Sgshapiro LogUsrErrs = false; \ 32390792Sgshapiro } \ 32490792Sgshapiro usrerr("550 5.7.1 Command rejected"); \ 32590792Sgshapiro break; \ 32690792Sgshapiro \ 32790792Sgshapiro case SMFIR_DISCARD: \ 32890792Sgshapiro if (MilterLogLevel > 3) \ 32990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 33090792Sgshapiro "Milter: %s=%s, discard", \ 33190792Sgshapiro str, addr); \ 33290792Sgshapiro e->e_flags |= EF_DISCARD; \ 33390792Sgshapiro break; \ 33490792Sgshapiro \ 33590792Sgshapiro case SMFIR_TEMPFAIL: \ 33690792Sgshapiro if (MilterLogLevel > 3) \ 33790792Sgshapiro { \ 33890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 33990792Sgshapiro "Milter: %s=%s, reject=%s", \ 34090792Sgshapiro str, addr, MSG_TEMPFAIL); \ 34190792Sgshapiro LogUsrErrs = false; \ 34290792Sgshapiro } \ 34390792Sgshapiro usrerr(MSG_TEMPFAIL); \ 34490792Sgshapiro break; \ 34590792Sgshapiro } \ 34690792Sgshapiro LogUsrErrs = savelogusrerrs; \ 34790792Sgshapiro if (response != NULL) \ 34890792Sgshapiro sm_free(response); /* XXX */ \ 34990792Sgshapiro } 35090792Sgshapiro 35190792Sgshapiro#else /* MILTER */ 35290792Sgshapiro# define MILTER_ABORT(e) 35390792Sgshapiro#endif /* MILTER */ 35490792Sgshapiro 35590792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */ 35690792Sgshapiro#define CLEAR_STATE(cmd) \ 357132943Sgshapirodo \ 35890792Sgshapiro{ \ 35990792Sgshapiro /* abort milter filters */ \ 36090792Sgshapiro MILTER_ABORT(e); \ 36190792Sgshapiro \ 36290792Sgshapiro if (smtp.sm_nrcpts > 0) \ 36390792Sgshapiro { \ 36490792Sgshapiro logundelrcpts(e, cmd, 10, false); \ 36590792Sgshapiro smtp.sm_nrcpts = 0; \ 36690792Sgshapiro macdefine(&e->e_macro, A_PERM, \ 36790792Sgshapiro macid("{nrcpts}"), "0"); \ 36890792Sgshapiro } \ 36990792Sgshapiro \ 37090792Sgshapiro e->e_sendqueue = NULL; \ 37190792Sgshapiro e->e_flags |= EF_CLRQUEUE; \ 37290792Sgshapiro \ 37390792Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \ 37490792Sgshapiro logsender(e, NULL); \ 37590792Sgshapiro e->e_flags &= ~EF_LOGSENDER; \ 37690792Sgshapiro \ 37790792Sgshapiro /* clean up a bit */ \ 37890792Sgshapiro smtp.sm_gotmail = false; \ 37990792Sgshapiro SuprErrs = true; \ 38090792Sgshapiro dropenvelope(e, true, false); \ 38190792Sgshapiro sm_rpool_free(e->e_rpool); \ 38290792Sgshapiro e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \ 38390792Sgshapiro CurEnv = e; \ 384132943Sgshapiro \ 385132943Sgshapiro /* put back discard bit */ \ 386132943Sgshapiro if (smtp.sm_discard) \ 387132943Sgshapiro e->e_flags |= EF_DISCARD; \ 388132943Sgshapiro \ 389132943Sgshapiro /* restore connection quarantining */ \ 390132943Sgshapiro if (smtp.sm_quarmsg == NULL) \ 391132943Sgshapiro { \ 392132943Sgshapiro e->e_quarmsg = NULL; \ 393132943Sgshapiro macdefine(&e->e_macro, A_PERM, \ 394132943Sgshapiro macid("{quarantine}"), ""); \ 395132943Sgshapiro } \ 396132943Sgshapiro else \ 397132943Sgshapiro { \ 398132943Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 399132943Sgshapiro smtp.sm_quarmsg); \ 400132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 401132943Sgshapiro e->e_quarmsg); \ 402132943Sgshapiro } \ 403132943Sgshapiro} while (0) 40490792Sgshapiro 40590792Sgshapiro/* sleep to flatten out connection load */ 40690792Sgshapiro#define MIN_DELAY_LOG 15 /* wait before logging this again */ 40790792Sgshapiro 40890792Sgshapiro/* is it worth setting the process title for 1s? */ 40990792Sgshapiro#define DELAY_CONN(cmd) \ 41090792Sgshapiro if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \ 41190792Sgshapiro { \ 41290792Sgshapiro time_t dnow; \ 41390792Sgshapiro \ 41490792Sgshapiro sm_setproctitle(true, e, \ 41590792Sgshapiro "%s: %s: delaying %s: load average: %d", \ 41690792Sgshapiro qid_printname(e), CurSmtpClient, \ 41790792Sgshapiro cmd, DelayLA); \ 41890792Sgshapiro if (LogLevel > 8 && (dnow = curtime()) > log_delay) \ 41990792Sgshapiro { \ 42090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 42190792Sgshapiro "delaying=%s, load average=%d >= %d", \ 42290792Sgshapiro cmd, CurrentLA, DelayLA); \ 42390792Sgshapiro log_delay = dnow + MIN_DELAY_LOG; \ 42490792Sgshapiro } \ 42590792Sgshapiro (void) sleep(1); \ 42690792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", \ 42790792Sgshapiro qid_printname(e), CurSmtpClient, inp); \ 42890792Sgshapiro } 42990792Sgshapiro 43090792Sgshapiro 43138032Spetervoid 43264562Sgshapirosmtp(nullserver, d_flags, e) 43364562Sgshapiro char *volatile nullserver; 43464562Sgshapiro BITMAP256 d_flags; 43538032Speter register ENVELOPE *volatile e; 43638032Speter{ 43738032Speter register char *volatile p; 43864562Sgshapiro register struct cmd *volatile c = NULL; 43938032Speter char *cmd; 44038032Speter auto ADDRESS *vrfyqueue; 44138032Speter ADDRESS *a; 44238032Speter volatile bool gothello; /* helo command received */ 44338032Speter bool vrfy; /* set if this is a vrfy command */ 44438032Speter char *volatile protocol; /* sending protocol */ 44538032Speter char *volatile sendinghost; /* sending hostname */ 44638032Speter char *volatile peerhostname; /* name of SMTP peer or "localhost" */ 44738032Speter auto char *delimptr; 44838032Speter char *id; 44990792Sgshapiro volatile unsigned int n_badcmds = 0; /* count of bad commands */ 45090792Sgshapiro volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */ 45190792Sgshapiro volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */ 45290792Sgshapiro volatile unsigned int n_etrn = 0; /* count of ETRN */ 45390792Sgshapiro volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */ 45490792Sgshapiro volatile unsigned int n_helo = 0; /* count of HELO/EHLO */ 455120256Sgshapiro volatile int save_sevenbitinput; 45638032Speter bool ok; 457132943Sgshapiro#if _FFR_BLOCK_PROXIES 45890792Sgshapiro volatile bool first; 459132943Sgshapiro#endif /* _FFR_BLOCK_PROXIES */ 46090792Sgshapiro volatile bool tempfail = false; 46164562Sgshapiro volatile time_t wt; /* timeout after too many commands */ 46264562Sgshapiro volatile time_t previous; /* time after checksmtpattack() */ 46390792Sgshapiro volatile bool lognullconnection = true; 46438032Speter register char *q; 46590792Sgshapiro SMTP_T smtp; 46664562Sgshapiro char *addr; 46764562Sgshapiro char *greetcode = "220"; 46890792Sgshapiro char *hostname; /* my hostname ($j) */ 46938032Speter QUEUE_CHAR *new; 47064562Sgshapiro int argno; 47164562Sgshapiro char *args[MAXSMTPARGS]; 47238032Speter char inp[MAXLINE]; 47338032Speter char cmdbuf[MAXLINE]; 47490792Sgshapiro#if SASL 47564562Sgshapiro sasl_conn_t *conn; 47664562Sgshapiro volatile bool sasl_ok; 47790792Sgshapiro volatile unsigned int n_auth = 0; /* count of AUTH commands */ 47864562Sgshapiro bool ismore; 47964562Sgshapiro int result; 48064562Sgshapiro volatile int authenticating; 48164562Sgshapiro char *user; 48298121Sgshapiro char *in, *out2; 48398121Sgshapiro# if SASL >= 20000 484102528Sgshapiro char *auth_id; 48598121Sgshapiro const char *out; 486102528Sgshapiro sasl_ssf_t ext_ssf; 487120256Sgshapiro char localip[60], remoteip[60]; 48898121Sgshapiro# else /* SASL >= 20000 */ 48998121Sgshapiro char *out; 49064562Sgshapiro const char *errstr; 49198121Sgshapiro sasl_external_properties_t ext_ssf; 492120256Sgshapiro struct sockaddr_in saddr_l; 493120256Sgshapiro struct sockaddr_in saddr_r; 49498121Sgshapiro# endif /* SASL >= 20000 */ 49598121Sgshapiro sasl_security_properties_t ssp; 49698121Sgshapiro sasl_ssf_t *ssf; 49790792Sgshapiro unsigned int inlen, out2len; 49864562Sgshapiro unsigned int outlen; 49964562Sgshapiro char *volatile auth_type; 50064562Sgshapiro char *mechlist; 50190792Sgshapiro volatile unsigned int n_mechs; 50290792Sgshapiro unsigned int len; 50390792Sgshapiro#endif /* SASL */ 504132943Sgshapiro int r; 50590792Sgshapiro#if STARTTLS 506132943Sgshapiro int fdfl; 50766494Sgshapiro int rfd, wfd; 50890792Sgshapiro volatile bool tls_active = false; 509132943Sgshapiro volatile bool smtps = bitnset(D_SMTPS, d_flags); 51064562Sgshapiro bool saveQuickAbort; 51164562Sgshapiro bool saveSuprErrs; 51290792Sgshapiro time_t tlsstart; 51390792Sgshapiro#endif /* STARTTLS */ 51490792Sgshapiro volatile unsigned int features; 51590792Sgshapiro#if PIPELINING 51690792Sgshapiro# if _FFR_NO_PIPE 51790792Sgshapiro int np_log = 0; 51890792Sgshapiro# endif /* _FFR_NO_PIPE */ 51990792Sgshapiro#endif /* PIPELINING */ 52090792Sgshapiro volatile time_t log_delay = (time_t) 0; 52138032Speter 522120256Sgshapiro save_sevenbitinput = SevenBitInput; 52390792Sgshapiro smtp.sm_nrcpts = 0; 52490792Sgshapiro#if MILTER 52590792Sgshapiro smtp.sm_milterize = (nullserver == NULL); 52690792Sgshapiro smtp.sm_milterlist = false; 52790792Sgshapiro#endif /* MILTER */ 52890792Sgshapiro 52990792Sgshapiro /* setup I/O fd correctly for the SMTP server */ 53090792Sgshapiro setup_smtpd_io(); 53190792Sgshapiro 53290792Sgshapiro#if SM_HEAP_CHECK 53390792Sgshapiro if (sm_debug_active(&DebugLeakSmtp, 1)) 53438032Speter { 53590792Sgshapiro sm_heap_newgroup(); 53690792Sgshapiro sm_dprintf("smtp() heap group #%d\n", sm_heap_group()); 53738032Speter } 53890792Sgshapiro#endif /* SM_HEAP_CHECK */ 53964562Sgshapiro 54090792Sgshapiro /* XXX the rpool should be set when e is initialized in main() */ 54190792Sgshapiro e->e_rpool = sm_rpool_new_x(NULL); 54290792Sgshapiro e->e_macro.mac_rpool = e->e_rpool; 54390792Sgshapiro 54438032Speter settime(e); 54590792Sgshapiro sm_getla(); 54638032Speter peerhostname = RealHostName; 54738032Speter if (peerhostname == NULL) 54838032Speter peerhostname = "localhost"; 54938032Speter CurHostName = peerhostname; 55038032Speter CurSmtpClient = macvalue('_', e); 55138032Speter if (CurSmtpClient == NULL) 55238032Speter CurSmtpClient = CurHostName; 55338032Speter 55438032Speter /* check_relay may have set discard bit, save for later */ 55590792Sgshapiro smtp.sm_discard = bitset(EF_DISCARD, e->e_flags); 55638032Speter 55790792Sgshapiro#if PIPELINING 55890792Sgshapiro /* auto-flush output when reading input */ 55990792Sgshapiro (void) sm_io_autoflush(InChannel, OutChannel); 56090792Sgshapiro#endif /* PIPELINING */ 56164562Sgshapiro 56290792Sgshapiro sm_setproctitle(true, e, "server %s startup", CurSmtpClient); 56390792Sgshapiro 56490792Sgshapiro /* Set default features for server. */ 56590792Sgshapiro features = ((bitset(PRIV_NOETRN, PrivacyFlags) || 56690792Sgshapiro bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN) 56790792Sgshapiro | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE) 56890792Sgshapiro | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE 56990792Sgshapiro : (SRV_OFFER_EXPN 57090792Sgshapiro | (bitset(PRIV_NOVERB, PrivacyFlags) 57190792Sgshapiro ? SRV_NONE : SRV_OFFER_VERB))) 57290792Sgshapiro | (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE 57390792Sgshapiro : SRV_OFFER_DSN) 57490792Sgshapiro#if SASL 57590792Sgshapiro | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH) 576132943Sgshapiro | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC 577132943Sgshapiro : SRV_NONE) 57890792Sgshapiro#endif /* SASL */ 57990792Sgshapiro#if PIPELINING 58090792Sgshapiro | SRV_OFFER_PIPE 58190792Sgshapiro#endif /* PIPELINING */ 58290792Sgshapiro#if STARTTLS 58390792Sgshapiro | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS) 58490792Sgshapiro | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE 58590792Sgshapiro : SRV_VRFY_CLT) 58690792Sgshapiro#endif /* STARTTLS */ 58790792Sgshapiro ; 58890792Sgshapiro if (nullserver == NULL) 58990792Sgshapiro { 59090792Sgshapiro features = srvfeatures(e, CurSmtpClient, features); 59190792Sgshapiro if (bitset(SRV_TMP_FAIL, features)) 59290792Sgshapiro { 59390792Sgshapiro if (LogLevel > 4) 59490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 59590792Sgshapiro "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled", 59690792Sgshapiro CurSmtpClient); 59790792Sgshapiro nullserver = "450 4.3.0 Please try again later."; 59890792Sgshapiro } 599132943Sgshapiro else 600132943Sgshapiro { 60190792Sgshapiro#if PIPELINING 60290792Sgshapiro# if _FFR_NO_PIPE 603132943Sgshapiro if (bitset(SRV_NO_PIPE, features)) 604132943Sgshapiro { 605132943Sgshapiro /* for consistency */ 606132943Sgshapiro features &= ~SRV_OFFER_PIPE; 607132943Sgshapiro } 60890792Sgshapiro# endif /* _FFR_NO_PIPE */ 60990792Sgshapiro#endif /* PIPELINING */ 610132943Sgshapiro#if SASL 611132943Sgshapiro if (bitset(SRV_REQ_SEC, features)) 612132943Sgshapiro SASLOpts |= SASL_SEC_NOPLAINTEXT; 613132943Sgshapiro else 614132943Sgshapiro SASLOpts &= ~SASL_SEC_NOPLAINTEXT; 615132943Sgshapiro#endif /* SASL */ 616132943Sgshapiro } 61790792Sgshapiro } 618132943Sgshapiro else if (strncmp(nullserver, "421 ", 4) == 0) 619132943Sgshapiro { 620132943Sgshapiro message(nullserver); 621132943Sgshapiro goto doquit; 622132943Sgshapiro } 62390792Sgshapiro 62490792Sgshapiro hostname = macvalue('j', e); 62590792Sgshapiro#if SASL 626132943Sgshapiro if (AuthRealm == NULL) 627132943Sgshapiro AuthRealm = hostname; 62890792Sgshapiro sasl_ok = bitset(SRV_OFFER_AUTH, features); 62964562Sgshapiro n_mechs = 0; 63090792Sgshapiro authenticating = SASL_NOT_AUTH; 63164562Sgshapiro 63264562Sgshapiro /* SASL server new connection */ 63390792Sgshapiro if (sasl_ok) 63438032Speter { 63598121Sgshapiro# if SASL >= 20000 636132943Sgshapiro result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL, 63798121Sgshapiro NULL, 0, &conn); 63898121Sgshapiro# elif SASL > 10505 63990792Sgshapiro /* use empty realm: only works in SASL > 1.5.5 */ 640132943Sgshapiro result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn); 64198121Sgshapiro# else /* SASL >= 20000 */ 64290792Sgshapiro /* use no realm -> realm is set to hostname by SASL lib */ 643132943Sgshapiro result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0, 64490792Sgshapiro &conn); 64598121Sgshapiro# endif /* SASL >= 20000 */ 64690792Sgshapiro sasl_ok = result == SASL_OK; 64790792Sgshapiro if (!sasl_ok) 64890792Sgshapiro { 64990792Sgshapiro if (LogLevel > 9) 65090792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 65190792Sgshapiro "AUTH error: sasl_server_new failed=%d", 65290792Sgshapiro result); 65390792Sgshapiro } 65490792Sgshapiro } 65590792Sgshapiro if (sasl_ok) 65690792Sgshapiro { 65764562Sgshapiro /* 65864562Sgshapiro ** SASL set properties for sasl 65964562Sgshapiro ** set local/remote IP 66098121Sgshapiro ** XXX Cyrus SASL v1 only supports IPv4 66164562Sgshapiro ** 66264562Sgshapiro ** XXX where exactly are these used/required? 66364562Sgshapiro ** Kerberos_v4 66464562Sgshapiro */ 66564562Sgshapiro 66698121Sgshapiro# if SASL >= 20000 66798121Sgshapiro# if NETINET || NETINET6 66890792Sgshapiro in = macvalue(macid("{daemon_family}"), e); 66998121Sgshapiro if (in != NULL && ( 67098121Sgshapiro# if NETINET6 67198121Sgshapiro strcmp(in, "inet6") == 0 || 67298121Sgshapiro# endif /* NETINET6 */ 67398121Sgshapiro strcmp(in, "inet") == 0)) 67498121Sgshapiro { 67598121Sgshapiro SOCKADDR_LEN_T addrsize; 67698121Sgshapiro SOCKADDR saddr_l; 67798121Sgshapiro SOCKADDR saddr_r; 67898121Sgshapiro 67998121Sgshapiro addrsize = sizeof(saddr_r); 68098121Sgshapiro if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 68198121Sgshapiro NULL), 68298121Sgshapiro (struct sockaddr *) &saddr_r, 68398121Sgshapiro &addrsize) == 0) 68498121Sgshapiro { 68598121Sgshapiro if (iptostring(&saddr_r, addrsize, 68698121Sgshapiro remoteip, sizeof remoteip)) 68798121Sgshapiro { 68898121Sgshapiro sasl_setprop(conn, SASL_IPREMOTEPORT, 68998121Sgshapiro remoteip); 69098121Sgshapiro } 69198121Sgshapiro addrsize = sizeof(saddr_l); 69298121Sgshapiro if (getsockname(sm_io_getinfo(InChannel, 69398121Sgshapiro SM_IO_WHAT_FD, 69498121Sgshapiro NULL), 69598121Sgshapiro (struct sockaddr *) &saddr_l, 69698121Sgshapiro &addrsize) == 0) 69798121Sgshapiro { 69898121Sgshapiro if (iptostring(&saddr_l, addrsize, 69998121Sgshapiro localip, 70098121Sgshapiro sizeof localip)) 70198121Sgshapiro { 70298121Sgshapiro sasl_setprop(conn, 70398121Sgshapiro SASL_IPLOCALPORT, 70498121Sgshapiro localip); 70598121Sgshapiro } 70698121Sgshapiro } 70798121Sgshapiro } 70898121Sgshapiro } 70998121Sgshapiro# endif /* NETINET || NETINET6 */ 71098121Sgshapiro# else /* SASL >= 20000 */ 71198121Sgshapiro# if NETINET 71298121Sgshapiro in = macvalue(macid("{daemon_family}"), e); 71364562Sgshapiro if (in != NULL && strcmp(in, "inet") == 0) 71464562Sgshapiro { 71564562Sgshapiro SOCKADDR_LEN_T addrsize; 71664562Sgshapiro 71764562Sgshapiro addrsize = sizeof(struct sockaddr_in); 71890792Sgshapiro if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 71990792Sgshapiro NULL), 72064562Sgshapiro (struct sockaddr *)&saddr_r, 72164562Sgshapiro &addrsize) == 0) 72264562Sgshapiro { 72364562Sgshapiro sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r); 72464562Sgshapiro addrsize = sizeof(struct sockaddr_in); 72590792Sgshapiro if (getsockname(sm_io_getinfo(InChannel, 72690792Sgshapiro SM_IO_WHAT_FD, 72790792Sgshapiro NULL), 72864562Sgshapiro (struct sockaddr *)&saddr_l, 72964562Sgshapiro &addrsize) == 0) 73064562Sgshapiro sasl_setprop(conn, SASL_IP_LOCAL, 73164562Sgshapiro &saddr_l); 73264562Sgshapiro } 73364562Sgshapiro } 73498121Sgshapiro# endif /* NETINET */ 73598121Sgshapiro# endif /* SASL >= 20000 */ 73664562Sgshapiro 73764562Sgshapiro auth_type = NULL; 73864562Sgshapiro mechlist = NULL; 73964562Sgshapiro user = NULL; 74090792Sgshapiro# if 0 74190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 74290792Sgshapiro macid("{auth_author}"), NULL); 74390792Sgshapiro# endif /* 0 */ 74464562Sgshapiro 74564562Sgshapiro /* set properties */ 74664562Sgshapiro (void) memset(&ssp, '\0', sizeof ssp); 74790792Sgshapiro 74864562Sgshapiro /* XXX should these be options settable via .cf ? */ 74964562Sgshapiro /* ssp.min_ssf = 0; is default due to memset() */ 75094334Sgshapiro# if STARTTLS 75194334Sgshapiro# endif /* STARTTLS */ 75264562Sgshapiro { 75390792Sgshapiro ssp.max_ssf = MaxSLBits; 75464562Sgshapiro ssp.maxbufsize = MAXOUTLEN; 75564562Sgshapiro } 75664562Sgshapiro ssp.security_flags = SASLOpts & SASL_SEC_MASK; 75764562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK; 75864562Sgshapiro 75964562Sgshapiro if (sasl_ok) 76064562Sgshapiro { 76164562Sgshapiro /* 76264562Sgshapiro ** external security strength factor; 76390792Sgshapiro ** currently we have none so zero 76464562Sgshapiro */ 76590792Sgshapiro 76698121Sgshapiro# if SASL >= 20000 76798121Sgshapiro ext_ssf = 0; 76898121Sgshapiro auth_id = NULL; 76998121Sgshapiro sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL, 77098121Sgshapiro &ext_ssf) == SASL_OK) && 77198121Sgshapiro (sasl_setprop(conn, SASL_AUTH_EXTERNAL, 772102528Sgshapiro auth_id) == SASL_OK)); 77398121Sgshapiro# else /* SASL >= 20000 */ 77464562Sgshapiro ext_ssf.ssf = 0; 77564562Sgshapiro ext_ssf.auth_id = NULL; 77664562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, 77764562Sgshapiro &ext_ssf) == SASL_OK; 77898121Sgshapiro# endif /* SASL >= 20000 */ 77964562Sgshapiro } 78064562Sgshapiro if (sasl_ok) 78164562Sgshapiro n_mechs = saslmechs(conn, &mechlist); 78238032Speter } 78390792Sgshapiro#endif /* SASL */ 78438032Speter 785120256Sgshapiro#if STARTTLS 786120256Sgshapiro#endif /* STARTTLS */ 787120256Sgshapiro 78890792Sgshapiro#if MILTER 78990792Sgshapiro if (smtp.sm_milterize) 79064562Sgshapiro { 79164562Sgshapiro char state; 79264562Sgshapiro 79364562Sgshapiro /* initialize mail filter connection */ 79490792Sgshapiro smtp.sm_milterlist = milter_init(e, &state); 79564562Sgshapiro switch (state) 79664562Sgshapiro { 79764562Sgshapiro case SMFIR_REJECT: 79890792Sgshapiro if (MilterLogLevel > 3) 79990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 80094334Sgshapiro "Milter: initialization failed, rejecting commands"); 80164562Sgshapiro greetcode = "554"; 80264562Sgshapiro nullserver = "Command rejected"; 80390792Sgshapiro smtp.sm_milterize = false; 80464562Sgshapiro break; 80564562Sgshapiro 80664562Sgshapiro case SMFIR_TEMPFAIL: 80790792Sgshapiro if (MilterLogLevel > 3) 80890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 80994334Sgshapiro "Milter: initialization failed, temp failing commands"); 81090792Sgshapiro tempfail = true; 81190792Sgshapiro smtp.sm_milterize = false; 81264562Sgshapiro break; 81364562Sgshapiro } 81464562Sgshapiro } 81564562Sgshapiro 81690792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 81790792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 81864562Sgshapiro { 81964562Sgshapiro char state; 82090792Sgshapiro char *response; 82164562Sgshapiro 82290792Sgshapiro response = milter_connect(peerhostname, RealHostAddr, 82390792Sgshapiro e, &state); 82464562Sgshapiro switch (state) 82564562Sgshapiro { 82664562Sgshapiro case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */ 82764562Sgshapiro case SMFIR_REJECT: 82890792Sgshapiro if (MilterLogLevel > 3) 82990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 83090792Sgshapiro "Milter: connect: host=%s, addr=%s, rejecting commands", 83190792Sgshapiro peerhostname, 83290792Sgshapiro anynet_ntoa(&RealHostAddr)); 83364562Sgshapiro greetcode = "554"; 83464562Sgshapiro nullserver = "Command rejected"; 83590792Sgshapiro smtp.sm_milterize = false; 83664562Sgshapiro break; 83764562Sgshapiro 83864562Sgshapiro case SMFIR_TEMPFAIL: 83990792Sgshapiro if (MilterLogLevel > 3) 84090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 84190792Sgshapiro "Milter: connect: host=%s, addr=%s, temp failing commands", 84290792Sgshapiro peerhostname, 84390792Sgshapiro anynet_ntoa(&RealHostAddr)); 84490792Sgshapiro tempfail = true; 84590792Sgshapiro smtp.sm_milterize = false; 84664562Sgshapiro break; 847110560Sgshapiro 848110560Sgshapiro case SMFIR_SHUTDOWN: 849110560Sgshapiro if (MilterLogLevel > 3) 850110560Sgshapiro sm_syslog(LOG_INFO, e->e_id, 851110560Sgshapiro "Milter: connect: host=%s, addr=%s, shutdown", 852110560Sgshapiro peerhostname, 853110560Sgshapiro anynet_ntoa(&RealHostAddr)); 854110560Sgshapiro tempfail = true; 855110560Sgshapiro smtp.sm_milterize = false; 856110560Sgshapiro message("421 4.7.0 %s closing connection", 857110560Sgshapiro MyHostName); 858110560Sgshapiro 859110560Sgshapiro /* arrange to ignore send list */ 860110560Sgshapiro e->e_sendqueue = NULL; 861110560Sgshapiro goto doquit; 86264562Sgshapiro } 86390792Sgshapiro if (response != NULL) 86490792Sgshapiro sm_free(response); /* XXX */ 86564562Sgshapiro } 86690792Sgshapiro#endif /* MILTER */ 86764562Sgshapiro 868132943Sgshapiro /* 869132943Sgshapiro ** Broken proxies and SMTP slammers 870132943Sgshapiro ** push data without waiting, catch them 871132943Sgshapiro */ 872132943Sgshapiro 873132943Sgshapiro if ( 87490792Sgshapiro#if STARTTLS 875132943Sgshapiro !smtps && 876132943Sgshapiro#endif /* STARTTLS */ 877132943Sgshapiro *greetcode == '2') 878132943Sgshapiro { 879132943Sgshapiro time_t msecs = 0; 880132943Sgshapiro char **pvp; 881132943Sgshapiro char pvpbuf[PSBUFSIZE]; 882132943Sgshapiro 883132943Sgshapiro /* Ask the rulesets how long to pause */ 884132943Sgshapiro pvp = NULL; 885132943Sgshapiro r = rscap("greet_pause", peerhostname, 886132943Sgshapiro anynet_ntoa(&RealHostAddr), e, 887132943Sgshapiro &pvp, pvpbuf, sizeof(pvpbuf)); 888132943Sgshapiro if (r == EX_OK && pvp != NULL && pvp[0] != NULL && 889132943Sgshapiro (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL) 890132943Sgshapiro { 891132943Sgshapiro msecs = strtol(pvp[1], NULL, 10); 892132943Sgshapiro } 893132943Sgshapiro 894132943Sgshapiro if (msecs > 0) 895132943Sgshapiro { 896132943Sgshapiro int fd; 897132943Sgshapiro fd_set readfds; 898132943Sgshapiro struct timeval timeout; 899132943Sgshapiro 900132943Sgshapiro /* pause for a moment */ 901132943Sgshapiro timeout.tv_sec = msecs / 1000; 902132943Sgshapiro timeout.tv_usec = (msecs % 1000) * 1000; 903132943Sgshapiro 904132943Sgshapiro /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */ 905132943Sgshapiro if (timeout.tv_sec >= 300) 906132943Sgshapiro { 907132943Sgshapiro timeout.tv_sec = 300; 908132943Sgshapiro timeout.tv_usec = 0; 909132943Sgshapiro } 910132943Sgshapiro 911132943Sgshapiro /* check if data is on the socket during the pause */ 912132943Sgshapiro fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 913132943Sgshapiro FD_ZERO(&readfds); 914132943Sgshapiro SM_FD_SET(fd, &readfds); 915132943Sgshapiro if (select(fd + 1, FDSET_CAST &readfds, 916132943Sgshapiro NULL, NULL, &timeout) > 0 && 917132943Sgshapiro FD_ISSET(fd, &readfds)) 918132943Sgshapiro { 919132943Sgshapiro greetcode = "554"; 920132943Sgshapiro nullserver = "Command rejected"; 921132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 922132943Sgshapiro "rejecting commands from %s [%s] due to pre-greeting traffic", 923132943Sgshapiro peerhostname, 924132943Sgshapiro anynet_ntoa(&RealHostAddr)); 925132943Sgshapiro } 926132943Sgshapiro } 927132943Sgshapiro } 928132943Sgshapiro 929132943Sgshapiro#if STARTTLS 93090792Sgshapiro /* If this an smtps connection, start TLS now */ 93190792Sgshapiro if (smtps) 932120256Sgshapiro { 933120256Sgshapiro Errors = 0; 93490792Sgshapiro goto starttls; 935120256Sgshapiro } 93690792Sgshapiro 93790792Sgshapiro greeting: 93890792Sgshapiro 93990792Sgshapiro#endif /* STARTTLS */ 94090792Sgshapiro 94138032Speter /* output the first line, inserting "ESMTP" as second word */ 94290792Sgshapiro if (*greetcode == '5') 94390792Sgshapiro (void) sm_snprintf(inp, sizeof inp, "%s not accepting messages", 94490792Sgshapiro hostname); 94590792Sgshapiro else 94690792Sgshapiro expand(SmtpGreeting, inp, sizeof inp, e); 94790792Sgshapiro 94838032Speter p = strchr(inp, '\n'); 94938032Speter if (p != NULL) 95038032Speter *p++ = '\0'; 95138032Speter id = strchr(inp, ' '); 95238032Speter if (id == NULL) 95338032Speter id = &inp[strlen(inp)]; 95464562Sgshapiro if (p == NULL) 95590792Sgshapiro (void) sm_snprintf(cmdbuf, sizeof cmdbuf, 95664562Sgshapiro "%s %%.*s ESMTP%%s", greetcode); 95764562Sgshapiro else 95890792Sgshapiro (void) sm_snprintf(cmdbuf, sizeof cmdbuf, 95964562Sgshapiro "%s-%%.*s ESMTP%%s", greetcode); 96066494Sgshapiro message(cmdbuf, (int) (id - inp), inp, id); 96138032Speter 96238032Speter /* output remaining lines */ 96338032Speter while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 96438032Speter { 96538032Speter *p++ = '\0'; 96638032Speter if (isascii(*id) && isspace(*id)) 96738032Speter id++; 96890792Sgshapiro (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s"); 96964562Sgshapiro message(cmdbuf, id); 97038032Speter } 97138032Speter if (id != NULL) 97238032Speter { 97338032Speter if (isascii(*id) && isspace(*id)) 97438032Speter id++; 97590792Sgshapiro (void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s"); 97664562Sgshapiro message(cmdbuf, id); 97738032Speter } 97838032Speter 97938032Speter protocol = NULL; 98038032Speter sendinghost = macvalue('s', e); 98190792Sgshapiro 98290792Sgshapiro /* If quarantining by a connect/ehlo action, save between messages */ 98390792Sgshapiro if (e->e_quarmsg == NULL) 98490792Sgshapiro smtp.sm_quarmsg = NULL; 98590792Sgshapiro else 98690792Sgshapiro smtp.sm_quarmsg = newstr(e->e_quarmsg); 98790792Sgshapiro 98890792Sgshapiro /* sendinghost's storage must outlive the current envelope */ 98990792Sgshapiro if (sendinghost != NULL) 99090792Sgshapiro sendinghost = sm_strdup_x(sendinghost); 991132943Sgshapiro#if _FFR_BLOCK_PROXIES 99290792Sgshapiro first = true; 993132943Sgshapiro#endif /* _FFR_BLOCK_PROXIES */ 99490792Sgshapiro gothello = false; 99590792Sgshapiro smtp.sm_gotmail = false; 99638032Speter for (;;) 99738032Speter { 99890792Sgshapiro SM_TRY 99990792Sgshapiro { 100090792Sgshapiro QuickAbort = false; 100190792Sgshapiro HoldErrs = false; 100290792Sgshapiro SuprErrs = false; 100390792Sgshapiro LogUsrErrs = false; 100490792Sgshapiro OnlyOneError = true; 100538032Speter e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 100638032Speter 100738032Speter /* setup for the read */ 100838032Speter e->e_to = NULL; 100938032Speter Errors = 0; 101064562Sgshapiro FileName = NULL; 101190792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 101238032Speter 101338032Speter /* read the input line */ 101438032Speter SmtpPhase = "server cmd read"; 101590792Sgshapiro sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient); 101690792Sgshapiro#if SASL 101764562Sgshapiro /* 101890792Sgshapiro ** XXX SMTP AUTH requires accepting any length, 101990792Sgshapiro ** at least for challenge/response 102064562Sgshapiro */ 102190792Sgshapiro#endif /* SASL */ 102238032Speter 102338032Speter /* handle errors */ 102490792Sgshapiro if (sm_io_error(OutChannel) || 102564562Sgshapiro (p = sfgets(inp, sizeof inp, InChannel, 102664562Sgshapiro TimeOuts.to_nextcommand, SmtpPhase)) == NULL) 102738032Speter { 102864562Sgshapiro char *d; 102964562Sgshapiro 103090792Sgshapiro d = macvalue(macid("{daemon_name}"), e); 103164562Sgshapiro if (d == NULL) 103264562Sgshapiro d = "stdin"; 103338032Speter /* end of file, just die */ 103438032Speter disconnect(1, e); 103564562Sgshapiro 103690792Sgshapiro#if MILTER 103764562Sgshapiro /* close out milter filters */ 103864562Sgshapiro milter_quit(e); 103990792Sgshapiro#endif /* MILTER */ 104064562Sgshapiro 104164562Sgshapiro message("421 4.4.1 %s Lost input channel from %s", 104238032Speter MyHostName, CurSmtpClient); 104390792Sgshapiro if (LogLevel > (smtp.sm_gotmail ? 1 : 19)) 104438032Speter sm_syslog(LOG_NOTICE, e->e_id, 1045110560Sgshapiro "lost input channel from %s to %s after %s", 104664562Sgshapiro CurSmtpClient, d, 104764562Sgshapiro (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name); 104838032Speter /* 104942575Speter ** If have not accepted mail (DATA), do not bounce 105042575Speter ** bad addresses back to sender. 105138032Speter */ 105264562Sgshapiro 105338032Speter if (bitset(EF_CLRQUEUE, e->e_flags)) 105438032Speter e->e_sendqueue = NULL; 105564562Sgshapiro goto doquit; 105638032Speter } 105738032Speter 1058132943Sgshapiro#if _FFR_BLOCK_PROXIES 105990792Sgshapiro if (first) 106090792Sgshapiro { 1061110560Sgshapiro size_t inplen, cmdlen; 1062110560Sgshapiro int idx; 1063110560Sgshapiro char *http_cmd; 1064110560Sgshapiro static char *http_cmds[] = { "GET", "POST", 1065110560Sgshapiro "CONNECT", "USER", NULL }; 1066110560Sgshapiro 1067110560Sgshapiro inplen = strlen(inp); 1068110560Sgshapiro for (idx = 0; (http_cmd = http_cmds[idx]) != NULL; 1069110560Sgshapiro idx++) 1070110560Sgshapiro { 1071110560Sgshapiro cmdlen = strlen(http_cmd); 1072110560Sgshapiro if (cmdlen < inplen && 1073110560Sgshapiro sm_strncasecmp(inp, http_cmd, cmdlen) == 0 && 1074110560Sgshapiro isascii(inp[cmdlen]) && isspace(inp[cmdlen])) 1075110560Sgshapiro { 1076110560Sgshapiro /* Open proxy, drop it */ 1077110560Sgshapiro message("421 4.7.0 %s Rejecting open proxy %s", 1078110560Sgshapiro MyHostName, CurSmtpClient); 1079110560Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1080110560Sgshapiro "%s: probable open proxy: command=%.40s", 1081110560Sgshapiro CurSmtpClient, inp); 1082110560Sgshapiro goto doquit; 1083110560Sgshapiro } 1084110560Sgshapiro } 108590792Sgshapiro first = false; 108690792Sgshapiro } 1087132943Sgshapiro#endif /* _FFR_BLOCK_PROXIES */ 108890792Sgshapiro 108938032Speter /* clean up end of line */ 109090792Sgshapiro fixcrlf(inp, true); 109138032Speter 109290792Sgshapiro#if PIPELINING 109390792Sgshapiro# if _FFR_NO_PIPE 109490792Sgshapiro /* 109590792Sgshapiro ** if there is more input and pipelining is disabled: 109690792Sgshapiro ** delay ... (and maybe discard the input?) 109790792Sgshapiro ** XXX this doesn't really work, at least in tests using 109890792Sgshapiro ** telnet SM_IO_IS_READABLE only returns 1 if there were 109990792Sgshapiro ** more than 2 input lines available. 110090792Sgshapiro */ 110190792Sgshapiro 110290792Sgshapiro if (bitset(SRV_NO_PIPE, features) && 1103110560Sgshapiro sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0) 110490792Sgshapiro { 110590792Sgshapiro if (++np_log < 3) 110690792Sgshapiro sm_syslog(LOG_INFO, NOQID, 110790792Sgshapiro "unauthorized PIPELINING, sleeping"); 110890792Sgshapiro sleep(1); 110990792Sgshapiro } 111090792Sgshapiro 111190792Sgshapiro# endif /* _FFR_NO_PIPE */ 111290792Sgshapiro#endif /* PIPELINING */ 111390792Sgshapiro 111490792Sgshapiro#if SASL 111564562Sgshapiro if (authenticating == SASL_PROC_AUTH) 111664562Sgshapiro { 111790792Sgshapiro# if 0 111864562Sgshapiro if (*inp == '\0') 111964562Sgshapiro { 112064562Sgshapiro authenticating = SASL_NOT_AUTH; 112164562Sgshapiro message("501 5.5.2 missing input"); 1122120256Sgshapiro RESET_SASLCONN; 112364562Sgshapiro continue; 112464562Sgshapiro } 112590792Sgshapiro# endif /* 0 */ 112664562Sgshapiro if (*inp == '*' && *(inp + 1) == '\0') 112764562Sgshapiro { 112864562Sgshapiro authenticating = SASL_NOT_AUTH; 112964562Sgshapiro 113064562Sgshapiro /* rfc 2254 4. */ 113164562Sgshapiro message("501 5.0.0 AUTH aborted"); 1132120256Sgshapiro RESET_SASLCONN; 113364562Sgshapiro continue; 113464562Sgshapiro } 113564562Sgshapiro 113664562Sgshapiro /* could this be shorter? XXX */ 113798121Sgshapiro# if SASL >= 20000 113898121Sgshapiro in = xalloc(strlen(inp) + 1); 113998121Sgshapiro result = sasl_decode64(inp, strlen(inp), in, 114098121Sgshapiro strlen(inp), &inlen); 114198121Sgshapiro# else /* SASL >= 20000 */ 114264562Sgshapiro out = xalloc(strlen(inp)); 114364562Sgshapiro result = sasl_decode64(inp, strlen(inp), out, &outlen); 114498121Sgshapiro# endif /* SASL >= 20000 */ 114564562Sgshapiro if (result != SASL_OK) 114664562Sgshapiro { 114764562Sgshapiro authenticating = SASL_NOT_AUTH; 114864562Sgshapiro 114964562Sgshapiro /* rfc 2254 4. */ 115064562Sgshapiro message("501 5.5.4 cannot decode AUTH parameter %s", 115164562Sgshapiro inp); 115298121Sgshapiro# if SASL >= 20000 115398121Sgshapiro sm_free(in); 115498121Sgshapiro# endif /* SASL >= 20000 */ 1155120256Sgshapiro RESET_SASLCONN; 115664562Sgshapiro continue; 115764562Sgshapiro } 115864562Sgshapiro 115998121Sgshapiro# if SASL >= 20000 116098121Sgshapiro result = sasl_server_step(conn, in, inlen, 116198121Sgshapiro &out, &outlen); 116298121Sgshapiro sm_free(in); 116398121Sgshapiro# else /* SASL >= 20000 */ 116464562Sgshapiro result = sasl_server_step(conn, out, outlen, 116564562Sgshapiro &out, &outlen, &errstr); 116698121Sgshapiro# endif /* SASL >= 20000 */ 116764562Sgshapiro 116864562Sgshapiro /* get an OK if we're done */ 116964562Sgshapiro if (result == SASL_OK) 117064562Sgshapiro { 117164562Sgshapiro authenticated: 117264562Sgshapiro message("235 2.0.0 OK Authenticated"); 117364562Sgshapiro authenticating = SASL_IS_AUTH; 117490792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 117590792Sgshapiro macid("{auth_type}"), auth_type); 117664562Sgshapiro 117798121Sgshapiro# if SASL >= 20000 117898121Sgshapiro user = macvalue(macid("{auth_authen}"), e); 117998121Sgshapiro 118098121Sgshapiro /* get security strength (features) */ 118198121Sgshapiro result = sasl_getprop(conn, SASL_SSF, 118298121Sgshapiro (const void **) &ssf); 118398121Sgshapiro# else /* SASL >= 20000 */ 118464562Sgshapiro result = sasl_getprop(conn, SASL_USERNAME, 118564562Sgshapiro (void **)&user); 118664562Sgshapiro if (result != SASL_OK) 118764562Sgshapiro { 118864562Sgshapiro user = ""; 118990792Sgshapiro macdefine(&BlankEnvelope.e_macro, 119090792Sgshapiro A_PERM, 119190792Sgshapiro macid("{auth_authen}"), NULL); 119264562Sgshapiro } 119364562Sgshapiro else 119464562Sgshapiro { 119590792Sgshapiro macdefine(&BlankEnvelope.e_macro, 119690792Sgshapiro A_TEMP, 1197132943Sgshapiro macid("{auth_authen}"), 1198132943Sgshapiro xtextify(user, "<>\")")); 119964562Sgshapiro } 120064562Sgshapiro 120190792Sgshapiro# if 0 120264562Sgshapiro /* get realm? */ 120364562Sgshapiro sasl_getprop(conn, SASL_REALM, (void **) &data); 120490792Sgshapiro# endif /* 0 */ 120564562Sgshapiro 120664562Sgshapiro /* get security strength (features) */ 120764562Sgshapiro result = sasl_getprop(conn, SASL_SSF, 120864562Sgshapiro (void **) &ssf); 120998121Sgshapiro# endif /* SASL >= 20000 */ 121064562Sgshapiro if (result != SASL_OK) 121164562Sgshapiro { 121290792Sgshapiro macdefine(&BlankEnvelope.e_macro, 121390792Sgshapiro A_PERM, 121490792Sgshapiro macid("{auth_ssf}"), "0"); 121564562Sgshapiro ssf = NULL; 121664562Sgshapiro } 121764562Sgshapiro else 121864562Sgshapiro { 121964562Sgshapiro char pbuf[8]; 122064562Sgshapiro 122190792Sgshapiro (void) sm_snprintf(pbuf, sizeof pbuf, 122290792Sgshapiro "%u", *ssf); 122390792Sgshapiro macdefine(&BlankEnvelope.e_macro, 122490792Sgshapiro A_TEMP, 122590792Sgshapiro macid("{auth_ssf}"), pbuf); 122664562Sgshapiro if (tTd(95, 8)) 122790792Sgshapiro sm_dprintf("AUTH auth_ssf: %u\n", 122890792Sgshapiro *ssf); 122964562Sgshapiro } 123090792Sgshapiro 123164562Sgshapiro /* 123290792Sgshapiro ** Only switch to encrypted connection 123364562Sgshapiro ** if a security layer has been negotiated 123464562Sgshapiro */ 123590792Sgshapiro 123664562Sgshapiro if (ssf != NULL && *ssf > 0) 123764562Sgshapiro { 123864562Sgshapiro /* 123990792Sgshapiro ** Convert I/O layer to use SASL. 124090792Sgshapiro ** If the call fails, the connection 124190792Sgshapiro ** is aborted. 124264562Sgshapiro */ 124390792Sgshapiro 124490792Sgshapiro if (sfdcsasl(&InChannel, &OutChannel, 124590792Sgshapiro conn) == 0) 124664562Sgshapiro { 124764562Sgshapiro /* restart dialogue */ 124864562Sgshapiro n_helo = 0; 124994334Sgshapiro# if PIPELINING 125090792Sgshapiro (void) sm_io_autoflush(InChannel, 125190792Sgshapiro OutChannel); 125294334Sgshapiro# endif /* PIPELINING */ 125364562Sgshapiro } 125464562Sgshapiro else 125564562Sgshapiro syserr("503 5.3.3 SASL TLS failed"); 125664562Sgshapiro } 125790792Sgshapiro 125890792Sgshapiro /* NULL pointer ok since it's our function */ 125990792Sgshapiro if (LogLevel > 8) 126064562Sgshapiro sm_syslog(LOG_INFO, NOQID, 1261110560Sgshapiro "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d", 126290792Sgshapiro CurSmtpClient, 126390792Sgshapiro shortenstring(user, 128), 126490792Sgshapiro auth_type, *ssf); 126564562Sgshapiro } 126664562Sgshapiro else if (result == SASL_CONTINUE) 126764562Sgshapiro { 126864562Sgshapiro len = ENC64LEN(outlen); 126964562Sgshapiro out2 = xalloc(len); 127064562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 127190792Sgshapiro &out2len); 127264562Sgshapiro if (result != SASL_OK) 127364562Sgshapiro { 127464562Sgshapiro /* correct code? XXX */ 127564562Sgshapiro /* 454 Temp. authentication failure */ 127664562Sgshapiro message("454 4.5.4 Internal error: unable to encode64"); 127764562Sgshapiro if (LogLevel > 5) 127864562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 127990792Sgshapiro "AUTH encode64 error [%d for \"%s\"]", 128064562Sgshapiro result, out); 128164562Sgshapiro /* start over? */ 128264562Sgshapiro authenticating = SASL_NOT_AUTH; 128364562Sgshapiro } 128464562Sgshapiro else 128564562Sgshapiro { 128664562Sgshapiro message("334 %s", out2); 128764562Sgshapiro if (tTd(95, 2)) 128890792Sgshapiro sm_dprintf("AUTH continue: msg='%s' len=%u\n", 128990792Sgshapiro out2, out2len); 129064562Sgshapiro } 129198121Sgshapiro# if SASL >= 20000 129298121Sgshapiro sm_free(out2); 129398121Sgshapiro# endif /* SASL >= 20000 */ 129464562Sgshapiro } 129564562Sgshapiro else 129664562Sgshapiro { 129764562Sgshapiro /* not SASL_OK or SASL_CONT */ 129898121Sgshapiro message("535 5.7.0 authentication failed"); 129964562Sgshapiro if (LogLevel > 9) 130064562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 130190792Sgshapiro "AUTH failure (%s): %s (%d) %s", 130264562Sgshapiro auth_type, 130364562Sgshapiro sasl_errstring(result, NULL, 130464562Sgshapiro NULL), 130590792Sgshapiro result, 130698121Sgshapiro# if SASL >= 20000 130798121Sgshapiro sasl_errdetail(conn)); 130898121Sgshapiro# else /* SASL >= 20000 */ 130990792Sgshapiro errstr == NULL ? "" : errstr); 131098121Sgshapiro# endif /* SASL >= 20000 */ 1311120256Sgshapiro RESET_SASLCONN; 131264562Sgshapiro authenticating = SASL_NOT_AUTH; 131364562Sgshapiro } 131464562Sgshapiro } 131564562Sgshapiro else 131664562Sgshapiro { 131764562Sgshapiro /* don't want to do any of this if authenticating */ 131890792Sgshapiro#endif /* SASL */ 131964562Sgshapiro 132038032Speter /* echo command to transcript */ 132138032Speter if (e->e_xfp != NULL) 132290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 132390792Sgshapiro "<<< %s\n", inp); 132438032Speter 132590792Sgshapiro if (LogLevel > 14) 132690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp); 132738032Speter 132838032Speter /* break off command */ 132938032Speter for (p = inp; isascii(*p) && isspace(*p); p++) 133038032Speter continue; 133138032Speter cmd = cmdbuf; 133238032Speter while (*p != '\0' && 133338032Speter !(isascii(*p) && isspace(*p)) && 133438032Speter cmd < &cmdbuf[sizeof cmdbuf - 2]) 133538032Speter *cmd++ = *p++; 133638032Speter *cmd = '\0'; 133738032Speter 133838032Speter /* throw away leading whitespace */ 133990792Sgshapiro SKIP_SPACE(p); 134038032Speter 134138032Speter /* decode command */ 134264562Sgshapiro for (c = CmdTab; c->cmd_name != NULL; c++) 134338032Speter { 134490792Sgshapiro if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) 134538032Speter break; 134638032Speter } 134738032Speter 134838032Speter /* reset errors */ 134938032Speter errno = 0; 135038032Speter 135190792Sgshapiro /* check whether a "non-null" command has been used */ 135290792Sgshapiro switch (c->cmd_code) 135390792Sgshapiro { 135490792Sgshapiro#if SASL 135590792Sgshapiro case CMDAUTH: 135690792Sgshapiro /* avoid information leak; take first two words? */ 135790792Sgshapiro q = "AUTH"; 135890792Sgshapiro break; 135990792Sgshapiro#endif /* SASL */ 136090792Sgshapiro 136190792Sgshapiro case CMDMAIL: 136290792Sgshapiro case CMDEXPN: 136390792Sgshapiro case CMDVRFY: 136490792Sgshapiro case CMDETRN: 136590792Sgshapiro lognullconnection = false; 136690792Sgshapiro /* FALLTHROUGH */ 136790792Sgshapiro default: 136890792Sgshapiro q = inp; 136990792Sgshapiro break; 137090792Sgshapiro } 137190792Sgshapiro 137290792Sgshapiro if (e->e_id == NULL) 137390792Sgshapiro sm_setproctitle(true, e, "%s: %.80s", 137490792Sgshapiro CurSmtpClient, q); 137590792Sgshapiro else 137690792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", 137790792Sgshapiro qid_printname(e), 137890792Sgshapiro CurSmtpClient, q); 137990792Sgshapiro 138038032Speter /* 138138032Speter ** Process command. 138238032Speter ** 138338032Speter ** If we are running as a null server, return 550 138490792Sgshapiro ** to almost everything. 138538032Speter */ 138638032Speter 138764562Sgshapiro if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags)) 138838032Speter { 138964562Sgshapiro switch (c->cmd_code) 139038032Speter { 139138032Speter case CMDQUIT: 139238032Speter case CMDHELO: 139338032Speter case CMDEHLO: 139438032Speter case CMDNOOP: 139564562Sgshapiro case CMDRSET: 1396125820Sgshapiro case CMDERROR: 139738032Speter /* process normally */ 139838032Speter break; 139938032Speter 140064562Sgshapiro case CMDETRN: 140164562Sgshapiro if (bitnset(D_ETRNONLY, d_flags) && 140264562Sgshapiro nullserver == NULL) 140364562Sgshapiro break; 140490792Sgshapiro DELAY_CONN("ETRN"); 140580785Sgshapiro /* FALLTHROUGH */ 140664562Sgshapiro 140738032Speter default: 140890792Sgshapiro#if MAXBADCOMMANDS > 0 140990792Sgshapiro /* theoretically this could overflow */ 141090792Sgshapiro if (nullserver != NULL && 141190792Sgshapiro ++n_badcmds > MAXBADCOMMANDS) 141264562Sgshapiro { 141390792Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 141490792Sgshapiro MyHostName); 141590792Sgshapiro 141690792Sgshapiro /* arrange to ignore send list */ 141790792Sgshapiro e->e_sendqueue = NULL; 141890792Sgshapiro goto doquit; 141964562Sgshapiro } 142090792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 142164562Sgshapiro if (nullserver != NULL) 142264562Sgshapiro { 142364562Sgshapiro if (ISSMTPREPLY(nullserver)) 142464562Sgshapiro usrerr(nullserver); 142564562Sgshapiro else 142690792Sgshapiro usrerr("550 5.0.0 %s", 142790792Sgshapiro nullserver); 142864562Sgshapiro } 142964562Sgshapiro else 143064562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 143138032Speter continue; 143238032Speter } 143338032Speter } 143438032Speter 143564562Sgshapiro switch (c->cmd_code) 143638032Speter { 143790792Sgshapiro#if SASL 143864562Sgshapiro case CMDAUTH: /* sasl */ 143990792Sgshapiro DELAY_CONN("AUTH"); 144090792Sgshapiro if (!sasl_ok || n_mechs <= 0) 144164562Sgshapiro { 144264562Sgshapiro message("503 5.3.3 AUTH not available"); 144364562Sgshapiro break; 144464562Sgshapiro } 144564562Sgshapiro if (authenticating == SASL_IS_AUTH) 144664562Sgshapiro { 144764562Sgshapiro message("503 5.5.0 Already Authenticated"); 144864562Sgshapiro break; 144964562Sgshapiro } 145090792Sgshapiro if (smtp.sm_gotmail) 145164562Sgshapiro { 145264562Sgshapiro message("503 5.5.0 AUTH not permitted during a mail transaction"); 145364562Sgshapiro break; 145464562Sgshapiro } 145564562Sgshapiro if (tempfail) 145664562Sgshapiro { 145764562Sgshapiro if (LogLevel > 9) 145864562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1459110560Sgshapiro "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)", 146064562Sgshapiro p, CurSmtpClient); 1461132943Sgshapiro usrerr("454 4.3.0 Please try again later"); 146264562Sgshapiro break; 146364562Sgshapiro } 146464562Sgshapiro 146590792Sgshapiro ismore = false; 146664562Sgshapiro 146764562Sgshapiro /* crude way to avoid crack attempts */ 1468132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1, 1469132943Sgshapiro true, "AUTH", e)); 147064562Sgshapiro 147190792Sgshapiro /* make sure mechanism (p) is a valid string */ 147264562Sgshapiro for (q = p; *q != '\0' && isascii(*q); q++) 147364562Sgshapiro { 147464562Sgshapiro if (isspace(*q)) 147564562Sgshapiro { 147664562Sgshapiro *q = '\0'; 147764562Sgshapiro while (*++q != '\0' && 147864562Sgshapiro isascii(*q) && isspace(*q)) 147964562Sgshapiro continue; 148064562Sgshapiro *(q - 1) = '\0'; 148164562Sgshapiro ismore = (*q != '\0'); 148264562Sgshapiro break; 148364562Sgshapiro } 148464562Sgshapiro } 148564562Sgshapiro 148698121Sgshapiro if (*p == '\0') 148798121Sgshapiro { 148898121Sgshapiro message("501 5.5.2 AUTH mechanism must be specified"); 148998121Sgshapiro break; 149098121Sgshapiro } 149198121Sgshapiro 149264562Sgshapiro /* check whether mechanism is available */ 149364562Sgshapiro if (iteminlist(p, mechlist, " ") == NULL) 149464562Sgshapiro { 149598121Sgshapiro message("504 5.3.3 AUTH mechanism %.32s not available", 149664562Sgshapiro p); 149764562Sgshapiro break; 149864562Sgshapiro } 149964562Sgshapiro 150064562Sgshapiro if (ismore) 150164562Sgshapiro { 150264562Sgshapiro /* could this be shorter? XXX */ 150398121Sgshapiro# if SASL >= 20000 150498121Sgshapiro in = xalloc(strlen(q) + 1); 1505102528Sgshapiro result = sasl_decode64(q, strlen(q), in, 150698121Sgshapiro strlen(q), &inlen); 150798121Sgshapiro# else /* SASL >= 20000 */ 150890792Sgshapiro in = sm_rpool_malloc(e->e_rpool, strlen(q)); 150964562Sgshapiro result = sasl_decode64(q, strlen(q), in, 151090792Sgshapiro &inlen); 151198121Sgshapiro# endif /* SASL >= 20000 */ 151264562Sgshapiro if (result != SASL_OK) 151364562Sgshapiro { 151464562Sgshapiro message("501 5.5.4 cannot BASE64 decode '%s'", 151564562Sgshapiro q); 151664562Sgshapiro if (LogLevel > 5) 151764562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 151890792Sgshapiro "AUTH decode64 error [%d for \"%s\"]", 151964562Sgshapiro result, q); 152064562Sgshapiro /* start over? */ 152164562Sgshapiro authenticating = SASL_NOT_AUTH; 152298121Sgshapiro# if SASL >= 20000 152398121Sgshapiro sm_free(in); 152498121Sgshapiro# endif /* SASL >= 20000 */ 152564562Sgshapiro in = NULL; 152664562Sgshapiro inlen = 0; 152764562Sgshapiro break; 152864562Sgshapiro } 152964562Sgshapiro } 153064562Sgshapiro else 153164562Sgshapiro { 153264562Sgshapiro in = NULL; 153364562Sgshapiro inlen = 0; 153464562Sgshapiro } 153564562Sgshapiro 153664562Sgshapiro /* see if that auth type exists */ 153798121Sgshapiro# if SASL >= 20000 1538102528Sgshapiro result = sasl_server_start(conn, p, in, inlen, 153998121Sgshapiro &out, &outlen); 154098121Sgshapiro if (in != NULL) 154198121Sgshapiro sm_free(in); 154298121Sgshapiro# else /* SASL >= 20000 */ 154364562Sgshapiro result = sasl_server_start(conn, p, in, inlen, 154464562Sgshapiro &out, &outlen, &errstr); 154598121Sgshapiro# endif /* SASL >= 20000 */ 154664562Sgshapiro 154764562Sgshapiro if (result != SASL_OK && result != SASL_CONTINUE) 154864562Sgshapiro { 154998121Sgshapiro message("535 5.7.0 authentication failed"); 155064562Sgshapiro if (LogLevel > 9) 155164562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 155290792Sgshapiro "AUTH failure (%s): %s (%d) %s", 155364562Sgshapiro p, 155464562Sgshapiro sasl_errstring(result, NULL, 155564562Sgshapiro NULL), 155690792Sgshapiro result, 155798121Sgshapiro# if SASL >= 20000 155898121Sgshapiro sasl_errdetail(conn)); 155998121Sgshapiro# else /* SASL >= 20000 */ 156090792Sgshapiro errstr); 156198121Sgshapiro# endif /* SASL >= 20000 */ 1562120256Sgshapiro RESET_SASLCONN; 156364562Sgshapiro break; 156464562Sgshapiro } 156564562Sgshapiro auth_type = newstr(p); 156664562Sgshapiro 156764562Sgshapiro if (result == SASL_OK) 156864562Sgshapiro { 156964562Sgshapiro /* ugly, but same code */ 157064562Sgshapiro goto authenticated; 157164562Sgshapiro /* authenticated by the initial response */ 157264562Sgshapiro } 157364562Sgshapiro 157464562Sgshapiro /* len is at least 2 */ 157564562Sgshapiro len = ENC64LEN(outlen); 157664562Sgshapiro out2 = xalloc(len); 157764562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 157890792Sgshapiro &out2len); 157964562Sgshapiro 158064562Sgshapiro if (result != SASL_OK) 158164562Sgshapiro { 158264562Sgshapiro message("454 4.5.4 Temporary authentication failure"); 158364562Sgshapiro if (LogLevel > 5) 158464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 158590792Sgshapiro "AUTH encode64 error [%d for \"%s\"]", 158664562Sgshapiro result, out); 158764562Sgshapiro 158864562Sgshapiro /* start over? */ 158964562Sgshapiro authenticating = SASL_NOT_AUTH; 1590120256Sgshapiro RESET_SASLCONN; 159164562Sgshapiro } 159264562Sgshapiro else 159364562Sgshapiro { 159464562Sgshapiro message("334 %s", out2); 159564562Sgshapiro authenticating = SASL_PROC_AUTH; 159664562Sgshapiro } 159798121Sgshapiro# if SASL >= 20000 159898121Sgshapiro sm_free(out2); 159998121Sgshapiro# endif /* SASL >= 20000 */ 160064562Sgshapiro break; 160190792Sgshapiro#endif /* SASL */ 160264562Sgshapiro 160390792Sgshapiro#if STARTTLS 160464562Sgshapiro case CMDSTLS: /* starttls */ 160590792Sgshapiro DELAY_CONN("STARTTLS"); 160664562Sgshapiro if (*p != '\0') 160764562Sgshapiro { 160864562Sgshapiro message("501 5.5.2 Syntax error (no parameters allowed)"); 160964562Sgshapiro break; 161064562Sgshapiro } 161190792Sgshapiro if (!bitset(SRV_OFFER_TLS, features)) 161264562Sgshapiro { 161364562Sgshapiro message("503 5.5.0 TLS not available"); 161464562Sgshapiro break; 161564562Sgshapiro } 161677349Sgshapiro if (!tls_ok_srv) 161764562Sgshapiro { 161864562Sgshapiro message("454 4.3.3 TLS not available after start"); 161964562Sgshapiro break; 162064562Sgshapiro } 162190792Sgshapiro if (smtp.sm_gotmail) 162264562Sgshapiro { 162364562Sgshapiro message("503 5.5.0 TLS not permitted during a mail transaction"); 162464562Sgshapiro break; 162564562Sgshapiro } 162664562Sgshapiro if (tempfail) 162764562Sgshapiro { 162864562Sgshapiro if (LogLevel > 9) 162964562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1630110560Sgshapiro "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)", 163164562Sgshapiro p, CurSmtpClient); 1632132943Sgshapiro usrerr("454 4.7.0 Please try again later"); 163364562Sgshapiro break; 163464562Sgshapiro } 163590792Sgshapiro starttls: 163664562Sgshapiro# if TLS_NO_RSA 163764562Sgshapiro /* 163864562Sgshapiro ** XXX do we need a temp key ? 163964562Sgshapiro */ 164064562Sgshapiro# else /* TLS_NO_RSA */ 164164562Sgshapiro# endif /* TLS_NO_RSA */ 164290792Sgshapiro 164390792Sgshapiro# if TLS_VRFY_PER_CTX 164490792Sgshapiro /* 164590792Sgshapiro ** Note: this sets the verification globally 164690792Sgshapiro ** (per SSL_CTX) 164790792Sgshapiro ** it's ok since it applies only to one transaction 164890792Sgshapiro */ 164990792Sgshapiro 165090792Sgshapiro TLS_VERIFY_CLIENT(); 165190792Sgshapiro# endif /* TLS_VRFY_PER_CTX */ 165290792Sgshapiro 165364562Sgshapiro if (srv_ssl != NULL) 165464562Sgshapiro SSL_clear(srv_ssl); 165564562Sgshapiro else if ((srv_ssl = SSL_new(srv_ctx)) == NULL) 165664562Sgshapiro { 165764562Sgshapiro message("454 4.3.3 TLS not available: error generating SSL handle"); 1658120256Sgshapiro if (LogLevel > 8) 1659120256Sgshapiro tlslogerr("server"); 166090792Sgshapiro goto tls_done; 166164562Sgshapiro } 166290792Sgshapiro 166390792Sgshapiro# if !TLS_VRFY_PER_CTX 166490792Sgshapiro /* 166590792Sgshapiro ** this could be used if it were possible to set 166690792Sgshapiro ** verification per SSL (connection) 166790792Sgshapiro ** not just per SSL_CTX (global) 166890792Sgshapiro */ 166990792Sgshapiro 167090792Sgshapiro TLS_VERIFY_CLIENT(); 167190792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */ 167290792Sgshapiro 167390792Sgshapiro rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 167490792Sgshapiro wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); 167590792Sgshapiro 167666494Sgshapiro if (rfd < 0 || wfd < 0 || 167766494Sgshapiro SSL_set_rfd(srv_ssl, rfd) <= 0 || 167866494Sgshapiro SSL_set_wfd(srv_ssl, wfd) <= 0) 167964562Sgshapiro { 168064562Sgshapiro message("454 4.3.3 TLS not available: error set fd"); 168164562Sgshapiro SSL_free(srv_ssl); 168264562Sgshapiro srv_ssl = NULL; 168390792Sgshapiro goto tls_done; 168464562Sgshapiro } 168590792Sgshapiro if (!smtps) 168690792Sgshapiro message("220 2.0.0 Ready to start TLS"); 168790792Sgshapiro# if PIPELINING 168890792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 168990792Sgshapiro# endif /* PIPELINING */ 169090792Sgshapiro 169164562Sgshapiro SSL_set_accept_state(srv_ssl); 169264562Sgshapiro 169364562Sgshapiro# define SSL_ACC(s) SSL_accept(s) 169490792Sgshapiro 169590792Sgshapiro tlsstart = curtime(); 1696132943Sgshapiro fdfl = fcntl(rfd, F_GETFL); 1697132943Sgshapiro if (fdfl != -1) 1698132943Sgshapiro fcntl(rfd, F_SETFL, fdfl|O_NONBLOCK); 169990792Sgshapiro ssl_retry: 170064562Sgshapiro if ((r = SSL_ACC(srv_ssl)) <= 0) 170164562Sgshapiro { 170264562Sgshapiro int i; 170390792Sgshapiro bool timedout; 170490792Sgshapiro time_t left; 170590792Sgshapiro time_t now = curtime(); 170690792Sgshapiro struct timeval tv; 170764562Sgshapiro 170864562Sgshapiro /* what to do in this case? */ 170964562Sgshapiro i = SSL_get_error(srv_ssl, r); 171090792Sgshapiro 171190792Sgshapiro /* 171290792Sgshapiro ** For SSL_ERROR_WANT_{READ,WRITE}: 171390792Sgshapiro ** There is no SSL record available yet 171490792Sgshapiro ** or there is only a partial SSL record 171590792Sgshapiro ** removed from the network (socket) buffer 171690792Sgshapiro ** into the SSL buffer. The SSL_accept will 171790792Sgshapiro ** only succeed when a full SSL record is 171890792Sgshapiro ** available (assuming a "real" error 171990792Sgshapiro ** doesn't happen). To handle when a "real" 172090792Sgshapiro ** error does happen the select is set for 172190792Sgshapiro ** exceptions too. 172290792Sgshapiro ** The connection may be re-negotiated 172390792Sgshapiro ** during this time so both read and write 172490792Sgshapiro ** "want errors" need to be handled. 172590792Sgshapiro ** A select() exception loops back so that 172690792Sgshapiro ** a proper SSL error message can be gotten. 172790792Sgshapiro */ 172890792Sgshapiro 172990792Sgshapiro left = TimeOuts.to_starttls - (now - tlsstart); 173090792Sgshapiro timedout = left <= 0; 173190792Sgshapiro if (!timedout) 173290792Sgshapiro { 173390792Sgshapiro tv.tv_sec = left; 173490792Sgshapiro tv.tv_usec = 0; 173590792Sgshapiro } 173690792Sgshapiro 1737110560Sgshapiro if (!timedout && FD_SETSIZE > 0 && 1738110560Sgshapiro (rfd >= FD_SETSIZE || 1739110560Sgshapiro (i == SSL_ERROR_WANT_WRITE && 1740110560Sgshapiro wfd >= FD_SETSIZE))) 1741110560Sgshapiro { 1742110560Sgshapiro if (LogLevel > 5) 1743110560Sgshapiro { 1744110560Sgshapiro sm_syslog(LOG_ERR, NOQID, 1745110560Sgshapiro "STARTTLS=server, error: fd %d/%d too large", 1746110560Sgshapiro rfd, wfd); 1747110560Sgshapiro if (LogLevel > 8) 1748110560Sgshapiro tlslogerr("server"); 1749110560Sgshapiro } 1750110560Sgshapiro goto tlsfail; 1751110560Sgshapiro } 1752110560Sgshapiro 175390792Sgshapiro /* XXX what about SSL_pending() ? */ 175490792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_READ) 175590792Sgshapiro { 175690792Sgshapiro fd_set ssl_maskr, ssl_maskx; 175790792Sgshapiro 175890792Sgshapiro FD_ZERO(&ssl_maskr); 175990792Sgshapiro FD_SET(rfd, &ssl_maskr); 176090792Sgshapiro FD_ZERO(&ssl_maskx); 176190792Sgshapiro FD_SET(rfd, &ssl_maskx); 176290792Sgshapiro if (select(rfd + 1, &ssl_maskr, NULL, 176390792Sgshapiro &ssl_maskx, &tv) > 0) 176490792Sgshapiro goto ssl_retry; 176590792Sgshapiro } 176690792Sgshapiro if (!timedout && i == SSL_ERROR_WANT_WRITE) 176790792Sgshapiro { 176890792Sgshapiro fd_set ssl_maskw, ssl_maskx; 176990792Sgshapiro 177090792Sgshapiro FD_ZERO(&ssl_maskw); 177190792Sgshapiro FD_SET(wfd, &ssl_maskw); 177290792Sgshapiro FD_ZERO(&ssl_maskx); 177390792Sgshapiro FD_SET(rfd, &ssl_maskx); 177490792Sgshapiro if (select(wfd + 1, NULL, &ssl_maskw, 177590792Sgshapiro &ssl_maskx, &tv) > 0) 177690792Sgshapiro goto ssl_retry; 177790792Sgshapiro } 177864562Sgshapiro if (LogLevel > 5) 177964562Sgshapiro { 178090792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 1781111823Sgshapiro "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d, errno=%d", 1782111823Sgshapiro r, i, (int) timedout, errno); 178390792Sgshapiro if (LogLevel > 8) 178490792Sgshapiro tlslogerr("server"); 178564562Sgshapiro } 1786110560Sgshapirotlsfail: 178790792Sgshapiro tls_ok_srv = false; 178864562Sgshapiro SSL_free(srv_ssl); 178964562Sgshapiro srv_ssl = NULL; 179064562Sgshapiro 179164562Sgshapiro /* 179264562Sgshapiro ** according to the next draft of 179364562Sgshapiro ** RFC 2487 the connection should be dropped 179464562Sgshapiro */ 179564562Sgshapiro 179664562Sgshapiro /* arrange to ignore any current send list */ 179764562Sgshapiro e->e_sendqueue = NULL; 179864562Sgshapiro goto doquit; 179964562Sgshapiro } 180064562Sgshapiro 1801132943Sgshapiro if (fdfl != -1) 1802132943Sgshapiro fcntl(rfd, F_SETFL, fdfl); 1803132943Sgshapiro 180464562Sgshapiro /* ignore return code for now, it's in {verify} */ 180590792Sgshapiro (void) tls_get_info(srv_ssl, true, 180690792Sgshapiro CurSmtpClient, 180790792Sgshapiro &BlankEnvelope.e_macro, 180890792Sgshapiro bitset(SRV_VRFY_CLT, features)); 180964562Sgshapiro 181064562Sgshapiro /* 181164562Sgshapiro ** call Stls_client to find out whether 181264562Sgshapiro ** to accept the connection from the client 181364562Sgshapiro */ 181464562Sgshapiro 181564562Sgshapiro saveQuickAbort = QuickAbort; 181664562Sgshapiro saveSuprErrs = SuprErrs; 181790792Sgshapiro SuprErrs = true; 181890792Sgshapiro QuickAbort = false; 181964562Sgshapiro if (rscheck("tls_client", 182090792Sgshapiro macvalue(macid("{verify}"), e), 1821102528Sgshapiro "STARTTLS", e, 1822102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 1823102528Sgshapiro 5, NULL, NOQID) != EX_OK || 182490792Sgshapiro Errors > 0) 182564562Sgshapiro { 182664562Sgshapiro extern char MsgBuf[]; 182764562Sgshapiro 182864562Sgshapiro if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf)) 182964562Sgshapiro nullserver = newstr(MsgBuf); 183064562Sgshapiro else 183164562Sgshapiro nullserver = "503 5.7.0 Authentication required."; 183264562Sgshapiro } 183364562Sgshapiro QuickAbort = saveQuickAbort; 183464562Sgshapiro SuprErrs = saveSuprErrs; 183564562Sgshapiro 183690792Sgshapiro tls_ok_srv = false; /* don't offer STARTTLS again */ 183764562Sgshapiro n_helo = 0; 183890792Sgshapiro# if SASL 183964562Sgshapiro if (sasl_ok) 184064562Sgshapiro { 1841132943Sgshapiro int cipher_bits; 1842132943Sgshapiro bool verified; 1843132943Sgshapiro char *s, *v, *c; 184464562Sgshapiro 184590792Sgshapiro s = macvalue(macid("{cipher_bits}"), e); 1846132943Sgshapiro v = macvalue(macid("{verify}"), e); 1847132943Sgshapiro c = macvalue(macid("{cert_subject}"), e); 1848132943Sgshapiro verified = (v != NULL && strcmp(v, "OK") == 0); 1849132943Sgshapiro if (s != NULL && (cipher_bits = atoi(s)) > 0) 1850132943Sgshapiro { 185198121Sgshapiro# if SASL >= 20000 1852132943Sgshapiro ext_ssf = cipher_bits; 1853132943Sgshapiro auth_id = verified ? c : NULL; 1854132943Sgshapiro sasl_ok = ((sasl_setprop(conn, 1855132943Sgshapiro SASL_SSF_EXTERNAL, 1856132943Sgshapiro &ext_ssf) == SASL_OK) && 1857132943Sgshapiro (sasl_setprop(conn, 1858132943Sgshapiro SASL_AUTH_EXTERNAL, 1859132943Sgshapiro auth_id) == SASL_OK)); 186098121Sgshapiro# else /* SASL >= 20000 */ 1861132943Sgshapiro ext_ssf.ssf = cipher_bits; 1862132943Sgshapiro ext_ssf.auth_id = verified ? c : NULL; 1863132943Sgshapiro sasl_ok = sasl_setprop(conn, 1864132943Sgshapiro SASL_SSF_EXTERNAL, 1865132943Sgshapiro &ext_ssf) == SASL_OK; 186698121Sgshapiro# endif /* SASL >= 20000 */ 186764562Sgshapiro mechlist = NULL; 186864562Sgshapiro if (sasl_ok) 186964562Sgshapiro n_mechs = saslmechs(conn, 187064562Sgshapiro &mechlist); 187164562Sgshapiro } 187264562Sgshapiro } 187390792Sgshapiro# endif /* SASL */ 187464562Sgshapiro 187564562Sgshapiro /* switch to secure connection */ 187690792Sgshapiro if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0) 187790792Sgshapiro { 187890792Sgshapiro tls_active = true; 187990792Sgshapiro# if PIPELINING 188090792Sgshapiro (void) sm_io_autoflush(InChannel, OutChannel); 188190792Sgshapiro# endif /* PIPELINING */ 188290792Sgshapiro } 188364562Sgshapiro else 188464562Sgshapiro { 188564562Sgshapiro /* 188664562Sgshapiro ** XXX this is an internal error 188764562Sgshapiro ** how to deal with it? 188864562Sgshapiro ** we can't generate an error message 188964562Sgshapiro ** since the other side switched to an 189064562Sgshapiro ** encrypted layer, but we could not... 189164562Sgshapiro ** just "hang up"? 189264562Sgshapiro */ 189390792Sgshapiro 189464562Sgshapiro nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer"; 189590792Sgshapiro syserr("STARTTLS: can't switch to encrypted layer"); 189664562Sgshapiro } 189790792Sgshapiro tls_done: 189890792Sgshapiro if (smtps) 189990792Sgshapiro { 190090792Sgshapiro if (tls_active) 190190792Sgshapiro goto greeting; 190290792Sgshapiro else 190390792Sgshapiro goto doquit; 190490792Sgshapiro } 190564562Sgshapiro break; 190690792Sgshapiro#endif /* STARTTLS */ 190764562Sgshapiro 190838032Speter case CMDHELO: /* hello -- introduce yourself */ 190938032Speter case CMDEHLO: /* extended hello */ 191090792Sgshapiro DELAY_CONN("EHLO"); 191164562Sgshapiro if (c->cmd_code == CMDEHLO) 191238032Speter { 191338032Speter protocol = "ESMTP"; 191438032Speter SmtpPhase = "server EHLO"; 191538032Speter } 191638032Speter else 191738032Speter { 191838032Speter protocol = "SMTP"; 191938032Speter SmtpPhase = "server HELO"; 192038032Speter } 192138032Speter 192238032Speter /* avoid denial-of-service */ 1923132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS, 1924132943Sgshapiro true, "HELO/EHLO", e)); 192538032Speter 192690792Sgshapiro#if 0 192790792Sgshapiro /* RFC2821 4.1.4 allows duplicate HELO/EHLO */ 192838032Speter /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ 192938032Speter if (gothello) 193038032Speter { 193138032Speter usrerr("503 %s Duplicate HELO/EHLO", 193273188Sgshapiro MyHostName); 193338032Speter break; 193438032Speter } 193590792Sgshapiro#endif /* 0 */ 193638032Speter 193738032Speter /* check for valid domain name (re 1123 5.2.5) */ 193838032Speter if (*p == '\0' && !AllowBogusHELO) 193938032Speter { 194038032Speter usrerr("501 %s requires domain address", 194138032Speter cmdbuf); 194238032Speter break; 194338032Speter } 194438032Speter 194538032Speter /* check for long domain name (hides Received: info) */ 194638032Speter if (strlen(p) > MAXNAME) 194738032Speter { 194838032Speter usrerr("501 Invalid domain name"); 194964562Sgshapiro if (LogLevel > 9) 195064562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 1951110560Sgshapiro "invalid domain name (too long) from %s", 195264562Sgshapiro CurSmtpClient); 195338032Speter break; 195438032Speter } 195538032Speter 195698121Sgshapiro ok = true; 195738032Speter for (q = p; *q != '\0'; q++) 195838032Speter { 195938032Speter if (!isascii(*q)) 196038032Speter break; 196138032Speter if (isalnum(*q)) 196238032Speter continue; 196338032Speter if (isspace(*q)) 196438032Speter { 196538032Speter *q = '\0'; 196698121Sgshapiro 196798121Sgshapiro /* only complain if strict check */ 196898121Sgshapiro ok = AllowBogusHELO; 196938032Speter break; 197038032Speter } 1971120256Sgshapiro if (strchr("[].-_#:", *q) == NULL) 197238032Speter break; 197338032Speter } 197464562Sgshapiro 197598121Sgshapiro if (*q == '\0' && ok) 197638032Speter { 197738032Speter q = "pleased to meet you"; 197890792Sgshapiro sendinghost = sm_strdup_x(p); 197938032Speter } 198038032Speter else if (!AllowBogusHELO) 198138032Speter { 198238032Speter usrerr("501 Invalid domain name"); 198364562Sgshapiro if (LogLevel > 9) 198464562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 1985110560Sgshapiro "invalid domain name (%s) from %.100s", 198664562Sgshapiro p, CurSmtpClient); 198738032Speter break; 198838032Speter } 198938032Speter else 199038032Speter { 199138032Speter q = "accepting invalid domain name"; 199238032Speter } 199338032Speter 199490792Sgshapiro if (gothello) 199590792Sgshapiro { 199690792Sgshapiro CLEAR_STATE(cmdbuf); 199790792Sgshapiro } 199890792Sgshapiro 199990792Sgshapiro#if MILTER 200090792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 200190792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 200264562Sgshapiro { 200364562Sgshapiro char state; 200464562Sgshapiro char *response; 200564562Sgshapiro 200664562Sgshapiro response = milter_helo(p, e, &state); 200764562Sgshapiro switch (state) 200864562Sgshapiro { 200964562Sgshapiro case SMFIR_REPLYCODE: 201090792Sgshapiro if (MilterLogLevel > 3) 201190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 201290792Sgshapiro "Milter: helo=%s, reject=%s", 201390792Sgshapiro p, response); 201490792Sgshapiro nullserver = newstr(response); 201590792Sgshapiro smtp.sm_milterize = false; 201664562Sgshapiro break; 201764562Sgshapiro 201864562Sgshapiro case SMFIR_REJECT: 201990792Sgshapiro if (MilterLogLevel > 3) 202090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 202190792Sgshapiro "Milter: helo=%s, reject=Command rejected", 202290792Sgshapiro p); 202364562Sgshapiro nullserver = "Command rejected"; 202490792Sgshapiro smtp.sm_milterize = false; 202564562Sgshapiro break; 202664562Sgshapiro 202764562Sgshapiro case SMFIR_TEMPFAIL: 202890792Sgshapiro if (MilterLogLevel > 3) 202990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 203090792Sgshapiro "Milter: helo=%s, reject=%s", 203190792Sgshapiro p, MSG_TEMPFAIL); 203290792Sgshapiro tempfail = true; 203390792Sgshapiro smtp.sm_milterize = false; 203464562Sgshapiro break; 203564562Sgshapiro } 203690792Sgshapiro if (response != NULL) 203790792Sgshapiro sm_free(response); 203890792Sgshapiro 203990792Sgshapiro /* 204090792Sgshapiro ** If quarantining by a connect/ehlo action, 204190792Sgshapiro ** save between messages 204290792Sgshapiro */ 204390792Sgshapiro 204490792Sgshapiro if (smtp.sm_quarmsg == NULL && 204590792Sgshapiro e->e_quarmsg != NULL) 204690792Sgshapiro smtp.sm_quarmsg = newstr(e->e_quarmsg); 204764562Sgshapiro } 204890792Sgshapiro#endif /* MILTER */ 204990792Sgshapiro gothello = true; 205064562Sgshapiro 205138032Speter /* print HELO response message */ 205264562Sgshapiro if (c->cmd_code != CMDEHLO) 205338032Speter { 205438032Speter message("250 %s Hello %s, %s", 205538032Speter MyHostName, CurSmtpClient, q); 205638032Speter break; 205738032Speter } 205838032Speter 205938032Speter message("250-%s Hello %s, %s", 206038032Speter MyHostName, CurSmtpClient, q); 206138032Speter 206264562Sgshapiro /* offer ENHSC even for nullserver */ 206364562Sgshapiro if (nullserver != NULL) 206464562Sgshapiro { 206564562Sgshapiro message("250 ENHANCEDSTATUSCODES"); 206664562Sgshapiro break; 206764562Sgshapiro } 206864562Sgshapiro 206966494Sgshapiro /* 207066494Sgshapiro ** print EHLO features list 207166494Sgshapiro ** 207266494Sgshapiro ** Note: If you change this list, 207390792Sgshapiro ** remember to update 'helpfile' 207466494Sgshapiro */ 207566494Sgshapiro 207664562Sgshapiro message("250-ENHANCEDSTATUSCODES"); 207790792Sgshapiro#if PIPELINING 207890792Sgshapiro if (bitset(SRV_OFFER_PIPE, features)) 207990792Sgshapiro message("250-PIPELINING"); 208090792Sgshapiro#endif /* PIPELINING */ 208190792Sgshapiro if (bitset(SRV_OFFER_EXPN, features)) 208238032Speter { 208338032Speter message("250-EXPN"); 208490792Sgshapiro if (bitset(SRV_OFFER_VERB, features)) 208538032Speter message("250-VERB"); 208638032Speter } 208790792Sgshapiro#if MIME8TO7 208838032Speter message("250-8BITMIME"); 208990792Sgshapiro#endif /* MIME8TO7 */ 209038032Speter if (MaxMessageSize > 0) 209138032Speter message("250-SIZE %ld", MaxMessageSize); 209238032Speter else 209338032Speter message("250-SIZE"); 209490792Sgshapiro#if DSN 209590792Sgshapiro if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features)) 209638032Speter message("250-DSN"); 209790792Sgshapiro#endif /* DSN */ 209890792Sgshapiro if (bitset(SRV_OFFER_ETRN, features)) 209938032Speter message("250-ETRN"); 210090792Sgshapiro#if SASL 210164562Sgshapiro if (sasl_ok && mechlist != NULL && *mechlist != '\0') 210264562Sgshapiro message("250-AUTH %s", mechlist); 210390792Sgshapiro#endif /* SASL */ 210490792Sgshapiro#if STARTTLS 210598841Sgshapiro if (tls_ok_srv && 210698841Sgshapiro bitset(SRV_OFFER_TLS, features)) 210764562Sgshapiro message("250-STARTTLS"); 210890792Sgshapiro#endif /* STARTTLS */ 210990792Sgshapiro if (DeliverByMin > 0) 211090792Sgshapiro message("250-DELIVERBY %ld", 211190792Sgshapiro (long) DeliverByMin); 211290792Sgshapiro else if (DeliverByMin == 0) 211390792Sgshapiro message("250-DELIVERBY"); 211490792Sgshapiro 211590792Sgshapiro /* < 0: no deliver-by */ 211690792Sgshapiro 211738032Speter message("250 HELP"); 211838032Speter break; 211938032Speter 212038032Speter case CMDMAIL: /* mail -- designate sender */ 212138032Speter SmtpPhase = "server MAIL"; 212290792Sgshapiro DELAY_CONN("MAIL"); 212338032Speter 212438032Speter /* check for validity of this command */ 212538032Speter if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 212638032Speter { 212764562Sgshapiro usrerr("503 5.0.0 Polite people say HELO first"); 212838032Speter break; 212938032Speter } 213090792Sgshapiro if (smtp.sm_gotmail) 213138032Speter { 213264562Sgshapiro usrerr("503 5.5.0 Sender already specified"); 213338032Speter break; 213438032Speter } 213590792Sgshapiro#if SASL 213690792Sgshapiro if (bitset(SRV_REQ_AUTH, features) && 213764562Sgshapiro authenticating != SASL_IS_AUTH) 213864562Sgshapiro { 213964562Sgshapiro usrerr("530 5.7.0 Authentication required"); 214064562Sgshapiro break; 214164562Sgshapiro } 214290792Sgshapiro#endif /* SASL */ 214338032Speter 214464562Sgshapiro p = skipword(p, "from"); 214564562Sgshapiro if (p == NULL) 214664562Sgshapiro break; 214764562Sgshapiro if (tempfail) 214864562Sgshapiro { 214964562Sgshapiro if (LogLevel > 9) 215064562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2151110560Sgshapiro "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)", 215264562Sgshapiro p, CurSmtpClient); 215390792Sgshapiro usrerr(MSG_TEMPFAIL); 215464562Sgshapiro break; 215564562Sgshapiro } 215664562Sgshapiro 215738032Speter /* make sure we know who the sending host is */ 215838032Speter if (sendinghost == NULL) 215938032Speter sendinghost = peerhostname; 216038032Speter 216138032Speter 216290792Sgshapiro#if SM_HEAP_CHECK 216390792Sgshapiro if (sm_debug_active(&DebugLeakSmtp, 1)) 216490792Sgshapiro { 216590792Sgshapiro sm_heap_newgroup(); 216690792Sgshapiro sm_dprintf("smtp() heap group #%d\n", 216790792Sgshapiro sm_heap_group()); 216890792Sgshapiro } 216990792Sgshapiro#endif /* SM_HEAP_CHECK */ 217064562Sgshapiro 217138032Speter if (Errors > 0) 217290792Sgshapiro goto undo_no_pm; 217338032Speter if (!gothello) 217438032Speter { 217590792Sgshapiro auth_warning(e, "%s didn't use HELO protocol", 217690792Sgshapiro CurSmtpClient); 217738032Speter } 217890792Sgshapiro#ifdef PICKY_HELO_CHECK 217990792Sgshapiro if (sm_strcasecmp(sendinghost, peerhostname) != 0 && 218090792Sgshapiro (sm_strcasecmp(peerhostname, "localhost") != 0 || 218190792Sgshapiro sm_strcasecmp(sendinghost, MyHostName) != 0)) 218238032Speter { 218338032Speter auth_warning(e, "Host %s claimed to be %s", 218490792Sgshapiro CurSmtpClient, sendinghost); 218538032Speter } 218690792Sgshapiro#endif /* PICKY_HELO_CHECK */ 218738032Speter 218838032Speter if (protocol == NULL) 218938032Speter protocol = "SMTP"; 219090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'r', protocol); 219190792Sgshapiro macdefine(&e->e_macro, A_PERM, 's', sendinghost); 219264562Sgshapiro 219338032Speter if (Errors > 0) 219490792Sgshapiro goto undo_no_pm; 219590792Sgshapiro smtp.sm_nrcpts = 0; 219690792Sgshapiro n_badrcpts = 0; 219790792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0"); 219890792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0"); 2199132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"), 2200132943Sgshapiro "0"); 220164562Sgshapiro e->e_flags |= EF_CLRQUEUE; 220290792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", 220364562Sgshapiro qid_printname(e), 220464562Sgshapiro CurSmtpClient, inp); 220538032Speter 220690792Sgshapiro /* do the processing */ 220790792Sgshapiro SM_TRY 220890792Sgshapiro { 220994334Sgshapiro extern char *FullName; 221094334Sgshapiro 221190792Sgshapiro QuickAbort = true; 221294334Sgshapiro SM_FREE_CLR(FullName); 221364562Sgshapiro 221438032Speter /* must parse sender first */ 221538032Speter delimptr = NULL; 221690792Sgshapiro setsender(p, e, &delimptr, ' ', false); 221738032Speter if (delimptr != NULL && *delimptr != '\0') 221838032Speter *delimptr++ = '\0'; 221938032Speter if (Errors > 0) 222090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 222138032Speter 222264562Sgshapiro /* Successfully set e_from, allow logging */ 222364562Sgshapiro e->e_flags |= EF_LOGSENDER; 222438032Speter 222564562Sgshapiro /* put resulting triple from parseaddr() into macros */ 222664562Sgshapiro if (e->e_from.q_mailer != NULL) 222790792Sgshapiro macdefine(&e->e_macro, A_PERM, 222890792Sgshapiro macid("{mail_mailer}"), 222990792Sgshapiro e->e_from.q_mailer->m_name); 223064562Sgshapiro else 223190792Sgshapiro macdefine(&e->e_macro, A_PERM, 223290792Sgshapiro macid("{mail_mailer}"), NULL); 223364562Sgshapiro if (e->e_from.q_host != NULL) 223490792Sgshapiro macdefine(&e->e_macro, A_PERM, 223590792Sgshapiro macid("{mail_host}"), 223690792Sgshapiro e->e_from.q_host); 223764562Sgshapiro else 223890792Sgshapiro macdefine(&e->e_macro, A_PERM, 223990792Sgshapiro macid("{mail_host}"), "localhost"); 224064562Sgshapiro if (e->e_from.q_user != NULL) 224190792Sgshapiro macdefine(&e->e_macro, A_PERM, 224290792Sgshapiro macid("{mail_addr}"), 224390792Sgshapiro e->e_from.q_user); 224464562Sgshapiro else 224590792Sgshapiro macdefine(&e->e_macro, A_PERM, 224690792Sgshapiro macid("{mail_addr}"), NULL); 224764562Sgshapiro if (Errors > 0) 224890792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 224964562Sgshapiro 225038032Speter /* check for possible spoofing */ 225138032Speter if (RealUid != 0 && OpMode == MD_SMTP && 225238032Speter !wordinclass(RealUserName, 't') && 225364562Sgshapiro (!bitnset(M_LOCALMAILER, 225464562Sgshapiro e->e_from.q_mailer->m_flags) || 225564562Sgshapiro strcmp(e->e_from.q_user, RealUserName) != 0)) 225638032Speter { 225738032Speter auth_warning(e, "%s owned process doing -bs", 225838032Speter RealUserName); 225938032Speter } 226038032Speter 2261120256Sgshapiro /* reset to default value */ 2262120256Sgshapiro SevenBitInput = save_sevenbitinput; 2263120256Sgshapiro 226438032Speter /* now parse ESMTP arguments */ 226538032Speter e->e_msgsize = 0; 226664562Sgshapiro addr = p; 226764562Sgshapiro argno = 0; 226864562Sgshapiro args[argno++] = p; 226938032Speter p = delimptr; 227038032Speter while (p != NULL && *p != '\0') 227138032Speter { 227238032Speter char *kp; 227338032Speter char *vp = NULL; 227464562Sgshapiro char *equal = NULL; 227538032Speter 227638032Speter /* locate the beginning of the keyword */ 227790792Sgshapiro SKIP_SPACE(p); 227838032Speter if (*p == '\0') 227938032Speter break; 228038032Speter kp = p; 228138032Speter 228238032Speter /* skip to the value portion */ 228338032Speter while ((isascii(*p) && isalnum(*p)) || *p == '-') 228438032Speter p++; 228538032Speter if (*p == '=') 228638032Speter { 228764562Sgshapiro equal = p; 228838032Speter *p++ = '\0'; 228938032Speter vp = p; 229038032Speter 229138032Speter /* skip to the end of the value */ 229238032Speter while (*p != '\0' && *p != ' ' && 229338032Speter !(isascii(*p) && iscntrl(*p)) && 229438032Speter *p != '=') 229538032Speter p++; 229638032Speter } 229738032Speter 229838032Speter if (*p != '\0') 229938032Speter *p++ = '\0'; 230038032Speter 230138032Speter if (tTd(19, 1)) 230290792Sgshapiro sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp, 230338032Speter vp == NULL ? "<null>" : vp); 230438032Speter 230538032Speter mail_esmtp_args(kp, vp, e); 230664562Sgshapiro if (equal != NULL) 230764562Sgshapiro *equal = '='; 230864562Sgshapiro args[argno++] = kp; 230964562Sgshapiro if (argno >= MAXSMTPARGS - 1) 231064562Sgshapiro usrerr("501 5.5.4 Too many parameters"); 231138032Speter if (Errors > 0) 231290792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 231338032Speter } 231464562Sgshapiro args[argno] = NULL; 231538032Speter if (Errors > 0) 231690792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 231738032Speter 231890792Sgshapiro#if SASL 231990792Sgshapiro# if _FFR_AUTH_PASSING 232090792Sgshapiro /* set the default AUTH= if the sender didn't */ 232190792Sgshapiro if (e->e_auth_param == NULL) 232290792Sgshapiro { 232390792Sgshapiro /* XXX only do this for an MSA? */ 232490792Sgshapiro e->e_auth_param = macvalue(macid("{auth_authen}"), 232590792Sgshapiro e); 232690792Sgshapiro if (e->e_auth_param == NULL) 232790792Sgshapiro e->e_auth_param = "<>"; 232890792Sgshapiro 232990792Sgshapiro /* 233090792Sgshapiro ** XXX should we invoke Strust_auth now? 233190792Sgshapiro ** authorizing as the client that just 233290792Sgshapiro ** authenticated, so we'll trust implicitly 233390792Sgshapiro */ 233490792Sgshapiro } 233590792Sgshapiro# endif /* _FFR_AUTH_PASSING */ 233690792Sgshapiro#endif /* SASL */ 233790792Sgshapiro 233864562Sgshapiro /* do config file checking of the sender */ 233990792Sgshapiro macdefine(&e->e_macro, A_PERM, 234090792Sgshapiro macid("{addr_type}"), "e s"); 234190792Sgshapiro#if _FFR_MAIL_MACRO 234290792Sgshapiro /* make the "real" sender address available */ 234390792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"), 234490792Sgshapiro e->e_from.q_paddr); 234590792Sgshapiro#endif /* _FFR_MAIL_MACRO */ 234664562Sgshapiro if (rscheck("check_mail", addr, 2347102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 3, 2348102528Sgshapiro NULL, e->e_id) != EX_OK || 234964562Sgshapiro Errors > 0) 235090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 235190792Sgshapiro macdefine(&e->e_macro, A_PERM, 235290792Sgshapiro macid("{addr_type}"), NULL); 235364562Sgshapiro 235466494Sgshapiro if (MaxMessageSize > 0 && 235577349Sgshapiro (e->e_msgsize > MaxMessageSize || 235677349Sgshapiro e->e_msgsize < 0)) 235738032Speter { 235864562Sgshapiro usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)", 235938032Speter MaxMessageSize); 236090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 236138032Speter } 236264562Sgshapiro 236390792Sgshapiro /* 236490792Sgshapiro ** XXX always check whether there is at least one fs 236590792Sgshapiro ** with enough space? 236690792Sgshapiro ** However, this may not help much: the queue group 236790792Sgshapiro ** selection may later on select a FS that hasn't 236890792Sgshapiro ** enough space. 236990792Sgshapiro */ 237090792Sgshapiro 237190792Sgshapiro if ((NumFileSys == 1 || NumQueue == 1) && 237290792Sgshapiro !enoughdiskspace(e->e_msgsize, e) 237390792Sgshapiro#if _FFR_ANY_FREE_FS 237490792Sgshapiro && !filesys_free(e->e_msgsize) 237590792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 237690792Sgshapiro ) 237738032Speter { 237890792Sgshapiro /* 237990792Sgshapiro ** We perform this test again when the 238090792Sgshapiro ** queue directory is selected, in collect. 238190792Sgshapiro */ 238290792Sgshapiro 238364562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 238490792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 238538032Speter } 238638032Speter if (Errors > 0) 238790792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 238864562Sgshapiro 238990792Sgshapiro LogUsrErrs = true; 239090792Sgshapiro#if MILTER 239190792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 239290792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 239364562Sgshapiro { 239464562Sgshapiro char state; 239564562Sgshapiro char *response; 239664562Sgshapiro 239764562Sgshapiro response = milter_envfrom(args, e, &state); 239890792Sgshapiro MILTER_REPLY("from"); 239964562Sgshapiro } 240090792Sgshapiro#endif /* MILTER */ 240164562Sgshapiro if (Errors > 0) 240290792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 240364562Sgshapiro 240464562Sgshapiro message("250 2.1.0 Sender ok"); 240590792Sgshapiro smtp.sm_gotmail = true; 240690792Sgshapiro } 240790792Sgshapiro SM_EXCEPT(exc, "[!F]*") 240890792Sgshapiro { 240990792Sgshapiro /* 241090792Sgshapiro ** An error occurred while processing a MAIL command. 241190792Sgshapiro ** Jump to the common error handling code. 241290792Sgshapiro */ 241390792Sgshapiro 241490792Sgshapiro sm_exc_free(exc); 241590792Sgshapiro goto undo_no_pm; 241690792Sgshapiro } 241790792Sgshapiro SM_END_TRY 241838032Speter break; 241938032Speter 242090792Sgshapiro undo_no_pm: 242190792Sgshapiro e->e_flags &= ~EF_PM_NOTIFY; 242290792Sgshapiro undo: 242390792Sgshapiro break; 242490792Sgshapiro 242538032Speter case CMDRCPT: /* rcpt -- designate recipient */ 242690792Sgshapiro DELAY_CONN("RCPT"); 2427125820Sgshapiro if (BadRcptThrottle > 0 && 2428125820Sgshapiro n_badrcpts >= BadRcptThrottle) 2429125820Sgshapiro { 2430125820Sgshapiro if (LogLevel > 5 && 2431125820Sgshapiro n_badrcpts == BadRcptThrottle) 2432125820Sgshapiro { 2433125820Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2434125820Sgshapiro "%s: Possible SMTP RCPT flood, throttling.", 2435125820Sgshapiro CurSmtpClient); 2436125820Sgshapiro 2437125820Sgshapiro /* To avoid duplicated message */ 2438125820Sgshapiro n_badrcpts++; 2439125820Sgshapiro } 2440132943Sgshapiro NBADRCPTS; 2441125820Sgshapiro 2442125820Sgshapiro /* 2443125820Sgshapiro ** Don't use exponential backoff for now. 2444125820Sgshapiro ** Some servers will open more connections 2445125820Sgshapiro ** and actually overload the receiver even 2446125820Sgshapiro ** more. 2447125820Sgshapiro */ 2448125820Sgshapiro 2449125820Sgshapiro (void) sleep(1); 2450125820Sgshapiro } 245190792Sgshapiro if (!smtp.sm_gotmail) 245238032Speter { 245364562Sgshapiro usrerr("503 5.0.0 Need MAIL before RCPT"); 245438032Speter break; 245538032Speter } 245638032Speter SmtpPhase = "server RCPT"; 245790792Sgshapiro SM_TRY 245890792Sgshapiro { 245990792Sgshapiro QuickAbort = true; 246090792Sgshapiro LogUsrErrs = true; 246138032Speter 246238032Speter /* limit flooding of our machine */ 246390792Sgshapiro if (MaxRcptPerMsg > 0 && 246490792Sgshapiro smtp.sm_nrcpts >= MaxRcptPerMsg) 246538032Speter { 246690792Sgshapiro /* sleep(1); / * slow down? */ 246764562Sgshapiro usrerr("452 4.5.3 Too many recipients"); 246890792Sgshapiro goto rcpt_done; 246938032Speter } 247038032Speter 247138032Speter if (e->e_sendmode != SM_DELIVER) 247238032Speter e->e_flags |= EF_VRFYONLY; 247338032Speter 247490792Sgshapiro#if MILTER 247564562Sgshapiro /* 247664562Sgshapiro ** If the filter will be deleting recipients, 247764562Sgshapiro ** don't expand them at RCPT time (in the call 247864562Sgshapiro ** to recipient()). If they are expanded, it 247964562Sgshapiro ** is impossible for removefromlist() to figure 248064562Sgshapiro ** out the expanded members of the original 248164562Sgshapiro ** recipient and mark them as QS_DONTSEND. 248264562Sgshapiro */ 248364562Sgshapiro 248464562Sgshapiro if (milter_can_delrcpts()) 248564562Sgshapiro e->e_flags |= EF_VRFYONLY; 248690792Sgshapiro#endif /* MILTER */ 248764562Sgshapiro 248838032Speter p = skipword(p, "to"); 248938032Speter if (p == NULL) 249090792Sgshapiro goto rcpt_done; 249190792Sgshapiro macdefine(&e->e_macro, A_PERM, 249290792Sgshapiro macid("{addr_type}"), "e r"); 249390792Sgshapiro a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, 249490792Sgshapiro e, true); 249590792Sgshapiro macdefine(&e->e_macro, A_PERM, 249690792Sgshapiro macid("{addr_type}"), NULL); 249742575Speter if (Errors > 0) 249890792Sgshapiro goto rcpt_done; 249942575Speter if (a == NULL) 250042575Speter { 250164562Sgshapiro usrerr("501 5.0.0 Missing recipient"); 250290792Sgshapiro goto rcpt_done; 250342575Speter } 250442575Speter 250538032Speter if (delimptr != NULL && *delimptr != '\0') 250638032Speter *delimptr++ = '\0'; 250738032Speter 250864562Sgshapiro /* put resulting triple from parseaddr() into macros */ 250964562Sgshapiro if (a->q_mailer != NULL) 251090792Sgshapiro macdefine(&e->e_macro, A_PERM, 251190792Sgshapiro macid("{rcpt_mailer}"), 251290792Sgshapiro a->q_mailer->m_name); 251364562Sgshapiro else 251490792Sgshapiro macdefine(&e->e_macro, A_PERM, 251590792Sgshapiro macid("{rcpt_mailer}"), NULL); 251664562Sgshapiro if (a->q_host != NULL) 251790792Sgshapiro macdefine(&e->e_macro, A_PERM, 251890792Sgshapiro macid("{rcpt_host}"), a->q_host); 251964562Sgshapiro else 252090792Sgshapiro macdefine(&e->e_macro, A_PERM, 252190792Sgshapiro macid("{rcpt_host}"), "localhost"); 252264562Sgshapiro if (a->q_user != NULL) 252390792Sgshapiro macdefine(&e->e_macro, A_PERM, 252490792Sgshapiro macid("{rcpt_addr}"), a->q_user); 252564562Sgshapiro else 252690792Sgshapiro macdefine(&e->e_macro, A_PERM, 252790792Sgshapiro macid("{rcpt_addr}"), NULL); 252864562Sgshapiro if (Errors > 0) 252990792Sgshapiro goto rcpt_done; 253038032Speter 253138032Speter /* now parse ESMTP arguments */ 253264562Sgshapiro addr = p; 253364562Sgshapiro argno = 0; 253464562Sgshapiro args[argno++] = p; 253538032Speter p = delimptr; 253638032Speter while (p != NULL && *p != '\0') 253738032Speter { 253838032Speter char *kp; 253938032Speter char *vp = NULL; 254064562Sgshapiro char *equal = NULL; 254138032Speter 254238032Speter /* locate the beginning of the keyword */ 254390792Sgshapiro SKIP_SPACE(p); 254438032Speter if (*p == '\0') 254538032Speter break; 254638032Speter kp = p; 254738032Speter 254838032Speter /* skip to the value portion */ 254938032Speter while ((isascii(*p) && isalnum(*p)) || *p == '-') 255038032Speter p++; 255138032Speter if (*p == '=') 255238032Speter { 255364562Sgshapiro equal = p; 255438032Speter *p++ = '\0'; 255538032Speter vp = p; 255638032Speter 255738032Speter /* skip to the end of the value */ 255838032Speter while (*p != '\0' && *p != ' ' && 255938032Speter !(isascii(*p) && iscntrl(*p)) && 256038032Speter *p != '=') 256138032Speter p++; 256238032Speter } 256338032Speter 256438032Speter if (*p != '\0') 256538032Speter *p++ = '\0'; 256638032Speter 256738032Speter if (tTd(19, 1)) 256890792Sgshapiro sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp, 256938032Speter vp == NULL ? "<null>" : vp); 257038032Speter 257138032Speter rcpt_esmtp_args(a, kp, vp, e); 257264562Sgshapiro if (equal != NULL) 257364562Sgshapiro *equal = '='; 257464562Sgshapiro args[argno++] = kp; 257564562Sgshapiro if (argno >= MAXSMTPARGS - 1) 257664562Sgshapiro usrerr("501 5.5.4 Too many parameters"); 257738032Speter if (Errors > 0) 257838032Speter break; 257938032Speter } 258064562Sgshapiro args[argno] = NULL; 258138032Speter if (Errors > 0) 258290792Sgshapiro goto rcpt_done; 258338032Speter 258464562Sgshapiro /* do config file checking of the recipient */ 258590792Sgshapiro macdefine(&e->e_macro, A_PERM, 258690792Sgshapiro macid("{addr_type}"), "e r"); 258764562Sgshapiro if (rscheck("check_rcpt", addr, 2588102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 3, 2589102528Sgshapiro NULL, e->e_id) != EX_OK || 259064562Sgshapiro Errors > 0) 259190792Sgshapiro goto rcpt_done; 259290792Sgshapiro macdefine(&e->e_macro, A_PERM, 259390792Sgshapiro macid("{addr_type}"), NULL); 259464562Sgshapiro 2595102528Sgshapiro /* If discarding, don't bother to verify user */ 2596102528Sgshapiro if (bitset(EF_DISCARD, e->e_flags)) 2597102528Sgshapiro a->q_state = QS_VERIFIED; 2598102528Sgshapiro 259990792Sgshapiro#if MILTER 260090792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 260190792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 260264562Sgshapiro { 260364562Sgshapiro char state; 260464562Sgshapiro char *response; 260564562Sgshapiro 260664562Sgshapiro response = milter_envrcpt(args, e, &state); 260790792Sgshapiro MILTER_REPLY("to"); 260864562Sgshapiro } 260990792Sgshapiro#endif /* MILTER */ 261064562Sgshapiro 261190792Sgshapiro macdefine(&e->e_macro, A_PERM, 261290792Sgshapiro macid("{rcpt_mailer}"), NULL); 261390792Sgshapiro macdefine(&e->e_macro, A_PERM, 261498121Sgshapiro macid("{rcpt_host}"), NULL); 261590792Sgshapiro macdefine(&e->e_macro, A_PERM, 261690792Sgshapiro macid("{rcpt_addr}"), NULL); 261790792Sgshapiro macdefine(&e->e_macro, A_PERM, 261890792Sgshapiro macid("{dsn_notify}"), NULL); 261964562Sgshapiro if (Errors > 0) 262090792Sgshapiro goto rcpt_done; 262164562Sgshapiro 262238032Speter /* save in recipient list after ESMTP mods */ 262338032Speter a = recipient(a, &e->e_sendqueue, 0, e); 262438032Speter if (Errors > 0) 262590792Sgshapiro goto rcpt_done; 262638032Speter 262738032Speter /* no errors during parsing, but might be a duplicate */ 262838032Speter e->e_to = a->q_paddr; 262964562Sgshapiro if (!QS_IS_BADADDR(a->q_state)) 263038032Speter { 263190792Sgshapiro if (smtp.sm_nrcpts == 0) 263264562Sgshapiro initsys(e); 263364562Sgshapiro message("250 2.1.5 Recipient ok%s", 263464562Sgshapiro QS_IS_QUEUEUP(a->q_state) ? 263538032Speter " (will queue)" : ""); 263690792Sgshapiro smtp.sm_nrcpts++; 263738032Speter } 263838032Speter else 263938032Speter { 264038032Speter /* punt -- should keep message in ADDRESS.... */ 264164562Sgshapiro usrerr("550 5.1.1 Addressee unknown"); 264238032Speter } 264390792Sgshapiro rcpt_done: 264490792Sgshapiro if (Errors > 0) 2645132943Sgshapiro { 264690792Sgshapiro ++n_badrcpts; 2647132943Sgshapiro NBADRCPTS; 2648132943Sgshapiro } 264990792Sgshapiro } 265090792Sgshapiro SM_EXCEPT(exc, "[!F]*") 265190792Sgshapiro { 265290792Sgshapiro /* An exception occurred while processing RCPT */ 265390792Sgshapiro e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY); 265490792Sgshapiro ++n_badrcpts; 2655132943Sgshapiro NBADRCPTS; 265690792Sgshapiro } 265790792Sgshapiro SM_END_TRY 265838032Speter break; 265938032Speter 266038032Speter case CMDDATA: /* data -- text of mail */ 266190792Sgshapiro DELAY_CONN("DATA"); 2662132943Sgshapiro if (!smtp_data(&smtp, e)) 2663132943Sgshapiro goto doquit; 266438032Speter break; 266538032Speter 266638032Speter case CMDRSET: /* rset -- reset state */ 266738032Speter if (tTd(94, 100)) 266864562Sgshapiro message("451 4.0.0 Test failure"); 266938032Speter else 267064562Sgshapiro message("250 2.0.0 Reset state"); 267190792Sgshapiro CLEAR_STATE(cmdbuf); 267238032Speter break; 267338032Speter 267438032Speter case CMDVRFY: /* vrfy -- verify address */ 267538032Speter case CMDEXPN: /* expn -- expand address */ 267690792Sgshapiro vrfy = c->cmd_code == CMDVRFY; 267790792Sgshapiro DELAY_CONN(vrfy ? "VRFY" : "EXPN"); 267864562Sgshapiro if (tempfail) 267964562Sgshapiro { 268064562Sgshapiro if (LogLevel > 9) 268164562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2682110560Sgshapiro "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)", 268390792Sgshapiro vrfy ? "VRFY" : "EXPN", 268464562Sgshapiro p, CurSmtpClient); 268590792Sgshapiro 268690792Sgshapiro /* RFC 821 doesn't allow 4xy reply code */ 268764562Sgshapiro usrerr("550 5.7.1 Please try again later"); 268864562Sgshapiro break; 268964562Sgshapiro } 269090792Sgshapiro wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS, 269190792Sgshapiro false, vrfy ? "VRFY" : "EXPN", e); 2692132943Sgshapiro STOP_IF_ATTACK(wt); 269364562Sgshapiro previous = curtime(); 2694110560Sgshapiro if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) || 2695110560Sgshapiro (!vrfy && !bitset(SRV_OFFER_EXPN, features))) 269638032Speter { 269738032Speter if (vrfy) 269864562Sgshapiro message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 269938032Speter else 270064562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 270138032Speter if (LogLevel > 5) 270238032Speter sm_syslog(LOG_INFO, e->e_id, 2703110560Sgshapiro "%s: %s [rejected]", 270464562Sgshapiro CurSmtpClient, 270564562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 270638032Speter break; 270738032Speter } 270838032Speter else if (!gothello && 270938032Speter bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 271038032Speter PrivacyFlags)) 271138032Speter { 271264562Sgshapiro usrerr("503 5.0.0 I demand that you introduce yourself first"); 271338032Speter break; 271438032Speter } 271590792Sgshapiro if (Errors > 0) 271638032Speter break; 271738032Speter if (LogLevel > 5) 2718110560Sgshapiro sm_syslog(LOG_INFO, e->e_id, "%s: %s", 271964562Sgshapiro CurSmtpClient, 272064562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 272190792Sgshapiro SM_TRY 272290792Sgshapiro { 272390792Sgshapiro QuickAbort = true; 272438032Speter vrfyqueue = NULL; 272538032Speter if (vrfy) 272638032Speter e->e_flags |= EF_VRFYONLY; 272738032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 272838032Speter p++; 272938032Speter if (*p == '\0') 273038032Speter { 273164562Sgshapiro usrerr("501 5.5.2 Argument required"); 273238032Speter } 273338032Speter else 273438032Speter { 273564562Sgshapiro /* do config file checking of the address */ 273664562Sgshapiro if (rscheck(vrfy ? "check_vrfy" : "check_expn", 2737102528Sgshapiro p, NULL, e, RSF_RMCOMM, 2738102528Sgshapiro 3, NULL, NOQID) != EX_OK || 273990792Sgshapiro Errors > 0) 274090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 274138032Speter (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 274238032Speter } 274364562Sgshapiro if (wt > 0) 274471345Sgshapiro { 274571345Sgshapiro time_t t; 274671345Sgshapiro 274771345Sgshapiro t = wt - (curtime() - previous); 274871345Sgshapiro if (t > 0) 274971345Sgshapiro (void) sleep(t); 275071345Sgshapiro } 275138032Speter if (Errors > 0) 275290792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 275338032Speter if (vrfyqueue == NULL) 275438032Speter { 275564562Sgshapiro usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 275638032Speter } 275738032Speter while (vrfyqueue != NULL) 275838032Speter { 275964562Sgshapiro if (!QS_IS_UNDELIVERED(vrfyqueue->q_state)) 276064562Sgshapiro { 276164562Sgshapiro vrfyqueue = vrfyqueue->q_next; 276264562Sgshapiro continue; 276364562Sgshapiro } 276438032Speter 276564562Sgshapiro /* see if there is more in the vrfy list */ 276638032Speter a = vrfyqueue; 276738032Speter while ((a = a->q_next) != NULL && 276866494Sgshapiro (!QS_IS_UNDELIVERED(a->q_state))) 276938032Speter continue; 277064562Sgshapiro printvrfyaddr(vrfyqueue, a == NULL, vrfy); 277164562Sgshapiro vrfyqueue = a; 277238032Speter } 277390792Sgshapiro } 277490792Sgshapiro SM_EXCEPT(exc, "[!F]*") 277590792Sgshapiro { 277690792Sgshapiro /* 277790792Sgshapiro ** An exception occurred while processing VRFY/EXPN 277890792Sgshapiro */ 277990792Sgshapiro 278090792Sgshapiro sm_exc_free(exc); 278190792Sgshapiro goto undo; 278290792Sgshapiro } 278390792Sgshapiro SM_END_TRY 278438032Speter break; 278538032Speter 278638032Speter case CMDETRN: /* etrn -- force queue flush */ 278790792Sgshapiro DELAY_CONN("ETRN"); 278890792Sgshapiro 278990792Sgshapiro /* Don't leak queue information via debug flags */ 279090792Sgshapiro if (!bitset(SRV_OFFER_ETRN, features) || UseMSP || 279190792Sgshapiro (RealUid != 0 && RealUid != TrustedUid && 279290792Sgshapiro OpMode == MD_SMTP)) 279338032Speter { 279464562Sgshapiro /* different message for MSA ? */ 279564562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 279638032Speter if (LogLevel > 5) 279738032Speter sm_syslog(LOG_INFO, e->e_id, 2798110560Sgshapiro "%s: %s [rejected]", 279964562Sgshapiro CurSmtpClient, 280064562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 280138032Speter break; 280238032Speter } 280364562Sgshapiro if (tempfail) 280464562Sgshapiro { 280564562Sgshapiro if (LogLevel > 9) 280664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2807110560Sgshapiro "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)", 280864562Sgshapiro p, CurSmtpClient); 280990792Sgshapiro usrerr(MSG_TEMPFAIL); 281064562Sgshapiro break; 281164562Sgshapiro } 281238032Speter 281338032Speter if (strlen(p) <= 0) 281438032Speter { 281564562Sgshapiro usrerr("500 5.5.2 Parameter required"); 281638032Speter break; 281738032Speter } 281838032Speter 281938032Speter /* crude way to avoid denial-of-service attacks */ 2820132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS, 2821132943Sgshapiro true, "ETRN", e)); 282238032Speter 282390792Sgshapiro /* 282490792Sgshapiro ** Do config file checking of the parameter. 282590792Sgshapiro ** Even though we have srv_features now, we still 282690792Sgshapiro ** need this ruleset because the former is called 282790792Sgshapiro ** when the connection has been established, while 282890792Sgshapiro ** this ruleset is called when the command is 282990792Sgshapiro ** actually issued and therefore has all information 283090792Sgshapiro ** available to make a decision. 283190792Sgshapiro */ 283290792Sgshapiro 2833102528Sgshapiro if (rscheck("check_etrn", p, NULL, e, 2834102528Sgshapiro RSF_RMCOMM, 3, NULL, NOQID) != EX_OK || 2835102528Sgshapiro Errors > 0) 283664562Sgshapiro break; 283764562Sgshapiro 283838032Speter if (LogLevel > 5) 283938032Speter sm_syslog(LOG_INFO, e->e_id, 2840110560Sgshapiro "%s: ETRN %s", CurSmtpClient, 284164562Sgshapiro shortenstring(p, MAXSHORTSTR)); 284238032Speter 284338032Speter id = p; 284490792Sgshapiro if (*id == '#') 284590792Sgshapiro { 2846111823Sgshapiro int i, qgrp; 284790792Sgshapiro 284890792Sgshapiro id++; 2849111823Sgshapiro qgrp = name2qid(id); 2850111823Sgshapiro if (!ISVALIDQGRP(qgrp)) 285190792Sgshapiro { 285290792Sgshapiro usrerr("459 4.5.4 Queue %s unknown", 285390792Sgshapiro id); 285490792Sgshapiro break; 285590792Sgshapiro } 2856111823Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; 2857111823Sgshapiro i++) 2858111823Sgshapiro Queue[i]->qg_nextrun = (time_t) -1; 2859111823Sgshapiro Queue[qgrp]->qg_nextrun = 0; 2860111823Sgshapiro ok = run_work_group(Queue[qgrp]->qg_wgrp, 2861111823Sgshapiro RWG_FORK|RWG_FORCE); 286290792Sgshapiro if (ok && Errors == 0) 286390792Sgshapiro message("250 2.0.0 Queuing for queue group %s started", id); 286490792Sgshapiro break; 286590792Sgshapiro } 286690792Sgshapiro 286738032Speter if (*id == '@') 286838032Speter id++; 286938032Speter else 287038032Speter *--id = '@'; 287164562Sgshapiro 287290792Sgshapiro new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR)); 287390792Sgshapiro if (new == NULL) 287490792Sgshapiro { 287590792Sgshapiro syserr("500 5.5.0 ETRN out of memory"); 287690792Sgshapiro break; 287790792Sgshapiro } 287838032Speter new->queue_match = id; 287990792Sgshapiro new->queue_negate = false; 288038032Speter new->queue_next = NULL; 288138032Speter QueueLimitRecipient = new; 288290792Sgshapiro ok = runqueue(true, false, false, true); 288390792Sgshapiro sm_free(QueueLimitRecipient); /* XXX */ 288438032Speter QueueLimitRecipient = NULL; 288538032Speter if (ok && Errors == 0) 288664562Sgshapiro message("250 2.0.0 Queuing for node %s started", p); 288738032Speter break; 288838032Speter 288938032Speter case CMDHELP: /* help -- give user info */ 289090792Sgshapiro DELAY_CONN("HELP"); 289164562Sgshapiro help(p, e); 289238032Speter break; 289338032Speter 289438032Speter case CMDNOOP: /* noop -- do nothing */ 289590792Sgshapiro DELAY_CONN("NOOP"); 2896132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS, 2897132943Sgshapiro true, "NOOP", e)); 289864562Sgshapiro message("250 2.0.0 OK"); 289938032Speter break; 290038032Speter 290138032Speter case CMDQUIT: /* quit -- leave mail */ 290264562Sgshapiro message("221 2.0.0 %s closing connection", MyHostName); 290390792Sgshapiro#if PIPELINING 290490792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 290590792Sgshapiro#endif /* PIPELINING */ 290638032Speter 290790792Sgshapiro if (smtp.sm_nrcpts > 0) 290890792Sgshapiro logundelrcpts(e, "aborted by sender", 9, false); 290990792Sgshapiro 291038032Speter /* arrange to ignore any current send list */ 291138032Speter e->e_sendqueue = NULL; 291238032Speter 291390792Sgshapiro#if STARTTLS 291464562Sgshapiro /* shutdown TLS connection */ 291564562Sgshapiro if (tls_active) 291664562Sgshapiro { 291764562Sgshapiro (void) endtls(srv_ssl, "server"); 291890792Sgshapiro tls_active = false; 291964562Sgshapiro } 292090792Sgshapiro#endif /* STARTTLS */ 292190792Sgshapiro#if SASL 292264562Sgshapiro if (authenticating == SASL_IS_AUTH) 292364562Sgshapiro { 292464562Sgshapiro sasl_dispose(&conn); 292564562Sgshapiro authenticating = SASL_NOT_AUTH; 292690792Sgshapiro /* XXX sasl_done(); this is a child */ 292764562Sgshapiro } 292890792Sgshapiro#endif /* SASL */ 292964562Sgshapiro 293064562Sgshapirodoquit: 293138032Speter /* avoid future 050 messages */ 293238032Speter disconnect(1, e); 293338032Speter 293490792Sgshapiro#if MILTER 293564562Sgshapiro /* close out milter filters */ 293664562Sgshapiro milter_quit(e); 293790792Sgshapiro#endif /* MILTER */ 293864562Sgshapiro 293964562Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 294064562Sgshapiro logsender(e, NULL); 294164562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 294264562Sgshapiro 294398121Sgshapiro if (lognullconnection && LogLevel > 5 && 294498121Sgshapiro nullserver == NULL) 294564562Sgshapiro { 294664562Sgshapiro char *d; 294764562Sgshapiro 294890792Sgshapiro d = macvalue(macid("{daemon_name}"), e); 294964562Sgshapiro if (d == NULL) 295064562Sgshapiro d = "stdin"; 295190792Sgshapiro 295290792Sgshapiro /* 295390792Sgshapiro ** even though this id is "bogus", it makes 295490792Sgshapiro ** it simpler to "grep" related events, e.g., 295590792Sgshapiro ** timeouts for the same connection. 295690792Sgshapiro */ 295790792Sgshapiro 295890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2959110560Sgshapiro "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s", 296064562Sgshapiro CurSmtpClient, d); 296164562Sgshapiro } 2962110560Sgshapiro if (tTd(93, 100)) 2963110560Sgshapiro { 2964110560Sgshapiro /* return to handle next connection */ 2965110560Sgshapiro return; 2966110560Sgshapiro } 296790792Sgshapiro finis(true, true, ExitStat); 296864562Sgshapiro /* NOTREACHED */ 296938032Speter 297038032Speter case CMDVERB: /* set verbose mode */ 297190792Sgshapiro DELAY_CONN("VERB"); 2972110560Sgshapiro if (!bitset(SRV_OFFER_EXPN, features) || 2973110560Sgshapiro !bitset(SRV_OFFER_VERB, features)) 297438032Speter { 297538032Speter /* this would give out the same info */ 297664562Sgshapiro message("502 5.7.0 Verbose unavailable"); 297738032Speter break; 297838032Speter } 2979132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS, 2980132943Sgshapiro true, "VERB", e)); 298138032Speter Verbose = 1; 298264562Sgshapiro set_delivery_mode(SM_DELIVER, e); 298364562Sgshapiro message("250 2.0.0 Verbose mode"); 298438032Speter break; 298538032Speter 298690792Sgshapiro#if SMTPDEBUG 298738032Speter case CMDDBGQSHOW: /* show queues */ 298890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 298990792Sgshapiro "Send Queue="); 2990132943Sgshapiro printaddr(smioout, e->e_sendqueue, true); 299138032Speter break; 299238032Speter 299338032Speter case CMDDBGDEBUG: /* set debug mode */ 299438032Speter tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 299538032Speter tTflag(p); 299664562Sgshapiro message("200 2.0.0 Debug set"); 299738032Speter break; 299838032Speter 299990792Sgshapiro#else /* SMTPDEBUG */ 300038032Speter case CMDDBGQSHOW: /* show queues */ 300138032Speter case CMDDBGDEBUG: /* set debug mode */ 300290792Sgshapiro#endif /* SMTPDEBUG */ 300338032Speter case CMDLOGBOGUS: /* bogus command */ 300490792Sgshapiro DELAY_CONN("Bogus"); 300538032Speter if (LogLevel > 0) 300638032Speter sm_syslog(LOG_CRIT, e->e_id, 3007110560Sgshapiro "\"%s\" command from %s (%.100s)", 300864562Sgshapiro c->cmd_name, CurSmtpClient, 300964562Sgshapiro anynet_ntoa(&RealHostAddr)); 301064562Sgshapiro /* FALLTHROUGH */ 301138032Speter 301238032Speter case CMDERROR: /* unknown command */ 301390792Sgshapiro#if MAXBADCOMMANDS > 0 301490792Sgshapiro if (++n_badcmds > MAXBADCOMMANDS) 301538032Speter { 3016132943Sgshapiro stopattack: 301764562Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 301838032Speter MyHostName); 301964562Sgshapiro 302064562Sgshapiro /* arrange to ignore any current send list */ 302164562Sgshapiro e->e_sendqueue = NULL; 302238032Speter goto doquit; 302338032Speter } 302490792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 302538032Speter 3026132943Sgshapiro#if MILTER && SMFI_VERSION > 2 3027132943Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 3028132943Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 3029132943Sgshapiro { 3030132943Sgshapiro char state; 3031132943Sgshapiro char *response; 3032132943Sgshapiro 3033132943Sgshapiro if (MilterLogLevel > 9) 3034132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3035132943Sgshapiro "Sending \"%s\" to Milter", inp); 3036132943Sgshapiro response = milter_unknown(inp, e, &state); 3037132943Sgshapiro MILTER_REPLY("unknown"); 3038132943Sgshapiro if (state == SMFIR_REPLYCODE || 3039132943Sgshapiro state == SMFIR_REJECT || 3040132943Sgshapiro state == SMFIR_TEMPFAIL) 3041132943Sgshapiro { 3042132943Sgshapiro /* MILTER_REPLY already gave an error */ 3043132943Sgshapiro break; 3044132943Sgshapiro } 3045132943Sgshapiro } 3046132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 2 */ 3047132943Sgshapiro 304864562Sgshapiro usrerr("500 5.5.1 Command unrecognized: \"%s\"", 304964562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 305038032Speter break; 305138032Speter 305264562Sgshapiro case CMDUNIMPL: 305390792Sgshapiro DELAY_CONN("Unimpl"); 305464562Sgshapiro usrerr("502 5.5.1 Command not implemented: \"%s\"", 305564562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 305664562Sgshapiro break; 305764562Sgshapiro 305838032Speter default: 305990792Sgshapiro DELAY_CONN("default"); 306038032Speter errno = 0; 306164562Sgshapiro syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code); 306238032Speter break; 306338032Speter } 306490792Sgshapiro#if SASL 306564562Sgshapiro } 306690792Sgshapiro#endif /* SASL */ 306790792Sgshapiro } 306890792Sgshapiro SM_EXCEPT(exc, "[!F]*") 306990792Sgshapiro { 307090792Sgshapiro /* 307190792Sgshapiro ** The only possible exception is "E:mta.quickabort". 307290792Sgshapiro ** There is nothing to do except fall through and loop. 307390792Sgshapiro */ 307490792Sgshapiro } 307590792Sgshapiro SM_END_TRY 307638032Speter } 307790792Sgshapiro} 307890792Sgshapiro/* 307990792Sgshapiro** SMTP_DATA -- implement the SMTP DATA command. 308090792Sgshapiro** 308190792Sgshapiro** Parameters: 308290792Sgshapiro** smtp -- status of SMTP connection. 308390792Sgshapiro** e -- envelope. 308490792Sgshapiro** 308590792Sgshapiro** Returns: 3086132943Sgshapiro** true iff SMTP session can continue. 308790792Sgshapiro** 308890792Sgshapiro** Side Effects: 308990792Sgshapiro** possibly sends message. 309090792Sgshapiro*/ 309164562Sgshapiro 3092132943Sgshapirostatic bool 309390792Sgshapirosmtp_data(smtp, e) 309490792Sgshapiro SMTP_T *smtp; 309590792Sgshapiro ENVELOPE *e; 309690792Sgshapiro{ 309790792Sgshapiro#if MILTER 309890792Sgshapiro bool milteraccept; 309990792Sgshapiro#endif /* MILTER */ 310090792Sgshapiro bool aborting; 310190792Sgshapiro bool doublequeue; 310290792Sgshapiro ADDRESS *a; 310390792Sgshapiro ENVELOPE *ee; 310490792Sgshapiro char *id; 310598121Sgshapiro char *oldid; 310690792Sgshapiro char buf[32]; 310790792Sgshapiro 310890792Sgshapiro SmtpPhase = "server DATA"; 310990792Sgshapiro if (!smtp->sm_gotmail) 311090792Sgshapiro { 311190792Sgshapiro usrerr("503 5.0.0 Need MAIL command"); 3112132943Sgshapiro return true; 311390792Sgshapiro } 311490792Sgshapiro else if (smtp->sm_nrcpts <= 0) 311590792Sgshapiro { 311690792Sgshapiro usrerr("503 5.0.0 Need RCPT (recipient)"); 3117132943Sgshapiro return true; 311890792Sgshapiro } 311990792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts); 312090792Sgshapiro if (rscheck("check_data", buf, NULL, e, 3121102528Sgshapiro RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL, 3122102528Sgshapiro e->e_id) != EX_OK) 3123132943Sgshapiro return true; 312490792Sgshapiro 3125132943Sgshapiro#if MILTER && SMFI_VERSION > 3 3126132943Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize && 3127132943Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 3128132943Sgshapiro { 3129132943Sgshapiro char state; 3130132943Sgshapiro char *response; 3131132943Sgshapiro int savelogusrerrs = LogUsrErrs; 3132132943Sgshapiro 3133132943Sgshapiro response = milter_data_cmd(e, &state); 3134132943Sgshapiro switch (state) 3135132943Sgshapiro { 3136132943Sgshapiro case SMFIR_REPLYCODE: 3137132943Sgshapiro if (MilterLogLevel > 3) 3138132943Sgshapiro { 3139132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3140132943Sgshapiro "Milter: cmd=data, reject=%s", 3141132943Sgshapiro response); 3142132943Sgshapiro LogUsrErrs = false; 3143132943Sgshapiro } 3144132943Sgshapiro usrerr(response); 3145132943Sgshapiro if (strncmp(response, "421 ", 4) == 0) 3146132943Sgshapiro { 3147132943Sgshapiro e->e_sendqueue = NULL; 3148132943Sgshapiro return false; 3149132943Sgshapiro } 3150132943Sgshapiro return true; 3151132943Sgshapiro 3152132943Sgshapiro case SMFIR_REJECT: 3153132943Sgshapiro if (MilterLogLevel > 3) 3154132943Sgshapiro { 3155132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3156132943Sgshapiro "Milter: cmd=data, reject=550 5.7.1 Command rejected"); 3157132943Sgshapiro LogUsrErrs = false; 3158132943Sgshapiro } 3159132943Sgshapiro usrerr("550 5.7.1 Command rejected"); 3160132943Sgshapiro return true; 3161132943Sgshapiro 3162132943Sgshapiro case SMFIR_DISCARD: 3163132943Sgshapiro if (MilterLogLevel > 3) 3164132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3165132943Sgshapiro "Milter: cmd=data, discard"); 3166132943Sgshapiro e->e_flags |= EF_DISCARD; 3167132943Sgshapiro break; 3168132943Sgshapiro 3169132943Sgshapiro case SMFIR_TEMPFAIL: 3170132943Sgshapiro if (MilterLogLevel > 3) 3171132943Sgshapiro { 3172132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3173132943Sgshapiro "Milter: cmd=data, reject=%s", 3174132943Sgshapiro MSG_TEMPFAIL); 3175132943Sgshapiro LogUsrErrs = false; 3176132943Sgshapiro } 3177132943Sgshapiro usrerr(MSG_TEMPFAIL); 3178132943Sgshapiro return true; 3179132943Sgshapiro } 3180132943Sgshapiro LogUsrErrs = savelogusrerrs; 3181132943Sgshapiro if (response != NULL) 3182132943Sgshapiro sm_free(response); /* XXX */ 3183132943Sgshapiro } 3184132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 3 */ 3185132943Sgshapiro 318690792Sgshapiro /* put back discard bit */ 318790792Sgshapiro if (smtp->sm_discard) 318890792Sgshapiro e->e_flags |= EF_DISCARD; 318990792Sgshapiro 319090792Sgshapiro /* check to see if we need to re-expand aliases */ 319190792Sgshapiro /* also reset QS_BADADDR on already-diagnosted addrs */ 319290792Sgshapiro doublequeue = false; 319390792Sgshapiro for (a = e->e_sendqueue; a != NULL; a = a->q_next) 319490792Sgshapiro { 319590792Sgshapiro if (QS_IS_VERIFIED(a->q_state) && 319690792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 319790792Sgshapiro { 319890792Sgshapiro /* need to re-expand aliases */ 319990792Sgshapiro doublequeue = true; 320090792Sgshapiro } 320190792Sgshapiro if (QS_IS_BADADDR(a->q_state)) 320290792Sgshapiro { 320390792Sgshapiro /* make this "go away" */ 320490792Sgshapiro a->q_state = QS_DONTSEND; 320590792Sgshapiro } 320690792Sgshapiro } 320790792Sgshapiro 320890792Sgshapiro /* collect the text of the message */ 320990792Sgshapiro SmtpPhase = "collect"; 321090792Sgshapiro buffer_errors(); 321190792Sgshapiro 3212120256Sgshapiro collect(InChannel, true, NULL, e, true); 321390792Sgshapiro 321490792Sgshapiro /* redefine message size */ 321590792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize); 321690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); 321790792Sgshapiro 321890792Sgshapiro#if _FFR_CHECK_EOM 321990792Sgshapiro /* rscheck() will set Errors or EF_DISCARD if it trips */ 3220102528Sgshapiro (void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT, 3221102528Sgshapiro 3, NULL, e->e_id); 322290792Sgshapiro#endif /* _FFR_CHECK_EOM */ 322390792Sgshapiro 322490792Sgshapiro#if MILTER 322590792Sgshapiro milteraccept = true; 322690792Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize && 322790792Sgshapiro Errors <= 0 && 322890792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 322990792Sgshapiro { 323090792Sgshapiro char state; 323190792Sgshapiro char *response; 323290792Sgshapiro 323390792Sgshapiro response = milter_data(e, &state); 323490792Sgshapiro switch (state) 323590792Sgshapiro { 323690792Sgshapiro case SMFIR_REPLYCODE: 323790792Sgshapiro if (MilterLogLevel > 3) 323890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 323990792Sgshapiro "Milter: data, reject=%s", 324090792Sgshapiro response); 324190792Sgshapiro milteraccept = false; 324290792Sgshapiro usrerr(response); 324390792Sgshapiro break; 324490792Sgshapiro 324590792Sgshapiro case SMFIR_REJECT: 324690792Sgshapiro milteraccept = false; 324790792Sgshapiro if (MilterLogLevel > 3) 324890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 324990792Sgshapiro "Milter: data, reject=554 5.7.1 Command rejected"); 325090792Sgshapiro usrerr("554 5.7.1 Command rejected"); 325190792Sgshapiro break; 325290792Sgshapiro 325390792Sgshapiro case SMFIR_DISCARD: 325490792Sgshapiro if (MilterLogLevel > 3) 325590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 325690792Sgshapiro "Milter: data, discard"); 325790792Sgshapiro milteraccept = false; 325890792Sgshapiro e->e_flags |= EF_DISCARD; 325990792Sgshapiro break; 326090792Sgshapiro 326190792Sgshapiro case SMFIR_TEMPFAIL: 326290792Sgshapiro if (MilterLogLevel > 3) 326390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 326490792Sgshapiro "Milter: data, reject=%s", 326590792Sgshapiro MSG_TEMPFAIL); 326690792Sgshapiro milteraccept = false; 326790792Sgshapiro usrerr(MSG_TEMPFAIL); 326890792Sgshapiro break; 326990792Sgshapiro } 327090792Sgshapiro if (response != NULL) 327190792Sgshapiro sm_free(response); 327290792Sgshapiro } 327390792Sgshapiro 327490792Sgshapiro /* Milter may have changed message size */ 327590792Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize); 327690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); 327790792Sgshapiro 327890792Sgshapiro /* abort message filters that didn't get the body & log msg is OK */ 327990792Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize) 328090792Sgshapiro { 328190792Sgshapiro milter_abort(e); 328290792Sgshapiro if (milteraccept && MilterLogLevel > 9) 328390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter accept: message"); 328490792Sgshapiro } 3285132943Sgshapiro 3286132943Sgshapiro /* 3287132943Sgshapiro ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or 3288132943Sgshapiro ** milter accepted message, sync it now 3289132943Sgshapiro ** 3290132943Sgshapiro ** XXX This is almost a copy of the code in collect(): put it into 3291132943Sgshapiro ** a function that is called from both places? 3292132943Sgshapiro */ 3293132943Sgshapiro 3294132943Sgshapiro if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER) 3295132943Sgshapiro { 3296132943Sgshapiro int afd; 3297132943Sgshapiro SM_FILE_T *volatile df; 3298132943Sgshapiro char *dfname; 3299132943Sgshapiro 3300132943Sgshapiro df = e->e_dfp; 3301132943Sgshapiro dfname = queuename(e, DATAFL_LETTER); 3302132943Sgshapiro if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 3303132943Sgshapiro && errno != EINVAL) 3304132943Sgshapiro { 3305132943Sgshapiro int save_errno; 3306132943Sgshapiro 3307132943Sgshapiro save_errno = errno; 3308132943Sgshapiro if (save_errno == EEXIST) 3309132943Sgshapiro { 3310132943Sgshapiro struct stat st; 3311132943Sgshapiro int dfd; 3312132943Sgshapiro 3313132943Sgshapiro if (stat(dfname, &st) < 0) 3314132943Sgshapiro st.st_size = -1; 3315132943Sgshapiro errno = EEXIST; 3316132943Sgshapiro syserr("@collect: bfcommit(%s): already on disk, size=%ld", 3317132943Sgshapiro dfname, (long) st.st_size); 3318132943Sgshapiro dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL); 3319132943Sgshapiro if (dfd >= 0) 3320132943Sgshapiro dumpfd(dfd, true, true); 3321132943Sgshapiro } 3322132943Sgshapiro errno = save_errno; 3323132943Sgshapiro dferror(df, "bfcommit", e); 3324132943Sgshapiro flush_errors(true); 3325132943Sgshapiro finis(save_errno != EEXIST, true, ExitStat); 3326132943Sgshapiro } 3327132943Sgshapiro else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0) 3328132943Sgshapiro { 3329132943Sgshapiro dferror(df, "sm_io_getinfo", e); 3330132943Sgshapiro flush_errors(true); 3331132943Sgshapiro finis(true, true, ExitStat); 3332132943Sgshapiro /* NOTREACHED */ 3333132943Sgshapiro } 3334132943Sgshapiro else if (fsync(afd) < 0) 3335132943Sgshapiro { 3336132943Sgshapiro dferror(df, "fsync", e); 3337132943Sgshapiro flush_errors(true); 3338132943Sgshapiro finis(true, true, ExitStat); 3339132943Sgshapiro /* NOTREACHED */ 3340132943Sgshapiro } 3341132943Sgshapiro else if (sm_io_close(df, SM_TIME_DEFAULT) < 0) 3342132943Sgshapiro { 3343132943Sgshapiro dferror(df, "sm_io_close", e); 3344132943Sgshapiro flush_errors(true); 3345132943Sgshapiro finis(true, true, ExitStat); 3346132943Sgshapiro /* NOTREACHED */ 3347132943Sgshapiro } 3348132943Sgshapiro 3349132943Sgshapiro /* Now reopen the df file */ 3350132943Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 3351132943Sgshapiro SM_IO_RDONLY, NULL); 3352132943Sgshapiro if (e->e_dfp == NULL) 3353132943Sgshapiro { 3354132943Sgshapiro /* we haven't acked receipt yet, so just chuck this */ 3355132943Sgshapiro syserr("@Cannot reopen %s", dfname); 3356132943Sgshapiro finis(true, true, ExitStat); 3357132943Sgshapiro /* NOTREACHED */ 3358132943Sgshapiro } 3359132943Sgshapiro } 336090792Sgshapiro#endif /* MILTER */ 336190792Sgshapiro 336290792Sgshapiro /* Check if quarantining stats should be updated */ 336390792Sgshapiro if (e->e_quarmsg != NULL) 336490792Sgshapiro markstats(e, NULL, STATS_QUARANTINE); 336590792Sgshapiro 336690792Sgshapiro /* 336790792Sgshapiro ** If a header/body check (header checks or milter) 336890792Sgshapiro ** set EF_DISCARD, don't queueup the message -- 336990792Sgshapiro ** that would lose the EF_DISCARD bit and deliver 337090792Sgshapiro ** the message. 337190792Sgshapiro */ 337290792Sgshapiro 337390792Sgshapiro if (bitset(EF_DISCARD, e->e_flags)) 337490792Sgshapiro doublequeue = false; 337590792Sgshapiro 337690792Sgshapiro aborting = Errors > 0; 3377125820Sgshapiro if (!(aborting || bitset(EF_DISCARD, e->e_flags)) && 337890792Sgshapiro (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) && 337990792Sgshapiro !split_by_recipient(e)) 338090792Sgshapiro aborting = bitset(EF_FATALERRS, e->e_flags); 338190792Sgshapiro 338290792Sgshapiro if (aborting) 338390792Sgshapiro { 338490792Sgshapiro /* Log who the mail would have gone to */ 338590792Sgshapiro logundelrcpts(e, e->e_message, 8, false); 338690792Sgshapiro flush_errors(true); 338790792Sgshapiro buffer_errors(); 338890792Sgshapiro goto abortmessage; 338990792Sgshapiro } 339090792Sgshapiro 339190792Sgshapiro /* from now on, we have to operate silently */ 339290792Sgshapiro buffer_errors(); 339390792Sgshapiro 339490792Sgshapiro#if 0 339590792Sgshapiro /* 339690792Sgshapiro ** Clear message, it may contain an error from the SMTP dialogue. 339790792Sgshapiro ** This error must not show up in the queue. 339890792Sgshapiro ** Some error message should show up, e.g., alias database 339990792Sgshapiro ** not available, but others shouldn't, e.g., from check_rcpt. 340090792Sgshapiro */ 340190792Sgshapiro 340290792Sgshapiro e->e_message = NULL; 340390792Sgshapiro#endif /* 0 */ 340490792Sgshapiro 340590792Sgshapiro /* 340690792Sgshapiro ** Arrange to send to everyone. 340790792Sgshapiro ** If sending to multiple people, mail back 340890792Sgshapiro ** errors rather than reporting directly. 340990792Sgshapiro ** In any case, don't mail back errors for 341090792Sgshapiro ** anything that has happened up to 341190792Sgshapiro ** now (the other end will do this). 341290792Sgshapiro ** Truncate our transcript -- the mail has gotten 341390792Sgshapiro ** to us successfully, and if we have 341490792Sgshapiro ** to mail this back, it will be easier 341590792Sgshapiro ** on the reader. 341690792Sgshapiro ** Then send to everyone. 341790792Sgshapiro ** Finally give a reply code. If an error has 341890792Sgshapiro ** already been given, don't mail a 341990792Sgshapiro ** message back. 342090792Sgshapiro ** We goose error returns by clearing error bit. 342190792Sgshapiro */ 342290792Sgshapiro 342390792Sgshapiro SmtpPhase = "delivery"; 342490792Sgshapiro (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL); 342590792Sgshapiro id = e->e_id; 342690792Sgshapiro 342790792Sgshapiro#if NAMED_BIND 342890792Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 342990792Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 343090792Sgshapiro#endif /* NAMED_BIND */ 343190792Sgshapiro 343290792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 343390792Sgshapiro { 343490792Sgshapiro /* make sure we actually do delivery */ 343590792Sgshapiro ee->e_flags &= ~EF_CLRQUEUE; 343690792Sgshapiro 343790792Sgshapiro /* from now on, operate silently */ 343890792Sgshapiro ee->e_errormode = EM_MAIL; 343990792Sgshapiro 344090792Sgshapiro if (doublequeue) 344190792Sgshapiro { 344290792Sgshapiro /* make sure it is in the queue */ 344390792Sgshapiro queueup(ee, false, true); 344490792Sgshapiro } 344590792Sgshapiro else 344690792Sgshapiro { 344790792Sgshapiro /* send to all recipients */ 344890792Sgshapiro sendall(ee, SM_DEFAULT); 344990792Sgshapiro } 345090792Sgshapiro ee->e_to = NULL; 345190792Sgshapiro } 345290792Sgshapiro 345398121Sgshapiro /* put back id for SMTP logging in putoutmsg() */ 345498121Sgshapiro oldid = CurEnv->e_id; 345598121Sgshapiro CurEnv->e_id = id; 345698121Sgshapiro 345790792Sgshapiro /* issue success message */ 345890792Sgshapiro message("250 2.0.0 %s Message accepted for delivery", id); 345998121Sgshapiro CurEnv->e_id = oldid; 346090792Sgshapiro 346190792Sgshapiro /* if we just queued, poke it */ 346290792Sgshapiro if (doublequeue) 346390792Sgshapiro { 346490792Sgshapiro bool anything_to_send = false; 346590792Sgshapiro 346690792Sgshapiro sm_getla(); 346790792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 346890792Sgshapiro { 346990792Sgshapiro if (WILL_BE_QUEUED(ee->e_sendmode)) 347090792Sgshapiro continue; 347190792Sgshapiro if (shouldqueue(ee->e_msgpriority, ee->e_ctime)) 347290792Sgshapiro { 347390792Sgshapiro ee->e_sendmode = SM_QUEUE; 347490792Sgshapiro continue; 347590792Sgshapiro } 347690792Sgshapiro else if (QueueMode != QM_QUARANTINE && 347790792Sgshapiro ee->e_quarmsg != NULL) 347890792Sgshapiro { 347990792Sgshapiro ee->e_sendmode = SM_QUEUE; 348090792Sgshapiro continue; 348190792Sgshapiro } 348290792Sgshapiro anything_to_send = true; 348390792Sgshapiro 348490792Sgshapiro /* close all the queue files */ 348590792Sgshapiro closexscript(ee); 348690792Sgshapiro if (ee->e_dfp != NULL) 348790792Sgshapiro { 348890792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 348990792Sgshapiro ee->e_dfp = NULL; 349090792Sgshapiro } 349190792Sgshapiro unlockqueue(ee); 349290792Sgshapiro } 349390792Sgshapiro if (anything_to_send) 349490792Sgshapiro { 349590792Sgshapiro#if PIPELINING 349690792Sgshapiro /* 349790792Sgshapiro ** XXX if we don't do this, we get 250 twice 349890792Sgshapiro ** because it is also flushed in the child. 349990792Sgshapiro */ 350090792Sgshapiro 350190792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 350290792Sgshapiro#endif /* PIPELINING */ 350390792Sgshapiro (void) doworklist(e, true, true); 350490792Sgshapiro } 350590792Sgshapiro } 350690792Sgshapiro 350790792Sgshapiro abortmessage: 350890792Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 350990792Sgshapiro logsender(e, NULL); 351090792Sgshapiro e->e_flags &= ~EF_LOGSENDER; 351190792Sgshapiro 351290792Sgshapiro /* clean up a bit */ 351390792Sgshapiro smtp->sm_gotmail = false; 351490792Sgshapiro 351590792Sgshapiro /* 351690792Sgshapiro ** Call dropenvelope if and only if the envelope is *not* 351790792Sgshapiro ** being processed by the child process forked by doworklist(). 351890792Sgshapiro */ 351990792Sgshapiro 352090792Sgshapiro if (aborting || bitset(EF_DISCARD, e->e_flags)) 352190792Sgshapiro dropenvelope(e, true, false); 352290792Sgshapiro else 352390792Sgshapiro { 352490792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 352590792Sgshapiro { 352690792Sgshapiro if (!doublequeue && 352790792Sgshapiro QueueMode != QM_QUARANTINE && 352890792Sgshapiro ee->e_quarmsg != NULL) 352990792Sgshapiro { 353090792Sgshapiro dropenvelope(ee, true, false); 353190792Sgshapiro continue; 353290792Sgshapiro } 353390792Sgshapiro if (WILL_BE_QUEUED(ee->e_sendmode)) 353490792Sgshapiro dropenvelope(ee, true, false); 353590792Sgshapiro } 353690792Sgshapiro } 353790792Sgshapiro sm_rpool_free(e->e_rpool); 353890792Sgshapiro 353990792Sgshapiro /* 354090792Sgshapiro ** At this point, e == &MainEnvelope, but if we did splitting, 354190792Sgshapiro ** then CurEnv may point to an envelope structure that was just 354290792Sgshapiro ** freed with the rpool. So reset CurEnv *before* calling 354390792Sgshapiro ** newenvelope. 354490792Sgshapiro */ 354590792Sgshapiro 354690792Sgshapiro CurEnv = e; 354790792Sgshapiro newenvelope(e, e, sm_rpool_new_x(NULL)); 354890792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 354990792Sgshapiro 355090792Sgshapiro /* restore connection quarantining */ 355190792Sgshapiro if (smtp->sm_quarmsg == NULL) 355290792Sgshapiro { 355390792Sgshapiro e->e_quarmsg = NULL; 355490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), ""); 355590792Sgshapiro } 355690792Sgshapiro else 355790792Sgshapiro { 355890792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg); 355990792Sgshapiro macdefine(&e->e_macro, A_PERM, 356090792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 356190792Sgshapiro } 3562132943Sgshapiro return true; 356338032Speter} 356490792Sgshapiro/* 356590792Sgshapiro** LOGUNDELRCPTS -- log undelivered (or all) recipients. 356690792Sgshapiro** 356790792Sgshapiro** Parameters: 356890792Sgshapiro** e -- envelope. 356990792Sgshapiro** msg -- message for Stat= 357090792Sgshapiro** level -- log level. 357190792Sgshapiro** all -- log all recipients. 357290792Sgshapiro** 357390792Sgshapiro** Returns: 357490792Sgshapiro** none. 357590792Sgshapiro** 357690792Sgshapiro** Side Effects: 357790792Sgshapiro** logs undelivered (or all) recipients 357890792Sgshapiro*/ 357990792Sgshapiro 358090792Sgshapirovoid 358190792Sgshapirologundelrcpts(e, msg, level, all) 358290792Sgshapiro ENVELOPE *e; 358390792Sgshapiro char *msg; 358490792Sgshapiro int level; 358590792Sgshapiro bool all; 358690792Sgshapiro{ 358790792Sgshapiro ADDRESS *a; 358890792Sgshapiro 358990792Sgshapiro if (LogLevel <= level || msg == NULL || *msg == '\0') 359090792Sgshapiro return; 359190792Sgshapiro 359290792Sgshapiro /* Clear $h so relay= doesn't get mislogged by logdelivery() */ 359390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', NULL); 359490792Sgshapiro 359590792Sgshapiro /* Log who the mail would have gone to */ 359690792Sgshapiro for (a = e->e_sendqueue; a != NULL; a = a->q_next) 359790792Sgshapiro { 359890792Sgshapiro if (!QS_IS_UNDELIVERED(a->q_state) && !all) 359990792Sgshapiro continue; 360090792Sgshapiro e->e_to = a->q_paddr; 360190792Sgshapiro logdelivery(NULL, NULL, a->q_status, msg, NULL, 360290792Sgshapiro (time_t) 0, e); 360390792Sgshapiro } 360490792Sgshapiro e->e_to = NULL; 360590792Sgshapiro} 360690792Sgshapiro/* 360738032Speter** CHECKSMTPATTACK -- check for denial-of-service attack by repetition 360838032Speter** 360938032Speter** Parameters: 361038032Speter** pcounter -- pointer to a counter for this command. 361138032Speter** maxcount -- maximum value for this counter before we 361238032Speter** slow down. 361364562Sgshapiro** waitnow -- sleep now (in this routine)? 361438032Speter** cname -- command name for logging. 361538032Speter** e -- the current envelope. 361638032Speter** 361738032Speter** Returns: 3618132943Sgshapiro** time to wait, 3619132943Sgshapiro** STOP_ATTACK if twice as many commands as allowed and 3620132943Sgshapiro** MaxChildren > 0. 362138032Speter** 362238032Speter** Side Effects: 362338032Speter** Slows down if we seem to be under attack. 362438032Speter*/ 362538032Speter 362664562Sgshapirostatic time_t 362764562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e) 362890792Sgshapiro volatile unsigned int *pcounter; 3629132943Sgshapiro unsigned int maxcount; 363064562Sgshapiro bool waitnow; 363138032Speter char *cname; 363238032Speter ENVELOPE *e; 363338032Speter{ 363490792Sgshapiro if (maxcount <= 0) /* no limit */ 363590792Sgshapiro return (time_t) 0; 363690792Sgshapiro 363738032Speter if (++(*pcounter) >= maxcount) 363838032Speter { 3639132943Sgshapiro unsigned int shift; 364064562Sgshapiro time_t s; 364164562Sgshapiro 364238032Speter if (*pcounter == maxcount && LogLevel > 5) 364338032Speter { 364438032Speter sm_syslog(LOG_INFO, e->e_id, 3645110560Sgshapiro "%s: possible SMTP attack: command=%.40s, count=%u", 364677349Sgshapiro CurSmtpClient, cname, *pcounter); 364738032Speter } 3648132943Sgshapiro shift = *pcounter - maxcount; 3649132943Sgshapiro s = 1 << shift; 3650132943Sgshapiro if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0) 365164562Sgshapiro s = MAXTIMEOUT; 365290792Sgshapiro 3653132943Sgshapiro#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \ 3654132943Sgshapiro ? STOP_ATTACK : (time_t) s) 3655132943Sgshapiro 365664562Sgshapiro /* sleep at least 1 second before returning */ 365764562Sgshapiro (void) sleep(*pcounter / maxcount); 365864562Sgshapiro s -= *pcounter / maxcount; 3659132943Sgshapiro if (s >= MAXTIMEOUT || s < 0) 3660132943Sgshapiro s = MAXTIMEOUT; 3661132943Sgshapiro if (waitnow && s > 0) 366264562Sgshapiro { 366364562Sgshapiro (void) sleep(s); 3664132943Sgshapiro return IS_ATTACK(0); 366564562Sgshapiro } 3666132943Sgshapiro return IS_ATTACK(s); 366738032Speter } 366890792Sgshapiro return (time_t) 0; 366938032Speter} 367090792Sgshapiro/* 367190792Sgshapiro** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server 367290792Sgshapiro** 367390792Sgshapiro** Parameters: 367490792Sgshapiro** none. 367590792Sgshapiro** 367690792Sgshapiro** Returns: 367790792Sgshapiro** nothing. 367890792Sgshapiro** 367990792Sgshapiro** Side Effects: 368090792Sgshapiro** may change I/O fd. 368190792Sgshapiro*/ 368290792Sgshapiro 368390792Sgshapirostatic void 368490792Sgshapirosetup_smtpd_io() 368590792Sgshapiro{ 368690792Sgshapiro int inchfd, outchfd, outfd; 368790792Sgshapiro 368890792Sgshapiro inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 368990792Sgshapiro outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); 369090792Sgshapiro outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL); 369190792Sgshapiro if (outchfd != outfd) 369290792Sgshapiro { 369390792Sgshapiro /* arrange for debugging output to go to remote host */ 369490792Sgshapiro (void) dup2(outchfd, outfd); 369590792Sgshapiro } 369690792Sgshapiro 369790792Sgshapiro /* 369890792Sgshapiro ** if InChannel and OutChannel are stdin/stdout 369990792Sgshapiro ** and connected to ttys 370090792Sgshapiro ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT, 370190792Sgshapiro ** then "chain" them together. 370290792Sgshapiro */ 370390792Sgshapiro 370490792Sgshapiro if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO && 370590792Sgshapiro isatty(inchfd) && isatty(outchfd)) 370690792Sgshapiro { 370790792Sgshapiro int inmode, outmode; 370890792Sgshapiro 370990792Sgshapiro inmode = fcntl(inchfd, F_GETFL, 0); 371090792Sgshapiro if (inmode == -1) 371190792Sgshapiro { 371290792Sgshapiro if (LogLevel > 11) 371390792Sgshapiro sm_syslog(LOG_INFO, NOQID, 371490792Sgshapiro "fcntl(inchfd, F_GETFL) failed: %s", 371590792Sgshapiro sm_errstring(errno)); 371690792Sgshapiro return; 371790792Sgshapiro } 371890792Sgshapiro outmode = fcntl(outchfd, F_GETFL, 0); 371990792Sgshapiro if (outmode == -1) 372090792Sgshapiro { 372190792Sgshapiro if (LogLevel > 11) 372290792Sgshapiro sm_syslog(LOG_INFO, NOQID, 372390792Sgshapiro "fcntl(outchfd, F_GETFL) failed: %s", 372490792Sgshapiro sm_errstring(errno)); 372590792Sgshapiro return; 372690792Sgshapiro } 372790792Sgshapiro if (bitset(O_NONBLOCK, inmode) || 372890792Sgshapiro bitset(O_NONBLOCK, outmode) || 372990792Sgshapiro fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1) 373090792Sgshapiro return; 373190792Sgshapiro outmode = fcntl(outchfd, F_GETFL, 0); 373290792Sgshapiro if (outmode != -1 && bitset(O_NONBLOCK, outmode)) 373390792Sgshapiro { 373490792Sgshapiro /* changing InChannel also changes OutChannel */ 373590792Sgshapiro sm_io_automode(OutChannel, InChannel); 373690792Sgshapiro if (tTd(97, 4) && LogLevel > 9) 373790792Sgshapiro sm_syslog(LOG_INFO, NOQID, 373890792Sgshapiro "set automode for I (%d)/O (%d) in SMTP server", 373990792Sgshapiro inchfd, outchfd); 374090792Sgshapiro } 374190792Sgshapiro 374290792Sgshapiro /* undo change of inchfd */ 374390792Sgshapiro (void) fcntl(inchfd, F_SETFL, inmode); 374490792Sgshapiro } 374590792Sgshapiro} 374690792Sgshapiro/* 374738032Speter** SKIPWORD -- skip a fixed word. 374838032Speter** 374938032Speter** Parameters: 375038032Speter** p -- place to start looking. 375138032Speter** w -- word to skip. 375238032Speter** 375338032Speter** Returns: 375438032Speter** p following w. 375538032Speter** NULL on error. 375638032Speter** 375738032Speter** Side Effects: 375838032Speter** clobbers the p data area. 375938032Speter*/ 376038032Speter 376138032Speterstatic char * 376238032Speterskipword(p, w) 376338032Speter register char *volatile p; 376438032Speter char *w; 376538032Speter{ 376638032Speter register char *q; 376738032Speter char *firstp = p; 376838032Speter 376938032Speter /* find beginning of word */ 377090792Sgshapiro SKIP_SPACE(p); 377138032Speter q = p; 377238032Speter 377338032Speter /* find end of word */ 377438032Speter while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 377538032Speter p++; 377638032Speter while (isascii(*p) && isspace(*p)) 377738032Speter *p++ = '\0'; 377838032Speter if (*p != ':') 377938032Speter { 378038032Speter syntax: 378164562Sgshapiro usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"", 378238032Speter shortenstring(firstp, MAXSHORTSTR)); 378364562Sgshapiro return NULL; 378438032Speter } 378538032Speter *p++ = '\0'; 378690792Sgshapiro SKIP_SPACE(p); 378738032Speter 378838032Speter if (*p == '\0') 378938032Speter goto syntax; 379038032Speter 379138032Speter /* see if the input word matches desired word */ 379290792Sgshapiro if (sm_strcasecmp(q, w)) 379338032Speter goto syntax; 379438032Speter 379564562Sgshapiro return p; 379638032Speter} 379790792Sgshapiro/* 379838032Speter** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 379938032Speter** 380038032Speter** Parameters: 380138032Speter** kp -- the parameter key. 380238032Speter** vp -- the value of that parameter. 380338032Speter** e -- the envelope. 380438032Speter** 380538032Speter** Returns: 380638032Speter** none. 380738032Speter*/ 380838032Speter 380964562Sgshapirostatic void 381038032Spetermail_esmtp_args(kp, vp, e) 381138032Speter char *kp; 381238032Speter char *vp; 381338032Speter ENVELOPE *e; 381438032Speter{ 381590792Sgshapiro if (sm_strcasecmp(kp, "size") == 0) 381638032Speter { 381738032Speter if (vp == NULL) 381838032Speter { 381964562Sgshapiro usrerr("501 5.5.2 SIZE requires a value"); 382038032Speter /* NOTREACHED */ 382138032Speter } 382290792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp); 382390792Sgshapiro errno = 0; 382471345Sgshapiro e->e_msgsize = strtol(vp, (char **) NULL, 10); 382566494Sgshapiro if (e->e_msgsize == LONG_MAX && errno == ERANGE) 382666494Sgshapiro { 382766494Sgshapiro usrerr("552 5.2.3 Message size exceeds maximum value"); 382866494Sgshapiro /* NOTREACHED */ 382966494Sgshapiro } 383090792Sgshapiro if (e->e_msgsize < 0) 383190792Sgshapiro { 383290792Sgshapiro usrerr("552 5.2.3 Message size invalid"); 383390792Sgshapiro /* NOTREACHED */ 383490792Sgshapiro } 383538032Speter } 383690792Sgshapiro else if (sm_strcasecmp(kp, "body") == 0) 383738032Speter { 383838032Speter if (vp == NULL) 383938032Speter { 384064562Sgshapiro usrerr("501 5.5.2 BODY requires a value"); 384138032Speter /* NOTREACHED */ 384238032Speter } 384390792Sgshapiro else if (sm_strcasecmp(vp, "8bitmime") == 0) 384438032Speter { 384590792Sgshapiro SevenBitInput = false; 384638032Speter } 384790792Sgshapiro else if (sm_strcasecmp(vp, "7bit") == 0) 384838032Speter { 384990792Sgshapiro SevenBitInput = true; 385038032Speter } 385138032Speter else 385238032Speter { 385390792Sgshapiro usrerr("501 5.5.4 Unknown BODY type %s", vp); 385438032Speter /* NOTREACHED */ 385538032Speter } 385690792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp); 385738032Speter } 385890792Sgshapiro else if (sm_strcasecmp(kp, "envid") == 0) 385938032Speter { 386064562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 386164562Sgshapiro { 386264562Sgshapiro usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN"); 386364562Sgshapiro /* NOTREACHED */ 386464562Sgshapiro } 386538032Speter if (vp == NULL) 386638032Speter { 386764562Sgshapiro usrerr("501 5.5.2 ENVID requires a value"); 386838032Speter /* NOTREACHED */ 386938032Speter } 387038032Speter if (!xtextok(vp)) 387138032Speter { 387264562Sgshapiro usrerr("501 5.5.4 Syntax error in ENVID parameter value"); 387338032Speter /* NOTREACHED */ 387438032Speter } 387538032Speter if (e->e_envid != NULL) 387638032Speter { 387764562Sgshapiro usrerr("501 5.5.0 Duplicate ENVID parameter"); 387838032Speter /* NOTREACHED */ 387938032Speter } 388090792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp); 388190792Sgshapiro macdefine(&e->e_macro, A_PERM, 388290792Sgshapiro macid("{dsn_envid}"), e->e_envid); 388338032Speter } 388490792Sgshapiro else if (sm_strcasecmp(kp, "ret") == 0) 388538032Speter { 388664562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 388764562Sgshapiro { 388864562Sgshapiro usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN"); 388964562Sgshapiro /* NOTREACHED */ 389064562Sgshapiro } 389138032Speter if (vp == NULL) 389238032Speter { 389364562Sgshapiro usrerr("501 5.5.2 RET requires a value"); 389438032Speter /* NOTREACHED */ 389538032Speter } 389638032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 389738032Speter { 389864562Sgshapiro usrerr("501 5.5.0 Duplicate RET parameter"); 389938032Speter /* NOTREACHED */ 390038032Speter } 390138032Speter e->e_flags |= EF_RET_PARAM; 390290792Sgshapiro if (sm_strcasecmp(vp, "hdrs") == 0) 390338032Speter e->e_flags |= EF_NO_BODY_RETN; 390490792Sgshapiro else if (sm_strcasecmp(vp, "full") != 0) 390538032Speter { 390664562Sgshapiro usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp); 390738032Speter /* NOTREACHED */ 390838032Speter } 390990792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp); 391038032Speter } 391190792Sgshapiro#if SASL 391290792Sgshapiro else if (sm_strcasecmp(kp, "auth") == 0) 391364562Sgshapiro { 391464562Sgshapiro int len; 391564562Sgshapiro char *q; 391664562Sgshapiro char *auth_param; /* the value of the AUTH=x */ 391764562Sgshapiro bool saveQuickAbort = QuickAbort; 391864562Sgshapiro bool saveSuprErrs = SuprErrs; 391990792Sgshapiro bool saveExitStat = ExitStat; 392064562Sgshapiro 392164562Sgshapiro if (vp == NULL) 392264562Sgshapiro { 392364562Sgshapiro usrerr("501 5.5.2 AUTH= requires a value"); 392464562Sgshapiro /* NOTREACHED */ 392564562Sgshapiro } 392664562Sgshapiro if (e->e_auth_param != NULL) 392764562Sgshapiro { 392864562Sgshapiro usrerr("501 5.5.0 Duplicate AUTH parameter"); 392964562Sgshapiro /* NOTREACHED */ 393064562Sgshapiro } 393164562Sgshapiro if ((q = strchr(vp, ' ')) != NULL) 393264562Sgshapiro len = q - vp + 1; 393364562Sgshapiro else 393464562Sgshapiro len = strlen(vp) + 1; 393564562Sgshapiro auth_param = xalloc(len); 393690792Sgshapiro (void) sm_strlcpy(auth_param, vp, len); 393764562Sgshapiro if (!xtextok(auth_param)) 393864562Sgshapiro { 393964562Sgshapiro usrerr("501 5.5.4 Syntax error in AUTH parameter value"); 394064562Sgshapiro /* just a warning? */ 394164562Sgshapiro /* NOTREACHED */ 394264562Sgshapiro } 394364562Sgshapiro 394464562Sgshapiro /* XXX define this always or only if trusted? */ 3945132943Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), 3946132943Sgshapiro auth_param); 394764562Sgshapiro 394864562Sgshapiro /* 394964562Sgshapiro ** call Strust_auth to find out whether 395064562Sgshapiro ** auth_param is acceptable (trusted) 395164562Sgshapiro ** we shouldn't trust it if not authenticated 395264562Sgshapiro ** (required by RFC, leave it to ruleset?) 395364562Sgshapiro */ 395464562Sgshapiro 395590792Sgshapiro SuprErrs = true; 395690792Sgshapiro QuickAbort = false; 395764562Sgshapiro if (strcmp(auth_param, "<>") != 0 && 3958132943Sgshapiro (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM, 3959102528Sgshapiro 9, NULL, NOQID) != EX_OK || Errors > 0)) 396064562Sgshapiro { 396164562Sgshapiro if (tTd(95, 8)) 396264562Sgshapiro { 396364562Sgshapiro q = e->e_auth_param; 396490792Sgshapiro sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n", 3965132943Sgshapiro auth_param, (q == NULL) ? "" : q); 396664562Sgshapiro } 396790792Sgshapiro 396864562Sgshapiro /* not trusted */ 396990792Sgshapiro e->e_auth_param = "<>"; 397090792Sgshapiro# if _FFR_AUTH_PASSING 397190792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 397290792Sgshapiro macid("{auth_author}"), NULL); 397390792Sgshapiro# endif /* _FFR_AUTH_PASSING */ 397464562Sgshapiro } 397564562Sgshapiro else 397664562Sgshapiro { 397764562Sgshapiro if (tTd(95, 8)) 3978132943Sgshapiro sm_dprintf("auth=\"%.100s\" trusted\n", auth_param); 397990792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, 398090792Sgshapiro auth_param); 398164562Sgshapiro } 398290792Sgshapiro sm_free(auth_param); /* XXX */ 398377349Sgshapiro 398464562Sgshapiro /* reset values */ 398564562Sgshapiro Errors = 0; 398664562Sgshapiro QuickAbort = saveQuickAbort; 398764562Sgshapiro SuprErrs = saveSuprErrs; 398890792Sgshapiro ExitStat = saveExitStat; 398964562Sgshapiro } 399090792Sgshapiro#endif /* SASL */ 399190792Sgshapiro#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?') 399290792Sgshapiro 399390792Sgshapiro /* 399490792Sgshapiro ** "by" is only accepted if DeliverByMin >= 0. 399590792Sgshapiro ** We maybe could add this to the list of server_features. 399690792Sgshapiro */ 399790792Sgshapiro 399890792Sgshapiro else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0) 399990792Sgshapiro { 400090792Sgshapiro char *s; 400190792Sgshapiro 400290792Sgshapiro if (vp == NULL) 400390792Sgshapiro { 400490792Sgshapiro usrerr("501 5.5.2 BY= requires a value"); 400590792Sgshapiro /* NOTREACHED */ 400690792Sgshapiro } 400790792Sgshapiro errno = 0; 400890792Sgshapiro e->e_deliver_by = strtol(vp, &s, 10); 400990792Sgshapiro if (e->e_deliver_by == LONG_MIN || 401090792Sgshapiro e->e_deliver_by == LONG_MAX || 401190792Sgshapiro e->e_deliver_by > 999999999l || 401290792Sgshapiro e->e_deliver_by < -999999999l) 401390792Sgshapiro { 401490792Sgshapiro usrerr("501 5.5.2 BY=%s out of range", vp); 401590792Sgshapiro /* NOTREACHED */ 401690792Sgshapiro } 401790792Sgshapiro if (s == NULL || *s != ';') 401890792Sgshapiro { 401990792Sgshapiro usrerr("501 5.5.2 BY= missing ';'"); 402090792Sgshapiro /* NOTREACHED */ 402190792Sgshapiro } 402290792Sgshapiro e->e_dlvr_flag = 0; 402390792Sgshapiro ++s; /* XXX: spaces allowed? */ 402490792Sgshapiro SKIP_SPACE(s); 402590792Sgshapiro switch (tolower(*s)) 402690792Sgshapiro { 402790792Sgshapiro case 'n': 402890792Sgshapiro e->e_dlvr_flag = DLVR_NOTIFY; 402990792Sgshapiro break; 403090792Sgshapiro case 'r': 403190792Sgshapiro e->e_dlvr_flag = DLVR_RETURN; 403290792Sgshapiro if (e->e_deliver_by <= 0) 403390792Sgshapiro { 403490792Sgshapiro usrerr("501 5.5.4 mode R requires BY time > 0"); 403590792Sgshapiro /* NOTREACHED */ 403690792Sgshapiro } 403790792Sgshapiro if (DeliverByMin > 0 && e->e_deliver_by > 0 && 403890792Sgshapiro e->e_deliver_by < DeliverByMin) 403990792Sgshapiro { 404090792Sgshapiro usrerr("555 5.5.2 time %ld less than %ld", 404190792Sgshapiro e->e_deliver_by, (long) DeliverByMin); 404290792Sgshapiro /* NOTREACHED */ 404390792Sgshapiro } 404490792Sgshapiro break; 404590792Sgshapiro default: 404690792Sgshapiro usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s)); 404790792Sgshapiro /* NOTREACHED */ 404890792Sgshapiro } 404990792Sgshapiro ++s; /* XXX: spaces allowed? */ 405090792Sgshapiro SKIP_SPACE(s); 405190792Sgshapiro switch (tolower(*s)) 405290792Sgshapiro { 405390792Sgshapiro case 't': 405490792Sgshapiro e->e_dlvr_flag |= DLVR_TRACE; 405590792Sgshapiro break; 405690792Sgshapiro case '\0': 405790792Sgshapiro break; 405890792Sgshapiro default: 405990792Sgshapiro usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s)); 406090792Sgshapiro /* NOTREACHED */ 406190792Sgshapiro } 406290792Sgshapiro 406390792Sgshapiro /* XXX: check whether more characters follow? */ 406490792Sgshapiro } 406538032Speter else 406638032Speter { 406766494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 406838032Speter /* NOTREACHED */ 406938032Speter } 407038032Speter} 407190792Sgshapiro/* 407238032Speter** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 407338032Speter** 407438032Speter** Parameters: 407538032Speter** a -- the address corresponding to the To: parameter. 407638032Speter** kp -- the parameter key. 407738032Speter** vp -- the value of that parameter. 407838032Speter** e -- the envelope. 407938032Speter** 408038032Speter** Returns: 408138032Speter** none. 408238032Speter*/ 408338032Speter 408464562Sgshapirostatic void 408538032Speterrcpt_esmtp_args(a, kp, vp, e) 408638032Speter ADDRESS *a; 408738032Speter char *kp; 408838032Speter char *vp; 408938032Speter ENVELOPE *e; 409038032Speter{ 409190792Sgshapiro if (sm_strcasecmp(kp, "notify") == 0) 409238032Speter { 409338032Speter char *p; 409438032Speter 409564562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 409664562Sgshapiro { 409764562Sgshapiro usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN"); 409864562Sgshapiro /* NOTREACHED */ 409964562Sgshapiro } 410038032Speter if (vp == NULL) 410138032Speter { 410264562Sgshapiro usrerr("501 5.5.2 NOTIFY requires a value"); 410338032Speter /* NOTREACHED */ 410438032Speter } 410538032Speter a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 410638032Speter a->q_flags |= QHASNOTIFY; 410790792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp); 410864562Sgshapiro 410990792Sgshapiro if (sm_strcasecmp(vp, "never") == 0) 411038032Speter return; 411138032Speter for (p = vp; p != NULL; vp = p) 411238032Speter { 411338032Speter p = strchr(p, ','); 411438032Speter if (p != NULL) 411538032Speter *p++ = '\0'; 411690792Sgshapiro if (sm_strcasecmp(vp, "success") == 0) 411738032Speter a->q_flags |= QPINGONSUCCESS; 411890792Sgshapiro else if (sm_strcasecmp(vp, "failure") == 0) 411938032Speter a->q_flags |= QPINGONFAILURE; 412090792Sgshapiro else if (sm_strcasecmp(vp, "delay") == 0) 412138032Speter a->q_flags |= QPINGONDELAY; 412238032Speter else 412338032Speter { 412464562Sgshapiro usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY", 412538032Speter vp); 412638032Speter /* NOTREACHED */ 412738032Speter } 412838032Speter } 412938032Speter } 413090792Sgshapiro else if (sm_strcasecmp(kp, "orcpt") == 0) 413138032Speter { 413264562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 413364562Sgshapiro { 413464562Sgshapiro usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN"); 413564562Sgshapiro /* NOTREACHED */ 413664562Sgshapiro } 413738032Speter if (vp == NULL) 413838032Speter { 413964562Sgshapiro usrerr("501 5.5.2 ORCPT requires a value"); 414038032Speter /* NOTREACHED */ 414138032Speter } 414238032Speter if (strchr(vp, ';') == NULL || !xtextok(vp)) 414338032Speter { 414464562Sgshapiro usrerr("501 5.5.4 Syntax error in ORCPT parameter value"); 414538032Speter /* NOTREACHED */ 414638032Speter } 414738032Speter if (a->q_orcpt != NULL) 414838032Speter { 414964562Sgshapiro usrerr("501 5.5.0 Duplicate ORCPT parameter"); 415038032Speter /* NOTREACHED */ 415138032Speter } 415290792Sgshapiro a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp); 415338032Speter } 415438032Speter else 415538032Speter { 415666494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 415738032Speter /* NOTREACHED */ 415838032Speter } 415938032Speter} 416090792Sgshapiro/* 416138032Speter** PRINTVRFYADDR -- print an entry in the verify queue 416238032Speter** 416338032Speter** Parameters: 416490792Sgshapiro** a -- the address to print. 416538032Speter** last -- set if this is the last one. 416638032Speter** vrfy -- set if this is a VRFY command. 416738032Speter** 416838032Speter** Returns: 416938032Speter** none. 417038032Speter** 417138032Speter** Side Effects: 417238032Speter** Prints the appropriate 250 codes. 417338032Speter*/ 417464562Sgshapiro#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */ 417538032Speter 417664562Sgshapirostatic void 417738032Speterprintvrfyaddr(a, last, vrfy) 417838032Speter register ADDRESS *a; 417938032Speter bool last; 418038032Speter bool vrfy; 418138032Speter{ 418264562Sgshapiro char fmtbuf[30]; 418338032Speter 418438032Speter if (vrfy && a->q_mailer != NULL && 418538032Speter !bitnset(M_VRFY250, a->q_mailer->m_flags)) 418690792Sgshapiro (void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf); 418738032Speter else 418890792Sgshapiro (void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf); 418938032Speter fmtbuf[3] = last ? ' ' : '-'; 419090792Sgshapiro (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4); 419138032Speter if (a->q_fullname == NULL) 419238032Speter { 419364562Sgshapiro if ((a->q_mailer == NULL || 419464562Sgshapiro a->q_mailer->m_addrtype == NULL || 419590792Sgshapiro sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 419664562Sgshapiro strchr(a->q_user, '@') == NULL) 419790792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>", 419864562Sgshapiro sizeof fmtbuf - OFFF); 419938032Speter else 420090792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>", 420164562Sgshapiro sizeof fmtbuf - OFFF); 420238032Speter message(fmtbuf, a->q_user, MyHostName); 420338032Speter } 420438032Speter else 420538032Speter { 420664562Sgshapiro if ((a->q_mailer == NULL || 420764562Sgshapiro a->q_mailer->m_addrtype == NULL || 420890792Sgshapiro sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 420964562Sgshapiro strchr(a->q_user, '@') == NULL) 421090792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>", 421164562Sgshapiro sizeof fmtbuf - OFFF); 421238032Speter else 421390792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>", 421464562Sgshapiro sizeof fmtbuf - OFFF); 421538032Speter message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 421638032Speter } 421738032Speter} 421838032Speter 421990792Sgshapiro#if SASL 422090792Sgshapiro/* 422164562Sgshapiro** SASLMECHS -- get list of possible AUTH mechanisms 422264562Sgshapiro** 422364562Sgshapiro** Parameters: 422490792Sgshapiro** conn -- SASL connection info. 422590792Sgshapiro** mechlist -- output parameter for list of mechanisms. 422664562Sgshapiro** 422764562Sgshapiro** Returns: 422890792Sgshapiro** number of mechs. 422964562Sgshapiro*/ 423064562Sgshapiro 423164562Sgshapirostatic int 423264562Sgshapirosaslmechs(conn, mechlist) 423364562Sgshapiro sasl_conn_t *conn; 423464562Sgshapiro char **mechlist; 423564562Sgshapiro{ 423664562Sgshapiro int len, num, result; 423764562Sgshapiro 423864562Sgshapiro /* "user" is currently unused */ 423998121Sgshapiro# if SASL >= 20000 424098121Sgshapiro result = sasl_listmech(conn, NULL, 424198121Sgshapiro "", " ", "", (const char **) mechlist, 4242120256Sgshapiro (unsigned int *)&len, &num); 424398121Sgshapiro# else /* SASL >= 20000 */ 424464562Sgshapiro result = sasl_listmech(conn, "user", /* XXX */ 424564562Sgshapiro "", " ", "", mechlist, 424690792Sgshapiro (unsigned int *)&len, (unsigned int *)&num); 424798121Sgshapiro# endif /* SASL >= 20000 */ 424890792Sgshapiro if (result != SASL_OK) 424964562Sgshapiro { 425090792Sgshapiro if (LogLevel > 9) 425190792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 425290792Sgshapiro "AUTH error: listmech=%d, num=%d", 425390792Sgshapiro result, num); 425490792Sgshapiro num = 0; 425590792Sgshapiro } 425690792Sgshapiro if (num > 0) 425790792Sgshapiro { 425864562Sgshapiro if (LogLevel > 11) 425964562Sgshapiro sm_syslog(LOG_INFO, NOQID, 426090792Sgshapiro "AUTH: available mech=%s, allowed mech=%s", 426164562Sgshapiro *mechlist, AuthMechanisms); 426290792Sgshapiro *mechlist = intersect(AuthMechanisms, *mechlist, NULL); 426364562Sgshapiro } 426464562Sgshapiro else 426564562Sgshapiro { 426690792Sgshapiro *mechlist = NULL; /* be paranoid... */ 426790792Sgshapiro if (result == SASL_OK && LogLevel > 9) 426864562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 426990792Sgshapiro "AUTH warning: no mechanisms"); 427064562Sgshapiro } 427164562Sgshapiro return num; 427264562Sgshapiro} 427398121Sgshapiro 427498121Sgshapiro# if SASL >= 20000 427590792Sgshapiro/* 427664562Sgshapiro** PROXY_POLICY -- define proxy policy for AUTH 427764562Sgshapiro** 427864562Sgshapiro** Parameters: 427998121Sgshapiro** conn -- unused. 428090792Sgshapiro** context -- unused. 428198121Sgshapiro** requested_user -- authorization identity. 428298121Sgshapiro** rlen -- authorization identity length. 428390792Sgshapiro** auth_identity -- authentication identity. 428498121Sgshapiro** alen -- authentication identity length. 428598121Sgshapiro** def_realm -- default user realm. 428698121Sgshapiro** urlen -- user realm length. 428798121Sgshapiro** propctx -- unused. 428898121Sgshapiro** 428998121Sgshapiro** Returns: 429098121Sgshapiro** ok? 429198121Sgshapiro** 429298121Sgshapiro** Side Effects: 429398121Sgshapiro** sets {auth_authen} macro. 429498121Sgshapiro*/ 429598121Sgshapiro 429698121Sgshapiroint 429798121Sgshapiroproxy_policy(conn, context, requested_user, rlen, auth_identity, alen, 429898121Sgshapiro def_realm, urlen, propctx) 429998121Sgshapiro sasl_conn_t *conn; 430098121Sgshapiro void *context; 430198121Sgshapiro const char *requested_user; 430298121Sgshapiro unsigned rlen; 430398121Sgshapiro const char *auth_identity; 430498121Sgshapiro unsigned alen; 430598121Sgshapiro const char *def_realm; 430698121Sgshapiro unsigned urlen; 430798121Sgshapiro struct propctx *propctx; 430898121Sgshapiro{ 430998121Sgshapiro if (auth_identity == NULL) 431098121Sgshapiro return SASL_FAIL; 431198121Sgshapiro 431298121Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 431398121Sgshapiro macid("{auth_authen}"), (char *) auth_identity); 431498121Sgshapiro 431598121Sgshapiro return SASL_OK; 431698121Sgshapiro} 431798121Sgshapiro# else /* SASL >= 20000 */ 431898121Sgshapiro 431998121Sgshapiro/* 432098121Sgshapiro** PROXY_POLICY -- define proxy policy for AUTH 432198121Sgshapiro** 432298121Sgshapiro** Parameters: 432398121Sgshapiro** context -- unused. 432498121Sgshapiro** auth_identity -- authentication identity. 432590792Sgshapiro** requested_user -- authorization identity. 432690792Sgshapiro** user -- allowed user (output). 432790792Sgshapiro** errstr -- possible error string (output). 432864562Sgshapiro** 432964562Sgshapiro** Returns: 433064562Sgshapiro** ok? 433164562Sgshapiro*/ 433264562Sgshapiro 433364562Sgshapiroint 433464562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr) 433564562Sgshapiro void *context; 433664562Sgshapiro const char *auth_identity; 433764562Sgshapiro const char *requested_user; 433864562Sgshapiro const char **user; 433964562Sgshapiro const char **errstr; 434064562Sgshapiro{ 434164562Sgshapiro if (user == NULL || auth_identity == NULL) 434264562Sgshapiro return SASL_FAIL; 434364562Sgshapiro *user = newstr(auth_identity); 434464562Sgshapiro return SASL_OK; 434564562Sgshapiro} 434698121Sgshapiro# endif /* SASL >= 20000 */ 434790792Sgshapiro#endif /* SASL */ 434864562Sgshapiro 434990792Sgshapiro#if STARTTLS 435090792Sgshapiro/* 435190792Sgshapiro** INITSRVTLS -- initialize server side TLS 435264562Sgshapiro** 435364562Sgshapiro** Parameters: 435490792Sgshapiro** tls_ok -- should tls initialization be done? 435564562Sgshapiro** 435664562Sgshapiro** Returns: 435790792Sgshapiro** succeeded? 435864562Sgshapiro** 435964562Sgshapiro** Side Effects: 436090792Sgshapiro** sets tls_ok_srv which is a static variable in this module. 436190792Sgshapiro** Do NOT remove assignments to it! 436264562Sgshapiro*/ 436364562Sgshapiro 436466494Sgshapirobool 436590792Sgshapiroinitsrvtls(tls_ok) 436690792Sgshapiro bool tls_ok; 436764562Sgshapiro{ 436890792Sgshapiro if (!tls_ok) 436990792Sgshapiro return false; 437064562Sgshapiro 437190792Sgshapiro /* do NOT remove assignment */ 4372110560Sgshapiro tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile, 4373110560Sgshapiro SrvKeyFile, CACertPath, CACertFile, DHParams); 437490792Sgshapiro return tls_ok_srv; 437564562Sgshapiro} 437690792Sgshapiro#endif /* STARTTLS */ 437764562Sgshapiro/* 437890792Sgshapiro** SRVFEATURES -- get features for SMTP server 437964562Sgshapiro** 438064562Sgshapiro** Parameters: 438190792Sgshapiro** e -- envelope (should be session context). 438290792Sgshapiro** clientname -- name of client. 438390792Sgshapiro** features -- default features for this invocation. 438464562Sgshapiro** 438564562Sgshapiro** Returns: 438690792Sgshapiro** server features. 438764562Sgshapiro*/ 438864562Sgshapiro 438990792Sgshapiro/* table with options: it uses just one character, how about strings? */ 439090792Sgshapirostatic struct 439164562Sgshapiro{ 439290792Sgshapiro char srvf_opt; 439390792Sgshapiro unsigned int srvf_flag; 439490792Sgshapiro} srv_feat_table[] = 439564562Sgshapiro{ 439690792Sgshapiro { 'A', SRV_OFFER_AUTH }, 4397132943Sgshapiro { 'B', SRV_OFFER_VERB }, 4398132943Sgshapiro { 'C', SRV_REQ_SEC }, 4399132943Sgshapiro { 'D', SRV_OFFER_DSN }, 4400132943Sgshapiro { 'E', SRV_OFFER_ETRN }, 4401132943Sgshapiro { 'L', SRV_REQ_AUTH }, 440290792Sgshapiro#if PIPELINING 440390792Sgshapiro# if _FFR_NO_PIPE 440490792Sgshapiro { 'N', SRV_NO_PIPE }, 440590792Sgshapiro# endif /* _FFR_NO_PIPE */ 440690792Sgshapiro { 'P', SRV_OFFER_PIPE }, 440790792Sgshapiro#endif /* PIPELINING */ 4408132943Sgshapiro { 'R', SRV_VRFY_CLT }, /* same as V; not documented */ 440990792Sgshapiro { 'S', SRV_OFFER_TLS }, 441090792Sgshapiro/* { 'T', SRV_TMP_FAIL }, */ 441190792Sgshapiro { 'V', SRV_VRFY_CLT }, 4412132943Sgshapiro { 'X', SRV_OFFER_EXPN }, 441390792Sgshapiro/* { 'Y', SRV_OFFER_VRFY }, */ 441490792Sgshapiro { '\0', SRV_NONE } 441590792Sgshapiro}; 441664562Sgshapiro 441790792Sgshapirostatic unsigned int 441890792Sgshapirosrvfeatures(e, clientname, features) 441990792Sgshapiro ENVELOPE *e; 442090792Sgshapiro char *clientname; 442190792Sgshapiro unsigned int features; 442277349Sgshapiro{ 442390792Sgshapiro int r, i, j; 442490792Sgshapiro char **pvp, c, opt; 442590792Sgshapiro char pvpbuf[PSBUFSIZE]; 442677349Sgshapiro 442790792Sgshapiro pvp = NULL; 442890792Sgshapiro r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf, 442990792Sgshapiro sizeof(pvpbuf)); 443090792Sgshapiro if (r != EX_OK) 443190792Sgshapiro return features; 443290792Sgshapiro if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 443390792Sgshapiro return features; 443490792Sgshapiro if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0) 443590792Sgshapiro return SRV_TMP_FAIL; 443677349Sgshapiro 443764562Sgshapiro /* 443890792Sgshapiro ** General rule (see sendmail.h, d_flags): 443990792Sgshapiro ** lower case: required/offered, upper case: Not required/available 444090792Sgshapiro ** 444190792Sgshapiro ** Since we can change some features per daemon, we have both 444290792Sgshapiro ** cases here: turn on/off a feature. 444364562Sgshapiro */ 444464562Sgshapiro 444590792Sgshapiro for (i = 1; pvp[i] != NULL; i++) 444664562Sgshapiro { 444790792Sgshapiro c = pvp[i][0]; 444890792Sgshapiro j = 0; 444990792Sgshapiro for (;;) 445064562Sgshapiro { 445190792Sgshapiro if ((opt = srv_feat_table[j].srvf_opt) == '\0') 445264562Sgshapiro { 445390792Sgshapiro if (LogLevel > 9) 445490792Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 445590792Sgshapiro "srvfeatures: unknown feature %s", 445690792Sgshapiro pvp[i]); 445790792Sgshapiro break; 445864562Sgshapiro } 445990792Sgshapiro if (c == opt) 446064562Sgshapiro { 446190792Sgshapiro features &= ~(srv_feat_table[j].srvf_flag); 446290792Sgshapiro break; 446364562Sgshapiro } 446490792Sgshapiro if (c == tolower(opt)) 446564562Sgshapiro { 446690792Sgshapiro features |= srv_feat_table[j].srvf_flag; 446790792Sgshapiro break; 446864562Sgshapiro } 446990792Sgshapiro ++j; 447064562Sgshapiro } 447164562Sgshapiro } 447290792Sgshapiro return features; 447364562Sgshapiro} 447464562Sgshapiro 447590792Sgshapiro/* 447638032Speter** HELP -- implement the HELP command. 447738032Speter** 447838032Speter** Parameters: 447938032Speter** topic -- the topic we want help for. 448090792Sgshapiro** e -- envelope. 448138032Speter** 448238032Speter** Returns: 448338032Speter** none. 448438032Speter** 448538032Speter** Side Effects: 448638032Speter** outputs the help file to message output. 448738032Speter*/ 448864562Sgshapiro#define HELPVSTR "#vers " 448964562Sgshapiro#define HELPVERSION 2 449038032Speter 449138032Spetervoid 449264562Sgshapirohelp(topic, e) 449338032Speter char *topic; 449464562Sgshapiro ENVELOPE *e; 449538032Speter{ 449690792Sgshapiro register SM_FILE_T *hf; 449764562Sgshapiro register char *p; 449838032Speter int len; 449938032Speter bool noinfo; 450090792Sgshapiro bool first = true; 450164562Sgshapiro long sff = SFF_OPENASROOT|SFF_REGONLY; 450238032Speter char buf[MAXLINE]; 450364562Sgshapiro char inp[MAXLINE]; 450464562Sgshapiro static int foundvers = -1; 450538032Speter extern char Version[]; 450638032Speter 450738032Speter if (DontLockReadFiles) 450838032Speter sff |= SFF_NOLOCK; 450964562Sgshapiro if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail)) 451038032Speter sff |= SFF_SAFEDIRPATH; 451138032Speter 451238032Speter if (HelpFile == NULL || 451338032Speter (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL) 451438032Speter { 451538032Speter /* no help */ 451638032Speter errno = 0; 451764562Sgshapiro message("502 5.3.0 Sendmail %s -- HELP not implemented", 451864562Sgshapiro Version); 451938032Speter return; 452038032Speter } 452138032Speter 452238032Speter if (topic == NULL || *topic == '\0') 452338032Speter { 452438032Speter topic = "smtp"; 452590792Sgshapiro noinfo = false; 452638032Speter } 452738032Speter else 452838032Speter { 452938032Speter makelower(topic); 453090792Sgshapiro noinfo = true; 453138032Speter } 453238032Speter 453338032Speter len = strlen(topic); 453438032Speter 453590792Sgshapiro while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 453638032Speter { 453764562Sgshapiro if (buf[0] == '#') 453864562Sgshapiro { 453964562Sgshapiro if (foundvers < 0 && 454064562Sgshapiro strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0) 454164562Sgshapiro { 454264562Sgshapiro int h; 454364562Sgshapiro 454490792Sgshapiro if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d", 454590792Sgshapiro &h) == 1) 454664562Sgshapiro foundvers = h; 454764562Sgshapiro } 454864562Sgshapiro continue; 454964562Sgshapiro } 455038032Speter if (strncmp(buf, topic, len) == 0) 455138032Speter { 455264562Sgshapiro if (first) 455364562Sgshapiro { 455490792Sgshapiro first = false; 455538032Speter 455664562Sgshapiro /* print version if no/old vers# in file */ 455764562Sgshapiro if (foundvers < 2 && !noinfo) 455864562Sgshapiro message("214-2.0.0 This is Sendmail version %s", Version); 455964562Sgshapiro } 456064562Sgshapiro p = strpbrk(buf, " \t"); 456138032Speter if (p == NULL) 456264562Sgshapiro p = buf + strlen(buf) - 1; 456338032Speter else 456438032Speter p++; 456590792Sgshapiro fixcrlf(p, true); 456664562Sgshapiro if (foundvers >= 2) 456764562Sgshapiro { 456864562Sgshapiro translate_dollars(p); 456964562Sgshapiro expand(p, inp, sizeof inp, e); 457064562Sgshapiro p = inp; 457164562Sgshapiro } 457264562Sgshapiro message("214-2.0.0 %s", p); 457390792Sgshapiro noinfo = false; 457438032Speter } 457538032Speter } 457638032Speter 457738032Speter if (noinfo) 457864562Sgshapiro message("504 5.3.0 HELP topic \"%.10s\" unknown", topic); 457938032Speter else 458064562Sgshapiro message("214 2.0.0 End of HELP info"); 458164562Sgshapiro 458264562Sgshapiro if (foundvers != 0 && foundvers < HELPVERSION) 458364562Sgshapiro { 458464562Sgshapiro if (LogLevel > 1) 458564562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 458664562Sgshapiro "%s too old (require version %d)", 458764562Sgshapiro HelpFile, HELPVERSION); 458864562Sgshapiro 458964562Sgshapiro /* avoid log next time */ 459064562Sgshapiro foundvers = 0; 459164562Sgshapiro } 459264562Sgshapiro 459390792Sgshapiro (void) sm_io_close(hf, SM_TIME_DEFAULT); 459438032Speter} 4595120256Sgshapiro 4596120256Sgshapiro#if SASL 4597120256Sgshapiro/* 4598120256Sgshapiro** RESET_SASLCONN -- reset SASL connection data 4599120256Sgshapiro** 4600120256Sgshapiro** Parameters: 4601120256Sgshapiro** conn -- SASL connection context 4602120256Sgshapiro** hostname -- host name 4603120256Sgshapiro** various connection data 4604120256Sgshapiro** 4605120256Sgshapiro** Returns: 4606120256Sgshapiro** SASL result 4607120256Sgshapiro*/ 4608120256Sgshapiro 4609120256Sgshapirostatic int 4610120256Sgshapiroreset_saslconn(sasl_conn_t ** conn, char *hostname, 4611120256Sgshapiro# if SASL >= 20000 4612120256Sgshapiro char *remoteip, char *localip, 4613120256Sgshapiro char *auth_id, sasl_ssf_t * ext_ssf) 4614120256Sgshapiro# else /* SASL >= 20000 */ 4615120256Sgshapiro struct sockaddr_in * saddr_r, struct sockaddr_in * saddr_l, 4616120256Sgshapiro sasl_external_properties_t * ext_ssf) 4617120256Sgshapiro# endif /* SASL >= 20000 */ 4618120256Sgshapiro{ 4619120256Sgshapiro int result; 4620120256Sgshapiro 4621120256Sgshapiro sasl_dispose(conn); 4622120256Sgshapiro# if SASL >= 20000 4623120256Sgshapiro result = sasl_server_new("smtp", hostname, NULL, NULL, NULL, 4624120256Sgshapiro NULL, 0, conn); 4625120256Sgshapiro# elif SASL > 10505 4626120256Sgshapiro /* use empty realm: only works in SASL > 1.5.5 */ 4627120256Sgshapiro result = sasl_server_new("smtp", hostname, "", NULL, 0, conn); 4628120256Sgshapiro# else /* SASL >= 20000 */ 4629120256Sgshapiro /* use no realm -> realm is set to hostname by SASL lib */ 4630120256Sgshapiro result = sasl_server_new("smtp", hostname, NULL, NULL, 0, 4631120256Sgshapiro conn); 4632120256Sgshapiro# endif /* SASL >= 20000 */ 4633120256Sgshapiro if (result != SASL_OK) 4634120256Sgshapiro return result; 4635120256Sgshapiro 4636120256Sgshapiro# if SASL >= 20000 4637120256Sgshapiro# if NETINET || NETINET6 4638120256Sgshapiro if (remoteip != NULL) 4639120256Sgshapiro result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip); 4640120256Sgshapiro if (result != SASL_OK) 4641120256Sgshapiro return result; 4642120256Sgshapiro 4643120256Sgshapiro if (localip != NULL) 4644120256Sgshapiro result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip); 4645120256Sgshapiro if (result != SASL_OK) 4646120256Sgshapiro return result; 4647120256Sgshapiro# endif /* NETINET || NETINET6 */ 4648120256Sgshapiro 4649120256Sgshapiro result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf); 4650120256Sgshapiro if (result != SASL_OK) 4651120256Sgshapiro return result; 4652120256Sgshapiro 4653120256Sgshapiro result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id); 4654120256Sgshapiro if (result != SASL_OK) 4655120256Sgshapiro return result; 4656120256Sgshapiro# else /* SASL >= 20000 */ 4657120256Sgshapiro# if NETINET 4658120256Sgshapiro if (saddr_r != NULL) 4659120256Sgshapiro result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r); 4660120256Sgshapiro if (result != SASL_OK) 4661120256Sgshapiro return result; 4662120256Sgshapiro 4663120256Sgshapiro if (saddr_l != NULL) 4664120256Sgshapiro result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l); 4665120256Sgshapiro if (result != SASL_OK) 4666120256Sgshapiro return result; 4667120256Sgshapiro# endif /* NETINET */ 4668120256Sgshapiro 4669120256Sgshapiro result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf); 4670120256Sgshapiro if (result != SASL_OK) 4671120256Sgshapiro return result; 4672120256Sgshapiro# endif /* SASL >= 20000 */ 4673120256Sgshapiro return SASL_OK; 4674120256Sgshapiro} 4675120256Sgshapiro#endif /* SASL */ 4676