138032Speter/* 2285229Sgshapiro * Copyright (c) 1998-2010, 2012-2014 Proofpoint, 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> 18363466Sgshapiro#endif 1964562Sgshapiro 20266527SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $") 2164562Sgshapiro 22157001Sgshapiro#include <sm/time.h> 23132943Sgshapiro#include <sm/fdset.h> 24132943Sgshapiro 2590792Sgshapiro#if SASL || STARTTLS 26363466Sgshapiro# include <tls.h> 2790792Sgshapiro# include "sfsasl.h" 28363466Sgshapiro#endif 2990792Sgshapiro#if SASL 3090792Sgshapiro# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1) 3164562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **)); 32363466Sgshapiro#endif 3390792Sgshapiro#if STARTTLS 34285229Sgshapiro# include <openssl/err.h> 3590792Sgshapiro# include <sysexits.h> 3638032Speter 3790792Sgshapirostatic SSL_CTX *srv_ctx = NULL; /* TLS server context */ 3890792Sgshapirostatic SSL *srv_ssl = NULL; /* per connection context */ 39363466Sgshapirostatic tlsi_ctx_T tlsi_ctx; /* TLS information context */ 4038032Speter 4190792Sgshapirostatic bool tls_ok_srv = false; 4290792Sgshapiro 4390792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \ 4490792Sgshapiro bitset(SRV_VRFY_CLT, features)) 4590792Sgshapiro#endif /* STARTTLS */ 4690792Sgshapiro 47168515Sgshapiro#if _FFR_DM_ONE 48168515Sgshapirostatic bool NotFirstDelivery = false; 49363466Sgshapiro#endif 50168515Sgshapiro 5190792Sgshapiro/* server features */ 5290792Sgshapiro#define SRV_NONE 0x0000 /* none... */ 5390792Sgshapiro#define SRV_OFFER_TLS 0x0001 /* offer STARTTLS */ 5490792Sgshapiro#define SRV_VRFY_CLT 0x0002 /* request a cert */ 5590792Sgshapiro#define SRV_OFFER_AUTH 0x0004 /* offer AUTH */ 5690792Sgshapiro#define SRV_OFFER_ETRN 0x0008 /* offer ETRN */ 5790792Sgshapiro#define SRV_OFFER_VRFY 0x0010 /* offer VRFY (not yet used) */ 5890792Sgshapiro#define SRV_OFFER_EXPN 0x0020 /* offer EXPN */ 5990792Sgshapiro#define SRV_OFFER_VERB 0x0040 /* offer VERB */ 6090792Sgshapiro#define SRV_OFFER_DSN 0x0080 /* offer DSN */ 6190792Sgshapiro#if PIPELINING 6290792Sgshapiro# define SRV_OFFER_PIPE 0x0100 /* offer PIPELINING */ 6390792Sgshapiro# if _FFR_NO_PIPE 6490792Sgshapiro# define SRV_NO_PIPE 0x0200 /* disable PIPELINING, sleep if used */ 65363466Sgshapiro# endif 6690792Sgshapiro#endif /* PIPELINING */ 6790792Sgshapiro#define SRV_REQ_AUTH 0x0400 /* require AUTH */ 68132943Sgshapiro#define SRV_REQ_SEC 0x0800 /* require security - equiv to AuthOptions=p */ 6990792Sgshapiro#define SRV_TMP_FAIL 0x1000 /* ruleset caused a temporary failure */ 70363466Sgshapiro#if _FFR_EAI 71363466Sgshapiro# define SRV_OFFER_EAI 0x2000 /* offer SMTPUTF* */ 72363466Sgshapiro#endif 7390792Sgshapiro 7490792Sgshapirostatic unsigned int srvfeatures __P((ENVELOPE *, char *, unsigned int)); 7590792Sgshapiro 76132943Sgshapiro#define STOP_ATTACK ((time_t) -1) 77132943Sgshapirostatic time_t checksmtpattack __P((volatile unsigned int *, unsigned int, 78132943Sgshapiro bool, char *, ENVELOPE *)); 7964562Sgshapirostatic void printvrfyaddr __P((ADDRESS *, bool, bool)); 8064562Sgshapirostatic char *skipword __P((char *volatile, char *)); 8190792Sgshapirostatic void setup_smtpd_io __P((void)); 82120256Sgshapiro 83120256Sgshapiro#if SASL 84363466Sgshapiro# ifndef MAX_AUTH_USER_LEN 85363466Sgshapiro# define MAX_AUTH_USER_LEN 256 86363466Sgshapiro# endif 87363466Sgshapiro# ifndef MAX_AUTH_LOG_LEN 88363466Sgshapiro# define MAX_AUTH_LOG_LEN 64 89363466Sgshapiro# endif 90363466Sgshapirostatic void get_sasl_user __P((char *, unsigned int, const char *, char *out, size_t)); 91363466Sgshapiro# define RESET_AUTH_FAIL_LOG_USER \ 92363466Sgshapiro do \ 93363466Sgshapiro { \ 94363466Sgshapiro (void) memset(auth_user, 0, sizeof(auth_user)); \ 95363466Sgshapiro (void) memset(auth_user_tmp, 0, sizeof(auth_user_tmp)); \ 96363466Sgshapiro auth_user_len = 0; \ 97363466Sgshapiro } while (0) 98363466Sgshapiro# define SET_AUTH_USER_TMP(s, len) \ 99363466Sgshapiro do \ 100363466Sgshapiro { \ 101363466Sgshapiro auth_user_len = SM_MIN(len, MAX_AUTH_USER_LEN-1); \ 102363466Sgshapiro (void) memcpy(auth_user_tmp, s, auth_user_len); \ 103363466Sgshapiro } while (0) 104363466Sgshapiro# define SET_AUTH_USER \ 105363466Sgshapiro get_sasl_user(auth_user_tmp, auth_user_len, auth_type, auth_user, sizeof(auth_user)) 106363466Sgshapiro# define SET_AUTH_USER_CONDITIONALLY \ 107363466Sgshapiro if ('\0' == auth_user[0]) \ 108363466Sgshapiro SET_AUTH_USER; 109363466Sgshapiro# define LOG_AUTH_FAIL_USER ", user=", (int)MAX_AUTH_LOG_LEN, auth_user 110120256Sgshapiro# if SASL >= 20000 111120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname, 112120256Sgshapiro char *_remoteip, char *_localip, 113120256Sgshapiro char *_auth_id, sasl_ssf_t *_ext_ssf)); 114120256Sgshapiro 115120256Sgshapiro# define RESET_SASLCONN \ 116147078Sgshapiro do \ 117147078Sgshapiro { \ 118363466Sgshapiro RESET_AUTH_FAIL_LOG_USER; \ 119147078Sgshapiro result = reset_saslconn(&conn, AuthRealm, remoteip, \ 120147078Sgshapiro localip, auth_id, &ext_ssf); \ 121147078Sgshapiro if (result != SASL_OK) \ 122147078Sgshapiro sasl_ok = false; \ 123147078Sgshapiro } while (0) 124120256Sgshapiro 125120256Sgshapiro# else /* SASL >= 20000 */ 126120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname, 127120256Sgshapiro struct sockaddr_in *_saddr_r, 128120256Sgshapiro struct sockaddr_in *_saddr_l, 129120256Sgshapiro sasl_external_properties_t *_ext_ssf)); 130120256Sgshapiro# define RESET_SASLCONN \ 131147078Sgshapiro do \ 132147078Sgshapiro { \ 133363466Sgshapiro RESET_AUTH_FAIL_LOG_USER; \ 134147078Sgshapiro result = reset_saslconn(&conn, AuthRealm, &saddr_r, \ 135147078Sgshapiro &saddr_l, &ext_ssf); \ 136147078Sgshapiro if (result != SASL_OK) \ 137147078Sgshapiro sasl_ok = false; \ 138147078Sgshapiro } while (0) 139120256Sgshapiro 140120256Sgshapiro# endif /* SASL >= 20000 */ 141120256Sgshapiro#endif /* SASL */ 142120256Sgshapiro 143363466Sgshapiro#if !defined(RESET_AUTH_FAIL_LOG_USER) 144363466Sgshapiro# define RESET_AUTH_FAIL_LOG_USER 145363466Sgshapiro#endif 146363466Sgshapiro 14764562Sgshapiroextern ENVELOPE BlankEnvelope; 14838032Speter 149132943Sgshapiro#define NBADRCPTS \ 150132943Sgshapiro do \ 151132943Sgshapiro { \ 152132943Sgshapiro char buf[16]; \ 153168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%d", \ 154132943Sgshapiro BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \ 155132943Sgshapiro ? n_badrcpts - 1 : n_badrcpts); \ 156132943Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \ 157132943Sgshapiro } while (0) 158132943Sgshapiro 159363466Sgshapiro#define SKIP_SPACE(s) while (SM_ISSPACE(*s)) \ 16090792Sgshapiro (s)++ 16190792Sgshapiro 162363466Sgshapiro#if _FFR_EAI 16338032Speter/* 164363466Sgshapiro** ADDR_IS_ASCII -- check whether an address is 100% printable ASCII 165168515Sgshapiro** 166168515Sgshapiro** Parameters: 167363466Sgshapiro** a -- an address (or other string) 168363466Sgshapiro** 169363466Sgshapiro** Returns: 170363466Sgshapiro** TRUE if a is non-NULL and points to only printable ASCII 171363466Sgshapiro** FALSE if a is NULL and points to printable ASCII 172363466Sgshapiro** FALSE if a is non-NULL and points to something containing 8-bittery 173363466Sgshapiro*/ 174363466Sgshapiro 175363466Sgshapirobool 176363466Sgshapiroaddr_is_ascii(a) 177363466Sgshapiro const char * a; 178363466Sgshapiro{ 179363466Sgshapiro while (a != NULL && *a != '\0' && *a >= ' ' && (unsigned char)*a < 127) 180363466Sgshapiro a++; 181363466Sgshapiro return (a != NULL && *a == '\0'); 182363466Sgshapiro} 183363466Sgshapiro#endif 184363466Sgshapiro 185363466Sgshapiro/* 186363466Sgshapiro** PARSE_ESMTP_ARGS -- parse ESMTP arguments (for MAIL, RCPT) 187363466Sgshapiro** 188363466Sgshapiro** Parameters: 189168515Sgshapiro** e -- the envelope 190168515Sgshapiro** addr_st -- address (RCPT only) 191168515Sgshapiro** p -- read buffer 192168515Sgshapiro** delimptr -- current position in read buffer 193168515Sgshapiro** which -- MAIL/RCPT 194168515Sgshapiro** args -- arguments (output) 195168515Sgshapiro** esmtp_args -- function to process a single ESMTP argument 196168515Sgshapiro** 197168515Sgshapiro** Returns: 198168515Sgshapiro** none 199168515Sgshapiro*/ 200168515Sgshapiro 201168515Sgshapirovoid 202168515Sgshapiroparse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args) 203168515Sgshapiro ENVELOPE *e; 204168515Sgshapiro ADDRESS *addr_st; 205168515Sgshapiro char *p; 206168515Sgshapiro char *delimptr; 207168515Sgshapiro char *which; 208168515Sgshapiro char *args[]; 209168515Sgshapiro esmtp_args_F esmtp_args; 210168515Sgshapiro{ 211168515Sgshapiro int argno; 212168515Sgshapiro 213168515Sgshapiro argno = 0; 214168515Sgshapiro if (args != NULL) 215168515Sgshapiro args[argno++] = p; 216168515Sgshapiro p = delimptr; 217168515Sgshapiro while (p != NULL && *p != '\0') 218168515Sgshapiro { 219168515Sgshapiro char *kp; 220168515Sgshapiro char *vp = NULL; 221168515Sgshapiro char *equal = NULL; 222168515Sgshapiro 223168515Sgshapiro /* locate the beginning of the keyword */ 224168515Sgshapiro SKIP_SPACE(p); 225168515Sgshapiro if (*p == '\0') 226168515Sgshapiro break; 227168515Sgshapiro kp = p; 228168515Sgshapiro 229168515Sgshapiro /* skip to the value portion */ 230168515Sgshapiro while ((isascii(*p) && isalnum(*p)) || *p == '-') 231168515Sgshapiro p++; 232168515Sgshapiro if (*p == '=') 233168515Sgshapiro { 234168515Sgshapiro equal = p; 235168515Sgshapiro *p++ = '\0'; 236168515Sgshapiro vp = p; 237168515Sgshapiro 238168515Sgshapiro /* skip to the end of the value */ 239168515Sgshapiro while (*p != '\0' && *p != ' ' && 240168515Sgshapiro !(isascii(*p) && iscntrl(*p)) && 241168515Sgshapiro *p != '=') 242168515Sgshapiro p++; 243168515Sgshapiro } 244168515Sgshapiro 245168515Sgshapiro if (*p != '\0') 246168515Sgshapiro *p++ = '\0'; 247168515Sgshapiro 248168515Sgshapiro if (tTd(19, 1)) 249168515Sgshapiro sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp, 250168515Sgshapiro vp == NULL ? "<null>" : vp); 251168515Sgshapiro 252168515Sgshapiro esmtp_args(addr_st, kp, vp, e); 253168515Sgshapiro if (equal != NULL) 254168515Sgshapiro *equal = '='; 255168515Sgshapiro if (args != NULL) 256168515Sgshapiro args[argno] = kp; 257168515Sgshapiro argno++; 258168515Sgshapiro if (argno >= MAXSMTPARGS - 1) 259168515Sgshapiro usrerr("501 5.5.4 Too many parameters"); 260168515Sgshapiro if (Errors > 0) 261168515Sgshapiro break; 262168515Sgshapiro } 263168515Sgshapiro if (args != NULL) 264168515Sgshapiro args[argno] = NULL; 265168515Sgshapiro} 266168515Sgshapiro 267285229Sgshapiro#if _FFR_ADD_BCC 268285229Sgshapiro 269168515Sgshapiro/* 270285229Sgshapiro** ADDRCPT -- Add a rcpt to sendq list 271285229Sgshapiro** 272285229Sgshapiro** Parameters: 273285229Sgshapiro** rcpt -- rcpt 274285229Sgshapiro** sendq -- a pointer to the head of a queue to put 275285229Sgshapiro** these people into. 276285229Sgshapiro** e -- the envelope in which to add these recipients. 277285229Sgshapiro** 278285229Sgshapiro** Returns: 279285229Sgshapiro** The number of addresses added to the list. 280285229Sgshapiro*/ 281285229Sgshapiro 282285229Sgshapirostatic int 283285229Sgshapiroaddrcpt(rcpt, sendq, e) 284285229Sgshapiro char *rcpt; 285285229Sgshapiro ADDRESS **sendq; 286285229Sgshapiro ENVELOPE *e; 287285229Sgshapiro{ 288285229Sgshapiro int r; 289285229Sgshapiro char *oldto; 290285229Sgshapiro ADDRESS *a; 291285229Sgshapiro 292285229Sgshapiro SM_REQUIRE(rcpt != NULL); 293285229Sgshapiro SM_REQUIRE(sendq != NULL); 294285229Sgshapiro SM_REQUIRE(e != NULL); 295285229Sgshapiro oldto = e->e_to; 296285229Sgshapiro if (tTd(25, 1)) 297285229Sgshapiro sm_dprintf("addrcpt: rcpt=%s\n", rcpt); 298285229Sgshapiro r = Errors; 299285229Sgshapiro a = NULL; 300285229Sgshapiro SM_TRY 301285229Sgshapiro { 302285229Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e b"); 303285229Sgshapiro a = parseaddr(rcpt, NULLADDR, RF_COPYALL, ' ', NULL, e, true); 304285229Sgshapiro if (a == NULL) 305285229Sgshapiro return 0; 306285229Sgshapiro 307285229Sgshapiro a->q_flags &= ~Q_PINGFLAGS; 308285229Sgshapiro a->q_flags |= QINTBCC; 309285229Sgshapiro a->q_owner = "<>"; 310285229Sgshapiro 311285229Sgshapiro /* disable alias expansion? */ 312285229Sgshapiro a = recipient(a, sendq, 0, e); 313285229Sgshapiro } 314285229Sgshapiro SM_FINALLY 315285229Sgshapiro { 316285229Sgshapiro e->e_to = oldto; 317285229Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL); 318285229Sgshapiro } 319285229Sgshapiro SM_END_TRY 320285229Sgshapiro if (tTd(25, 1)) 321285229Sgshapiro sm_dprintf("addrcpt: rcpt=%s, flags=%#lx\n", rcpt, 322285229Sgshapiro a != NULL ? a->q_flags : 0); 323285229Sgshapiro Errors = r; 324285229Sgshapiro return 1; 325285229Sgshapiro} 326285229Sgshapiro 327285229Sgshapiro/* 328285229Sgshapiro** ADDBCC -- Maybe create a copy of an e-mail 329285229Sgshapiro** 330285229Sgshapiro** Parameters: 331285229Sgshapiro** a -- current RCPT 332285229Sgshapiro** e -- the envelope. 333285229Sgshapiro** 334285229Sgshapiro** Returns: 335285229Sgshapiro** nothing 336285229Sgshapiro** 337285229Sgshapiro** Side Effects: 338285229Sgshapiro** rscheck() can trigger an "exception" 339285229Sgshapiro*/ 340285229Sgshapiro 341285229Sgshapirostatic void 342285229Sgshapiroaddbcc(a, e) 343285229Sgshapiro ADDRESS *a; 344285229Sgshapiro ENVELOPE *e; 345285229Sgshapiro{ 346285229Sgshapiro int nobcc; 347285229Sgshapiro char *newrcpt, empty[1]; 348285229Sgshapiro 349285229Sgshapiro if (!AddBcc) 350285229Sgshapiro return; 351285229Sgshapiro 352285229Sgshapiro nobcc = false; 353285229Sgshapiro empty[0] = '\0'; 354285229Sgshapiro newrcpt = empty; 355285229Sgshapiro 356285229Sgshapiro nobcc = rscheck("bcc", a->q_paddr, NULL, e, RSF_ADDR, 12, NULL, NOQID, 357285229Sgshapiro NULL, &newrcpt); 358285229Sgshapiro if (tTd(25, 1)) 359285229Sgshapiro sm_dprintf("addbcc: nobcc=%d, Errors=%d, newrcpt=<%s>\n", nobcc, Errors, newrcpt); 360285229Sgshapiro if (nobcc != EX_OK || Errors > 0 || *newrcpt == '\0') 361285229Sgshapiro return; 362285229Sgshapiro 363285229Sgshapiro (void) addrcpt(newrcpt, &e->e_sendqueue, e); 364285229Sgshapiro return; 365285229Sgshapiro} 366285229Sgshapiro#else /* _FFR_ADD_BCC */ 367285229Sgshapiro# define addbcc(a, e) 368285229Sgshapiro#endif /* _FFR_ADD_BCC */ 369285229Sgshapiro 370285229Sgshapiro#if _FFR_RCPTFLAGS 371285229Sgshapiro/* 372285229Sgshapiro** RCPTMODS -- Perform rcpt modifications if requested 373285229Sgshapiro** 374285229Sgshapiro** Parameters: 375285229Sgshapiro** rcpt -- current RCPT 376285229Sgshapiro** e -- the envelope. 377285229Sgshapiro** 378285229Sgshapiro** Returns: 379285229Sgshapiro** nothing. 380285229Sgshapiro*/ 381285229Sgshapiro 382285229Sgshapirovoid 383285229Sgshapirorcptmods(rcpt, e) 384285229Sgshapiro ADDRESS *rcpt; 385285229Sgshapiro ENVELOPE *e; 386285229Sgshapiro{ 387285229Sgshapiro char *fl; 388285229Sgshapiro 389285229Sgshapiro SM_REQUIRE(rcpt != NULL); 390285229Sgshapiro SM_REQUIRE(e != NULL); 391285229Sgshapiro 392285229Sgshapiro fl = macvalue(macid("{rcpt_flags}"), e); 393285229Sgshapiro if (fl == NULL || *fl == '\0') 394285229Sgshapiro return; 395285229Sgshapiro if (tTd(25, 1)) 396285229Sgshapiro sm_dprintf("rcptmods: rcpt=%s, flags=%s\n", rcpt->q_paddr, fl); 397285229Sgshapiro 398285229Sgshapiro /* parse flags */ 399285229Sgshapiro for ( ; *fl != '\0'; ++fl) 400285229Sgshapiro { 401285229Sgshapiro switch (*fl) 402285229Sgshapiro { 403285229Sgshapiro case 'n': 404285229Sgshapiro rcpt->q_flags &= ~Q_PINGFLAGS; 405285229Sgshapiro rcpt->q_flags |= QINTBCC; 406285229Sgshapiro rcpt->q_owner = "<>"; 407285229Sgshapiro break; 408285229Sgshapiro 409285229Sgshapiro case 'N': 410285229Sgshapiro rcpt->q_flags &= ~Q_PINGFLAGS; 411285229Sgshapiro rcpt->q_owner = "<>"; 412285229Sgshapiro break; 413285229Sgshapiro 414285229Sgshapiro case QDYNMAILFLG: 415285229Sgshapiro rcpt->q_flags |= QDYNMAILER; 416285229Sgshapiro newmodmailer(rcpt, *fl); 417285229Sgshapiro break; 418285229Sgshapiro 419285229Sgshapiro default: 420285229Sgshapiro sm_syslog(LOG_INFO, e->e_id, 421285229Sgshapiro "rcpt=%s, rcpt_flags=%s, status=unknown", 422285229Sgshapiro rcpt->q_paddr, fl); 423285229Sgshapiro break; 424285229Sgshapiro } 425285229Sgshapiro } 426285229Sgshapiro 427285229Sgshapiro /* reset macro to avoid confusion later on */ 428285229Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{rcpt_flags}"), NULL); 429285229Sgshapiro 430285229Sgshapiro} 431285229Sgshapiro#else /* _FFR_RCPTFLAGS */ 432285229Sgshapiro# define rcptmods(a, e) 433285229Sgshapiro#endif /* _FFR_RCPTFLAGS */ 434285229Sgshapiro 435285229Sgshapiro/* 43638032Speter** SMTP -- run the SMTP protocol. 43738032Speter** 43838032Speter** Parameters: 43938032Speter** nullserver -- if non-NULL, rejection message for 44090792Sgshapiro** (almost) all SMTP commands. 44190792Sgshapiro** d_flags -- daemon flags 44238032Speter** e -- the envelope. 44338032Speter** 44438032Speter** Returns: 44538032Speter** never. 44638032Speter** 44738032Speter** Side Effects: 44890792Sgshapiro** Reads commands from the input channel and processes them. 44938032Speter*/ 45038032Speter 45190792Sgshapiro/* 45290792Sgshapiro** Notice: The smtp server doesn't have a session context like the client 45390792Sgshapiro** side has (mci). Therefore some data (session oriented) is allocated 45490792Sgshapiro** or assigned to the "wrong" structure (esp. STARTTLS, AUTH). 45590792Sgshapiro** This should be fixed in a successor version. 45690792Sgshapiro*/ 45790792Sgshapiro 45838032Speterstruct cmd 45938032Speter{ 46064562Sgshapiro char *cmd_name; /* command name */ 46164562Sgshapiro int cmd_code; /* internal code, see below */ 46238032Speter}; 46338032Speter 46464562Sgshapiro/* values for cmd_code */ 46590792Sgshapiro#define CMDERROR 0 /* bad command */ 46690792Sgshapiro#define CMDMAIL 1 /* mail -- designate sender */ 46790792Sgshapiro#define CMDRCPT 2 /* rcpt -- designate recipient */ 46890792Sgshapiro#define CMDDATA 3 /* data -- send message text */ 46990792Sgshapiro#define CMDRSET 4 /* rset -- reset state */ 47090792Sgshapiro#define CMDVRFY 5 /* vrfy -- verify address */ 47190792Sgshapiro#define CMDEXPN 6 /* expn -- expand address */ 47290792Sgshapiro#define CMDNOOP 7 /* noop -- do nothing */ 47390792Sgshapiro#define CMDQUIT 8 /* quit -- close connection and die */ 47490792Sgshapiro#define CMDHELO 9 /* helo -- be polite */ 47590792Sgshapiro#define CMDHELP 10 /* help -- give usage info */ 47690792Sgshapiro#define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 47790792Sgshapiro#define CMDETRN 12 /* etrn -- flush queue */ 47890792Sgshapiro#if SASL 47990792Sgshapiro# define CMDAUTH 13 /* auth -- SASL authenticate */ 480363466Sgshapiro#endif 48190792Sgshapiro#if STARTTLS 48290792Sgshapiro# define CMDSTLS 14 /* STARTTLS -- start TLS session */ 483363466Sgshapiro#endif 48438032Speter/* non-standard commands */ 48590792Sgshapiro#define CMDVERB 17 /* verb -- go into verbose mode */ 48664562Sgshapiro/* unimplemented commands from RFC 821 */ 48790792Sgshapiro#define CMDUNIMPL 19 /* unimplemented rfc821 commands */ 48838032Speter/* use this to catch and log "door handle" attempts on your system */ 48990792Sgshapiro#define CMDLOGBOGUS 23 /* bogus command that should be logged */ 49038032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */ 49190792Sgshapiro#define CMDDBGQSHOW 24 /* showq -- show send queue */ 49290792Sgshapiro#define CMDDBGDEBUG 25 /* debug -- set debug mode */ 49338032Speter 49466494Sgshapiro/* 49590792Sgshapiro** Note: If you change this list, remember to update 'helpfile' 49666494Sgshapiro*/ 49766494Sgshapiro 49838032Speterstatic struct cmd CmdTab[] = 49938032Speter{ 50038032Speter { "mail", CMDMAIL }, 50138032Speter { "rcpt", CMDRCPT }, 50238032Speter { "data", CMDDATA }, 50338032Speter { "rset", CMDRSET }, 50438032Speter { "vrfy", CMDVRFY }, 50538032Speter { "expn", CMDEXPN }, 50638032Speter { "help", CMDHELP }, 50738032Speter { "noop", CMDNOOP }, 50838032Speter { "quit", CMDQUIT }, 50938032Speter { "helo", CMDHELO }, 51038032Speter { "ehlo", CMDEHLO }, 51138032Speter { "etrn", CMDETRN }, 51238032Speter { "verb", CMDVERB }, 51364562Sgshapiro { "send", CMDUNIMPL }, 51464562Sgshapiro { "saml", CMDUNIMPL }, 51564562Sgshapiro { "soml", CMDUNIMPL }, 51664562Sgshapiro { "turn", CMDUNIMPL }, 51790792Sgshapiro#if SASL 51864562Sgshapiro { "auth", CMDAUTH, }, 519363466Sgshapiro#endif 52090792Sgshapiro#if STARTTLS 52164562Sgshapiro { "starttls", CMDSTLS, }, 522363466Sgshapiro#endif 52338032Speter /* remaining commands are here only to trap and log attempts to use them */ 52438032Speter { "showq", CMDDBGQSHOW }, 52538032Speter { "debug", CMDDBGDEBUG }, 52638032Speter { "wiz", CMDLOGBOGUS }, 52738032Speter 52838032Speter { NULL, CMDERROR } 52938032Speter}; 53038032Speter 53164562Sgshapirostatic char *CurSmtpClient; /* who's at the other end of channel */ 53238032Speter 53390792Sgshapiro#ifndef MAXBADCOMMANDS 53490792Sgshapiro# define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 535363466Sgshapiro#endif 53690792Sgshapiro#ifndef MAXHELOCOMMANDS 53790792Sgshapiro# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ 538363466Sgshapiro#endif 53990792Sgshapiro#ifndef MAXVRFYCOMMANDS 54090792Sgshapiro# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ 541363466Sgshapiro#endif 54290792Sgshapiro#ifndef MAXETRNCOMMANDS 54390792Sgshapiro# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ 544363466Sgshapiro#endif 54590792Sgshapiro#ifndef MAXTIMEOUT 54690792Sgshapiro# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */ 547363466Sgshapiro#endif 54838032Speter 549132943Sgshapiro/* 550132943Sgshapiro** Maximum shift value to compute timeout for bad commands. 551132943Sgshapiro** This introduces an upper limit of 2^MAXSHIFT for the timeout. 552132943Sgshapiro*/ 553132943Sgshapiro 554132943Sgshapiro#ifndef MAXSHIFT 555132943Sgshapiro# define MAXSHIFT 8 556363466Sgshapiro#endif 557132943Sgshapiro#if MAXSHIFT > 31 558132943Sgshapiro ERROR _MAXSHIFT > 31 is invalid 559363466Sgshapiro#endif 560132943Sgshapiro 561132943Sgshapiro 562132943Sgshapiro#if MAXBADCOMMANDS > 0 563132943Sgshapiro# define STOP_IF_ATTACK(r) do \ 564132943Sgshapiro { \ 565132943Sgshapiro if ((r) == STOP_ATTACK) \ 566132943Sgshapiro goto stopattack; \ 567132943Sgshapiro } while (0) 568132943Sgshapiro 569132943Sgshapiro#else /* MAXBADCOMMANDS > 0 */ 570132943Sgshapiro# define STOP_IF_ATTACK(r) r 571132943Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 572132943Sgshapiro 573132943Sgshapiro 57490792Sgshapiro#if SM_HEAP_CHECK 57590792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp", 57690792Sgshapiro "@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $"); 577363466Sgshapiro#endif 57838032Speter 57990792Sgshapirotypedef struct 58090792Sgshapiro{ 581173340Sgshapiro bool sm_gotmail; /* mail command received */ 582173340Sgshapiro unsigned int sm_nrcpts; /* number of successful RCPT commands */ 583173340Sgshapiro bool sm_discard; 58490792Sgshapiro#if MILTER 585173340Sgshapiro bool sm_milterize; 586173340Sgshapiro bool sm_milterlist; /* any filters in the list? */ 587173340Sgshapiro milters_T sm_milters; 588173340Sgshapiro 589173340Sgshapiro /* e_nrcpts from envelope before recipient() call */ 590173340Sgshapiro unsigned int sm_e_nrcpts_orig; 59190792Sgshapiro#endif /* MILTER */ 592173340Sgshapiro char *sm_quarmsg; /* carry quarantining across messages */ 59390792Sgshapiro} SMTP_T; 59490792Sgshapiro 595132943Sgshapirostatic bool smtp_data __P((SMTP_T *, ENVELOPE *)); 59690792Sgshapiro 597132943Sgshapiro#define MSG_TEMPFAIL "451 4.3.2 Please try again later" 59890792Sgshapiro 59990792Sgshapiro#if MILTER 60090792Sgshapiro# define MILTER_ABORT(e) milter_abort((e)) 601110560Sgshapiro 60290792Sgshapiro# define MILTER_REPLY(str) \ 60390792Sgshapiro { \ 60490792Sgshapiro int savelogusrerrs = LogUsrErrs; \ 60590792Sgshapiro \ 606168515Sgshapiro milter_cmd_fail = true; \ 60790792Sgshapiro switch (state) \ 60890792Sgshapiro { \ 609157001Sgshapiro case SMFIR_SHUTDOWN: \ 610157001Sgshapiro if (MilterLogLevel > 3) \ 611157001Sgshapiro { \ 612157001Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 613157001Sgshapiro "Milter: %s=%s, reject=421, errormode=4", \ 614157001Sgshapiro str, addr); \ 615157001Sgshapiro LogUsrErrs = false; \ 616157001Sgshapiro } \ 617157001Sgshapiro { \ 618157001Sgshapiro bool tsave = QuickAbort; \ 619157001Sgshapiro \ 620157001Sgshapiro QuickAbort = false; \ 621157001Sgshapiro usrerr("421 4.3.0 closing connection"); \ 622157001Sgshapiro QuickAbort = tsave; \ 623157001Sgshapiro e->e_sendqueue = NULL; \ 624157001Sgshapiro goto doquit; \ 625157001Sgshapiro } \ 626157001Sgshapiro break; \ 62790792Sgshapiro case SMFIR_REPLYCODE: \ 62890792Sgshapiro if (MilterLogLevel > 3) \ 62990792Sgshapiro { \ 63090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 63190792Sgshapiro "Milter: %s=%s, reject=%s", \ 63290792Sgshapiro str, addr, response); \ 63390792Sgshapiro LogUsrErrs = false; \ 63490792Sgshapiro } \ 635157001Sgshapiro if (strncmp(response, "421 ", 4) == 0 \ 636157001Sgshapiro || strncmp(response, "421-", 4) == 0) \ 637132943Sgshapiro { \ 638132943Sgshapiro bool tsave = QuickAbort; \ 639132943Sgshapiro \ 640132943Sgshapiro QuickAbort = false; \ 641132943Sgshapiro usrerr(response); \ 642132943Sgshapiro QuickAbort = tsave; \ 643132943Sgshapiro e->e_sendqueue = NULL; \ 644132943Sgshapiro goto doquit; \ 645132943Sgshapiro } \ 646132943Sgshapiro else \ 647132943Sgshapiro usrerr(response); \ 64890792Sgshapiro break; \ 64990792Sgshapiro \ 65090792Sgshapiro case SMFIR_REJECT: \ 65190792Sgshapiro if (MilterLogLevel > 3) \ 65290792Sgshapiro { \ 65390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 65490792Sgshapiro "Milter: %s=%s, reject=550 5.7.1 Command rejected", \ 65590792Sgshapiro str, addr); \ 65690792Sgshapiro LogUsrErrs = false; \ 65790792Sgshapiro } \ 65890792Sgshapiro usrerr("550 5.7.1 Command rejected"); \ 65990792Sgshapiro break; \ 66090792Sgshapiro \ 66190792Sgshapiro case SMFIR_DISCARD: \ 66290792Sgshapiro if (MilterLogLevel > 3) \ 66390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 66490792Sgshapiro "Milter: %s=%s, discard", \ 66590792Sgshapiro str, addr); \ 66690792Sgshapiro e->e_flags |= EF_DISCARD; \ 667168515Sgshapiro milter_cmd_fail = false; \ 66890792Sgshapiro break; \ 66990792Sgshapiro \ 67090792Sgshapiro case SMFIR_TEMPFAIL: \ 67190792Sgshapiro if (MilterLogLevel > 3) \ 67290792Sgshapiro { \ 67390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 67490792Sgshapiro "Milter: %s=%s, reject=%s", \ 67590792Sgshapiro str, addr, MSG_TEMPFAIL); \ 67690792Sgshapiro LogUsrErrs = false; \ 67790792Sgshapiro } \ 67890792Sgshapiro usrerr(MSG_TEMPFAIL); \ 67990792Sgshapiro break; \ 680168515Sgshapiro default: \ 681168515Sgshapiro milter_cmd_fail = false; \ 682168515Sgshapiro break; \ 68390792Sgshapiro } \ 68490792Sgshapiro LogUsrErrs = savelogusrerrs; \ 68590792Sgshapiro if (response != NULL) \ 68690792Sgshapiro sm_free(response); /* XXX */ \ 68790792Sgshapiro } 68890792Sgshapiro 68990792Sgshapiro#else /* MILTER */ 69090792Sgshapiro# define MILTER_ABORT(e) 69190792Sgshapiro#endif /* MILTER */ 69290792Sgshapiro 69390792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */ 69490792Sgshapiro#define CLEAR_STATE(cmd) \ 695132943Sgshapirodo \ 69690792Sgshapiro{ \ 69790792Sgshapiro /* abort milter filters */ \ 69890792Sgshapiro MILTER_ABORT(e); \ 69990792Sgshapiro \ 70090792Sgshapiro if (smtp.sm_nrcpts > 0) \ 70190792Sgshapiro { \ 70290792Sgshapiro logundelrcpts(e, cmd, 10, false); \ 70390792Sgshapiro smtp.sm_nrcpts = 0; \ 70490792Sgshapiro macdefine(&e->e_macro, A_PERM, \ 70590792Sgshapiro macid("{nrcpts}"), "0"); \ 70690792Sgshapiro } \ 70790792Sgshapiro \ 70890792Sgshapiro e->e_sendqueue = NULL; \ 70990792Sgshapiro e->e_flags |= EF_CLRQUEUE; \ 71090792Sgshapiro \ 711203004Sgshapiro if (tTd(92, 2)) \ 712203004Sgshapiro sm_dprintf("CLEAR_STATE: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",\ 713203004Sgshapiro e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);\ 71490792Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) \ 71590792Sgshapiro logsender(e, NULL); \ 71690792Sgshapiro e->e_flags &= ~EF_LOGSENDER; \ 71790792Sgshapiro \ 71890792Sgshapiro /* clean up a bit */ \ 71990792Sgshapiro smtp.sm_gotmail = false; \ 72090792Sgshapiro SuprErrs = true; \ 721203004Sgshapiro (void) dropenvelope(e, true, false); \ 72290792Sgshapiro sm_rpool_free(e->e_rpool); \ 72390792Sgshapiro e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL)); \ 72490792Sgshapiro CurEnv = e; \ 725168515Sgshapiro e->e_features = features; \ 726132943Sgshapiro \ 727132943Sgshapiro /* put back discard bit */ \ 728132943Sgshapiro if (smtp.sm_discard) \ 729132943Sgshapiro e->e_flags |= EF_DISCARD; \ 730132943Sgshapiro \ 731132943Sgshapiro /* restore connection quarantining */ \ 732132943Sgshapiro if (smtp.sm_quarmsg == NULL) \ 733132943Sgshapiro { \ 734132943Sgshapiro e->e_quarmsg = NULL; \ 735132943Sgshapiro macdefine(&e->e_macro, A_PERM, \ 736132943Sgshapiro macid("{quarantine}"), ""); \ 737132943Sgshapiro } \ 738132943Sgshapiro else \ 739132943Sgshapiro { \ 740132943Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, \ 741132943Sgshapiro smtp.sm_quarmsg); \ 742132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), \ 743132943Sgshapiro e->e_quarmsg); \ 744132943Sgshapiro } \ 745132943Sgshapiro} while (0) 74690792Sgshapiro 74790792Sgshapiro/* sleep to flatten out connection load */ 74890792Sgshapiro#define MIN_DELAY_LOG 15 /* wait before logging this again */ 74990792Sgshapiro 75090792Sgshapiro/* is it worth setting the process title for 1s? */ 75190792Sgshapiro#define DELAY_CONN(cmd) \ 75290792Sgshapiro if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA) \ 75390792Sgshapiro { \ 75490792Sgshapiro time_t dnow; \ 75590792Sgshapiro \ 75690792Sgshapiro sm_setproctitle(true, e, \ 75790792Sgshapiro "%s: %s: delaying %s: load average: %d", \ 75890792Sgshapiro qid_printname(e), CurSmtpClient, \ 75990792Sgshapiro cmd, DelayLA); \ 76090792Sgshapiro if (LogLevel > 8 && (dnow = curtime()) > log_delay) \ 76190792Sgshapiro { \ 76290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, \ 76390792Sgshapiro "delaying=%s, load average=%d >= %d", \ 76490792Sgshapiro cmd, CurrentLA, DelayLA); \ 76590792Sgshapiro log_delay = dnow + MIN_DELAY_LOG; \ 76690792Sgshapiro } \ 76790792Sgshapiro (void) sleep(1); \ 76890792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", \ 76990792Sgshapiro qid_printname(e), CurSmtpClient, inp); \ 77090792Sgshapiro } 77190792Sgshapiro 772285229Sgshapiro/* 773285229Sgshapiro** Determine the correct protocol keyword to use in the 774285229Sgshapiro** Received: header, following RFC 3848. 775285229Sgshapiro*/ 776285229Sgshapiro 777285229Sgshapiro#if !STARTTLS 778285229Sgshapiro# define tls_active false 779285229Sgshapiro#endif 780285229Sgshapiro#if SASL 781285229Sgshapiro# define auth_active (authenticating == SASL_IS_AUTH) 782285229Sgshapiro#else 783285229Sgshapiro# define auth_active false 784285229Sgshapiro#endif 785363466Sgshapiro#if _FFR_EAI 786285229Sgshapiro#define GET_PROTOCOL() \ 787363466Sgshapiro (e->e_smtputf8 \ 788363466Sgshapiro ? (auth_active \ 789363466Sgshapiro ? (tls_active ? "UTF8SMTPSA" : "UTF8SMTPA") \ 790363466Sgshapiro : (tls_active ? "UTF8SMTPS" : "UTF8SMTP")) \ 791363466Sgshapiro : (auth_active \ 792363466Sgshapiro ? (tls_active ? "ESMTPSA" : "ESMTPA") \ 793363466Sgshapiro : (tls_active ? "ESMTPS" : "ESMTP"))) 794363466Sgshapiro#else /* _FFR_EAI */ 795363466Sgshapiro#define GET_PROTOCOL() \ 796285229Sgshapiro (auth_active \ 797285229Sgshapiro ? (tls_active ? "ESMTPSA" : "ESMTPA") \ 798285229Sgshapiro : (tls_active ? "ESMTPS" : "ESMTP")) 799363466Sgshapiro#endif /* _FFR_EAI */ 800285229Sgshapiro 801168515Sgshapirostatic bool SevenBitInput_Saved; /* saved version of SevenBitInput */ 80290792Sgshapiro 80338032Spetervoid 80464562Sgshapirosmtp(nullserver, d_flags, e) 80564562Sgshapiro char *volatile nullserver; 80664562Sgshapiro BITMAP256 d_flags; 80738032Speter register ENVELOPE *volatile e; 80838032Speter{ 80938032Speter register char *volatile p; 81064562Sgshapiro register struct cmd *volatile c = NULL; 81138032Speter char *cmd; 81238032Speter auto ADDRESS *vrfyqueue; 81338032Speter ADDRESS *a; 81438032Speter volatile bool gothello; /* helo command received */ 81538032Speter bool vrfy; /* set if this is a vrfy command */ 81638032Speter char *volatile protocol; /* sending protocol */ 81738032Speter char *volatile sendinghost; /* sending hostname */ 81838032Speter char *volatile peerhostname; /* name of SMTP peer or "localhost" */ 81938032Speter auto char *delimptr; 82038032Speter char *id; 82190792Sgshapiro volatile unsigned int n_badcmds = 0; /* count of bad commands */ 82290792Sgshapiro volatile unsigned int n_badrcpts = 0; /* number of rejected RCPT */ 82390792Sgshapiro volatile unsigned int n_verifies = 0; /* count of VRFY/EXPN */ 82490792Sgshapiro volatile unsigned int n_etrn = 0; /* count of ETRN */ 82590792Sgshapiro volatile unsigned int n_noop = 0; /* count of NOOP/VERB/etc */ 82690792Sgshapiro volatile unsigned int n_helo = 0; /* count of HELO/EHLO */ 82738032Speter bool ok; 82890792Sgshapiro volatile bool first; 82990792Sgshapiro volatile bool tempfail = false; 83064562Sgshapiro volatile time_t wt; /* timeout after too many commands */ 83164562Sgshapiro volatile time_t previous; /* time after checksmtpattack() */ 83290792Sgshapiro volatile bool lognullconnection = true; 83338032Speter register char *q; 83490792Sgshapiro SMTP_T smtp; 83564562Sgshapiro char *addr; 83664562Sgshapiro char *greetcode = "220"; 837285229Sgshapiro const char *greetmsg = "not accepting messages"; 83890792Sgshapiro char *hostname; /* my hostname ($j) */ 83938032Speter QUEUE_CHAR *new; 84064562Sgshapiro char *args[MAXSMTPARGS]; 841168515Sgshapiro char inp[MAXINPLINE]; 842168515Sgshapiro#if MAXINPLINE < MAXLINE 843168515Sgshapiro ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE 844363466Sgshapiro#endif 84538032Speter char cmdbuf[MAXLINE]; 84690792Sgshapiro#if SASL 84764562Sgshapiro sasl_conn_t *conn; 84864562Sgshapiro volatile bool sasl_ok; 84990792Sgshapiro volatile unsigned int n_auth = 0; /* count of AUTH commands */ 85064562Sgshapiro bool ismore; 85164562Sgshapiro int result; 85264562Sgshapiro volatile int authenticating; 85364562Sgshapiro char *user; 85498121Sgshapiro char *in, *out2; 855363466Sgshapiro char auth_user[MAX_AUTH_USER_LEN], auth_user_tmp[MAX_AUTH_USER_LEN]; 856363466Sgshapiro unsigned int auth_user_len; 85798121Sgshapiro# if SASL >= 20000 858168515Sgshapiro char *auth_id = NULL; 85998121Sgshapiro const char *out; 860102528Sgshapiro sasl_ssf_t ext_ssf; 861120256Sgshapiro char localip[60], remoteip[60]; 86298121Sgshapiro# else /* SASL >= 20000 */ 86398121Sgshapiro char *out; 86464562Sgshapiro const char *errstr; 86598121Sgshapiro sasl_external_properties_t ext_ssf; 866120256Sgshapiro struct sockaddr_in saddr_l; 867120256Sgshapiro struct sockaddr_in saddr_r; 86898121Sgshapiro# endif /* SASL >= 20000 */ 86998121Sgshapiro sasl_security_properties_t ssp; 87098121Sgshapiro sasl_ssf_t *ssf; 87190792Sgshapiro unsigned int inlen, out2len; 87264562Sgshapiro unsigned int outlen; 87364562Sgshapiro char *volatile auth_type; 87464562Sgshapiro char *mechlist; 87590792Sgshapiro volatile unsigned int n_mechs; 87690792Sgshapiro unsigned int len; 87790792Sgshapiro#endif /* SASL */ 878132943Sgshapiro int r; 87990792Sgshapiro#if STARTTLS 88066494Sgshapiro int rfd, wfd; 88190792Sgshapiro volatile bool tls_active = false; 882132943Sgshapiro volatile bool smtps = bitnset(D_SMTPS, d_flags); 88364562Sgshapiro bool saveQuickAbort; 88464562Sgshapiro bool saveSuprErrs; 88590792Sgshapiro time_t tlsstart; 886363466Sgshapiro int ssl_err, tlsret; 887363466Sgshapiro int save_errno; 888363466Sgshapiro extern int TLSsslidx; 88990792Sgshapiro#endif /* STARTTLS */ 89090792Sgshapiro volatile unsigned int features; 89190792Sgshapiro#if PIPELINING 89290792Sgshapiro# if _FFR_NO_PIPE 89390792Sgshapiro int np_log = 0; 894363466Sgshapiro# endif 89590792Sgshapiro#endif /* PIPELINING */ 89690792Sgshapiro volatile time_t log_delay = (time_t) 0; 897168515Sgshapiro#if MILTER 898168515Sgshapiro volatile bool milter_cmd_done, milter_cmd_safe; 899168515Sgshapiro volatile bool milter_rcpt_added, milter_cmd_fail; 900168515Sgshapiro ADDRESS addr_st; 901168515Sgshapiro# define p_addr_st &addr_st 902168515Sgshapiro#else /* MILTER */ 903168515Sgshapiro# define p_addr_st NULL 904168515Sgshapiro#endif /* MILTER */ 905168515Sgshapiro size_t inplen; 906182352Sgshapiro#if _FFR_BADRCPT_SHUTDOWN 907182352Sgshapiro int n_badrcpts_adj; 908363466Sgshapiro#endif 90938032Speter 910363466Sgshapiro RESET_AUTH_FAIL_LOG_USER; 911168515Sgshapiro SevenBitInput_Saved = SevenBitInput; 91290792Sgshapiro smtp.sm_nrcpts = 0; 91390792Sgshapiro#if MILTER 91490792Sgshapiro smtp.sm_milterize = (nullserver == NULL); 91590792Sgshapiro smtp.sm_milterlist = false; 916168515Sgshapiro addr = NULL; 917363466Sgshapiro#endif 91890792Sgshapiro 91990792Sgshapiro /* setup I/O fd correctly for the SMTP server */ 92090792Sgshapiro setup_smtpd_io(); 92190792Sgshapiro 92290792Sgshapiro#if SM_HEAP_CHECK 92390792Sgshapiro if (sm_debug_active(&DebugLeakSmtp, 1)) 92438032Speter { 92590792Sgshapiro sm_heap_newgroup(); 92690792Sgshapiro sm_dprintf("smtp() heap group #%d\n", sm_heap_group()); 92738032Speter } 92890792Sgshapiro#endif /* SM_HEAP_CHECK */ 92964562Sgshapiro 93090792Sgshapiro /* XXX the rpool should be set when e is initialized in main() */ 93190792Sgshapiro e->e_rpool = sm_rpool_new_x(NULL); 93290792Sgshapiro e->e_macro.mac_rpool = e->e_rpool; 93390792Sgshapiro 93438032Speter settime(e); 93590792Sgshapiro sm_getla(); 93638032Speter peerhostname = RealHostName; 93738032Speter if (peerhostname == NULL) 93838032Speter peerhostname = "localhost"; 93938032Speter CurHostName = peerhostname; 94038032Speter CurSmtpClient = macvalue('_', e); 94138032Speter if (CurSmtpClient == NULL) 94238032Speter CurSmtpClient = CurHostName; 94338032Speter 94438032Speter /* check_relay may have set discard bit, save for later */ 94590792Sgshapiro smtp.sm_discard = bitset(EF_DISCARD, e->e_flags); 94638032Speter 94790792Sgshapiro#if PIPELINING 94890792Sgshapiro /* auto-flush output when reading input */ 94990792Sgshapiro (void) sm_io_autoflush(InChannel, OutChannel); 95090792Sgshapiro#endif /* PIPELINING */ 95164562Sgshapiro 95290792Sgshapiro sm_setproctitle(true, e, "server %s startup", CurSmtpClient); 95390792Sgshapiro 95490792Sgshapiro /* Set default features for server. */ 95590792Sgshapiro features = ((bitset(PRIV_NOETRN, PrivacyFlags) || 95690792Sgshapiro bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN) 95790792Sgshapiro | (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE) 95890792Sgshapiro | (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE 95990792Sgshapiro : (SRV_OFFER_EXPN 96090792Sgshapiro | (bitset(PRIV_NOVERB, PrivacyFlags) 96190792Sgshapiro ? SRV_NONE : SRV_OFFER_VERB))) 962159609Sgshapiro | ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors) 963159609Sgshapiro ? SRV_NONE : SRV_OFFER_DSN) 96490792Sgshapiro#if SASL 96590792Sgshapiro | (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH) 966132943Sgshapiro | (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC 967132943Sgshapiro : SRV_NONE) 96890792Sgshapiro#endif /* SASL */ 96990792Sgshapiro#if PIPELINING 97090792Sgshapiro | SRV_OFFER_PIPE 971363466Sgshapiro#endif 97290792Sgshapiro#if STARTTLS 97390792Sgshapiro | (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS) 97490792Sgshapiro | (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE 97590792Sgshapiro : SRV_VRFY_CLT) 976363466Sgshapiro#endif 977363466Sgshapiro#if _FFR_EAI 978363466Sgshapiro | SRV_OFFER_EAI 979363466Sgshapiro#endif /* _FFR_EAI */ 98090792Sgshapiro ; 98190792Sgshapiro if (nullserver == NULL) 98290792Sgshapiro { 98390792Sgshapiro features = srvfeatures(e, CurSmtpClient, features); 98490792Sgshapiro if (bitset(SRV_TMP_FAIL, features)) 98590792Sgshapiro { 98690792Sgshapiro if (LogLevel > 4) 98790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 98890792Sgshapiro "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled", 98990792Sgshapiro CurSmtpClient); 99090792Sgshapiro nullserver = "450 4.3.0 Please try again later."; 99190792Sgshapiro } 992132943Sgshapiro else 993132943Sgshapiro { 99490792Sgshapiro#if PIPELINING 99590792Sgshapiro# if _FFR_NO_PIPE 996132943Sgshapiro if (bitset(SRV_NO_PIPE, features)) 997132943Sgshapiro { 998132943Sgshapiro /* for consistency */ 999132943Sgshapiro features &= ~SRV_OFFER_PIPE; 1000132943Sgshapiro } 100190792Sgshapiro# endif /* _FFR_NO_PIPE */ 100290792Sgshapiro#endif /* PIPELINING */ 1003132943Sgshapiro#if SASL 1004132943Sgshapiro if (bitset(SRV_REQ_SEC, features)) 1005132943Sgshapiro SASLOpts |= SASL_SEC_NOPLAINTEXT; 1006132943Sgshapiro else 1007132943Sgshapiro SASLOpts &= ~SASL_SEC_NOPLAINTEXT; 1008132943Sgshapiro#endif /* SASL */ 1009132943Sgshapiro } 101090792Sgshapiro } 1011132943Sgshapiro else if (strncmp(nullserver, "421 ", 4) == 0) 1012132943Sgshapiro { 1013363466Sgshapiro /* Can't use ("%s", ...) due to message() requirements */ 1014132943Sgshapiro message(nullserver); 1015132943Sgshapiro goto doquit; 1016132943Sgshapiro } 101790792Sgshapiro 1018168515Sgshapiro e->e_features = features; 101990792Sgshapiro hostname = macvalue('j', e); 102090792Sgshapiro#if SASL 1021132943Sgshapiro if (AuthRealm == NULL) 1022132943Sgshapiro AuthRealm = hostname; 102390792Sgshapiro sasl_ok = bitset(SRV_OFFER_AUTH, features); 102464562Sgshapiro n_mechs = 0; 102590792Sgshapiro authenticating = SASL_NOT_AUTH; 102664562Sgshapiro 102764562Sgshapiro /* SASL server new connection */ 102890792Sgshapiro if (sasl_ok) 102938032Speter { 103098121Sgshapiro# if SASL >= 20000 1031132943Sgshapiro result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL, 103298121Sgshapiro NULL, 0, &conn); 103398121Sgshapiro# elif SASL > 10505 103490792Sgshapiro /* use empty realm: only works in SASL > 1.5.5 */ 1035132943Sgshapiro result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn); 103698121Sgshapiro# else /* SASL >= 20000 */ 103790792Sgshapiro /* use no realm -> realm is set to hostname by SASL lib */ 1038132943Sgshapiro result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0, 103990792Sgshapiro &conn); 104098121Sgshapiro# endif /* SASL >= 20000 */ 104190792Sgshapiro sasl_ok = result == SASL_OK; 104290792Sgshapiro if (!sasl_ok) 104390792Sgshapiro { 104490792Sgshapiro if (LogLevel > 9) 104590792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 104690792Sgshapiro "AUTH error: sasl_server_new failed=%d", 104790792Sgshapiro result); 104890792Sgshapiro } 104990792Sgshapiro } 105090792Sgshapiro if (sasl_ok) 105190792Sgshapiro { 105264562Sgshapiro /* 105364562Sgshapiro ** SASL set properties for sasl 105464562Sgshapiro ** set local/remote IP 105598121Sgshapiro ** XXX Cyrus SASL v1 only supports IPv4 105664562Sgshapiro ** 105764562Sgshapiro ** XXX where exactly are these used/required? 105864562Sgshapiro ** Kerberos_v4 105964562Sgshapiro */ 106064562Sgshapiro 106198121Sgshapiro# if SASL >= 20000 1062147078Sgshapiro localip[0] = remoteip[0] = '\0'; 106398121Sgshapiro# if NETINET || NETINET6 106490792Sgshapiro in = macvalue(macid("{daemon_family}"), e); 106598121Sgshapiro if (in != NULL && ( 106698121Sgshapiro# if NETINET6 106798121Sgshapiro strcmp(in, "inet6") == 0 || 1068363466Sgshapiro# endif 106998121Sgshapiro strcmp(in, "inet") == 0)) 107098121Sgshapiro { 107198121Sgshapiro SOCKADDR_LEN_T addrsize; 107298121Sgshapiro SOCKADDR saddr_l; 107398121Sgshapiro SOCKADDR saddr_r; 107498121Sgshapiro 107598121Sgshapiro addrsize = sizeof(saddr_r); 107698121Sgshapiro if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 107798121Sgshapiro NULL), 107898121Sgshapiro (struct sockaddr *) &saddr_r, 107998121Sgshapiro &addrsize) == 0) 108098121Sgshapiro { 108198121Sgshapiro if (iptostring(&saddr_r, addrsize, 1082168515Sgshapiro remoteip, sizeof(remoteip))) 108398121Sgshapiro { 108498121Sgshapiro sasl_setprop(conn, SASL_IPREMOTEPORT, 108598121Sgshapiro remoteip); 108698121Sgshapiro } 108798121Sgshapiro addrsize = sizeof(saddr_l); 108898121Sgshapiro if (getsockname(sm_io_getinfo(InChannel, 108998121Sgshapiro SM_IO_WHAT_FD, 109098121Sgshapiro NULL), 109198121Sgshapiro (struct sockaddr *) &saddr_l, 109298121Sgshapiro &addrsize) == 0) 109398121Sgshapiro { 109498121Sgshapiro if (iptostring(&saddr_l, addrsize, 109598121Sgshapiro localip, 1096168515Sgshapiro sizeof(localip))) 109798121Sgshapiro { 109898121Sgshapiro sasl_setprop(conn, 109998121Sgshapiro SASL_IPLOCALPORT, 110098121Sgshapiro localip); 110198121Sgshapiro } 110298121Sgshapiro } 110398121Sgshapiro } 110498121Sgshapiro } 110598121Sgshapiro# endif /* NETINET || NETINET6 */ 110698121Sgshapiro# else /* SASL >= 20000 */ 110798121Sgshapiro# if NETINET 110898121Sgshapiro in = macvalue(macid("{daemon_family}"), e); 110964562Sgshapiro if (in != NULL && strcmp(in, "inet") == 0) 111064562Sgshapiro { 111164562Sgshapiro SOCKADDR_LEN_T addrsize; 111264562Sgshapiro 111364562Sgshapiro addrsize = sizeof(struct sockaddr_in); 111490792Sgshapiro if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD, 111590792Sgshapiro NULL), 111664562Sgshapiro (struct sockaddr *)&saddr_r, 111764562Sgshapiro &addrsize) == 0) 111864562Sgshapiro { 111964562Sgshapiro sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r); 112064562Sgshapiro addrsize = sizeof(struct sockaddr_in); 112190792Sgshapiro if (getsockname(sm_io_getinfo(InChannel, 112290792Sgshapiro SM_IO_WHAT_FD, 112390792Sgshapiro NULL), 112464562Sgshapiro (struct sockaddr *)&saddr_l, 112564562Sgshapiro &addrsize) == 0) 112664562Sgshapiro sasl_setprop(conn, SASL_IP_LOCAL, 112764562Sgshapiro &saddr_l); 112864562Sgshapiro } 112964562Sgshapiro } 113098121Sgshapiro# endif /* NETINET */ 113198121Sgshapiro# endif /* SASL >= 20000 */ 113264562Sgshapiro 113364562Sgshapiro auth_type = NULL; 113464562Sgshapiro mechlist = NULL; 113564562Sgshapiro user = NULL; 113690792Sgshapiro# if 0 113790792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 113890792Sgshapiro macid("{auth_author}"), NULL); 1139363466Sgshapiro# endif 114064562Sgshapiro 114164562Sgshapiro /* set properties */ 1142168515Sgshapiro (void) memset(&ssp, '\0', sizeof(ssp)); 114390792Sgshapiro 114464562Sgshapiro /* XXX should these be options settable via .cf ? */ 114564562Sgshapiro /* ssp.min_ssf = 0; is default due to memset() */ 1146223067Sgshapiro ssp.max_ssf = MaxSLBits; 1147223067Sgshapiro ssp.maxbufsize = MAXOUTLEN; 114864562Sgshapiro ssp.security_flags = SASLOpts & SASL_SEC_MASK; 114964562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK; 115064562Sgshapiro 115164562Sgshapiro if (sasl_ok) 115264562Sgshapiro { 115364562Sgshapiro /* 115464562Sgshapiro ** external security strength factor; 115590792Sgshapiro ** currently we have none so zero 115664562Sgshapiro */ 115790792Sgshapiro 115898121Sgshapiro# if SASL >= 20000 115998121Sgshapiro ext_ssf = 0; 116098121Sgshapiro auth_id = NULL; 116198121Sgshapiro sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL, 116298121Sgshapiro &ext_ssf) == SASL_OK) && 116398121Sgshapiro (sasl_setprop(conn, SASL_AUTH_EXTERNAL, 1164102528Sgshapiro auth_id) == SASL_OK)); 116598121Sgshapiro# else /* SASL >= 20000 */ 116664562Sgshapiro ext_ssf.ssf = 0; 116764562Sgshapiro ext_ssf.auth_id = NULL; 116864562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, 116964562Sgshapiro &ext_ssf) == SASL_OK; 117098121Sgshapiro# endif /* SASL >= 20000 */ 117164562Sgshapiro } 117264562Sgshapiro if (sasl_ok) 117364562Sgshapiro n_mechs = saslmechs(conn, &mechlist); 117438032Speter } 117590792Sgshapiro#endif /* SASL */ 117638032Speter 1177285229Sgshapiro (void) set_tls_rd_tmo(TimeOuts.to_nextcommand); 1178182352Sgshapiro 117990792Sgshapiro#if MILTER 118090792Sgshapiro if (smtp.sm_milterize) 118164562Sgshapiro { 118264562Sgshapiro char state; 118364562Sgshapiro 118464562Sgshapiro /* initialize mail filter connection */ 1185173340Sgshapiro smtp.sm_milterlist = milter_init(e, &state, &smtp.sm_milters); 118664562Sgshapiro switch (state) 118764562Sgshapiro { 118864562Sgshapiro case SMFIR_REJECT: 118990792Sgshapiro if (MilterLogLevel > 3) 119090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 119194334Sgshapiro "Milter: initialization failed, rejecting commands"); 119264562Sgshapiro greetcode = "554"; 119364562Sgshapiro nullserver = "Command rejected"; 119490792Sgshapiro smtp.sm_milterize = false; 119564562Sgshapiro break; 119664562Sgshapiro 119764562Sgshapiro case SMFIR_TEMPFAIL: 119890792Sgshapiro if (MilterLogLevel > 3) 119990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 120094334Sgshapiro "Milter: initialization failed, temp failing commands"); 120190792Sgshapiro tempfail = true; 120290792Sgshapiro smtp.sm_milterize = false; 120364562Sgshapiro break; 1204157001Sgshapiro 1205157001Sgshapiro case SMFIR_SHUTDOWN: 1206157001Sgshapiro if (MilterLogLevel > 3) 1207157001Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1208157001Sgshapiro "Milter: initialization failed, closing connection"); 1209157001Sgshapiro tempfail = true; 1210157001Sgshapiro smtp.sm_milterize = false; 1211157001Sgshapiro message("421 4.7.0 %s closing connection", 1212157001Sgshapiro MyHostName); 1213157001Sgshapiro 1214157001Sgshapiro /* arrange to ignore send list */ 1215157001Sgshapiro e->e_sendqueue = NULL; 1216182352Sgshapiro lognullconnection = false; 1217157001Sgshapiro goto doquit; 121864562Sgshapiro } 121964562Sgshapiro } 122064562Sgshapiro 122190792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 122290792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 122364562Sgshapiro { 122464562Sgshapiro char state; 122590792Sgshapiro char *response; 122664562Sgshapiro 1227161389Sgshapiro q = macvalue(macid("{client_name}"), e); 1228168515Sgshapiro SM_ASSERT(q != NULL || OpMode == MD_SMTP); 1229168515Sgshapiro if (q == NULL) 1230168515Sgshapiro q = "localhost"; 1231161389Sgshapiro response = milter_connect(q, RealHostAddr, e, &state); 123264562Sgshapiro switch (state) 123364562Sgshapiro { 1234285229Sgshapiro#if _FFR_MILTER_CONNECT_REPLYCODE 1235285229Sgshapiro case SMFIR_REPLYCODE: 1236285229Sgshapiro if (*response == '5') 1237285229Sgshapiro { 1238285229Sgshapiro if (MilterLogLevel > 3) 1239285229Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1240285229Sgshapiro "Milter: connect: host=%s, addr=%s, reject=%s", 1241285229Sgshapiro peerhostname, 1242285229Sgshapiro anynet_ntoa(&RealHostAddr), 1243285229Sgshapiro response); 1244285229Sgshapiro greetcode = "554"; /* Required by 2821 3.1 */ 1245285229Sgshapiro nullserver = newstr(response); 1246285229Sgshapiro if (strlen(nullserver) > 4) 1247285229Sgshapiro { 1248285229Sgshapiro int skip; 1249285229Sgshapiro 1250285229Sgshapiro greetmsg = nullserver + 4; 1251285229Sgshapiro 1252285229Sgshapiro /* skip over enhanced status code */ 1253285229Sgshapiro skip = isenhsc(greetmsg, ' '); 1254285229Sgshapiro if (skip > 0) 1255285229Sgshapiro greetmsg += skip + 1; 1256285229Sgshapiro } 1257285229Sgshapiro smtp.sm_milterize = false; 1258285229Sgshapiro break; 1259285229Sgshapiro } 1260285229Sgshapiro else if (strncmp(response, "421 ", 4) == 0) 1261285229Sgshapiro { 1262285229Sgshapiro int skip; 1263285229Sgshapiro const char *msg = response + 4; 1264285229Sgshapiro 1265285229Sgshapiro if (MilterLogLevel > 3) 1266285229Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1267285229Sgshapiro "Milter: connect: host=%s, addr=%s, shutdown=%s", 1268285229Sgshapiro peerhostname, 1269285229Sgshapiro anynet_ntoa(&RealHostAddr), 1270285229Sgshapiro response); 1271285229Sgshapiro tempfail = true; 1272285229Sgshapiro smtp.sm_milterize = false; 1273285229Sgshapiro 1274285229Sgshapiro /* skip over enhanced status code */ 1275285229Sgshapiro skip = isenhsc(msg, ' '); 1276285229Sgshapiro if (skip > 0) 1277285229Sgshapiro msg += skip + 1; 1278285229Sgshapiro message("421 %s %s", MyHostName, msg); 1279285229Sgshapiro 1280285229Sgshapiro /* arrange to ignore send list */ 1281285229Sgshapiro e->e_sendqueue = NULL; 1282285229Sgshapiro goto doquit; 1283285229Sgshapiro } 1284285229Sgshapiro else 1285285229Sgshapiro { 1286285229Sgshapiro if (MilterLogLevel > 3) 1287285229Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1288285229Sgshapiro "Milter: connect: host=%s, addr=%s, temp failing commands=%s", 1289285229Sgshapiro peerhostname, 1290285229Sgshapiro anynet_ntoa(&RealHostAddr), 1291285229Sgshapiro response); 1292285229Sgshapiro /*tempfail = true;*/ 1293285229Sgshapiro smtp.sm_milterize = false; 1294285229Sgshapiro nullserver = newstr(response); 1295285229Sgshapiro break; 1296285229Sgshapiro } 1297285229Sgshapiro 1298285229Sgshapiro#else /* _FFR_MILTER_CONNECT_REPLYCODE */ 129964562Sgshapiro case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */ 1300285229Sgshapiro#endif /* _FFR_MILTER_CONNECT_REPLYCODE */ 130164562Sgshapiro case SMFIR_REJECT: 130290792Sgshapiro if (MilterLogLevel > 3) 130390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 130490792Sgshapiro "Milter: connect: host=%s, addr=%s, rejecting commands", 130590792Sgshapiro peerhostname, 130690792Sgshapiro anynet_ntoa(&RealHostAddr)); 130764562Sgshapiro greetcode = "554"; 130864562Sgshapiro nullserver = "Command rejected"; 130990792Sgshapiro smtp.sm_milterize = false; 131064562Sgshapiro break; 131164562Sgshapiro 131264562Sgshapiro case SMFIR_TEMPFAIL: 131390792Sgshapiro if (MilterLogLevel > 3) 131490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 131590792Sgshapiro "Milter: connect: host=%s, addr=%s, temp failing commands", 131690792Sgshapiro peerhostname, 131790792Sgshapiro anynet_ntoa(&RealHostAddr)); 131890792Sgshapiro tempfail = true; 131990792Sgshapiro smtp.sm_milterize = false; 132064562Sgshapiro break; 1321110560Sgshapiro 1322110560Sgshapiro case SMFIR_SHUTDOWN: 1323110560Sgshapiro if (MilterLogLevel > 3) 1324110560Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1325110560Sgshapiro "Milter: connect: host=%s, addr=%s, shutdown", 1326110560Sgshapiro peerhostname, 1327110560Sgshapiro anynet_ntoa(&RealHostAddr)); 1328110560Sgshapiro tempfail = true; 1329110560Sgshapiro smtp.sm_milterize = false; 1330110560Sgshapiro message("421 4.7.0 %s closing connection", 1331110560Sgshapiro MyHostName); 1332110560Sgshapiro 1333110560Sgshapiro /* arrange to ignore send list */ 1334110560Sgshapiro e->e_sendqueue = NULL; 1335110560Sgshapiro goto doquit; 133664562Sgshapiro } 133790792Sgshapiro if (response != NULL) 1338285229Sgshapiro sm_free(response); 133964562Sgshapiro } 134090792Sgshapiro#endif /* MILTER */ 134164562Sgshapiro 1342132943Sgshapiro /* 1343132943Sgshapiro ** Broken proxies and SMTP slammers 1344132943Sgshapiro ** push data without waiting, catch them 1345132943Sgshapiro */ 1346132943Sgshapiro 1347132943Sgshapiro if ( 134890792Sgshapiro#if STARTTLS 1349132943Sgshapiro !smtps && 1350363466Sgshapiro#endif 1351168515Sgshapiro *greetcode == '2' && nullserver == NULL) 1352132943Sgshapiro { 1353132943Sgshapiro time_t msecs = 0; 1354132943Sgshapiro char **pvp; 1355132943Sgshapiro char pvpbuf[PSBUFSIZE]; 1356132943Sgshapiro 1357132943Sgshapiro /* Ask the rulesets how long to pause */ 1358132943Sgshapiro pvp = NULL; 1359132943Sgshapiro r = rscap("greet_pause", peerhostname, 1360132943Sgshapiro anynet_ntoa(&RealHostAddr), e, 1361132943Sgshapiro &pvp, pvpbuf, sizeof(pvpbuf)); 1362132943Sgshapiro if (r == EX_OK && pvp != NULL && pvp[0] != NULL && 1363132943Sgshapiro (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL) 1364132943Sgshapiro { 1365132943Sgshapiro msecs = strtol(pvp[1], NULL, 10); 1366132943Sgshapiro } 1367132943Sgshapiro 1368132943Sgshapiro if (msecs > 0) 1369132943Sgshapiro { 1370132943Sgshapiro int fd; 1371132943Sgshapiro fd_set readfds; 1372132943Sgshapiro struct timeval timeout; 1373157001Sgshapiro struct timeval bp, ep, tp; /* {begin,end,total}pause */ 1374168515Sgshapiro int eoftest; 1375132943Sgshapiro 1376132943Sgshapiro /* pause for a moment */ 1377132943Sgshapiro timeout.tv_sec = msecs / 1000; 1378132943Sgshapiro timeout.tv_usec = (msecs % 1000) * 1000; 1379132943Sgshapiro 1380132943Sgshapiro /* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */ 1381132943Sgshapiro if (timeout.tv_sec >= 300) 1382132943Sgshapiro { 1383132943Sgshapiro timeout.tv_sec = 300; 1384132943Sgshapiro timeout.tv_usec = 0; 1385132943Sgshapiro } 1386132943Sgshapiro 1387132943Sgshapiro /* check if data is on the socket during the pause */ 1388132943Sgshapiro fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 1389132943Sgshapiro FD_ZERO(&readfds); 1390132943Sgshapiro SM_FD_SET(fd, &readfds); 1391157001Sgshapiro gettimeofday(&bp, NULL); 1392132943Sgshapiro if (select(fd + 1, FDSET_CAST &readfds, 1393132943Sgshapiro NULL, NULL, &timeout) > 0 && 1394168515Sgshapiro FD_ISSET(fd, &readfds) && 1395182352Sgshapiro (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT)) 1396168515Sgshapiro != SM_IO_EOF) 1397132943Sgshapiro { 1398182352Sgshapiro sm_io_ungetc(InChannel, SM_TIME_DEFAULT, 1399168515Sgshapiro eoftest); 1400157001Sgshapiro gettimeofday(&ep, NULL); 1401157001Sgshapiro timersub(&ep, &bp, &tp); 1402132943Sgshapiro greetcode = "554"; 1403132943Sgshapiro nullserver = "Command rejected"; 1404132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1405168515Sgshapiro "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds", 1406132943Sgshapiro peerhostname, 1407168515Sgshapiro anynet_ntoa(&RealHostAddr), 1408168515Sgshapiro (int) tp.tv_sec + 1409157001Sgshapiro (tp.tv_usec >= 500000 ? 1 : 0) 1410157001Sgshapiro ); 1411132943Sgshapiro } 1412132943Sgshapiro } 1413132943Sgshapiro } 1414132943Sgshapiro 1415132943Sgshapiro#if STARTTLS 141690792Sgshapiro /* If this an smtps connection, start TLS now */ 141790792Sgshapiro if (smtps) 1418120256Sgshapiro { 1419363466Sgshapiro if (!tls_ok_srv || srv_ctx == NULL) 1420363466Sgshapiro { 1421363466Sgshapiro sm_syslog(LOG_ERR, e->e_id, 1422363466Sgshapiro "smtps: TLS not available, exiting"); 1423363466Sgshapiro exit(EX_CONFIG); 1424363466Sgshapiro } 1425120256Sgshapiro Errors = 0; 1426363466Sgshapiro first = true; 1427363466Sgshapiro gothello = false; 1428363466Sgshapiro smtp.sm_gotmail = false; 142990792Sgshapiro goto starttls; 1430120256Sgshapiro } 143190792Sgshapiro 143290792Sgshapiro greeting: 143390792Sgshapiro 143490792Sgshapiro#endif /* STARTTLS */ 143590792Sgshapiro 143638032Speter /* output the first line, inserting "ESMTP" as second word */ 143790792Sgshapiro if (*greetcode == '5') 1438285229Sgshapiro (void) sm_snprintf(inp, sizeof(inp), "%s %s", hostname, 1439285229Sgshapiro greetmsg); 144090792Sgshapiro else 1441168515Sgshapiro expand(SmtpGreeting, inp, sizeof(inp), e); 144290792Sgshapiro 144338032Speter p = strchr(inp, '\n'); 144438032Speter if (p != NULL) 144538032Speter *p++ = '\0'; 144638032Speter id = strchr(inp, ' '); 144738032Speter if (id == NULL) 144838032Speter id = &inp[strlen(inp)]; 144964562Sgshapiro if (p == NULL) 1450168515Sgshapiro (void) sm_snprintf(cmdbuf, sizeof(cmdbuf), 145164562Sgshapiro "%s %%.*s ESMTP%%s", greetcode); 145264562Sgshapiro else 1453168515Sgshapiro (void) sm_snprintf(cmdbuf, sizeof(cmdbuf), 145464562Sgshapiro "%s-%%.*s ESMTP%%s", greetcode); 145566494Sgshapiro message(cmdbuf, (int) (id - inp), inp, id); 145638032Speter 145738032Speter /* output remaining lines */ 145838032Speter while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 145938032Speter { 146038032Speter *p++ = '\0'; 1461363466Sgshapiro if (SM_ISSPACE(*id)) 146238032Speter id++; 1463168515Sgshapiro (void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s"); 146464562Sgshapiro message(cmdbuf, id); 146538032Speter } 146638032Speter if (id != NULL) 146738032Speter { 1468363466Sgshapiro if (SM_ISSPACE(*id)) 146938032Speter id++; 1470168515Sgshapiro (void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s"); 147164562Sgshapiro message(cmdbuf, id); 147238032Speter } 147338032Speter 147438032Speter protocol = NULL; 147538032Speter sendinghost = macvalue('s', e); 147690792Sgshapiro 147790792Sgshapiro /* If quarantining by a connect/ehlo action, save between messages */ 147890792Sgshapiro if (e->e_quarmsg == NULL) 147990792Sgshapiro smtp.sm_quarmsg = NULL; 148090792Sgshapiro else 148190792Sgshapiro smtp.sm_quarmsg = newstr(e->e_quarmsg); 148290792Sgshapiro 148390792Sgshapiro /* sendinghost's storage must outlive the current envelope */ 148490792Sgshapiro if (sendinghost != NULL) 148590792Sgshapiro sendinghost = sm_strdup_x(sendinghost); 148690792Sgshapiro first = true; 148790792Sgshapiro gothello = false; 148890792Sgshapiro smtp.sm_gotmail = false; 148938032Speter for (;;) 149038032Speter { 149190792Sgshapiro SM_TRY 149290792Sgshapiro { 149390792Sgshapiro QuickAbort = false; 149490792Sgshapiro HoldErrs = false; 149590792Sgshapiro SuprErrs = false; 149690792Sgshapiro LogUsrErrs = false; 149790792Sgshapiro OnlyOneError = true; 149838032Speter e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 1499168515Sgshapiro#if MILTER 1500168515Sgshapiro milter_cmd_fail = false; 1501363466Sgshapiro#endif 150238032Speter 150338032Speter /* setup for the read */ 150438032Speter e->e_to = NULL; 150538032Speter Errors = 0; 150664562Sgshapiro FileName = NULL; 150790792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 150838032Speter 150938032Speter /* read the input line */ 151038032Speter SmtpPhase = "server cmd read"; 151190792Sgshapiro sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient); 151238032Speter 151338032Speter /* handle errors */ 151490792Sgshapiro if (sm_io_error(OutChannel) || 1515168515Sgshapiro (p = sfgets(inp, sizeof(inp), InChannel, 151664562Sgshapiro TimeOuts.to_nextcommand, SmtpPhase)) == NULL) 151738032Speter { 151864562Sgshapiro char *d; 151964562Sgshapiro 152090792Sgshapiro d = macvalue(macid("{daemon_name}"), e); 152164562Sgshapiro if (d == NULL) 152264562Sgshapiro d = "stdin"; 152338032Speter /* end of file, just die */ 152438032Speter disconnect(1, e); 152564562Sgshapiro 152690792Sgshapiro#if MILTER 152764562Sgshapiro /* close out milter filters */ 152864562Sgshapiro milter_quit(e); 1529363466Sgshapiro#endif 153064562Sgshapiro 153164562Sgshapiro message("421 4.4.1 %s Lost input channel from %s", 153238032Speter MyHostName, CurSmtpClient); 153390792Sgshapiro if (LogLevel > (smtp.sm_gotmail ? 1 : 19)) 153438032Speter sm_syslog(LOG_NOTICE, e->e_id, 1535110560Sgshapiro "lost input channel from %s to %s after %s", 153664562Sgshapiro CurSmtpClient, d, 153764562Sgshapiro (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name); 153838032Speter /* 153942575Speter ** If have not accepted mail (DATA), do not bounce 154042575Speter ** bad addresses back to sender. 154138032Speter */ 154264562Sgshapiro 154338032Speter if (bitset(EF_CLRQUEUE, e->e_flags)) 154438032Speter e->e_sendqueue = NULL; 154564562Sgshapiro goto doquit; 154638032Speter } 154738032Speter 1548168515Sgshapiro /* also used by "proxy" check below */ 1549168515Sgshapiro inplen = strlen(inp); 1550168515Sgshapiro#if SASL 1551168515Sgshapiro /* 1552168515Sgshapiro ** SMTP AUTH requires accepting any length, 1553168515Sgshapiro ** at least for challenge/response. However, not imposing 1554168515Sgshapiro ** a limit is a bad idea (denial of service). 1555168515Sgshapiro */ 1556168515Sgshapiro 1557168515Sgshapiro if (authenticating != SASL_PROC_AUTH 1558168515Sgshapiro && sm_strncasecmp(inp, "AUTH ", 5) != 0 1559168515Sgshapiro && inplen > MAXLINE) 1560168515Sgshapiro { 1561168515Sgshapiro message("421 4.7.0 %s Command too long, possible attack %s", 1562168515Sgshapiro MyHostName, CurSmtpClient); 1563168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1564168515Sgshapiro "%s: SMTP violation, input too long: %lu", 1565168515Sgshapiro CurSmtpClient, (unsigned long) inplen); 1566168515Sgshapiro goto doquit; 1567168515Sgshapiro } 1568168515Sgshapiro#endif /* SASL */ 1569168515Sgshapiro 157090792Sgshapiro if (first) 157190792Sgshapiro { 1572168515Sgshapiro size_t cmdlen; 1573110560Sgshapiro int idx; 1574110560Sgshapiro char *http_cmd; 1575110560Sgshapiro static char *http_cmds[] = { "GET", "POST", 1576110560Sgshapiro "CONNECT", "USER", NULL }; 1577110560Sgshapiro 1578110560Sgshapiro for (idx = 0; (http_cmd = http_cmds[idx]) != NULL; 1579110560Sgshapiro idx++) 1580110560Sgshapiro { 1581110560Sgshapiro cmdlen = strlen(http_cmd); 1582110560Sgshapiro if (cmdlen < inplen && 1583110560Sgshapiro sm_strncasecmp(inp, http_cmd, cmdlen) == 0 && 1584363466Sgshapiro SM_ISSPACE(inp[cmdlen])) 1585110560Sgshapiro { 1586110560Sgshapiro /* Open proxy, drop it */ 1587110560Sgshapiro message("421 4.7.0 %s Rejecting open proxy %s", 1588110560Sgshapiro MyHostName, CurSmtpClient); 1589110560Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1590110560Sgshapiro "%s: probable open proxy: command=%.40s", 1591110560Sgshapiro CurSmtpClient, inp); 1592110560Sgshapiro goto doquit; 1593110560Sgshapiro } 1594110560Sgshapiro } 159590792Sgshapiro first = false; 159690792Sgshapiro } 159790792Sgshapiro 159838032Speter /* clean up end of line */ 159990792Sgshapiro fixcrlf(inp, true); 160038032Speter 160190792Sgshapiro#if PIPELINING 160290792Sgshapiro# if _FFR_NO_PIPE 160390792Sgshapiro /* 160490792Sgshapiro ** if there is more input and pipelining is disabled: 160590792Sgshapiro ** delay ... (and maybe discard the input?) 160690792Sgshapiro ** XXX this doesn't really work, at least in tests using 160790792Sgshapiro ** telnet SM_IO_IS_READABLE only returns 1 if there were 160890792Sgshapiro ** more than 2 input lines available. 160990792Sgshapiro */ 161090792Sgshapiro 161190792Sgshapiro if (bitset(SRV_NO_PIPE, features) && 1612110560Sgshapiro sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0) 161390792Sgshapiro { 161490792Sgshapiro if (++np_log < 3) 161590792Sgshapiro sm_syslog(LOG_INFO, NOQID, 1616203004Sgshapiro "unauthorized PIPELINING, sleeping, relay=%.100s", 1617203004Sgshapiro CurSmtpClient); 161890792Sgshapiro sleep(1); 161990792Sgshapiro } 162090792Sgshapiro 162190792Sgshapiro# endif /* _FFR_NO_PIPE */ 162290792Sgshapiro#endif /* PIPELINING */ 162390792Sgshapiro 162490792Sgshapiro#if SASL 162564562Sgshapiro if (authenticating == SASL_PROC_AUTH) 162664562Sgshapiro { 162790792Sgshapiro# if 0 162864562Sgshapiro if (*inp == '\0') 162964562Sgshapiro { 163064562Sgshapiro authenticating = SASL_NOT_AUTH; 163164562Sgshapiro message("501 5.5.2 missing input"); 1632120256Sgshapiro RESET_SASLCONN; 163364562Sgshapiro continue; 163464562Sgshapiro } 163590792Sgshapiro# endif /* 0 */ 163664562Sgshapiro if (*inp == '*' && *(inp + 1) == '\0') 163764562Sgshapiro { 163864562Sgshapiro authenticating = SASL_NOT_AUTH; 163964562Sgshapiro 1640173340Sgshapiro /* RFC 2554 4. */ 164164562Sgshapiro message("501 5.0.0 AUTH aborted"); 1642120256Sgshapiro RESET_SASLCONN; 164364562Sgshapiro continue; 164464562Sgshapiro } 164564562Sgshapiro 164664562Sgshapiro /* could this be shorter? XXX */ 164798121Sgshapiro# if SASL >= 20000 164898121Sgshapiro in = xalloc(strlen(inp) + 1); 164998121Sgshapiro result = sasl_decode64(inp, strlen(inp), in, 165098121Sgshapiro strlen(inp), &inlen); 165198121Sgshapiro# else /* SASL >= 20000 */ 165264562Sgshapiro out = xalloc(strlen(inp)); 165364562Sgshapiro result = sasl_decode64(inp, strlen(inp), out, &outlen); 165498121Sgshapiro# endif /* SASL >= 20000 */ 165564562Sgshapiro if (result != SASL_OK) 165664562Sgshapiro { 165764562Sgshapiro authenticating = SASL_NOT_AUTH; 165864562Sgshapiro 1659173340Sgshapiro /* RFC 2554 4. */ 166064562Sgshapiro message("501 5.5.4 cannot decode AUTH parameter %s", 166164562Sgshapiro inp); 166298121Sgshapiro# if SASL >= 20000 166398121Sgshapiro sm_free(in); 1664363466Sgshapiro# endif 1665120256Sgshapiro RESET_SASLCONN; 166664562Sgshapiro continue; 166764562Sgshapiro } 166864562Sgshapiro 166998121Sgshapiro# if SASL >= 20000 1670363466Sgshapiro SET_AUTH_USER_TMP(in, inlen); 167198121Sgshapiro result = sasl_server_step(conn, in, inlen, 167298121Sgshapiro &out, &outlen); 167398121Sgshapiro sm_free(in); 167498121Sgshapiro# else /* SASL >= 20000 */ 1675363466Sgshapiro SET_AUTH_USER_TMP(out, outlen); 167664562Sgshapiro result = sasl_server_step(conn, out, outlen, 167764562Sgshapiro &out, &outlen, &errstr); 167898121Sgshapiro# endif /* SASL >= 20000 */ 167964562Sgshapiro 168064562Sgshapiro /* get an OK if we're done */ 168164562Sgshapiro if (result == SASL_OK) 168264562Sgshapiro { 168364562Sgshapiro authenticated: 168464562Sgshapiro message("235 2.0.0 OK Authenticated"); 168564562Sgshapiro authenticating = SASL_IS_AUTH; 168690792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 168790792Sgshapiro macid("{auth_type}"), auth_type); 168864562Sgshapiro 168998121Sgshapiro# if SASL >= 20000 169098121Sgshapiro user = macvalue(macid("{auth_authen}"), e); 169198121Sgshapiro 169298121Sgshapiro /* get security strength (features) */ 169398121Sgshapiro result = sasl_getprop(conn, SASL_SSF, 169498121Sgshapiro (const void **) &ssf); 169598121Sgshapiro# else /* SASL >= 20000 */ 169664562Sgshapiro result = sasl_getprop(conn, SASL_USERNAME, 169764562Sgshapiro (void **)&user); 169864562Sgshapiro if (result != SASL_OK) 169964562Sgshapiro { 170064562Sgshapiro user = ""; 170190792Sgshapiro macdefine(&BlankEnvelope.e_macro, 170290792Sgshapiro A_PERM, 170390792Sgshapiro macid("{auth_authen}"), NULL); 170464562Sgshapiro } 170564562Sgshapiro else 170664562Sgshapiro { 170790792Sgshapiro macdefine(&BlankEnvelope.e_macro, 170890792Sgshapiro A_TEMP, 1709132943Sgshapiro macid("{auth_authen}"), 1710132943Sgshapiro xtextify(user, "<>\")")); 171164562Sgshapiro } 171264562Sgshapiro 171390792Sgshapiro# if 0 171464562Sgshapiro /* get realm? */ 171564562Sgshapiro sasl_getprop(conn, SASL_REALM, (void **) &data); 1716363466Sgshapiro# endif 171764562Sgshapiro 171864562Sgshapiro /* get security strength (features) */ 171964562Sgshapiro result = sasl_getprop(conn, SASL_SSF, 172064562Sgshapiro (void **) &ssf); 172198121Sgshapiro# endif /* SASL >= 20000 */ 172264562Sgshapiro if (result != SASL_OK) 172364562Sgshapiro { 172490792Sgshapiro macdefine(&BlankEnvelope.e_macro, 172590792Sgshapiro A_PERM, 172690792Sgshapiro macid("{auth_ssf}"), "0"); 172764562Sgshapiro ssf = NULL; 172864562Sgshapiro } 172964562Sgshapiro else 173064562Sgshapiro { 173164562Sgshapiro char pbuf[8]; 173264562Sgshapiro 1733168515Sgshapiro (void) sm_snprintf(pbuf, sizeof(pbuf), 173490792Sgshapiro "%u", *ssf); 173590792Sgshapiro macdefine(&BlankEnvelope.e_macro, 173690792Sgshapiro A_TEMP, 173790792Sgshapiro macid("{auth_ssf}"), pbuf); 173864562Sgshapiro if (tTd(95, 8)) 173990792Sgshapiro sm_dprintf("AUTH auth_ssf: %u\n", 174090792Sgshapiro *ssf); 174164562Sgshapiro } 174290792Sgshapiro 1743285229Sgshapiro protocol = GET_PROTOCOL(); 1744285229Sgshapiro 174564562Sgshapiro /* 174690792Sgshapiro ** Only switch to encrypted connection 174764562Sgshapiro ** if a security layer has been negotiated 174864562Sgshapiro */ 174990792Sgshapiro 175064562Sgshapiro if (ssf != NULL && *ssf > 0) 175164562Sgshapiro { 1752159609Sgshapiro int tmo; 1753159609Sgshapiro 175464562Sgshapiro /* 175590792Sgshapiro ** Convert I/O layer to use SASL. 175690792Sgshapiro ** If the call fails, the connection 175790792Sgshapiro ** is aborted. 175864562Sgshapiro */ 175990792Sgshapiro 1760159609Sgshapiro tmo = TimeOuts.to_datablock * 1000; 176190792Sgshapiro if (sfdcsasl(&InChannel, &OutChannel, 1762159609Sgshapiro conn, tmo) == 0) 176364562Sgshapiro { 176464562Sgshapiro /* restart dialogue */ 176564562Sgshapiro n_helo = 0; 176694334Sgshapiro# if PIPELINING 176790792Sgshapiro (void) sm_io_autoflush(InChannel, 176890792Sgshapiro OutChannel); 176994334Sgshapiro# endif /* PIPELINING */ 177064562Sgshapiro } 177164562Sgshapiro else 177264562Sgshapiro syserr("503 5.3.3 SASL TLS failed"); 177364562Sgshapiro } 177490792Sgshapiro 177590792Sgshapiro /* NULL pointer ok since it's our function */ 177690792Sgshapiro if (LogLevel > 8) 177764562Sgshapiro sm_syslog(LOG_INFO, NOQID, 1778110560Sgshapiro "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d", 177990792Sgshapiro CurSmtpClient, 178090792Sgshapiro shortenstring(user, 128), 178190792Sgshapiro auth_type, *ssf); 178264562Sgshapiro } 178364562Sgshapiro else if (result == SASL_CONTINUE) 178464562Sgshapiro { 1785363466Sgshapiro SET_AUTH_USER; 1786363466Sgshapiro 178764562Sgshapiro len = ENC64LEN(outlen); 178864562Sgshapiro out2 = xalloc(len); 178964562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 179090792Sgshapiro &out2len); 179164562Sgshapiro if (result != SASL_OK) 179264562Sgshapiro { 179364562Sgshapiro /* correct code? XXX */ 179464562Sgshapiro /* 454 Temp. authentication failure */ 179564562Sgshapiro message("454 4.5.4 Internal error: unable to encode64"); 179664562Sgshapiro if (LogLevel > 5) 179764562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 1798203004Sgshapiro "AUTH encode64 error [%d for \"%s\"], relay=%.100s", 1799203004Sgshapiro result, out, 1800203004Sgshapiro CurSmtpClient); 180164562Sgshapiro /* start over? */ 180264562Sgshapiro authenticating = SASL_NOT_AUTH; 180364562Sgshapiro } 180464562Sgshapiro else 180564562Sgshapiro { 180664562Sgshapiro message("334 %s", out2); 180764562Sgshapiro if (tTd(95, 2)) 180890792Sgshapiro sm_dprintf("AUTH continue: msg='%s' len=%u\n", 180990792Sgshapiro out2, out2len); 181064562Sgshapiro } 181198121Sgshapiro# if SASL >= 20000 181298121Sgshapiro sm_free(out2); 1813363466Sgshapiro# endif 181464562Sgshapiro } 181564562Sgshapiro else 181664562Sgshapiro { 1817363466Sgshapiro 181898121Sgshapiro# if SASL >= 20000 1819363466Sgshapiro# define SASLERR sasl_errdetail(conn) 1820363466Sgshapiro# else 1821363466Sgshapiro# define SASLERR errstr == NULL ? "" : errstr 1822363466Sgshapiro# endif 1823363466Sgshapiro#define LOGAUTHFAIL \ 1824363466Sgshapiro do \ 1825363466Sgshapiro { \ 1826363466Sgshapiro SET_AUTH_USER_CONDITIONALLY \ 1827363466Sgshapiro message("535 5.7.0 authentication failed"); \ 1828363466Sgshapiro if (LogLevel >= 9) \ 1829363466Sgshapiro sm_syslog(LOG_WARNING, e->e_id, \ 1830363466Sgshapiro "AUTH failure (%s): %s (%d) %s%s%.*s, relay=%.100s", \ 1831363466Sgshapiro (auth_type != NULL) ? auth_type : "unknown", \ 1832363466Sgshapiro sasl_errstring(result, NULL, NULL), \ 1833363466Sgshapiro result, \ 1834363466Sgshapiro SASLERR, \ 1835363466Sgshapiro LOG_AUTH_FAIL_USER, \ 1836363466Sgshapiro CurSmtpClient); \ 1837363466Sgshapiro RESET_SASLCONN; \ 1838363466Sgshapiro } while (0) 1839363466Sgshapiro 1840363466Sgshapiro 1841363466Sgshapiro LOGAUTHFAIL; 184264562Sgshapiro authenticating = SASL_NOT_AUTH; 184364562Sgshapiro } 184464562Sgshapiro } 184564562Sgshapiro else 184664562Sgshapiro { 184764562Sgshapiro /* don't want to do any of this if authenticating */ 184890792Sgshapiro#endif /* SASL */ 184964562Sgshapiro 185038032Speter /* echo command to transcript */ 185138032Speter if (e->e_xfp != NULL) 185290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 185390792Sgshapiro "<<< %s\n", inp); 185438032Speter 185590792Sgshapiro if (LogLevel > 14) 185690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp); 185738032Speter 185838032Speter /* break off command */ 1859363466Sgshapiro for (p = inp; SM_ISSPACE(*p); p++) 186038032Speter continue; 186138032Speter cmd = cmdbuf; 186238032Speter while (*p != '\0' && 1863363466Sgshapiro !(SM_ISSPACE(*p)) && 1864168515Sgshapiro cmd < &cmdbuf[sizeof(cmdbuf) - 2]) 186538032Speter *cmd++ = *p++; 186638032Speter *cmd = '\0'; 186738032Speter 186838032Speter /* throw away leading whitespace */ 186990792Sgshapiro SKIP_SPACE(p); 187038032Speter 187138032Speter /* decode command */ 187264562Sgshapiro for (c = CmdTab; c->cmd_name != NULL; c++) 187338032Speter { 187490792Sgshapiro if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) 187538032Speter break; 187638032Speter } 187738032Speter 187838032Speter /* reset errors */ 187938032Speter errno = 0; 188038032Speter 188190792Sgshapiro /* check whether a "non-null" command has been used */ 188290792Sgshapiro switch (c->cmd_code) 188390792Sgshapiro { 188490792Sgshapiro#if SASL 188590792Sgshapiro case CMDAUTH: 188690792Sgshapiro /* avoid information leak; take first two words? */ 188790792Sgshapiro q = "AUTH"; 188890792Sgshapiro break; 188990792Sgshapiro#endif /* SASL */ 189090792Sgshapiro 189190792Sgshapiro case CMDMAIL: 189290792Sgshapiro case CMDEXPN: 189390792Sgshapiro case CMDVRFY: 189490792Sgshapiro case CMDETRN: 189590792Sgshapiro lognullconnection = false; 189690792Sgshapiro /* FALLTHROUGH */ 189790792Sgshapiro default: 189890792Sgshapiro q = inp; 189990792Sgshapiro break; 190090792Sgshapiro } 190190792Sgshapiro 190290792Sgshapiro if (e->e_id == NULL) 190390792Sgshapiro sm_setproctitle(true, e, "%s: %.80s", 190490792Sgshapiro CurSmtpClient, q); 190590792Sgshapiro else 190690792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", 190790792Sgshapiro qid_printname(e), 190890792Sgshapiro CurSmtpClient, q); 190990792Sgshapiro 191038032Speter /* 191138032Speter ** Process command. 191238032Speter ** 191338032Speter ** If we are running as a null server, return 550 191490792Sgshapiro ** to almost everything. 191538032Speter */ 191638032Speter 191764562Sgshapiro if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags)) 191838032Speter { 191964562Sgshapiro switch (c->cmd_code) 192038032Speter { 192138032Speter case CMDQUIT: 192238032Speter case CMDHELO: 192338032Speter case CMDEHLO: 192438032Speter case CMDNOOP: 192564562Sgshapiro case CMDRSET: 1926125820Sgshapiro case CMDERROR: 192738032Speter /* process normally */ 192838032Speter break; 192938032Speter 193064562Sgshapiro case CMDETRN: 193164562Sgshapiro if (bitnset(D_ETRNONLY, d_flags) && 193264562Sgshapiro nullserver == NULL) 193364562Sgshapiro break; 193490792Sgshapiro DELAY_CONN("ETRN"); 193580785Sgshapiro /* FALLTHROUGH */ 193664562Sgshapiro 193738032Speter default: 193890792Sgshapiro#if MAXBADCOMMANDS > 0 193990792Sgshapiro /* theoretically this could overflow */ 194090792Sgshapiro if (nullserver != NULL && 194190792Sgshapiro ++n_badcmds > MAXBADCOMMANDS) 194264562Sgshapiro { 194390792Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 194490792Sgshapiro MyHostName); 194590792Sgshapiro 194690792Sgshapiro /* arrange to ignore send list */ 194790792Sgshapiro e->e_sendqueue = NULL; 194890792Sgshapiro goto doquit; 194964562Sgshapiro } 195090792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 195164562Sgshapiro if (nullserver != NULL) 195264562Sgshapiro { 195364562Sgshapiro if (ISSMTPREPLY(nullserver)) 1954363466Sgshapiro { 1955363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 195664562Sgshapiro usrerr(nullserver); 1957363466Sgshapiro } 195864562Sgshapiro else 1959363466Sgshapiro { 196090792Sgshapiro usrerr("550 5.0.0 %s", 196190792Sgshapiro nullserver); 1962363466Sgshapiro } 196364562Sgshapiro } 196464562Sgshapiro else 196564562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 196638032Speter continue; 196738032Speter } 196838032Speter } 196938032Speter 197064562Sgshapiro switch (c->cmd_code) 197138032Speter { 197290792Sgshapiro#if SASL 197364562Sgshapiro case CMDAUTH: /* sasl */ 197490792Sgshapiro DELAY_CONN("AUTH"); 197590792Sgshapiro if (!sasl_ok || n_mechs <= 0) 197664562Sgshapiro { 197764562Sgshapiro message("503 5.3.3 AUTH not available"); 197864562Sgshapiro break; 197964562Sgshapiro } 198064562Sgshapiro if (authenticating == SASL_IS_AUTH) 198164562Sgshapiro { 198264562Sgshapiro message("503 5.5.0 Already Authenticated"); 198364562Sgshapiro break; 198464562Sgshapiro } 198590792Sgshapiro if (smtp.sm_gotmail) 198664562Sgshapiro { 198764562Sgshapiro message("503 5.5.0 AUTH not permitted during a mail transaction"); 198864562Sgshapiro break; 198964562Sgshapiro } 199064562Sgshapiro if (tempfail) 199164562Sgshapiro { 199264562Sgshapiro if (LogLevel > 9) 199364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 1994110560Sgshapiro "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)", 199564562Sgshapiro p, CurSmtpClient); 1996132943Sgshapiro usrerr("454 4.3.0 Please try again later"); 199764562Sgshapiro break; 199864562Sgshapiro } 199964562Sgshapiro 200090792Sgshapiro ismore = false; 200164562Sgshapiro 200264562Sgshapiro /* crude way to avoid crack attempts */ 2003132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1, 2004132943Sgshapiro true, "AUTH", e)); 200564562Sgshapiro 200690792Sgshapiro /* make sure mechanism (p) is a valid string */ 200764562Sgshapiro for (q = p; *q != '\0' && isascii(*q); q++) 200864562Sgshapiro { 200964562Sgshapiro if (isspace(*q)) 201064562Sgshapiro { 201164562Sgshapiro *q = '\0'; 2012363466Sgshapiro while (*++q != '\0' && SM_ISSPACE(*q)) 201364562Sgshapiro continue; 201464562Sgshapiro *(q - 1) = '\0'; 201564562Sgshapiro ismore = (*q != '\0'); 201664562Sgshapiro break; 201764562Sgshapiro } 201864562Sgshapiro } 201964562Sgshapiro 202098121Sgshapiro if (*p == '\0') 202198121Sgshapiro { 202298121Sgshapiro message("501 5.5.2 AUTH mechanism must be specified"); 202398121Sgshapiro break; 202498121Sgshapiro } 202598121Sgshapiro 202664562Sgshapiro /* check whether mechanism is available */ 202764562Sgshapiro if (iteminlist(p, mechlist, " ") == NULL) 202864562Sgshapiro { 202998121Sgshapiro message("504 5.3.3 AUTH mechanism %.32s not available", 203064562Sgshapiro p); 203164562Sgshapiro break; 203264562Sgshapiro } 203364562Sgshapiro 2034173340Sgshapiro /* 2035173340Sgshapiro ** RFC 2554 4. 2036173340Sgshapiro ** Unlike a zero-length client answer to a 2037173340Sgshapiro ** 334 reply, a zero- length initial response 2038173340Sgshapiro ** is sent as a single equals sign ("="). 2039173340Sgshapiro */ 2040173340Sgshapiro 2041173340Sgshapiro if (ismore && *q == '=' && *(q + 1) == '\0') 204264562Sgshapiro { 2043173340Sgshapiro /* will be free()d, don't use in=""; */ 2044173340Sgshapiro in = xalloc(1); 2045173340Sgshapiro *in = '\0'; 2046173340Sgshapiro inlen = 0; 2047173340Sgshapiro } 2048173340Sgshapiro else if (ismore) 2049173340Sgshapiro { 205064562Sgshapiro /* could this be shorter? XXX */ 205198121Sgshapiro# if SASL >= 20000 205298121Sgshapiro in = xalloc(strlen(q) + 1); 2053102528Sgshapiro result = sasl_decode64(q, strlen(q), in, 205498121Sgshapiro strlen(q), &inlen); 205598121Sgshapiro# else /* SASL >= 20000 */ 205690792Sgshapiro in = sm_rpool_malloc(e->e_rpool, strlen(q)); 205764562Sgshapiro result = sasl_decode64(q, strlen(q), in, 205890792Sgshapiro &inlen); 205998121Sgshapiro# endif /* SASL >= 20000 */ 2060363466Sgshapiro 206164562Sgshapiro if (result != SASL_OK) 206264562Sgshapiro { 206364562Sgshapiro message("501 5.5.4 cannot BASE64 decode '%s'", 206464562Sgshapiro q); 206564562Sgshapiro if (LogLevel > 5) 206664562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 2067203004Sgshapiro "AUTH decode64 error [%d for \"%s\"], relay=%.100s", 2068203004Sgshapiro result, q, 2069203004Sgshapiro CurSmtpClient); 207064562Sgshapiro /* start over? */ 207164562Sgshapiro authenticating = SASL_NOT_AUTH; 207298121Sgshapiro# if SASL >= 20000 207398121Sgshapiro sm_free(in); 2074363466Sgshapiro# endif 207564562Sgshapiro in = NULL; 207664562Sgshapiro inlen = 0; 207764562Sgshapiro break; 207864562Sgshapiro } 2079363466Sgshapiro SET_AUTH_USER_TMP(in, inlen); 208064562Sgshapiro } 208164562Sgshapiro else 208264562Sgshapiro { 208364562Sgshapiro in = NULL; 208464562Sgshapiro inlen = 0; 208564562Sgshapiro } 208664562Sgshapiro 208764562Sgshapiro /* see if that auth type exists */ 208898121Sgshapiro# if SASL >= 20000 2089102528Sgshapiro result = sasl_server_start(conn, p, in, inlen, 209098121Sgshapiro &out, &outlen); 2091363466Sgshapiro SM_FREE(in); 209298121Sgshapiro# else /* SASL >= 20000 */ 209364562Sgshapiro result = sasl_server_start(conn, p, in, inlen, 209464562Sgshapiro &out, &outlen, &errstr); 209598121Sgshapiro# endif /* SASL >= 20000 */ 209664562Sgshapiro 2097363466Sgshapiro if (p != NULL) 2098363466Sgshapiro auth_type = newstr(p); 209964562Sgshapiro if (result != SASL_OK && result != SASL_CONTINUE) 210064562Sgshapiro { 2101363466Sgshapiro LOGAUTHFAIL; 210264562Sgshapiro break; 210364562Sgshapiro } 210464562Sgshapiro 210564562Sgshapiro if (result == SASL_OK) 210664562Sgshapiro { 210764562Sgshapiro /* ugly, but same code */ 210864562Sgshapiro goto authenticated; 210964562Sgshapiro /* authenticated by the initial response */ 211064562Sgshapiro } 211164562Sgshapiro 2112363466Sgshapiro SET_AUTH_USER; 2113363466Sgshapiro 211464562Sgshapiro /* len is at least 2 */ 211564562Sgshapiro len = ENC64LEN(outlen); 211664562Sgshapiro out2 = xalloc(len); 211764562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 211890792Sgshapiro &out2len); 211964562Sgshapiro 212064562Sgshapiro if (result != SASL_OK) 212164562Sgshapiro { 212264562Sgshapiro message("454 4.5.4 Temporary authentication failure"); 212364562Sgshapiro if (LogLevel > 5) 212464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 212590792Sgshapiro "AUTH encode64 error [%d for \"%s\"]", 212664562Sgshapiro result, out); 212764562Sgshapiro 212864562Sgshapiro /* start over? */ 212964562Sgshapiro authenticating = SASL_NOT_AUTH; 2130120256Sgshapiro RESET_SASLCONN; 213164562Sgshapiro } 213264562Sgshapiro else 213364562Sgshapiro { 213464562Sgshapiro message("334 %s", out2); 213564562Sgshapiro authenticating = SASL_PROC_AUTH; 213664562Sgshapiro } 213798121Sgshapiro# if SASL >= 20000 213898121Sgshapiro sm_free(out2); 2139363466Sgshapiro# endif 214064562Sgshapiro break; 214190792Sgshapiro#endif /* SASL */ 214264562Sgshapiro 214390792Sgshapiro#if STARTTLS 214464562Sgshapiro case CMDSTLS: /* starttls */ 214590792Sgshapiro DELAY_CONN("STARTTLS"); 214664562Sgshapiro if (*p != '\0') 214764562Sgshapiro { 214864562Sgshapiro message("501 5.5.2 Syntax error (no parameters allowed)"); 214964562Sgshapiro break; 215064562Sgshapiro } 215190792Sgshapiro if (!bitset(SRV_OFFER_TLS, features)) 215264562Sgshapiro { 215364562Sgshapiro message("503 5.5.0 TLS not available"); 215464562Sgshapiro break; 215564562Sgshapiro } 2156363466Sgshapiro starttls: 215777349Sgshapiro if (!tls_ok_srv) 215864562Sgshapiro { 215964562Sgshapiro message("454 4.3.3 TLS not available after start"); 216064562Sgshapiro break; 216164562Sgshapiro } 216290792Sgshapiro if (smtp.sm_gotmail) 216364562Sgshapiro { 216464562Sgshapiro message("503 5.5.0 TLS not permitted during a mail transaction"); 216564562Sgshapiro break; 216664562Sgshapiro } 216764562Sgshapiro if (tempfail) 216864562Sgshapiro { 216964562Sgshapiro if (LogLevel > 9) 217064562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2171110560Sgshapiro "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)", 217264562Sgshapiro p, CurSmtpClient); 2173132943Sgshapiro usrerr("454 4.7.0 Please try again later"); 217464562Sgshapiro break; 217564562Sgshapiro } 2176363466Sgshapiro if (!TLS_set_engine(SSLEngine, false)) 2177223067Sgshapiro { 2178363466Sgshapiro sm_syslog(LOG_ERR, NOQID, 2179363466Sgshapiro "STARTTLS=server, engine=%s, TLS_set_engine=failed", 2180363466Sgshapiro SSLEngine); 2181363466Sgshapiro tls_ok_srv = false; 2182363466Sgshapiro message("454 4.3.3 TLS not available right now"); 2183363466Sgshapiro break; 2184223067Sgshapiro } 218564562Sgshapiro# if TLS_NO_RSA 218664562Sgshapiro /* 218764562Sgshapiro ** XXX do we need a temp key ? 218864562Sgshapiro */ 2189363466Sgshapiro# endif 219090792Sgshapiro 219190792Sgshapiro# if TLS_VRFY_PER_CTX 219290792Sgshapiro /* 219390792Sgshapiro ** Note: this sets the verification globally 219490792Sgshapiro ** (per SSL_CTX) 219590792Sgshapiro ** it's ok since it applies only to one transaction 219690792Sgshapiro */ 219790792Sgshapiro 219890792Sgshapiro TLS_VERIFY_CLIENT(); 219990792Sgshapiro# endif /* TLS_VRFY_PER_CTX */ 220090792Sgshapiro 2201363466Sgshapiro#define SMTLSFAILED \ 2202363466Sgshapiro do { \ 2203363466Sgshapiro SM_SSL_FREE(srv_ssl); \ 2204363466Sgshapiro goto tls_done; \ 2205363466Sgshapiro } while (0) 2206363466Sgshapiro 220764562Sgshapiro if (srv_ssl != NULL) 220864562Sgshapiro SSL_clear(srv_ssl); 220964562Sgshapiro else if ((srv_ssl = SSL_new(srv_ctx)) == NULL) 221064562Sgshapiro { 221164562Sgshapiro message("454 4.3.3 TLS not available: error generating SSL handle"); 2212363466Sgshapiro tlslogerr(LOG_WARNING, 8, "server"); 221390792Sgshapiro goto tls_done; 221464562Sgshapiro } 2215363466Sgshapiro if (get_tls_se_options(e, srv_ssl, &tlsi_ctx, true) 2216363466Sgshapiro != 0) 2217285229Sgshapiro { 2218285229Sgshapiro message("454 4.3.3 TLS not available: error setting options"); 2219363466Sgshapiro SMTLSFAILED; 2220285229Sgshapiro } 2221363466Sgshapiro r = SSL_set_ex_data(srv_ssl, TLSsslidx, &tlsi_ctx); 2222363466Sgshapiro if (0 == r) 2223363466Sgshapiro { 2224363466Sgshapiro if (LogLevel > 5) 2225363466Sgshapiro { 2226363466Sgshapiro sm_syslog(LOG_ERR, NOQID, 2227363466Sgshapiro "STARTTLS=server, error: SSL_set_ex_data failed=%d", 2228363466Sgshapiro r); 2229363466Sgshapiro tlslogerr(LOG_WARNING, 9, "server"); 2230363466Sgshapiro } 2231363466Sgshapiro SMTLSFAILED; 2232363466Sgshapiro } 2233285229Sgshapiro 223490792Sgshapiro# if !TLS_VRFY_PER_CTX 223590792Sgshapiro /* 223690792Sgshapiro ** this could be used if it were possible to set 223790792Sgshapiro ** verification per SSL (connection) 223890792Sgshapiro ** not just per SSL_CTX (global) 223990792Sgshapiro */ 224090792Sgshapiro 224190792Sgshapiro TLS_VERIFY_CLIENT(); 224290792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */ 224390792Sgshapiro 224490792Sgshapiro rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 224590792Sgshapiro wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); 224690792Sgshapiro 224766494Sgshapiro if (rfd < 0 || wfd < 0 || 224866494Sgshapiro SSL_set_rfd(srv_ssl, rfd) <= 0 || 224966494Sgshapiro SSL_set_wfd(srv_ssl, wfd) <= 0) 225064562Sgshapiro { 225164562Sgshapiro message("454 4.3.3 TLS not available: error set fd"); 2252363466Sgshapiro SMTLSFAILED; 225364562Sgshapiro } 225490792Sgshapiro if (!smtps) 225590792Sgshapiro message("220 2.0.0 Ready to start TLS"); 225690792Sgshapiro# if PIPELINING 225790792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 2258363466Sgshapiro# endif 225990792Sgshapiro 226064562Sgshapiro SSL_set_accept_state(srv_ssl); 226164562Sgshapiro 226290792Sgshapiro tlsstart = curtime(); 2263363466Sgshapiro 2264363466Sgshapiro ssl_err = SSL_ERROR_WANT_READ; 2265363466Sgshapiro save_errno = 0; 2266363466Sgshapiro do 226764562Sgshapiro { 2268363466Sgshapiro tlsret = tls_retry(srv_ssl, rfd, wfd, tlsstart, 2269157001Sgshapiro TimeOuts.to_starttls, ssl_err, 2270157001Sgshapiro "server"); 2271363466Sgshapiro if (tlsret <= 0) 227264562Sgshapiro { 2273363466Sgshapiro if (LogLevel > 5) 2274363466Sgshapiro { 2275363466Sgshapiro unsigned long l; 2276363466Sgshapiro const char *sr; 2277244833Sgshapiro 2278363466Sgshapiro l = ERR_peek_error(); 2279363466Sgshapiro sr = ERR_reason_error_string(l); 2280363466Sgshapiro 2281363466Sgshapiro sm_syslog(LOG_WARNING, NOQID, 2282363466Sgshapiro "STARTTLS=server, error: accept failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d, relay=%.100s", 2283363466Sgshapiro r, sr == NULL ? "unknown" 2284363466Sgshapiro : sr, 2285363466Sgshapiro ssl_err, save_errno, 2286363466Sgshapiro tlsret, CurSmtpClient); 2287363466Sgshapiro tlslogerr(LOG_WARNING, 9, "server"); 2288363466Sgshapiro } 2289363466Sgshapiro tls_ok_srv = false; 2290363466Sgshapiro SM_SSL_FREE(srv_ssl); 2291363466Sgshapiro 2292363466Sgshapiro /* 2293363466Sgshapiro ** according to the next draft of 2294363466Sgshapiro ** RFC 2487 the connection should 2295363466Sgshapiro ** be dropped 2296363466Sgshapiro ** 2297363466Sgshapiro ** arrange to ignore any current 2298363466Sgshapiro ** send list 2299363466Sgshapiro */ 2300363466Sgshapiro 2301363466Sgshapiro e->e_sendqueue = NULL; 2302363466Sgshapiro goto doquit; 230364562Sgshapiro } 230464562Sgshapiro 2305363466Sgshapiro r = SSL_accept(srv_ssl); 2306363466Sgshapiro save_errno = 0; 2307363466Sgshapiro if (r <= 0) 2308363466Sgshapiro ssl_err = SSL_get_error(srv_ssl, r); 2309363466Sgshapiro } while (r <= 0); 231064562Sgshapiro 231164562Sgshapiro /* ignore return code for now, it's in {verify} */ 231290792Sgshapiro (void) tls_get_info(srv_ssl, true, 231390792Sgshapiro CurSmtpClient, 231490792Sgshapiro &BlankEnvelope.e_macro, 231590792Sgshapiro bitset(SRV_VRFY_CLT, features)); 231664562Sgshapiro 231764562Sgshapiro /* 231864562Sgshapiro ** call Stls_client to find out whether 231964562Sgshapiro ** to accept the connection from the client 232064562Sgshapiro */ 232164562Sgshapiro 232264562Sgshapiro saveQuickAbort = QuickAbort; 232364562Sgshapiro saveSuprErrs = SuprErrs; 232490792Sgshapiro SuprErrs = true; 232590792Sgshapiro QuickAbort = false; 232664562Sgshapiro if (rscheck("tls_client", 232790792Sgshapiro macvalue(macid("{verify}"), e), 2328102528Sgshapiro "STARTTLS", e, 2329102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 2330285229Sgshapiro 5, NULL, NOQID, NULL, NULL) != EX_OK || 233190792Sgshapiro Errors > 0) 233264562Sgshapiro { 233364562Sgshapiro extern char MsgBuf[]; 233464562Sgshapiro 233564562Sgshapiro if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf)) 233664562Sgshapiro nullserver = newstr(MsgBuf); 233764562Sgshapiro else 233864562Sgshapiro nullserver = "503 5.7.0 Authentication required."; 233964562Sgshapiro } 234064562Sgshapiro QuickAbort = saveQuickAbort; 234164562Sgshapiro SuprErrs = saveSuprErrs; 234264562Sgshapiro 234390792Sgshapiro tls_ok_srv = false; /* don't offer STARTTLS again */ 234464562Sgshapiro n_helo = 0; 234590792Sgshapiro# if SASL 234664562Sgshapiro if (sasl_ok) 234764562Sgshapiro { 2348132943Sgshapiro int cipher_bits; 2349132943Sgshapiro bool verified; 2350132943Sgshapiro char *s, *v, *c; 235164562Sgshapiro 235290792Sgshapiro s = macvalue(macid("{cipher_bits}"), e); 2353132943Sgshapiro v = macvalue(macid("{verify}"), e); 2354132943Sgshapiro c = macvalue(macid("{cert_subject}"), e); 2355132943Sgshapiro verified = (v != NULL && strcmp(v, "OK") == 0); 2356132943Sgshapiro if (s != NULL && (cipher_bits = atoi(s)) > 0) 2357132943Sgshapiro { 235898121Sgshapiro# if SASL >= 20000 2359132943Sgshapiro ext_ssf = cipher_bits; 2360132943Sgshapiro auth_id = verified ? c : NULL; 2361132943Sgshapiro sasl_ok = ((sasl_setprop(conn, 2362132943Sgshapiro SASL_SSF_EXTERNAL, 2363132943Sgshapiro &ext_ssf) == SASL_OK) && 2364132943Sgshapiro (sasl_setprop(conn, 2365132943Sgshapiro SASL_AUTH_EXTERNAL, 2366132943Sgshapiro auth_id) == SASL_OK)); 236798121Sgshapiro# else /* SASL >= 20000 */ 2368132943Sgshapiro ext_ssf.ssf = cipher_bits; 2369132943Sgshapiro ext_ssf.auth_id = verified ? c : NULL; 2370132943Sgshapiro sasl_ok = sasl_setprop(conn, 2371132943Sgshapiro SASL_SSF_EXTERNAL, 2372132943Sgshapiro &ext_ssf) == SASL_OK; 237398121Sgshapiro# endif /* SASL >= 20000 */ 237464562Sgshapiro mechlist = NULL; 237564562Sgshapiro if (sasl_ok) 237664562Sgshapiro n_mechs = saslmechs(conn, 237764562Sgshapiro &mechlist); 237864562Sgshapiro } 237964562Sgshapiro } 238090792Sgshapiro# endif /* SASL */ 238164562Sgshapiro 238264562Sgshapiro /* switch to secure connection */ 238390792Sgshapiro if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0) 238490792Sgshapiro { 238590792Sgshapiro tls_active = true; 238690792Sgshapiro# if PIPELINING 238790792Sgshapiro (void) sm_io_autoflush(InChannel, OutChannel); 2388363466Sgshapiro# endif 238990792Sgshapiro } 239064562Sgshapiro else 239164562Sgshapiro { 239264562Sgshapiro /* 239364562Sgshapiro ** XXX this is an internal error 239464562Sgshapiro ** how to deal with it? 239564562Sgshapiro ** we can't generate an error message 239664562Sgshapiro ** since the other side switched to an 239764562Sgshapiro ** encrypted layer, but we could not... 239864562Sgshapiro ** just "hang up"? 239964562Sgshapiro */ 240090792Sgshapiro 240164562Sgshapiro nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer"; 240290792Sgshapiro syserr("STARTTLS: can't switch to encrypted layer"); 240364562Sgshapiro } 240490792Sgshapiro tls_done: 240590792Sgshapiro if (smtps) 240690792Sgshapiro { 240790792Sgshapiro if (tls_active) 240890792Sgshapiro goto greeting; 240990792Sgshapiro else 241090792Sgshapiro goto doquit; 241190792Sgshapiro } 241264562Sgshapiro break; 241390792Sgshapiro#endif /* STARTTLS */ 241464562Sgshapiro 241538032Speter case CMDHELO: /* hello -- introduce yourself */ 241638032Speter case CMDEHLO: /* extended hello */ 241790792Sgshapiro DELAY_CONN("EHLO"); 241864562Sgshapiro if (c->cmd_code == CMDEHLO) 241938032Speter { 2420285229Sgshapiro protocol = GET_PROTOCOL(); 242138032Speter SmtpPhase = "server EHLO"; 242238032Speter } 242338032Speter else 242438032Speter { 242538032Speter protocol = "SMTP"; 242638032Speter SmtpPhase = "server HELO"; 242738032Speter } 242838032Speter 242938032Speter /* avoid denial-of-service */ 2430132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS, 2431132943Sgshapiro true, "HELO/EHLO", e)); 243238032Speter 243390792Sgshapiro#if 0 243490792Sgshapiro /* RFC2821 4.1.4 allows duplicate HELO/EHLO */ 243538032Speter /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ 243638032Speter if (gothello) 243738032Speter { 243838032Speter usrerr("503 %s Duplicate HELO/EHLO", 243973188Sgshapiro MyHostName); 244038032Speter break; 244138032Speter } 244290792Sgshapiro#endif /* 0 */ 244338032Speter 244438032Speter /* check for valid domain name (re 1123 5.2.5) */ 244538032Speter if (*p == '\0' && !AllowBogusHELO) 244638032Speter { 244738032Speter usrerr("501 %s requires domain address", 244838032Speter cmdbuf); 244938032Speter break; 245038032Speter } 245138032Speter 245238032Speter /* check for long domain name (hides Received: info) */ 245338032Speter if (strlen(p) > MAXNAME) 245438032Speter { 245538032Speter usrerr("501 Invalid domain name"); 245664562Sgshapiro if (LogLevel > 9) 245764562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 2458110560Sgshapiro "invalid domain name (too long) from %s", 245964562Sgshapiro CurSmtpClient); 246038032Speter break; 246138032Speter } 246238032Speter 246398121Sgshapiro ok = true; 246438032Speter for (q = p; *q != '\0'; q++) 246538032Speter { 246638032Speter if (!isascii(*q)) 246738032Speter break; 246838032Speter if (isalnum(*q)) 246938032Speter continue; 247038032Speter if (isspace(*q)) 247138032Speter { 247238032Speter *q = '\0'; 247398121Sgshapiro 247498121Sgshapiro /* only complain if strict check */ 247598121Sgshapiro ok = AllowBogusHELO; 2476141858Sgshapiro 2477141858Sgshapiro /* allow trailing whitespace */ 2478141858Sgshapiro while (!ok && *++q != '\0' && 2479141858Sgshapiro isspace(*q)) 2480141858Sgshapiro ; 2481141858Sgshapiro if (*q == '\0') 2482141858Sgshapiro ok = true; 248338032Speter break; 248438032Speter } 2485120256Sgshapiro if (strchr("[].-_#:", *q) == NULL) 248638032Speter break; 248738032Speter } 248864562Sgshapiro 248998121Sgshapiro if (*q == '\0' && ok) 249038032Speter { 249138032Speter q = "pleased to meet you"; 249290792Sgshapiro sendinghost = sm_strdup_x(p); 249338032Speter } 249438032Speter else if (!AllowBogusHELO) 249538032Speter { 249638032Speter usrerr("501 Invalid domain name"); 249764562Sgshapiro if (LogLevel > 9) 249864562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 2499110560Sgshapiro "invalid domain name (%s) from %.100s", 250064562Sgshapiro p, CurSmtpClient); 250138032Speter break; 250238032Speter } 250338032Speter else 250438032Speter { 250538032Speter q = "accepting invalid domain name"; 250638032Speter } 250738032Speter 2508157001Sgshapiro if (gothello || smtp.sm_gotmail) 250990792Sgshapiro CLEAR_STATE(cmdbuf); 251090792Sgshapiro 251190792Sgshapiro#if MILTER 251290792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 251390792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 251464562Sgshapiro { 251564562Sgshapiro char state; 251664562Sgshapiro char *response; 251764562Sgshapiro 251864562Sgshapiro response = milter_helo(p, e, &state); 251964562Sgshapiro switch (state) 252064562Sgshapiro { 252164562Sgshapiro case SMFIR_REJECT: 252290792Sgshapiro if (MilterLogLevel > 3) 252390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 252490792Sgshapiro "Milter: helo=%s, reject=Command rejected", 252590792Sgshapiro p); 252664562Sgshapiro nullserver = "Command rejected"; 252790792Sgshapiro smtp.sm_milterize = false; 252864562Sgshapiro break; 252964562Sgshapiro 253064562Sgshapiro case SMFIR_TEMPFAIL: 253190792Sgshapiro if (MilterLogLevel > 3) 253290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 253390792Sgshapiro "Milter: helo=%s, reject=%s", 253490792Sgshapiro p, MSG_TEMPFAIL); 253590792Sgshapiro tempfail = true; 253690792Sgshapiro smtp.sm_milterize = false; 253764562Sgshapiro break; 2538157001Sgshapiro 2539168515Sgshapiro case SMFIR_REPLYCODE: 2540157001Sgshapiro if (MilterLogLevel > 3) 2541157001Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2542168515Sgshapiro "Milter: helo=%s, reject=%s", 2543168515Sgshapiro p, response); 2544168515Sgshapiro if (strncmp(response, "421 ", 4) != 0 2545168515Sgshapiro && strncmp(response, "421-", 4) != 0) 2546168515Sgshapiro { 2547168515Sgshapiro nullserver = newstr(response); 2548168515Sgshapiro smtp.sm_milterize = false; 2549168515Sgshapiro break; 2550168515Sgshapiro } 2551168515Sgshapiro /* FALLTHROUGH */ 2552168515Sgshapiro 2553168515Sgshapiro case SMFIR_SHUTDOWN: 2554168515Sgshapiro if (MilterLogLevel > 3 && 2555168515Sgshapiro response == NULL) 2556168515Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2557159609Sgshapiro "Milter: helo=%s, reject=421 4.7.0 %s closing connection", 2558157001Sgshapiro p, MyHostName); 2559157001Sgshapiro tempfail = true; 2560157001Sgshapiro smtp.sm_milterize = false; 2561168515Sgshapiro if (response != NULL) 2562363466Sgshapiro { 2563363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 2564168515Sgshapiro usrerr(response); 2565363466Sgshapiro } 2566168515Sgshapiro else 2567363466Sgshapiro { 2568168515Sgshapiro message("421 4.7.0 %s closing connection", 2569168515Sgshapiro MyHostName); 2570363466Sgshapiro } 2571157001Sgshapiro /* arrange to ignore send list */ 2572157001Sgshapiro e->e_sendqueue = NULL; 2573168515Sgshapiro lognullconnection = false; 2574157001Sgshapiro goto doquit; 257564562Sgshapiro } 257690792Sgshapiro if (response != NULL) 257790792Sgshapiro sm_free(response); 257890792Sgshapiro 257990792Sgshapiro /* 258090792Sgshapiro ** If quarantining by a connect/ehlo action, 258190792Sgshapiro ** save between messages 258290792Sgshapiro */ 258390792Sgshapiro 258490792Sgshapiro if (smtp.sm_quarmsg == NULL && 258590792Sgshapiro e->e_quarmsg != NULL) 258690792Sgshapiro smtp.sm_quarmsg = newstr(e->e_quarmsg); 258764562Sgshapiro } 258890792Sgshapiro#endif /* MILTER */ 258990792Sgshapiro gothello = true; 259064562Sgshapiro 259138032Speter /* print HELO response message */ 259264562Sgshapiro if (c->cmd_code != CMDEHLO) 259338032Speter { 259438032Speter message("250 %s Hello %s, %s", 259538032Speter MyHostName, CurSmtpClient, q); 259638032Speter break; 259738032Speter } 259838032Speter 259938032Speter message("250-%s Hello %s, %s", 260038032Speter MyHostName, CurSmtpClient, q); 260138032Speter 260264562Sgshapiro /* offer ENHSC even for nullserver */ 260364562Sgshapiro if (nullserver != NULL) 260464562Sgshapiro { 260564562Sgshapiro message("250 ENHANCEDSTATUSCODES"); 260664562Sgshapiro break; 260764562Sgshapiro } 260864562Sgshapiro 260966494Sgshapiro /* 261066494Sgshapiro ** print EHLO features list 261166494Sgshapiro ** 261266494Sgshapiro ** Note: If you change this list, 261390792Sgshapiro ** remember to update 'helpfile' 261466494Sgshapiro */ 261566494Sgshapiro 261664562Sgshapiro message("250-ENHANCEDSTATUSCODES"); 261790792Sgshapiro#if PIPELINING 261890792Sgshapiro if (bitset(SRV_OFFER_PIPE, features)) 261990792Sgshapiro message("250-PIPELINING"); 2620363466Sgshapiro#endif 262190792Sgshapiro if (bitset(SRV_OFFER_EXPN, features)) 262238032Speter { 262338032Speter message("250-EXPN"); 262490792Sgshapiro if (bitset(SRV_OFFER_VERB, features)) 262538032Speter message("250-VERB"); 262638032Speter } 262790792Sgshapiro#if MIME8TO7 262838032Speter message("250-8BITMIME"); 2629363466Sgshapiro#endif 263038032Speter if (MaxMessageSize > 0) 263138032Speter message("250-SIZE %ld", MaxMessageSize); 263238032Speter else 263338032Speter message("250-SIZE"); 263490792Sgshapiro#if DSN 263590792Sgshapiro if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features)) 263638032Speter message("250-DSN"); 2637363466Sgshapiro#endif 2638363466Sgshapiro#if _FFR_EAI 2639363466Sgshapiro if (bitset(SRV_OFFER_EAI, features)) 2640363466Sgshapiro message("250-SMTPUTF8"); 2641363466Sgshapiro#endif /* _FFR_EAI */ 264290792Sgshapiro if (bitset(SRV_OFFER_ETRN, features)) 264338032Speter message("250-ETRN"); 264490792Sgshapiro#if SASL 264564562Sgshapiro if (sasl_ok && mechlist != NULL && *mechlist != '\0') 264664562Sgshapiro message("250-AUTH %s", mechlist); 2647363466Sgshapiro#endif 264890792Sgshapiro#if STARTTLS 2649223067Sgshapiro if (tls_ok_srv && bitset(SRV_OFFER_TLS, features)) 265064562Sgshapiro message("250-STARTTLS"); 2651363466Sgshapiro#endif 265290792Sgshapiro if (DeliverByMin > 0) 265390792Sgshapiro message("250-DELIVERBY %ld", 265490792Sgshapiro (long) DeliverByMin); 265590792Sgshapiro else if (DeliverByMin == 0) 265690792Sgshapiro message("250-DELIVERBY"); 265790792Sgshapiro 265890792Sgshapiro /* < 0: no deliver-by */ 265990792Sgshapiro 266038032Speter message("250 HELP"); 266138032Speter break; 266238032Speter 266338032Speter case CMDMAIL: /* mail -- designate sender */ 266438032Speter SmtpPhase = "server MAIL"; 266590792Sgshapiro DELAY_CONN("MAIL"); 266638032Speter 266738032Speter /* check for validity of this command */ 266838032Speter if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 266938032Speter { 267064562Sgshapiro usrerr("503 5.0.0 Polite people say HELO first"); 267138032Speter break; 267238032Speter } 267390792Sgshapiro if (smtp.sm_gotmail) 267438032Speter { 267564562Sgshapiro usrerr("503 5.5.0 Sender already specified"); 267638032Speter break; 267738032Speter } 267890792Sgshapiro#if SASL 267990792Sgshapiro if (bitset(SRV_REQ_AUTH, features) && 268064562Sgshapiro authenticating != SASL_IS_AUTH) 268164562Sgshapiro { 268264562Sgshapiro usrerr("530 5.7.0 Authentication required"); 268364562Sgshapiro break; 268464562Sgshapiro } 268590792Sgshapiro#endif /* SASL */ 268638032Speter 268764562Sgshapiro p = skipword(p, "from"); 268864562Sgshapiro if (p == NULL) 268964562Sgshapiro break; 269064562Sgshapiro if (tempfail) 269164562Sgshapiro { 269264562Sgshapiro if (LogLevel > 9) 269364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2694110560Sgshapiro "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)", 269564562Sgshapiro p, CurSmtpClient); 2696363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 269790792Sgshapiro usrerr(MSG_TEMPFAIL); 269864562Sgshapiro break; 269964562Sgshapiro } 270064562Sgshapiro 270138032Speter /* make sure we know who the sending host is */ 270238032Speter if (sendinghost == NULL) 270338032Speter sendinghost = peerhostname; 270438032Speter 270538032Speter 270690792Sgshapiro#if SM_HEAP_CHECK 270790792Sgshapiro if (sm_debug_active(&DebugLeakSmtp, 1)) 270890792Sgshapiro { 270990792Sgshapiro sm_heap_newgroup(); 271090792Sgshapiro sm_dprintf("smtp() heap group #%d\n", 271190792Sgshapiro sm_heap_group()); 271290792Sgshapiro } 271390792Sgshapiro#endif /* SM_HEAP_CHECK */ 271464562Sgshapiro 271538032Speter if (Errors > 0) 271690792Sgshapiro goto undo_no_pm; 271738032Speter if (!gothello) 271838032Speter { 271990792Sgshapiro auth_warning(e, "%s didn't use HELO protocol", 272090792Sgshapiro CurSmtpClient); 272138032Speter } 272290792Sgshapiro#ifdef PICKY_HELO_CHECK 272390792Sgshapiro if (sm_strcasecmp(sendinghost, peerhostname) != 0 && 272490792Sgshapiro (sm_strcasecmp(peerhostname, "localhost") != 0 || 272590792Sgshapiro sm_strcasecmp(sendinghost, MyHostName) != 0)) 272638032Speter { 272738032Speter auth_warning(e, "Host %s claimed to be %s", 272890792Sgshapiro CurSmtpClient, sendinghost); 272938032Speter } 273090792Sgshapiro#endif /* PICKY_HELO_CHECK */ 273138032Speter 273238032Speter if (protocol == NULL) 273338032Speter protocol = "SMTP"; 273490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'r', protocol); 273590792Sgshapiro macdefine(&e->e_macro, A_PERM, 's', sendinghost); 273664562Sgshapiro 273738032Speter if (Errors > 0) 273890792Sgshapiro goto undo_no_pm; 273990792Sgshapiro smtp.sm_nrcpts = 0; 274090792Sgshapiro n_badrcpts = 0; 274190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0"); 274290792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0"); 2743132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"), 2744132943Sgshapiro "0"); 274564562Sgshapiro e->e_flags |= EF_CLRQUEUE; 274690792Sgshapiro sm_setproctitle(true, e, "%s %s: %.80s", 274764562Sgshapiro qid_printname(e), 274864562Sgshapiro CurSmtpClient, inp); 274938032Speter 275090792Sgshapiro /* do the processing */ 275190792Sgshapiro SM_TRY 275290792Sgshapiro { 275394334Sgshapiro extern char *FullName; 275494334Sgshapiro 275590792Sgshapiro QuickAbort = true; 2756363466Sgshapiro SM_FREE(FullName); 275764562Sgshapiro 275838032Speter /* must parse sender first */ 275938032Speter delimptr = NULL; 276090792Sgshapiro setsender(p, e, &delimptr, ' ', false); 276138032Speter if (delimptr != NULL && *delimptr != '\0') 276238032Speter *delimptr++ = '\0'; 276338032Speter if (Errors > 0) 276490792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 276538032Speter 276664562Sgshapiro /* Successfully set e_from, allow logging */ 276764562Sgshapiro e->e_flags |= EF_LOGSENDER; 276838032Speter 276964562Sgshapiro /* put resulting triple from parseaddr() into macros */ 277064562Sgshapiro if (e->e_from.q_mailer != NULL) 277190792Sgshapiro macdefine(&e->e_macro, A_PERM, 277290792Sgshapiro macid("{mail_mailer}"), 277390792Sgshapiro e->e_from.q_mailer->m_name); 277464562Sgshapiro else 277590792Sgshapiro macdefine(&e->e_macro, A_PERM, 277690792Sgshapiro macid("{mail_mailer}"), NULL); 277764562Sgshapiro if (e->e_from.q_host != NULL) 277890792Sgshapiro macdefine(&e->e_macro, A_PERM, 277990792Sgshapiro macid("{mail_host}"), 278090792Sgshapiro e->e_from.q_host); 278164562Sgshapiro else 278290792Sgshapiro macdefine(&e->e_macro, A_PERM, 278390792Sgshapiro macid("{mail_host}"), "localhost"); 278464562Sgshapiro if (e->e_from.q_user != NULL) 278590792Sgshapiro macdefine(&e->e_macro, A_PERM, 278690792Sgshapiro macid("{mail_addr}"), 278790792Sgshapiro e->e_from.q_user); 278864562Sgshapiro else 278990792Sgshapiro macdefine(&e->e_macro, A_PERM, 279090792Sgshapiro macid("{mail_addr}"), NULL); 279164562Sgshapiro if (Errors > 0) 279290792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 279364562Sgshapiro 279438032Speter /* check for possible spoofing */ 279538032Speter if (RealUid != 0 && OpMode == MD_SMTP && 279638032Speter !wordinclass(RealUserName, 't') && 279764562Sgshapiro (!bitnset(M_LOCALMAILER, 279864562Sgshapiro e->e_from.q_mailer->m_flags) || 279964562Sgshapiro strcmp(e->e_from.q_user, RealUserName) != 0)) 280038032Speter { 280138032Speter auth_warning(e, "%s owned process doing -bs", 280238032Speter RealUserName); 280338032Speter } 280438032Speter 2805120256Sgshapiro /* reset to default value */ 2806168515Sgshapiro SevenBitInput = SevenBitInput_Saved; 2807120256Sgshapiro 280838032Speter /* now parse ESMTP arguments */ 280938032Speter e->e_msgsize = 0; 281064562Sgshapiro addr = p; 2811168515Sgshapiro parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args, 2812168515Sgshapiro mail_esmtp_args); 281338032Speter if (Errors > 0) 281490792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 281538032Speter 2816363466Sgshapiro#if _FFR_EAI 2817363466Sgshapiro if (e->e_smtputf8) 2818363466Sgshapiro { 2819363466Sgshapiro protocol = GET_PROTOCOL(); 2820363466Sgshapiro macdefine(&e->e_macro, A_PERM, 'r', protocol); 2821363466Sgshapiro } 2822363466Sgshapiro 2823363466Sgshapiro /* UTF8 addresses are only legal with SMTPUTF8 */ 2824363466Sgshapiro if (!e->e_smtputf8 && !addr_is_ascii(e->e_from.q_paddr)) 2825363466Sgshapiro { 2826363466Sgshapiro usrerr("553 5.6.7 That address requires SMTPUTF8"); 2827363466Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 2828363466Sgshapiro } 2829363466Sgshapiro#endif 2830363466Sgshapiro 283190792Sgshapiro#if SASL 283290792Sgshapiro# if _FFR_AUTH_PASSING 283390792Sgshapiro /* set the default AUTH= if the sender didn't */ 283490792Sgshapiro if (e->e_auth_param == NULL) 283590792Sgshapiro { 283690792Sgshapiro /* XXX only do this for an MSA? */ 283790792Sgshapiro e->e_auth_param = macvalue(macid("{auth_authen}"), 283890792Sgshapiro e); 283990792Sgshapiro if (e->e_auth_param == NULL) 284090792Sgshapiro e->e_auth_param = "<>"; 284190792Sgshapiro 284290792Sgshapiro /* 284390792Sgshapiro ** XXX should we invoke Strust_auth now? 284490792Sgshapiro ** authorizing as the client that just 284590792Sgshapiro ** authenticated, so we'll trust implicitly 284690792Sgshapiro */ 284790792Sgshapiro } 284890792Sgshapiro# endif /* _FFR_AUTH_PASSING */ 284990792Sgshapiro#endif /* SASL */ 285090792Sgshapiro 285164562Sgshapiro /* do config file checking of the sender */ 285290792Sgshapiro macdefine(&e->e_macro, A_PERM, 285390792Sgshapiro macid("{addr_type}"), "e s"); 285490792Sgshapiro#if _FFR_MAIL_MACRO 285590792Sgshapiro /* make the "real" sender address available */ 285690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"), 285790792Sgshapiro e->e_from.q_paddr); 2858363466Sgshapiro#endif 285964562Sgshapiro if (rscheck("check_mail", addr, 2860102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 3, 2861285229Sgshapiro NULL, e->e_id, NULL, NULL) != EX_OK || 286264562Sgshapiro Errors > 0) 286390792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 286490792Sgshapiro macdefine(&e->e_macro, A_PERM, 286590792Sgshapiro macid("{addr_type}"), NULL); 286664562Sgshapiro 286766494Sgshapiro if (MaxMessageSize > 0 && 286877349Sgshapiro (e->e_msgsize > MaxMessageSize || 286977349Sgshapiro e->e_msgsize < 0)) 287038032Speter { 287164562Sgshapiro usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)", 287238032Speter MaxMessageSize); 287390792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 287438032Speter } 287564562Sgshapiro 287690792Sgshapiro /* 287790792Sgshapiro ** XXX always check whether there is at least one fs 287890792Sgshapiro ** with enough space? 287990792Sgshapiro ** However, this may not help much: the queue group 288090792Sgshapiro ** selection may later on select a FS that hasn't 288190792Sgshapiro ** enough space. 288290792Sgshapiro */ 288390792Sgshapiro 288490792Sgshapiro if ((NumFileSys == 1 || NumQueue == 1) && 288590792Sgshapiro !enoughdiskspace(e->e_msgsize, e) 288690792Sgshapiro#if _FFR_ANY_FREE_FS 288790792Sgshapiro && !filesys_free(e->e_msgsize) 2888363466Sgshapiro#endif 288990792Sgshapiro ) 289038032Speter { 289190792Sgshapiro /* 289290792Sgshapiro ** We perform this test again when the 289390792Sgshapiro ** queue directory is selected, in collect. 289490792Sgshapiro */ 289590792Sgshapiro 289664562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 289790792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 289838032Speter } 289938032Speter if (Errors > 0) 290090792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 290164562Sgshapiro 290290792Sgshapiro LogUsrErrs = true; 290390792Sgshapiro#if MILTER 290490792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 290590792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 290664562Sgshapiro { 290764562Sgshapiro char state; 290864562Sgshapiro char *response; 290964562Sgshapiro 291064562Sgshapiro response = milter_envfrom(args, e, &state); 291190792Sgshapiro MILTER_REPLY("from"); 291264562Sgshapiro } 291390792Sgshapiro#endif /* MILTER */ 291464562Sgshapiro if (Errors > 0) 291590792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 291664562Sgshapiro 291764562Sgshapiro message("250 2.1.0 Sender ok"); 291890792Sgshapiro smtp.sm_gotmail = true; 291990792Sgshapiro } 292090792Sgshapiro SM_EXCEPT(exc, "[!F]*") 292190792Sgshapiro { 292290792Sgshapiro /* 292390792Sgshapiro ** An error occurred while processing a MAIL command. 292490792Sgshapiro ** Jump to the common error handling code. 292590792Sgshapiro */ 292690792Sgshapiro 292790792Sgshapiro sm_exc_free(exc); 292890792Sgshapiro goto undo_no_pm; 292990792Sgshapiro } 293090792Sgshapiro SM_END_TRY 293138032Speter break; 293238032Speter 293390792Sgshapiro undo_no_pm: 293490792Sgshapiro e->e_flags &= ~EF_PM_NOTIFY; 293590792Sgshapiro undo: 293690792Sgshapiro break; 293790792Sgshapiro 293838032Speter case CMDRCPT: /* rcpt -- designate recipient */ 293990792Sgshapiro DELAY_CONN("RCPT"); 2940168515Sgshapiro macdefine(&e->e_macro, A_PERM, 2941168515Sgshapiro macid("{rcpt_mailer}"), NULL); 2942168515Sgshapiro macdefine(&e->e_macro, A_PERM, 2943168515Sgshapiro macid("{rcpt_host}"), NULL); 2944168515Sgshapiro macdefine(&e->e_macro, A_PERM, 2945168515Sgshapiro macid("{rcpt_addr}"), NULL); 2946168515Sgshapiro#if MILTER 2947168515Sgshapiro (void) memset(&addr_st, '\0', sizeof(addr_st)); 2948168515Sgshapiro a = NULL; 2949168515Sgshapiro milter_rcpt_added = false; 2950173340Sgshapiro smtp.sm_e_nrcpts_orig = e->e_nrcpts; 2951168515Sgshapiro#endif 2952182352Sgshapiro#if _FFR_BADRCPT_SHUTDOWN 2953182352Sgshapiro /* 2954182352Sgshapiro ** hack to deal with hack, see below: 2955203004Sgshapiro ** n_badrcpts is increased if limit is reached. 2956182352Sgshapiro */ 2957182352Sgshapiro 2958182352Sgshapiro n_badrcpts_adj = (BadRcptThrottle > 0 && 2959182352Sgshapiro n_badrcpts > BadRcptThrottle && 2960182352Sgshapiro LogLevel > 5) 2961182352Sgshapiro ? n_badrcpts - 1 : n_badrcpts; 2962182352Sgshapiro if (BadRcptShutdown > 0 && 2963182352Sgshapiro n_badrcpts_adj >= BadRcptShutdown && 2964182352Sgshapiro (BadRcptShutdownGood == 0 || 2965182352Sgshapiro smtp.sm_nrcpts == 0 || 2966182352Sgshapiro (n_badrcpts_adj * 100 / 2967182352Sgshapiro (smtp.sm_nrcpts + n_badrcpts) >= 2968182352Sgshapiro BadRcptShutdownGood))) 2969182352Sgshapiro { 2970182352Sgshapiro if (LogLevel > 5) 2971182352Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2972182352Sgshapiro "%s: Possible SMTP RCPT flood, shutting down connection.", 2973182352Sgshapiro CurSmtpClient); 2974182352Sgshapiro message("421 4.7.0 %s Too many bad recipients; closing connection", 2975182352Sgshapiro MyHostName); 2976182352Sgshapiro 2977182352Sgshapiro /* arrange to ignore any current send list */ 2978182352Sgshapiro e->e_sendqueue = NULL; 2979182352Sgshapiro goto doquit; 2980182352Sgshapiro } 2981182352Sgshapiro#endif /* _FFR_BADRCPT_SHUTDOWN */ 2982125820Sgshapiro if (BadRcptThrottle > 0 && 2983125820Sgshapiro n_badrcpts >= BadRcptThrottle) 2984125820Sgshapiro { 2985125820Sgshapiro if (LogLevel > 5 && 2986125820Sgshapiro n_badrcpts == BadRcptThrottle) 2987125820Sgshapiro { 2988125820Sgshapiro sm_syslog(LOG_INFO, e->e_id, 2989125820Sgshapiro "%s: Possible SMTP RCPT flood, throttling.", 2990125820Sgshapiro CurSmtpClient); 2991125820Sgshapiro 2992125820Sgshapiro /* To avoid duplicated message */ 2993125820Sgshapiro n_badrcpts++; 2994125820Sgshapiro } 2995132943Sgshapiro NBADRCPTS; 2996125820Sgshapiro 2997125820Sgshapiro /* 2998125820Sgshapiro ** Don't use exponential backoff for now. 2999203004Sgshapiro ** Some systems will open more connections 3000125820Sgshapiro ** and actually overload the receiver even 3001125820Sgshapiro ** more. 3002125820Sgshapiro */ 3003125820Sgshapiro 3004203004Sgshapiro (void) sleep(BadRcptThrottleDelay); 3005125820Sgshapiro } 300690792Sgshapiro if (!smtp.sm_gotmail) 300738032Speter { 300864562Sgshapiro usrerr("503 5.0.0 Need MAIL before RCPT"); 300938032Speter break; 301038032Speter } 301138032Speter SmtpPhase = "server RCPT"; 301290792Sgshapiro SM_TRY 301390792Sgshapiro { 301490792Sgshapiro QuickAbort = true; 301590792Sgshapiro LogUsrErrs = true; 301638032Speter 301738032Speter /* limit flooding of our machine */ 301890792Sgshapiro if (MaxRcptPerMsg > 0 && 301990792Sgshapiro smtp.sm_nrcpts >= MaxRcptPerMsg) 302038032Speter { 302190792Sgshapiro /* sleep(1); / * slow down? */ 302264562Sgshapiro usrerr("452 4.5.3 Too many recipients"); 302390792Sgshapiro goto rcpt_done; 302438032Speter } 302538032Speter 3026223067Sgshapiro if (!SM_IS_INTERACTIVE(e->e_sendmode) 3027157001Sgshapiro#if _FFR_DM_ONE 3028157001Sgshapiro && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode) 3029363466Sgshapiro#endif 3030157001Sgshapiro ) 303138032Speter e->e_flags |= EF_VRFYONLY; 303238032Speter 303390792Sgshapiro#if MILTER 303464562Sgshapiro /* 3035168515Sgshapiro ** Do not expand recipients at RCPT time (in the call 3036173340Sgshapiro ** to recipient()) if a milter can delete or reject 3037173340Sgshapiro ** a RCPT. If they are expanded, it is impossible 3038173340Sgshapiro ** for removefromlist() to figure out the expanded 3039173340Sgshapiro ** members of the original recipient and mark them 3040173340Sgshapiro ** as QS_DONTSEND. 304164562Sgshapiro */ 304264562Sgshapiro 3043363466Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 3044363466Sgshapiro !bitset(EF_DISCARD, e->e_flags) && 3045173340Sgshapiro (smtp.sm_milters.mis_flags & 3046173340Sgshapiro (MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0) 3047173340Sgshapiro e->e_flags |= EF_VRFYONLY; 3048168515Sgshapiro milter_cmd_done = false; 3049168515Sgshapiro milter_cmd_safe = false; 305090792Sgshapiro#endif /* MILTER */ 305164562Sgshapiro 305238032Speter p = skipword(p, "to"); 305338032Speter if (p == NULL) 305490792Sgshapiro goto rcpt_done; 305590792Sgshapiro macdefine(&e->e_macro, A_PERM, 305690792Sgshapiro macid("{addr_type}"), "e r"); 305790792Sgshapiro a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, 305890792Sgshapiro e, true); 305990792Sgshapiro macdefine(&e->e_macro, A_PERM, 306090792Sgshapiro macid("{addr_type}"), NULL); 306142575Speter if (Errors > 0) 306290792Sgshapiro goto rcpt_done; 306342575Speter if (a == NULL) 306442575Speter { 306564562Sgshapiro usrerr("501 5.0.0 Missing recipient"); 306690792Sgshapiro goto rcpt_done; 306742575Speter } 3068363466Sgshapiro#if _FFR_EAI 3069363466Sgshapiro if (!e->e_smtputf8 && !addr_is_ascii(a->q_paddr)) 3070363466Sgshapiro { 3071363466Sgshapiro usrerr("553 5.6.7 Address requires SMTPUTF8"); 3072363466Sgshapiro goto rcpt_done; 3073363466Sgshapiro } 3074363466Sgshapiro#endif 307542575Speter 307638032Speter if (delimptr != NULL && *delimptr != '\0') 307738032Speter *delimptr++ = '\0'; 307838032Speter 307964562Sgshapiro /* put resulting triple from parseaddr() into macros */ 308064562Sgshapiro if (a->q_mailer != NULL) 308190792Sgshapiro macdefine(&e->e_macro, A_PERM, 308290792Sgshapiro macid("{rcpt_mailer}"), 308390792Sgshapiro a->q_mailer->m_name); 308464562Sgshapiro else 308590792Sgshapiro macdefine(&e->e_macro, A_PERM, 308690792Sgshapiro macid("{rcpt_mailer}"), NULL); 308764562Sgshapiro if (a->q_host != NULL) 308890792Sgshapiro macdefine(&e->e_macro, A_PERM, 308990792Sgshapiro macid("{rcpt_host}"), a->q_host); 309064562Sgshapiro else 309190792Sgshapiro macdefine(&e->e_macro, A_PERM, 309290792Sgshapiro macid("{rcpt_host}"), "localhost"); 309364562Sgshapiro if (a->q_user != NULL) 309490792Sgshapiro macdefine(&e->e_macro, A_PERM, 309590792Sgshapiro macid("{rcpt_addr}"), a->q_user); 309664562Sgshapiro else 309790792Sgshapiro macdefine(&e->e_macro, A_PERM, 309890792Sgshapiro macid("{rcpt_addr}"), NULL); 309964562Sgshapiro if (Errors > 0) 310090792Sgshapiro goto rcpt_done; 310138032Speter 310238032Speter /* now parse ESMTP arguments */ 310364562Sgshapiro addr = p; 3104168515Sgshapiro parse_esmtp_args(e, a, p, delimptr, "RCPT", args, 3105168515Sgshapiro rcpt_esmtp_args); 310638032Speter if (Errors > 0) 310790792Sgshapiro goto rcpt_done; 310838032Speter 3109168515Sgshapiro#if MILTER 3110168515Sgshapiro /* 3111168515Sgshapiro ** rscheck() can trigger an "exception" 3112168515Sgshapiro ** in which case the execution continues at 3113168515Sgshapiro ** SM_EXCEPT(exc, "[!F]*") 3114168515Sgshapiro ** This means milter_cmd_safe is not set 3115168515Sgshapiro ** and hence milter is not invoked. 3116168515Sgshapiro ** Would it be "safe" to change that, i.e., use 3117168515Sgshapiro ** milter_cmd_safe = true; 3118168515Sgshapiro ** here so a milter is informed (if requested) 3119168515Sgshapiro ** about RCPTs that are rejected by check_rcpt? 3120168515Sgshapiro */ 3121168515Sgshapiro# if _FFR_MILTER_CHECK_REJECTIONS_TOO 3122168515Sgshapiro milter_cmd_safe = true; 3123168515Sgshapiro# endif 3124168515Sgshapiro#endif 3125168515Sgshapiro 312664562Sgshapiro /* do config file checking of the recipient */ 312790792Sgshapiro macdefine(&e->e_macro, A_PERM, 312890792Sgshapiro macid("{addr_type}"), "e r"); 312964562Sgshapiro if (rscheck("check_rcpt", addr, 3130102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 3, 3131285229Sgshapiro NULL, e->e_id, p_addr_st, NULL) != EX_OK || 313264562Sgshapiro Errors > 0) 313390792Sgshapiro goto rcpt_done; 313490792Sgshapiro macdefine(&e->e_macro, A_PERM, 313590792Sgshapiro macid("{addr_type}"), NULL); 313664562Sgshapiro 3137102528Sgshapiro /* If discarding, don't bother to verify user */ 3138102528Sgshapiro if (bitset(EF_DISCARD, e->e_flags)) 3139102528Sgshapiro a->q_state = QS_VERIFIED; 3140168515Sgshapiro#if MILTER 3141168515Sgshapiro milter_cmd_safe = true; 3142168515Sgshapiro#endif 3143102528Sgshapiro 3144285229Sgshapiro addbcc(a, e); 3145285229Sgshapiro rcptmods(a, e); 3146285229Sgshapiro 3147168515Sgshapiro /* save in recipient list after ESMTP mods */ 3148168515Sgshapiro a = recipient(a, &e->e_sendqueue, 0, e); 3149168515Sgshapiro /* may trigger exception... */ 3150168515Sgshapiro 315190792Sgshapiro#if MILTER 3152168515Sgshapiro milter_rcpt_added = true; 3153168515Sgshapiro#endif 3154168515Sgshapiro 3155168515Sgshapiro if(!(Errors > 0) && QS_IS_BADADDR(a->q_state)) 3156168515Sgshapiro { 3157168515Sgshapiro /* punt -- should keep message in ADDRESS.... */ 3158168515Sgshapiro usrerr("550 5.1.1 Addressee unknown"); 3159168515Sgshapiro } 3160168515Sgshapiro 3161168515Sgshapiro#if MILTER 3162168515Sgshapiro rcpt_done: 316390792Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 316490792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 316564562Sgshapiro { 316664562Sgshapiro char state; 316764562Sgshapiro char *response; 316864562Sgshapiro 3169168515Sgshapiro /* how to get the error codes? */ 3170168515Sgshapiro if (Errors > 0) 3171168515Sgshapiro { 3172168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3173168515Sgshapiro macid("{rcpt_mailer}"), 3174168515Sgshapiro "error"); 3175168515Sgshapiro if (a != NULL && 3176168515Sgshapiro a->q_status != NULL && 3177168515Sgshapiro a->q_rstatus != NULL) 3178168515Sgshapiro { 3179168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3180168515Sgshapiro macid("{rcpt_host}"), 3181168515Sgshapiro a->q_status); 3182168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3183168515Sgshapiro macid("{rcpt_addr}"), 3184168515Sgshapiro a->q_rstatus); 3185168515Sgshapiro } 3186168515Sgshapiro else 3187168515Sgshapiro { 3188168515Sgshapiro if (addr_st.q_host != NULL) 3189168515Sgshapiro macdefine(&e->e_macro, 3190168515Sgshapiro A_PERM, 3191168515Sgshapiro macid("{rcpt_host}"), 3192168515Sgshapiro addr_st.q_host); 3193168515Sgshapiro if (addr_st.q_user != NULL) 3194168515Sgshapiro macdefine(&e->e_macro, 3195168515Sgshapiro A_PERM, 3196168515Sgshapiro macid("{rcpt_addr}"), 3197168515Sgshapiro addr_st.q_user); 3198168515Sgshapiro } 3199168515Sgshapiro } 3200168515Sgshapiro 3201168515Sgshapiro response = milter_envrcpt(args, e, &state, 3202168515Sgshapiro Errors > 0); 3203168515Sgshapiro milter_cmd_done = true; 320490792Sgshapiro MILTER_REPLY("to"); 320564562Sgshapiro } 320690792Sgshapiro#endif /* MILTER */ 320764562Sgshapiro 320838032Speter /* no errors during parsing, but might be a duplicate */ 320938032Speter e->e_to = a->q_paddr; 3210168515Sgshapiro if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state)) 321138032Speter { 321290792Sgshapiro if (smtp.sm_nrcpts == 0) 321364562Sgshapiro initsys(e); 321464562Sgshapiro message("250 2.1.5 Recipient ok%s", 321564562Sgshapiro QS_IS_QUEUEUP(a->q_state) ? 321638032Speter " (will queue)" : ""); 321790792Sgshapiro smtp.sm_nrcpts++; 321838032Speter } 3219168515Sgshapiro 3220168515Sgshapiro /* Is this needed? */ 3221168515Sgshapiro#if !MILTER 3222168515Sgshapiro rcpt_done: 3223363466Sgshapiro#endif 3224363466Sgshapiro 3225168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3226168515Sgshapiro macid("{rcpt_mailer}"), NULL); 3227168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3228168515Sgshapiro macid("{rcpt_host}"), NULL); 3229168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3230168515Sgshapiro macid("{rcpt_addr}"), NULL); 3231168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3232168515Sgshapiro macid("{dsn_notify}"), NULL); 3233168515Sgshapiro 323490792Sgshapiro if (Errors > 0) 3235132943Sgshapiro { 323690792Sgshapiro ++n_badrcpts; 3237132943Sgshapiro NBADRCPTS; 3238132943Sgshapiro } 323990792Sgshapiro } 324090792Sgshapiro SM_EXCEPT(exc, "[!F]*") 324190792Sgshapiro { 324290792Sgshapiro /* An exception occurred while processing RCPT */ 324390792Sgshapiro e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY); 324490792Sgshapiro ++n_badrcpts; 3245132943Sgshapiro NBADRCPTS; 3246168515Sgshapiro#if MILTER 3247168515Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 3248168515Sgshapiro !bitset(EF_DISCARD, e->e_flags) && 3249168515Sgshapiro !milter_cmd_done && milter_cmd_safe) 3250168515Sgshapiro { 3251168515Sgshapiro char state; 3252168515Sgshapiro char *response; 3253168515Sgshapiro 3254168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3255168515Sgshapiro macid("{rcpt_mailer}"), "error"); 3256168515Sgshapiro 3257168515Sgshapiro /* how to get the error codes? */ 3258168515Sgshapiro if (addr_st.q_host != NULL) 3259168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3260168515Sgshapiro macid("{rcpt_host}"), 3261168515Sgshapiro addr_st.q_host); 3262168515Sgshapiro else if (a != NULL && a->q_status != NULL) 3263168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3264168515Sgshapiro macid("{rcpt_host}"), 3265168515Sgshapiro a->q_status); 3266168515Sgshapiro 3267168515Sgshapiro if (addr_st.q_user != NULL) 3268168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3269168515Sgshapiro macid("{rcpt_addr}"), 3270168515Sgshapiro addr_st.q_user); 3271168515Sgshapiro else if (a != NULL && a->q_rstatus != NULL) 3272168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3273168515Sgshapiro macid("{rcpt_addr}"), 3274168515Sgshapiro a->q_rstatus); 3275168515Sgshapiro 3276168515Sgshapiro response = milter_envrcpt(args, e, &state, 3277168515Sgshapiro true); 3278168515Sgshapiro milter_cmd_done = true; 3279168515Sgshapiro MILTER_REPLY("to"); 3280168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3281168515Sgshapiro macid("{rcpt_mailer}"), NULL); 3282168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3283168515Sgshapiro macid("{rcpt_host}"), NULL); 3284168515Sgshapiro macdefine(&e->e_macro, A_PERM, 3285168515Sgshapiro macid("{rcpt_addr}"), NULL); 3286168515Sgshapiro } 3287168515Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 3288168515Sgshapiro milter_rcpt_added && milter_cmd_done && 3289168515Sgshapiro milter_cmd_fail) 3290168515Sgshapiro { 3291168515Sgshapiro (void) removefromlist(addr, &e->e_sendqueue, e); 3292168515Sgshapiro milter_cmd_fail = false; 3293173340Sgshapiro if (smtp.sm_e_nrcpts_orig < e->e_nrcpts) 3294173340Sgshapiro e->e_nrcpts = smtp.sm_e_nrcpts_orig; 3295168515Sgshapiro } 3296168515Sgshapiro#endif /* MILTER */ 329790792Sgshapiro } 329890792Sgshapiro SM_END_TRY 329938032Speter break; 330038032Speter 330138032Speter case CMDDATA: /* data -- text of mail */ 330290792Sgshapiro DELAY_CONN("DATA"); 3303132943Sgshapiro if (!smtp_data(&smtp, e)) 3304132943Sgshapiro goto doquit; 330538032Speter break; 330638032Speter 330738032Speter case CMDRSET: /* rset -- reset state */ 330838032Speter if (tTd(94, 100)) 330964562Sgshapiro message("451 4.0.0 Test failure"); 331038032Speter else 331164562Sgshapiro message("250 2.0.0 Reset state"); 331290792Sgshapiro CLEAR_STATE(cmdbuf); 331338032Speter break; 331438032Speter 331538032Speter case CMDVRFY: /* vrfy -- verify address */ 331638032Speter case CMDEXPN: /* expn -- expand address */ 331790792Sgshapiro vrfy = c->cmd_code == CMDVRFY; 331890792Sgshapiro DELAY_CONN(vrfy ? "VRFY" : "EXPN"); 331964562Sgshapiro if (tempfail) 332064562Sgshapiro { 332164562Sgshapiro if (LogLevel > 9) 332264562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3323110560Sgshapiro "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)", 332490792Sgshapiro vrfy ? "VRFY" : "EXPN", 332564562Sgshapiro p, CurSmtpClient); 332690792Sgshapiro 332790792Sgshapiro /* RFC 821 doesn't allow 4xy reply code */ 332864562Sgshapiro usrerr("550 5.7.1 Please try again later"); 332964562Sgshapiro break; 333064562Sgshapiro } 333190792Sgshapiro wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS, 333290792Sgshapiro false, vrfy ? "VRFY" : "EXPN", e); 3333132943Sgshapiro STOP_IF_ATTACK(wt); 333464562Sgshapiro previous = curtime(); 3335110560Sgshapiro if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) || 3336110560Sgshapiro (!vrfy && !bitset(SRV_OFFER_EXPN, features))) 333738032Speter { 333838032Speter if (vrfy) 333964562Sgshapiro message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 334038032Speter else 334164562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 334238032Speter if (LogLevel > 5) 334338032Speter sm_syslog(LOG_INFO, e->e_id, 3344110560Sgshapiro "%s: %s [rejected]", 334564562Sgshapiro CurSmtpClient, 334664562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 334738032Speter break; 334838032Speter } 334938032Speter else if (!gothello && 335038032Speter bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 335138032Speter PrivacyFlags)) 335238032Speter { 335364562Sgshapiro usrerr("503 5.0.0 I demand that you introduce yourself first"); 335438032Speter break; 335538032Speter } 335690792Sgshapiro if (Errors > 0) 335738032Speter break; 335838032Speter if (LogLevel > 5) 3359110560Sgshapiro sm_syslog(LOG_INFO, e->e_id, "%s: %s", 336064562Sgshapiro CurSmtpClient, 336164562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 336290792Sgshapiro SM_TRY 336390792Sgshapiro { 336490792Sgshapiro QuickAbort = true; 336538032Speter vrfyqueue = NULL; 336638032Speter if (vrfy) 336738032Speter e->e_flags |= EF_VRFYONLY; 3368363466Sgshapiro while (*p != '\0' && SM_ISSPACE(*p)) 336938032Speter p++; 337038032Speter if (*p == '\0') 337138032Speter { 337264562Sgshapiro usrerr("501 5.5.2 Argument required"); 337338032Speter } 337438032Speter else 337538032Speter { 337664562Sgshapiro /* do config file checking of the address */ 337764562Sgshapiro if (rscheck(vrfy ? "check_vrfy" : "check_expn", 3378285229Sgshapiro p, NULL, e, RSF_RMCOMM, 3, NULL, 3379285229Sgshapiro NOQID, NULL, NULL) != EX_OK || 338090792Sgshapiro Errors > 0) 338190792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 338238032Speter (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 338338032Speter } 338464562Sgshapiro if (wt > 0) 338571345Sgshapiro { 338671345Sgshapiro time_t t; 338771345Sgshapiro 338871345Sgshapiro t = wt - (curtime() - previous); 338971345Sgshapiro if (t > 0) 339071345Sgshapiro (void) sleep(t); 339171345Sgshapiro } 339238032Speter if (Errors > 0) 339390792Sgshapiro sm_exc_raisenew_x(&EtypeQuickAbort, 1); 339438032Speter if (vrfyqueue == NULL) 339538032Speter { 339664562Sgshapiro usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 339738032Speter } 339838032Speter while (vrfyqueue != NULL) 339938032Speter { 340064562Sgshapiro if (!QS_IS_UNDELIVERED(vrfyqueue->q_state)) 340164562Sgshapiro { 340264562Sgshapiro vrfyqueue = vrfyqueue->q_next; 340364562Sgshapiro continue; 340464562Sgshapiro } 340538032Speter 340664562Sgshapiro /* see if there is more in the vrfy list */ 340738032Speter a = vrfyqueue; 340838032Speter while ((a = a->q_next) != NULL && 340966494Sgshapiro (!QS_IS_UNDELIVERED(a->q_state))) 341038032Speter continue; 341164562Sgshapiro printvrfyaddr(vrfyqueue, a == NULL, vrfy); 341264562Sgshapiro vrfyqueue = a; 341338032Speter } 341490792Sgshapiro } 341590792Sgshapiro SM_EXCEPT(exc, "[!F]*") 341690792Sgshapiro { 341790792Sgshapiro /* 341890792Sgshapiro ** An exception occurred while processing VRFY/EXPN 341990792Sgshapiro */ 342090792Sgshapiro 342190792Sgshapiro sm_exc_free(exc); 342290792Sgshapiro goto undo; 342390792Sgshapiro } 342490792Sgshapiro SM_END_TRY 342538032Speter break; 342638032Speter 342738032Speter case CMDETRN: /* etrn -- force queue flush */ 342890792Sgshapiro DELAY_CONN("ETRN"); 342990792Sgshapiro 343090792Sgshapiro /* Don't leak queue information via debug flags */ 343190792Sgshapiro if (!bitset(SRV_OFFER_ETRN, features) || UseMSP || 343290792Sgshapiro (RealUid != 0 && RealUid != TrustedUid && 343390792Sgshapiro OpMode == MD_SMTP)) 343438032Speter { 343564562Sgshapiro /* different message for MSA ? */ 343664562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 343738032Speter if (LogLevel > 5) 343838032Speter sm_syslog(LOG_INFO, e->e_id, 3439110560Sgshapiro "%s: %s [rejected]", 344064562Sgshapiro CurSmtpClient, 344164562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 344238032Speter break; 344338032Speter } 344464562Sgshapiro if (tempfail) 344564562Sgshapiro { 344664562Sgshapiro if (LogLevel > 9) 344764562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3448110560Sgshapiro "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)", 344964562Sgshapiro p, CurSmtpClient); 3450363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 345190792Sgshapiro usrerr(MSG_TEMPFAIL); 345264562Sgshapiro break; 345364562Sgshapiro } 345438032Speter 345538032Speter if (strlen(p) <= 0) 345638032Speter { 345764562Sgshapiro usrerr("500 5.5.2 Parameter required"); 345838032Speter break; 345938032Speter } 346038032Speter 346138032Speter /* crude way to avoid denial-of-service attacks */ 3462132943Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS, 3463132943Sgshapiro true, "ETRN", e)); 346438032Speter 346590792Sgshapiro /* 346690792Sgshapiro ** Do config file checking of the parameter. 346790792Sgshapiro ** Even though we have srv_features now, we still 346890792Sgshapiro ** need this ruleset because the former is called 346990792Sgshapiro ** when the connection has been established, while 347090792Sgshapiro ** this ruleset is called when the command is 347190792Sgshapiro ** actually issued and therefore has all information 347290792Sgshapiro ** available to make a decision. 347390792Sgshapiro */ 347490792Sgshapiro 3475285229Sgshapiro if (rscheck("check_etrn", p, NULL, e, RSF_RMCOMM, 3, 3476285229Sgshapiro NULL, NOQID, NULL, NULL) != EX_OK || 3477102528Sgshapiro Errors > 0) 347864562Sgshapiro break; 347964562Sgshapiro 348038032Speter if (LogLevel > 5) 348138032Speter sm_syslog(LOG_INFO, e->e_id, 3482110560Sgshapiro "%s: ETRN %s", CurSmtpClient, 348364562Sgshapiro shortenstring(p, MAXSHORTSTR)); 348438032Speter 348538032Speter id = p; 348690792Sgshapiro if (*id == '#') 348790792Sgshapiro { 3488111823Sgshapiro int i, qgrp; 348990792Sgshapiro 349090792Sgshapiro id++; 3491111823Sgshapiro qgrp = name2qid(id); 3492111823Sgshapiro if (!ISVALIDQGRP(qgrp)) 349390792Sgshapiro { 349490792Sgshapiro usrerr("459 4.5.4 Queue %s unknown", 349590792Sgshapiro id); 349690792Sgshapiro break; 349790792Sgshapiro } 3498111823Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; 3499111823Sgshapiro i++) 3500111823Sgshapiro Queue[i]->qg_nextrun = (time_t) -1; 3501111823Sgshapiro Queue[qgrp]->qg_nextrun = 0; 3502111823Sgshapiro ok = run_work_group(Queue[qgrp]->qg_wgrp, 3503111823Sgshapiro RWG_FORK|RWG_FORCE); 350490792Sgshapiro if (ok && Errors == 0) 350590792Sgshapiro message("250 2.0.0 Queuing for queue group %s started", id); 350690792Sgshapiro break; 350790792Sgshapiro } 350890792Sgshapiro 350938032Speter if (*id == '@') 351038032Speter id++; 351138032Speter else 351238032Speter *--id = '@'; 351364562Sgshapiro 351490792Sgshapiro new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR)); 351590792Sgshapiro if (new == NULL) 351690792Sgshapiro { 351790792Sgshapiro syserr("500 5.5.0 ETRN out of memory"); 351890792Sgshapiro break; 351990792Sgshapiro } 352038032Speter new->queue_match = id; 352190792Sgshapiro new->queue_negate = false; 352238032Speter new->queue_next = NULL; 352338032Speter QueueLimitRecipient = new; 352490792Sgshapiro ok = runqueue(true, false, false, true); 352590792Sgshapiro sm_free(QueueLimitRecipient); /* XXX */ 352638032Speter QueueLimitRecipient = NULL; 352738032Speter if (ok && Errors == 0) 352864562Sgshapiro message("250 2.0.0 Queuing for node %s started", p); 352938032Speter break; 353038032Speter 353138032Speter case CMDHELP: /* help -- give user info */ 353290792Sgshapiro DELAY_CONN("HELP"); 353364562Sgshapiro help(p, e); 353438032Speter break; 353538032Speter 353638032Speter case CMDNOOP: /* noop -- do nothing */ 353790792Sgshapiro DELAY_CONN("NOOP"); 3538157001Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands, 3539132943Sgshapiro true, "NOOP", e)); 354064562Sgshapiro message("250 2.0.0 OK"); 354138032Speter break; 354238032Speter 354338032Speter case CMDQUIT: /* quit -- leave mail */ 354464562Sgshapiro message("221 2.0.0 %s closing connection", MyHostName); 354590792Sgshapiro#if PIPELINING 354690792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 3547363466Sgshapiro#endif 354838032Speter 354990792Sgshapiro if (smtp.sm_nrcpts > 0) 355090792Sgshapiro logundelrcpts(e, "aborted by sender", 9, false); 355190792Sgshapiro 355238032Speter /* arrange to ignore any current send list */ 355338032Speter e->e_sendqueue = NULL; 355438032Speter 355590792Sgshapiro#if STARTTLS 355664562Sgshapiro /* shutdown TLS connection */ 355764562Sgshapiro if (tls_active) 355864562Sgshapiro { 3559363466Sgshapiro (void) endtls(&srv_ssl, "server"); 356090792Sgshapiro tls_active = false; 356164562Sgshapiro } 356290792Sgshapiro#endif /* STARTTLS */ 356390792Sgshapiro#if SASL 356464562Sgshapiro if (authenticating == SASL_IS_AUTH) 356564562Sgshapiro { 356664562Sgshapiro sasl_dispose(&conn); 356764562Sgshapiro authenticating = SASL_NOT_AUTH; 356890792Sgshapiro /* XXX sasl_done(); this is a child */ 356964562Sgshapiro } 357090792Sgshapiro#endif /* SASL */ 357164562Sgshapiro 357264562Sgshapirodoquit: 357338032Speter /* avoid future 050 messages */ 357438032Speter disconnect(1, e); 357538032Speter 357690792Sgshapiro#if MILTER 357764562Sgshapiro /* close out milter filters */ 357864562Sgshapiro milter_quit(e); 3579363466Sgshapiro#endif 358064562Sgshapiro 3581203004Sgshapiro if (tTd(92, 2)) 3582203004Sgshapiro sm_dprintf("QUIT: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n", 3583203004Sgshapiro e->e_id, 3584203004Sgshapiro bitset(EF_LOGSENDER, e->e_flags), 3585203004Sgshapiro LogLevel); 358664562Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 358764562Sgshapiro logsender(e, NULL); 358864562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 358964562Sgshapiro 359098121Sgshapiro if (lognullconnection && LogLevel > 5 && 359198121Sgshapiro nullserver == NULL) 359264562Sgshapiro { 359364562Sgshapiro char *d; 359464562Sgshapiro 359590792Sgshapiro d = macvalue(macid("{daemon_name}"), e); 359664562Sgshapiro if (d == NULL) 359764562Sgshapiro d = "stdin"; 359890792Sgshapiro 359990792Sgshapiro /* 360090792Sgshapiro ** even though this id is "bogus", it makes 360190792Sgshapiro ** it simpler to "grep" related events, e.g., 360290792Sgshapiro ** timeouts for the same connection. 360390792Sgshapiro */ 360490792Sgshapiro 360590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3606110560Sgshapiro "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s", 360764562Sgshapiro CurSmtpClient, d); 360864562Sgshapiro } 3609110560Sgshapiro if (tTd(93, 100)) 3610110560Sgshapiro { 3611110560Sgshapiro /* return to handle next connection */ 3612110560Sgshapiro return; 3613110560Sgshapiro } 361490792Sgshapiro finis(true, true, ExitStat); 361564562Sgshapiro /* NOTREACHED */ 361638032Speter 3617157001Sgshapiro /* just to avoid bogus warning from some compilers */ 3618157001Sgshapiro exit(EX_OSERR); 3619157001Sgshapiro 362038032Speter case CMDVERB: /* set verbose mode */ 362190792Sgshapiro DELAY_CONN("VERB"); 3622110560Sgshapiro if (!bitset(SRV_OFFER_EXPN, features) || 3623110560Sgshapiro !bitset(SRV_OFFER_VERB, features)) 362438032Speter { 362538032Speter /* this would give out the same info */ 362664562Sgshapiro message("502 5.7.0 Verbose unavailable"); 362738032Speter break; 362838032Speter } 3629157001Sgshapiro STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands, 3630132943Sgshapiro true, "VERB", e)); 363138032Speter Verbose = 1; 363264562Sgshapiro set_delivery_mode(SM_DELIVER, e); 363364562Sgshapiro message("250 2.0.0 Verbose mode"); 363438032Speter break; 363538032Speter 363690792Sgshapiro#if SMTPDEBUG 363738032Speter case CMDDBGQSHOW: /* show queues */ 363890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 363990792Sgshapiro "Send Queue="); 3640132943Sgshapiro printaddr(smioout, e->e_sendqueue, true); 364138032Speter break; 364238032Speter 364338032Speter case CMDDBGDEBUG: /* set debug mode */ 3644168515Sgshapiro tTsetup(tTdvect, sizeof(tTdvect), "0-99.1"); 364538032Speter tTflag(p); 364664562Sgshapiro message("200 2.0.0 Debug set"); 364738032Speter break; 364838032Speter 364990792Sgshapiro#else /* SMTPDEBUG */ 365038032Speter case CMDDBGQSHOW: /* show queues */ 365138032Speter case CMDDBGDEBUG: /* set debug mode */ 365290792Sgshapiro#endif /* SMTPDEBUG */ 365338032Speter case CMDLOGBOGUS: /* bogus command */ 365490792Sgshapiro DELAY_CONN("Bogus"); 365538032Speter if (LogLevel > 0) 365638032Speter sm_syslog(LOG_CRIT, e->e_id, 3657110560Sgshapiro "\"%s\" command from %s (%.100s)", 365864562Sgshapiro c->cmd_name, CurSmtpClient, 365964562Sgshapiro anynet_ntoa(&RealHostAddr)); 366064562Sgshapiro /* FALLTHROUGH */ 366138032Speter 366238032Speter case CMDERROR: /* unknown command */ 366390792Sgshapiro#if MAXBADCOMMANDS > 0 366490792Sgshapiro if (++n_badcmds > MAXBADCOMMANDS) 366538032Speter { 3666132943Sgshapiro stopattack: 366764562Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 366838032Speter MyHostName); 366964562Sgshapiro 367064562Sgshapiro /* arrange to ignore any current send list */ 367164562Sgshapiro e->e_sendqueue = NULL; 367238032Speter goto doquit; 367338032Speter } 367490792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */ 367538032Speter 3676132943Sgshapiro#if MILTER && SMFI_VERSION > 2 3677132943Sgshapiro if (smtp.sm_milterlist && smtp.sm_milterize && 3678132943Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 3679132943Sgshapiro { 3680132943Sgshapiro char state; 3681132943Sgshapiro char *response; 3682132943Sgshapiro 3683132943Sgshapiro if (MilterLogLevel > 9) 3684132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3685132943Sgshapiro "Sending \"%s\" to Milter", inp); 3686132943Sgshapiro response = milter_unknown(inp, e, &state); 3687132943Sgshapiro MILTER_REPLY("unknown"); 3688132943Sgshapiro if (state == SMFIR_REPLYCODE || 3689132943Sgshapiro state == SMFIR_REJECT || 3690157001Sgshapiro state == SMFIR_TEMPFAIL || 3691157001Sgshapiro state == SMFIR_SHUTDOWN) 3692132943Sgshapiro { 3693132943Sgshapiro /* MILTER_REPLY already gave an error */ 3694132943Sgshapiro break; 3695132943Sgshapiro } 3696132943Sgshapiro } 3697132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 2 */ 3698132943Sgshapiro 369964562Sgshapiro usrerr("500 5.5.1 Command unrecognized: \"%s\"", 370064562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 370138032Speter break; 370238032Speter 370364562Sgshapiro case CMDUNIMPL: 370490792Sgshapiro DELAY_CONN("Unimpl"); 370564562Sgshapiro usrerr("502 5.5.1 Command not implemented: \"%s\"", 370664562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 370764562Sgshapiro break; 370864562Sgshapiro 370938032Speter default: 371090792Sgshapiro DELAY_CONN("default"); 371138032Speter errno = 0; 371264562Sgshapiro syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code); 371338032Speter break; 371438032Speter } 371590792Sgshapiro#if SASL 371664562Sgshapiro } 3717363466Sgshapiro#endif 371890792Sgshapiro } 371990792Sgshapiro SM_EXCEPT(exc, "[!F]*") 372090792Sgshapiro { 372190792Sgshapiro /* 372290792Sgshapiro ** The only possible exception is "E:mta.quickabort". 372390792Sgshapiro ** There is nothing to do except fall through and loop. 372490792Sgshapiro */ 372590792Sgshapiro } 372690792Sgshapiro SM_END_TRY 372738032Speter } 372890792Sgshapiro} 372990792Sgshapiro/* 373090792Sgshapiro** SMTP_DATA -- implement the SMTP DATA command. 373190792Sgshapiro** 373290792Sgshapiro** Parameters: 373390792Sgshapiro** smtp -- status of SMTP connection. 373490792Sgshapiro** e -- envelope. 373590792Sgshapiro** 373690792Sgshapiro** Returns: 3737132943Sgshapiro** true iff SMTP session can continue. 373890792Sgshapiro** 373990792Sgshapiro** Side Effects: 374090792Sgshapiro** possibly sends message. 374190792Sgshapiro*/ 374264562Sgshapiro 3743132943Sgshapirostatic bool 374490792Sgshapirosmtp_data(smtp, e) 374590792Sgshapiro SMTP_T *smtp; 374690792Sgshapiro ENVELOPE *e; 374790792Sgshapiro{ 374890792Sgshapiro#if MILTER 374990792Sgshapiro bool milteraccept; 3750363466Sgshapiro#endif 375190792Sgshapiro bool aborting; 375290792Sgshapiro bool doublequeue; 3753168515Sgshapiro bool rv = true; 375490792Sgshapiro ADDRESS *a; 375590792Sgshapiro ENVELOPE *ee; 375690792Sgshapiro char *id; 375798121Sgshapiro char *oldid; 3758168515Sgshapiro unsigned int features; 375990792Sgshapiro char buf[32]; 376090792Sgshapiro 376190792Sgshapiro SmtpPhase = "server DATA"; 376290792Sgshapiro if (!smtp->sm_gotmail) 376390792Sgshapiro { 376490792Sgshapiro usrerr("503 5.0.0 Need MAIL command"); 3765132943Sgshapiro return true; 376690792Sgshapiro } 376790792Sgshapiro else if (smtp->sm_nrcpts <= 0) 376890792Sgshapiro { 376990792Sgshapiro usrerr("503 5.0.0 Need RCPT (recipient)"); 3770132943Sgshapiro return true; 377190792Sgshapiro } 3772168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts); 377390792Sgshapiro if (rscheck("check_data", buf, NULL, e, 3774102528Sgshapiro RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL, 3775285229Sgshapiro e->e_id, NULL, NULL) != EX_OK) 3776132943Sgshapiro return true; 377790792Sgshapiro 3778132943Sgshapiro#if MILTER && SMFI_VERSION > 3 3779132943Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize && 3780132943Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 3781132943Sgshapiro { 3782132943Sgshapiro char state; 3783132943Sgshapiro char *response; 3784132943Sgshapiro int savelogusrerrs = LogUsrErrs; 3785132943Sgshapiro 3786132943Sgshapiro response = milter_data_cmd(e, &state); 3787132943Sgshapiro switch (state) 3788132943Sgshapiro { 3789132943Sgshapiro case SMFIR_REPLYCODE: 3790132943Sgshapiro if (MilterLogLevel > 3) 3791132943Sgshapiro { 3792132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3793132943Sgshapiro "Milter: cmd=data, reject=%s", 3794132943Sgshapiro response); 3795132943Sgshapiro LogUsrErrs = false; 3796132943Sgshapiro } 3797203004Sgshapiro#if _FFR_MILTER_ENHSC 3798203004Sgshapiro if (ISSMTPCODE(response)) 3799203004Sgshapiro (void) extenhsc(response + 4, ' ', e->e_enhsc); 3800363466Sgshapiro#endif 3801203004Sgshapiro 3802363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 3803132943Sgshapiro usrerr(response); 3804157001Sgshapiro if (strncmp(response, "421 ", 4) == 0 3805157001Sgshapiro || strncmp(response, "421-", 4) == 0) 3806132943Sgshapiro { 3807132943Sgshapiro e->e_sendqueue = NULL; 3808132943Sgshapiro return false; 3809132943Sgshapiro } 3810132943Sgshapiro return true; 3811132943Sgshapiro 3812132943Sgshapiro case SMFIR_REJECT: 3813132943Sgshapiro if (MilterLogLevel > 3) 3814132943Sgshapiro { 3815132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3816132943Sgshapiro "Milter: cmd=data, reject=550 5.7.1 Command rejected"); 3817132943Sgshapiro LogUsrErrs = false; 3818132943Sgshapiro } 3819203004Sgshapiro#if _FFR_MILTER_ENHSC 3820203004Sgshapiro (void) sm_strlcpy(e->e_enhsc, "5.7.1", 3821203004Sgshapiro sizeof(e->e_enhsc)); 3822363466Sgshapiro#endif 3823132943Sgshapiro usrerr("550 5.7.1 Command rejected"); 3824132943Sgshapiro return true; 3825132943Sgshapiro 3826132943Sgshapiro case SMFIR_DISCARD: 3827132943Sgshapiro if (MilterLogLevel > 3) 3828132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3829132943Sgshapiro "Milter: cmd=data, discard"); 3830132943Sgshapiro e->e_flags |= EF_DISCARD; 3831132943Sgshapiro break; 3832132943Sgshapiro 3833132943Sgshapiro case SMFIR_TEMPFAIL: 3834132943Sgshapiro if (MilterLogLevel > 3) 3835132943Sgshapiro { 3836132943Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3837132943Sgshapiro "Milter: cmd=data, reject=%s", 3838132943Sgshapiro MSG_TEMPFAIL); 3839132943Sgshapiro LogUsrErrs = false; 3840132943Sgshapiro } 3841203004Sgshapiro#if _FFR_MILTER_ENHSC 3842203004Sgshapiro (void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc); 3843363466Sgshapiro#endif 3844363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 3845132943Sgshapiro usrerr(MSG_TEMPFAIL); 3846132943Sgshapiro return true; 3847157001Sgshapiro 3848157001Sgshapiro case SMFIR_SHUTDOWN: 3849157001Sgshapiro if (MilterLogLevel > 3) 3850157001Sgshapiro { 3851157001Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3852157001Sgshapiro "Milter: cmd=data, reject=421 4.7.0 %s closing connection", 3853157001Sgshapiro MyHostName); 3854157001Sgshapiro LogUsrErrs = false; 3855157001Sgshapiro } 3856157001Sgshapiro usrerr("421 4.7.0 %s closing connection", MyHostName); 3857157001Sgshapiro e->e_sendqueue = NULL; 3858157001Sgshapiro return false; 3859132943Sgshapiro } 3860132943Sgshapiro LogUsrErrs = savelogusrerrs; 3861132943Sgshapiro if (response != NULL) 3862132943Sgshapiro sm_free(response); /* XXX */ 3863132943Sgshapiro } 3864132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 3 */ 3865132943Sgshapiro 386690792Sgshapiro /* put back discard bit */ 386790792Sgshapiro if (smtp->sm_discard) 386890792Sgshapiro e->e_flags |= EF_DISCARD; 386990792Sgshapiro 387090792Sgshapiro /* check to see if we need to re-expand aliases */ 387190792Sgshapiro /* also reset QS_BADADDR on already-diagnosted addrs */ 387290792Sgshapiro doublequeue = false; 387390792Sgshapiro for (a = e->e_sendqueue; a != NULL; a = a->q_next) 387490792Sgshapiro { 387590792Sgshapiro if (QS_IS_VERIFIED(a->q_state) && 387690792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 387790792Sgshapiro { 387890792Sgshapiro /* need to re-expand aliases */ 387990792Sgshapiro doublequeue = true; 388090792Sgshapiro } 388190792Sgshapiro if (QS_IS_BADADDR(a->q_state)) 388290792Sgshapiro { 388390792Sgshapiro /* make this "go away" */ 388490792Sgshapiro a->q_state = QS_DONTSEND; 388590792Sgshapiro } 388690792Sgshapiro } 388790792Sgshapiro 388890792Sgshapiro /* collect the text of the message */ 388990792Sgshapiro SmtpPhase = "collect"; 389090792Sgshapiro buffer_errors(); 389190792Sgshapiro 3892120256Sgshapiro collect(InChannel, true, NULL, e, true); 389390792Sgshapiro 389490792Sgshapiro /* redefine message size */ 3895244833Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize)); 389690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); 389790792Sgshapiro 389890792Sgshapiro /* rscheck() will set Errors or EF_DISCARD if it trips */ 3899102528Sgshapiro (void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT, 3900285229Sgshapiro 3, NULL, e->e_id, NULL, NULL); 390190792Sgshapiro 390290792Sgshapiro#if MILTER 390390792Sgshapiro milteraccept = true; 390490792Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize && 390590792Sgshapiro Errors <= 0 && 390690792Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 390790792Sgshapiro { 390890792Sgshapiro char state; 390990792Sgshapiro char *response; 391090792Sgshapiro 391190792Sgshapiro response = milter_data(e, &state); 391290792Sgshapiro switch (state) 391390792Sgshapiro { 391490792Sgshapiro case SMFIR_REPLYCODE: 391590792Sgshapiro if (MilterLogLevel > 3) 391690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 391790792Sgshapiro "Milter: data, reject=%s", 391890792Sgshapiro response); 391990792Sgshapiro milteraccept = false; 3920203004Sgshapiro#if _FFR_MILTER_ENHSC 3921203004Sgshapiro if (ISSMTPCODE(response)) 3922203004Sgshapiro (void) extenhsc(response + 4, ' ', e->e_enhsc); 3923363466Sgshapiro#endif 3924363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 392590792Sgshapiro usrerr(response); 3926203004Sgshapiro if (strncmp(response, "421 ", 4) == 0 3927203004Sgshapiro || strncmp(response, "421-", 4) == 0) 3928203004Sgshapiro rv = false; 392990792Sgshapiro break; 393090792Sgshapiro 393190792Sgshapiro case SMFIR_REJECT: 393290792Sgshapiro milteraccept = false; 393390792Sgshapiro if (MilterLogLevel > 3) 393490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 393590792Sgshapiro "Milter: data, reject=554 5.7.1 Command rejected"); 393690792Sgshapiro usrerr("554 5.7.1 Command rejected"); 393790792Sgshapiro break; 393890792Sgshapiro 393990792Sgshapiro case SMFIR_DISCARD: 394090792Sgshapiro if (MilterLogLevel > 3) 394190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 394290792Sgshapiro "Milter: data, discard"); 394390792Sgshapiro milteraccept = false; 394490792Sgshapiro e->e_flags |= EF_DISCARD; 394590792Sgshapiro break; 394690792Sgshapiro 394790792Sgshapiro case SMFIR_TEMPFAIL: 394890792Sgshapiro if (MilterLogLevel > 3) 394990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 395090792Sgshapiro "Milter: data, reject=%s", 395190792Sgshapiro MSG_TEMPFAIL); 395290792Sgshapiro milteraccept = false; 3953203004Sgshapiro#if _FFR_MILTER_ENHSC 3954203004Sgshapiro (void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc); 3955363466Sgshapiro#endif 3956363466Sgshapiro /* Can't use ("%s", ...) due to usrerr() requirements */ 395790792Sgshapiro usrerr(MSG_TEMPFAIL); 395890792Sgshapiro break; 3959157001Sgshapiro 3960157001Sgshapiro case SMFIR_SHUTDOWN: 3961157001Sgshapiro if (MilterLogLevel > 3) 3962157001Sgshapiro sm_syslog(LOG_INFO, e->e_id, 3963157001Sgshapiro "Milter: data, reject=421 4.7.0 %s closing connection", 3964157001Sgshapiro MyHostName); 3965157001Sgshapiro milteraccept = false; 3966157001Sgshapiro usrerr("421 4.7.0 %s closing connection", MyHostName); 3967157001Sgshapiro rv = false; 3968157001Sgshapiro break; 396990792Sgshapiro } 397090792Sgshapiro if (response != NULL) 397190792Sgshapiro sm_free(response); 397290792Sgshapiro } 397390792Sgshapiro 397490792Sgshapiro /* Milter may have changed message size */ 3975244833Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize)); 397690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf); 397790792Sgshapiro 397890792Sgshapiro /* abort message filters that didn't get the body & log msg is OK */ 397990792Sgshapiro if (smtp->sm_milterlist && smtp->sm_milterize) 398090792Sgshapiro { 398190792Sgshapiro milter_abort(e); 398290792Sgshapiro if (milteraccept && MilterLogLevel > 9) 398390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "Milter accept: message"); 398490792Sgshapiro } 3985132943Sgshapiro 3986132943Sgshapiro /* 3987132943Sgshapiro ** If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or 3988132943Sgshapiro ** milter accepted message, sync it now 3989132943Sgshapiro ** 3990132943Sgshapiro ** XXX This is almost a copy of the code in collect(): put it into 3991132943Sgshapiro ** a function that is called from both places? 3992132943Sgshapiro */ 3993132943Sgshapiro 3994132943Sgshapiro if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER) 3995132943Sgshapiro { 3996132943Sgshapiro int afd; 3997132943Sgshapiro SM_FILE_T *volatile df; 3998132943Sgshapiro char *dfname; 3999132943Sgshapiro 4000132943Sgshapiro df = e->e_dfp; 4001132943Sgshapiro dfname = queuename(e, DATAFL_LETTER); 4002132943Sgshapiro if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0 4003132943Sgshapiro && errno != EINVAL) 4004132943Sgshapiro { 4005132943Sgshapiro int save_errno; 4006132943Sgshapiro 4007132943Sgshapiro save_errno = errno; 4008132943Sgshapiro if (save_errno == EEXIST) 4009132943Sgshapiro { 4010132943Sgshapiro struct stat st; 4011132943Sgshapiro int dfd; 4012132943Sgshapiro 4013132943Sgshapiro if (stat(dfname, &st) < 0) 4014132943Sgshapiro st.st_size = -1; 4015132943Sgshapiro errno = EEXIST; 4016132943Sgshapiro syserr("@collect: bfcommit(%s): already on disk, size=%ld", 4017132943Sgshapiro dfname, (long) st.st_size); 4018132943Sgshapiro dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL); 4019132943Sgshapiro if (dfd >= 0) 4020132943Sgshapiro dumpfd(dfd, true, true); 4021132943Sgshapiro } 4022132943Sgshapiro errno = save_errno; 4023132943Sgshapiro dferror(df, "bfcommit", e); 4024132943Sgshapiro flush_errors(true); 4025132943Sgshapiro finis(save_errno != EEXIST, true, ExitStat); 4026132943Sgshapiro } 4027132943Sgshapiro else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0) 4028132943Sgshapiro { 4029132943Sgshapiro dferror(df, "sm_io_getinfo", e); 4030132943Sgshapiro flush_errors(true); 4031132943Sgshapiro finis(true, true, ExitStat); 4032132943Sgshapiro /* NOTREACHED */ 4033132943Sgshapiro } 4034132943Sgshapiro else if (fsync(afd) < 0) 4035132943Sgshapiro { 4036132943Sgshapiro dferror(df, "fsync", e); 4037132943Sgshapiro flush_errors(true); 4038132943Sgshapiro finis(true, true, ExitStat); 4039132943Sgshapiro /* NOTREACHED */ 4040132943Sgshapiro } 4041132943Sgshapiro else if (sm_io_close(df, SM_TIME_DEFAULT) < 0) 4042132943Sgshapiro { 4043132943Sgshapiro dferror(df, "sm_io_close", e); 4044132943Sgshapiro flush_errors(true); 4045132943Sgshapiro finis(true, true, ExitStat); 4046132943Sgshapiro /* NOTREACHED */ 4047132943Sgshapiro } 4048132943Sgshapiro 4049132943Sgshapiro /* Now reopen the df file */ 4050132943Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname, 4051132943Sgshapiro SM_IO_RDONLY, NULL); 4052132943Sgshapiro if (e->e_dfp == NULL) 4053132943Sgshapiro { 4054132943Sgshapiro /* we haven't acked receipt yet, so just chuck this */ 4055132943Sgshapiro syserr("@Cannot reopen %s", dfname); 4056132943Sgshapiro finis(true, true, ExitStat); 4057132943Sgshapiro /* NOTREACHED */ 4058132943Sgshapiro } 4059132943Sgshapiro } 406090792Sgshapiro#endif /* MILTER */ 406190792Sgshapiro 406290792Sgshapiro /* Check if quarantining stats should be updated */ 406390792Sgshapiro if (e->e_quarmsg != NULL) 406490792Sgshapiro markstats(e, NULL, STATS_QUARANTINE); 406590792Sgshapiro 406690792Sgshapiro /* 406790792Sgshapiro ** If a header/body check (header checks or milter) 406890792Sgshapiro ** set EF_DISCARD, don't queueup the message -- 406990792Sgshapiro ** that would lose the EF_DISCARD bit and deliver 407090792Sgshapiro ** the message. 407190792Sgshapiro */ 407290792Sgshapiro 407390792Sgshapiro if (bitset(EF_DISCARD, e->e_flags)) 407490792Sgshapiro doublequeue = false; 407590792Sgshapiro 407690792Sgshapiro aborting = Errors > 0; 4077125820Sgshapiro if (!(aborting || bitset(EF_DISCARD, e->e_flags)) && 407890792Sgshapiro (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) && 407990792Sgshapiro !split_by_recipient(e)) 408090792Sgshapiro aborting = bitset(EF_FATALERRS, e->e_flags); 408190792Sgshapiro 408290792Sgshapiro if (aborting) 408390792Sgshapiro { 4084173340Sgshapiro ADDRESS *q; 4085173340Sgshapiro 408690792Sgshapiro /* Log who the mail would have gone to */ 408790792Sgshapiro logundelrcpts(e, e->e_message, 8, false); 4088173340Sgshapiro 4089173340Sgshapiro /* 4090173340Sgshapiro ** If something above refused the message, we still haven't 4091173340Sgshapiro ** accepted responsibility for it. Don't send DSNs. 4092173340Sgshapiro */ 4093173340Sgshapiro 4094173340Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 4095173340Sgshapiro q->q_flags &= ~Q_PINGFLAGS; 4096173340Sgshapiro 409790792Sgshapiro flush_errors(true); 409890792Sgshapiro buffer_errors(); 409990792Sgshapiro goto abortmessage; 410090792Sgshapiro } 410190792Sgshapiro 410290792Sgshapiro /* from now on, we have to operate silently */ 410390792Sgshapiro buffer_errors(); 410490792Sgshapiro 410590792Sgshapiro#if 0 410690792Sgshapiro /* 410790792Sgshapiro ** Clear message, it may contain an error from the SMTP dialogue. 410890792Sgshapiro ** This error must not show up in the queue. 410990792Sgshapiro ** Some error message should show up, e.g., alias database 411090792Sgshapiro ** not available, but others shouldn't, e.g., from check_rcpt. 411190792Sgshapiro */ 411290792Sgshapiro 411390792Sgshapiro e->e_message = NULL; 411490792Sgshapiro#endif /* 0 */ 411590792Sgshapiro 411690792Sgshapiro /* 411790792Sgshapiro ** Arrange to send to everyone. 411890792Sgshapiro ** If sending to multiple people, mail back 411990792Sgshapiro ** errors rather than reporting directly. 412090792Sgshapiro ** In any case, don't mail back errors for 412190792Sgshapiro ** anything that has happened up to 412290792Sgshapiro ** now (the other end will do this). 412390792Sgshapiro ** Truncate our transcript -- the mail has gotten 412490792Sgshapiro ** to us successfully, and if we have 412590792Sgshapiro ** to mail this back, it will be easier 412690792Sgshapiro ** on the reader. 412790792Sgshapiro ** Then send to everyone. 412890792Sgshapiro ** Finally give a reply code. If an error has 412990792Sgshapiro ** already been given, don't mail a 413090792Sgshapiro ** message back. 413190792Sgshapiro ** We goose error returns by clearing error bit. 413290792Sgshapiro */ 413390792Sgshapiro 413490792Sgshapiro SmtpPhase = "delivery"; 413590792Sgshapiro (void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL); 413690792Sgshapiro id = e->e_id; 413790792Sgshapiro 413890792Sgshapiro#if NAMED_BIND 413990792Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 414090792Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 4141363466Sgshapiro#endif 414290792Sgshapiro 4143285229Sgshapiro#if _FFR_PROXY 4144285229Sgshapiro if (SM_PROXY_REQ == e->e_sendmode) 4145285229Sgshapiro { 4146285229Sgshapiro /* is proxy mode possible? */ 4147285229Sgshapiro if (e->e_sibling == NULL && e->e_nrcpts == 1 4148285229Sgshapiro && smtp->sm_nrcpts == 1 4149285229Sgshapiro && (a = e->e_sendqueue) != NULL && a->q_next == NULL) 4150285229Sgshapiro { 4151285229Sgshapiro a->q_flags &= ~(QPINGONFAILURE|QPINGONSUCCESS| 4152285229Sgshapiro QPINGONDELAY); 4153285229Sgshapiro e->e_errormode = EM_QUIET; 4154285229Sgshapiro e->e_sendmode = SM_PROXY; 4155285229Sgshapiro } 4156285229Sgshapiro else 4157285229Sgshapiro { 4158285229Sgshapiro if (tTd(87, 2)) 4159285229Sgshapiro { 4160285229Sgshapiro a = e->e_sendqueue; 4161285229Sgshapiro sm_dprintf("srv: mode=%c, e=%p, sibling=%p, nrcpts=%d, sm_nrcpts=%d, sendqueue=%p, next=%p\n", 4162285229Sgshapiro e->e_sendmode, e, e->e_sibling, e->e_nrcpts, 4163285229Sgshapiro smtp->sm_nrcpts, a, 4164285229Sgshapiro (a == NULL) ? (void *)0 : a->q_next); 4165285229Sgshapiro } 4166223067Sgshapiro 4167285229Sgshapiro /* switch to interactive mode */ 4168285229Sgshapiro e->e_sendmode = SM_DELIVER; 4169285229Sgshapiro if (LogLevel > 9) 4170285229Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, 4171285229Sgshapiro "proxy mode requested but not possible"); 4172285229Sgshapiro } 4173285229Sgshapiro } 4174285229Sgshapiro#endif /* _FFR_PROXY */ 4175285229Sgshapiro 417690792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 417790792Sgshapiro { 417890792Sgshapiro /* make sure we actually do delivery */ 417990792Sgshapiro ee->e_flags &= ~EF_CLRQUEUE; 418090792Sgshapiro 418190792Sgshapiro /* from now on, operate silently */ 418290792Sgshapiro ee->e_errormode = EM_MAIL; 418390792Sgshapiro 418490792Sgshapiro if (doublequeue) 418590792Sgshapiro { 418690792Sgshapiro /* make sure it is in the queue */ 418790792Sgshapiro queueup(ee, false, true); 418890792Sgshapiro } 418990792Sgshapiro else 419090792Sgshapiro { 4191157001Sgshapiro int mode; 4192157001Sgshapiro 419390792Sgshapiro /* send to all recipients */ 4194157001Sgshapiro mode = SM_DEFAULT; 4195157001Sgshapiro#if _FFR_DM_ONE 4196157001Sgshapiro if (SM_DM_ONE == e->e_sendmode) 4197157001Sgshapiro { 4198157001Sgshapiro if (NotFirstDelivery) 4199157001Sgshapiro { 4200157001Sgshapiro mode = SM_QUEUE; 4201157001Sgshapiro e->e_sendmode = SM_QUEUE; 4202157001Sgshapiro } 4203157001Sgshapiro else 4204157001Sgshapiro { 4205157001Sgshapiro mode = SM_FORK; 4206157001Sgshapiro NotFirstDelivery = true; 4207157001Sgshapiro } 4208157001Sgshapiro } 4209157001Sgshapiro#endif /* _FFR_DM_ONE */ 4210157001Sgshapiro sendall(ee, mode); 421190792Sgshapiro } 421290792Sgshapiro ee->e_to = NULL; 421390792Sgshapiro } 421490792Sgshapiro 421598121Sgshapiro /* put back id for SMTP logging in putoutmsg() */ 421698121Sgshapiro oldid = CurEnv->e_id; 421798121Sgshapiro CurEnv->e_id = id; 421898121Sgshapiro 4219285229Sgshapiro#if _FFR_PROXY 4220285229Sgshapiro a = e->e_sendqueue; 4221285229Sgshapiro if (tTd(87, 1)) 4222285229Sgshapiro { 4223285229Sgshapiro sm_dprintf("srv: mode=%c, e=%p, sibling=%p, nrcpts=%d, msg=%s, sendqueue=%p, next=%p, state=%d, SmtpError=%s, rcode=%d, renhsc=%s, text=%s\n", 4224285229Sgshapiro e->e_sendmode, e, e->e_sibling, e->e_nrcpts, e->e_message, a, 4225285229Sgshapiro (a == NULL) ? (void *)0 : a->q_next, 4226285229Sgshapiro (a == NULL) ? -1 : a->q_state, SmtpError, e->e_rcode, 4227285229Sgshapiro e->e_renhsc, e->e_text); 4228285229Sgshapiro } 4229285229Sgshapiro 4230285229Sgshapiro if (SM_PROXY == e->e_sendmode && a->q_state != QS_SENT && 4231285229Sgshapiro a->q_state != QS_VERIFIED) /* discarded! */ 4232285229Sgshapiro { 4233285229Sgshapiro char *m, *errtext; 4234285229Sgshapiro char replycode[4]; 4235285229Sgshapiro char enhsc[10]; 4236285229Sgshapiro int offset; 4237285229Sgshapiro 4238285229Sgshapiro#define NN_MSG(e) (((e)->e_message != NULL) ? (e)->e_message : "") 4239285229Sgshapiro m = e->e_message; 4240285229Sgshapiro#define SM_MSG_DEFERRED "Deferred: " 4241285229Sgshapiro if (m != NULL && strncmp(SM_MSG_DEFERRED, m, 4242285229Sgshapiro sizeof(SM_MSG_DEFERRED) - 1) == 0) 4243285229Sgshapiro m += sizeof(SM_MSG_DEFERRED) - 1; 4244285229Sgshapiro offset = extsc(m, ' ', replycode, enhsc); 4245285229Sgshapiro 4246285229Sgshapiro if (tTd(87, 2)) 4247285229Sgshapiro { 4248285229Sgshapiro sm_dprintf("srv: SmtpError=%s, rcode=%d, renhsc=%s, replycode=%s, enhsc=%s, offset=%d\n", 4249285229Sgshapiro SmtpError, e->e_rcode, e->e_renhsc, 4250285229Sgshapiro replycode, enhsc, offset); 4251285229Sgshapiro } 4252285229Sgshapiro 4253285229Sgshapiro#define DIG2CHAR(d) ((d) + '0') 4254285229Sgshapiro if (e->e_rcode != 0 && (replycode[0] == '\0' || 4255285229Sgshapiro replycode[0] == DIG2CHAR(REPLYTYPE(e->e_rcode)))) 4256285229Sgshapiro { 4257285229Sgshapiro replycode[0] = DIG2CHAR(REPLYTYPE(e->e_rcode)); 4258285229Sgshapiro replycode[1] = DIG2CHAR(REPLYCLASS(e->e_rcode)); 4259285229Sgshapiro replycode[2] = DIG2CHAR(REPLYMINOR(e->e_rcode)); 4260285229Sgshapiro replycode[3] = '\0'; 4261285229Sgshapiro if (e->e_renhsc[0] == replycode[0]) 4262285229Sgshapiro sm_strlcpy(enhsc, e->e_renhsc, sizeof(enhsc)); 4263285229Sgshapiro if (offset < 0) 4264285229Sgshapiro offset = 0; 4265285229Sgshapiro } 4266285229Sgshapiro if (e->e_text != NULL) 4267285229Sgshapiro { 4268285229Sgshapiro (void) strreplnonprt(e->e_text, '_'); 4269285229Sgshapiro errtext = e->e_text; 4270285229Sgshapiro } 4271285229Sgshapiro else 4272285229Sgshapiro errtext = m + offset; 4273285229Sgshapiro 4274285229Sgshapiro if (replycode[0] != '\0' && enhsc[0] != '\0') 4275285229Sgshapiro emessage(replycode, enhsc, "%s", errtext); 4276285229Sgshapiro else if (replycode[0] != '\0') 4277285229Sgshapiro emessage(replycode, smtptodsn(atoi(replycode)), 4278285229Sgshapiro "%s", errtext); 4279285229Sgshapiro else if (QS_IS_TEMPFAIL(a->q_state)) 4280285229Sgshapiro { 4281285229Sgshapiro if (m != NULL) 4282285229Sgshapiro message("450 4.5.1 %s", m); 4283285229Sgshapiro else 4284285229Sgshapiro message("450 4.5.1 Temporary error"); 4285285229Sgshapiro } 4286285229Sgshapiro else 4287285229Sgshapiro { 4288285229Sgshapiro if (m != NULL) 4289285229Sgshapiro message("550 5.5.1 %s", m); 4290285229Sgshapiro else 4291285229Sgshapiro message("550 5.0.0 Permanent error"); 4292285229Sgshapiro } 4293285229Sgshapiro } 4294285229Sgshapiro else 4295285229Sgshapiro { 4296285229Sgshapiro#endif /* _FFR_PROXY */ 4297223067Sgshapiro /* issue success message */ 4298157001Sgshapiro#if _FFR_MSG_ACCEPT 4299223067Sgshapiro if (MessageAccept != NULL && *MessageAccept != '\0') 4300223067Sgshapiro { 4301223067Sgshapiro char msg[MAXLINE]; 4302157001Sgshapiro 4303223067Sgshapiro expand(MessageAccept, msg, sizeof(msg), e); 4304223067Sgshapiro message("250 2.0.0 %s", msg); 4305223067Sgshapiro } 4306223067Sgshapiro else 4307157001Sgshapiro#endif /* _FFR_MSG_ACCEPT */ 4308223067Sgshapiro message("250 2.0.0 %s Message accepted for delivery", id); 4309285229Sgshapiro#if _FFR_PROXY 4310285229Sgshapiro } 4311363466Sgshapiro#endif 431298121Sgshapiro CurEnv->e_id = oldid; 431390792Sgshapiro 431490792Sgshapiro /* if we just queued, poke it */ 431590792Sgshapiro if (doublequeue) 431690792Sgshapiro { 431790792Sgshapiro bool anything_to_send = false; 431890792Sgshapiro 431990792Sgshapiro sm_getla(); 432090792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 432190792Sgshapiro { 432290792Sgshapiro if (WILL_BE_QUEUED(ee->e_sendmode)) 432390792Sgshapiro continue; 432490792Sgshapiro if (shouldqueue(ee->e_msgpriority, ee->e_ctime)) 432590792Sgshapiro { 432690792Sgshapiro ee->e_sendmode = SM_QUEUE; 432790792Sgshapiro continue; 432890792Sgshapiro } 432990792Sgshapiro else if (QueueMode != QM_QUARANTINE && 433090792Sgshapiro ee->e_quarmsg != NULL) 433190792Sgshapiro { 433290792Sgshapiro ee->e_sendmode = SM_QUEUE; 433390792Sgshapiro continue; 433490792Sgshapiro } 433590792Sgshapiro anything_to_send = true; 433690792Sgshapiro 433790792Sgshapiro /* close all the queue files */ 433890792Sgshapiro closexscript(ee); 433990792Sgshapiro if (ee->e_dfp != NULL) 434090792Sgshapiro { 434190792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 434290792Sgshapiro ee->e_dfp = NULL; 434390792Sgshapiro } 434490792Sgshapiro unlockqueue(ee); 434590792Sgshapiro } 434690792Sgshapiro if (anything_to_send) 434790792Sgshapiro { 434890792Sgshapiro#if PIPELINING 434990792Sgshapiro /* 435090792Sgshapiro ** XXX if we don't do this, we get 250 twice 435190792Sgshapiro ** because it is also flushed in the child. 435290792Sgshapiro */ 435390792Sgshapiro 435490792Sgshapiro (void) sm_io_flush(OutChannel, SM_TIME_DEFAULT); 435590792Sgshapiro#endif /* PIPELINING */ 435690792Sgshapiro (void) doworklist(e, true, true); 435790792Sgshapiro } 435890792Sgshapiro } 435990792Sgshapiro 436090792Sgshapiro abortmessage: 4361203004Sgshapiro if (tTd(92, 2)) 4362203004Sgshapiro sm_dprintf("abortmessage: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n", 4363203004Sgshapiro e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel); 436490792Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 436590792Sgshapiro logsender(e, NULL); 436690792Sgshapiro e->e_flags &= ~EF_LOGSENDER; 436790792Sgshapiro 436890792Sgshapiro /* clean up a bit */ 436990792Sgshapiro smtp->sm_gotmail = false; 437090792Sgshapiro 437190792Sgshapiro /* 437290792Sgshapiro ** Call dropenvelope if and only if the envelope is *not* 437390792Sgshapiro ** being processed by the child process forked by doworklist(). 437490792Sgshapiro */ 437590792Sgshapiro 437690792Sgshapiro if (aborting || bitset(EF_DISCARD, e->e_flags)) 4377203004Sgshapiro (void) dropenvelope(e, true, false); 437890792Sgshapiro else 437990792Sgshapiro { 438090792Sgshapiro for (ee = e; ee != NULL; ee = ee->e_sibling) 438190792Sgshapiro { 438290792Sgshapiro if (!doublequeue && 438390792Sgshapiro QueueMode != QM_QUARANTINE && 438490792Sgshapiro ee->e_quarmsg != NULL) 438590792Sgshapiro { 4386203004Sgshapiro (void) dropenvelope(ee, true, false); 438790792Sgshapiro continue; 438890792Sgshapiro } 438990792Sgshapiro if (WILL_BE_QUEUED(ee->e_sendmode)) 4390203004Sgshapiro (void) dropenvelope(ee, true, false); 439190792Sgshapiro } 439290792Sgshapiro } 439390792Sgshapiro 439490792Sgshapiro CurEnv = e; 4395168515Sgshapiro features = e->e_features; 4396182352Sgshapiro sm_rpool_free(e->e_rpool); 439790792Sgshapiro newenvelope(e, e, sm_rpool_new_x(NULL)); 439890792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 4399168515Sgshapiro e->e_features = features; 440090792Sgshapiro 440190792Sgshapiro /* restore connection quarantining */ 440290792Sgshapiro if (smtp->sm_quarmsg == NULL) 440390792Sgshapiro { 440490792Sgshapiro e->e_quarmsg = NULL; 440590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), ""); 440690792Sgshapiro } 440790792Sgshapiro else 440890792Sgshapiro { 440990792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg); 441090792Sgshapiro macdefine(&e->e_macro, A_PERM, 441190792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 441290792Sgshapiro } 4413157001Sgshapiro return rv; 441438032Speter} 441590792Sgshapiro/* 441690792Sgshapiro** LOGUNDELRCPTS -- log undelivered (or all) recipients. 441790792Sgshapiro** 441890792Sgshapiro** Parameters: 441990792Sgshapiro** e -- envelope. 442090792Sgshapiro** msg -- message for Stat= 442190792Sgshapiro** level -- log level. 442290792Sgshapiro** all -- log all recipients. 442390792Sgshapiro** 442490792Sgshapiro** Returns: 442590792Sgshapiro** none. 442690792Sgshapiro** 442790792Sgshapiro** Side Effects: 442890792Sgshapiro** logs undelivered (or all) recipients 442990792Sgshapiro*/ 443090792Sgshapiro 443190792Sgshapirovoid 443290792Sgshapirologundelrcpts(e, msg, level, all) 443390792Sgshapiro ENVELOPE *e; 443490792Sgshapiro char *msg; 443590792Sgshapiro int level; 443690792Sgshapiro bool all; 443790792Sgshapiro{ 443890792Sgshapiro ADDRESS *a; 443990792Sgshapiro 444090792Sgshapiro if (LogLevel <= level || msg == NULL || *msg == '\0') 444190792Sgshapiro return; 444290792Sgshapiro 444390792Sgshapiro /* Clear $h so relay= doesn't get mislogged by logdelivery() */ 444490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', NULL); 444590792Sgshapiro 444690792Sgshapiro /* Log who the mail would have gone to */ 444790792Sgshapiro for (a = e->e_sendqueue; a != NULL; a = a->q_next) 444890792Sgshapiro { 444990792Sgshapiro if (!QS_IS_UNDELIVERED(a->q_state) && !all) 445090792Sgshapiro continue; 445190792Sgshapiro e->e_to = a->q_paddr; 4452203004Sgshapiro logdelivery(NULL, NULL, 4453203004Sgshapiro#if _FFR_MILTER_ENHSC 4454203004Sgshapiro (a->q_status == NULL && e->e_enhsc[0] != '\0') 4455203004Sgshapiro ? e->e_enhsc : 4456363466Sgshapiro#endif 4457203004Sgshapiro a->q_status, 4458285229Sgshapiro msg, NULL, (time_t) 0, e, a, EX_OK /* ??? */); 445990792Sgshapiro } 446090792Sgshapiro e->e_to = NULL; 446190792Sgshapiro} 446290792Sgshapiro/* 446338032Speter** CHECKSMTPATTACK -- check for denial-of-service attack by repetition 446438032Speter** 446538032Speter** Parameters: 446638032Speter** pcounter -- pointer to a counter for this command. 446738032Speter** maxcount -- maximum value for this counter before we 446838032Speter** slow down. 446964562Sgshapiro** waitnow -- sleep now (in this routine)? 447038032Speter** cname -- command name for logging. 447138032Speter** e -- the current envelope. 447238032Speter** 447338032Speter** Returns: 4474132943Sgshapiro** time to wait, 4475132943Sgshapiro** STOP_ATTACK if twice as many commands as allowed and 4476132943Sgshapiro** MaxChildren > 0. 447738032Speter** 447838032Speter** Side Effects: 447938032Speter** Slows down if we seem to be under attack. 448038032Speter*/ 448138032Speter 448264562Sgshapirostatic time_t 448364562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e) 448490792Sgshapiro volatile unsigned int *pcounter; 4485132943Sgshapiro unsigned int maxcount; 448664562Sgshapiro bool waitnow; 448738032Speter char *cname; 448838032Speter ENVELOPE *e; 448938032Speter{ 449090792Sgshapiro if (maxcount <= 0) /* no limit */ 449190792Sgshapiro return (time_t) 0; 449290792Sgshapiro 449338032Speter if (++(*pcounter) >= maxcount) 449438032Speter { 4495132943Sgshapiro unsigned int shift; 449664562Sgshapiro time_t s; 449764562Sgshapiro 449838032Speter if (*pcounter == maxcount && LogLevel > 5) 449938032Speter { 450038032Speter sm_syslog(LOG_INFO, e->e_id, 4501110560Sgshapiro "%s: possible SMTP attack: command=%.40s, count=%u", 450277349Sgshapiro CurSmtpClient, cname, *pcounter); 450338032Speter } 4504132943Sgshapiro shift = *pcounter - maxcount; 4505132943Sgshapiro s = 1 << shift; 4506132943Sgshapiro if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0) 450764562Sgshapiro s = MAXTIMEOUT; 450890792Sgshapiro 4509132943Sgshapiro#define IS_ATTACK(s) ((MaxChildren > 0 && *pcounter >= maxcount * 2) \ 4510132943Sgshapiro ? STOP_ATTACK : (time_t) s) 4511132943Sgshapiro 451264562Sgshapiro /* sleep at least 1 second before returning */ 451364562Sgshapiro (void) sleep(*pcounter / maxcount); 451464562Sgshapiro s -= *pcounter / maxcount; 4515132943Sgshapiro if (s >= MAXTIMEOUT || s < 0) 4516132943Sgshapiro s = MAXTIMEOUT; 4517132943Sgshapiro if (waitnow && s > 0) 451864562Sgshapiro { 451964562Sgshapiro (void) sleep(s); 4520132943Sgshapiro return IS_ATTACK(0); 452164562Sgshapiro } 4522132943Sgshapiro return IS_ATTACK(s); 452338032Speter } 452490792Sgshapiro return (time_t) 0; 452538032Speter} 452690792Sgshapiro/* 452790792Sgshapiro** SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server 452890792Sgshapiro** 452990792Sgshapiro** Parameters: 453090792Sgshapiro** none. 453190792Sgshapiro** 453290792Sgshapiro** Returns: 453390792Sgshapiro** nothing. 453490792Sgshapiro** 453590792Sgshapiro** Side Effects: 453690792Sgshapiro** may change I/O fd. 453790792Sgshapiro*/ 453890792Sgshapiro 453990792Sgshapirostatic void 454090792Sgshapirosetup_smtpd_io() 454190792Sgshapiro{ 454290792Sgshapiro int inchfd, outchfd, outfd; 454390792Sgshapiro 454490792Sgshapiro inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL); 454590792Sgshapiro outchfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL); 454690792Sgshapiro outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL); 454790792Sgshapiro if (outchfd != outfd) 454890792Sgshapiro { 454990792Sgshapiro /* arrange for debugging output to go to remote host */ 455090792Sgshapiro (void) dup2(outchfd, outfd); 455190792Sgshapiro } 455290792Sgshapiro 455390792Sgshapiro /* 455490792Sgshapiro ** if InChannel and OutChannel are stdin/stdout 455590792Sgshapiro ** and connected to ttys 455690792Sgshapiro ** and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT, 455790792Sgshapiro ** then "chain" them together. 455890792Sgshapiro */ 455990792Sgshapiro 456090792Sgshapiro if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO && 456190792Sgshapiro isatty(inchfd) && isatty(outchfd)) 456290792Sgshapiro { 456390792Sgshapiro int inmode, outmode; 456490792Sgshapiro 456590792Sgshapiro inmode = fcntl(inchfd, F_GETFL, 0); 456690792Sgshapiro if (inmode == -1) 456790792Sgshapiro { 456890792Sgshapiro if (LogLevel > 11) 456990792Sgshapiro sm_syslog(LOG_INFO, NOQID, 457090792Sgshapiro "fcntl(inchfd, F_GETFL) failed: %s", 457190792Sgshapiro sm_errstring(errno)); 457290792Sgshapiro return; 457390792Sgshapiro } 457490792Sgshapiro outmode = fcntl(outchfd, F_GETFL, 0); 457590792Sgshapiro if (outmode == -1) 457690792Sgshapiro { 457790792Sgshapiro if (LogLevel > 11) 457890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 457990792Sgshapiro "fcntl(outchfd, F_GETFL) failed: %s", 458090792Sgshapiro sm_errstring(errno)); 458190792Sgshapiro return; 458290792Sgshapiro } 458390792Sgshapiro if (bitset(O_NONBLOCK, inmode) || 458490792Sgshapiro bitset(O_NONBLOCK, outmode) || 458590792Sgshapiro fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1) 458690792Sgshapiro return; 458790792Sgshapiro outmode = fcntl(outchfd, F_GETFL, 0); 458890792Sgshapiro if (outmode != -1 && bitset(O_NONBLOCK, outmode)) 458990792Sgshapiro { 459090792Sgshapiro /* changing InChannel also changes OutChannel */ 459190792Sgshapiro sm_io_automode(OutChannel, InChannel); 459290792Sgshapiro if (tTd(97, 4) && LogLevel > 9) 459390792Sgshapiro sm_syslog(LOG_INFO, NOQID, 459490792Sgshapiro "set automode for I (%d)/O (%d) in SMTP server", 459590792Sgshapiro inchfd, outchfd); 459690792Sgshapiro } 459790792Sgshapiro 459890792Sgshapiro /* undo change of inchfd */ 459990792Sgshapiro (void) fcntl(inchfd, F_SETFL, inmode); 460090792Sgshapiro } 460190792Sgshapiro} 460290792Sgshapiro/* 460338032Speter** SKIPWORD -- skip a fixed word. 460438032Speter** 460538032Speter** Parameters: 460638032Speter** p -- place to start looking. 460738032Speter** w -- word to skip. 460838032Speter** 460938032Speter** Returns: 461038032Speter** p following w. 461138032Speter** NULL on error. 461238032Speter** 461338032Speter** Side Effects: 461438032Speter** clobbers the p data area. 461538032Speter*/ 461638032Speter 461738032Speterstatic char * 461838032Speterskipword(p, w) 461938032Speter register char *volatile p; 462038032Speter char *w; 462138032Speter{ 462238032Speter register char *q; 462338032Speter char *firstp = p; 462438032Speter 462538032Speter /* find beginning of word */ 462690792Sgshapiro SKIP_SPACE(p); 462738032Speter q = p; 462838032Speter 462938032Speter /* find end of word */ 4630363466Sgshapiro while (*p != '\0' && *p != ':' && !(SM_ISSPACE(*p))) 463138032Speter p++; 4632363466Sgshapiro while (SM_ISSPACE(*p)) 463338032Speter *p++ = '\0'; 463438032Speter if (*p != ':') 463538032Speter { 463638032Speter syntax: 463764562Sgshapiro usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"", 463838032Speter shortenstring(firstp, MAXSHORTSTR)); 463964562Sgshapiro return NULL; 464038032Speter } 464138032Speter *p++ = '\0'; 464290792Sgshapiro SKIP_SPACE(p); 464338032Speter 464438032Speter if (*p == '\0') 464538032Speter goto syntax; 464638032Speter 464738032Speter /* see if the input word matches desired word */ 464890792Sgshapiro if (sm_strcasecmp(q, w)) 464938032Speter goto syntax; 465038032Speter 465164562Sgshapiro return p; 465238032Speter} 4653159609Sgshapiro 465490792Sgshapiro/* 4655168515Sgshapiro** RESET_MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 4656168515Sgshapiro** 4657168515Sgshapiro** Parameters: 4658168515Sgshapiro** e -- the envelope. 4659168515Sgshapiro** 4660168515Sgshapiro** Returns: 4661168515Sgshapiro** none. 4662168515Sgshapiro*/ 4663168515Sgshapiro 4664168515Sgshapirovoid 4665168515Sgshapiroreset_mail_esmtp_args(e) 4666168515Sgshapiro ENVELOPE *e; 4667168515Sgshapiro{ 4668168515Sgshapiro /* "size": no reset */ 4669168515Sgshapiro 4670168515Sgshapiro /* "body" */ 4671168515Sgshapiro SevenBitInput = SevenBitInput_Saved; 4672168515Sgshapiro e->e_bodytype = NULL; 4673168515Sgshapiro 4674168515Sgshapiro /* "envid" */ 4675168515Sgshapiro e->e_envid = NULL; 4676168515Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL); 4677168515Sgshapiro 4678168515Sgshapiro /* "ret" */ 4679173340Sgshapiro e->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); 4680168515Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL); 4681168515Sgshapiro 4682168515Sgshapiro#if SASL 4683168515Sgshapiro /* "auth" */ 4684168515Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL); 4685168515Sgshapiro e->e_auth_param = ""; 4686168515Sgshapiro# if _FFR_AUTH_PASSING 4687168515Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 4688168515Sgshapiro macid("{auth_author}"), NULL); 4689363466Sgshapiro# endif 4690168515Sgshapiro#endif /* SASL */ 4691168515Sgshapiro 4692168515Sgshapiro /* "by" */ 4693168515Sgshapiro e->e_deliver_by = 0; 4694168515Sgshapiro e->e_dlvr_flag = 0; 4695168515Sgshapiro} 4696168515Sgshapiro 4697168515Sgshapiro/* 469838032Speter** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 469938032Speter** 470038032Speter** Parameters: 4701168515Sgshapiro** a -- address (unused, for compatibility with rcpt_esmtp_args) 470238032Speter** kp -- the parameter key. 470338032Speter** vp -- the value of that parameter. 470438032Speter** e -- the envelope. 470538032Speter** 470638032Speter** Returns: 470738032Speter** none. 470838032Speter*/ 470938032Speter 4710168515Sgshapirovoid 4711168515Sgshapiromail_esmtp_args(a, kp, vp, e) 4712168515Sgshapiro ADDRESS *a; 471338032Speter char *kp; 471438032Speter char *vp; 471538032Speter ENVELOPE *e; 471638032Speter{ 471790792Sgshapiro if (sm_strcasecmp(kp, "size") == 0) 471838032Speter { 471938032Speter if (vp == NULL) 472038032Speter { 472164562Sgshapiro usrerr("501 5.5.2 SIZE requires a value"); 472238032Speter /* NOTREACHED */ 472338032Speter } 472490792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp); 472590792Sgshapiro errno = 0; 472671345Sgshapiro e->e_msgsize = strtol(vp, (char **) NULL, 10); 472766494Sgshapiro if (e->e_msgsize == LONG_MAX && errno == ERANGE) 472866494Sgshapiro { 472966494Sgshapiro usrerr("552 5.2.3 Message size exceeds maximum value"); 473066494Sgshapiro /* NOTREACHED */ 473166494Sgshapiro } 473290792Sgshapiro if (e->e_msgsize < 0) 473390792Sgshapiro { 473490792Sgshapiro usrerr("552 5.2.3 Message size invalid"); 473590792Sgshapiro /* NOTREACHED */ 473690792Sgshapiro } 473738032Speter } 473890792Sgshapiro else if (sm_strcasecmp(kp, "body") == 0) 473938032Speter { 474038032Speter if (vp == NULL) 474138032Speter { 474264562Sgshapiro usrerr("501 5.5.2 BODY requires a value"); 474338032Speter /* NOTREACHED */ 474438032Speter } 474590792Sgshapiro else if (sm_strcasecmp(vp, "8bitmime") == 0) 474638032Speter { 474790792Sgshapiro SevenBitInput = false; 474838032Speter } 474990792Sgshapiro else if (sm_strcasecmp(vp, "7bit") == 0) 475038032Speter { 475190792Sgshapiro SevenBitInput = true; 475238032Speter } 475338032Speter else 475438032Speter { 475590792Sgshapiro usrerr("501 5.5.4 Unknown BODY type %s", vp); 475638032Speter /* NOTREACHED */ 475738032Speter } 475890792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp); 475938032Speter } 476090792Sgshapiro else if (sm_strcasecmp(kp, "envid") == 0) 476138032Speter { 4762168515Sgshapiro if (!bitset(SRV_OFFER_DSN, e->e_features)) 476364562Sgshapiro { 476464562Sgshapiro usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN"); 476564562Sgshapiro /* NOTREACHED */ 476664562Sgshapiro } 476738032Speter if (vp == NULL) 476838032Speter { 476964562Sgshapiro usrerr("501 5.5.2 ENVID requires a value"); 477038032Speter /* NOTREACHED */ 477138032Speter } 477238032Speter if (!xtextok(vp)) 477338032Speter { 477464562Sgshapiro usrerr("501 5.5.4 Syntax error in ENVID parameter value"); 477538032Speter /* NOTREACHED */ 477638032Speter } 477738032Speter if (e->e_envid != NULL) 477838032Speter { 477964562Sgshapiro usrerr("501 5.5.0 Duplicate ENVID parameter"); 478038032Speter /* NOTREACHED */ 478138032Speter } 478290792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp); 478390792Sgshapiro macdefine(&e->e_macro, A_PERM, 478490792Sgshapiro macid("{dsn_envid}"), e->e_envid); 478538032Speter } 478690792Sgshapiro else if (sm_strcasecmp(kp, "ret") == 0) 478738032Speter { 4788168515Sgshapiro if (!bitset(SRV_OFFER_DSN, e->e_features)) 478964562Sgshapiro { 479064562Sgshapiro usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN"); 479164562Sgshapiro /* NOTREACHED */ 479264562Sgshapiro } 479338032Speter if (vp == NULL) 479438032Speter { 479564562Sgshapiro usrerr("501 5.5.2 RET requires a value"); 479638032Speter /* NOTREACHED */ 479738032Speter } 479838032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 479938032Speter { 480064562Sgshapiro usrerr("501 5.5.0 Duplicate RET parameter"); 480138032Speter /* NOTREACHED */ 480238032Speter } 480338032Speter e->e_flags |= EF_RET_PARAM; 480490792Sgshapiro if (sm_strcasecmp(vp, "hdrs") == 0) 480538032Speter e->e_flags |= EF_NO_BODY_RETN; 480690792Sgshapiro else if (sm_strcasecmp(vp, "full") != 0) 480738032Speter { 480864562Sgshapiro usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp); 480938032Speter /* NOTREACHED */ 481038032Speter } 481190792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp); 481238032Speter } 481390792Sgshapiro#if SASL 481490792Sgshapiro else if (sm_strcasecmp(kp, "auth") == 0) 481564562Sgshapiro { 481664562Sgshapiro int len; 481764562Sgshapiro char *q; 481864562Sgshapiro char *auth_param; /* the value of the AUTH=x */ 481964562Sgshapiro bool saveQuickAbort = QuickAbort; 482064562Sgshapiro bool saveSuprErrs = SuprErrs; 482190792Sgshapiro bool saveExitStat = ExitStat; 482264562Sgshapiro 482364562Sgshapiro if (vp == NULL) 482464562Sgshapiro { 482564562Sgshapiro usrerr("501 5.5.2 AUTH= requires a value"); 482664562Sgshapiro /* NOTREACHED */ 482764562Sgshapiro } 482864562Sgshapiro if (e->e_auth_param != NULL) 482964562Sgshapiro { 483064562Sgshapiro usrerr("501 5.5.0 Duplicate AUTH parameter"); 483164562Sgshapiro /* NOTREACHED */ 483264562Sgshapiro } 483364562Sgshapiro if ((q = strchr(vp, ' ')) != NULL) 483464562Sgshapiro len = q - vp + 1; 483564562Sgshapiro else 483664562Sgshapiro len = strlen(vp) + 1; 483764562Sgshapiro auth_param = xalloc(len); 483890792Sgshapiro (void) sm_strlcpy(auth_param, vp, len); 483964562Sgshapiro if (!xtextok(auth_param)) 484064562Sgshapiro { 484164562Sgshapiro usrerr("501 5.5.4 Syntax error in AUTH parameter value"); 484264562Sgshapiro /* just a warning? */ 484364562Sgshapiro /* NOTREACHED */ 484464562Sgshapiro } 484564562Sgshapiro 484664562Sgshapiro /* XXX define this always or only if trusted? */ 4847132943Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), 4848132943Sgshapiro auth_param); 484964562Sgshapiro 485064562Sgshapiro /* 485164562Sgshapiro ** call Strust_auth to find out whether 485264562Sgshapiro ** auth_param is acceptable (trusted) 485364562Sgshapiro ** we shouldn't trust it if not authenticated 485464562Sgshapiro ** (required by RFC, leave it to ruleset?) 485564562Sgshapiro */ 485664562Sgshapiro 485790792Sgshapiro SuprErrs = true; 485890792Sgshapiro QuickAbort = false; 485964562Sgshapiro if (strcmp(auth_param, "<>") != 0 && 4860285229Sgshapiro (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM, 9, 4861285229Sgshapiro NULL, NOQID, NULL, NULL) != EX_OK || Errors > 0)) 486264562Sgshapiro { 486364562Sgshapiro if (tTd(95, 8)) 486464562Sgshapiro { 486564562Sgshapiro q = e->e_auth_param; 486690792Sgshapiro sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n", 4867132943Sgshapiro auth_param, (q == NULL) ? "" : q); 486864562Sgshapiro } 486990792Sgshapiro 487064562Sgshapiro /* not trusted */ 487190792Sgshapiro e->e_auth_param = "<>"; 487290792Sgshapiro# if _FFR_AUTH_PASSING 487390792Sgshapiro macdefine(&BlankEnvelope.e_macro, A_PERM, 487490792Sgshapiro macid("{auth_author}"), NULL); 4875363466Sgshapiro# endif 487664562Sgshapiro } 487764562Sgshapiro else 487864562Sgshapiro { 487964562Sgshapiro if (tTd(95, 8)) 4880132943Sgshapiro sm_dprintf("auth=\"%.100s\" trusted\n", auth_param); 488190792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, 488290792Sgshapiro auth_param); 488364562Sgshapiro } 488490792Sgshapiro sm_free(auth_param); /* XXX */ 488577349Sgshapiro 488664562Sgshapiro /* reset values */ 488764562Sgshapiro Errors = 0; 488864562Sgshapiro QuickAbort = saveQuickAbort; 488964562Sgshapiro SuprErrs = saveSuprErrs; 489090792Sgshapiro ExitStat = saveExitStat; 489164562Sgshapiro } 489290792Sgshapiro#endif /* SASL */ 489390792Sgshapiro#define PRTCHAR(c) ((isascii(c) && isprint(c)) ? (c) : '?') 489490792Sgshapiro 489590792Sgshapiro /* 489690792Sgshapiro ** "by" is only accepted if DeliverByMin >= 0. 489790792Sgshapiro ** We maybe could add this to the list of server_features. 489890792Sgshapiro */ 489990792Sgshapiro 490090792Sgshapiro else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0) 490190792Sgshapiro { 490290792Sgshapiro char *s; 490390792Sgshapiro 490490792Sgshapiro if (vp == NULL) 490590792Sgshapiro { 490690792Sgshapiro usrerr("501 5.5.2 BY= requires a value"); 490790792Sgshapiro /* NOTREACHED */ 490890792Sgshapiro } 490990792Sgshapiro errno = 0; 491090792Sgshapiro e->e_deliver_by = strtol(vp, &s, 10); 491190792Sgshapiro if (e->e_deliver_by == LONG_MIN || 491290792Sgshapiro e->e_deliver_by == LONG_MAX || 491390792Sgshapiro e->e_deliver_by > 999999999l || 491490792Sgshapiro e->e_deliver_by < -999999999l) 491590792Sgshapiro { 491690792Sgshapiro usrerr("501 5.5.2 BY=%s out of range", vp); 491790792Sgshapiro /* NOTREACHED */ 491890792Sgshapiro } 491990792Sgshapiro if (s == NULL || *s != ';') 492090792Sgshapiro { 492190792Sgshapiro usrerr("501 5.5.2 BY= missing ';'"); 492290792Sgshapiro /* NOTREACHED */ 492390792Sgshapiro } 492490792Sgshapiro e->e_dlvr_flag = 0; 492590792Sgshapiro ++s; /* XXX: spaces allowed? */ 492690792Sgshapiro SKIP_SPACE(s); 492790792Sgshapiro switch (tolower(*s)) 492890792Sgshapiro { 492990792Sgshapiro case 'n': 493090792Sgshapiro e->e_dlvr_flag = DLVR_NOTIFY; 493190792Sgshapiro break; 493290792Sgshapiro case 'r': 493390792Sgshapiro e->e_dlvr_flag = DLVR_RETURN; 493490792Sgshapiro if (e->e_deliver_by <= 0) 493590792Sgshapiro { 493690792Sgshapiro usrerr("501 5.5.4 mode R requires BY time > 0"); 493790792Sgshapiro /* NOTREACHED */ 493890792Sgshapiro } 493990792Sgshapiro if (DeliverByMin > 0 && e->e_deliver_by > 0 && 494090792Sgshapiro e->e_deliver_by < DeliverByMin) 494190792Sgshapiro { 494290792Sgshapiro usrerr("555 5.5.2 time %ld less than %ld", 494390792Sgshapiro e->e_deliver_by, (long) DeliverByMin); 494490792Sgshapiro /* NOTREACHED */ 494590792Sgshapiro } 494690792Sgshapiro break; 494790792Sgshapiro default: 494890792Sgshapiro usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s)); 494990792Sgshapiro /* NOTREACHED */ 495090792Sgshapiro } 495190792Sgshapiro ++s; /* XXX: spaces allowed? */ 495290792Sgshapiro SKIP_SPACE(s); 495390792Sgshapiro switch (tolower(*s)) 495490792Sgshapiro { 495590792Sgshapiro case 't': 495690792Sgshapiro e->e_dlvr_flag |= DLVR_TRACE; 495790792Sgshapiro break; 495890792Sgshapiro case '\0': 495990792Sgshapiro break; 496090792Sgshapiro default: 496190792Sgshapiro usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s)); 496290792Sgshapiro /* NOTREACHED */ 496390792Sgshapiro } 496490792Sgshapiro 496590792Sgshapiro /* XXX: check whether more characters follow? */ 496690792Sgshapiro } 4967363466Sgshapiro#if _FFR_EAI 4968363466Sgshapiro else if (sm_strcasecmp(kp, "smtputf8") == 0) 4969363466Sgshapiro { 4970363466Sgshapiro if (!bitset(SRV_OFFER_EAI, e->e_features)) 4971363466Sgshapiro { 4972363466Sgshapiro usrerr("504 5.7.0 Sorry, SMTPUTF8 not supported/enabled"); 4973363466Sgshapiro /* NOTREACHED */ 4974363466Sgshapiro } 4975363466Sgshapiro e->e_smtputf8 = true; 4976363466Sgshapiro } 4977363466Sgshapiro#endif 497838032Speter else 497938032Speter { 498066494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 498138032Speter /* NOTREACHED */ 498238032Speter } 498338032Speter} 4984168515Sgshapiro 498590792Sgshapiro/* 498638032Speter** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 498738032Speter** 498838032Speter** Parameters: 498938032Speter** a -- the address corresponding to the To: parameter. 499038032Speter** kp -- the parameter key. 499138032Speter** vp -- the value of that parameter. 499238032Speter** e -- the envelope. 499338032Speter** 499438032Speter** Returns: 499538032Speter** none. 499638032Speter*/ 499738032Speter 4998168515Sgshapirovoid 4999168515Sgshapirorcpt_esmtp_args(a, kp, vp, e) 500038032Speter ADDRESS *a; 500138032Speter char *kp; 500238032Speter char *vp; 500338032Speter ENVELOPE *e; 500438032Speter{ 500590792Sgshapiro if (sm_strcasecmp(kp, "notify") == 0) 500638032Speter { 500738032Speter char *p; 500838032Speter 5009168515Sgshapiro if (!bitset(SRV_OFFER_DSN, e->e_features)) 501064562Sgshapiro { 501164562Sgshapiro usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN"); 501264562Sgshapiro /* NOTREACHED */ 501364562Sgshapiro } 501438032Speter if (vp == NULL) 501538032Speter { 501664562Sgshapiro usrerr("501 5.5.2 NOTIFY requires a value"); 501738032Speter /* NOTREACHED */ 501838032Speter } 501938032Speter a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 502038032Speter a->q_flags |= QHASNOTIFY; 502190792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp); 502264562Sgshapiro 502390792Sgshapiro if (sm_strcasecmp(vp, "never") == 0) 502438032Speter return; 502538032Speter for (p = vp; p != NULL; vp = p) 502638032Speter { 5027141858Sgshapiro char *s; 5028141858Sgshapiro 5029141858Sgshapiro s = p = strchr(p, ','); 503038032Speter if (p != NULL) 503138032Speter *p++ = '\0'; 503290792Sgshapiro if (sm_strcasecmp(vp, "success") == 0) 503338032Speter a->q_flags |= QPINGONSUCCESS; 503490792Sgshapiro else if (sm_strcasecmp(vp, "failure") == 0) 503538032Speter a->q_flags |= QPINGONFAILURE; 503690792Sgshapiro else if (sm_strcasecmp(vp, "delay") == 0) 503738032Speter a->q_flags |= QPINGONDELAY; 503838032Speter else 503938032Speter { 504064562Sgshapiro usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY", 504138032Speter vp); 504238032Speter /* NOTREACHED */ 504338032Speter } 5044141858Sgshapiro if (s != NULL) 5045141858Sgshapiro *s = ','; 504638032Speter } 504738032Speter } 504890792Sgshapiro else if (sm_strcasecmp(kp, "orcpt") == 0) 504938032Speter { 5050249729Sgshapiro char *p; 5051249729Sgshapiro 5052168515Sgshapiro if (!bitset(SRV_OFFER_DSN, e->e_features)) 505364562Sgshapiro { 505464562Sgshapiro usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN"); 505564562Sgshapiro /* NOTREACHED */ 505664562Sgshapiro } 505738032Speter if (vp == NULL) 505838032Speter { 505964562Sgshapiro usrerr("501 5.5.2 ORCPT requires a value"); 506038032Speter /* NOTREACHED */ 506138032Speter } 5062249729Sgshapiro if (a->q_orcpt != NULL) 506338032Speter { 5064249729Sgshapiro usrerr("501 5.5.0 Duplicate ORCPT parameter"); 5065249729Sgshapiro /* NOTREACHED */ 5066249729Sgshapiro } 5067249729Sgshapiro p = strchr(vp, ';'); 5068249729Sgshapiro if (p == NULL) 5069249729Sgshapiro { 507064562Sgshapiro usrerr("501 5.5.4 Syntax error in ORCPT parameter value"); 507138032Speter /* NOTREACHED */ 507238032Speter } 5073249729Sgshapiro *p = '\0'; 5074249729Sgshapiro if (!isatom(vp) || !xtextok(p + 1)) 507538032Speter { 5076249729Sgshapiro *p = ';'; 5077249729Sgshapiro usrerr("501 5.5.4 Syntax error in ORCPT parameter value"); 507838032Speter /* NOTREACHED */ 507938032Speter } 5080249729Sgshapiro *p = ';'; 508190792Sgshapiro a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp); 508238032Speter } 508338032Speter else 508438032Speter { 508566494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 508638032Speter /* NOTREACHED */ 508738032Speter } 508838032Speter} 508990792Sgshapiro/* 509038032Speter** PRINTVRFYADDR -- print an entry in the verify queue 509138032Speter** 509238032Speter** Parameters: 509390792Sgshapiro** a -- the address to print. 509438032Speter** last -- set if this is the last one. 509538032Speter** vrfy -- set if this is a VRFY command. 509638032Speter** 509738032Speter** Returns: 509838032Speter** none. 509938032Speter** 510038032Speter** Side Effects: 510138032Speter** Prints the appropriate 250 codes. 510238032Speter*/ 510364562Sgshapiro#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */ 510438032Speter 510564562Sgshapirostatic void 510638032Speterprintvrfyaddr(a, last, vrfy) 510738032Speter register ADDRESS *a; 510838032Speter bool last; 510938032Speter bool vrfy; 511038032Speter{ 511164562Sgshapiro char fmtbuf[30]; 511238032Speter 511338032Speter if (vrfy && a->q_mailer != NULL && 511438032Speter !bitnset(M_VRFY250, a->q_mailer->m_flags)) 5115168515Sgshapiro (void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf)); 511638032Speter else 5117168515Sgshapiro (void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf)); 511838032Speter fmtbuf[3] = last ? ' ' : '-'; 5119168515Sgshapiro (void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4); 512038032Speter if (a->q_fullname == NULL) 512138032Speter { 512264562Sgshapiro if ((a->q_mailer == NULL || 512364562Sgshapiro a->q_mailer->m_addrtype == NULL || 512490792Sgshapiro sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 512564562Sgshapiro strchr(a->q_user, '@') == NULL) 512690792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>", 5127168515Sgshapiro sizeof(fmtbuf) - OFFF); 512838032Speter else 512990792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "<%s>", 5130168515Sgshapiro sizeof(fmtbuf) - OFFF); 513138032Speter message(fmtbuf, a->q_user, MyHostName); 513238032Speter } 513338032Speter else 513438032Speter { 513564562Sgshapiro if ((a->q_mailer == NULL || 513664562Sgshapiro a->q_mailer->m_addrtype == NULL || 513790792Sgshapiro sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 513864562Sgshapiro strchr(a->q_user, '@') == NULL) 513990792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>", 5140168515Sgshapiro sizeof(fmtbuf) - OFFF); 514138032Speter else 514290792Sgshapiro (void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>", 5143168515Sgshapiro sizeof(fmtbuf) - OFFF); 514438032Speter message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 514538032Speter } 514638032Speter} 514738032Speter 514890792Sgshapiro#if SASL 514990792Sgshapiro/* 515064562Sgshapiro** SASLMECHS -- get list of possible AUTH mechanisms 515164562Sgshapiro** 515264562Sgshapiro** Parameters: 515390792Sgshapiro** conn -- SASL connection info. 515490792Sgshapiro** mechlist -- output parameter for list of mechanisms. 515564562Sgshapiro** 515664562Sgshapiro** Returns: 515790792Sgshapiro** number of mechs. 515864562Sgshapiro*/ 515964562Sgshapiro 516064562Sgshapirostatic int 516164562Sgshapirosaslmechs(conn, mechlist) 516264562Sgshapiro sasl_conn_t *conn; 516364562Sgshapiro char **mechlist; 516464562Sgshapiro{ 516564562Sgshapiro int len, num, result; 516664562Sgshapiro 516764562Sgshapiro /* "user" is currently unused */ 516898121Sgshapiro# if SASL >= 20000 516998121Sgshapiro result = sasl_listmech(conn, NULL, 517098121Sgshapiro "", " ", "", (const char **) mechlist, 5171120256Sgshapiro (unsigned int *)&len, &num); 517298121Sgshapiro# else /* SASL >= 20000 */ 517364562Sgshapiro result = sasl_listmech(conn, "user", /* XXX */ 517464562Sgshapiro "", " ", "", mechlist, 517590792Sgshapiro (unsigned int *)&len, (unsigned int *)&num); 517698121Sgshapiro# endif /* SASL >= 20000 */ 517790792Sgshapiro if (result != SASL_OK) 517864562Sgshapiro { 517990792Sgshapiro if (LogLevel > 9) 518090792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 518190792Sgshapiro "AUTH error: listmech=%d, num=%d", 518290792Sgshapiro result, num); 518390792Sgshapiro num = 0; 518490792Sgshapiro } 518590792Sgshapiro if (num > 0) 518690792Sgshapiro { 518764562Sgshapiro if (LogLevel > 11) 518864562Sgshapiro sm_syslog(LOG_INFO, NOQID, 518990792Sgshapiro "AUTH: available mech=%s, allowed mech=%s", 519064562Sgshapiro *mechlist, AuthMechanisms); 519190792Sgshapiro *mechlist = intersect(AuthMechanisms, *mechlist, NULL); 519264562Sgshapiro } 519364562Sgshapiro else 519464562Sgshapiro { 519590792Sgshapiro *mechlist = NULL; /* be paranoid... */ 519690792Sgshapiro if (result == SASL_OK && LogLevel > 9) 519764562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 519890792Sgshapiro "AUTH warning: no mechanisms"); 519964562Sgshapiro } 520064562Sgshapiro return num; 520164562Sgshapiro} 520298121Sgshapiro 520398121Sgshapiro# if SASL >= 20000 520490792Sgshapiro/* 520564562Sgshapiro** PROXY_POLICY -- define proxy policy for AUTH 520664562Sgshapiro** 520764562Sgshapiro** Parameters: 520898121Sgshapiro** conn -- unused. 520990792Sgshapiro** context -- unused. 521098121Sgshapiro** requested_user -- authorization identity. 521198121Sgshapiro** rlen -- authorization identity length. 521290792Sgshapiro** auth_identity -- authentication identity. 521398121Sgshapiro** alen -- authentication identity length. 521498121Sgshapiro** def_realm -- default user realm. 521598121Sgshapiro** urlen -- user realm length. 521698121Sgshapiro** propctx -- unused. 521798121Sgshapiro** 521898121Sgshapiro** Returns: 521998121Sgshapiro** ok? 522098121Sgshapiro** 522198121Sgshapiro** Side Effects: 522298121Sgshapiro** sets {auth_authen} macro. 522398121Sgshapiro*/ 522498121Sgshapiro 522598121Sgshapiroint 522698121Sgshapiroproxy_policy(conn, context, requested_user, rlen, auth_identity, alen, 522798121Sgshapiro def_realm, urlen, propctx) 522898121Sgshapiro sasl_conn_t *conn; 522998121Sgshapiro void *context; 523098121Sgshapiro const char *requested_user; 523198121Sgshapiro unsigned rlen; 523298121Sgshapiro const char *auth_identity; 523398121Sgshapiro unsigned alen; 523498121Sgshapiro const char *def_realm; 523598121Sgshapiro unsigned urlen; 523698121Sgshapiro struct propctx *propctx; 523798121Sgshapiro{ 523898121Sgshapiro if (auth_identity == NULL) 523998121Sgshapiro return SASL_FAIL; 524098121Sgshapiro 524198121Sgshapiro macdefine(&BlankEnvelope.e_macro, A_TEMP, 5242223067Sgshapiro macid("{auth_authen}"), 5243223067Sgshapiro xtextify((char *) auth_identity, "=<>\")")); 524498121Sgshapiro 524598121Sgshapiro return SASL_OK; 524698121Sgshapiro} 524798121Sgshapiro# else /* SASL >= 20000 */ 524898121Sgshapiro 524998121Sgshapiro/* 525098121Sgshapiro** PROXY_POLICY -- define proxy policy for AUTH 525198121Sgshapiro** 525298121Sgshapiro** Parameters: 525398121Sgshapiro** context -- unused. 525498121Sgshapiro** auth_identity -- authentication identity. 525590792Sgshapiro** requested_user -- authorization identity. 525690792Sgshapiro** user -- allowed user (output). 525790792Sgshapiro** errstr -- possible error string (output). 525864562Sgshapiro** 525964562Sgshapiro** Returns: 526064562Sgshapiro** ok? 526164562Sgshapiro*/ 526264562Sgshapiro 526364562Sgshapiroint 526464562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr) 526564562Sgshapiro void *context; 526664562Sgshapiro const char *auth_identity; 526764562Sgshapiro const char *requested_user; 526864562Sgshapiro const char **user; 526964562Sgshapiro const char **errstr; 527064562Sgshapiro{ 527164562Sgshapiro if (user == NULL || auth_identity == NULL) 527264562Sgshapiro return SASL_FAIL; 527364562Sgshapiro *user = newstr(auth_identity); 527464562Sgshapiro return SASL_OK; 527564562Sgshapiro} 527698121Sgshapiro# endif /* SASL >= 20000 */ 527790792Sgshapiro#endif /* SASL */ 527864562Sgshapiro 527990792Sgshapiro#if STARTTLS 528090792Sgshapiro/* 528190792Sgshapiro** INITSRVTLS -- initialize server side TLS 528264562Sgshapiro** 528364562Sgshapiro** Parameters: 528490792Sgshapiro** tls_ok -- should tls initialization be done? 528564562Sgshapiro** 528664562Sgshapiro** Returns: 528790792Sgshapiro** succeeded? 528864562Sgshapiro** 528964562Sgshapiro** Side Effects: 529090792Sgshapiro** sets tls_ok_srv which is a static variable in this module. 529190792Sgshapiro** Do NOT remove assignments to it! 529264562Sgshapiro*/ 529364562Sgshapiro 529466494Sgshapirobool 529590792Sgshapiroinitsrvtls(tls_ok) 529690792Sgshapiro bool tls_ok; 529764562Sgshapiro{ 529890792Sgshapiro if (!tls_ok) 529990792Sgshapiro return false; 530064562Sgshapiro 530190792Sgshapiro /* do NOT remove assignment */ 5302203004Sgshapiro tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, Srv_SSL_Options, true, 5303203004Sgshapiro SrvCertFile, SrvKeyFile, 5304203004Sgshapiro CACertPath, CACertFile, DHParams); 530590792Sgshapiro return tls_ok_srv; 530664562Sgshapiro} 530790792Sgshapiro#endif /* STARTTLS */ 530864562Sgshapiro/* 530990792Sgshapiro** SRVFEATURES -- get features for SMTP server 531064562Sgshapiro** 531164562Sgshapiro** Parameters: 531290792Sgshapiro** e -- envelope (should be session context). 531390792Sgshapiro** clientname -- name of client. 531490792Sgshapiro** features -- default features for this invocation. 531564562Sgshapiro** 531664562Sgshapiro** Returns: 531790792Sgshapiro** server features. 531864562Sgshapiro*/ 531964562Sgshapiro 532090792Sgshapiro/* table with options: it uses just one character, how about strings? */ 532190792Sgshapirostatic struct 532264562Sgshapiro{ 532390792Sgshapiro char srvf_opt; 532490792Sgshapiro unsigned int srvf_flag; 532590792Sgshapiro} srv_feat_table[] = 532664562Sgshapiro{ 532790792Sgshapiro { 'A', SRV_OFFER_AUTH }, 5328132943Sgshapiro { 'B', SRV_OFFER_VERB }, 5329132943Sgshapiro { 'C', SRV_REQ_SEC }, 5330132943Sgshapiro { 'D', SRV_OFFER_DSN }, 5331132943Sgshapiro { 'E', SRV_OFFER_ETRN }, 5332363466Sgshapiro#if _FFR_EAI 5333363466Sgshapiro { 'I', SRV_OFFER_EAI }, 5334363466Sgshapiro#endif 5335132943Sgshapiro { 'L', SRV_REQ_AUTH }, 533690792Sgshapiro#if PIPELINING 533790792Sgshapiro# if _FFR_NO_PIPE 533890792Sgshapiro { 'N', SRV_NO_PIPE }, 5339363466Sgshapiro# endif 534090792Sgshapiro { 'P', SRV_OFFER_PIPE }, 534190792Sgshapiro#endif /* PIPELINING */ 5342132943Sgshapiro { 'R', SRV_VRFY_CLT }, /* same as V; not documented */ 534390792Sgshapiro { 'S', SRV_OFFER_TLS }, 534490792Sgshapiro/* { 'T', SRV_TMP_FAIL }, */ 534590792Sgshapiro { 'V', SRV_VRFY_CLT }, 5346132943Sgshapiro { 'X', SRV_OFFER_EXPN }, 534790792Sgshapiro/* { 'Y', SRV_OFFER_VRFY }, */ 534890792Sgshapiro { '\0', SRV_NONE } 534990792Sgshapiro}; 535064562Sgshapiro 535190792Sgshapirostatic unsigned int 535290792Sgshapirosrvfeatures(e, clientname, features) 535390792Sgshapiro ENVELOPE *e; 535490792Sgshapiro char *clientname; 535590792Sgshapiro unsigned int features; 535677349Sgshapiro{ 535790792Sgshapiro int r, i, j; 535890792Sgshapiro char **pvp, c, opt; 535990792Sgshapiro char pvpbuf[PSBUFSIZE]; 536077349Sgshapiro 536190792Sgshapiro pvp = NULL; 536290792Sgshapiro r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf, 536390792Sgshapiro sizeof(pvpbuf)); 536490792Sgshapiro if (r != EX_OK) 536590792Sgshapiro return features; 536690792Sgshapiro if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) 536790792Sgshapiro return features; 536890792Sgshapiro if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0) 536990792Sgshapiro return SRV_TMP_FAIL; 537077349Sgshapiro 537164562Sgshapiro /* 537290792Sgshapiro ** General rule (see sendmail.h, d_flags): 537390792Sgshapiro ** lower case: required/offered, upper case: Not required/available 537490792Sgshapiro ** 537590792Sgshapiro ** Since we can change some features per daemon, we have both 537690792Sgshapiro ** cases here: turn on/off a feature. 537764562Sgshapiro */ 537864562Sgshapiro 537990792Sgshapiro for (i = 1; pvp[i] != NULL; i++) 538064562Sgshapiro { 538190792Sgshapiro c = pvp[i][0]; 538290792Sgshapiro j = 0; 538390792Sgshapiro for (;;) 538464562Sgshapiro { 538590792Sgshapiro if ((opt = srv_feat_table[j].srvf_opt) == '\0') 538664562Sgshapiro { 538790792Sgshapiro if (LogLevel > 9) 538890792Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 538990792Sgshapiro "srvfeatures: unknown feature %s", 539090792Sgshapiro pvp[i]); 539190792Sgshapiro break; 539264562Sgshapiro } 539390792Sgshapiro if (c == opt) 539464562Sgshapiro { 539590792Sgshapiro features &= ~(srv_feat_table[j].srvf_flag); 539690792Sgshapiro break; 539764562Sgshapiro } 539890792Sgshapiro if (c == tolower(opt)) 539964562Sgshapiro { 540090792Sgshapiro features |= srv_feat_table[j].srvf_flag; 540190792Sgshapiro break; 540264562Sgshapiro } 540390792Sgshapiro ++j; 540464562Sgshapiro } 540564562Sgshapiro } 540690792Sgshapiro return features; 540764562Sgshapiro} 540864562Sgshapiro 540990792Sgshapiro/* 541038032Speter** HELP -- implement the HELP command. 541138032Speter** 541238032Speter** Parameters: 541338032Speter** topic -- the topic we want help for. 541490792Sgshapiro** e -- envelope. 541538032Speter** 541638032Speter** Returns: 541738032Speter** none. 541838032Speter** 541938032Speter** Side Effects: 542038032Speter** outputs the help file to message output. 542138032Speter*/ 542264562Sgshapiro#define HELPVSTR "#vers " 542364562Sgshapiro#define HELPVERSION 2 542438032Speter 542538032Spetervoid 542664562Sgshapirohelp(topic, e) 542738032Speter char *topic; 542864562Sgshapiro ENVELOPE *e; 542938032Speter{ 543090792Sgshapiro register SM_FILE_T *hf; 543164562Sgshapiro register char *p; 543238032Speter int len; 543338032Speter bool noinfo; 543490792Sgshapiro bool first = true; 543564562Sgshapiro long sff = SFF_OPENASROOT|SFF_REGONLY; 543638032Speter char buf[MAXLINE]; 543764562Sgshapiro char inp[MAXLINE]; 543864562Sgshapiro static int foundvers = -1; 543938032Speter extern char Version[]; 544038032Speter 544138032Speter if (DontLockReadFiles) 544238032Speter sff |= SFF_NOLOCK; 544364562Sgshapiro if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail)) 544438032Speter sff |= SFF_SAFEDIRPATH; 544538032Speter 544638032Speter if (HelpFile == NULL || 544738032Speter (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL) 544838032Speter { 544938032Speter /* no help */ 545038032Speter errno = 0; 545164562Sgshapiro message("502 5.3.0 Sendmail %s -- HELP not implemented", 545264562Sgshapiro Version); 545338032Speter return; 545438032Speter } 545538032Speter 545638032Speter if (topic == NULL || *topic == '\0') 545738032Speter { 545838032Speter topic = "smtp"; 545990792Sgshapiro noinfo = false; 546038032Speter } 546138032Speter else 546238032Speter { 546338032Speter makelower(topic); 546490792Sgshapiro noinfo = true; 546538032Speter } 546638032Speter 546738032Speter len = strlen(topic); 546838032Speter 5469249729Sgshapiro while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 547038032Speter { 547164562Sgshapiro if (buf[0] == '#') 547264562Sgshapiro { 547364562Sgshapiro if (foundvers < 0 && 547464562Sgshapiro strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0) 547564562Sgshapiro { 547664562Sgshapiro int h; 547764562Sgshapiro 547890792Sgshapiro if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d", 547990792Sgshapiro &h) == 1) 548064562Sgshapiro foundvers = h; 548164562Sgshapiro } 548264562Sgshapiro continue; 548364562Sgshapiro } 548438032Speter if (strncmp(buf, topic, len) == 0) 548538032Speter { 548664562Sgshapiro if (first) 548764562Sgshapiro { 548890792Sgshapiro first = false; 548938032Speter 549064562Sgshapiro /* print version if no/old vers# in file */ 549164562Sgshapiro if (foundvers < 2 && !noinfo) 549264562Sgshapiro message("214-2.0.0 This is Sendmail version %s", Version); 549364562Sgshapiro } 549464562Sgshapiro p = strpbrk(buf, " \t"); 549538032Speter if (p == NULL) 549664562Sgshapiro p = buf + strlen(buf) - 1; 549738032Speter else 549838032Speter p++; 549990792Sgshapiro fixcrlf(p, true); 550064562Sgshapiro if (foundvers >= 2) 550164562Sgshapiro { 5502168515Sgshapiro char *lbp; 5503168515Sgshapiro int lbs = sizeof(buf) - (p - buf); 5504168515Sgshapiro 5505168515Sgshapiro lbp = translate_dollars(p, p, &lbs); 5506168515Sgshapiro expand(lbp, inp, sizeof(inp), e); 5507168515Sgshapiro if (p != lbp) 5508168515Sgshapiro sm_free(lbp); 550964562Sgshapiro p = inp; 551064562Sgshapiro } 551164562Sgshapiro message("214-2.0.0 %s", p); 551290792Sgshapiro noinfo = false; 551338032Speter } 551438032Speter } 551538032Speter 551638032Speter if (noinfo) 551764562Sgshapiro message("504 5.3.0 HELP topic \"%.10s\" unknown", topic); 551838032Speter else 551964562Sgshapiro message("214 2.0.0 End of HELP info"); 552064562Sgshapiro 552164562Sgshapiro if (foundvers != 0 && foundvers < HELPVERSION) 552264562Sgshapiro { 552364562Sgshapiro if (LogLevel > 1) 552464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 552564562Sgshapiro "%s too old (require version %d)", 552664562Sgshapiro HelpFile, HELPVERSION); 552764562Sgshapiro 552864562Sgshapiro /* avoid log next time */ 552964562Sgshapiro foundvers = 0; 553064562Sgshapiro } 553164562Sgshapiro 553290792Sgshapiro (void) sm_io_close(hf, SM_TIME_DEFAULT); 553338032Speter} 5534120256Sgshapiro 5535120256Sgshapiro#if SASL 5536120256Sgshapiro/* 5537120256Sgshapiro** RESET_SASLCONN -- reset SASL connection data 5538120256Sgshapiro** 5539120256Sgshapiro** Parameters: 5540120256Sgshapiro** conn -- SASL connection context 5541120256Sgshapiro** hostname -- host name 5542120256Sgshapiro** various connection data 5543120256Sgshapiro** 5544120256Sgshapiro** Returns: 5545120256Sgshapiro** SASL result 5546120256Sgshapiro*/ 5547120256Sgshapiro 5548120256Sgshapirostatic int 5549147078Sgshapiroreset_saslconn(sasl_conn_t **conn, char *hostname, 5550120256Sgshapiro# if SASL >= 20000 5551120256Sgshapiro char *remoteip, char *localip, 5552120256Sgshapiro char *auth_id, sasl_ssf_t * ext_ssf) 5553120256Sgshapiro# else /* SASL >= 20000 */ 5554147078Sgshapiro struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l, 5555120256Sgshapiro sasl_external_properties_t * ext_ssf) 5556120256Sgshapiro# endif /* SASL >= 20000 */ 5557120256Sgshapiro{ 5558120256Sgshapiro int result; 5559120256Sgshapiro 5560120256Sgshapiro sasl_dispose(conn); 5561120256Sgshapiro# if SASL >= 20000 5562120256Sgshapiro result = sasl_server_new("smtp", hostname, NULL, NULL, NULL, 5563120256Sgshapiro NULL, 0, conn); 5564120256Sgshapiro# elif SASL > 10505 5565120256Sgshapiro /* use empty realm: only works in SASL > 1.5.5 */ 5566120256Sgshapiro result = sasl_server_new("smtp", hostname, "", NULL, 0, conn); 5567120256Sgshapiro# else /* SASL >= 20000 */ 5568120256Sgshapiro /* use no realm -> realm is set to hostname by SASL lib */ 5569120256Sgshapiro result = sasl_server_new("smtp", hostname, NULL, NULL, 0, 5570120256Sgshapiro conn); 5571120256Sgshapiro# endif /* SASL >= 20000 */ 5572120256Sgshapiro if (result != SASL_OK) 5573120256Sgshapiro return result; 5574120256Sgshapiro 5575120256Sgshapiro# if SASL >= 20000 5576120256Sgshapiro# if NETINET || NETINET6 5577147078Sgshapiro if (remoteip != NULL && *remoteip != '\0') 5578120256Sgshapiro result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip); 5579120256Sgshapiro if (result != SASL_OK) 5580120256Sgshapiro return result; 5581120256Sgshapiro 5582147078Sgshapiro if (localip != NULL && *localip != '\0') 5583120256Sgshapiro result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip); 5584120256Sgshapiro if (result != SASL_OK) 5585120256Sgshapiro return result; 5586120256Sgshapiro# endif /* NETINET || NETINET6 */ 5587120256Sgshapiro 5588120256Sgshapiro result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf); 5589120256Sgshapiro if (result != SASL_OK) 5590120256Sgshapiro return result; 5591120256Sgshapiro 5592120256Sgshapiro result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id); 5593120256Sgshapiro if (result != SASL_OK) 5594120256Sgshapiro return result; 5595120256Sgshapiro# else /* SASL >= 20000 */ 5596120256Sgshapiro# if NETINET 5597120256Sgshapiro if (saddr_r != NULL) 5598120256Sgshapiro result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r); 5599120256Sgshapiro if (result != SASL_OK) 5600120256Sgshapiro return result; 5601120256Sgshapiro 5602120256Sgshapiro if (saddr_l != NULL) 5603120256Sgshapiro result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l); 5604120256Sgshapiro if (result != SASL_OK) 5605120256Sgshapiro return result; 5606120256Sgshapiro# endif /* NETINET */ 5607120256Sgshapiro 5608120256Sgshapiro result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf); 5609120256Sgshapiro if (result != SASL_OK) 5610120256Sgshapiro return result; 5611120256Sgshapiro# endif /* SASL >= 20000 */ 5612120256Sgshapiro return SASL_OK; 5613120256Sgshapiro} 5614363466Sgshapiro 5615363466Sgshapiro/* 5616363466Sgshapiro** GET_SASL_USER -- extract user part from SASL reply 5617363466Sgshapiro** 5618363466Sgshapiro** Parameters: 5619363466Sgshapiro** val -- sasl reply (may contain NUL) 5620363466Sgshapiro** len -- length of val 5621363466Sgshapiro** auth_type -- auth_type (can be NULL) 5622363466Sgshapiro** user -- output buffer for extract user 5623363466Sgshapiro** user_len -- length of output buffer (user) 5624363466Sgshapiro** 5625363466Sgshapiro** Returns: 5626363466Sgshapiro** none. 5627363466Sgshapiro** 5628363466Sgshapiro** Note: val is supplied by the client and hence may contain "bad" 5629363466Sgshapiro** (non-printable) characters, but the returned value (user) 5630363466Sgshapiro** is only used for logging which converts those characters. 5631363466Sgshapiro*/ 5632363466Sgshapiro 5633363466Sgshapirostatic void 5634363466Sgshapiroget_sasl_user(val, len, auth_type, user, user_len) 5635363466Sgshapiro char *val; 5636363466Sgshapiro unsigned int len; 5637363466Sgshapiro const char *auth_type; 5638363466Sgshapiro char *user; 5639363466Sgshapiro size_t user_len; 5640363466Sgshapiro{ 5641363466Sgshapiro unsigned int u; 5642363466Sgshapiro 5643363466Sgshapiro SM_ASSERT(val != NULL); 5644363466Sgshapiro SM_ASSERT(user != NULL); 5645363466Sgshapiro SM_ASSERT(user_len > 0); 5646363466Sgshapiro 5647363466Sgshapiro *user = '\0'; 5648363466Sgshapiro if (NULL == auth_type || '\0' == *auth_type) 5649363466Sgshapiro return; 5650363466Sgshapiro if (0 == len) 5651363466Sgshapiro return; 5652363466Sgshapiro 5653363466Sgshapiro# define DIGMD5U "username=\"" 5654363466Sgshapiro# define DIGMD5U_L (sizeof(DIGMD5U) - 1) 5655363466Sgshapiro if (sm_strcasecmp(auth_type, "digest-md5") == 0 && 5656363466Sgshapiro strncmp(val, DIGMD5U, DIGMD5U_L) == 0) 5657363466Sgshapiro { 5658363466Sgshapiro char *s; 5659363466Sgshapiro 5660363466Sgshapiro val += DIGMD5U_L; 5661363466Sgshapiro if (len <= DIGMD5U_L) 5662363466Sgshapiro return; 5663363466Sgshapiro len -= DIGMD5U_L; 5664363466Sgshapiro 5665363466Sgshapiro /* format? could there be a quoted '"'? */ 5666363466Sgshapiro for (s = val, u = 0; *s != '\0' && u < len; s++) 5667363466Sgshapiro { 5668363466Sgshapiro if ('"' == *s) 5669363466Sgshapiro { 5670363466Sgshapiro *s = '\0'; 5671363466Sgshapiro break; 5672363466Sgshapiro } 5673363466Sgshapiro if ('\\' == *s) 5674363466Sgshapiro { 5675363466Sgshapiro ++s; 5676363466Sgshapiro if ('\0' == *s) 5677363466Sgshapiro break; 5678363466Sgshapiro } 5679363466Sgshapiro } 5680363466Sgshapiro } 5681363466Sgshapiro else if (sm_strcasecmp(auth_type, "cram-md5") == 0) 5682363466Sgshapiro { 5683363466Sgshapiro char *s; 5684363466Sgshapiro 5685363466Sgshapiro for (s = val, u = 0; *s != '\0' && u < len; s++) 5686363466Sgshapiro { 5687363466Sgshapiro if (' ' == *s) 5688363466Sgshapiro { 5689363466Sgshapiro *s = '\0'; 5690363466Sgshapiro break; 5691363466Sgshapiro } 5692363466Sgshapiro } 5693363466Sgshapiro } 5694363466Sgshapiro 5695363466Sgshapiro else if (sm_strcasecmp(auth_type, "plain") == 0 || 5696363466Sgshapiro sm_strcasecmp(auth_type, "login") == 0) 5697363466Sgshapiro { 5698363466Sgshapiro /* 5699363466Sgshapiro ** RFC 4616: The PLAIN Simple Authentication and 5700363466Sgshapiro ** Security Layer (SASL) Mechanism 5701363466Sgshapiro ** message = [authzid] UTF8NUL authcid UTF8NUL passwd 5702363466Sgshapiro ** each part: 1*SAFE ; MUST accept up to 255 octets 5703363466Sgshapiro ** UTF8NUL = %x00 ; UTF-8 encoded NUL character 5704363466Sgshapiro ** 5705363466Sgshapiro ** draft-murchison-sasl-login: it's just username by its own 5706363466Sgshapiro */ 5707363466Sgshapiro 5708363466Sgshapiro for (u = 0; u < len; u++) 5709363466Sgshapiro { 5710363466Sgshapiro if (val[u] == '\0') 5711363466Sgshapiro { 5712363466Sgshapiro val[u] = '/'; 5713363466Sgshapiro (void) sm_strlcpy(user, 5714363466Sgshapiro val + ((0 == u) ? 1 : 0), 5715363466Sgshapiro user_len); 5716363466Sgshapiro return; 5717363466Sgshapiro } 5718363466Sgshapiro } 5719363466Sgshapiro } 5720363466Sgshapiro else 5721363466Sgshapiro { 5722363466Sgshapiro /* 5723363466Sgshapiro ** Extracting the "user" from other mechanisms 5724363466Sgshapiro ** is currently not supported. 5725363466Sgshapiro */ 5726363466Sgshapiro 5727363466Sgshapiro return; 5728363466Sgshapiro } 5729363466Sgshapiro 5730363466Sgshapiro /* 5731363466Sgshapiro ** Does the input buffer has an NUL in it so it can be treated 5732363466Sgshapiro ** as a C string? 5733363466Sgshapiro */ 5734363466Sgshapiro 5735363466Sgshapiro /* SM_ASSERT(len > 0); see above */ 5736363466Sgshapiro u = len - 1; 5737363466Sgshapiro if (val[u] != '\0') 5738363466Sgshapiro { 5739363466Sgshapiro for (u = 0; u < len; u++) 5740363466Sgshapiro { 5741363466Sgshapiro if (val[u] == '\0') 5742363466Sgshapiro break; 5743363466Sgshapiro } 5744363466Sgshapiro } 5745363466Sgshapiro if (val[u] != '\0') 5746363466Sgshapiro user_len = SM_MIN(len, user_len); 5747363466Sgshapiro 5748363466Sgshapiro (void) sm_strlcpy(user, val, user_len); 5749363466Sgshapiro} 5750120256Sgshapiro#endif /* SASL */ 5751