srvrsmtp.c revision 66494
138032Speter/* 264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1438032Speter 1564562Sgshapiro#include <sendmail.h> 1664562Sgshapiro 1738032Speter#ifndef lint 1864562Sgshapiro# if SMTP 1966494Sgshapirostatic char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (with SMTP)"; 2064562Sgshapiro# else /* SMTP */ 2166494Sgshapirostatic char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (without SMTP)"; 2264562Sgshapiro# endif /* SMTP */ 2364562Sgshapiro#endif /* ! lint */ 2464562Sgshapiro 2538032Speter#if SMTP 2666494Sgshapiro# if SASL || STARTTLS 2766494Sgshapiro# include "sfsasl.h" 2866494Sgshapiro# endif /* SASL || STARTTLS */ 2964562Sgshapiro# if SASL 3064562Sgshapiro# define ENC64LEN(l) (((l) + 2) * 4 / 3 + 1) 3164562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **)); 3264562Sgshapiro# endif /* SASL */ 3364562Sgshapiro# if STARTTLS 3464562Sgshapiro# include <sysexits.h> 3564562Sgshapiro# include <openssl/err.h> 3664562Sgshapiro# include <openssl/bio.h> 3764562Sgshapiro# include <openssl/pem.h> 3864562Sgshapiro# ifndef HASURANDOMDEV 3964562Sgshapiro# include <openssl/rand.h> 4064562Sgshapiro# endif /* !HASURANDOMDEV */ 4138032Speter 4264562Sgshapirostatic SSL *srv_ssl = NULL; 4364562Sgshapirostatic SSL_CTX *srv_ctx = NULL; 4464562Sgshapiro# if !TLS_NO_RSA 4564562Sgshapirostatic RSA *rsa = NULL; 4664562Sgshapiro# endif /* !TLS_NO_RSA */ 4764562Sgshapirostatic bool tls_ok = FALSE; 4864562Sgshapirostatic int tls_verify_cb __P((X509_STORE_CTX *)); 4964562Sgshapiro# if !TLS_NO_RSA 5064562Sgshapiro# define RSA_KEYLENGTH 512 5164562Sgshapiro# endif /* !TLS_NO_RSA */ 5264562Sgshapiro# endif /* STARTTLS */ 5338032Speter 5464562Sgshapirostatic time_t checksmtpattack __P((volatile int *, int, bool, 5564562Sgshapiro char *, ENVELOPE *)); 5664562Sgshapirostatic void mail_esmtp_args __P((char *, char *, ENVELOPE *)); 5764562Sgshapirostatic void printvrfyaddr __P((ADDRESS *, bool, bool)); 5864562Sgshapirostatic void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); 5964562Sgshapirostatic int runinchild __P((char *, ENVELOPE *)); 6064562Sgshapirostatic char *skipword __P((char *volatile, char *)); 6164562Sgshapiroextern ENVELOPE BlankEnvelope; 6238032Speter 6338032Speter/* 6438032Speter** SMTP -- run the SMTP protocol. 6538032Speter** 6638032Speter** Parameters: 6738032Speter** nullserver -- if non-NULL, rejection message for 6838032Speter** all SMTP commands. 6938032Speter** e -- the envelope. 7038032Speter** 7138032Speter** Returns: 7238032Speter** never. 7338032Speter** 7438032Speter** Side Effects: 7538032Speter** Reads commands from the input channel and processes 7638032Speter** them. 7738032Speter*/ 7838032Speter 7938032Speterstruct cmd 8038032Speter{ 8164562Sgshapiro char *cmd_name; /* command name */ 8264562Sgshapiro int cmd_code; /* internal code, see below */ 8338032Speter}; 8438032Speter 8564562Sgshapiro/* values for cmd_code */ 8638032Speter# define CMDERROR 0 /* bad command */ 8738032Speter# define CMDMAIL 1 /* mail -- designate sender */ 8838032Speter# define CMDRCPT 2 /* rcpt -- designate recipient */ 8938032Speter# define CMDDATA 3 /* data -- send message text */ 9038032Speter# define CMDRSET 4 /* rset -- reset state */ 9138032Speter# define CMDVRFY 5 /* vrfy -- verify address */ 9238032Speter# define CMDEXPN 6 /* expn -- expand address */ 9338032Speter# define CMDNOOP 7 /* noop -- do nothing */ 9438032Speter# define CMDQUIT 8 /* quit -- close connection and die */ 9538032Speter# define CMDHELO 9 /* helo -- be polite */ 9638032Speter# define CMDHELP 10 /* help -- give usage info */ 9738032Speter# define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ 9838032Speter# define CMDETRN 12 /* etrn -- flush queue */ 9964562Sgshapiro# if SASL 10064562Sgshapiro# define CMDAUTH 13 /* auth -- SASL authenticate */ 10164562Sgshapiro# endif /* SASL */ 10264562Sgshapiro# if STARTTLS 10364562Sgshapiro# define CMDSTLS 14 /* STARTTLS -- start TLS session */ 10464562Sgshapiro# endif /* STARTTLS */ 10538032Speter/* non-standard commands */ 10638032Speter# define CMDONEX 16 /* onex -- sending one transaction only */ 10738032Speter# define CMDVERB 17 /* verb -- go into verbose mode */ 10838032Speter# define CMDXUSR 18 /* xusr -- initial (user) submission */ 10964562Sgshapiro/* unimplemented commands from RFC 821 */ 11064562Sgshapiro# define CMDUNIMPL 19 /* unimplemented rfc821 commands */ 11138032Speter/* use this to catch and log "door handle" attempts on your system */ 11238032Speter# define CMDLOGBOGUS 23 /* bogus command that should be logged */ 11338032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */ 11438032Speter# define CMDDBGQSHOW 24 /* showq -- show send queue */ 11538032Speter# define CMDDBGDEBUG 25 /* debug -- set debug mode */ 11638032Speter 11766494Sgshapiro/* 11866494Sgshapiro** Note: If you change this list, 11966494Sgshapiro** remember to update 'helpfile' 12066494Sgshapiro*/ 12166494Sgshapiro 12238032Speterstatic struct cmd CmdTab[] = 12338032Speter{ 12438032Speter { "mail", CMDMAIL }, 12538032Speter { "rcpt", CMDRCPT }, 12638032Speter { "data", CMDDATA }, 12738032Speter { "rset", CMDRSET }, 12838032Speter { "vrfy", CMDVRFY }, 12938032Speter { "expn", CMDEXPN }, 13038032Speter { "help", CMDHELP }, 13138032Speter { "noop", CMDNOOP }, 13238032Speter { "quit", CMDQUIT }, 13338032Speter { "helo", CMDHELO }, 13438032Speter { "ehlo", CMDEHLO }, 13538032Speter { "etrn", CMDETRN }, 13638032Speter { "verb", CMDVERB }, 13738032Speter { "onex", CMDONEX }, 13838032Speter { "xusr", CMDXUSR }, 13964562Sgshapiro { "send", CMDUNIMPL }, 14064562Sgshapiro { "saml", CMDUNIMPL }, 14164562Sgshapiro { "soml", CMDUNIMPL }, 14264562Sgshapiro { "turn", CMDUNIMPL }, 14364562Sgshapiro# if SASL 14464562Sgshapiro { "auth", CMDAUTH, }, 14564562Sgshapiro# endif /* SASL */ 14664562Sgshapiro# if STARTTLS 14764562Sgshapiro { "starttls", CMDSTLS, }, 14864562Sgshapiro# endif /* STARTTLS */ 14938032Speter /* remaining commands are here only to trap and log attempts to use them */ 15038032Speter { "showq", CMDDBGQSHOW }, 15138032Speter { "debug", CMDDBGDEBUG }, 15238032Speter { "wiz", CMDLOGBOGUS }, 15338032Speter 15438032Speter { NULL, CMDERROR } 15538032Speter}; 15638032Speter 15764562Sgshapirostatic bool OneXact = FALSE; /* one xaction only this run */ 15864562Sgshapirostatic char *CurSmtpClient; /* who's at the other end of channel */ 15938032Speter 16064562Sgshapiro# define MAXBADCOMMANDS 25 /* maximum number of bad commands */ 16164562Sgshapiro# define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ 16264562Sgshapiro# define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ 16364562Sgshapiro# define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ 16464562Sgshapiro# define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ 16564562Sgshapiro# define MAXTIMEOUT (4 * 60) /* max timeout for bad commands */ 16638032Speter 16764562Sgshapiro/* runinchild() returns */ 16864562Sgshapiro# define RIC_INCHILD 0 /* in a child process */ 16964562Sgshapiro# define RIC_INPARENT 1 /* still in parent process */ 17064562Sgshapiro# define RIC_TEMPFAIL 2 /* temporary failure occurred */ 17138032Speter 17238032Spetervoid 17364562Sgshapirosmtp(nullserver, d_flags, e) 17464562Sgshapiro char *volatile nullserver; 17564562Sgshapiro BITMAP256 d_flags; 17638032Speter register ENVELOPE *volatile e; 17738032Speter{ 17838032Speter register char *volatile p; 17964562Sgshapiro register struct cmd *volatile c = NULL; 18038032Speter char *cmd; 18138032Speter auto ADDRESS *vrfyqueue; 18238032Speter ADDRESS *a; 18338032Speter volatile bool gotmail; /* mail command received */ 18438032Speter volatile bool gothello; /* helo command received */ 18538032Speter bool vrfy; /* set if this is a vrfy command */ 18638032Speter char *volatile protocol; /* sending protocol */ 18738032Speter char *volatile sendinghost; /* sending hostname */ 18838032Speter char *volatile peerhostname; /* name of SMTP peer or "localhost" */ 18938032Speter auto char *delimptr; 19038032Speter char *id; 19138032Speter volatile int nrcpts = 0; /* number of RCPT commands */ 19264562Sgshapiro int ric; 19338032Speter bool doublequeue; 19442575Speter volatile bool discard; 19538032Speter volatile int badcommands = 0; /* count of bad commands */ 19638032Speter volatile int nverifies = 0; /* count of VRFY/EXPN commands */ 19738032Speter volatile int n_etrn = 0; /* count of ETRN commands */ 19838032Speter volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */ 19938032Speter volatile int n_helo = 0; /* count of HELO/EHLO commands */ 20064562Sgshapiro volatile int delay = 1; /* timeout for bad commands */ 20138032Speter bool ok; 20264562Sgshapiro volatile bool tempfail = FALSE; 20364562Sgshapiro# if _FFR_MILTER 20464562Sgshapiro volatile bool milterize = (nullserver == NULL); 20564562Sgshapiro# endif /* _FFR_MILTER */ 20664562Sgshapiro volatile time_t wt; /* timeout after too many commands */ 20764562Sgshapiro volatile time_t previous; /* time after checksmtpattack() */ 20864562Sgshapiro volatile bool lognullconnection = TRUE; 20938032Speter register char *q; 21064562Sgshapiro char *addr; 21164562Sgshapiro char *greetcode = "220"; 21238032Speter QUEUE_CHAR *new; 21364562Sgshapiro int argno; 21464562Sgshapiro char *args[MAXSMTPARGS]; 21538032Speter char inp[MAXLINE]; 21638032Speter char cmdbuf[MAXLINE]; 21764562Sgshapiro# if SASL 21864562Sgshapiro sasl_conn_t *conn; 21964562Sgshapiro volatile bool sasl_ok; 22064562Sgshapiro volatile int n_auth = 0; /* count of AUTH commands */ 22164562Sgshapiro bool ismore; 22264562Sgshapiro int result; 22364562Sgshapiro volatile int authenticating; 22464562Sgshapiro char *hostname; 22564562Sgshapiro char *user; 22664562Sgshapiro char *in, *out, *out2; 22764562Sgshapiro const char *errstr; 22864562Sgshapiro int inlen, out2len; 22964562Sgshapiro unsigned int outlen; 23064562Sgshapiro char *volatile auth_type; 23164562Sgshapiro char *mechlist; 23264562Sgshapiro volatile int n_mechs; 23364562Sgshapiro int len; 23464562Sgshapiro sasl_security_properties_t ssp; 23564562Sgshapiro sasl_external_properties_t ext_ssf; 23664562Sgshapiro# if SFIO 23764562Sgshapiro sasl_ssf_t *ssf; 23864562Sgshapiro# endif /* SFIO */ 23964562Sgshapiro# endif /* SASL */ 24064562Sgshapiro# if STARTTLS 24164562Sgshapiro int r; 24266494Sgshapiro int rfd, wfd; 24364562Sgshapiro volatile bool usetls = TRUE; 24464562Sgshapiro volatile bool tls_active = FALSE; 24564562Sgshapiro bool saveQuickAbort; 24664562Sgshapiro bool saveSuprErrs; 24764562Sgshapiro# endif /* STARTTLS */ 24838032Speter 24938032Speter if (fileno(OutChannel) != fileno(stdout)) 25038032Speter { 25138032Speter /* arrange for debugging output to go to remote host */ 25238032Speter (void) dup2(fileno(OutChannel), fileno(stdout)); 25338032Speter } 25464562Sgshapiro 25538032Speter settime(e); 25664562Sgshapiro (void)sm_getla(e); 25738032Speter peerhostname = RealHostName; 25838032Speter if (peerhostname == NULL) 25938032Speter peerhostname = "localhost"; 26038032Speter CurHostName = peerhostname; 26138032Speter CurSmtpClient = macvalue('_', e); 26238032Speter if (CurSmtpClient == NULL) 26338032Speter CurSmtpClient = CurHostName; 26438032Speter 26538032Speter /* check_relay may have set discard bit, save for later */ 26638032Speter discard = bitset(EF_DISCARD, e->e_flags); 26738032Speter 26864562Sgshapiro sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient); 26964562Sgshapiro 27064562Sgshapiro# if SASL 27164562Sgshapiro sasl_ok = FALSE; /* SASL can't be used (yet) */ 27264562Sgshapiro n_mechs = 0; 27364562Sgshapiro 27464562Sgshapiro /* SASL server new connection */ 27564562Sgshapiro hostname = macvalue('j', e); 27664562Sgshapiro# if SASL > 10505 27764562Sgshapiro /* use empty realm: only works in SASL > 1.5.5 */ 27864562Sgshapiro result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn); 27964562Sgshapiro# else /* SASL > 10505 */ 28064562Sgshapiro /* use no realm -> realm is set to hostname by SASL lib */ 28164562Sgshapiro result = sasl_server_new("smtp", hostname, NULL, NULL, 0, &conn); 28264562Sgshapiro# endif /* SASL > 10505 */ 28364562Sgshapiro if (result == SASL_OK) 28438032Speter { 28564562Sgshapiro sasl_ok = TRUE; 28664562Sgshapiro 28764562Sgshapiro /* 28864562Sgshapiro ** SASL set properties for sasl 28964562Sgshapiro ** set local/remote IP 29064562Sgshapiro ** XXX only IPv4: Cyrus SASL doesn't support anything else 29164562Sgshapiro ** 29264562Sgshapiro ** XXX where exactly are these used/required? 29364562Sgshapiro ** Kerberos_v4 29464562Sgshapiro */ 29564562Sgshapiro 29664562Sgshapiro# if NETINET 29764562Sgshapiro in = macvalue(macid("{daemon_family}", NULL), e); 29864562Sgshapiro if (in != NULL && strcmp(in, "inet") == 0) 29964562Sgshapiro { 30064562Sgshapiro SOCKADDR_LEN_T addrsize; 30164562Sgshapiro struct sockaddr_in saddr_l; 30264562Sgshapiro struct sockaddr_in saddr_r; 30364562Sgshapiro 30464562Sgshapiro addrsize = sizeof(struct sockaddr_in); 30564562Sgshapiro if (getpeername(fileno(InChannel), 30664562Sgshapiro (struct sockaddr *)&saddr_r, 30764562Sgshapiro &addrsize) == 0) 30864562Sgshapiro { 30964562Sgshapiro sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r); 31064562Sgshapiro addrsize = sizeof(struct sockaddr_in); 31164562Sgshapiro if (getsockname(fileno(InChannel), 31264562Sgshapiro (struct sockaddr *)&saddr_l, 31364562Sgshapiro &addrsize) == 0) 31464562Sgshapiro sasl_setprop(conn, SASL_IP_LOCAL, 31564562Sgshapiro &saddr_l); 31664562Sgshapiro } 31764562Sgshapiro } 31864562Sgshapiro# endif /* NETINET */ 31964562Sgshapiro 32064562Sgshapiro authenticating = SASL_NOT_AUTH; 32164562Sgshapiro auth_type = NULL; 32264562Sgshapiro mechlist = NULL; 32364562Sgshapiro user = NULL; 32464562Sgshapiro# if 0 32564562Sgshapiro define(macid("{auth_author}", NULL), NULL, &BlankEnvelope); 32664562Sgshapiro# endif /* 0 */ 32764562Sgshapiro 32864562Sgshapiro /* set properties */ 32964562Sgshapiro (void) memset(&ssp, '\0', sizeof ssp); 33064562Sgshapiro# if SFIO 33164562Sgshapiro /* XXX should these be options settable via .cf ? */ 33264562Sgshapiro /* ssp.min_ssf = 0; is default due to memset() */ 33364562Sgshapiro { 33464562Sgshapiro ssp.max_ssf = INT_MAX; 33564562Sgshapiro ssp.maxbufsize = MAXOUTLEN; 33664562Sgshapiro } 33764562Sgshapiro# endif /* SFIO */ 33864562Sgshapiro# if _FFR_SASL_OPTS 33964562Sgshapiro ssp.security_flags = SASLOpts & SASL_SEC_MASK; 34064562Sgshapiro# endif /* _FFR_SASL_OPTS */ 34164562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK; 34264562Sgshapiro 34364562Sgshapiro if (sasl_ok) 34464562Sgshapiro { 34564562Sgshapiro /* 34664562Sgshapiro ** external security strength factor; 34764562Sgshapiro ** we have none so zero 34864562Sgshapiro# if STARTTLS 34964562Sgshapiro ** we may have to change this for STARTTLS 35064562Sgshapiro ** (dynamically) 35164562Sgshapiro# endif 35264562Sgshapiro */ 35364562Sgshapiro ext_ssf.ssf = 0; 35464562Sgshapiro ext_ssf.auth_id = NULL; 35564562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, 35664562Sgshapiro &ext_ssf) == SASL_OK; 35764562Sgshapiro } 35864562Sgshapiro if (sasl_ok) 35964562Sgshapiro { 36064562Sgshapiro n_mechs = saslmechs(conn, &mechlist); 36164562Sgshapiro sasl_ok = n_mechs > 0; 36264562Sgshapiro } 36338032Speter } 36464562Sgshapiro else 36564562Sgshapiro { 36664562Sgshapiro if (LogLevel > 9) 36764562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 36864562Sgshapiro "SASL error: sasl_server_new failed=%d", 36964562Sgshapiro result); 37064562Sgshapiro } 37164562Sgshapiro# endif /* SASL */ 37238032Speter 37364562Sgshapiro# if STARTTLS 37464562Sgshapiro# if _FFR_TLS_O_T 37564562Sgshapiro saveQuickAbort = QuickAbort; 37664562Sgshapiro saveSuprErrs = SuprErrs; 37764562Sgshapiro SuprErrs = TRUE; 37864562Sgshapiro QuickAbort = FALSE; 37964562Sgshapiro if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8) != EX_OK 38064562Sgshapiro || Errors > 0) 38164562Sgshapiro usetls = FALSE; 38264562Sgshapiro QuickAbort = saveQuickAbort; 38364562Sgshapiro SuprErrs = saveSuprErrs; 38464562Sgshapiro# endif /* _FFR_TLS_O_T */ 38564562Sgshapiro# endif /* STARTTLS */ 38664562Sgshapiro 38764562Sgshapiro# if _FFR_MILTER 38864562Sgshapiro if (milterize) 38964562Sgshapiro { 39064562Sgshapiro char state; 39164562Sgshapiro 39264562Sgshapiro /* initialize mail filter connection */ 39364562Sgshapiro milter_init(e, &state); 39464562Sgshapiro switch (state) 39564562Sgshapiro { 39664562Sgshapiro case SMFIR_REJECT: 39764562Sgshapiro greetcode = "554"; 39864562Sgshapiro nullserver = "Command rejected"; 39964562Sgshapiro milterize = FALSE; 40064562Sgshapiro break; 40164562Sgshapiro 40264562Sgshapiro case SMFIR_TEMPFAIL: 40364562Sgshapiro tempfail = TRUE; 40464562Sgshapiro milterize = FALSE; 40564562Sgshapiro break; 40664562Sgshapiro } 40764562Sgshapiro } 40864562Sgshapiro 40964562Sgshapiro if (milterize && !bitset(EF_DISCARD, e->e_flags)) 41064562Sgshapiro { 41164562Sgshapiro char state; 41264562Sgshapiro 41364562Sgshapiro (void) milter_connect(peerhostname, RealHostAddr, 41464562Sgshapiro e, &state); 41564562Sgshapiro switch (state) 41664562Sgshapiro { 41764562Sgshapiro case SMFIR_REPLYCODE: /* REPLYCODE shouldn't happen */ 41864562Sgshapiro case SMFIR_REJECT: 41964562Sgshapiro greetcode = "554"; 42064562Sgshapiro nullserver = "Command rejected"; 42164562Sgshapiro milterize = FALSE; 42264562Sgshapiro break; 42364562Sgshapiro 42464562Sgshapiro case SMFIR_TEMPFAIL: 42564562Sgshapiro tempfail = TRUE; 42664562Sgshapiro milterize = FALSE; 42764562Sgshapiro break; 42864562Sgshapiro } 42964562Sgshapiro } 43064562Sgshapiro# endif /* _FFR_MILTER */ 43164562Sgshapiro 43238032Speter /* output the first line, inserting "ESMTP" as second word */ 43338032Speter expand(SmtpGreeting, inp, sizeof inp, e); 43438032Speter p = strchr(inp, '\n'); 43538032Speter if (p != NULL) 43638032Speter *p++ = '\0'; 43738032Speter id = strchr(inp, ' '); 43838032Speter if (id == NULL) 43938032Speter id = &inp[strlen(inp)]; 44064562Sgshapiro if (p == NULL) 44164562Sgshapiro snprintf(cmdbuf, sizeof cmdbuf, 44264562Sgshapiro "%s %%.*s ESMTP%%s", greetcode); 44364562Sgshapiro else 44464562Sgshapiro snprintf(cmdbuf, sizeof cmdbuf, 44564562Sgshapiro "%s-%%.*s ESMTP%%s", greetcode); 44666494Sgshapiro message(cmdbuf, (int) (id - inp), inp, id); 44738032Speter 44838032Speter /* output remaining lines */ 44938032Speter while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) 45038032Speter { 45138032Speter *p++ = '\0'; 45238032Speter if (isascii(*id) && isspace(*id)) 45338032Speter id++; 45464562Sgshapiro (void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode); 45564562Sgshapiro message(cmdbuf, id); 45638032Speter } 45738032Speter if (id != NULL) 45838032Speter { 45938032Speter if (isascii(*id) && isspace(*id)) 46038032Speter id++; 46164562Sgshapiro (void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode); 46264562Sgshapiro message(cmdbuf, id); 46338032Speter } 46438032Speter 46538032Speter protocol = NULL; 46638032Speter sendinghost = macvalue('s', e); 46738032Speter gothello = FALSE; 46838032Speter gotmail = FALSE; 46938032Speter for (;;) 47038032Speter { 47138032Speter /* arrange for backout */ 47238032Speter (void) setjmp(TopFrame); 47338032Speter QuickAbort = FALSE; 47438032Speter HoldErrs = FALSE; 47538032Speter SuprErrs = FALSE; 47638032Speter LogUsrErrs = FALSE; 47738032Speter OnlyOneError = TRUE; 47838032Speter e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); 47938032Speter 48038032Speter /* setup for the read */ 48138032Speter e->e_to = NULL; 48238032Speter Errors = 0; 48364562Sgshapiro FileName = NULL; 48438032Speter (void) fflush(stdout); 48538032Speter 48638032Speter /* read the input line */ 48738032Speter SmtpPhase = "server cmd read"; 48864562Sgshapiro sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient); 48964562Sgshapiro# if SASL 49064562Sgshapiro /* 49164562Sgshapiro ** SMTP AUTH requires accepting any length, 49264562Sgshapiro ** at least for challenge/response 49364562Sgshapiro ** XXX 49464562Sgshapiro */ 49564562Sgshapiro# endif /* SASL */ 49638032Speter 49738032Speter /* handle errors */ 49864562Sgshapiro if (ferror(OutChannel) || 49964562Sgshapiro (p = sfgets(inp, sizeof inp, InChannel, 50064562Sgshapiro TimeOuts.to_nextcommand, SmtpPhase)) == NULL) 50138032Speter { 50264562Sgshapiro char *d; 50364562Sgshapiro 50464562Sgshapiro d = macvalue(macid("{daemon_name}", NULL), e); 50564562Sgshapiro if (d == NULL) 50664562Sgshapiro d = "stdin"; 50738032Speter /* end of file, just die */ 50838032Speter disconnect(1, e); 50964562Sgshapiro 51064562Sgshapiro# if _FFR_MILTER 51164562Sgshapiro /* close out milter filters */ 51264562Sgshapiro milter_quit(e); 51364562Sgshapiro# endif /* _FFR_MILTER */ 51464562Sgshapiro 51564562Sgshapiro message("421 4.4.1 %s Lost input channel from %s", 51638032Speter MyHostName, CurSmtpClient); 51738032Speter if (LogLevel > (gotmail ? 1 : 19)) 51838032Speter sm_syslog(LOG_NOTICE, e->e_id, 51964562Sgshapiro "lost input channel from %.100s to %s after %s", 52064562Sgshapiro CurSmtpClient, d, 52164562Sgshapiro (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name); 52238032Speter /* 52342575Speter ** If have not accepted mail (DATA), do not bounce 52442575Speter ** bad addresses back to sender. 52538032Speter */ 52664562Sgshapiro 52738032Speter if (bitset(EF_CLRQUEUE, e->e_flags)) 52838032Speter e->e_sendqueue = NULL; 52964562Sgshapiro goto doquit; 53038032Speter } 53138032Speter 53238032Speter /* clean up end of line */ 53338032Speter fixcrlf(inp, TRUE); 53438032Speter 53564562Sgshapiro# if SASL 53664562Sgshapiro if (authenticating == SASL_PROC_AUTH) 53764562Sgshapiro { 53864562Sgshapiro# if 0 53964562Sgshapiro if (*inp == '\0') 54064562Sgshapiro { 54164562Sgshapiro authenticating = SASL_NOT_AUTH; 54264562Sgshapiro message("501 5.5.2 missing input"); 54364562Sgshapiro continue; 54464562Sgshapiro } 54564562Sgshapiro# endif /* 0 */ 54664562Sgshapiro if (*inp == '*' && *(inp + 1) == '\0') 54764562Sgshapiro { 54864562Sgshapiro authenticating = SASL_NOT_AUTH; 54964562Sgshapiro 55064562Sgshapiro /* rfc 2254 4. */ 55164562Sgshapiro message("501 5.0.0 AUTH aborted"); 55264562Sgshapiro continue; 55364562Sgshapiro } 55464562Sgshapiro 55564562Sgshapiro /* could this be shorter? XXX */ 55664562Sgshapiro out = xalloc(strlen(inp)); 55764562Sgshapiro result = sasl_decode64(inp, strlen(inp), out, &outlen); 55864562Sgshapiro if (result != SASL_OK) 55964562Sgshapiro { 56064562Sgshapiro authenticating = SASL_NOT_AUTH; 56164562Sgshapiro 56264562Sgshapiro /* rfc 2254 4. */ 56364562Sgshapiro message("501 5.5.4 cannot decode AUTH parameter %s", 56464562Sgshapiro inp); 56564562Sgshapiro continue; 56664562Sgshapiro } 56764562Sgshapiro 56864562Sgshapiro result = sasl_server_step(conn, out, outlen, 56964562Sgshapiro &out, &outlen, &errstr); 57064562Sgshapiro 57164562Sgshapiro /* get an OK if we're done */ 57264562Sgshapiro if (result == SASL_OK) 57364562Sgshapiro { 57464562Sgshapiro authenticated: 57564562Sgshapiro message("235 2.0.0 OK Authenticated"); 57664562Sgshapiro authenticating = SASL_IS_AUTH; 57764562Sgshapiro define(macid("{auth_type}", NULL), 57864562Sgshapiro newstr(auth_type), &BlankEnvelope); 57964562Sgshapiro 58064562Sgshapiro result = sasl_getprop(conn, SASL_USERNAME, 58164562Sgshapiro (void **)&user); 58264562Sgshapiro if (result != SASL_OK) 58364562Sgshapiro { 58464562Sgshapiro user = ""; 58564562Sgshapiro define(macid("{auth_authen}", NULL), 58664562Sgshapiro NULL, &BlankEnvelope); 58764562Sgshapiro } 58864562Sgshapiro else 58964562Sgshapiro { 59064562Sgshapiro define(macid("{auth_authen}", NULL), 59164562Sgshapiro newstr(user), &BlankEnvelope); 59264562Sgshapiro } 59364562Sgshapiro 59464562Sgshapiro# if 0 59564562Sgshapiro /* get realm? */ 59664562Sgshapiro sasl_getprop(conn, SASL_REALM, (void **) &data); 59764562Sgshapiro# endif /* 0 */ 59864562Sgshapiro 59964562Sgshapiro 60064562Sgshapiro# if SFIO 60164562Sgshapiro /* get security strength (features) */ 60264562Sgshapiro result = sasl_getprop(conn, SASL_SSF, 60364562Sgshapiro (void **) &ssf); 60464562Sgshapiro if (result != SASL_OK) 60564562Sgshapiro { 60664562Sgshapiro define(macid("{auth_ssf}", NULL), 60764562Sgshapiro "0", &BlankEnvelope); 60864562Sgshapiro ssf = NULL; 60964562Sgshapiro } 61064562Sgshapiro else 61164562Sgshapiro { 61264562Sgshapiro char pbuf[8]; 61364562Sgshapiro 61464562Sgshapiro snprintf(pbuf, sizeof pbuf, "%u", *ssf); 61564562Sgshapiro define(macid("{auth_ssf}", NULL), 61664562Sgshapiro newstr(pbuf), &BlankEnvelope); 61764562Sgshapiro if (tTd(95, 8)) 61864562Sgshapiro dprintf("SASL auth_ssf: %u\n", 61964562Sgshapiro *ssf); 62064562Sgshapiro } 62164562Sgshapiro /* 62264562Sgshapiro ** only switch to encrypted connection 62364562Sgshapiro ** if a security layer has been negotiated 62464562Sgshapiro */ 62564562Sgshapiro if (ssf != NULL && *ssf > 0) 62664562Sgshapiro { 62764562Sgshapiro /* 62864562Sgshapiro ** convert sfio stuff to use SASL 62964562Sgshapiro ** check return values 63064562Sgshapiro ** if the call fails, 63164562Sgshapiro ** fall back to unencrypted version 63264562Sgshapiro ** unless some cf option requires 63364562Sgshapiro ** encryption then the connection must 63464562Sgshapiro ** be aborted 63564562Sgshapiro */ 63664562Sgshapiro if (sfdcsasl(InChannel, OutChannel, 63764562Sgshapiro conn) == 0) 63864562Sgshapiro { 63964562Sgshapiro /* restart dialogue */ 64064562Sgshapiro gothello = FALSE; 64164562Sgshapiro OneXact = TRUE; 64264562Sgshapiro n_helo = 0; 64364562Sgshapiro } 64464562Sgshapiro else 64564562Sgshapiro syserr("503 5.3.3 SASL TLS failed"); 64664562Sgshapiro if (LogLevel > 9) 64764562Sgshapiro sm_syslog(LOG_INFO, 64864562Sgshapiro NOQID, 64964562Sgshapiro "SASL: connection from %.64s: mech=%.16s, id=%.64s, bits=%d", 65064562Sgshapiro CurSmtpClient, 65164562Sgshapiro auth_type, user, 65264562Sgshapiro *ssf); 65364562Sgshapiro } 65464562Sgshapiro# else /* SFIO */ 65564562Sgshapiro if (LogLevel > 9) 65664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 65764562Sgshapiro "SASL: connection from %.64s: mech=%.16s, id=%.64s", 65864562Sgshapiro CurSmtpClient, auth_type, 65964562Sgshapiro user); 66064562Sgshapiro# endif /* SFIO */ 66164562Sgshapiro } 66264562Sgshapiro else if (result == SASL_CONTINUE) 66364562Sgshapiro { 66464562Sgshapiro len = ENC64LEN(outlen); 66564562Sgshapiro out2 = xalloc(len); 66664562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 66764562Sgshapiro (u_int *)&out2len); 66864562Sgshapiro if (result != SASL_OK) 66964562Sgshapiro { 67064562Sgshapiro /* correct code? XXX */ 67164562Sgshapiro /* 454 Temp. authentication failure */ 67264562Sgshapiro message("454 4.5.4 Internal error: unable to encode64"); 67364562Sgshapiro if (LogLevel > 5) 67464562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 67564562Sgshapiro "SASL encode64 error [%d for \"%s\"]", 67664562Sgshapiro result, out); 67764562Sgshapiro /* start over? */ 67864562Sgshapiro authenticating = SASL_NOT_AUTH; 67964562Sgshapiro } 68064562Sgshapiro else 68164562Sgshapiro { 68264562Sgshapiro message("334 %s", out2); 68364562Sgshapiro if (tTd(95, 2)) 68464562Sgshapiro dprintf("SASL continue: msg='%s' len=%d\n", 68564562Sgshapiro out2, out2len); 68664562Sgshapiro } 68764562Sgshapiro } 68864562Sgshapiro else 68964562Sgshapiro { 69064562Sgshapiro /* not SASL_OK or SASL_CONT */ 69164562Sgshapiro message("500 5.7.0 authentication failed"); 69264562Sgshapiro if (LogLevel > 9) 69364562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 69464562Sgshapiro "AUTH failure (%s): %s (%d)", 69564562Sgshapiro auth_type, 69664562Sgshapiro sasl_errstring(result, NULL, 69764562Sgshapiro NULL), 69864562Sgshapiro result); 69964562Sgshapiro authenticating = SASL_NOT_AUTH; 70064562Sgshapiro } 70164562Sgshapiro } 70264562Sgshapiro else 70364562Sgshapiro { 70464562Sgshapiro /* don't want to do any of this if authenticating */ 70564562Sgshapiro# endif /* SASL */ 70664562Sgshapiro 70738032Speter /* echo command to transcript */ 70838032Speter if (e->e_xfp != NULL) 70938032Speter fprintf(e->e_xfp, "<<< %s\n", inp); 71038032Speter 71138032Speter if (LogLevel >= 15) 71238032Speter sm_syslog(LOG_INFO, e->e_id, 71364562Sgshapiro "<-- %s", 71464562Sgshapiro inp); 71538032Speter 71638032Speter if (e->e_id == NULL) 71764562Sgshapiro sm_setproctitle(TRUE, e, "%s: %.80s", 71864562Sgshapiro CurSmtpClient, inp); 71938032Speter else 72064562Sgshapiro sm_setproctitle(TRUE, e, "%s %s: %.80s", 72164562Sgshapiro qid_printname(e), 72264562Sgshapiro CurSmtpClient, inp); 72338032Speter 72438032Speter /* break off command */ 72538032Speter for (p = inp; isascii(*p) && isspace(*p); p++) 72638032Speter continue; 72738032Speter cmd = cmdbuf; 72838032Speter while (*p != '\0' && 72938032Speter !(isascii(*p) && isspace(*p)) && 73038032Speter cmd < &cmdbuf[sizeof cmdbuf - 2]) 73138032Speter *cmd++ = *p++; 73238032Speter *cmd = '\0'; 73338032Speter 73438032Speter /* throw away leading whitespace */ 73538032Speter while (isascii(*p) && isspace(*p)) 73638032Speter p++; 73738032Speter 73838032Speter /* decode command */ 73964562Sgshapiro for (c = CmdTab; c->cmd_name != NULL; c++) 74038032Speter { 74164562Sgshapiro if (strcasecmp(c->cmd_name, cmdbuf) == 0) 74238032Speter break; 74338032Speter } 74438032Speter 74538032Speter /* reset errors */ 74638032Speter errno = 0; 74738032Speter 74838032Speter /* 74938032Speter ** Process command. 75038032Speter ** 75138032Speter ** If we are running as a null server, return 550 75238032Speter ** to everything. 75338032Speter */ 75438032Speter 75564562Sgshapiro if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags)) 75638032Speter { 75764562Sgshapiro switch (c->cmd_code) 75838032Speter { 75938032Speter case CMDQUIT: 76038032Speter case CMDHELO: 76138032Speter case CMDEHLO: 76238032Speter case CMDNOOP: 76364562Sgshapiro case CMDRSET: 76438032Speter /* process normally */ 76538032Speter break; 76638032Speter 76764562Sgshapiro case CMDETRN: 76864562Sgshapiro if (bitnset(D_ETRNONLY, d_flags) && 76964562Sgshapiro nullserver == NULL) 77064562Sgshapiro break; 77164562Sgshapiro continue; 77264562Sgshapiro 77338032Speter default: 77438032Speter if (++badcommands > MAXBADCOMMANDS) 77564562Sgshapiro { 77664562Sgshapiro delay *= 2; 77764562Sgshapiro if (delay >= MAXTIMEOUT) 77864562Sgshapiro delay = MAXTIMEOUT; 77964562Sgshapiro (void) sleep(delay); 78064562Sgshapiro } 78164562Sgshapiro if (nullserver != NULL) 78264562Sgshapiro { 78364562Sgshapiro if (ISSMTPREPLY(nullserver)) 78464562Sgshapiro usrerr(nullserver); 78564562Sgshapiro else 78664562Sgshapiro usrerr("550 5.0.0 %s", nullserver); 78764562Sgshapiro } 78864562Sgshapiro else 78964562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 79038032Speter continue; 79138032Speter } 79238032Speter } 79338032Speter 79438032Speter /* non-null server */ 79564562Sgshapiro switch (c->cmd_code) 79638032Speter { 79738032Speter case CMDMAIL: 79838032Speter case CMDEXPN: 79938032Speter case CMDVRFY: 80038032Speter case CMDETRN: 80138032Speter lognullconnection = FALSE; 80238032Speter } 80338032Speter 80464562Sgshapiro switch (c->cmd_code) 80538032Speter { 80664562Sgshapiro# if SASL 80764562Sgshapiro case CMDAUTH: /* sasl */ 80864562Sgshapiro if (!sasl_ok) 80964562Sgshapiro { 81064562Sgshapiro message("503 5.3.3 AUTH not available"); 81164562Sgshapiro break; 81264562Sgshapiro } 81364562Sgshapiro if (authenticating == SASL_IS_AUTH) 81464562Sgshapiro { 81564562Sgshapiro message("503 5.5.0 Already Authenticated"); 81664562Sgshapiro break; 81764562Sgshapiro } 81864562Sgshapiro if (gotmail) 81964562Sgshapiro { 82064562Sgshapiro message("503 5.5.0 AUTH not permitted during a mail transaction"); 82164562Sgshapiro break; 82264562Sgshapiro } 82364562Sgshapiro if (tempfail) 82464562Sgshapiro { 82564562Sgshapiro if (LogLevel > 9) 82664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 82764562Sgshapiro "SMTP AUTH command (%.100s) from %.100s tempfailed (due to previous checks)", 82864562Sgshapiro p, CurSmtpClient); 82964562Sgshapiro usrerr("454 4.7.1 Please try again later"); 83064562Sgshapiro break; 83164562Sgshapiro } 83264562Sgshapiro 83364562Sgshapiro ismore = FALSE; 83464562Sgshapiro 83564562Sgshapiro /* crude way to avoid crack attempts */ 83664562Sgshapiro (void) checksmtpattack(&n_auth, n_mechs + 1, TRUE, 83764562Sgshapiro "AUTH", e); 83864562Sgshapiro 83964562Sgshapiro /* make sure it's a valid string */ 84064562Sgshapiro for (q = p; *q != '\0' && isascii(*q); q++) 84164562Sgshapiro { 84264562Sgshapiro if (isspace(*q)) 84364562Sgshapiro { 84464562Sgshapiro *q = '\0'; 84564562Sgshapiro while (*++q != '\0' && 84664562Sgshapiro isascii(*q) && isspace(*q)) 84764562Sgshapiro continue; 84864562Sgshapiro *(q - 1) = '\0'; 84964562Sgshapiro ismore = (*q != '\0'); 85064562Sgshapiro break; 85164562Sgshapiro } 85264562Sgshapiro } 85364562Sgshapiro 85464562Sgshapiro /* check whether mechanism is available */ 85564562Sgshapiro if (iteminlist(p, mechlist, " ") == NULL) 85664562Sgshapiro { 85764562Sgshapiro message("503 5.3.3 AUTH mechanism %s not available", 85864562Sgshapiro p); 85964562Sgshapiro break; 86064562Sgshapiro } 86164562Sgshapiro 86264562Sgshapiro if (ismore) 86364562Sgshapiro { 86464562Sgshapiro /* could this be shorter? XXX */ 86564562Sgshapiro in = xalloc(strlen(q)); 86664562Sgshapiro result = sasl_decode64(q, strlen(q), in, 86764562Sgshapiro (u_int *)&inlen); 86864562Sgshapiro if (result != SASL_OK) 86964562Sgshapiro { 87064562Sgshapiro message("501 5.5.4 cannot BASE64 decode '%s'", 87164562Sgshapiro q); 87264562Sgshapiro if (LogLevel > 5) 87364562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 87464562Sgshapiro "SASL decode64 error [%d for \"%s\"]", 87564562Sgshapiro result, q); 87664562Sgshapiro /* start over? */ 87764562Sgshapiro authenticating = SASL_NOT_AUTH; 87864562Sgshapiro in = NULL; 87964562Sgshapiro inlen = 0; 88064562Sgshapiro break; 88164562Sgshapiro } 88264562Sgshapiro# if 0 88364562Sgshapiro if (tTd(95, 99)) 88464562Sgshapiro { 88564562Sgshapiro int i; 88664562Sgshapiro 88764562Sgshapiro dprintf("AUTH: more \""); 88864562Sgshapiro for (i = 0; i < inlen; i++) 88964562Sgshapiro { 89064562Sgshapiro if (isascii(in[i]) && 89164562Sgshapiro isprint(in[i])) 89264562Sgshapiro dprintf("%c", in[i]); 89364562Sgshapiro else 89464562Sgshapiro dprintf("_"); 89564562Sgshapiro } 89664562Sgshapiro dprintf("\"\n"); 89764562Sgshapiro } 89864562Sgshapiro# endif /* 0 */ 89964562Sgshapiro } 90064562Sgshapiro else 90164562Sgshapiro { 90264562Sgshapiro in = NULL; 90364562Sgshapiro inlen = 0; 90464562Sgshapiro } 90564562Sgshapiro 90664562Sgshapiro /* see if that auth type exists */ 90764562Sgshapiro result = sasl_server_start(conn, p, in, inlen, 90864562Sgshapiro &out, &outlen, &errstr); 90964562Sgshapiro 91064562Sgshapiro if (result != SASL_OK && result != SASL_CONTINUE) 91164562Sgshapiro { 91264562Sgshapiro message("500 5.7.0 authentication failed"); 91364562Sgshapiro if (LogLevel > 9) 91464562Sgshapiro sm_syslog(LOG_ERR, e->e_id, 91564562Sgshapiro "AUTH failure (%s): %s (%d)", 91664562Sgshapiro p, 91764562Sgshapiro sasl_errstring(result, NULL, 91864562Sgshapiro NULL), 91964562Sgshapiro result); 92064562Sgshapiro break; 92164562Sgshapiro } 92264562Sgshapiro auth_type = newstr(p); 92364562Sgshapiro 92464562Sgshapiro if (result == SASL_OK) 92564562Sgshapiro { 92664562Sgshapiro /* ugly, but same code */ 92764562Sgshapiro goto authenticated; 92864562Sgshapiro /* authenticated by the initial response */ 92964562Sgshapiro } 93064562Sgshapiro 93164562Sgshapiro /* len is at least 2 */ 93264562Sgshapiro len = ENC64LEN(outlen); 93364562Sgshapiro out2 = xalloc(len); 93464562Sgshapiro result = sasl_encode64(out, outlen, out2, len, 93564562Sgshapiro (u_int *)&out2len); 93664562Sgshapiro 93764562Sgshapiro if (result != SASL_OK) 93864562Sgshapiro { 93964562Sgshapiro message("454 4.5.4 Temporary authentication failure"); 94064562Sgshapiro if (LogLevel > 5) 94164562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 94264562Sgshapiro "SASL encode64 error [%d for \"%s\"]", 94364562Sgshapiro result, out); 94464562Sgshapiro 94564562Sgshapiro /* start over? */ 94664562Sgshapiro authenticating = SASL_NOT_AUTH; 94764562Sgshapiro } 94864562Sgshapiro else 94964562Sgshapiro { 95064562Sgshapiro message("334 %s", out2); 95164562Sgshapiro authenticating = SASL_PROC_AUTH; 95264562Sgshapiro } 95364562Sgshapiro 95464562Sgshapiro break; 95564562Sgshapiro# endif /* SASL */ 95664562Sgshapiro 95764562Sgshapiro# if STARTTLS 95864562Sgshapiro case CMDSTLS: /* starttls */ 95964562Sgshapiro if (*p != '\0') 96064562Sgshapiro { 96164562Sgshapiro message("501 5.5.2 Syntax error (no parameters allowed)"); 96264562Sgshapiro break; 96364562Sgshapiro } 96464562Sgshapiro if (!usetls) 96564562Sgshapiro { 96664562Sgshapiro message("503 5.5.0 TLS not available"); 96764562Sgshapiro break; 96864562Sgshapiro } 96964562Sgshapiro if (!tls_ok) 97064562Sgshapiro { 97164562Sgshapiro message("454 4.3.3 TLS not available after start"); 97264562Sgshapiro break; 97364562Sgshapiro } 97464562Sgshapiro if (gotmail) 97564562Sgshapiro { 97664562Sgshapiro message("503 5.5.0 TLS not permitted during a mail transaction"); 97764562Sgshapiro break; 97864562Sgshapiro } 97964562Sgshapiro if (tempfail) 98064562Sgshapiro { 98164562Sgshapiro if (LogLevel > 9) 98264562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 98364562Sgshapiro "SMTP STARTTLS command (%.100s) from %.100s tempfailed (due to previous checks)", 98464562Sgshapiro p, CurSmtpClient); 98564562Sgshapiro usrerr("454 4.7.1 Please try again later"); 98664562Sgshapiro break; 98764562Sgshapiro } 98864562Sgshapiro# if TLS_NO_RSA 98964562Sgshapiro /* 99064562Sgshapiro ** XXX do we need a temp key ? 99164562Sgshapiro */ 99264562Sgshapiro# else /* TLS_NO_RSA */ 99364562Sgshapiro if (SSL_CTX_need_tmp_RSA(srv_ctx) && 99464562Sgshapiro !SSL_CTX_set_tmp_rsa(srv_ctx, 99564562Sgshapiro (rsa = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, 99664562Sgshapiro NULL, NULL))) 99764562Sgshapiro ) 99864562Sgshapiro { 99964562Sgshapiro message("454 4.3.3 TLS not available: error generating RSA temp key"); 100064562Sgshapiro if (rsa != NULL) 100164562Sgshapiro RSA_free(rsa); 100264562Sgshapiro break; 100364562Sgshapiro } 100464562Sgshapiro# endif /* TLS_NO_RSA */ 100564562Sgshapiro if (srv_ssl != NULL) 100664562Sgshapiro SSL_clear(srv_ssl); 100764562Sgshapiro else if ((srv_ssl = SSL_new(srv_ctx)) == NULL) 100864562Sgshapiro { 100964562Sgshapiro message("454 4.3.3 TLS not available: error generating SSL handle"); 101064562Sgshapiro break; 101164562Sgshapiro } 101266494Sgshapiro rfd = fileno(InChannel); 101366494Sgshapiro wfd = fileno(OutChannel); 101466494Sgshapiro if (rfd < 0 || wfd < 0 || 101566494Sgshapiro SSL_set_rfd(srv_ssl, rfd) <= 0 || 101666494Sgshapiro SSL_set_wfd(srv_ssl, wfd) <= 0) 101764562Sgshapiro { 101864562Sgshapiro message("454 4.3.3 TLS not available: error set fd"); 101964562Sgshapiro SSL_free(srv_ssl); 102064562Sgshapiro srv_ssl = NULL; 102164562Sgshapiro break; 102264562Sgshapiro } 102364562Sgshapiro message("220 2.0.0 Ready to start TLS"); 102464562Sgshapiro SSL_set_accept_state(srv_ssl); 102564562Sgshapiro 102664562Sgshapiro# define SSL_ACC(s) SSL_accept(s) 102764562Sgshapiro if ((r = SSL_ACC(srv_ssl)) <= 0) 102864562Sgshapiro { 102964562Sgshapiro int i; 103064562Sgshapiro 103164562Sgshapiro /* what to do in this case? */ 103264562Sgshapiro i = SSL_get_error(srv_ssl, r); 103364562Sgshapiro if (LogLevel > 5) 103464562Sgshapiro { 103564562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 103664562Sgshapiro "TLS: error: accept failed=%d (%d)", 103764562Sgshapiro r, i); 103864562Sgshapiro if (LogLevel > 9) 103964562Sgshapiro tlslogerr(); 104064562Sgshapiro } 104164562Sgshapiro tls_ok = FALSE; 104264562Sgshapiro SSL_free(srv_ssl); 104364562Sgshapiro srv_ssl = NULL; 104464562Sgshapiro 104564562Sgshapiro /* 104664562Sgshapiro ** according to the next draft of 104764562Sgshapiro ** RFC 2487 the connection should be dropped 104864562Sgshapiro */ 104964562Sgshapiro 105064562Sgshapiro /* arrange to ignore any current send list */ 105164562Sgshapiro e->e_sendqueue = NULL; 105264562Sgshapiro goto doquit; 105364562Sgshapiro } 105464562Sgshapiro 105564562Sgshapiro /* ignore return code for now, it's in {verify} */ 105664562Sgshapiro (void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE, 105764562Sgshapiro CurSmtpClient); 105864562Sgshapiro 105964562Sgshapiro /* 106064562Sgshapiro ** call Stls_client to find out whether 106164562Sgshapiro ** to accept the connection from the client 106264562Sgshapiro */ 106364562Sgshapiro 106464562Sgshapiro saveQuickAbort = QuickAbort; 106564562Sgshapiro saveSuprErrs = SuprErrs; 106664562Sgshapiro SuprErrs = TRUE; 106764562Sgshapiro QuickAbort = FALSE; 106864562Sgshapiro if (rscheck("tls_client", 106964562Sgshapiro macvalue(macid("{verify}", NULL), e), 107064562Sgshapiro "STARTTLS", e, TRUE, TRUE, 6) != EX_OK || 107164562Sgshapiro Errors > 0) 107264562Sgshapiro { 107364562Sgshapiro extern char MsgBuf[]; 107464562Sgshapiro 107564562Sgshapiro if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf)) 107664562Sgshapiro nullserver = newstr(MsgBuf); 107764562Sgshapiro else 107864562Sgshapiro nullserver = "503 5.7.0 Authentication required."; 107964562Sgshapiro } 108064562Sgshapiro QuickAbort = saveQuickAbort; 108164562Sgshapiro SuprErrs = saveSuprErrs; 108264562Sgshapiro 108364562Sgshapiro tls_ok = FALSE; /* don't offer STARTTLS again */ 108464562Sgshapiro gothello = FALSE; /* discard info */ 108564562Sgshapiro n_helo = 0; 108664562Sgshapiro OneXact = TRUE; /* only one xaction this run */ 108764562Sgshapiro# if SASL 108864562Sgshapiro if (sasl_ok) 108964562Sgshapiro { 109064562Sgshapiro char *s; 109164562Sgshapiro 109264562Sgshapiro if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL && 109364562Sgshapiro (ext_ssf.ssf = atoi(s)) > 0) 109464562Sgshapiro { 109564562Sgshapiro# if _FFR_EXT_MECH 109664562Sgshapiro ext_ssf.auth_id = macvalue(macid("{cert_subject}", 109764562Sgshapiro NULL), 109864562Sgshapiro e); 109964562Sgshapiro# endif /* _FFR_EXT_MECH */ 110064562Sgshapiro sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL, 110164562Sgshapiro &ext_ssf) == SASL_OK; 110264562Sgshapiro if (mechlist != NULL) 110364562Sgshapiro free(mechlist); 110464562Sgshapiro mechlist = NULL; 110564562Sgshapiro if (sasl_ok) 110664562Sgshapiro { 110764562Sgshapiro n_mechs = saslmechs(conn, 110864562Sgshapiro &mechlist); 110964562Sgshapiro sasl_ok = n_mechs > 0; 111064562Sgshapiro } 111164562Sgshapiro } 111264562Sgshapiro } 111364562Sgshapiro# endif /* SASL */ 111464562Sgshapiro 111564562Sgshapiro /* switch to secure connection */ 111664562Sgshapiro#if SFIO 111764562Sgshapiro r = sfdctls(InChannel, OutChannel, srv_ssl); 111864562Sgshapiro#else /* SFIO */ 111964562Sgshapiro# if _FFR_TLS_TOREK 112064562Sgshapiro r = sfdctls(&InChannel, &OutChannel, srv_ssl); 112164562Sgshapiro# endif /* _FFR_TLS_TOREK */ 112264562Sgshapiro#endif /* SFIO */ 112364562Sgshapiro if (r == 0) 112464562Sgshapiro tls_active = TRUE; 112564562Sgshapiro else 112664562Sgshapiro { 112764562Sgshapiro /* 112864562Sgshapiro ** XXX this is an internal error 112964562Sgshapiro ** how to deal with it? 113064562Sgshapiro ** we can't generate an error message 113164562Sgshapiro ** since the other side switched to an 113264562Sgshapiro ** encrypted layer, but we could not... 113364562Sgshapiro ** just "hang up"? 113464562Sgshapiro */ 113564562Sgshapiro nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer"; 113664562Sgshapiro syserr("TLS: can't switch to encrypted layer"); 113764562Sgshapiro } 113864562Sgshapiro break; 113964562Sgshapiro# endif /* STARTTLS */ 114064562Sgshapiro 114138032Speter case CMDHELO: /* hello -- introduce yourself */ 114238032Speter case CMDEHLO: /* extended hello */ 114364562Sgshapiro if (c->cmd_code == CMDEHLO) 114438032Speter { 114538032Speter protocol = "ESMTP"; 114638032Speter SmtpPhase = "server EHLO"; 114738032Speter } 114838032Speter else 114938032Speter { 115038032Speter protocol = "SMTP"; 115138032Speter SmtpPhase = "server HELO"; 115238032Speter } 115338032Speter 115438032Speter /* avoid denial-of-service */ 115564562Sgshapiro (void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, TRUE, 115664562Sgshapiro "HELO/EHLO", e); 115738032Speter 115838032Speter /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ 115938032Speter if (gothello) 116038032Speter { 116138032Speter usrerr("503 %s Duplicate HELO/EHLO", 116238032Speter MyHostName); 116338032Speter break; 116438032Speter } 116538032Speter 116638032Speter /* check for valid domain name (re 1123 5.2.5) */ 116738032Speter if (*p == '\0' && !AllowBogusHELO) 116838032Speter { 116938032Speter usrerr("501 %s requires domain address", 117038032Speter cmdbuf); 117138032Speter break; 117238032Speter } 117338032Speter 117438032Speter /* check for long domain name (hides Received: info) */ 117538032Speter if (strlen(p) > MAXNAME) 117638032Speter { 117738032Speter usrerr("501 Invalid domain name"); 117864562Sgshapiro if (LogLevel > 9) 117964562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 118064562Sgshapiro "invalid domain name (too long) from %.100s", 118164562Sgshapiro CurSmtpClient); 118238032Speter break; 118338032Speter } 118438032Speter 118538032Speter for (q = p; *q != '\0'; q++) 118638032Speter { 118738032Speter if (!isascii(*q)) 118838032Speter break; 118938032Speter if (isalnum(*q)) 119038032Speter continue; 119138032Speter if (isspace(*q)) 119238032Speter { 119338032Speter *q = '\0'; 119438032Speter break; 119538032Speter } 119638032Speter if (strchr("[].-_#", *q) == NULL) 119738032Speter break; 119838032Speter } 119964562Sgshapiro 120038032Speter if (*q == '\0') 120138032Speter { 120238032Speter q = "pleased to meet you"; 120338032Speter sendinghost = newstr(p); 120438032Speter } 120538032Speter else if (!AllowBogusHELO) 120638032Speter { 120738032Speter usrerr("501 Invalid domain name"); 120864562Sgshapiro if (LogLevel > 9) 120964562Sgshapiro sm_syslog(LOG_INFO, CurEnv->e_id, 121064562Sgshapiro "invalid domain name (%.100s) from %.100s", 121164562Sgshapiro p, CurSmtpClient); 121238032Speter break; 121338032Speter } 121438032Speter else 121538032Speter { 121638032Speter q = "accepting invalid domain name"; 121738032Speter } 121838032Speter 121938032Speter gothello = TRUE; 122064562Sgshapiro 122164562Sgshapiro# if _FFR_MILTER 122264562Sgshapiro if (milterize && !bitset(EF_DISCARD, e->e_flags)) 122364562Sgshapiro { 122464562Sgshapiro char state; 122564562Sgshapiro char *response; 122664562Sgshapiro 122764562Sgshapiro response = milter_helo(p, e, &state); 122864562Sgshapiro switch (state) 122964562Sgshapiro { 123064562Sgshapiro case SMFIR_REPLYCODE: 123164562Sgshapiro nullserver = response; 123264562Sgshapiro milterize = FALSE; 123364562Sgshapiro break; 123464562Sgshapiro 123564562Sgshapiro case SMFIR_REJECT: 123664562Sgshapiro nullserver = "Command rejected"; 123764562Sgshapiro milterize = FALSE; 123864562Sgshapiro break; 123964562Sgshapiro 124064562Sgshapiro case SMFIR_TEMPFAIL: 124164562Sgshapiro tempfail = TRUE; 124264562Sgshapiro milterize = FALSE; 124364562Sgshapiro break; 124464562Sgshapiro } 124564562Sgshapiro } 124664562Sgshapiro# endif /* _FFR_MILTER */ 124764562Sgshapiro 124838032Speter /* print HELO response message */ 124964562Sgshapiro if (c->cmd_code != CMDEHLO) 125038032Speter { 125138032Speter message("250 %s Hello %s, %s", 125238032Speter MyHostName, CurSmtpClient, q); 125338032Speter break; 125438032Speter } 125538032Speter 125638032Speter message("250-%s Hello %s, %s", 125738032Speter MyHostName, CurSmtpClient, q); 125838032Speter 125964562Sgshapiro /* offer ENHSC even for nullserver */ 126064562Sgshapiro if (nullserver != NULL) 126164562Sgshapiro { 126264562Sgshapiro message("250 ENHANCEDSTATUSCODES"); 126364562Sgshapiro break; 126464562Sgshapiro } 126564562Sgshapiro 126666494Sgshapiro /* 126766494Sgshapiro ** print EHLO features list 126866494Sgshapiro ** 126966494Sgshapiro ** Note: If you change this list, 127066494Sgshapiro ** remember to update 'helpfile' 127166494Sgshapiro */ 127266494Sgshapiro 127366494Sgshapiro 127464562Sgshapiro message("250-ENHANCEDSTATUSCODES"); 127538032Speter if (!bitset(PRIV_NOEXPN, PrivacyFlags)) 127638032Speter { 127738032Speter message("250-EXPN"); 127838032Speter if (!bitset(PRIV_NOVERB, PrivacyFlags)) 127938032Speter message("250-VERB"); 128038032Speter } 128164562Sgshapiro# if MIME8TO7 128238032Speter message("250-8BITMIME"); 128364562Sgshapiro# endif /* MIME8TO7 */ 128438032Speter if (MaxMessageSize > 0) 128538032Speter message("250-SIZE %ld", MaxMessageSize); 128638032Speter else 128738032Speter message("250-SIZE"); 128864562Sgshapiro# if DSN 128964562Sgshapiro if (SendMIMEErrors && 129064562Sgshapiro !bitset(PRIV_NORECEIPTS, PrivacyFlags)) 129138032Speter message("250-DSN"); 129264562Sgshapiro# endif /* DSN */ 129338032Speter message("250-ONEX"); 129464562Sgshapiro if (!bitset(PRIV_NOETRN, PrivacyFlags) && 129564562Sgshapiro !bitnset(D_NOETRN, d_flags)) 129638032Speter message("250-ETRN"); 129738032Speter message("250-XUSR"); 129864562Sgshapiro 129964562Sgshapiro# if SASL 130064562Sgshapiro if (sasl_ok && mechlist != NULL && *mechlist != '\0') 130164562Sgshapiro message("250-AUTH %s", mechlist); 130264562Sgshapiro# endif /* SASL */ 130364562Sgshapiro# if STARTTLS 130464562Sgshapiro if (tls_ok && usetls) 130564562Sgshapiro message("250-STARTTLS"); 130664562Sgshapiro# endif /* STARTTLS */ 130738032Speter message("250 HELP"); 130838032Speter break; 130938032Speter 131038032Speter case CMDMAIL: /* mail -- designate sender */ 131138032Speter SmtpPhase = "server MAIL"; 131238032Speter 131338032Speter /* check for validity of this command */ 131438032Speter if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) 131538032Speter { 131664562Sgshapiro usrerr("503 5.0.0 Polite people say HELO first"); 131738032Speter break; 131838032Speter } 131938032Speter if (gotmail) 132038032Speter { 132164562Sgshapiro usrerr("503 5.5.0 Sender already specified"); 132238032Speter break; 132338032Speter } 132438032Speter if (InChild) 132538032Speter { 132638032Speter errno = 0; 132764562Sgshapiro syserr("503 5.5.0 Nested MAIL command: MAIL %s", p); 132842575Speter finis(TRUE, ExitStat); 132938032Speter } 133064562Sgshapiro# if SASL 133164562Sgshapiro if (bitnset(D_AUTHREQ, d_flags) && 133264562Sgshapiro authenticating != SASL_IS_AUTH) 133364562Sgshapiro { 133464562Sgshapiro usrerr("530 5.7.0 Authentication required"); 133564562Sgshapiro break; 133664562Sgshapiro } 133764562Sgshapiro# endif /* SASL */ 133838032Speter 133964562Sgshapiro p = skipword(p, "from"); 134064562Sgshapiro if (p == NULL) 134164562Sgshapiro break; 134264562Sgshapiro if (tempfail) 134364562Sgshapiro { 134464562Sgshapiro if (LogLevel > 9) 134564562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 134664562Sgshapiro "SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)", 134764562Sgshapiro p, CurSmtpClient); 134864562Sgshapiro usrerr("451 4.7.1 Please try again later"); 134964562Sgshapiro break; 135064562Sgshapiro } 135164562Sgshapiro 135238032Speter /* make sure we know who the sending host is */ 135338032Speter if (sendinghost == NULL) 135438032Speter sendinghost = peerhostname; 135538032Speter 135638032Speter 135738032Speter /* fork a subprocess to process this command */ 135864562Sgshapiro ric = runinchild("SMTP-MAIL", e); 135964562Sgshapiro 136064562Sgshapiro /* Catch a problem and stop processing */ 136164562Sgshapiro if (ric == RIC_TEMPFAIL && nullserver == NULL) 136264562Sgshapiro nullserver = "452 4.3.0 Internal software error"; 136364562Sgshapiro if (ric != RIC_INCHILD) 136438032Speter break; 136564562Sgshapiro 136638032Speter if (Errors > 0) 136738032Speter goto undo_subproc_no_pm; 136838032Speter if (!gothello) 136938032Speter { 137038032Speter auth_warning(e, 137138032Speter "%s didn't use HELO protocol", 137238032Speter CurSmtpClient); 137338032Speter } 137464562Sgshapiro# ifdef PICKY_HELO_CHECK 137538032Speter if (strcasecmp(sendinghost, peerhostname) != 0 && 137638032Speter (strcasecmp(peerhostname, "localhost") != 0 || 137738032Speter strcasecmp(sendinghost, MyHostName) != 0)) 137838032Speter { 137938032Speter auth_warning(e, "Host %s claimed to be %s", 138038032Speter CurSmtpClient, sendinghost); 138138032Speter } 138264562Sgshapiro# endif /* PICKY_HELO_CHECK */ 138338032Speter 138438032Speter if (protocol == NULL) 138538032Speter protocol = "SMTP"; 138638032Speter define('r', protocol, e); 138738032Speter define('s', sendinghost, e); 138864562Sgshapiro 138938032Speter if (Errors > 0) 139038032Speter goto undo_subproc_no_pm; 139138032Speter nrcpts = 0; 139264562Sgshapiro define(macid("{ntries}", NULL), "0", e); 139364562Sgshapiro e->e_flags |= EF_CLRQUEUE; 139464562Sgshapiro sm_setproctitle(TRUE, e, "%s %s: %.80s", 139564562Sgshapiro qid_printname(e), 139664562Sgshapiro CurSmtpClient, inp); 139738032Speter 139838032Speter /* child -- go do the processing */ 139938032Speter if (setjmp(TopFrame) > 0) 140038032Speter { 140138032Speter /* this failed -- undo work */ 140238032Speter undo_subproc_no_pm: 140338032Speter e->e_flags &= ~EF_PM_NOTIFY; 140438032Speter undo_subproc: 140538032Speter if (InChild) 140638032Speter { 140738032Speter QuickAbort = FALSE; 140838032Speter SuprErrs = TRUE; 140938032Speter e->e_flags &= ~EF_FATALERRS; 141064562Sgshapiro 141164562Sgshapiro if (LogLevel > 4 && 141264562Sgshapiro bitset(EF_LOGSENDER, e->e_flags)) 141364562Sgshapiro logsender(e, NULL); 141464562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 141564562Sgshapiro 141642575Speter finis(TRUE, ExitStat); 141738032Speter } 141838032Speter break; 141938032Speter } 142038032Speter QuickAbort = TRUE; 142138032Speter 142238032Speter /* must parse sender first */ 142338032Speter delimptr = NULL; 142438032Speter setsender(p, e, &delimptr, ' ', FALSE); 142538032Speter if (delimptr != NULL && *delimptr != '\0') 142638032Speter *delimptr++ = '\0'; 142738032Speter if (Errors > 0) 142838032Speter goto undo_subproc_no_pm; 142938032Speter 143064562Sgshapiro /* Successfully set e_from, allow logging */ 143164562Sgshapiro e->e_flags |= EF_LOGSENDER; 143238032Speter 143364562Sgshapiro /* put resulting triple from parseaddr() into macros */ 143464562Sgshapiro if (e->e_from.q_mailer != NULL) 143564562Sgshapiro define(macid("{mail_mailer}", NULL), 143664562Sgshapiro e->e_from.q_mailer->m_name, e); 143764562Sgshapiro else 143864562Sgshapiro define(macid("{mail_mailer}", NULL), 143964562Sgshapiro NULL, e); 144064562Sgshapiro if (e->e_from.q_host != NULL) 144164562Sgshapiro define(macid("{mail_host}", NULL), 144264562Sgshapiro e->e_from.q_host, e); 144364562Sgshapiro else 144464562Sgshapiro define(macid("{mail_host}", NULL), 144564562Sgshapiro "localhost", e); 144664562Sgshapiro if (e->e_from.q_user != NULL) 144764562Sgshapiro define(macid("{mail_addr}", NULL), 144864562Sgshapiro e->e_from.q_user, e); 144964562Sgshapiro else 145064562Sgshapiro define(macid("{mail_addr}", NULL), 145164562Sgshapiro NULL, e); 145264562Sgshapiro if (Errors > 0) 145364562Sgshapiro goto undo_subproc_no_pm; 145464562Sgshapiro 145538032Speter /* check for possible spoofing */ 145638032Speter if (RealUid != 0 && OpMode == MD_SMTP && 145738032Speter !wordinclass(RealUserName, 't') && 145864562Sgshapiro (!bitnset(M_LOCALMAILER, 145964562Sgshapiro e->e_from.q_mailer->m_flags) || 146064562Sgshapiro strcmp(e->e_from.q_user, RealUserName) != 0)) 146138032Speter { 146238032Speter auth_warning(e, "%s owned process doing -bs", 146338032Speter RealUserName); 146438032Speter } 146538032Speter 146638032Speter /* now parse ESMTP arguments */ 146738032Speter e->e_msgsize = 0; 146864562Sgshapiro addr = p; 146964562Sgshapiro argno = 0; 147064562Sgshapiro args[argno++] = p; 147138032Speter p = delimptr; 147238032Speter while (p != NULL && *p != '\0') 147338032Speter { 147438032Speter char *kp; 147538032Speter char *vp = NULL; 147664562Sgshapiro char *equal = NULL; 147738032Speter 147838032Speter /* locate the beginning of the keyword */ 147938032Speter while (isascii(*p) && isspace(*p)) 148038032Speter p++; 148138032Speter if (*p == '\0') 148238032Speter break; 148338032Speter kp = p; 148438032Speter 148538032Speter /* skip to the value portion */ 148638032Speter while ((isascii(*p) && isalnum(*p)) || *p == '-') 148738032Speter p++; 148838032Speter if (*p == '=') 148938032Speter { 149064562Sgshapiro equal = p; 149138032Speter *p++ = '\0'; 149238032Speter vp = p; 149338032Speter 149438032Speter /* skip to the end of the value */ 149538032Speter while (*p != '\0' && *p != ' ' && 149638032Speter !(isascii(*p) && iscntrl(*p)) && 149738032Speter *p != '=') 149838032Speter p++; 149938032Speter } 150038032Speter 150138032Speter if (*p != '\0') 150238032Speter *p++ = '\0'; 150338032Speter 150438032Speter if (tTd(19, 1)) 150564562Sgshapiro dprintf("MAIL: got arg %s=\"%s\"\n", kp, 150638032Speter vp == NULL ? "<null>" : vp); 150738032Speter 150838032Speter mail_esmtp_args(kp, vp, e); 150964562Sgshapiro if (equal != NULL) 151064562Sgshapiro *equal = '='; 151164562Sgshapiro args[argno++] = kp; 151264562Sgshapiro if (argno >= MAXSMTPARGS - 1) 151364562Sgshapiro usrerr("501 5.5.4 Too many parameters"); 151438032Speter if (Errors > 0) 151538032Speter goto undo_subproc_no_pm; 151638032Speter } 151764562Sgshapiro args[argno] = NULL; 151838032Speter if (Errors > 0) 151938032Speter goto undo_subproc_no_pm; 152038032Speter 152164562Sgshapiro /* do config file checking of the sender */ 152264562Sgshapiro if (rscheck("check_mail", addr, 152364562Sgshapiro NULL, e, TRUE, TRUE, 4) != EX_OK || 152464562Sgshapiro Errors > 0) 152564562Sgshapiro goto undo_subproc_no_pm; 152664562Sgshapiro 152766494Sgshapiro if (MaxMessageSize > 0 && 152866494Sgshapiro (e->e_msgsize > MaxMessageSize || e->e_msgsize < 0)) 152938032Speter { 153064562Sgshapiro usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)", 153138032Speter MaxMessageSize); 153238032Speter goto undo_subproc_no_pm; 153338032Speter } 153464562Sgshapiro 153564562Sgshapiro if (!enoughdiskspace(e->e_msgsize, TRUE)) 153638032Speter { 153764562Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 153838032Speter goto undo_subproc_no_pm; 153938032Speter } 154038032Speter if (Errors > 0) 154138032Speter goto undo_subproc_no_pm; 154264562Sgshapiro 154364562Sgshapiro# if _FFR_MILTER 154464562Sgshapiro LogUsrErrs = TRUE; 154564562Sgshapiro if (milterize && !bitset(EF_DISCARD, e->e_flags)) 154664562Sgshapiro { 154764562Sgshapiro char state; 154864562Sgshapiro char *response; 154964562Sgshapiro 155064562Sgshapiro response = milter_envfrom(args, e, &state); 155164562Sgshapiro switch (state) 155264562Sgshapiro { 155364562Sgshapiro case SMFIR_REPLYCODE: 155464562Sgshapiro usrerr(response); 155564562Sgshapiro break; 155664562Sgshapiro 155764562Sgshapiro case SMFIR_REJECT: 155864562Sgshapiro usrerr("550 5.7.1 Command rejected"); 155964562Sgshapiro break; 156064562Sgshapiro 156164562Sgshapiro case SMFIR_DISCARD: 156264562Sgshapiro e->e_flags |= EF_DISCARD; 156364562Sgshapiro break; 156464562Sgshapiro 156564562Sgshapiro case SMFIR_TEMPFAIL: 156666494Sgshapiro usrerr("451 4.7.1 Please try again later"); 156764562Sgshapiro break; 156864562Sgshapiro } 156964562Sgshapiro if (response != NULL) 157064562Sgshapiro free(response); 157164562Sgshapiro } 157264562Sgshapiro# endif /* _FFR_MILTER */ 157364562Sgshapiro if (Errors > 0) 157464562Sgshapiro goto undo_subproc_no_pm; 157564562Sgshapiro 157664562Sgshapiro message("250 2.1.0 Sender ok"); 157738032Speter gotmail = TRUE; 157838032Speter break; 157938032Speter 158038032Speter case CMDRCPT: /* rcpt -- designate recipient */ 158138032Speter if (!gotmail) 158238032Speter { 158364562Sgshapiro usrerr("503 5.0.0 Need MAIL before RCPT"); 158438032Speter break; 158538032Speter } 158638032Speter SmtpPhase = "server RCPT"; 158738032Speter if (setjmp(TopFrame) > 0) 158838032Speter { 158964562Sgshapiro e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY); 159038032Speter break; 159138032Speter } 159238032Speter QuickAbort = TRUE; 159338032Speter LogUsrErrs = TRUE; 159438032Speter 159538032Speter /* limit flooding of our machine */ 159638032Speter if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg) 159738032Speter { 159864562Sgshapiro usrerr("452 4.5.3 Too many recipients"); 159938032Speter break; 160038032Speter } 160138032Speter 160238032Speter if (e->e_sendmode != SM_DELIVER) 160338032Speter e->e_flags |= EF_VRFYONLY; 160438032Speter 160564562Sgshapiro# if _FFR_MILTER 160664562Sgshapiro /* 160764562Sgshapiro ** If the filter will be deleting recipients, 160864562Sgshapiro ** don't expand them at RCPT time (in the call 160964562Sgshapiro ** to recipient()). If they are expanded, it 161064562Sgshapiro ** is impossible for removefromlist() to figure 161164562Sgshapiro ** out the expanded members of the original 161264562Sgshapiro ** recipient and mark them as QS_DONTSEND. 161364562Sgshapiro */ 161464562Sgshapiro 161564562Sgshapiro if (milter_can_delrcpts()) 161664562Sgshapiro e->e_flags |= EF_VRFYONLY; 161764562Sgshapiro# endif /* _FFR_MILTER */ 161864562Sgshapiro 161938032Speter p = skipword(p, "to"); 162038032Speter if (p == NULL) 162138032Speter break; 162264562Sgshapiro# if _FFR_ADDR_TYPE 162364562Sgshapiro define(macid("{addr_type}", NULL), "e r", e); 162464562Sgshapiro# endif /* _FFR_ADDR_TYPE */ 162538032Speter a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); 162664562Sgshapiro#if _FFR_ADDR_TYPE 162764562Sgshapiro define(macid("{addr_type}", NULL), NULL, e); 162864562Sgshapiro#endif /* _FFR_ADDR_TYPE */ 162942575Speter if (Errors > 0) 163038032Speter break; 163142575Speter if (a == NULL) 163242575Speter { 163364562Sgshapiro usrerr("501 5.0.0 Missing recipient"); 163442575Speter break; 163542575Speter } 163642575Speter 163738032Speter if (delimptr != NULL && *delimptr != '\0') 163838032Speter *delimptr++ = '\0'; 163938032Speter 164064562Sgshapiro /* put resulting triple from parseaddr() into macros */ 164164562Sgshapiro if (a->q_mailer != NULL) 164264562Sgshapiro define(macid("{rcpt_mailer}", NULL), 164364562Sgshapiro a->q_mailer->m_name, e); 164464562Sgshapiro else 164564562Sgshapiro define(macid("{rcpt_mailer}", NULL), 164664562Sgshapiro NULL, e); 164764562Sgshapiro if (a->q_host != NULL) 164864562Sgshapiro define(macid("{rcpt_host}", NULL), 164964562Sgshapiro a->q_host, e); 165064562Sgshapiro else 165164562Sgshapiro define(macid("{rcpt_host}", NULL), 165264562Sgshapiro "localhost", e); 165364562Sgshapiro if (a->q_user != NULL) 165464562Sgshapiro define(macid("{rcpt_addr}", NULL), 165564562Sgshapiro a->q_user, e); 165664562Sgshapiro else 165764562Sgshapiro define(macid("{rcpt_addr}", NULL), 165864562Sgshapiro NULL, e); 165964562Sgshapiro if (Errors > 0) 166038032Speter break; 166138032Speter 166238032Speter /* now parse ESMTP arguments */ 166364562Sgshapiro addr = p; 166464562Sgshapiro argno = 0; 166564562Sgshapiro args[argno++] = p; 166638032Speter p = delimptr; 166738032Speter while (p != NULL && *p != '\0') 166838032Speter { 166938032Speter char *kp; 167038032Speter char *vp = NULL; 167164562Sgshapiro char *equal = NULL; 167238032Speter 167338032Speter /* locate the beginning of the keyword */ 167438032Speter while (isascii(*p) && isspace(*p)) 167538032Speter p++; 167638032Speter if (*p == '\0') 167738032Speter break; 167838032Speter kp = p; 167938032Speter 168038032Speter /* skip to the value portion */ 168138032Speter while ((isascii(*p) && isalnum(*p)) || *p == '-') 168238032Speter p++; 168338032Speter if (*p == '=') 168438032Speter { 168564562Sgshapiro equal = p; 168638032Speter *p++ = '\0'; 168738032Speter vp = p; 168838032Speter 168938032Speter /* skip to the end of the value */ 169038032Speter while (*p != '\0' && *p != ' ' && 169138032Speter !(isascii(*p) && iscntrl(*p)) && 169238032Speter *p != '=') 169338032Speter p++; 169438032Speter } 169538032Speter 169638032Speter if (*p != '\0') 169738032Speter *p++ = '\0'; 169838032Speter 169938032Speter if (tTd(19, 1)) 170064562Sgshapiro dprintf("RCPT: got arg %s=\"%s\"\n", kp, 170138032Speter vp == NULL ? "<null>" : vp); 170238032Speter 170338032Speter rcpt_esmtp_args(a, kp, vp, e); 170464562Sgshapiro if (equal != NULL) 170564562Sgshapiro *equal = '='; 170664562Sgshapiro args[argno++] = kp; 170764562Sgshapiro if (argno >= MAXSMTPARGS - 1) 170864562Sgshapiro usrerr("501 5.5.4 Too many parameters"); 170938032Speter if (Errors > 0) 171038032Speter break; 171138032Speter } 171264562Sgshapiro args[argno] = NULL; 171338032Speter if (Errors > 0) 171438032Speter break; 171538032Speter 171664562Sgshapiro /* do config file checking of the recipient */ 171764562Sgshapiro if (rscheck("check_rcpt", addr, 171864562Sgshapiro NULL, e, TRUE, TRUE, 4) != EX_OK || 171964562Sgshapiro Errors > 0) 172064562Sgshapiro break; 172164562Sgshapiro 172264562Sgshapiro# if _FFR_MILTER 172364562Sgshapiro if (milterize && !bitset(EF_DISCARD, e->e_flags)) 172464562Sgshapiro { 172564562Sgshapiro char state; 172664562Sgshapiro char *response; 172764562Sgshapiro 172864562Sgshapiro response = milter_envrcpt(args, e, &state); 172964562Sgshapiro switch (state) 173064562Sgshapiro { 173164562Sgshapiro case SMFIR_REPLYCODE: 173264562Sgshapiro usrerr(response); 173364562Sgshapiro break; 173464562Sgshapiro 173564562Sgshapiro case SMFIR_REJECT: 173664562Sgshapiro usrerr("550 5.7.1 Command rejected"); 173764562Sgshapiro break; 173864562Sgshapiro 173964562Sgshapiro case SMFIR_DISCARD: 174064562Sgshapiro e->e_flags |= EF_DISCARD; 174164562Sgshapiro break; 174264562Sgshapiro 174364562Sgshapiro case SMFIR_TEMPFAIL: 174466494Sgshapiro usrerr("451 4.7.1 Please try again later"); 174564562Sgshapiro break; 174664562Sgshapiro } 174764562Sgshapiro if (response != NULL) 174864562Sgshapiro free(response); 174964562Sgshapiro } 175064562Sgshapiro# endif /* _FFR_MILTER */ 175164562Sgshapiro 175264562Sgshapiro define(macid("{rcpt_mailer}", NULL), NULL, e); 175364562Sgshapiro define(macid("{rcpt_relay}", NULL), NULL, e); 175464562Sgshapiro define(macid("{rcpt_addr}", NULL), NULL, e); 175564562Sgshapiro define(macid("{dsn_notify}", NULL), NULL, e); 175664562Sgshapiro if (Errors > 0) 175764562Sgshapiro break; 175864562Sgshapiro 175938032Speter /* save in recipient list after ESMTP mods */ 176038032Speter a = recipient(a, &e->e_sendqueue, 0, e); 176138032Speter if (Errors > 0) 176238032Speter break; 176338032Speter 176438032Speter /* no errors during parsing, but might be a duplicate */ 176538032Speter e->e_to = a->q_paddr; 176664562Sgshapiro if (!QS_IS_BADADDR(a->q_state)) 176738032Speter { 176864562Sgshapiro if (e->e_queuedir == NOQDIR) 176964562Sgshapiro initsys(e); 177064562Sgshapiro message("250 2.1.5 Recipient ok%s", 177164562Sgshapiro QS_IS_QUEUEUP(a->q_state) ? 177238032Speter " (will queue)" : ""); 177338032Speter nrcpts++; 177438032Speter } 177538032Speter else 177638032Speter { 177738032Speter /* punt -- should keep message in ADDRESS.... */ 177864562Sgshapiro usrerr("550 5.1.1 Addressee unknown"); 177938032Speter } 178038032Speter break; 178138032Speter 178238032Speter case CMDDATA: /* data -- text of mail */ 178338032Speter SmtpPhase = "server DATA"; 178438032Speter if (!gotmail) 178538032Speter { 178664562Sgshapiro usrerr("503 5.0.0 Need MAIL command"); 178738032Speter break; 178838032Speter } 178938032Speter else if (nrcpts <= 0) 179038032Speter { 179164562Sgshapiro usrerr("503 5.0.0 Need RCPT (recipient)"); 179238032Speter break; 179338032Speter } 179438032Speter 179538032Speter /* put back discard bit */ 179638032Speter if (discard) 179738032Speter e->e_flags |= EF_DISCARD; 179838032Speter 179938032Speter /* check to see if we need to re-expand aliases */ 180064562Sgshapiro /* also reset QS_BADADDR on already-diagnosted addrs */ 180138032Speter doublequeue = FALSE; 180238032Speter for (a = e->e_sendqueue; a != NULL; a = a->q_next) 180338032Speter { 180464562Sgshapiro if (QS_IS_VERIFIED(a->q_state) && 180538032Speter !bitset(EF_DISCARD, e->e_flags)) 180638032Speter { 180738032Speter /* need to re-expand aliases */ 180838032Speter doublequeue = TRUE; 180938032Speter } 181064562Sgshapiro if (QS_IS_BADADDR(a->q_state)) 181138032Speter { 181238032Speter /* make this "go away" */ 181364562Sgshapiro a->q_state = QS_DONTSEND; 181438032Speter } 181538032Speter } 181638032Speter 181738032Speter /* collect the text of the message */ 181838032Speter SmtpPhase = "collect"; 181938032Speter buffer_errors(); 182038032Speter collect(InChannel, TRUE, NULL, e); 182164562Sgshapiro 182264562Sgshapiro# if _FFR_MILTER 182364562Sgshapiro if (milterize && 182464562Sgshapiro Errors <= 0 && 182564562Sgshapiro !bitset(EF_DISCARD, e->e_flags)) 182664562Sgshapiro { 182764562Sgshapiro char state; 182864562Sgshapiro char *response; 182964562Sgshapiro 183064562Sgshapiro response = milter_data(e, &state); 183164562Sgshapiro switch (state) 183264562Sgshapiro { 183364562Sgshapiro case SMFIR_REPLYCODE: 183464562Sgshapiro usrerr(response); 183564562Sgshapiro break; 183664562Sgshapiro 183764562Sgshapiro case SMFIR_REJECT: 183864562Sgshapiro usrerr("554 5.7.1 Command rejected"); 183964562Sgshapiro break; 184064562Sgshapiro 184164562Sgshapiro case SMFIR_DISCARD: 184264562Sgshapiro e->e_flags |= EF_DISCARD; 184364562Sgshapiro break; 184464562Sgshapiro 184564562Sgshapiro case SMFIR_TEMPFAIL: 184666494Sgshapiro usrerr("451 4.7.1 Please try again later"); 184764562Sgshapiro break; 184864562Sgshapiro } 184964562Sgshapiro if (response != NULL) 185064562Sgshapiro free(response); 185164562Sgshapiro } 185264562Sgshapiro 185364562Sgshapiro /* abort message filters that didn't get the body */ 185464562Sgshapiro if (milterize) 185564562Sgshapiro milter_abort(e); 185664562Sgshapiro# endif /* _FFR_MILTER */ 185764562Sgshapiro 185864562Sgshapiro /* redefine message size */ 185964562Sgshapiro if ((q = macvalue(macid("{msg_size}", NULL), e)) 186064562Sgshapiro != NULL) 186164562Sgshapiro free(q); 186264562Sgshapiro snprintf(inp, sizeof inp, "%ld", e->e_msgsize); 186364562Sgshapiro define(macid("{msg_size}", NULL), newstr(inp), e); 186438032Speter if (Errors > 0) 186538032Speter { 186664562Sgshapiro /* Log who the mail would have gone to */ 186764562Sgshapiro if (LogLevel > 8 && 186864562Sgshapiro e->e_message != NULL) 186964562Sgshapiro { 187064562Sgshapiro for (a = e->e_sendqueue; 187164562Sgshapiro a != NULL; 187264562Sgshapiro a = a->q_next) 187364562Sgshapiro { 187464562Sgshapiro if (!QS_IS_UNDELIVERED(a->q_state)) 187564562Sgshapiro continue; 187664562Sgshapiro 187764562Sgshapiro e->e_to = a->q_paddr; 187864562Sgshapiro logdelivery(NULL, NULL, 187964562Sgshapiro a->q_status, 188064562Sgshapiro e->e_message, 188164562Sgshapiro NULL, 188264562Sgshapiro (time_t) 0, e); 188364562Sgshapiro } 188464562Sgshapiro e->e_to = NULL; 188564562Sgshapiro } 188638032Speter flush_errors(TRUE); 188738032Speter buffer_errors(); 188838032Speter goto abortmessage; 188938032Speter } 189038032Speter 189138032Speter /* make sure we actually do delivery */ 189238032Speter e->e_flags &= ~EF_CLRQUEUE; 189338032Speter 189438032Speter /* from now on, we have to operate silently */ 189538032Speter buffer_errors(); 189638032Speter e->e_errormode = EM_MAIL; 189738032Speter 189838032Speter /* 189938032Speter ** Arrange to send to everyone. 190038032Speter ** If sending to multiple people, mail back 190138032Speter ** errors rather than reporting directly. 190238032Speter ** In any case, don't mail back errors for 190338032Speter ** anything that has happened up to 190438032Speter ** now (the other end will do this). 190538032Speter ** Truncate our transcript -- the mail has gotten 190638032Speter ** to us successfully, and if we have 190738032Speter ** to mail this back, it will be easier 190838032Speter ** on the reader. 190938032Speter ** Then send to everyone. 191038032Speter ** Finally give a reply code. If an error has 191138032Speter ** already been given, don't mail a 191238032Speter ** message back. 191338032Speter ** We goose error returns by clearing error bit. 191438032Speter */ 191538032Speter 191638032Speter SmtpPhase = "delivery"; 191764562Sgshapiro (void) bftruncate(e->e_xfp); 191838032Speter id = e->e_id; 191938032Speter 192064562Sgshapiro /* 192164562Sgshapiro ** If a header/body check (header checks or milter) 192264562Sgshapiro ** set EF_DISCARD, don't queueup the message -- 192364562Sgshapiro ** that would lose the EF_DISCARD bit and deliver 192464562Sgshapiro ** the message. 192564562Sgshapiro */ 192664562Sgshapiro 192764562Sgshapiro if (bitset(EF_DISCARD, e->e_flags)) 192864562Sgshapiro doublequeue = FALSE; 192964562Sgshapiro 193038032Speter if (doublequeue) 193138032Speter { 193238032Speter /* make sure it is in the queue */ 193338032Speter queueup(e, FALSE); 193438032Speter } 193538032Speter else 193638032Speter { 193738032Speter /* send to all recipients */ 193864562Sgshapiro# if NAMED_BIND 193964562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 194064562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 194164562Sgshapiro# endif /* NAMED_BIND */ 194238032Speter sendall(e, SM_DEFAULT); 194338032Speter } 194438032Speter e->e_to = NULL; 194538032Speter 194638032Speter /* issue success message */ 194764562Sgshapiro message("250 2.0.0 %s Message accepted for delivery", id); 194838032Speter 194938032Speter /* if we just queued, poke it */ 195038032Speter if (doublequeue && 195138032Speter e->e_sendmode != SM_QUEUE && 195238032Speter e->e_sendmode != SM_DEFER) 195338032Speter { 195464562Sgshapiro CurrentLA = sm_getla(e); 195538032Speter 195638032Speter if (!shouldqueue(e->e_msgpriority, e->e_ctime)) 195738032Speter { 195864562Sgshapiro /* close all the queue files */ 195964562Sgshapiro closexscript(e); 196064562Sgshapiro if (e->e_dfp != NULL) 196164562Sgshapiro (void) bfclose(e->e_dfp); 196264562Sgshapiro e->e_dfp = NULL; 196338032Speter unlockqueue(e); 196464562Sgshapiro 196564562Sgshapiro (void) dowork(e->e_queuedir, id, 196664562Sgshapiro TRUE, TRUE, e); 196738032Speter } 196838032Speter } 196938032Speter 197038032Speter abortmessage: 197164562Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 197264562Sgshapiro logsender(e, NULL); 197364562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 197464562Sgshapiro 197538032Speter /* if in a child, pop back to our parent */ 197638032Speter if (InChild) 197742575Speter finis(TRUE, ExitStat); 197838032Speter 197938032Speter /* clean up a bit */ 198038032Speter gotmail = FALSE; 198138032Speter dropenvelope(e, TRUE); 198238032Speter CurEnv = e = newenvelope(e, CurEnv); 198338032Speter e->e_flags = BlankEnvelope.e_flags; 198438032Speter break; 198538032Speter 198638032Speter case CMDRSET: /* rset -- reset state */ 198764562Sgshapiro# if _FFR_MILTER 198864562Sgshapiro /* abort milter filters */ 198964562Sgshapiro milter_abort(e); 199064562Sgshapiro# endif /* _FFR_MILTER */ 199164562Sgshapiro 199238032Speter if (tTd(94, 100)) 199364562Sgshapiro message("451 4.0.0 Test failure"); 199438032Speter else 199564562Sgshapiro message("250 2.0.0 Reset state"); 199638032Speter 199738032Speter /* arrange to ignore any current send list */ 199838032Speter e->e_sendqueue = NULL; 199938032Speter e->e_flags |= EF_CLRQUEUE; 200064562Sgshapiro 200164562Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 200264562Sgshapiro logsender(e, NULL); 200364562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 200464562Sgshapiro 200538032Speter if (InChild) 200642575Speter finis(TRUE, ExitStat); 200738032Speter 200838032Speter /* clean up a bit */ 200938032Speter gotmail = FALSE; 201038032Speter SuprErrs = TRUE; 201138032Speter dropenvelope(e, TRUE); 201238032Speter CurEnv = e = newenvelope(e, CurEnv); 201338032Speter break; 201438032Speter 201538032Speter case CMDVRFY: /* vrfy -- verify address */ 201638032Speter case CMDEXPN: /* expn -- expand address */ 201764562Sgshapiro if (tempfail) 201864562Sgshapiro { 201964562Sgshapiro if (LogLevel > 9) 202064562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 202164562Sgshapiro "SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)", 202264562Sgshapiro c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", 202364562Sgshapiro p, CurSmtpClient); 202464562Sgshapiro usrerr("550 5.7.1 Please try again later"); 202564562Sgshapiro break; 202664562Sgshapiro } 202764562Sgshapiro wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE, 202864562Sgshapiro c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e); 202964562Sgshapiro previous = curtime(); 203064562Sgshapiro vrfy = c->cmd_code == CMDVRFY; 203138032Speter if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, 203238032Speter PrivacyFlags)) 203338032Speter { 203438032Speter if (vrfy) 203564562Sgshapiro message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); 203638032Speter else 203764562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 203838032Speter if (LogLevel > 5) 203938032Speter sm_syslog(LOG_INFO, e->e_id, 204064562Sgshapiro "%.100s: %s [rejected]", 204164562Sgshapiro CurSmtpClient, 204264562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 204338032Speter break; 204438032Speter } 204538032Speter else if (!gothello && 204638032Speter bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, 204738032Speter PrivacyFlags)) 204838032Speter { 204964562Sgshapiro usrerr("503 5.0.0 I demand that you introduce yourself first"); 205038032Speter break; 205138032Speter } 205238032Speter if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) 205338032Speter break; 205438032Speter if (Errors > 0) 205538032Speter goto undo_subproc; 205638032Speter if (LogLevel > 5) 205738032Speter sm_syslog(LOG_INFO, e->e_id, 205864562Sgshapiro "%.100s: %s", 205964562Sgshapiro CurSmtpClient, 206064562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 206138032Speter if (setjmp(TopFrame) > 0) 206238032Speter goto undo_subproc; 206338032Speter QuickAbort = TRUE; 206438032Speter vrfyqueue = NULL; 206538032Speter if (vrfy) 206638032Speter e->e_flags |= EF_VRFYONLY; 206738032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 206838032Speter p++; 206938032Speter if (*p == '\0') 207038032Speter { 207164562Sgshapiro usrerr("501 5.5.2 Argument required"); 207238032Speter } 207338032Speter else 207438032Speter { 207564562Sgshapiro /* do config file checking of the address */ 207664562Sgshapiro if (rscheck(vrfy ? "check_vrfy" : "check_expn", 207764562Sgshapiro p, NULL, e, TRUE, FALSE, 4) 207864562Sgshapiro != EX_OK || Errors > 0) 207964562Sgshapiro goto undo_subproc; 208038032Speter (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); 208138032Speter } 208264562Sgshapiro if (wt > 0) 208364562Sgshapiro (void) sleep(wt - (curtime() - previous)); 208438032Speter if (Errors > 0) 208538032Speter goto undo_subproc; 208638032Speter if (vrfyqueue == NULL) 208738032Speter { 208864562Sgshapiro usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN"); 208938032Speter } 209038032Speter while (vrfyqueue != NULL) 209138032Speter { 209264562Sgshapiro if (!QS_IS_UNDELIVERED(vrfyqueue->q_state)) 209364562Sgshapiro { 209464562Sgshapiro vrfyqueue = vrfyqueue->q_next; 209564562Sgshapiro continue; 209664562Sgshapiro } 209738032Speter 209864562Sgshapiro /* see if there is more in the vrfy list */ 209938032Speter a = vrfyqueue; 210038032Speter while ((a = a->q_next) != NULL && 210166494Sgshapiro (!QS_IS_UNDELIVERED(a->q_state))) 210238032Speter continue; 210364562Sgshapiro printvrfyaddr(vrfyqueue, a == NULL, vrfy); 210464562Sgshapiro vrfyqueue = a; 210538032Speter } 210638032Speter if (InChild) 210742575Speter finis(TRUE, ExitStat); 210838032Speter break; 210938032Speter 211038032Speter case CMDETRN: /* etrn -- force queue flush */ 211164562Sgshapiro if (bitset(PRIV_NOETRN, PrivacyFlags) || 211264562Sgshapiro bitnset(D_NOETRN, d_flags)) 211338032Speter { 211464562Sgshapiro /* different message for MSA ? */ 211564562Sgshapiro message("502 5.7.0 Sorry, we do not allow this operation"); 211638032Speter if (LogLevel > 5) 211738032Speter sm_syslog(LOG_INFO, e->e_id, 211864562Sgshapiro "%.100s: %s [rejected]", 211964562Sgshapiro CurSmtpClient, 212064562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 212138032Speter break; 212238032Speter } 212364562Sgshapiro if (tempfail) 212464562Sgshapiro { 212564562Sgshapiro if (LogLevel > 9) 212664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 212764562Sgshapiro "SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)", 212864562Sgshapiro p, CurSmtpClient); 212964562Sgshapiro usrerr("451 4.7.1 Please try again later"); 213064562Sgshapiro break; 213164562Sgshapiro } 213238032Speter 213338032Speter if (strlen(p) <= 0) 213438032Speter { 213564562Sgshapiro usrerr("500 5.5.2 Parameter required"); 213638032Speter break; 213738032Speter } 213838032Speter 213938032Speter /* crude way to avoid denial-of-service attacks */ 214064562Sgshapiro (void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, TRUE, 214164562Sgshapiro "ETRN", e); 214238032Speter 214364562Sgshapiro /* do config file checking of the parameter */ 214464562Sgshapiro if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4) 214564562Sgshapiro != EX_OK || Errors > 0) 214664562Sgshapiro break; 214764562Sgshapiro 214838032Speter if (LogLevel > 5) 214938032Speter sm_syslog(LOG_INFO, e->e_id, 215064562Sgshapiro "%.100s: ETRN %s", 215164562Sgshapiro CurSmtpClient, 215264562Sgshapiro shortenstring(p, MAXSHORTSTR)); 215338032Speter 215438032Speter id = p; 215538032Speter if (*id == '@') 215638032Speter id++; 215738032Speter else 215838032Speter *--id = '@'; 215964562Sgshapiro 216038032Speter if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL) 216138032Speter { 216264562Sgshapiro syserr("500 5.5.0 ETRN out of memory"); 216338032Speter break; 216438032Speter } 216538032Speter new->queue_match = id; 216638032Speter new->queue_next = NULL; 216738032Speter QueueLimitRecipient = new; 216864562Sgshapiro ok = runqueue(TRUE, FALSE); 216938032Speter free(QueueLimitRecipient); 217038032Speter QueueLimitRecipient = NULL; 217138032Speter if (ok && Errors == 0) 217264562Sgshapiro message("250 2.0.0 Queuing for node %s started", p); 217338032Speter break; 217438032Speter 217538032Speter case CMDHELP: /* help -- give user info */ 217664562Sgshapiro help(p, e); 217738032Speter break; 217838032Speter 217938032Speter case CMDNOOP: /* noop -- do nothing */ 218064562Sgshapiro (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, 218164562Sgshapiro "NOOP", e); 218264562Sgshapiro message("250 2.0.0 OK"); 218338032Speter break; 218438032Speter 218538032Speter case CMDQUIT: /* quit -- leave mail */ 218664562Sgshapiro message("221 2.0.0 %s closing connection", MyHostName); 218738032Speter 218838032Speter /* arrange to ignore any current send list */ 218938032Speter e->e_sendqueue = NULL; 219038032Speter 219164562Sgshapiro# if STARTTLS 219264562Sgshapiro /* shutdown TLS connection */ 219364562Sgshapiro if (tls_active) 219464562Sgshapiro { 219564562Sgshapiro (void) endtls(srv_ssl, "server"); 219664562Sgshapiro tls_active = FALSE; 219764562Sgshapiro } 219864562Sgshapiro# endif /* STARTTLS */ 219964562Sgshapiro# if SASL 220064562Sgshapiro if (authenticating == SASL_IS_AUTH) 220164562Sgshapiro { 220264562Sgshapiro sasl_dispose(&conn); 220364562Sgshapiro authenticating = SASL_NOT_AUTH; 220464562Sgshapiro } 220564562Sgshapiro# endif /* SASL */ 220664562Sgshapiro 220764562Sgshapirodoquit: 220838032Speter /* avoid future 050 messages */ 220938032Speter disconnect(1, e); 221038032Speter 221164562Sgshapiro# if _FFR_MILTER 221264562Sgshapiro /* close out milter filters */ 221364562Sgshapiro milter_quit(e); 221464562Sgshapiro# endif /* _FFR_MILTER */ 221564562Sgshapiro 221638032Speter if (InChild) 221738032Speter ExitStat = EX_QUIT; 221864562Sgshapiro 221964562Sgshapiro if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags)) 222064562Sgshapiro logsender(e, NULL); 222164562Sgshapiro e->e_flags &= ~EF_LOGSENDER; 222264562Sgshapiro 222338032Speter if (lognullconnection && LogLevel > 5) 222464562Sgshapiro { 222564562Sgshapiro char *d; 222664562Sgshapiro 222764562Sgshapiro d = macvalue(macid("{daemon_name}", NULL), e); 222864562Sgshapiro if (d == NULL) 222964562Sgshapiro d = "stdin"; 223038032Speter sm_syslog(LOG_INFO, NULL, 223164562Sgshapiro "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s", 223264562Sgshapiro CurSmtpClient, d); 223364562Sgshapiro } 223442575Speter finis(TRUE, ExitStat); 223564562Sgshapiro /* NOTREACHED */ 223638032Speter 223738032Speter case CMDVERB: /* set verbose mode */ 223838032Speter if (bitset(PRIV_NOEXPN, PrivacyFlags) || 223938032Speter bitset(PRIV_NOVERB, PrivacyFlags)) 224038032Speter { 224138032Speter /* this would give out the same info */ 224264562Sgshapiro message("502 5.7.0 Verbose unavailable"); 224338032Speter break; 224438032Speter } 224564562Sgshapiro (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, 224664562Sgshapiro "VERB", e); 224738032Speter Verbose = 1; 224864562Sgshapiro set_delivery_mode(SM_DELIVER, e); 224964562Sgshapiro message("250 2.0.0 Verbose mode"); 225038032Speter break; 225138032Speter 225238032Speter case CMDONEX: /* doing one transaction only */ 225364562Sgshapiro (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, 225464562Sgshapiro "ONEX", e); 225538032Speter OneXact = TRUE; 225664562Sgshapiro message("250 2.0.0 Only one transaction"); 225738032Speter break; 225838032Speter 225938032Speter case CMDXUSR: /* initial (user) submission */ 226064562Sgshapiro (void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE, 226164562Sgshapiro "XUSR", e); 226264562Sgshapiro define(macid("{daemon_flags}", NULL), "c u", CurEnv); 226364562Sgshapiro message("250 2.0.0 Initial submission"); 226438032Speter break; 226538032Speter 226638032Speter# if SMTPDEBUG 226738032Speter case CMDDBGQSHOW: /* show queues */ 226838032Speter printf("Send Queue="); 226938032Speter printaddr(e->e_sendqueue, TRUE); 227038032Speter break; 227138032Speter 227238032Speter case CMDDBGDEBUG: /* set debug mode */ 227338032Speter tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); 227438032Speter tTflag(p); 227564562Sgshapiro message("200 2.0.0 Debug set"); 227638032Speter break; 227738032Speter 227864562Sgshapiro# else /* SMTPDEBUG */ 227938032Speter case CMDDBGQSHOW: /* show queues */ 228038032Speter case CMDDBGDEBUG: /* set debug mode */ 228138032Speter# endif /* SMTPDEBUG */ 228238032Speter case CMDLOGBOGUS: /* bogus command */ 228338032Speter if (LogLevel > 0) 228438032Speter sm_syslog(LOG_CRIT, e->e_id, 228564562Sgshapiro "\"%s\" command from %.100s (%.100s)", 228664562Sgshapiro c->cmd_name, CurSmtpClient, 228764562Sgshapiro anynet_ntoa(&RealHostAddr)); 228864562Sgshapiro /* FALLTHROUGH */ 228938032Speter 229038032Speter case CMDERROR: /* unknown command */ 229138032Speter if (++badcommands > MAXBADCOMMANDS) 229238032Speter { 229364562Sgshapiro message("421 4.7.0 %s Too many bad commands; closing connection", 229438032Speter MyHostName); 229564562Sgshapiro 229664562Sgshapiro /* arrange to ignore any current send list */ 229764562Sgshapiro e->e_sendqueue = NULL; 229838032Speter goto doquit; 229938032Speter } 230038032Speter 230164562Sgshapiro usrerr("500 5.5.1 Command unrecognized: \"%s\"", 230264562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 230338032Speter break; 230438032Speter 230564562Sgshapiro case CMDUNIMPL: 230664562Sgshapiro usrerr("502 5.5.1 Command not implemented: \"%s\"", 230764562Sgshapiro shortenstring(inp, MAXSHORTSTR)); 230864562Sgshapiro break; 230964562Sgshapiro 231038032Speter default: 231138032Speter errno = 0; 231264562Sgshapiro syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code); 231338032Speter break; 231438032Speter } 231564562Sgshapiro# if SASL 231664562Sgshapiro } 231764562Sgshapiro# endif /* SASL */ 231838032Speter } 231964562Sgshapiro 232038032Speter} 232138032Speter/* 232238032Speter** CHECKSMTPATTACK -- check for denial-of-service attack by repetition 232338032Speter** 232438032Speter** Parameters: 232538032Speter** pcounter -- pointer to a counter for this command. 232638032Speter** maxcount -- maximum value for this counter before we 232738032Speter** slow down. 232864562Sgshapiro** waitnow -- sleep now (in this routine)? 232938032Speter** cname -- command name for logging. 233038032Speter** e -- the current envelope. 233138032Speter** 233238032Speter** Returns: 233338032Speter** none. 233438032Speter** 233538032Speter** Side Effects: 233638032Speter** Slows down if we seem to be under attack. 233738032Speter*/ 233838032Speter 233964562Sgshapirostatic time_t 234064562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e) 234138032Speter volatile int *pcounter; 234238032Speter int maxcount; 234364562Sgshapiro bool waitnow; 234438032Speter char *cname; 234538032Speter ENVELOPE *e; 234638032Speter{ 234738032Speter if (++(*pcounter) >= maxcount) 234838032Speter { 234964562Sgshapiro time_t s; 235064562Sgshapiro 235138032Speter if (*pcounter == maxcount && LogLevel > 5) 235238032Speter { 235338032Speter sm_syslog(LOG_INFO, e->e_id, 235464562Sgshapiro "%.100s: %.40s attack?", 235564562Sgshapiro CurSmtpClient, cname); 235638032Speter } 235764562Sgshapiro s = 1 << (*pcounter - maxcount); 235864562Sgshapiro if (s >= MAXTIMEOUT) 235964562Sgshapiro s = MAXTIMEOUT; 236064562Sgshapiro /* sleep at least 1 second before returning */ 236164562Sgshapiro (void) sleep(*pcounter / maxcount); 236264562Sgshapiro s -= *pcounter / maxcount; 236364562Sgshapiro if (waitnow) 236464562Sgshapiro { 236564562Sgshapiro (void) sleep(s); 236664562Sgshapiro return(0); 236764562Sgshapiro } 236864562Sgshapiro return(s); 236938032Speter } 237064562Sgshapiro return((time_t) 0); 237138032Speter} 237238032Speter/* 237338032Speter** SKIPWORD -- skip a fixed word. 237438032Speter** 237538032Speter** Parameters: 237638032Speter** p -- place to start looking. 237738032Speter** w -- word to skip. 237838032Speter** 237938032Speter** Returns: 238038032Speter** p following w. 238138032Speter** NULL on error. 238238032Speter** 238338032Speter** Side Effects: 238438032Speter** clobbers the p data area. 238538032Speter*/ 238638032Speter 238738032Speterstatic char * 238838032Speterskipword(p, w) 238938032Speter register char *volatile p; 239038032Speter char *w; 239138032Speter{ 239238032Speter register char *q; 239338032Speter char *firstp = p; 239438032Speter 239538032Speter /* find beginning of word */ 239638032Speter while (isascii(*p) && isspace(*p)) 239738032Speter p++; 239838032Speter q = p; 239938032Speter 240038032Speter /* find end of word */ 240138032Speter while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) 240238032Speter p++; 240338032Speter while (isascii(*p) && isspace(*p)) 240438032Speter *p++ = '\0'; 240538032Speter if (*p != ':') 240638032Speter { 240738032Speter syntax: 240864562Sgshapiro usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"", 240938032Speter shortenstring(firstp, MAXSHORTSTR)); 241064562Sgshapiro return NULL; 241138032Speter } 241238032Speter *p++ = '\0'; 241338032Speter while (isascii(*p) && isspace(*p)) 241438032Speter p++; 241538032Speter 241638032Speter if (*p == '\0') 241738032Speter goto syntax; 241838032Speter 241938032Speter /* see if the input word matches desired word */ 242038032Speter if (strcasecmp(q, w)) 242138032Speter goto syntax; 242238032Speter 242364562Sgshapiro return p; 242438032Speter} 242538032Speter/* 242638032Speter** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line 242738032Speter** 242838032Speter** Parameters: 242938032Speter** kp -- the parameter key. 243038032Speter** vp -- the value of that parameter. 243138032Speter** e -- the envelope. 243238032Speter** 243338032Speter** Returns: 243438032Speter** none. 243538032Speter*/ 243638032Speter 243764562Sgshapirostatic void 243838032Spetermail_esmtp_args(kp, vp, e) 243938032Speter char *kp; 244038032Speter char *vp; 244138032Speter ENVELOPE *e; 244238032Speter{ 244338032Speter if (strcasecmp(kp, "size") == 0) 244438032Speter { 244538032Speter if (vp == NULL) 244638032Speter { 244764562Sgshapiro usrerr("501 5.5.2 SIZE requires a value"); 244838032Speter /* NOTREACHED */ 244938032Speter } 245064562Sgshapiro define(macid("{msg_size}", NULL), newstr(vp), e); 245166494Sgshapiro e->e_msgsize = strtol(vp, (char **) NULL, 10); 245266494Sgshapiro if (e->e_msgsize == LONG_MAX && errno == ERANGE) 245366494Sgshapiro { 245466494Sgshapiro usrerr("552 5.2.3 Message size exceeds maximum value"); 245566494Sgshapiro /* NOTREACHED */ 245666494Sgshapiro } 245738032Speter } 245838032Speter else if (strcasecmp(kp, "body") == 0) 245938032Speter { 246038032Speter if (vp == NULL) 246138032Speter { 246264562Sgshapiro usrerr("501 5.5.2 BODY requires a value"); 246338032Speter /* NOTREACHED */ 246438032Speter } 246538032Speter else if (strcasecmp(vp, "8bitmime") == 0) 246638032Speter { 246738032Speter SevenBitInput = FALSE; 246838032Speter } 246938032Speter else if (strcasecmp(vp, "7bit") == 0) 247038032Speter { 247138032Speter SevenBitInput = TRUE; 247238032Speter } 247338032Speter else 247438032Speter { 247564562Sgshapiro usrerr("501 5.5.4 Unknown BODY type %s", 247638032Speter vp); 247738032Speter /* NOTREACHED */ 247838032Speter } 247938032Speter e->e_bodytype = newstr(vp); 248038032Speter } 248138032Speter else if (strcasecmp(kp, "envid") == 0) 248238032Speter { 248364562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 248464562Sgshapiro { 248564562Sgshapiro usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN"); 248664562Sgshapiro /* NOTREACHED */ 248764562Sgshapiro } 248838032Speter if (vp == NULL) 248938032Speter { 249064562Sgshapiro usrerr("501 5.5.2 ENVID requires a value"); 249138032Speter /* NOTREACHED */ 249238032Speter } 249338032Speter if (!xtextok(vp)) 249438032Speter { 249564562Sgshapiro usrerr("501 5.5.4 Syntax error in ENVID parameter value"); 249638032Speter /* NOTREACHED */ 249738032Speter } 249838032Speter if (e->e_envid != NULL) 249938032Speter { 250064562Sgshapiro usrerr("501 5.5.0 Duplicate ENVID parameter"); 250138032Speter /* NOTREACHED */ 250238032Speter } 250338032Speter e->e_envid = newstr(vp); 250464562Sgshapiro define(macid("{dsn_envid}", NULL), newstr(vp), e); 250538032Speter } 250638032Speter else if (strcasecmp(kp, "ret") == 0) 250738032Speter { 250864562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 250964562Sgshapiro { 251064562Sgshapiro usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN"); 251164562Sgshapiro /* NOTREACHED */ 251264562Sgshapiro } 251338032Speter if (vp == NULL) 251438032Speter { 251564562Sgshapiro usrerr("501 5.5.2 RET requires a value"); 251638032Speter /* NOTREACHED */ 251738032Speter } 251838032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 251938032Speter { 252064562Sgshapiro usrerr("501 5.5.0 Duplicate RET parameter"); 252138032Speter /* NOTREACHED */ 252238032Speter } 252338032Speter e->e_flags |= EF_RET_PARAM; 252438032Speter if (strcasecmp(vp, "hdrs") == 0) 252538032Speter e->e_flags |= EF_NO_BODY_RETN; 252638032Speter else if (strcasecmp(vp, "full") != 0) 252738032Speter { 252864562Sgshapiro usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp); 252938032Speter /* NOTREACHED */ 253038032Speter } 253164562Sgshapiro define(macid("{dsn_ret}", NULL), newstr(vp), e); 253238032Speter } 253364562Sgshapiro# if SASL 253464562Sgshapiro else if (strcasecmp(kp, "auth") == 0) 253564562Sgshapiro { 253664562Sgshapiro int len; 253764562Sgshapiro char *q; 253864562Sgshapiro char *auth_param; /* the value of the AUTH=x */ 253964562Sgshapiro bool saveQuickAbort = QuickAbort; 254064562Sgshapiro bool saveSuprErrs = SuprErrs; 254164562Sgshapiro char pbuf[256]; 254264562Sgshapiro 254364562Sgshapiro if (vp == NULL) 254464562Sgshapiro { 254564562Sgshapiro usrerr("501 5.5.2 AUTH= requires a value"); 254664562Sgshapiro /* NOTREACHED */ 254764562Sgshapiro } 254864562Sgshapiro if (e->e_auth_param != NULL) 254964562Sgshapiro { 255064562Sgshapiro usrerr("501 5.5.0 Duplicate AUTH parameter"); 255164562Sgshapiro /* NOTREACHED */ 255264562Sgshapiro } 255364562Sgshapiro if ((q = strchr(vp, ' ')) != NULL) 255464562Sgshapiro len = q - vp + 1; 255564562Sgshapiro else 255664562Sgshapiro len = strlen(vp) + 1; 255764562Sgshapiro auth_param = xalloc(len); 255864562Sgshapiro (void) strlcpy(auth_param, vp, len); 255964562Sgshapiro if (!xtextok(auth_param)) 256064562Sgshapiro { 256164562Sgshapiro usrerr("501 5.5.4 Syntax error in AUTH parameter value"); 256264562Sgshapiro /* just a warning? */ 256364562Sgshapiro /* NOTREACHED */ 256464562Sgshapiro } 256564562Sgshapiro 256664562Sgshapiro /* XXX this might be cut off */ 256764562Sgshapiro snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param)); 256864562Sgshapiro /* xalloc() the buffer instead? */ 256964562Sgshapiro 257064562Sgshapiro /* XXX define this always or only if trusted? */ 257164562Sgshapiro define(macid("{auth_author}", NULL), newstr(pbuf), e); 257264562Sgshapiro 257364562Sgshapiro /* 257464562Sgshapiro ** call Strust_auth to find out whether 257564562Sgshapiro ** auth_param is acceptable (trusted) 257664562Sgshapiro ** we shouldn't trust it if not authenticated 257764562Sgshapiro ** (required by RFC, leave it to ruleset?) 257864562Sgshapiro */ 257964562Sgshapiro 258064562Sgshapiro SuprErrs = TRUE; 258164562Sgshapiro QuickAbort = FALSE; 258264562Sgshapiro if (strcmp(auth_param, "<>") != 0 && 258364562Sgshapiro (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10) 258464562Sgshapiro != EX_OK || Errors > 0)) 258564562Sgshapiro { 258664562Sgshapiro if (tTd(95, 8)) 258764562Sgshapiro { 258864562Sgshapiro q = e->e_auth_param; 258964562Sgshapiro dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n", 259064562Sgshapiro pbuf, (q == NULL) ? "" : q); 259164562Sgshapiro } 259264562Sgshapiro /* not trusted */ 259364562Sgshapiro e->e_auth_param = newstr("<>"); 259464562Sgshapiro } 259564562Sgshapiro else 259664562Sgshapiro { 259764562Sgshapiro if (tTd(95, 8)) 259864562Sgshapiro dprintf("auth=\"%.100s\" trusted\n", pbuf); 259964562Sgshapiro e->e_auth_param = newstr(auth_param); 260064562Sgshapiro } 260164562Sgshapiro free(auth_param); 260264562Sgshapiro /* reset values */ 260364562Sgshapiro Errors = 0; 260464562Sgshapiro QuickAbort = saveQuickAbort; 260564562Sgshapiro SuprErrs = saveSuprErrs; 260664562Sgshapiro } 260764562Sgshapiro# endif /* SASL */ 260838032Speter else 260938032Speter { 261066494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 261138032Speter /* NOTREACHED */ 261238032Speter } 261338032Speter} 261438032Speter/* 261538032Speter** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line 261638032Speter** 261738032Speter** Parameters: 261838032Speter** a -- the address corresponding to the To: parameter. 261938032Speter** kp -- the parameter key. 262038032Speter** vp -- the value of that parameter. 262138032Speter** e -- the envelope. 262238032Speter** 262338032Speter** Returns: 262438032Speter** none. 262538032Speter*/ 262638032Speter 262764562Sgshapirostatic void 262838032Speterrcpt_esmtp_args(a, kp, vp, e) 262938032Speter ADDRESS *a; 263038032Speter char *kp; 263138032Speter char *vp; 263238032Speter ENVELOPE *e; 263338032Speter{ 263438032Speter if (strcasecmp(kp, "notify") == 0) 263538032Speter { 263638032Speter char *p; 263738032Speter 263864562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 263964562Sgshapiro { 264064562Sgshapiro usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN"); 264164562Sgshapiro /* NOTREACHED */ 264264562Sgshapiro } 264338032Speter if (vp == NULL) 264438032Speter { 264564562Sgshapiro usrerr("501 5.5.2 NOTIFY requires a value"); 264638032Speter /* NOTREACHED */ 264738032Speter } 264838032Speter a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); 264938032Speter a->q_flags |= QHASNOTIFY; 265064562Sgshapiro define(macid("{dsn_notify}", NULL), newstr(vp), e); 265164562Sgshapiro 265238032Speter if (strcasecmp(vp, "never") == 0) 265338032Speter return; 265438032Speter for (p = vp; p != NULL; vp = p) 265538032Speter { 265638032Speter p = strchr(p, ','); 265738032Speter if (p != NULL) 265838032Speter *p++ = '\0'; 265938032Speter if (strcasecmp(vp, "success") == 0) 266038032Speter a->q_flags |= QPINGONSUCCESS; 266138032Speter else if (strcasecmp(vp, "failure") == 0) 266238032Speter a->q_flags |= QPINGONFAILURE; 266338032Speter else if (strcasecmp(vp, "delay") == 0) 266438032Speter a->q_flags |= QPINGONDELAY; 266538032Speter else 266638032Speter { 266764562Sgshapiro usrerr("501 5.5.4 Bad argument \"%s\" to NOTIFY", 266838032Speter vp); 266938032Speter /* NOTREACHED */ 267038032Speter } 267138032Speter } 267238032Speter } 267338032Speter else if (strcasecmp(kp, "orcpt") == 0) 267438032Speter { 267564562Sgshapiro if (bitset(PRIV_NORECEIPTS, PrivacyFlags)) 267664562Sgshapiro { 267764562Sgshapiro usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN"); 267864562Sgshapiro /* NOTREACHED */ 267964562Sgshapiro } 268038032Speter if (vp == NULL) 268138032Speter { 268264562Sgshapiro usrerr("501 5.5.2 ORCPT requires a value"); 268338032Speter /* NOTREACHED */ 268438032Speter } 268538032Speter if (strchr(vp, ';') == NULL || !xtextok(vp)) 268638032Speter { 268764562Sgshapiro usrerr("501 5.5.4 Syntax error in ORCPT parameter value"); 268838032Speter /* NOTREACHED */ 268938032Speter } 269038032Speter if (a->q_orcpt != NULL) 269138032Speter { 269264562Sgshapiro usrerr("501 5.5.0 Duplicate ORCPT parameter"); 269338032Speter /* NOTREACHED */ 269438032Speter } 269538032Speter a->q_orcpt = newstr(vp); 269638032Speter } 269738032Speter else 269838032Speter { 269966494Sgshapiro usrerr("555 5.5.4 %s parameter unrecognized", kp); 270038032Speter /* NOTREACHED */ 270138032Speter } 270238032Speter} 270338032Speter/* 270438032Speter** PRINTVRFYADDR -- print an entry in the verify queue 270538032Speter** 270638032Speter** Parameters: 270738032Speter** a -- the address to print 270838032Speter** last -- set if this is the last one. 270938032Speter** vrfy -- set if this is a VRFY command. 271038032Speter** 271138032Speter** Returns: 271238032Speter** none. 271338032Speter** 271438032Speter** Side Effects: 271538032Speter** Prints the appropriate 250 codes. 271638032Speter*/ 271764562Sgshapiro#define OFFF (3 + 1 + 5 + 1) /* offset in fmt: SMTP reply + enh. code */ 271838032Speter 271964562Sgshapirostatic void 272038032Speterprintvrfyaddr(a, last, vrfy) 272138032Speter register ADDRESS *a; 272238032Speter bool last; 272338032Speter bool vrfy; 272438032Speter{ 272564562Sgshapiro char fmtbuf[30]; 272638032Speter 272738032Speter if (vrfy && a->q_mailer != NULL && 272838032Speter !bitnset(M_VRFY250, a->q_mailer->m_flags)) 272964562Sgshapiro (void) strlcpy(fmtbuf, "252", sizeof fmtbuf); 273038032Speter else 273164562Sgshapiro (void) strlcpy(fmtbuf, "250", sizeof fmtbuf); 273238032Speter fmtbuf[3] = last ? ' ' : '-'; 273364562Sgshapiro (void) strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4); 273438032Speter if (a->q_fullname == NULL) 273538032Speter { 273664562Sgshapiro if ((a->q_mailer == NULL || 273764562Sgshapiro a->q_mailer->m_addrtype == NULL || 273864562Sgshapiro strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 273964562Sgshapiro strchr(a->q_user, '@') == NULL) 274064562Sgshapiro (void) strlcpy(&fmtbuf[OFFF], "<%s@%s>", 274164562Sgshapiro sizeof fmtbuf - OFFF); 274238032Speter else 274364562Sgshapiro (void) strlcpy(&fmtbuf[OFFF], "<%s>", 274464562Sgshapiro sizeof fmtbuf - OFFF); 274538032Speter message(fmtbuf, a->q_user, MyHostName); 274638032Speter } 274738032Speter else 274838032Speter { 274964562Sgshapiro if ((a->q_mailer == NULL || 275064562Sgshapiro a->q_mailer->m_addrtype == NULL || 275164562Sgshapiro strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) && 275264562Sgshapiro strchr(a->q_user, '@') == NULL) 275364562Sgshapiro (void) strlcpy(&fmtbuf[OFFF], "%s <%s@%s>", 275464562Sgshapiro sizeof fmtbuf - OFFF); 275538032Speter else 275664562Sgshapiro (void) strlcpy(&fmtbuf[OFFF], "%s <%s>", 275764562Sgshapiro sizeof fmtbuf - OFFF); 275838032Speter message(fmtbuf, a->q_fullname, a->q_user, MyHostName); 275938032Speter } 276038032Speter} 276138032Speter/* 276238032Speter** RUNINCHILD -- return twice -- once in the child, then in the parent again 276338032Speter** 276438032Speter** Parameters: 276538032Speter** label -- a string used in error messages 276638032Speter** 276738032Speter** Returns: 276864562Sgshapiro** RIC_INCHILD in the child 276964562Sgshapiro** RIC_INPARENT in the parent 277064562Sgshapiro** RIC_TEMPFAIL tempfail condition 277138032Speter** 277238032Speter** Side Effects: 277338032Speter** none. 277438032Speter*/ 277538032Speter 277664562Sgshapirostatic int 277738032Speterruninchild(label, e) 277838032Speter char *label; 277938032Speter register ENVELOPE *e; 278038032Speter{ 278138032Speter pid_t childpid; 278238032Speter 278338032Speter if (!OneXact) 278438032Speter { 278564562Sgshapiro extern int NumQueues; 278664562Sgshapiro 278738032Speter /* 278864562Sgshapiro ** advance state of PRNG 278964562Sgshapiro ** this is necessary because otherwise all child processes 279064562Sgshapiro ** will produce the same PRN sequence and hence the selection 279164562Sgshapiro ** of a queue directory is not "really" random. 279264562Sgshapiro */ 279364562Sgshapiro if (NumQueues > 1) 279464562Sgshapiro (void) get_random(); 279564562Sgshapiro 279664562Sgshapiro /* 279764562Sgshapiro ** Disable child process reaping, in case ETRN has preceded 279838032Speter ** MAIL command, and then fork. 279938032Speter */ 280038032Speter 280138032Speter (void) blocksignal(SIGCHLD); 280238032Speter 280338032Speter childpid = dofork(); 280438032Speter if (childpid < 0) 280538032Speter { 280664562Sgshapiro syserr("451 4.3.0 %s: cannot fork", label); 280738032Speter (void) releasesignal(SIGCHLD); 280864562Sgshapiro return RIC_INPARENT; 280938032Speter } 281038032Speter if (childpid > 0) 281138032Speter { 281238032Speter auto int st; 281338032Speter 281438032Speter /* parent -- wait for child to complete */ 281564562Sgshapiro sm_setproctitle(TRUE, e, "server %s child wait", 281664562Sgshapiro CurSmtpClient); 281738032Speter st = waitfor(childpid); 281838032Speter if (st == -1) 281964562Sgshapiro syserr("451 4.3.0 %s: lost child", label); 282038032Speter else if (!WIFEXITED(st)) 282164562Sgshapiro { 282264562Sgshapiro syserr("451 4.3.0 %s: died on signal %d", 282364562Sgshapiro label, st & 0177); 282464562Sgshapiro return RIC_TEMPFAIL; 282564562Sgshapiro } 282638032Speter 282764562Sgshapiro /* if exited on a QUIT command, complete the process */ 282838032Speter if (WEXITSTATUS(st) == EX_QUIT) 282938032Speter { 283038032Speter disconnect(1, e); 283142575Speter finis(TRUE, ExitStat); 283238032Speter } 283338032Speter 283438032Speter /* restore the child signal */ 283538032Speter (void) releasesignal(SIGCHLD); 283638032Speter 283764562Sgshapiro return RIC_INPARENT; 283838032Speter } 283938032Speter else 284038032Speter { 284138032Speter /* child */ 284238032Speter InChild = TRUE; 284338032Speter QuickAbort = FALSE; 284464562Sgshapiro clearstats(); 284538032Speter clearenvelope(e, FALSE); 284664562Sgshapiro assign_queueid(e); 284738032Speter (void) setsignal(SIGCHLD, SIG_DFL); 284838032Speter (void) releasesignal(SIGCHLD); 284938032Speter } 285038032Speter } 285164562Sgshapiro return RIC_INCHILD; 285238032Speter} 285338032Speter 285464562Sgshapiro# if SASL 285564562Sgshapiro 285638032Speter/* 285764562Sgshapiro** SASLMECHS -- get list of possible AUTH mechanisms 285864562Sgshapiro** 285964562Sgshapiro** Parameters: 286064562Sgshapiro** conn -- SASL connection info 286164562Sgshapiro** mechlist -- output parameter for list of mechanisms 286264562Sgshapiro** 286364562Sgshapiro** Returns: 286464562Sgshapiro** number of mechs 286564562Sgshapiro*/ 286664562Sgshapiro 286764562Sgshapirostatic int 286864562Sgshapirosaslmechs(conn, mechlist) 286964562Sgshapiro sasl_conn_t *conn; 287064562Sgshapiro char **mechlist; 287164562Sgshapiro{ 287264562Sgshapiro int len, num, result; 287364562Sgshapiro 287464562Sgshapiro /* "user" is currently unused */ 287564562Sgshapiro result = sasl_listmech(conn, "user", /* XXX */ 287664562Sgshapiro "", " ", "", mechlist, 287764562Sgshapiro (u_int *)&len, (u_int *)&num); 287864562Sgshapiro if (result == SASL_OK && num > 0) 287964562Sgshapiro { 288064562Sgshapiro if (LogLevel > 11) 288164562Sgshapiro sm_syslog(LOG_INFO, NOQID, 288264562Sgshapiro "SASL: available mech=%s, allowed mech=%s", 288364562Sgshapiro *mechlist, AuthMechanisms); 288464562Sgshapiro *mechlist = intersect(AuthMechanisms, *mechlist); 288564562Sgshapiro } 288664562Sgshapiro else 288764562Sgshapiro { 288864562Sgshapiro if (LogLevel > 9) 288964562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 289064562Sgshapiro "SASL error: listmech=%d, num=%d", 289164562Sgshapiro result, num); 289264562Sgshapiro } 289364562Sgshapiro return num; 289464562Sgshapiro} 289564562Sgshapiro 289664562Sgshapiro/* 289764562Sgshapiro** PROXY_POLICY -- define proxy policy for AUTH 289864562Sgshapiro** 289964562Sgshapiro** Parameters: 290064562Sgshapiro** conntext -- unused 290164562Sgshapiro** auth_identity -- authentication identity 290264562Sgshapiro** requested_user -- authorization identity 290364562Sgshapiro** user -- allowed user (output) 290464562Sgshapiro** errstr -- possible error string (output) 290564562Sgshapiro** 290664562Sgshapiro** Returns: 290764562Sgshapiro** ok? 290864562Sgshapiro*/ 290964562Sgshapiro 291064562Sgshapiroint 291164562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr) 291264562Sgshapiro void *context; 291364562Sgshapiro const char *auth_identity; 291464562Sgshapiro const char *requested_user; 291564562Sgshapiro const char **user; 291664562Sgshapiro const char **errstr; 291764562Sgshapiro{ 291864562Sgshapiro if (user == NULL || auth_identity == NULL) 291964562Sgshapiro return SASL_FAIL; 292064562Sgshapiro *user = newstr(auth_identity); 292164562Sgshapiro return SASL_OK; 292264562Sgshapiro} 292364562Sgshapiro 292464562Sgshapiro# endif /* SASL */ 292564562Sgshapiro 292664562Sgshapiro# if STARTTLS 292764562Sgshapiro# if !TLS_NO_RSA 292864562SgshapiroRSA *rsa_tmp; /* temporary RSA key */ 292964562Sgshapirostatic RSA * tmp_rsa_key __P((SSL *, int, int)); 293064562Sgshapiro# endif /* !TLS_NO_RSA */ 293164562Sgshapiro 293264562Sgshapiro# if !NO_DH 293364562Sgshapirostatic DH *get_dh512 __P((void)); 293464562Sgshapiro 293564562Sgshapirostatic unsigned char dh512_p[] = 293664562Sgshapiro{ 293764562Sgshapiro 0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75, 293864562Sgshapiro 0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F, 293964562Sgshapiro 0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3, 294064562Sgshapiro 0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12, 294164562Sgshapiro 0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C, 294264562Sgshapiro 0x47,0x74,0xE8,0x33 294364562Sgshapiro}; 294464562Sgshapirostatic unsigned char dh512_g[] = 294564562Sgshapiro{ 294664562Sgshapiro 0x02 294764562Sgshapiro}; 294864562Sgshapiro 294964562Sgshapirostatic DH * 295064562Sgshapiroget_dh512() 295164562Sgshapiro{ 295264562Sgshapiro DH *dh = NULL; 295364562Sgshapiro 295464562Sgshapiro if ((dh = DH_new()) == NULL) 295564562Sgshapiro return(NULL); 295664562Sgshapiro dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL); 295764562Sgshapiro dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL); 295864562Sgshapiro if ((dh->p == NULL) || (dh->g == NULL)) 295964562Sgshapiro return(NULL); 296064562Sgshapiro return(dh); 296164562Sgshapiro} 296264562Sgshapiro# endif /* !NO_DH */ 296364562Sgshapiro 296464562Sgshapiro/* 296564562Sgshapiro** TLS_RAND_INIT -- initialize STARTTLS random generator 296664562Sgshapiro** 296764562Sgshapiro** Parameters: 296864562Sgshapiro** randfile -- name of file with random data 296964562Sgshapiro** logl -- loglevel 297064562Sgshapiro** 297164562Sgshapiro** Returns: 297266494Sgshapiro** success/failure 297364562Sgshapiro** 297464562Sgshapiro** Side Effects: 297564562Sgshapiro** initializes PRNG for tls library. 297664562Sgshapiro*/ 297764562Sgshapiro 297864562Sgshapiro#define MIN_RAND_BYTES 16 /* 128 bits */ 297964562Sgshapiro 298066494Sgshapirobool 298164562Sgshapirotls_rand_init(randfile, logl) 298264562Sgshapiro char *randfile; 298364562Sgshapiro int logl; 298464562Sgshapiro{ 298566494Sgshapiro# ifndef HASURANDOMDEV 298664562Sgshapiro /* not required if /dev/urandom exists, OpenSSL does it internally */ 298764562Sgshapiro 298864562Sgshapiro#define RF_OK 0 /* randfile OK */ 298964562Sgshapiro#define RF_MISS 1 /* randfile == NULL || *randfile == '\0' */ 299064562Sgshapiro#define RF_UNKNOWN 2 /* unknown prefix for randfile */ 299164562Sgshapiro 299266494Sgshapiro#define RI_NONE 0 /* no init yet */ 299366494Sgshapiro#define RI_SUCCESS 1 /* init was successful */ 299466494Sgshapiro#define RI_FAIL 2 /* init failed */ 299566494Sgshapiro 299664562Sgshapiro bool ok; 299764562Sgshapiro int randdef; 299866494Sgshapiro static int done = RI_NONE; 299964562Sgshapiro 300064562Sgshapiro /* 300164562Sgshapiro ** initialize PRNG 300264562Sgshapiro */ 300364562Sgshapiro 300466494Sgshapiro /* did we try this before? if yes: return old value */ 300566494Sgshapiro if (done != RI_NONE) 300666494Sgshapiro return done == RI_SUCCESS; 300766494Sgshapiro 300866494Sgshapiro /* set default values */ 300964562Sgshapiro ok = FALSE; 301066494Sgshapiro done = RI_FAIL; 301164562Sgshapiro randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK; 301264562Sgshapiro# if EGD 301364562Sgshapiro if (randdef == RF_OK && strncasecmp(randfile, "egd:", 4) == 0) 301464562Sgshapiro { 301564562Sgshapiro randfile += 4; 301664562Sgshapiro if (RAND_egd(randfile) < 0) 301764562Sgshapiro { 301864562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 301964562Sgshapiro "TLS: RAND_egd(%s) failed: random number generator not seeded", 302064562Sgshapiro randfile); 302164562Sgshapiro } 302264562Sgshapiro else 302364562Sgshapiro ok = TRUE; 302464562Sgshapiro } 302564562Sgshapiro else 302664562Sgshapiro# endif /* EGD */ 302764562Sgshapiro if (randdef == RF_OK && strncasecmp(randfile, "file:", 5) == 0) 302864562Sgshapiro { 302964562Sgshapiro int fd; 303064562Sgshapiro long sff; 303164562Sgshapiro struct stat st; 303264562Sgshapiro 303364562Sgshapiro randfile += 5; 303464562Sgshapiro sff = SFF_SAFEDIRPATH | SFF_NOWLINK 303564562Sgshapiro | SFF_NOGWFILES | SFF_NOWWFILES 303664562Sgshapiro | SFF_NOGRFILES | SFF_NOWRFILES 303764562Sgshapiro | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT; 303864562Sgshapiro if ((fd = safeopen(randfile, O_RDONLY, 0, sff)) >= 0) 303964562Sgshapiro { 304064562Sgshapiro if (fstat(fd, &st) < 0) 304164562Sgshapiro { 304264562Sgshapiro if (LogLevel > logl) 304364562Sgshapiro sm_syslog(LOG_ERR, NOQID, 304464562Sgshapiro "TLS: can't fstat(%s)", 304564562Sgshapiro randfile); 304664562Sgshapiro } 304764562Sgshapiro else 304864562Sgshapiro { 304964562Sgshapiro bool use, problem; 305064562Sgshapiro 305164562Sgshapiro use = TRUE; 305264562Sgshapiro problem = FALSE; 305364562Sgshapiro if (st.st_mtime + 600 < curtime()) 305464562Sgshapiro { 305564562Sgshapiro use = bitnset(DBS_INSUFFICIENTENTROPY, 305664562Sgshapiro DontBlameSendmail); 305764562Sgshapiro problem = TRUE; 305864562Sgshapiro if (LogLevel > logl) 305964562Sgshapiro sm_syslog(LOG_ERR, NOQID, 306064562Sgshapiro "TLS: RandFile %s too old: %s", 306164562Sgshapiro randfile, 306264562Sgshapiro use ? "unsafe" : 306364562Sgshapiro "unusable"); 306464562Sgshapiro } 306564562Sgshapiro if (use && st.st_size < MIN_RAND_BYTES) 306664562Sgshapiro { 306764562Sgshapiro use = bitnset(DBS_INSUFFICIENTENTROPY, 306864562Sgshapiro DontBlameSendmail); 306964562Sgshapiro problem = TRUE; 307064562Sgshapiro if (LogLevel > logl) 307164562Sgshapiro sm_syslog(LOG_ERR, NOQID, 307264562Sgshapiro "TLS: size(%s) < %d: %s", 307364562Sgshapiro randfile, 307464562Sgshapiro MIN_RAND_BYTES, 307564562Sgshapiro use ? "unsafe" : 307664562Sgshapiro "unusable"); 307764562Sgshapiro } 307864562Sgshapiro if (use) 307964562Sgshapiro ok = RAND_load_file(randfile, -1) >= 308064562Sgshapiro MIN_RAND_BYTES; 308164562Sgshapiro if (use && !ok) 308264562Sgshapiro { 308364562Sgshapiro if (LogLevel > logl) 308464562Sgshapiro sm_syslog(LOG_WARNING, 308564562Sgshapiro NOQID, 308664562Sgshapiro "TLS: RAND_load_file(%s) failed: random number generator not seeded", 308764562Sgshapiro randfile); 308864562Sgshapiro } 308964562Sgshapiro if (problem) 309064562Sgshapiro ok = FALSE; 309164562Sgshapiro } 309264562Sgshapiro if (ok || bitnset(DBS_INSUFFICIENTENTROPY, 309364562Sgshapiro DontBlameSendmail)) 309464562Sgshapiro { 309564562Sgshapiro /* add this even if fstat() failed */ 309664562Sgshapiro RAND_seed((void *) &st, sizeof st); 309764562Sgshapiro } 309864562Sgshapiro (void) close(fd); 309964562Sgshapiro } 310064562Sgshapiro else 310164562Sgshapiro { 310264562Sgshapiro if (LogLevel > logl) 310364562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 310464562Sgshapiro "TLS: Warning: safeopen(%s) failed", 310564562Sgshapiro randfile); 310664562Sgshapiro } 310764562Sgshapiro } 310864562Sgshapiro else if (randdef == RF_OK) 310964562Sgshapiro { 311064562Sgshapiro if (LogLevel > logl) 311164562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 311264562Sgshapiro "TLS: Error: no proper random file definition %s", 311364562Sgshapiro randfile); 311464562Sgshapiro randdef = RF_UNKNOWN; 311564562Sgshapiro } 311664562Sgshapiro if (randdef == RF_MISS) 311764562Sgshapiro { 311864562Sgshapiro if (LogLevel > logl) 311964562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 312064562Sgshapiro "TLS: Error: missing random file definition"); 312164562Sgshapiro } 312264562Sgshapiro if (!ok && bitnset(DBS_INSUFFICIENTENTROPY, DontBlameSendmail)) 312364562Sgshapiro { 312464562Sgshapiro int i; 312564562Sgshapiro long r; 312664562Sgshapiro unsigned char buf[MIN_RAND_BYTES]; 312764562Sgshapiro 312864562Sgshapiro /* assert((MIN_RAND_BYTES % sizeof(long)) == 0); */ 312964562Sgshapiro for (i = 0; i <= sizeof(buf) - sizeof(long); i += sizeof(long)) 313064562Sgshapiro { 313164562Sgshapiro r = get_random(); 313264562Sgshapiro (void) memcpy(buf + i, (void *) &r, sizeof(long)); 313364562Sgshapiro } 313464562Sgshapiro RAND_seed(buf, sizeof buf); 313564562Sgshapiro if (LogLevel > logl) 313664562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 313764562Sgshapiro "TLS: Warning: random number generator not properly seeded"); 313866494Sgshapiro ok = TRUE; 313964562Sgshapiro } 314066494Sgshapiro done = ok ? RI_SUCCESS : RI_FAIL; 314166494Sgshapiro return ok; 314266494Sgshapiro# else /* !HASURANDOMDEV */ 314366494Sgshapiro return TRUE; 314466494Sgshapiro# endif /* !HASURANDOMDEV */ 314564562Sgshapiro} 314664562Sgshapiro 314764562Sgshapiro/* 314864562Sgshapiro** status in initialization 314964562Sgshapiro** these flags keep track of the status of the initialization 315064562Sgshapiro** i.e., whether a file exists (_EX) and whether it can be used (_OK) 315164562Sgshapiro** [due to permissions] 315264562Sgshapiro*/ 315364562Sgshapiro#define TLS_S_NONE 0x00000000 /* none yet */ 315464562Sgshapiro#define TLS_S_CERT_EX 0x00000001 /* CERT file exists */ 315564562Sgshapiro#define TLS_S_CERT_OK 0x00000002 /* CERT file is ok */ 315664562Sgshapiro#define TLS_S_KEY_EX 0x00000004 /* KEY file exists */ 315764562Sgshapiro#define TLS_S_KEY_OK 0x00000008 /* KEY file is ok */ 315864562Sgshapiro#define TLS_S_CERTP_EX 0x00000010 /* CA CERT PATH exists */ 315964562Sgshapiro#define TLS_S_CERTP_OK 0x00000020 /* CA CERT PATH is ok */ 316064562Sgshapiro#define TLS_S_CERTF_EX 0x00000040 /* CA CERT FILE exists */ 316164562Sgshapiro#define TLS_S_CERTF_OK 0x00000080 /* CA CERT FILE is ok */ 316264562Sgshapiro 316364562Sgshapiro# if _FFR_TLS_1 316464562Sgshapiro#define TLS_S_CERT2_EX 0x00001000 /* 2nd CERT file exists */ 316564562Sgshapiro#define TLS_S_CERT2_OK 0x00002000 /* 2nd CERT file is ok */ 316664562Sgshapiro#define TLS_S_KEY2_EX 0x00004000 /* 2nd KEY file exists */ 316764562Sgshapiro#define TLS_S_KEY2_OK 0x00008000 /* 2nd KEY file is ok */ 316864562Sgshapiro# endif /* _FFR_TLS_1 */ 316964562Sgshapiro 317064562Sgshapiro#define TLS_S_DH_OK 0x00200000 /* DH cert is ok */ 317164562Sgshapiro#define TLS_S_DHPAR_EX 0x00400000 /* DH param file exists */ 317264562Sgshapiro#define TLS_S_DHPAR_OK 0x00800000 /* DH param file is ok to use */ 317364562Sgshapiro 317464562Sgshapiro/* 317564562Sgshapiro** TLS_OK_F -- can var be an absolute filename? 317664562Sgshapiro** 317764562Sgshapiro** Parameters: 317864562Sgshapiro** var -- filename 317964562Sgshapiro** fn -- what is the filename used for? 318064562Sgshapiro** 318164562Sgshapiro** Returns: 318264562Sgshapiro** ok? 318364562Sgshapiro*/ 318464562Sgshapiro 318564562Sgshapirostatic bool 318664562Sgshapirotls_ok_f(var, fn) 318764562Sgshapiro char *var; 318864562Sgshapiro char *fn; 318964562Sgshapiro{ 319064562Sgshapiro /* must be absolute pathname */ 319164562Sgshapiro if (var != NULL && *var == '/') 319264562Sgshapiro return TRUE; 319364562Sgshapiro if (LogLevel > 12) 319464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "TLS: file %s missing", fn); 319564562Sgshapiro return FALSE; 319664562Sgshapiro} 319764562Sgshapiro 319864562Sgshapiro/* 319964562Sgshapiro** TLS_SAFE_F -- is a file safe to use? 320064562Sgshapiro** 320164562Sgshapiro** Parameters: 320264562Sgshapiro** var -- filename 320364562Sgshapiro** sff -- flags for safefile() 320464562Sgshapiro** 320564562Sgshapiro** Returns: 320664562Sgshapiro** ok? 320764562Sgshapiro*/ 320864562Sgshapiro 320964562Sgshapirostatic bool 321064562Sgshapirotls_safe_f(var, sff) 321164562Sgshapiro char *var; 321264562Sgshapiro long sff; 321364562Sgshapiro{ 321464562Sgshapiro int ret; 321564562Sgshapiro 321664562Sgshapiro if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff, 321764562Sgshapiro S_IRUSR, NULL)) == 0) 321864562Sgshapiro return TRUE; 321964562Sgshapiro if (LogLevel > 7) 322064562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "TLS: file %s unsafe: %s", 322164562Sgshapiro var, errstring(ret)); 322264562Sgshapiro return FALSE; 322364562Sgshapiro} 322464562Sgshapiro 322564562Sgshapiro/* 322664562Sgshapiro** TLS_OK_F -- macro to simplify calls to tls_ok_f 322764562Sgshapiro** 322864562Sgshapiro** Parameters: 322964562Sgshapiro** var -- filename 323064562Sgshapiro** fn -- what is the filename used for? 323164562Sgshapiro** req -- is the file required? 323264562Sgshapiro** st -- status bit to set if ok 323364562Sgshapiro** 323464562Sgshapiro** Side Effects: 323564562Sgshapiro** uses r, ok; may change ok and status. 323664562Sgshapiro** 323764562Sgshapiro*/ 323864562Sgshapiro 323964562Sgshapiro#define TLS_OK_F(var, fn, req, st) if (ok) \ 324064562Sgshapiro { \ 324164562Sgshapiro r = tls_ok_f(var, fn); \ 324264562Sgshapiro if (r) \ 324364562Sgshapiro status |= st; \ 324464562Sgshapiro else if (req) \ 324564562Sgshapiro ok = FALSE; \ 324664562Sgshapiro } 324764562Sgshapiro 324864562Sgshapiro/* 324964562Sgshapiro** TLS_UNR -- macro to return whether a file should be unreadable 325064562Sgshapiro** 325164562Sgshapiro** Parameters: 325264562Sgshapiro** bit -- flag to test 325364562Sgshapiro** req -- flags 325464562Sgshapiro** 325564562Sgshapiro** Returns: 325664562Sgshapiro** 0/SFF_NORFILES 325764562Sgshapiro*/ 325864562Sgshapiro#define TLS_UNR(bit, req) (bitset(bit, req) ? SFF_NORFILES : 0) 325964562Sgshapiro 326064562Sgshapiro/* 326164562Sgshapiro** TLS_SAFE_F -- macro to simplify calls to tls_safe_f 326264562Sgshapiro** 326364562Sgshapiro** Parameters: 326464562Sgshapiro** var -- filename 326564562Sgshapiro** sff -- flags for safefile() 326664562Sgshapiro** req -- is the file required? 326764562Sgshapiro** ex -- does the file exist? 326864562Sgshapiro** st -- status bit to set if ok 326964562Sgshapiro** 327064562Sgshapiro** Side Effects: 327164562Sgshapiro** uses r, ok, ex; may change ok and status. 327264562Sgshapiro** 327364562Sgshapiro*/ 327464562Sgshapiro 327564562Sgshapiro#define TLS_SAFE_F(var, sff, req, ex, st) if (ex && ok) \ 327664562Sgshapiro { \ 327764562Sgshapiro r = tls_safe_f(var, sff); \ 327864562Sgshapiro if (r) \ 327964562Sgshapiro status |= st; \ 328064562Sgshapiro else if (req) \ 328164562Sgshapiro ok = FALSE; \ 328264562Sgshapiro } 328364562Sgshapiro 328464562Sgshapiro/* 328564562Sgshapiro** INITTLS -- initialize TLS 328664562Sgshapiro** 328764562Sgshapiro** Parameters: 328864562Sgshapiro** ctx -- pointer to context 328964562Sgshapiro** req -- requirements for initialization (see sendmail.h) 329064562Sgshapiro** srv -- server side? 329164562Sgshapiro** certfile -- filename of certificate 329264562Sgshapiro** keyfile -- filename of private key 329364562Sgshapiro** cacertpath -- path to CAs 329464562Sgshapiro** cacertfile -- file with CA 329564562Sgshapiro** dhparam -- parameters for DH 329664562Sgshapiro** 329764562Sgshapiro** Returns: 329864562Sgshapiro** succeeded? 329964562Sgshapiro*/ 330064562Sgshapiro 330164562Sgshapirobool 330264562Sgshapiroinittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam) 330364562Sgshapiro SSL_CTX **ctx; 330464562Sgshapiro u_long req; 330564562Sgshapiro bool srv; 330664562Sgshapiro char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam; 330764562Sgshapiro{ 330864562Sgshapiro# if !NO_DH 330964562Sgshapiro static DH *dh = NULL; 331064562Sgshapiro# endif /* !NO_DH */ 331164562Sgshapiro int r; 331264562Sgshapiro bool ok; 331364562Sgshapiro long sff, status; 331464562Sgshapiro char *who; 331564562Sgshapiro# if _FFR_TLS_1 331664562Sgshapiro char *cf2, *kf2; 331764562Sgshapiro# endif /* _FFR_TLS_1 */ 331864562Sgshapiro 331964562Sgshapiro status = TLS_S_NONE; 332064562Sgshapiro who = srv ? "srv" : "clt"; 332164562Sgshapiro if (ctx == NULL) 332264562Sgshapiro syserr("TLS: %s:inittls: ctx == NULL", who); 332364562Sgshapiro 332464562Sgshapiro /* already initialized? (we could re-init...) */ 332564562Sgshapiro if (*ctx != NULL) 332664562Sgshapiro return TRUE; 332766494Sgshapiro 332866494Sgshapiro /* PRNG seeded? */ 332966494Sgshapiro if (!tls_rand_init(RandFile, 10)) 333066494Sgshapiro return FALSE; 333166494Sgshapiro 333266494Sgshapiro /* let's start with the assumption it will work */ 333364562Sgshapiro ok = TRUE; 333464562Sgshapiro 333564562Sgshapiro# if _FFR_TLS_1 333664562Sgshapiro /* 333764562Sgshapiro ** look for a second filename: it must be separated by a ',' 333864562Sgshapiro ** no blanks allowed (they won't be skipped). 333964562Sgshapiro ** we change a global variable here! this change will be undone 334064562Sgshapiro ** before return from the function but only if it returns TRUE. 334164562Sgshapiro ** this isn't a problem since in a failure case this function 334264562Sgshapiro ** won't be called again with the same (overwritten) values. 334364562Sgshapiro ** otherwise each return must be replaced with a goto endinittls. 334464562Sgshapiro */ 334564562Sgshapiro cf2 = NULL; 334664562Sgshapiro kf2 = NULL; 334764562Sgshapiro if (certfile != NULL && (cf2 = strchr(certfile, ',')) != NULL) 334864562Sgshapiro { 334964562Sgshapiro *cf2++ = '\0'; 335064562Sgshapiro if (keyfile != NULL && (kf2 = strchr(keyfile, ',')) != NULL) 335164562Sgshapiro *kf2++ = '\0'; 335264562Sgshapiro } 335364562Sgshapiro# endif /* _FFR_TLS_1 */ 335464562Sgshapiro 335564562Sgshapiro /* 335664562Sgshapiro ** what do we require from the client? 335764562Sgshapiro ** must it have CERTs? 335864562Sgshapiro ** introduce an option and decide based on that 335964562Sgshapiro */ 336064562Sgshapiro 336164562Sgshapiro TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req), 336264562Sgshapiro TLS_S_CERT_EX); 336364562Sgshapiro TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req), 336464562Sgshapiro TLS_S_KEY_EX); 336564562Sgshapiro TLS_OK_F(cacertpath, "CACERTPath", bitset(TLS_I_CERTP_EX, req), 336664562Sgshapiro TLS_S_CERTP_EX); 336764562Sgshapiro TLS_OK_F(cacertfile, "CACERTFile", bitset(TLS_I_CERTF_EX, req), 336864562Sgshapiro TLS_S_CERTF_EX); 336964562Sgshapiro 337064562Sgshapiro# if _FFR_TLS_1 337164562Sgshapiro if (cf2 != NULL) 337264562Sgshapiro { 337364562Sgshapiro TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req), 337464562Sgshapiro TLS_S_CERT2_EX); 337564562Sgshapiro } 337664562Sgshapiro if (kf2 != NULL) 337764562Sgshapiro { 337864562Sgshapiro TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req), 337964562Sgshapiro TLS_S_KEY2_EX); 338064562Sgshapiro } 338164562Sgshapiro# endif /* _FFR_TLS_1 */ 338264562Sgshapiro 338364562Sgshapiro /* 338464562Sgshapiro ** valid values for dhparam are (only the first char is checked) 338564562Sgshapiro ** none no parameters: don't use DH 338664562Sgshapiro ** 512 generate 512 bit parameters (fixed) 338764562Sgshapiro ** 1024 generate 1024 bit parameters 338864562Sgshapiro ** /file/name read parameters from /file/name 338964562Sgshapiro ** default is: 1024 for server, 512 for client (OK? XXX) 339064562Sgshapiro */ 339164562Sgshapiro if (bitset(TLS_I_TRY_DH, req)) 339264562Sgshapiro { 339364562Sgshapiro if (dhparam != NULL) 339464562Sgshapiro { 339564562Sgshapiro char c = *dhparam; 339664562Sgshapiro 339764562Sgshapiro if (c == '1') 339864562Sgshapiro req |= TLS_I_DH1024; 339964562Sgshapiro else if (c == '5') 340064562Sgshapiro req |= TLS_I_DH512; 340164562Sgshapiro else if (c != 'n' && c != 'N' && c != '/') 340264562Sgshapiro { 340364562Sgshapiro if (LogLevel > 12) 340464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 340564562Sgshapiro "TLS: error: illegal value '%s' for DHParam", 340664562Sgshapiro dhparam); 340764562Sgshapiro dhparam = NULL; 340864562Sgshapiro } 340964562Sgshapiro } 341064562Sgshapiro if (dhparam == NULL) 341166494Sgshapiro dhparam = srv ? "1" : "5"; 341264562Sgshapiro else if (*dhparam == '/') 341364562Sgshapiro { 341464562Sgshapiro TLS_OK_F(dhparam, "DHParameters", 341564562Sgshapiro bitset(TLS_I_DHPAR_EX, req), 341664562Sgshapiro TLS_S_DHPAR_EX); 341764562Sgshapiro } 341864562Sgshapiro } 341964562Sgshapiro if (!ok) 342064562Sgshapiro return ok; 342164562Sgshapiro 342264562Sgshapiro /* certfile etc. must be "safe". */ 342364562Sgshapiro sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK 342464562Sgshapiro | SFF_NOGWFILES | SFF_NOWWFILES 342564562Sgshapiro | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT; 342664562Sgshapiro if (DontLockReadFiles) 342764562Sgshapiro sff |= SFF_NOLOCK; 342864562Sgshapiro 342964562Sgshapiro TLS_SAFE_F(certfile, sff | TLS_UNR(TLS_I_CERT_UNR, req), 343064562Sgshapiro bitset(TLS_I_CERT_EX, req), 343164562Sgshapiro bitset(TLS_S_CERT_EX, status), TLS_S_CERT_OK); 343264562Sgshapiro TLS_SAFE_F(keyfile, sff | TLS_UNR(TLS_I_KEY_UNR, req), 343364562Sgshapiro bitset(TLS_I_KEY_EX, req), 343464562Sgshapiro bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK); 343564562Sgshapiro TLS_SAFE_F(cacertfile, sff | TLS_UNR(TLS_I_CERTF_UNR, req), 343664562Sgshapiro bitset(TLS_I_CERTF_EX, req), 343764562Sgshapiro bitset(TLS_S_CERTF_EX, status), TLS_S_CERTF_OK); 343864562Sgshapiro TLS_SAFE_F(dhparam, sff | TLS_UNR(TLS_I_DHPAR_UNR, req), 343964562Sgshapiro bitset(TLS_I_DHPAR_EX, req), 344064562Sgshapiro bitset(TLS_S_DHPAR_EX, status), TLS_S_DHPAR_OK); 344164562Sgshapiro if (!ok) 344264562Sgshapiro return ok; 344364562Sgshapiro# if _FFR_TLS_1 344464562Sgshapiro if (cf2 != NULL) 344564562Sgshapiro { 344664562Sgshapiro TLS_SAFE_F(cf2, sff | TLS_UNR(TLS_I_CERT_UNR, req), 344764562Sgshapiro bitset(TLS_I_CERT_EX, req), 344864562Sgshapiro bitset(TLS_S_CERT2_EX, status), TLS_S_CERT2_OK); 344964562Sgshapiro } 345064562Sgshapiro if (kf2 != NULL) 345164562Sgshapiro { 345264562Sgshapiro TLS_SAFE_F(kf2, sff | TLS_UNR(TLS_I_KEY_UNR, req), 345364562Sgshapiro bitset(TLS_I_KEY_EX, req), 345464562Sgshapiro bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK); 345564562Sgshapiro } 345664562Sgshapiro# endif /* _FFR_TLS_1 */ 345764562Sgshapiro 345864562Sgshapiro /* create a method and a new context */ 345964562Sgshapiro if (srv) 346064562Sgshapiro { 346164562Sgshapiro if ((*ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) 346264562Sgshapiro { 346364562Sgshapiro if (LogLevel > 7) 346464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 346564562Sgshapiro "TLS: error: SSL_CTX_new(SSLv23_server_method()) failed"); 346664562Sgshapiro return FALSE; 346764562Sgshapiro } 346864562Sgshapiro } 346964562Sgshapiro else 347064562Sgshapiro { 347164562Sgshapiro if ((*ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) 347264562Sgshapiro { 347364562Sgshapiro if (LogLevel > 7) 347464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 347564562Sgshapiro "TLS: error: SSL_CTX_new(SSLv23_client_method()) failed"); 347664562Sgshapiro return FALSE; 347764562Sgshapiro } 347864562Sgshapiro } 347964562Sgshapiro 348064562Sgshapiro# if TLS_NO_RSA 348164562Sgshapiro /* turn off backward compatibility, required for no-rsa */ 348264562Sgshapiro SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2); 348364562Sgshapiro# endif /* TLS_NO_RSA */ 348464562Sgshapiro 348564562Sgshapiro 348664562Sgshapiro# if !TLS_NO_RSA 348764562Sgshapiro /* 348864562Sgshapiro ** Create a temporary RSA key 348964562Sgshapiro ** XXX Maybe we shouldn't create this always (even though it 349064562Sgshapiro ** is only at startup). 349164562Sgshapiro ** It is a time-consuming operation and it is not always necessary. 349264562Sgshapiro ** maybe we should do it only on demand... 349364562Sgshapiro */ 349464562Sgshapiro if (bitset(TLS_I_RSA_TMP, req) && 349564562Sgshapiro (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL, 349664562Sgshapiro NULL)) == NULL 349764562Sgshapiro ) 349864562Sgshapiro { 349964562Sgshapiro if (LogLevel > 7) 350064562Sgshapiro { 350164562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 350264562Sgshapiro "TLS: error: %s: RSA_generate_key failed", 350364562Sgshapiro who); 350464562Sgshapiro if (LogLevel > 9) 350564562Sgshapiro tlslogerr(); 350664562Sgshapiro } 350764562Sgshapiro return FALSE; 350864562Sgshapiro } 350964562Sgshapiro# endif /* !TLS_NO_RSA */ 351064562Sgshapiro 351164562Sgshapiro /* 351264562Sgshapiro ** load private key 351364562Sgshapiro ** XXX change this for DSA-only version 351464562Sgshapiro */ 351564562Sgshapiro if (bitset(TLS_S_KEY_OK, status) && 351664562Sgshapiro SSL_CTX_use_PrivateKey_file(*ctx, keyfile, 351764562Sgshapiro SSL_FILETYPE_PEM) <= 0) 351864562Sgshapiro { 351964562Sgshapiro if (LogLevel > 7) 352064562Sgshapiro { 352164562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 352264562Sgshapiro "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed", 352364562Sgshapiro who, keyfile); 352464562Sgshapiro if (LogLevel > 9) 352564562Sgshapiro tlslogerr(); 352664562Sgshapiro } 352764562Sgshapiro if (bitset(TLS_I_USE_KEY, req)) 352864562Sgshapiro return FALSE; 352964562Sgshapiro } 353064562Sgshapiro 353164562Sgshapiro /* get the certificate file */ 353264562Sgshapiro if (bitset(TLS_S_CERT_OK, status) && 353364562Sgshapiro SSL_CTX_use_certificate_file(*ctx, certfile, 353464562Sgshapiro SSL_FILETYPE_PEM) <= 0) 353564562Sgshapiro { 353664562Sgshapiro if (LogLevel > 7) 353764562Sgshapiro { 353864562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 353964562Sgshapiro "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed", 354064562Sgshapiro who, certfile); 354164562Sgshapiro if (LogLevel > 9) 354264562Sgshapiro tlslogerr(); 354364562Sgshapiro } 354464562Sgshapiro if (bitset(TLS_I_USE_CERT, req)) 354564562Sgshapiro return FALSE; 354664562Sgshapiro } 354764562Sgshapiro 354864562Sgshapiro /* check the private key */ 354964562Sgshapiro if (bitset(TLS_S_KEY_OK, status) && 355064562Sgshapiro (r = SSL_CTX_check_private_key(*ctx)) <= 0) 355164562Sgshapiro { 355264562Sgshapiro /* Private key does not match the certificate public key */ 355364562Sgshapiro if (LogLevel > 5) 355464562Sgshapiro { 355564562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 355664562Sgshapiro "TLS: error: %s: SSL_CTX_check_private_key failed(%s): %d", 355764562Sgshapiro who, keyfile, r); 355864562Sgshapiro if (LogLevel > 9) 355964562Sgshapiro tlslogerr(); 356064562Sgshapiro } 356164562Sgshapiro if (bitset(TLS_I_USE_KEY, req)) 356264562Sgshapiro return FALSE; 356364562Sgshapiro } 356464562Sgshapiro 356564562Sgshapiro# if _FFR_TLS_1 356664562Sgshapiro /* XXX this code is pretty much duplicated from above! */ 356764562Sgshapiro 356864562Sgshapiro /* load private key */ 356964562Sgshapiro if (bitset(TLS_S_KEY2_OK, status) && 357064562Sgshapiro SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0) 357164562Sgshapiro { 357264562Sgshapiro if (LogLevel > 7) 357364562Sgshapiro { 357464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 357564562Sgshapiro "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed", 357664562Sgshapiro who, kf2); 357764562Sgshapiro if (LogLevel > 9) 357864562Sgshapiro tlslogerr(); 357964562Sgshapiro } 358064562Sgshapiro } 358164562Sgshapiro 358264562Sgshapiro /* get the certificate file */ 358364562Sgshapiro if (bitset(TLS_S_CERT2_OK, status) && 358464562Sgshapiro SSL_CTX_use_certificate_file(*ctx, cf2, SSL_FILETYPE_PEM) <= 0) 358564562Sgshapiro { 358664562Sgshapiro if (LogLevel > 7) 358764562Sgshapiro { 358864562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 358964562Sgshapiro "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed", 359064562Sgshapiro who, cf2); 359164562Sgshapiro if (LogLevel > 9) 359264562Sgshapiro tlslogerr(); 359364562Sgshapiro } 359464562Sgshapiro } 359564562Sgshapiro 359664562Sgshapiro /* we should also check the private key: */ 359764562Sgshapiro if (bitset(TLS_S_KEY2_OK, status) && 359864562Sgshapiro (r = SSL_CTX_check_private_key(*ctx)) <= 0) 359964562Sgshapiro { 360064562Sgshapiro /* Private key does not match the certificate public key */ 360164562Sgshapiro if (LogLevel > 5) 360264562Sgshapiro { 360364562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 360464562Sgshapiro "TLS: error: %s: SSL_CTX_check_private_key 2 failed: %d", 360564562Sgshapiro who, r); 360664562Sgshapiro if (LogLevel > 9) 360764562Sgshapiro tlslogerr(); 360864562Sgshapiro } 360964562Sgshapiro } 361064562Sgshapiro# endif /* _FFR_TLS_1 */ 361164562Sgshapiro 361264562Sgshapiro /* SSL_CTX_set_quiet_shutdown(*ctx, 1); violation of standard? */ 361364562Sgshapiro SSL_CTX_set_options(*ctx, SSL_OP_ALL); /* XXX bug compatibility? */ 361464562Sgshapiro 361564562Sgshapiro# if !NO_DH 361664562Sgshapiro /* Diffie-Hellman initialization */ 361764562Sgshapiro if (bitset(TLS_I_TRY_DH, req)) 361864562Sgshapiro { 361964562Sgshapiro if (bitset(TLS_S_DHPAR_OK, status)) 362064562Sgshapiro { 362164562Sgshapiro BIO *bio; 362264562Sgshapiro 362364562Sgshapiro if ((bio = BIO_new_file(dhparam, "r")) != NULL) 362464562Sgshapiro { 362564562Sgshapiro dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); 362664562Sgshapiro BIO_free(bio); 362764562Sgshapiro if (dh == NULL && LogLevel > 7) 362864562Sgshapiro { 362964562Sgshapiro u_long err; 363064562Sgshapiro 363164562Sgshapiro err = ERR_get_error(); 363264562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 363364562Sgshapiro "TLS: error: %s: cannot read DH parameters(%s): %s", 363464562Sgshapiro who, dhparam, 363564562Sgshapiro ERR_error_string(err, NULL)); 363664562Sgshapiro if (LogLevel > 9) 363764562Sgshapiro tlslogerr(); 363864562Sgshapiro } 363964562Sgshapiro } 364064562Sgshapiro else 364164562Sgshapiro { 364264562Sgshapiro if (LogLevel > 5) 364364562Sgshapiro { 364464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 364564562Sgshapiro "TLS: error: %s: BIO_new_file(%s) failed", 364664562Sgshapiro who, dhparam); 364764562Sgshapiro if (LogLevel > 9) 364864562Sgshapiro tlslogerr(); 364964562Sgshapiro } 365064562Sgshapiro } 365164562Sgshapiro } 365264562Sgshapiro if (dh == NULL && bitset(TLS_I_DH1024, req)) 365364562Sgshapiro { 365464562Sgshapiro DSA *dsa; 365564562Sgshapiro 365664562Sgshapiro /* this takes a while! (7-130s on a 450MHz AMD K6-2) */ 365764562Sgshapiro dsa = DSA_generate_parameters(1024, NULL, 0, NULL, 365864562Sgshapiro NULL, 0, NULL); 365964562Sgshapiro dh = DSA_dup_DH(dsa); 366064562Sgshapiro DSA_free(dsa); 366164562Sgshapiro } 366264562Sgshapiro else 366364562Sgshapiro if (dh == NULL && bitset(TLS_I_DH512, req)) 366464562Sgshapiro dh = get_dh512(); 366564562Sgshapiro 366664562Sgshapiro if (dh == NULL) 366764562Sgshapiro { 366864562Sgshapiro if (LogLevel > 9) 366964562Sgshapiro { 367064562Sgshapiro u_long err; 367164562Sgshapiro 367264562Sgshapiro err = ERR_get_error(); 367364562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 367464562Sgshapiro "TLS: error: %s: cannot read or set DH parameters(%s): %s", 367564562Sgshapiro who, dhparam, 367664562Sgshapiro ERR_error_string(err, NULL)); 367764562Sgshapiro } 367864562Sgshapiro if (bitset(TLS_I_REQ_DH, req)) 367964562Sgshapiro return FALSE; 368064562Sgshapiro } 368164562Sgshapiro else 368264562Sgshapiro { 368364562Sgshapiro SSL_CTX_set_tmp_dh(*ctx, dh); 368464562Sgshapiro 368564562Sgshapiro /* important to avoid small subgroup attacks */ 368664562Sgshapiro SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE); 368764562Sgshapiro if (LogLevel > 12) 368864562Sgshapiro sm_syslog(LOG_INFO, NOQID, 368964562Sgshapiro "TLS: %s: Diffie-Hellman init, key=%d bit (%c)", 369064562Sgshapiro who, 8 * DH_size(dh), *dhparam); 369164562Sgshapiro DH_free(dh); 369264562Sgshapiro } 369364562Sgshapiro } 369464562Sgshapiro# endif /* !NO_DH */ 369564562Sgshapiro 369664562Sgshapiro 369764562Sgshapiro /* XXX do we need this cache here? */ 369864562Sgshapiro if (bitset(TLS_I_CACHE, req)) 369964562Sgshapiro SSL_CTX_sess_set_cache_size(*ctx, 128); 370064562Sgshapiro /* timeout? SSL_CTX_set_timeout(*ctx, TimeOut...); */ 370164562Sgshapiro 370264562Sgshapiro /* load certificate locations and default CA paths */ 370364562Sgshapiro if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status)) 370464562Sgshapiro { 370564562Sgshapiro if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile, 370664562Sgshapiro cacertpath)) == 1) 370764562Sgshapiro { 370864562Sgshapiro# if !TLS_NO_RSA 370964562Sgshapiro if (bitset(TLS_I_RSA_TMP, req)) 371064562Sgshapiro SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key); 371164562Sgshapiro# endif /* !TLS_NO_RSA */ 371264562Sgshapiro 371364562Sgshapiro /* ask to verify the peer */ 371464562Sgshapiro SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL); 371564562Sgshapiro 371664562Sgshapiro /* install verify callback */ 371764562Sgshapiro SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb, 371864562Sgshapiro NULL); 371964562Sgshapiro SSL_CTX_set_client_CA_list(*ctx, 372064562Sgshapiro SSL_load_client_CA_file(cacertfile)); 372164562Sgshapiro } 372264562Sgshapiro else 372364562Sgshapiro { 372464562Sgshapiro /* 372564562Sgshapiro ** can't load CA data; do we care? 372664562Sgshapiro ** the data is necessary to authenticate the client, 372764562Sgshapiro ** which in turn would be necessary 372864562Sgshapiro ** if we want to allow relaying based on it. 372964562Sgshapiro */ 373064562Sgshapiro if (LogLevel > 5) 373164562Sgshapiro { 373264562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 373364562Sgshapiro "TLS: error: %s: %d load verify locs %s, %s", 373464562Sgshapiro who, r, cacertpath, cacertfile); 373564562Sgshapiro if (LogLevel > 9) 373664562Sgshapiro tlslogerr(); 373764562Sgshapiro } 373864562Sgshapiro if (bitset(TLS_I_VRFY_LOC, req)) 373964562Sgshapiro return FALSE; 374064562Sgshapiro } 374164562Sgshapiro } 374264562Sgshapiro 374364562Sgshapiro /* XXX: make this dependent on an option? */ 374464562Sgshapiro if (tTd(96, 9)) 374564562Sgshapiro SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb); 374664562Sgshapiro 374764562Sgshapiro# if _FFR_TLS_1 374864562Sgshapiro /* 374964562Sgshapiro ** XXX install our own cipher list: option? 375064562Sgshapiro */ 375164562Sgshapiro if (CipherList != NULL && *CipherList != '\0') 375264562Sgshapiro { 375364562Sgshapiro if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0) 375464562Sgshapiro { 375564562Sgshapiro if (LogLevel > 7) 375664562Sgshapiro { 375764562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 375864562Sgshapiro "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored", 375964562Sgshapiro who, CipherList); 376064562Sgshapiro 376164562Sgshapiro if (LogLevel > 9) 376264562Sgshapiro tlslogerr(); 376364562Sgshapiro } 376464562Sgshapiro /* failure if setting to this list is required? */ 376564562Sgshapiro } 376664562Sgshapiro } 376764562Sgshapiro# endif /* _FFR_TLS_1 */ 376864562Sgshapiro if (LogLevel > 12) 376964562Sgshapiro sm_syslog(LOG_INFO, NOQID, "TLS: init(%s)=%d", who, ok); 377064562Sgshapiro 377164562Sgshapiro# if _FFR_TLS_1 377264562Sgshapiro# if 0 377364562Sgshapiro /* 377464562Sgshapiro ** this label is required if we want to have a "clean" exit 377564562Sgshapiro ** see the comments above at the initialization of cf2 377664562Sgshapiro */ 377764562Sgshapiro endinittls: 377864562Sgshapiro# endif /* 0 */ 377964562Sgshapiro 378064562Sgshapiro /* undo damage to global variables */ 378164562Sgshapiro if (cf2 != NULL) 378264562Sgshapiro *--cf2 = ','; 378364562Sgshapiro if (kf2 != NULL) 378464562Sgshapiro *--kf2 = ','; 378564562Sgshapiro# endif /* _FFR_TLS_1 */ 378664562Sgshapiro 378764562Sgshapiro return ok; 378864562Sgshapiro} 378964562Sgshapiro/* 379064562Sgshapiro** INITSRVTLS -- initialize server side TLS 379164562Sgshapiro** 379264562Sgshapiro** Parameters: 379364562Sgshapiro** none. 379464562Sgshapiro** 379564562Sgshapiro** Returns: 379664562Sgshapiro** succeeded? 379764562Sgshapiro*/ 379864562Sgshapiro 379964562Sgshapirobool 380064562Sgshapiroinitsrvtls() 380164562Sgshapiro{ 380264562Sgshapiro 380364562Sgshapiro tls_ok = inittls(&srv_ctx, TLS_I_SRV, TRUE, SrvCERTfile, Srvkeyfile, 380464562Sgshapiro CACERTpath, CACERTfile, DHParams); 380564562Sgshapiro return tls_ok; 380664562Sgshapiro} 380764562Sgshapiro/* 380864562Sgshapiro** TLS_GET_INFO -- get information about TLS connection 380964562Sgshapiro** 381064562Sgshapiro** Parameters: 381164562Sgshapiro** ssl -- SSL connection structure 381264562Sgshapiro** e -- current envelope 381364562Sgshapiro** srv -- server or client 381464562Sgshapiro** host -- hostname of other side 381564562Sgshapiro** 381664562Sgshapiro** Returns: 381764562Sgshapiro** result of authentication. 381864562Sgshapiro** 381964562Sgshapiro** Side Effects: 382064562Sgshapiro** sets ${cipher}, ${tls_version}, ${verify}, ${cipher_bits}, 382164562Sgshapiro** ${cert} 382264562Sgshapiro*/ 382364562Sgshapiro 382464562Sgshapiroint 382564562Sgshapirotls_get_info(ssl, e, srv, host) 382664562Sgshapiro SSL *ssl; 382764562Sgshapiro ENVELOPE *e; 382864562Sgshapiro bool srv; 382964562Sgshapiro char *host; 383064562Sgshapiro{ 383164562Sgshapiro SSL_CIPHER *c; 383264562Sgshapiro int b, r; 383364562Sgshapiro char *s; 383464562Sgshapiro char bitstr[16]; 383564562Sgshapiro X509 *cert; 383664562Sgshapiro 383764562Sgshapiro c = SSL_get_current_cipher(ssl); 383864562Sgshapiro define(macid("{cipher}", NULL), newstr(SSL_CIPHER_get_name(c)), e); 383964562Sgshapiro b = SSL_CIPHER_get_bits(c, &r); 384064562Sgshapiro (void) snprintf(bitstr, sizeof bitstr, "%d", b); 384164562Sgshapiro define(macid("{cipher_bits}", NULL), newstr(bitstr), e); 384264562Sgshapiro# if _FFR_TLS_1 384364562Sgshapiro (void) snprintf(bitstr, sizeof bitstr, "%d", r); 384464562Sgshapiro define(macid("{alg_bits}", NULL), newstr(bitstr), e); 384564562Sgshapiro# endif /* _FFR_TLS_1 */ 384664562Sgshapiro s = SSL_CIPHER_get_version(c); 384764562Sgshapiro if (s == NULL) 384864562Sgshapiro s = "UNKNOWN"; 384964562Sgshapiro define(macid("{tls_version}", NULL), newstr(s), e); 385064562Sgshapiro 385164562Sgshapiro cert = SSL_get_peer_certificate(ssl); 385264562Sgshapiro if (LogLevel >= 14) 385364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 385466494Sgshapiro "TLS: get_verify in %s: %ld get_peer: 0x%lx", 385564562Sgshapiro srv ? "srv" : "clt", 385666494Sgshapiro SSL_get_verify_result(ssl), (u_long) cert); 385764562Sgshapiro if (cert != NULL) 385864562Sgshapiro { 385964562Sgshapiro char buf[MAXNAME]; 386064562Sgshapiro 386164562Sgshapiro X509_NAME_oneline(X509_get_subject_name(cert), 386264562Sgshapiro buf, sizeof buf); 386364562Sgshapiro define(macid("{cert_subject}", NULL), 386464562Sgshapiro newstr(xtextify(buf, "<>\")")), e); 386564562Sgshapiro X509_NAME_oneline(X509_get_issuer_name(cert), 386664562Sgshapiro buf, sizeof buf); 386764562Sgshapiro define(macid("{cert_issuer}", NULL), 386864562Sgshapiro newstr(xtextify(buf, "<>\")")), e); 386964562Sgshapiro# if _FFR_TLS_1 387064562Sgshapiro X509_NAME_get_text_by_NID(X509_get_subject_name(cert), 387164562Sgshapiro NID_commonName, buf, sizeof buf); 387264562Sgshapiro define(macid("{cn_subject}", NULL), 387364562Sgshapiro newstr(xtextify(buf, "<>\")")), e); 387464562Sgshapiro X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), 387564562Sgshapiro NID_commonName, buf, sizeof buf); 387664562Sgshapiro define(macid("{cn_issuer}", NULL), 387764562Sgshapiro newstr(xtextify(buf, "<>\")")), e); 387864562Sgshapiro# endif /* _FFR_TLS_1 */ 387964562Sgshapiro } 388064562Sgshapiro else 388164562Sgshapiro { 388264562Sgshapiro define(macid("{cert_subject}", NULL), "", e); 388364562Sgshapiro define(macid("{cert_issuer}", NULL), "", e); 388464562Sgshapiro# if _FFR_TLS_1 388564562Sgshapiro define(macid("{cn_subject}", NULL), "", e); 388664562Sgshapiro define(macid("{cn_issuer}", NULL), "", e); 388764562Sgshapiro# endif /* _FFR_TLS_1 */ 388864562Sgshapiro } 388964562Sgshapiro switch(SSL_get_verify_result(ssl)) 389064562Sgshapiro { 389164562Sgshapiro case X509_V_OK: 389264562Sgshapiro if (cert != NULL) 389364562Sgshapiro { 389464562Sgshapiro s = "OK"; 389564562Sgshapiro r = TLS_AUTH_OK; 389664562Sgshapiro } 389764562Sgshapiro else 389864562Sgshapiro { 389964562Sgshapiro s = "NO"; 390064562Sgshapiro r = TLS_AUTH_NO; 390164562Sgshapiro } 390264562Sgshapiro break; 390364562Sgshapiro default: 390464562Sgshapiro s = "FAIL"; 390564562Sgshapiro r = TLS_AUTH_FAIL; 390664562Sgshapiro break; 390764562Sgshapiro } 390864562Sgshapiro define(macid("{verify}", NULL), newstr(s), e); 390964562Sgshapiro if (cert != NULL) 391064562Sgshapiro X509_free(cert); 391164562Sgshapiro 391264562Sgshapiro /* do some logging */ 391364562Sgshapiro if (LogLevel > 9) 391464562Sgshapiro { 391564562Sgshapiro char *vers, *s1, *s2, *bits; 391664562Sgshapiro 391764562Sgshapiro vers = macvalue(macid("{tls_version}", NULL), e); 391864562Sgshapiro bits = macvalue(macid("{cipher_bits}", NULL), e); 391964562Sgshapiro s1 = macvalue(macid("{verify}", NULL), e); 392064562Sgshapiro s2 = macvalue(macid("{cipher}", NULL), e); 392164562Sgshapiro sm_syslog(LOG_INFO, NOQID, 392264562Sgshapiro "TLS: connection %s %.64s, version=%.16s, verify=%.16s, cipher=%.64s, bits=%.6s", 392364562Sgshapiro srv ? "from" : "to", 392464562Sgshapiro host == NULL ? "none" : host, 392564562Sgshapiro vers == NULL ? "none" : vers, 392664562Sgshapiro s1 == NULL ? "none" : s1, 392764562Sgshapiro s2 == NULL ? "none" : s2, 392864562Sgshapiro bits == NULL ? "0" : bits); 392964562Sgshapiro if (LogLevel > 11) 393064562Sgshapiro { 393164562Sgshapiro /* 393264562Sgshapiro ** maybe run xuntextify on the strings? 393364562Sgshapiro ** that is easier to read but makes it maybe a bit 393464562Sgshapiro ** more complicated to figure out the right values 393564562Sgshapiro ** for the access map... 393664562Sgshapiro */ 393764562Sgshapiro s1 = macvalue(macid("{cert_subject}", NULL), e); 393864562Sgshapiro s2 = macvalue(macid("{cert_issuer}", NULL), e); 393964562Sgshapiro sm_syslog(LOG_INFO, NOQID, 394064562Sgshapiro "TLS: %s cert subject:%.128s, cert issuer=%.128s", 394164562Sgshapiro srv ? "client" : "server", 394264562Sgshapiro s1 == NULL ? "none" : s1, 394364562Sgshapiro s2 == NULL ? "none" : s2); 394464562Sgshapiro } 394564562Sgshapiro } 394664562Sgshapiro 394764562Sgshapiro return r; 394864562Sgshapiro} 394964562Sgshapiro 395064562Sgshapiro# if !TLS_NO_RSA 395164562Sgshapiro/* 395264562Sgshapiro** TMP_RSA_KEY -- return temporary RSA key 395364562Sgshapiro** 395464562Sgshapiro** Parameters: 395564562Sgshapiro** s -- SSL connection structure 395664562Sgshapiro** export -- 395764562Sgshapiro** keylength -- 395864562Sgshapiro** 395964562Sgshapiro** Returns: 396064562Sgshapiro** temporary RSA key. 396164562Sgshapiro*/ 396264562Sgshapiro 396364562Sgshapiro/* ARGUSED0 */ 396464562Sgshapirostatic RSA * 396564562Sgshapirotmp_rsa_key(s, export, keylength) 396664562Sgshapiro SSL *s; 396764562Sgshapiro int export; 396864562Sgshapiro int keylength; 396964562Sgshapiro{ 397064562Sgshapiro return rsa_tmp; 397164562Sgshapiro} 397264562Sgshapiro# endif /* !TLS_NO_RSA */ 397364562Sgshapiro/* 397464562Sgshapiro** APPS_SSL_INFO_CB -- info callback for TLS connections 397564562Sgshapiro** 397664562Sgshapiro** Parameters: 397764562Sgshapiro** s -- SSL connection structure 397864562Sgshapiro** where -- 397964562Sgshapiro** ret -- 398064562Sgshapiro** 398164562Sgshapiro** Returns: 398264562Sgshapiro** none. 398364562Sgshapiro*/ 398464562Sgshapiro 398564562Sgshapirovoid 398664562Sgshapiroapps_ssl_info_cb(s, where, ret) 398764562Sgshapiro SSL *s; 398864562Sgshapiro int where; 398964562Sgshapiro int ret; 399064562Sgshapiro{ 399164562Sgshapiro char *str; 399264562Sgshapiro int w; 399364562Sgshapiro BIO *bio_err = NULL; 399464562Sgshapiro 399564562Sgshapiro if (LogLevel > 14) 399664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 399764562Sgshapiro "info_callback where 0x%x ret %d", where, ret); 399864562Sgshapiro 399964562Sgshapiro w = where & ~SSL_ST_MASK; 400064562Sgshapiro if (bio_err == NULL) 400164562Sgshapiro bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); 400264562Sgshapiro 400364562Sgshapiro if (w & SSL_ST_CONNECT) 400464562Sgshapiro str = "SSL_connect"; 400564562Sgshapiro else if (w & SSL_ST_ACCEPT) 400664562Sgshapiro str = "SSL_accept"; 400764562Sgshapiro else 400864562Sgshapiro str = "undefined"; 400964562Sgshapiro 401064562Sgshapiro if (where & SSL_CB_LOOP) 401164562Sgshapiro { 401264562Sgshapiro if (LogLevel > 12) 401364562Sgshapiro sm_syslog(LOG_NOTICE, NOQID, 401464562Sgshapiro "%s:%s\n", str, SSL_state_string_long(s)); 401564562Sgshapiro } 401664562Sgshapiro else if (where & SSL_CB_ALERT) 401764562Sgshapiro { 401864562Sgshapiro str = (where & SSL_CB_READ) ? "read" : "write"; 401964562Sgshapiro if (LogLevel > 12) 402064562Sgshapiro sm_syslog(LOG_NOTICE, NOQID, 402164562Sgshapiro "SSL3 alert %s:%s:%s\n", 402264562Sgshapiro str, SSL_alert_type_string_long(ret), 402364562Sgshapiro SSL_alert_desc_string_long(ret)); 402464562Sgshapiro } 402564562Sgshapiro else if (where & SSL_CB_EXIT) 402664562Sgshapiro { 402764562Sgshapiro if (ret == 0) 402864562Sgshapiro { 402964562Sgshapiro if (LogLevel > 7) 403064562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 403164562Sgshapiro "%s:failed in %s\n", 403264562Sgshapiro str, SSL_state_string_long(s)); 403364562Sgshapiro } 403464562Sgshapiro else if (ret < 0) 403564562Sgshapiro { 403664562Sgshapiro if (LogLevel > 7) 403764562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 403864562Sgshapiro "%s:error in %s\n", 403964562Sgshapiro str, SSL_state_string_long(s)); 404064562Sgshapiro } 404164562Sgshapiro } 404264562Sgshapiro} 404364562Sgshapiro/* 404464562Sgshapiro** TLS_VERIFY_LOG -- log verify error for TLS certificates 404564562Sgshapiro** 404664562Sgshapiro** Parameters: 404764562Sgshapiro** ok -- verify ok? 404864562Sgshapiro** ctx -- x509 context 404964562Sgshapiro** 405064562Sgshapiro** Returns: 405164562Sgshapiro** 0 -- fatal error 405264562Sgshapiro** 1 -- ok 405364562Sgshapiro*/ 405464562Sgshapiro 405564562Sgshapirostatic int 405664562Sgshapirotls_verify_log(ok, ctx) 405764562Sgshapiro int ok; 405864562Sgshapiro X509_STORE_CTX *ctx; 405964562Sgshapiro{ 406064562Sgshapiro SSL *ssl; 406164562Sgshapiro X509 *cert; 406264562Sgshapiro int reason, depth; 406364562Sgshapiro char buf[512]; 406464562Sgshapiro 406564562Sgshapiro cert = X509_STORE_CTX_get_current_cert(ctx); 406664562Sgshapiro reason = X509_STORE_CTX_get_error(ctx); 406764562Sgshapiro depth = X509_STORE_CTX_get_error_depth(ctx); 406864562Sgshapiro ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, 406964562Sgshapiro SSL_get_ex_data_X509_STORE_CTX_idx()); 407064562Sgshapiro 407164562Sgshapiro if (ssl == NULL) 407264562Sgshapiro { 407364562Sgshapiro /* internal error */ 407464562Sgshapiro sm_syslog(LOG_ERR, NOQID, 407564562Sgshapiro "TLS: internal error: tls_verify_cb: ssl == NULL"); 407664562Sgshapiro return 0; 407764562Sgshapiro } 407864562Sgshapiro 407964562Sgshapiro X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof buf); 408064562Sgshapiro sm_syslog(LOG_INFO, NOQID, 408164562Sgshapiro "TLS cert verify: depth=%d %s, state=%d, reason=%s\n", 408264562Sgshapiro depth, buf, ok, X509_verify_cert_error_string(reason)); 408364562Sgshapiro return 1; 408464562Sgshapiro} 408564562Sgshapiro 408664562Sgshapiro/* 408764562Sgshapiro** TLS_VERIFY_CB -- verify callback for TLS certificates 408864562Sgshapiro** 408964562Sgshapiro** Parameters: 409064562Sgshapiro** ctx -- x509 context 409164562Sgshapiro** 409264562Sgshapiro** Returns: 409364562Sgshapiro** accept connection? 409464562Sgshapiro** currently: always yes. 409564562Sgshapiro*/ 409664562Sgshapiro 409764562Sgshapirostatic int 409864562Sgshapirotls_verify_cb(ctx) 409964562Sgshapiro X509_STORE_CTX *ctx; 410064562Sgshapiro{ 410164562Sgshapiro int ok; 410264562Sgshapiro 410364562Sgshapiro ok = X509_verify_cert(ctx); 410464562Sgshapiro if (ok == 0) 410564562Sgshapiro { 410664562Sgshapiro if (LogLevel > 13) 410764562Sgshapiro return tls_verify_log(ok, ctx); 410864562Sgshapiro return 1; /* override it */ 410964562Sgshapiro } 411064562Sgshapiro return ok; 411164562Sgshapiro} 411264562Sgshapiro 411364562Sgshapiro 411464562Sgshapiro/* 411564562Sgshapiro** TLSLOGERR -- log the errors from the TLS error stack 411664562Sgshapiro** 411764562Sgshapiro** Parameters: 411864562Sgshapiro** none. 411964562Sgshapiro** 412064562Sgshapiro** Returns: 412164562Sgshapiro** none. 412264562Sgshapiro*/ 412364562Sgshapiro 412464562Sgshapirovoid 412564562Sgshapirotlslogerr() 412664562Sgshapiro{ 412764562Sgshapiro unsigned long l; 412864562Sgshapiro int line, flags; 412964562Sgshapiro unsigned long es; 413064562Sgshapiro char *file, *data; 413164562Sgshapiro char buf[256]; 413264562Sgshapiro#define CP (const char **) 413364562Sgshapiro 413464562Sgshapiro es = CRYPTO_thread_id(); 413564562Sgshapiro while ((l = ERR_get_error_line_data(CP &file, &line, CP &data, &flags)) 413664562Sgshapiro != 0) 413764562Sgshapiro { 413864562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 413964562Sgshapiro "TLS: %lu:%s:%s:%d:%s\n", es, ERR_error_string(l, buf), 414064562Sgshapiro file, line, (flags & ERR_TXT_STRING) ? data : ""); 414164562Sgshapiro } 414264562Sgshapiro} 414364562Sgshapiro 414464562Sgshapiro# endif /* STARTTLS */ 414564562Sgshapiro#endif /* SMTP */ 414664562Sgshapiro/* 414738032Speter** HELP -- implement the HELP command. 414838032Speter** 414938032Speter** Parameters: 415038032Speter** topic -- the topic we want help for. 415164562Sgshapiro** e -- envelope 415238032Speter** 415338032Speter** Returns: 415438032Speter** none. 415538032Speter** 415638032Speter** Side Effects: 415738032Speter** outputs the help file to message output. 415838032Speter*/ 415964562Sgshapiro#define HELPVSTR "#vers " 416064562Sgshapiro#define HELPVERSION 2 416138032Speter 416238032Spetervoid 416364562Sgshapirohelp(topic, e) 416438032Speter char *topic; 416564562Sgshapiro ENVELOPE *e; 416638032Speter{ 416738032Speter register FILE *hf; 416864562Sgshapiro register char *p; 416938032Speter int len; 417038032Speter bool noinfo; 417164562Sgshapiro bool first = TRUE; 417264562Sgshapiro long sff = SFF_OPENASROOT|SFF_REGONLY; 417338032Speter char buf[MAXLINE]; 417464562Sgshapiro char inp[MAXLINE]; 417564562Sgshapiro static int foundvers = -1; 417638032Speter extern char Version[]; 417738032Speter 417838032Speter if (DontLockReadFiles) 417938032Speter sff |= SFF_NOLOCK; 418064562Sgshapiro if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail)) 418138032Speter sff |= SFF_SAFEDIRPATH; 418238032Speter 418338032Speter if (HelpFile == NULL || 418438032Speter (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL) 418538032Speter { 418638032Speter /* no help */ 418738032Speter errno = 0; 418864562Sgshapiro message("502 5.3.0 Sendmail %s -- HELP not implemented", 418964562Sgshapiro Version); 419038032Speter return; 419138032Speter } 419238032Speter 419338032Speter if (topic == NULL || *topic == '\0') 419438032Speter { 419538032Speter topic = "smtp"; 419638032Speter noinfo = FALSE; 419738032Speter } 419838032Speter else 419938032Speter { 420038032Speter makelower(topic); 420138032Speter noinfo = TRUE; 420238032Speter } 420338032Speter 420438032Speter len = strlen(topic); 420538032Speter 420638032Speter while (fgets(buf, sizeof buf, hf) != NULL) 420738032Speter { 420864562Sgshapiro if (buf[0] == '#') 420964562Sgshapiro { 421064562Sgshapiro if (foundvers < 0 && 421164562Sgshapiro strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0) 421264562Sgshapiro { 421364562Sgshapiro int h; 421464562Sgshapiro 421564562Sgshapiro if (sscanf(buf + strlen(HELPVSTR), "%d", 421664562Sgshapiro &h) == 1) 421764562Sgshapiro foundvers = h; 421864562Sgshapiro } 421964562Sgshapiro continue; 422064562Sgshapiro } 422138032Speter if (strncmp(buf, topic, len) == 0) 422238032Speter { 422364562Sgshapiro if (first) 422464562Sgshapiro { 422564562Sgshapiro first = FALSE; 422638032Speter 422764562Sgshapiro /* print version if no/old vers# in file */ 422864562Sgshapiro if (foundvers < 2 && !noinfo) 422964562Sgshapiro message("214-2.0.0 This is Sendmail version %s", Version); 423064562Sgshapiro } 423164562Sgshapiro p = strpbrk(buf, " \t"); 423238032Speter if (p == NULL) 423364562Sgshapiro p = buf + strlen(buf) - 1; 423438032Speter else 423538032Speter p++; 423638032Speter fixcrlf(p, TRUE); 423764562Sgshapiro if (foundvers >= 2) 423864562Sgshapiro { 423964562Sgshapiro translate_dollars(p); 424064562Sgshapiro expand(p, inp, sizeof inp, e); 424164562Sgshapiro p = inp; 424264562Sgshapiro } 424364562Sgshapiro message("214-2.0.0 %s", p); 424438032Speter noinfo = FALSE; 424538032Speter } 424638032Speter } 424738032Speter 424838032Speter if (noinfo) 424964562Sgshapiro message("504 5.3.0 HELP topic \"%.10s\" unknown", topic); 425038032Speter else 425164562Sgshapiro message("214 2.0.0 End of HELP info"); 425264562Sgshapiro 425364562Sgshapiro if (foundvers != 0 && foundvers < HELPVERSION) 425464562Sgshapiro { 425564562Sgshapiro if (LogLevel > 1) 425664562Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 425764562Sgshapiro "%s too old (require version %d)", 425864562Sgshapiro HelpFile, HELPVERSION); 425964562Sgshapiro 426064562Sgshapiro /* avoid log next time */ 426164562Sgshapiro foundvers = 0; 426264562Sgshapiro } 426364562Sgshapiro 426438032Speter (void) fclose(hf); 426538032Speter} 4266