138032Speter/* 2261194Sgshapiro * Copyright (c) 1998-2010, 2012 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 15157001Sgshapiro#include <sm/time.h> 1638032Speter 17266527SgshapiroSM_RCSID("@(#)$Id: deliver.c,v 8.1030 2013-11-22 20:51:55 ca Exp $") 1864562Sgshapiro 1938032Speter#if HASSETUSERCONTEXT 2038032Speter# include <login_cap.h> 21363466Sgshapiro#endif 2238032Speter 2394334Sgshapiro#if NETINET || NETINET6 2494334Sgshapiro# include <arpa/inet.h> 25363466Sgshapiro#endif 2694334Sgshapiro 2790792Sgshapiro#if STARTTLS || SASL 2864562Sgshapiro# include "sfsasl.h" 29363466Sgshapiro# include "tls.h" 30363466Sgshapiro#endif 3164562Sgshapiro 3264562Sgshapirostatic int deliver __P((ENVELOPE *, ADDRESS *)); 3364562Sgshapirostatic void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 34141858Sgshapirostatic void mailfiletimeout __P((int)); 35141858Sgshapirostatic void endwaittimeout __P((int)); 3664562Sgshapirostatic int parse_hostsignature __P((char *, char **, MAILER *)); 3764562Sgshapirostatic void sendenvelope __P((ENVELOPE *, int)); 3890792Sgshapirostatic int coloncmp __P((const char *, const char *)); 3964562Sgshapiro 4090792Sgshapiro#if STARTTLS 41285229Sgshapiro# include <openssl/err.h> 42363466Sgshapiro# if DANE 43363466Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *, dane_vrfy_ctx_P)); 44363466Sgshapiro# else 4564562Sgshapirostatic int starttls __P((MAILER *, MCI *, ENVELOPE *)); 46363466Sgshapiro# endif 4790792Sgshapirostatic int endtlsclt __P((MCI *)); 4890792Sgshapiro#endif /* STARTTLS */ 49363466Sgshapiro#if STARTTLS || SASL 5090792Sgshapirostatic bool iscltflgset __P((ENVELOPE *, int)); 51363466Sgshapiro#endif 5238032Speter 53363466Sgshapiro#if _FFR_OCC 54363466Sgshapiro# include <ratectrl.h> 55363466Sgshapiro#endif 56363466Sgshapiro 5738032Speter/* 5838032Speter** SENDALL -- actually send all the messages. 5938032Speter** 6038032Speter** Parameters: 6138032Speter** e -- the envelope to send. 6238032Speter** mode -- the delivery mode to use. If SM_DEFAULT, use 6338032Speter** the current e->e_sendmode. 6438032Speter** 6538032Speter** Returns: 6638032Speter** none. 6738032Speter** 6838032Speter** Side Effects: 6938032Speter** Scans the send lists and sends everything it finds. 7038032Speter** Delivers any appropriate error messages. 7138032Speter** If we are running in a non-interactive mode, takes the 7238032Speter** appropriate action. 7338032Speter*/ 7438032Speter 7538032Spetervoid 7638032Spetersendall(e, mode) 7738032Speter ENVELOPE *e; 7838032Speter int mode; 7938032Speter{ 8038032Speter register ADDRESS *q; 8138032Speter char *owner; 8238032Speter int otherowners; 8364562Sgshapiro int save_errno; 8438032Speter register ENVELOPE *ee; 8538032Speter ENVELOPE *splitenv = NULL; 8638032Speter int oldverbose = Verbose; 8790792Sgshapiro bool somedeliveries = false, expensive = false; 8838032Speter pid_t pid; 8938032Speter 9038032Speter /* 9138032Speter ** If this message is to be discarded, don't bother sending 9238032Speter ** the message at all. 9338032Speter */ 9438032Speter 9538032Speter if (bitset(EF_DISCARD, e->e_flags)) 9638032Speter { 9738032Speter if (tTd(13, 1)) 9890792Sgshapiro sm_dprintf("sendall: discarding id %s\n", e->e_id); 9938032Speter e->e_flags |= EF_CLRQUEUE; 10090792Sgshapiro if (LogLevel > 9) 10190792Sgshapiro logundelrcpts(e, "discarded", 9, true); 10290792Sgshapiro else if (LogLevel > 4) 10338032Speter sm_syslog(LOG_INFO, e->e_id, "discarded"); 10490792Sgshapiro markstats(e, NULL, STATS_REJECT); 10538032Speter return; 10638032Speter } 10738032Speter 10838032Speter /* 10938032Speter ** If we have had global, fatal errors, don't bother sending 11038032Speter ** the message at all if we are in SMTP mode. Local errors 11138032Speter ** (e.g., a single address failing) will still cause the other 11238032Speter ** addresses to be sent. 11338032Speter */ 11438032Speter 11538032Speter if (bitset(EF_FATALERRS, e->e_flags) && 11638032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 11738032Speter { 11838032Speter e->e_flags |= EF_CLRQUEUE; 11938032Speter return; 12038032Speter } 12138032Speter 12238032Speter /* determine actual delivery mode */ 12338032Speter if (mode == SM_DEFAULT) 12438032Speter { 12538032Speter mode = e->e_sendmode; 12638032Speter if (mode != SM_VERIFY && mode != SM_DEFER && 12738032Speter shouldqueue(e->e_msgpriority, e->e_ctime)) 12838032Speter mode = SM_QUEUE; 12938032Speter } 13038032Speter 13138032Speter if (tTd(13, 1)) 13238032Speter { 13390792Sgshapiro sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 13438032Speter mode, e->e_id); 135132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 13690792Sgshapiro sm_dprintf("\te_flags = "); 13738032Speter printenvflags(e); 13890792Sgshapiro sm_dprintf("sendqueue:\n"); 139132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 14038032Speter } 14138032Speter 14238032Speter /* 14338032Speter ** Do any preprocessing necessary for the mode we are running. 14438032Speter ** Check to make sure the hop count is reasonable. 14538032Speter ** Delete sends to the sender in mailing lists. 14638032Speter */ 14738032Speter 14838032Speter CurEnv = e; 14938032Speter if (tTd(62, 1)) 15038032Speter checkfds(NULL); 15138032Speter 15238032Speter if (e->e_hopcount > MaxHopCount) 15338032Speter { 15477349Sgshapiro char *recip; 15577349Sgshapiro 15677349Sgshapiro if (e->e_sendqueue != NULL && 15777349Sgshapiro e->e_sendqueue->q_paddr != NULL) 15877349Sgshapiro recip = e->e_sendqueue->q_paddr; 15977349Sgshapiro else 16077349Sgshapiro recip = "(nobody)"; 16177349Sgshapiro 16238032Speter errno = 0; 16390792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), false); 16438032Speter e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 16564562Sgshapiro ExitStat = EX_UNAVAILABLE; 16677349Sgshapiro syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", 16777349Sgshapiro e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 16877349Sgshapiro RealHostName == NULL ? "localhost" : RealHostName, 16977349Sgshapiro recip); 17064562Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 17164562Sgshapiro { 17264562Sgshapiro if (QS_IS_DEAD(q->q_state)) 17364562Sgshapiro continue; 17464562Sgshapiro q->q_state = QS_BADADDR; 17564562Sgshapiro q->q_status = "5.4.6"; 17677349Sgshapiro q->q_rstatus = "554 5.4.6 Too many hops"; 17764562Sgshapiro } 17838032Speter return; 17938032Speter } 18038032Speter 18138032Speter /* 18238032Speter ** Do sender deletion. 18338032Speter ** 18464562Sgshapiro ** If the sender should be queued up, skip this. 18538032Speter ** This can happen if the name server is hosed when you 18638032Speter ** are trying to send mail. The result is that the sender 18738032Speter ** is instantiated in the queue as a recipient. 18838032Speter */ 18938032Speter 19038032Speter if (!bitset(EF_METOO, e->e_flags) && 19164562Sgshapiro !QS_IS_QUEUEUP(e->e_from.q_state)) 19238032Speter { 19338032Speter if (tTd(13, 5)) 19438032Speter { 19590792Sgshapiro sm_dprintf("sendall: QS_SENDER "); 196132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 19738032Speter } 19864562Sgshapiro e->e_from.q_state = QS_SENDER; 19938032Speter (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 20038032Speter } 20138032Speter 20238032Speter /* 20338032Speter ** Handle alias owners. 20438032Speter ** 20538032Speter ** We scan up the q_alias chain looking for owners. 20638032Speter ** We discard owners that are the same as the return path. 20738032Speter */ 20838032Speter 20938032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 21038032Speter { 21138032Speter register struct address *a; 21238032Speter 21338032Speter for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 21438032Speter continue; 21538032Speter if (a != NULL) 21638032Speter q->q_owner = a->q_owner; 21738032Speter 21838032Speter if (q->q_owner != NULL && 21964562Sgshapiro !QS_IS_DEAD(q->q_state) && 22038032Speter strcmp(q->q_owner, e->e_from.q_paddr) == 0) 22138032Speter q->q_owner = NULL; 22238032Speter } 22338032Speter 22438032Speter if (tTd(13, 25)) 22538032Speter { 22690792Sgshapiro sm_dprintf("\nAfter first owner pass, sendq =\n"); 227132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 22838032Speter } 22938032Speter 23038032Speter owner = ""; 23138032Speter otherowners = 1; 23238032Speter while (owner != NULL && otherowners > 0) 23338032Speter { 23438032Speter if (tTd(13, 28)) 23590792Sgshapiro sm_dprintf("owner = \"%s\", otherowners = %d\n", 23690792Sgshapiro owner, otherowners); 23738032Speter owner = NULL; 23838032Speter otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 23938032Speter 24038032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 24138032Speter { 24238032Speter if (tTd(13, 30)) 24338032Speter { 24490792Sgshapiro sm_dprintf("Checking "); 245132943Sgshapiro printaddr(sm_debug_file(), q, false); 24638032Speter } 24764562Sgshapiro if (QS_IS_DEAD(q->q_state)) 24838032Speter { 24938032Speter if (tTd(13, 30)) 25090792Sgshapiro sm_dprintf(" ... QS_IS_DEAD\n"); 25138032Speter continue; 25238032Speter } 25338032Speter if (tTd(13, 29) && !tTd(13, 30)) 25438032Speter { 25590792Sgshapiro sm_dprintf("Checking "); 256132943Sgshapiro printaddr(sm_debug_file(), q, false); 25738032Speter } 25838032Speter 25938032Speter if (q->q_owner != NULL) 26038032Speter { 26138032Speter if (owner == NULL) 26238032Speter { 26338032Speter if (tTd(13, 40)) 26490792Sgshapiro sm_dprintf(" ... First owner = \"%s\"\n", 26590792Sgshapiro q->q_owner); 26638032Speter owner = q->q_owner; 26738032Speter } 26838032Speter else if (owner != q->q_owner) 26938032Speter { 27038032Speter if (strcmp(owner, q->q_owner) == 0) 27138032Speter { 27238032Speter if (tTd(13, 40)) 27390792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 27490792Sgshapiro owner); 27538032Speter 27638032Speter /* make future comparisons cheap */ 27738032Speter q->q_owner = owner; 27838032Speter } 27938032Speter else 28038032Speter { 28138032Speter if (tTd(13, 40)) 28290792Sgshapiro sm_dprintf(" ... Another owner \"%s\"\n", 28390792Sgshapiro q->q_owner); 28438032Speter otherowners++; 28538032Speter } 28638032Speter owner = q->q_owner; 28738032Speter } 28838032Speter else if (tTd(13, 40)) 28990792Sgshapiro sm_dprintf(" ... Same owner = \"%s\"\n", 29090792Sgshapiro owner); 29138032Speter } 29238032Speter else 29338032Speter { 29438032Speter if (tTd(13, 40)) 29590792Sgshapiro sm_dprintf(" ... Null owner\n"); 29638032Speter otherowners++; 29738032Speter } 29838032Speter 29964562Sgshapiro if (QS_IS_BADADDR(q->q_state)) 30064562Sgshapiro { 30164562Sgshapiro if (tTd(13, 30)) 30290792Sgshapiro sm_dprintf(" ... QS_IS_BADADDR\n"); 30364562Sgshapiro continue; 30464562Sgshapiro } 30564562Sgshapiro 30664562Sgshapiro if (QS_IS_QUEUEUP(q->q_state)) 30764562Sgshapiro { 30864562Sgshapiro MAILER *m = q->q_mailer; 30964562Sgshapiro 31064562Sgshapiro /* 31164562Sgshapiro ** If we have temporary address failures 31264562Sgshapiro ** (e.g., dns failure) and a fallback MX is 31364562Sgshapiro ** set, send directly to the fallback MX host. 31464562Sgshapiro */ 31564562Sgshapiro 316132943Sgshapiro if (FallbackMX != NULL && 317132943Sgshapiro !wordinclass(FallbackMX, 'w') && 31864562Sgshapiro mode != SM_VERIFY && 31990792Sgshapiro !bitnset(M_NOMX, m->m_flags) && 32090792Sgshapiro strcmp(m->m_mailer, "[IPC]") == 0 && 32164562Sgshapiro m->m_argv[0] != NULL && 32290792Sgshapiro strcmp(m->m_argv[0], "TCP") == 0) 32364562Sgshapiro { 32464562Sgshapiro int len; 32564562Sgshapiro char *p; 32664562Sgshapiro 32764562Sgshapiro if (tTd(13, 30)) 328132943Sgshapiro sm_dprintf(" ... FallbackMX\n"); 32964562Sgshapiro 330132943Sgshapiro len = strlen(FallbackMX) + 1; 33190792Sgshapiro p = sm_rpool_malloc_x(e->e_rpool, len); 332132943Sgshapiro (void) sm_strlcpy(p, FallbackMX, len); 33364562Sgshapiro q->q_state = QS_OK; 33464562Sgshapiro q->q_host = p; 33564562Sgshapiro } 33664562Sgshapiro else 33764562Sgshapiro { 33864562Sgshapiro if (tTd(13, 30)) 33990792Sgshapiro sm_dprintf(" ... QS_IS_QUEUEUP\n"); 34064562Sgshapiro continue; 34164562Sgshapiro } 34264562Sgshapiro } 34364562Sgshapiro 34438032Speter /* 34538032Speter ** If this mailer is expensive, and if we don't 34638032Speter ** want to make connections now, just mark these 34738032Speter ** addresses and return. This is useful if we 34838032Speter ** want to batch connections to reduce load. This 34938032Speter ** will cause the messages to be queued up, and a 35038032Speter ** daemon will come along to send the messages later. 35138032Speter */ 35238032Speter 35364562Sgshapiro if (NoConnect && !Verbose && 35464562Sgshapiro bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 35538032Speter { 35638032Speter if (tTd(13, 30)) 35790792Sgshapiro sm_dprintf(" ... expensive\n"); 35864562Sgshapiro q->q_state = QS_QUEUEUP; 35990792Sgshapiro expensive = true; 36038032Speter } 36164562Sgshapiro else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 36264562Sgshapiro QueueLimitId == NULL && 36364562Sgshapiro QueueLimitSender == NULL && 36464562Sgshapiro QueueLimitRecipient == NULL) 36538032Speter { 36638032Speter if (tTd(13, 30)) 36790792Sgshapiro sm_dprintf(" ... hold\n"); 36864562Sgshapiro q->q_state = QS_QUEUEUP; 36990792Sgshapiro expensive = true; 37038032Speter } 37190792Sgshapiro else if (QueueMode != QM_QUARANTINE && 37290792Sgshapiro e->e_quarmsg != NULL) 37390792Sgshapiro { 37490792Sgshapiro if (tTd(13, 30)) 37590792Sgshapiro sm_dprintf(" ... quarantine: %s\n", 37690792Sgshapiro e->e_quarmsg); 37790792Sgshapiro q->q_state = QS_QUEUEUP; 37890792Sgshapiro expensive = true; 37990792Sgshapiro } 38038032Speter else 38138032Speter { 38238032Speter if (tTd(13, 30)) 38390792Sgshapiro sm_dprintf(" ... deliverable\n"); 38490792Sgshapiro somedeliveries = true; 38538032Speter } 38638032Speter } 38738032Speter 38838032Speter if (owner != NULL && otherowners > 0) 38938032Speter { 39038032Speter /* 39138032Speter ** Split this envelope into two. 39238032Speter */ 39338032Speter 39490792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 395168515Sgshapiro sizeof(*ee)); 39690792Sgshapiro STRUCTCOPY(*e, *ee); 39764562Sgshapiro ee->e_message = NULL; 39838032Speter ee->e_id = NULL; 39964562Sgshapiro assign_queueid(ee); 40038032Speter 40138032Speter if (tTd(13, 1)) 40290792Sgshapiro sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 40390792Sgshapiro e->e_id, ee->e_id, owner, 40490792Sgshapiro otherowners); 40538032Speter 40690792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 40790792Sgshapiro ee->e_sendqueue = copyqueue(e->e_sendqueue, 40890792Sgshapiro ee->e_rpool); 40990792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, 41090792Sgshapiro ee->e_rpool); 41138032Speter ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 41238032Speter ee->e_flags |= EF_NORECEIPT; 41390792Sgshapiro setsender(owner, ee, NULL, '\0', true); 41438032Speter if (tTd(13, 5)) 41538032Speter { 41690792Sgshapiro sm_dprintf("sendall(split): QS_SENDER "); 417132943Sgshapiro printaddr(sm_debug_file(), &ee->e_from, false); 41838032Speter } 41964562Sgshapiro ee->e_from.q_state = QS_SENDER; 42038032Speter ee->e_dfp = NULL; 42164562Sgshapiro ee->e_lockfp = NULL; 42238032Speter ee->e_xfp = NULL; 42390792Sgshapiro ee->e_qgrp = e->e_qgrp; 42490792Sgshapiro ee->e_qdir = e->e_qdir; 42538032Speter ee->e_errormode = EM_MAIL; 42638032Speter ee->e_sibling = splitenv; 42764562Sgshapiro ee->e_statmsg = NULL; 42890792Sgshapiro if (e->e_quarmsg != NULL) 42990792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 43090792Sgshapiro e->e_quarmsg); 43138032Speter splitenv = ee; 43238032Speter 43338032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 43438032Speter { 43538032Speter if (q->q_owner == owner) 43638032Speter { 43764562Sgshapiro q->q_state = QS_CLONED; 43838032Speter if (tTd(13, 6)) 43990792Sgshapiro sm_dprintf("\t... stripping %s from original envelope\n", 44090792Sgshapiro q->q_paddr); 44138032Speter } 44238032Speter } 44338032Speter for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 44438032Speter { 44538032Speter if (q->q_owner != owner) 44638032Speter { 44764562Sgshapiro q->q_state = QS_CLONED; 44838032Speter if (tTd(13, 6)) 44990792Sgshapiro sm_dprintf("\t... dropping %s from cloned envelope\n", 45090792Sgshapiro q->q_paddr); 45138032Speter } 45238032Speter else 45338032Speter { 45438032Speter /* clear DSN parameters */ 45538032Speter q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 45638032Speter q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 45738032Speter if (tTd(13, 6)) 45890792Sgshapiro sm_dprintf("\t... moving %s to cloned envelope\n", 45990792Sgshapiro q->q_paddr); 46038032Speter } 46138032Speter } 46238032Speter 46338032Speter if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 46490792Sgshapiro dup_queue_file(e, ee, DATAFL_LETTER); 46564562Sgshapiro 46664562Sgshapiro /* 46764562Sgshapiro ** Give the split envelope access to the parent 46864562Sgshapiro ** transcript file for errors obtained while 46964562Sgshapiro ** processing the recipients (done before the 47064562Sgshapiro ** envelope splitting). 47164562Sgshapiro */ 47264562Sgshapiro 47364562Sgshapiro if (e->e_xfp != NULL) 47490792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 47564562Sgshapiro 47664562Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 47764562Sgshapiro if (ee->e_xfp == NULL) 47864562Sgshapiro openxscript(ee); 47964562Sgshapiro 48042575Speter if (mode != SM_VERIFY && LogLevel > 4) 48190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 48290792Sgshapiro "%s: clone: owner=%s", 48390792Sgshapiro ee->e_id, owner); 48438032Speter } 48538032Speter } 48638032Speter 48738032Speter if (owner != NULL) 48838032Speter { 48990792Sgshapiro setsender(owner, e, NULL, '\0', true); 49038032Speter if (tTd(13, 5)) 49138032Speter { 49290792Sgshapiro sm_dprintf("sendall(owner): QS_SENDER "); 493132943Sgshapiro printaddr(sm_debug_file(), &e->e_from, false); 49438032Speter } 49564562Sgshapiro e->e_from.q_state = QS_SENDER; 49638032Speter e->e_errormode = EM_MAIL; 49738032Speter e->e_flags |= EF_NORECEIPT; 49838032Speter e->e_flags &= ~EF_FATALERRS; 49938032Speter } 50038032Speter 50138032Speter /* if nothing to be delivered, just queue up everything */ 50290792Sgshapiro if (!somedeliveries && !WILL_BE_QUEUED(mode) && 50338032Speter mode != SM_VERIFY) 50438032Speter { 50590792Sgshapiro time_t now; 50671345Sgshapiro 50738032Speter if (tTd(13, 29)) 508173340Sgshapiro sm_dprintf("No deliveries: auto-queueing\n"); 50938032Speter mode = SM_QUEUE; 51090792Sgshapiro now = curtime(); 51138032Speter 51238032Speter /* treat this as a delivery in terms of counting tries */ 51371345Sgshapiro e->e_dtime = now; 51438032Speter if (!expensive) 51538032Speter e->e_ntries++; 51638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 51738032Speter { 51871345Sgshapiro ee->e_dtime = now; 51938032Speter if (!expensive) 52038032Speter ee->e_ntries++; 52138032Speter } 52238032Speter } 52338032Speter 52490792Sgshapiro if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 525132943Sgshapiro (mode != SM_VERIFY && 526132943Sgshapiro (SuperSafe == SAFE_REALLY || 527132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER))) && 52838032Speter (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 52938032Speter { 53090792Sgshapiro bool msync; 53190792Sgshapiro 53266494Sgshapiro /* 53366494Sgshapiro ** Be sure everything is instantiated in the queue. 53466494Sgshapiro ** Split envelopes first in case the machine crashes. 53566494Sgshapiro ** If the original were done first, we may lose 53666494Sgshapiro ** recipients. 53766494Sgshapiro */ 53866494Sgshapiro 53990792Sgshapiro#if !HASFLOCK 54090792Sgshapiro msync = false; 541363466Sgshapiro#else 54290792Sgshapiro msync = mode == SM_FORK; 543363466Sgshapiro#endif 54490792Sgshapiro 54538032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 54690792Sgshapiro queueup(ee, WILL_BE_QUEUED(mode), msync); 54790792Sgshapiro queueup(e, WILL_BE_QUEUED(mode), msync); 54838032Speter } 54938032Speter 55038032Speter if (tTd(62, 10)) 55138032Speter checkfds("after envelope splitting"); 55238032Speter 55338032Speter /* 55438032Speter ** If we belong in background, fork now. 55538032Speter */ 55638032Speter 55738032Speter if (tTd(13, 20)) 55838032Speter { 55990792Sgshapiro sm_dprintf("sendall: final mode = %c\n", mode); 56038032Speter if (tTd(13, 21)) 56138032Speter { 56290792Sgshapiro sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 56390792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56490792Sgshapiro e->e_id, e->e_from.q_paddr); 565132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 56638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 56738032Speter { 56890792Sgshapiro sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 56990792Sgshapiro ee->e_id, ee->e_from.q_paddr); 570132943Sgshapiro printaddr(sm_debug_file(), ee->e_sendqueue, true); 57138032Speter } 57290792Sgshapiro sm_dprintf("==========================================================\n\n"); 57338032Speter } 57438032Speter } 57538032Speter switch (mode) 57638032Speter { 57738032Speter case SM_VERIFY: 57838032Speter Verbose = 2; 57938032Speter break; 58038032Speter 58138032Speter case SM_QUEUE: 58238032Speter case SM_DEFER: 58364562Sgshapiro#if HASFLOCK 58438032Speter queueonly: 585363466Sgshapiro#endif 58638032Speter if (e->e_nrcpts > 0) 58738032Speter e->e_flags |= EF_INQUEUE; 588203004Sgshapiro (void) dropenvelope(e, splitenv != NULL, true); 58938032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 59038032Speter { 59138032Speter if (ee->e_nrcpts > 0) 59238032Speter ee->e_flags |= EF_INQUEUE; 593203004Sgshapiro (void) dropenvelope(ee, false, true); 59438032Speter } 59538032Speter return; 59638032Speter 59738032Speter case SM_FORK: 59838032Speter if (e->e_xfp != NULL) 59990792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 60038032Speter 60164562Sgshapiro#if !HASFLOCK 60238032Speter /* 60338032Speter ** Since fcntl locking has the interesting semantic that 60438032Speter ** the lock is owned by a process, not by an open file 60538032Speter ** descriptor, we have to flush this to the queue, and 60638032Speter ** then restart from scratch in the child. 60738032Speter */ 60838032Speter 60938032Speter { 61038032Speter /* save id for future use */ 61138032Speter char *qid = e->e_id; 61238032Speter 61338032Speter /* now drop the envelope in the parent */ 61438032Speter e->e_flags |= EF_INQUEUE; 615203004Sgshapiro (void) dropenvelope(e, splitenv != NULL, false); 61638032Speter 61738032Speter /* arrange to reacquire lock after fork */ 61838032Speter e->e_id = qid; 61938032Speter } 62038032Speter 62138032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 62238032Speter { 62338032Speter /* save id for future use */ 62438032Speter char *qid = ee->e_id; 62538032Speter 62638032Speter /* drop envelope in parent */ 62738032Speter ee->e_flags |= EF_INQUEUE; 628203004Sgshapiro (void) dropenvelope(ee, false, false); 62938032Speter 63038032Speter /* and save qid for reacquisition */ 63138032Speter ee->e_id = qid; 63238032Speter } 633132943Sgshapiro 63464562Sgshapiro#endif /* !HASFLOCK */ 63538032Speter 63664562Sgshapiro /* 63764562Sgshapiro ** Since the delivery may happen in a child and the parent 63864562Sgshapiro ** does not wait, the parent may close the maps thereby 63964562Sgshapiro ** removing any shared memory used by the map. Therefore, 64064562Sgshapiro ** close the maps now so the child will dynamically open 64164562Sgshapiro ** them if necessary. 64264562Sgshapiro */ 64364562Sgshapiro 64490792Sgshapiro closemaps(false); 64564562Sgshapiro 64638032Speter pid = fork(); 64738032Speter if (pid < 0) 64838032Speter { 64964562Sgshapiro syserr("deliver: fork 1"); 65064562Sgshapiro#if HASFLOCK 65138032Speter goto queueonly; 65264562Sgshapiro#else /* HASFLOCK */ 65338032Speter e->e_id = NULL; 65438032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 65538032Speter ee->e_id = NULL; 65638032Speter return; 65764562Sgshapiro#endif /* HASFLOCK */ 65838032Speter } 65938032Speter else if (pid > 0) 66038032Speter { 66164562Sgshapiro#if HASFLOCK 66238032Speter /* be sure we leave the temp files to our child */ 66338032Speter /* close any random open files in the envelope */ 66438032Speter closexscript(e); 66538032Speter if (e->e_dfp != NULL) 66690792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 66738032Speter e->e_dfp = NULL; 66838032Speter e->e_flags &= ~EF_HAS_DF; 66938032Speter 67038032Speter /* can't call unlockqueue to avoid unlink of xfp */ 67138032Speter if (e->e_lockfp != NULL) 67290792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 67364562Sgshapiro else 67464562Sgshapiro syserr("%s: sendall: null lockfp", e->e_id); 67538032Speter e->e_lockfp = NULL; 67664562Sgshapiro#endif /* HASFLOCK */ 67738032Speter 67838032Speter /* make sure the parent doesn't own the envelope */ 67938032Speter e->e_id = NULL; 68038032Speter 68190792Sgshapiro#if USE_DOUBLE_FORK 68238032Speter /* catch intermediate zombie */ 68338032Speter (void) waitfor(pid); 684363466Sgshapiro#endif 68538032Speter return; 68638032Speter } 68738032Speter 68877349Sgshapiro /* Reset global flags */ 68977349Sgshapiro RestartRequest = NULL; 69090792Sgshapiro RestartWorkGroup = false; 69177349Sgshapiro ShutdownRequest = NULL; 69277349Sgshapiro PendingSignal = 0; 69377349Sgshapiro 69466494Sgshapiro /* 69590792Sgshapiro ** Initialize exception stack and default exception 69690792Sgshapiro ** handler for child process. 69790792Sgshapiro */ 69890792Sgshapiro 69990792Sgshapiro sm_exc_newthread(fatal_error); 70090792Sgshapiro 70190792Sgshapiro /* 70266494Sgshapiro ** Since we have accepted responsbility for the message, 70366494Sgshapiro ** change the SIGTERM handler. intsig() (the old handler) 70466494Sgshapiro ** would remove the envelope if this was a command line 70566494Sgshapiro ** message submission. 70666494Sgshapiro */ 70766494Sgshapiro 70890792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 70966494Sgshapiro 71090792Sgshapiro#if USE_DOUBLE_FORK 71138032Speter /* double fork to avoid zombies */ 71238032Speter pid = fork(); 71338032Speter if (pid > 0) 71438032Speter exit(EX_OK); 71564562Sgshapiro save_errno = errno; 71690792Sgshapiro#endif /* USE_DOUBLE_FORK */ 71738032Speter 71890792Sgshapiro CurrentPid = getpid(); 71990792Sgshapiro 72038032Speter /* be sure we are immune from the terminal */ 72138032Speter disconnect(2, e); 72264562Sgshapiro clearstats(); 72338032Speter 72438032Speter /* prevent parent from waiting if there was an error */ 72538032Speter if (pid < 0) 72638032Speter { 72764562Sgshapiro errno = save_errno; 72864562Sgshapiro syserr("deliver: fork 2"); 72964562Sgshapiro#if HASFLOCK 73038032Speter e->e_flags |= EF_INQUEUE; 731363466Sgshapiro#else 73238032Speter e->e_id = NULL; 733363466Sgshapiro#endif 73490792Sgshapiro finis(true, true, ExitStat); 73538032Speter } 73638032Speter 73738032Speter /* be sure to give error messages in child */ 73890792Sgshapiro QuickAbort = false; 73938032Speter 74038032Speter /* 74138032Speter ** Close any cached connections. 74238032Speter ** 74338032Speter ** We don't send the QUIT protocol because the parent 74438032Speter ** still knows about the connection. 74538032Speter ** 74638032Speter ** This should only happen when delivering an error 74738032Speter ** message. 74838032Speter */ 74938032Speter 75090792Sgshapiro mci_flush(false, NULL); 75138032Speter 75264562Sgshapiro#if HASFLOCK 75338032Speter break; 75464562Sgshapiro#else /* HASFLOCK */ 75538032Speter 75638032Speter /* 75738032Speter ** Now reacquire and run the various queue files. 75838032Speter */ 75938032Speter 76038032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 76138032Speter { 76238032Speter ENVELOPE *sibling = ee->e_sibling; 76338032Speter 76490792Sgshapiro (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 76590792Sgshapiro false, false, ee); 76638032Speter ee->e_sibling = sibling; 76738032Speter } 76890792Sgshapiro (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 76990792Sgshapiro false, false, e); 77090792Sgshapiro finis(true, true, ExitStat); 77164562Sgshapiro#endif /* HASFLOCK */ 77238032Speter } 77338032Speter 77438032Speter sendenvelope(e, mode); 775203004Sgshapiro (void) dropenvelope(e, true, true); 77638032Speter for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 77738032Speter { 77838032Speter CurEnv = ee; 77938032Speter if (mode != SM_VERIFY) 78038032Speter openxscript(ee); 78138032Speter sendenvelope(ee, mode); 782203004Sgshapiro (void) dropenvelope(ee, true, true); 78338032Speter } 78438032Speter CurEnv = e; 78538032Speter 78638032Speter Verbose = oldverbose; 78738032Speter if (mode == SM_FORK) 78890792Sgshapiro finis(true, true, ExitStat); 78938032Speter} 79038032Speter 79164562Sgshapirostatic void 79238032Spetersendenvelope(e, mode) 79338032Speter register ENVELOPE *e; 79438032Speter int mode; 79538032Speter{ 79638032Speter register ADDRESS *q; 79738032Speter bool didany; 79838032Speter 79938032Speter if (tTd(13, 10)) 80090792Sgshapiro sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 80190792Sgshapiro e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 80290792Sgshapiro e->e_flags); 80338032Speter if (LogLevel > 80) 80438032Speter sm_syslog(LOG_DEBUG, e->e_id, 80564562Sgshapiro "sendenvelope, flags=0x%lx", 80664562Sgshapiro e->e_flags); 80738032Speter 80838032Speter /* 80938032Speter ** If we have had global, fatal errors, don't bother sending 81038032Speter ** the message at all if we are in SMTP mode. Local errors 81138032Speter ** (e.g., a single address failing) will still cause the other 81238032Speter ** addresses to be sent. 81338032Speter */ 81438032Speter 81538032Speter if (bitset(EF_FATALERRS, e->e_flags) && 81638032Speter (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 81738032Speter { 81838032Speter e->e_flags |= EF_CLRQUEUE; 81938032Speter return; 82038032Speter } 82138032Speter 82290792Sgshapiro /* 82390792Sgshapiro ** Don't attempt deliveries if we want to bounce now 82490792Sgshapiro ** or if deliver-by time is exceeded. 82590792Sgshapiro */ 82690792Sgshapiro 82764562Sgshapiro if (!bitset(EF_RESPONSE, e->e_flags) && 82890792Sgshapiro (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 82990792Sgshapiro (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 83090792Sgshapiro curtime() > e->e_ctime + e->e_deliver_by))) 83164562Sgshapiro return; 83264562Sgshapiro 83338032Speter /* 83438032Speter ** Run through the list and send everything. 83538032Speter ** 83638032Speter ** Set EF_GLOBALERRS so that error messages during delivery 83738032Speter ** result in returned mail. 83838032Speter */ 83938032Speter 84038032Speter e->e_nsent = 0; 84138032Speter e->e_flags |= EF_GLOBALERRS; 84264562Sgshapiro 84390792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 84490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 84590792Sgshapiro didany = false; 84638032Speter 84790792Sgshapiro if (!bitset(EF_SPLIT, e->e_flags)) 84890792Sgshapiro { 84990792Sgshapiro ENVELOPE *oldsib; 85090792Sgshapiro ENVELOPE *ee; 85190792Sgshapiro 85290792Sgshapiro /* 85390792Sgshapiro ** Save old sibling and set it to NULL to avoid 85490792Sgshapiro ** queueing up the same envelopes again. 85590792Sgshapiro ** This requires that envelopes in that list have 85690792Sgshapiro ** been take care of before (or at some other place). 85790792Sgshapiro */ 85890792Sgshapiro 85990792Sgshapiro oldsib = e->e_sibling; 86090792Sgshapiro e->e_sibling = NULL; 86190792Sgshapiro if (!split_by_recipient(e) && 86290792Sgshapiro bitset(EF_FATALERRS, e->e_flags)) 86390792Sgshapiro { 86490792Sgshapiro if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 86590792Sgshapiro e->e_flags |= EF_CLRQUEUE; 86690792Sgshapiro return; 86790792Sgshapiro } 86890792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 86990792Sgshapiro queueup(ee, false, true); 87090792Sgshapiro 87190792Sgshapiro /* clean up */ 87290792Sgshapiro for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 87390792Sgshapiro { 87490792Sgshapiro /* now unlock the job */ 87590792Sgshapiro closexscript(ee); 87690792Sgshapiro unlockqueue(ee); 87790792Sgshapiro 87890792Sgshapiro /* this envelope is marked unused */ 87990792Sgshapiro if (ee->e_dfp != NULL) 88090792Sgshapiro { 88190792Sgshapiro (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 88290792Sgshapiro ee->e_dfp = NULL; 88390792Sgshapiro } 88490792Sgshapiro ee->e_id = NULL; 88590792Sgshapiro ee->e_flags &= ~EF_HAS_DF; 88690792Sgshapiro } 88790792Sgshapiro e->e_sibling = oldsib; 88890792Sgshapiro } 88990792Sgshapiro 89038032Speter /* now run through the queue */ 89138032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 89238032Speter { 89338032Speter#if XDEBUG 89438032Speter char wbuf[MAXNAME + 20]; 89538032Speter 896168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)", 89790792Sgshapiro MAXNAME, q->q_paddr); 89838032Speter checkfd012(wbuf); 89964562Sgshapiro#endif /* XDEBUG */ 90038032Speter if (mode == SM_VERIFY) 90138032Speter { 90238032Speter e->e_to = q->q_paddr; 90364562Sgshapiro if (QS_IS_SENDABLE(q->q_state)) 90438032Speter { 90538032Speter if (q->q_host != NULL && q->q_host[0] != '\0') 90638032Speter message("deliverable: mailer %s, host %s, user %s", 90738032Speter q->q_mailer->m_name, 90838032Speter q->q_host, 90938032Speter q->q_user); 91038032Speter else 91138032Speter message("deliverable: mailer %s, user %s", 91238032Speter q->q_mailer->m_name, 91338032Speter q->q_user); 91438032Speter } 91538032Speter } 91664562Sgshapiro else if (QS_IS_OK(q->q_state)) 91738032Speter { 91838032Speter /* 91938032Speter ** Checkpoint the send list every few addresses 92038032Speter */ 92138032Speter 92266494Sgshapiro if (CheckpointInterval > 0 && 92366494Sgshapiro e->e_nsent >= CheckpointInterval) 92438032Speter { 92590792Sgshapiro queueup(e, false, false); 92638032Speter e->e_nsent = 0; 92738032Speter } 92838032Speter (void) deliver(e, q); 92990792Sgshapiro didany = true; 93038032Speter } 93138032Speter } 93238032Speter if (didany) 93338032Speter { 93438032Speter e->e_dtime = curtime(); 93538032Speter e->e_ntries++; 93638032Speter } 93738032Speter 93838032Speter#if XDEBUG 93938032Speter checkfd012("end of sendenvelope"); 940363466Sgshapiro#endif 94138032Speter} 94290792Sgshapiro 94390792Sgshapiro#if REQUIRES_DIR_FSYNC 94490792Sgshapiro/* 94590792Sgshapiro** SYNC_DIR -- fsync a directory based on a filename 94690792Sgshapiro** 94790792Sgshapiro** Parameters: 94890792Sgshapiro** filename -- path of file 94990792Sgshapiro** panic -- panic? 95090792Sgshapiro** 95190792Sgshapiro** Returns: 95290792Sgshapiro** none 95390792Sgshapiro*/ 95490792Sgshapiro 95590792Sgshapirovoid 95690792Sgshapirosync_dir(filename, panic) 95790792Sgshapiro char *filename; 95890792Sgshapiro bool panic; 95990792Sgshapiro{ 96090792Sgshapiro int dirfd; 96190792Sgshapiro char *dirp; 96290792Sgshapiro char dir[MAXPATHLEN]; 96390792Sgshapiro 964110560Sgshapiro if (!RequiresDirfsync) 965110560Sgshapiro return; 966110560Sgshapiro 96790792Sgshapiro /* filesystems which require the directory be synced */ 96890792Sgshapiro dirp = strrchr(filename, '/'); 96990792Sgshapiro if (dirp != NULL) 97090792Sgshapiro { 971168515Sgshapiro if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir)) 97290792Sgshapiro return; 97390792Sgshapiro dir[dirp - filename] = '\0'; 97490792Sgshapiro dirp = dir; 97590792Sgshapiro } 97690792Sgshapiro else 97790792Sgshapiro dirp = "."; 97890792Sgshapiro dirfd = open(dirp, O_RDONLY, 0700); 97990792Sgshapiro if (tTd(40,32)) 98090792Sgshapiro sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 98190792Sgshapiro dirp, dirfd); 98290792Sgshapiro if (dirfd >= 0) 98390792Sgshapiro { 98490792Sgshapiro if (fsync(dirfd) < 0) 98590792Sgshapiro { 98690792Sgshapiro if (panic) 98790792Sgshapiro syserr("!sync_dir: cannot fsync directory %s", 98890792Sgshapiro dirp); 98990792Sgshapiro else if (LogLevel > 1) 99090792Sgshapiro sm_syslog(LOG_ERR, NOQID, 99190792Sgshapiro "sync_dir: cannot fsync directory %s: %s", 99290792Sgshapiro dirp, sm_errstring(errno)); 99390792Sgshapiro } 99490792Sgshapiro (void) close(dirfd); 99590792Sgshapiro } 99690792Sgshapiro} 99790792Sgshapiro#endif /* REQUIRES_DIR_FSYNC */ 99890792Sgshapiro/* 99938032Speter** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 100038032Speter** 100138032Speter** Parameters: 100238032Speter** e -- the existing envelope 100338032Speter** ee -- the new envelope 100490792Sgshapiro** type -- the queue file type (e.g., DATAFL_LETTER) 100538032Speter** 100638032Speter** Returns: 100738032Speter** none 100838032Speter*/ 100938032Speter 101064562Sgshapirostatic void 101138032Speterdup_queue_file(e, ee, type) 101290792Sgshapiro ENVELOPE *e, *ee; 101338032Speter int type; 101438032Speter{ 101564562Sgshapiro char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 101638032Speter 101738032Speter ee->e_dfp = NULL; 101838032Speter ee->e_xfp = NULL; 101964562Sgshapiro 102064562Sgshapiro /* 102164562Sgshapiro ** Make sure both are in the same directory. 102264562Sgshapiro */ 102364562Sgshapiro 1024168515Sgshapiro (void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf)); 1025168515Sgshapiro (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf)); 1026102528Sgshapiro 1027102528Sgshapiro /* Force the df to disk if it's not there yet */ 1028102528Sgshapiro if (type == DATAFL_LETTER && e->e_dfp != NULL && 1029102528Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1030102528Sgshapiro errno != EINVAL) 1031102528Sgshapiro { 1032102528Sgshapiro syserr("!dup_queue_file: can't commit %s", f1buf); 1033102528Sgshapiro /* NOTREACHED */ 1034102528Sgshapiro } 1035102528Sgshapiro 103638032Speter if (link(f1buf, f2buf) < 0) 103738032Speter { 103864562Sgshapiro int save_errno = errno; 103938032Speter 104038032Speter syserr("sendall: link(%s, %s)", f1buf, f2buf); 104164562Sgshapiro if (save_errno == EEXIST) 104238032Speter { 104338032Speter if (unlink(f2buf) < 0) 104438032Speter { 104538032Speter syserr("!sendall: unlink(%s): permanent", 104690792Sgshapiro f2buf); 104764562Sgshapiro /* NOTREACHED */ 104838032Speter } 104938032Speter if (link(f1buf, f2buf) < 0) 105038032Speter { 105138032Speter syserr("!sendall: link(%s, %s): permanent", 105290792Sgshapiro f1buf, f2buf); 105364562Sgshapiro /* NOTREACHED */ 105438032Speter } 105538032Speter } 105638032Speter } 105790792Sgshapiro SYNC_DIR(f2buf, true); 105838032Speter} 105990792Sgshapiro/* 106038032Speter** DOFORK -- do a fork, retrying a couple of times on failure. 106138032Speter** 106238032Speter** This MUST be a macro, since after a vfork we are running 106338032Speter** two processes on the same stack!!! 106438032Speter** 106538032Speter** Parameters: 106638032Speter** none. 106738032Speter** 106838032Speter** Returns: 106938032Speter** From a macro??? You've got to be kidding! 107038032Speter** 107138032Speter** Side Effects: 107238032Speter** Modifies the ==> LOCAL <== variable 'pid', leaving: 107338032Speter** pid of child in parent, zero in child. 107438032Speter** -1 on unrecoverable error. 107538032Speter** 107638032Speter** Notes: 107738032Speter** I'm awfully sorry this looks so awful. That's 107838032Speter** vfork for you..... 107938032Speter*/ 108038032Speter 108164562Sgshapiro#define NFORKTRIES 5 108238032Speter 108364562Sgshapiro#ifndef FORK 108438032Speter# define FORK fork 1085363466Sgshapiro#endif 108638032Speter 108764562Sgshapiro#define DOFORK(fORKfN) \ 108838032Speter{\ 108938032Speter register int i;\ 109038032Speter\ 109138032Speter for (i = NFORKTRIES; --i >= 0; )\ 109238032Speter {\ 109338032Speter pid = fORKfN();\ 109438032Speter if (pid >= 0)\ 109538032Speter break;\ 109638032Speter if (i > 0)\ 109764562Sgshapiro (void) sleep((unsigned) NFORKTRIES - i);\ 109838032Speter }\ 109938032Speter} 110090792Sgshapiro/* 110138032Speter** DOFORK -- simple fork interface to DOFORK. 110238032Speter** 110338032Speter** Parameters: 110438032Speter** none. 110538032Speter** 110638032Speter** Returns: 110738032Speter** pid of child in parent. 110838032Speter** zero in child. 110938032Speter** -1 on error. 111038032Speter** 111138032Speter** Side Effects: 111238032Speter** returns twice, once in parent and once in child. 111338032Speter*/ 111438032Speter 111577349Sgshapiropid_t 111638032Speterdofork() 111738032Speter{ 111838032Speter register pid_t pid = -1; 111938032Speter 112038032Speter DOFORK(fork); 112164562Sgshapiro return pid; 112238032Speter} 112390792Sgshapiro 112490792Sgshapiro/* 112590792Sgshapiro** COLONCMP -- compare host-signatures up to first ':' or EOS 112690792Sgshapiro** 112790792Sgshapiro** This takes two strings which happen to be host-signatures and 112890792Sgshapiro** compares them. If the lowest preference portions of the MX-RR's 112990792Sgshapiro** match (up to ':' or EOS, whichever is first), then we have 113090792Sgshapiro** match. This is used for coattail-piggybacking messages during 113190792Sgshapiro** message delivery. 113290792Sgshapiro** If the signatures are the same up to the first ':' the remainder of 113390792Sgshapiro** the signatures are then compared with a normal strcmp(). This saves 113490792Sgshapiro** re-examining the first part of the signatures. 113590792Sgshapiro** 113690792Sgshapiro** Parameters: 113790792Sgshapiro** a - first host-signature 113890792Sgshapiro** b - second host-signature 113990792Sgshapiro** 114090792Sgshapiro** Returns: 114190792Sgshapiro** HS_MATCH_NO -- no "match". 114290792Sgshapiro** HS_MATCH_FIRST -- "match" for the first MX preference 114390792Sgshapiro** (up to the first colon (':')). 114490792Sgshapiro** HS_MATCH_FULL -- match for the entire MX record. 114590792Sgshapiro** 114690792Sgshapiro** Side Effects: 114790792Sgshapiro** none. 114890792Sgshapiro*/ 114990792Sgshapiro 115090792Sgshapiro#define HS_MATCH_NO 0 115190792Sgshapiro#define HS_MATCH_FIRST 1 115290792Sgshapiro#define HS_MATCH_FULL 2 115390792Sgshapiro 115490792Sgshapirostatic int 115590792Sgshapirocoloncmp(a, b) 115690792Sgshapiro register const char *a; 115790792Sgshapiro register const char *b; 115890792Sgshapiro{ 115990792Sgshapiro int ret = HS_MATCH_NO; 116090792Sgshapiro int braclev = 0; 116190792Sgshapiro 116290792Sgshapiro while (*a == *b++) 116390792Sgshapiro { 116490792Sgshapiro /* Need to account for IPv6 bracketed addresses */ 116590792Sgshapiro if (*a == '[') 116690792Sgshapiro braclev++; 1167112810Sgshapiro else if (*a == ']' && braclev > 0) 116890792Sgshapiro braclev--; 116990792Sgshapiro else if (*a == ':' && braclev <= 0) 117090792Sgshapiro { 117190792Sgshapiro ret = HS_MATCH_FIRST; 117290792Sgshapiro a++; 117390792Sgshapiro break; 117490792Sgshapiro } 117590792Sgshapiro else if (*a == '\0') 117690792Sgshapiro return HS_MATCH_FULL; /* a full match */ 117790792Sgshapiro a++; 117890792Sgshapiro } 117990792Sgshapiro if (ret == HS_MATCH_NO && 118090792Sgshapiro braclev <= 0 && 118190792Sgshapiro ((*a == '\0' && *(b - 1) == ':') || 118290792Sgshapiro (*a == ':' && *(b - 1) == '\0'))) 118390792Sgshapiro return HS_MATCH_FIRST; 118490792Sgshapiro if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 118590792Sgshapiro return HS_MATCH_FULL; 118690792Sgshapiro 118790792Sgshapiro return ret; 118890792Sgshapiro} 1189132943Sgshapiro 119090792Sgshapiro/* 1191132943Sgshapiro** SHOULD_TRY_FBSH -- Should try FallbackSmartHost? 1192132943Sgshapiro** 1193132943Sgshapiro** Parameters: 1194132943Sgshapiro** e -- envelope 1195132943Sgshapiro** tried_fallbacksmarthost -- has been tried already? (in/out) 1196132943Sgshapiro** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out) 1197132943Sgshapiro** hbsz -- size of hostbuf 1198132943Sgshapiro** status -- current delivery status 1199132943Sgshapiro** 1200132943Sgshapiro** Returns: 1201132943Sgshapiro** true iff FallbackSmartHost should be tried. 1202132943Sgshapiro*/ 1203132943Sgshapiro 1204168515Sgshapirostatic bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int)); 1205168515Sgshapiro 1206132943Sgshapirostatic bool 1207132943Sgshapiroshould_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 1208132943Sgshapiro ENVELOPE *e; 1209132943Sgshapiro bool *tried_fallbacksmarthost; 1210132943Sgshapiro char *hostbuf; 1211132943Sgshapiro size_t hbsz; 1212132943Sgshapiro int status; 1213132943Sgshapiro{ 1214132943Sgshapiro /* 1215157001Sgshapiro ** If the host was not found or a temporary failure occurred 1216157001Sgshapiro ** and a FallbackSmartHost is defined (and we have not yet 1217157001Sgshapiro ** tried it), then make one last try with it as the host. 1218132943Sgshapiro */ 1219132943Sgshapiro 1220157001Sgshapiro if ((status == EX_NOHOST || status == EX_TEMPFAIL) && 1221157001Sgshapiro FallbackSmartHost != NULL && !*tried_fallbacksmarthost) 1222132943Sgshapiro { 1223132943Sgshapiro *tried_fallbacksmarthost = true; 1224132943Sgshapiro expand(FallbackSmartHost, hostbuf, hbsz, e); 1225132943Sgshapiro if (!wordinclass(hostbuf, 'w')) 1226132943Sgshapiro { 1227132943Sgshapiro if (tTd(11, 1)) 1228132943Sgshapiro sm_dprintf("one last try with FallbackSmartHost %s\n", 1229132943Sgshapiro hostbuf); 1230132943Sgshapiro return true; 1231132943Sgshapiro } 1232132943Sgshapiro } 1233132943Sgshapiro return false; 1234132943Sgshapiro} 1235285229Sgshapiro 1236132943Sgshapiro/* 123738032Speter** DELIVER -- Deliver a message to a list of addresses. 123838032Speter** 123938032Speter** This routine delivers to everyone on the same host as the 124038032Speter** user on the head of the list. It is clever about mailers 124138032Speter** that don't handle multiple users. It is NOT guaranteed 124238032Speter** that it will deliver to all these addresses however -- so 1243363466Sgshapiro** deliver should be called once for each address on the list. 124490792Sgshapiro** Deliver tries to be as opportunistic as possible about piggybacking 124590792Sgshapiro** messages. Some definitions to make understanding easier follow below. 124690792Sgshapiro** Piggybacking occurs when an existing connection to a mail host can 124790792Sgshapiro** be used to send the same message to more than one recipient at the 124890792Sgshapiro** same time. So "no piggybacking" means one message for one recipient 124990792Sgshapiro** per connection. "Intentional piggybacking" happens when the 125090792Sgshapiro** recipients' host address (not the mail host address) is used to 125190792Sgshapiro** attempt piggybacking. Recipients with the same host address 125290792Sgshapiro** have the same mail host. "Coincidental piggybacking" relies on 125390792Sgshapiro** piggybacking based on all the mail host addresses in the MX-RR. This 125490792Sgshapiro** is "coincidental" in the fact it could not be predicted until the 125590792Sgshapiro** MX Resource Records for the hosts were obtained and examined. For 125690792Sgshapiro** example (preference order and equivalence is important, not values): 125790792Sgshapiro** domain1 IN MX 10 mxhost-A 125890792Sgshapiro** IN MX 20 mxhost-B 125990792Sgshapiro** domain2 IN MX 4 mxhost-A 126090792Sgshapiro** IN MX 8 mxhost-B 126190792Sgshapiro** Domain1 and domain2 can piggyback the same message to mxhost-A or 126290792Sgshapiro** mxhost-B (if mxhost-A cannot be reached). 126390792Sgshapiro** "Coattail piggybacking" relaxes the strictness of "coincidental 126490792Sgshapiro** piggybacking" in the hope that most significant (lowest value) 126590792Sgshapiro** MX preference host(s) can create more piggybacking. For example 126690792Sgshapiro** (again, preference order and equivalence is important, not values): 126790792Sgshapiro** domain3 IN MX 100 mxhost-C 126890792Sgshapiro** IN MX 100 mxhost-D 126990792Sgshapiro** IN MX 200 mxhost-E 127090792Sgshapiro** domain4 IN MX 50 mxhost-C 127190792Sgshapiro** IN MX 50 mxhost-D 127290792Sgshapiro** IN MX 80 mxhost-F 127390792Sgshapiro** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 127490792Sgshapiro** is available. Same with mxhost-D because in both RR's the preference 127590792Sgshapiro** value is the same as mxhost-C, respectively. 127690792Sgshapiro** So deliver attempts coattail piggybacking when possible. If the 127790792Sgshapiro** first MX preference level hosts cannot be used then the piggybacking 127890792Sgshapiro** reverts to coincidental piggybacking. Using the above example you 127990792Sgshapiro** cannot deliver to mxhost-F for domain3 regardless of preference value. 128090792Sgshapiro** ("Coattail" from "riding on the coattails of your predecessor" meaning 128190792Sgshapiro** gaining benefit from a predecessor effort with no or little addition 128290792Sgshapiro** effort. The predecessor here being the preceding MX RR). 128338032Speter** 128438032Speter** Parameters: 128538032Speter** e -- the envelope to deliver. 128638032Speter** firstto -- head of the address list to deliver to. 128738032Speter** 128838032Speter** Returns: 128938032Speter** zero -- successfully delivered. 129038032Speter** else -- some failure, see ExitStat for more info. 129138032Speter** 129238032Speter** Side Effects: 129338032Speter** The standard input is passed off to someone. 129438032Speter*/ 129538032Speter 129664562Sgshapirostatic int 129738032Speterdeliver(e, firstto) 129838032Speter register ENVELOPE *e; 129938032Speter ADDRESS *firstto; 130038032Speter{ 130138032Speter char *host; /* host being sent to */ 130238032Speter char *user; /* user being sent to */ 130338032Speter char **pvp; 130438032Speter register char **mvp; 130538032Speter register char *p; 130638032Speter register MAILER *m; /* mailer for this recipient */ 130738032Speter ADDRESS *volatile ctladdr; 130890792Sgshapiro#if HASSETUSERCONTEXT 130938032Speter ADDRESS *volatile contextaddr = NULL; 1310363466Sgshapiro#endif 131138032Speter register MCI *volatile mci; 131290792Sgshapiro register ADDRESS *SM_NONVOLATILE to = firstto; 131390792Sgshapiro volatile bool clever = false; /* running user smtp to this mailer */ 131438032Speter ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 131538032Speter int rcode; /* response code */ 131690792Sgshapiro SM_NONVOLATILE int lmtp_rcode = EX_OK; 131790792Sgshapiro SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 131890792Sgshapiro SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 131938032Speter char *firstsig; /* signature of firstto */ 132090792Sgshapiro volatile pid_t pid = -1; 132138032Speter char *volatile curhost; 132290792Sgshapiro SM_NONVOLATILE unsigned short port = 0; 132390792Sgshapiro SM_NONVOLATILE time_t enough = 0; 132464562Sgshapiro#if NETUNIX 132590792Sgshapiro char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 1326363466Sgshapiro#endif 132738032Speter time_t xstart; 132838032Speter bool suidwarn; 132938032Speter bool anyok; /* at least one address was OK */ 133090792Sgshapiro SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 133164562Sgshapiro bool ovr; 133290792Sgshapiro bool quarantine; 1333363466Sgshapiro#if STARTTLS 1334363466Sgshapiro /* 0: try TLS, 1: try without TLS again, >1: don't try again */ 1335363466Sgshapiro int tlsstate; 1336363466Sgshapiro# if DANE 1337363466Sgshapiro dane_vrfy_ctx_T dane_vrfy_ctx; 1338363466Sgshapiro STAB *ste; 1339363466Sgshapiro# endif 1340363466Sgshapiro#endif 1341363466Sgshapiro#if STARTTLS || SASL 1342363466Sgshapiro int dotpos; 1343363466Sgshapiro 1344363466Sgshapiro# define RM_TRAIL_DOT(name) \ 1345363466Sgshapiro do { \ 1346363466Sgshapiro dotpos = strlen(name) - 1; \ 1347363466Sgshapiro if (dotpos >= 0) \ 1348363466Sgshapiro { \ 1349363466Sgshapiro if (name[dotpos] == '.') \ 1350363466Sgshapiro name[dotpos] = '\0'; \ 1351363466Sgshapiro else \ 1352363466Sgshapiro dotpos = -1; \ 1353363466Sgshapiro } \ 1354363466Sgshapiro } while (0) 1355363466Sgshapiro 1356363466Sgshapiro# define FIX_TRAIL_DOT(name) \ 1357363466Sgshapiro do { \ 1358363466Sgshapiro if (dotpos >= 0) \ 1359363466Sgshapiro name[dotpos] = '.'; \ 1360363466Sgshapiro } while (0) 1361363466Sgshapiro 1362363466Sgshapiro#endif 136364562Sgshapiro int strsize; 136464562Sgshapiro int rcptcount; 136590792Sgshapiro int ret; 136664562Sgshapiro static int tobufsize = 0; 136764562Sgshapiro static char *tobuf = NULL; 136890792Sgshapiro char *rpath; /* translated return path */ 136938032Speter int mpvect[2]; 137038032Speter int rpvect[2]; 137164562Sgshapiro char *mxhosts[MAXMXHOSTS + 1]; 137264562Sgshapiro char *pv[MAXPV + 1]; 137338032Speter char buf[MAXNAME + 1]; 137498121Sgshapiro char cbuf[MAXPATHLEN]; 137538032Speter 137638032Speter errno = 0; 1377168515Sgshapiro SM_REQUIRE(firstto != NULL); /* same as to */ 137864562Sgshapiro if (!QS_IS_OK(to->q_state)) 137964562Sgshapiro return 0; 138038032Speter 138138032Speter suidwarn = geteuid() == 0; 138238032Speter 1383168515Sgshapiro SM_REQUIRE(e != NULL); 138438032Speter m = to->q_mailer; 138538032Speter host = to->q_host; 138638032Speter CurEnv = e; /* just in case */ 138738032Speter e->e_statmsg = NULL; 138838032Speter SmtpError[0] = '\0'; 138938032Speter xstart = curtime(); 1390363466Sgshapiro#if STARTTLS 1391363466Sgshapiro tlsstate = 0; 1392363466Sgshapiro# if DANE 1393363466Sgshapiro memset(&dane_vrfy_ctx, '\0', sizeof(dane_vrfy_ctx)); 1394363466Sgshapiro ste = NULL; 1395363466Sgshapiro# endif 1396363466Sgshapiro#endif 139738032Speter 139838032Speter if (tTd(10, 1)) 139990792Sgshapiro sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 140038032Speter e->e_id, m->m_name, host, to->q_user); 140138032Speter if (tTd(10, 100)) 140290792Sgshapiro printopenfds(false); 140338032Speter 140438032Speter /* 140590792Sgshapiro ** Clear {client_*} macros if this is a bounce message to 140638032Speter ** prevent rejection by check_compat ruleset. 140738032Speter */ 140864562Sgshapiro 140938032Speter if (bitset(EF_RESPONSE, e->e_flags)) 141038032Speter { 141190792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 1412132943Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 141390792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 141490792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 141590792Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 141638032Speter } 141764562Sgshapiro 141890792Sgshapiro SM_TRY 141990792Sgshapiro { 142090792Sgshapiro ADDRESS *skip_back = NULL; 142190792Sgshapiro 142238032Speter /* 142338032Speter ** Do initial argv setup. 142438032Speter ** Insert the mailer name. Notice that $x expansion is 142538032Speter ** NOT done on the mailer name. Then, if the mailer has 142638032Speter ** a picky -f flag, we insert it as appropriate. This 142738032Speter ** code does not check for 'pv' overflow; this places a 142838032Speter ** manifest lower limit of 4 for MAXPV. 142938032Speter ** The from address rewrite is expected to make 143038032Speter ** the address relative to the other end. 143138032Speter */ 143238032Speter 143338032Speter /* rewrite from address, using rewriting rules */ 143438032Speter rcode = EX_OK; 1435168515Sgshapiro SM_ASSERT(e->e_from.q_mailer != NULL); 143638032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 143738032Speter p = e->e_sender; 143838032Speter else 143938032Speter p = e->e_from.q_paddr; 144090792Sgshapiro rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 1441285229Sgshapiro if (rcode != EX_OK && bitnset(M_xSMTP, m->m_flags)) 1442285229Sgshapiro goto cleanup; 1443203004Sgshapiro if (strlen(rpath) > MAXNAME) 144438032Speter { 144590792Sgshapiro rpath = shortenstring(rpath, MAXSHORTSTR); 144690792Sgshapiro 144790792Sgshapiro /* avoid bogus errno */ 144890792Sgshapiro errno = 0; 144990792Sgshapiro syserr("remotename: huge return path %s", rpath); 145038032Speter } 145190792Sgshapiro rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 145290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', rpath); 145390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', host); 145438032Speter Errors = 0; 145538032Speter pvp = pv; 145638032Speter *pvp++ = m->m_argv[0]; 145738032Speter 1458132943Sgshapiro /* ignore long term host status information if mailer flag W is set */ 1459132943Sgshapiro if (bitnset(M_NOHOSTSTAT, m->m_flags)) 1460132943Sgshapiro IgnoreHostStatus = true; 1461132943Sgshapiro 146238032Speter /* insert -f or -r flag as appropriate */ 146364562Sgshapiro if (FromFlag && 146464562Sgshapiro (bitnset(M_FOPT, m->m_flags) || 146564562Sgshapiro bitnset(M_ROPT, m->m_flags))) 146638032Speter { 146738032Speter if (bitnset(M_FOPT, m->m_flags)) 146838032Speter *pvp++ = "-f"; 146938032Speter else 147038032Speter *pvp++ = "-r"; 147190792Sgshapiro *pvp++ = rpath; 147238032Speter } 147338032Speter 147438032Speter /* 147538032Speter ** Append the other fixed parts of the argv. These run 147638032Speter ** up to the first entry containing "$u". There can only 147738032Speter ** be one of these, and there are only a few more slots 147838032Speter ** in the pv after it. 147938032Speter */ 148038032Speter 148138032Speter for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 148238032Speter { 148338032Speter /* can't use strchr here because of sign extension problems */ 148438032Speter while (*p != '\0') 148538032Speter { 148638032Speter if ((*p++ & 0377) == MACROEXPAND) 148738032Speter { 148838032Speter if (*p == 'u') 148938032Speter break; 149038032Speter } 149138032Speter } 149238032Speter 149338032Speter if (*p != '\0') 149438032Speter break; 149538032Speter 149638032Speter /* this entry is safe -- go ahead and process it */ 1497168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 149890792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 149938032Speter if (pvp >= &pv[MAXPV - 3]) 150038032Speter { 150164562Sgshapiro syserr("554 5.3.5 Too many parameters to %s before $u", 150264562Sgshapiro pv[0]); 150390792Sgshapiro rcode = -1; 150490792Sgshapiro goto cleanup; 150538032Speter } 150638032Speter } 150738032Speter 150838032Speter /* 150938032Speter ** If we have no substitution for the user name in the argument 151038032Speter ** list, we know that we must supply the names otherwise -- and 151138032Speter ** SMTP is the answer!! 151238032Speter */ 151338032Speter 151438032Speter if (*mvp == NULL) 151538032Speter { 151673188Sgshapiro /* running LMTP or SMTP */ 151790792Sgshapiro clever = true; 151838032Speter *pvp = NULL; 1519285229Sgshapiro setbitn(M_xSMTP, m->m_flags); 152038032Speter } 152173188Sgshapiro else if (bitnset(M_LMTP, m->m_flags)) 152273188Sgshapiro { 152373188Sgshapiro /* not running LMTP */ 152473188Sgshapiro sm_syslog(LOG_ERR, NULL, 152573188Sgshapiro "Warning: mailer %s: LMTP flag (F=z) turned off", 152673188Sgshapiro m->m_name); 152773188Sgshapiro clrbitn(M_LMTP, m->m_flags); 152873188Sgshapiro } 152938032Speter 153038032Speter /* 153138032Speter ** At this point *mvp points to the argument with $u. We 153238032Speter ** run through our address list and append all the addresses 153338032Speter ** we can. If we run out of space, do not fret! We can 153438032Speter ** always send another copy later. 153538032Speter */ 153638032Speter 153764562Sgshapiro e->e_to = NULL; 153864562Sgshapiro strsize = 2; 153964562Sgshapiro rcptcount = 0; 154090792Sgshapiro ctladdr = NULL; 154190792Sgshapiro if (firstto->q_signature == NULL) 154290792Sgshapiro firstto->q_signature = hostsignature(firstto->q_mailer, 1543363466Sgshapiro firstto->q_host, 1544363466Sgshapiro firstto->q_flags & QSECURE); 154590792Sgshapiro firstsig = firstto->q_signature; 154664562Sgshapiro 154738032Speter for (; to != NULL; to = to->q_next) 154838032Speter { 154938032Speter /* avoid sending multiple recipients to dumb mailers */ 155064562Sgshapiro if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 155164562Sgshapiro break; 155238032Speter 155338032Speter /* if already sent or not for this host, don't send */ 155490792Sgshapiro if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 155538032Speter continue; 155638032Speter 155790792Sgshapiro /* 155890792Sgshapiro ** Must be same mailer to keep grouping rcpts. 155990792Sgshapiro ** If mailers don't match: continue; sendqueue is not 156090792Sgshapiro ** sorted by mailers, so don't break; 156190792Sgshapiro */ 156290792Sgshapiro 156390792Sgshapiro if (to->q_mailer != firstto->q_mailer) 156490792Sgshapiro continue; 156590792Sgshapiro 156690792Sgshapiro if (to->q_signature == NULL) /* for safety */ 156790792Sgshapiro to->q_signature = hostsignature(to->q_mailer, 1568363466Sgshapiro to->q_host, 1569363466Sgshapiro to->q_flags & QSECURE); 157090792Sgshapiro 157190792Sgshapiro /* 157290792Sgshapiro ** This is for coincidental and tailcoat piggybacking messages 157390792Sgshapiro ** to the same mail host. While the signatures are identical 157490792Sgshapiro ** (that's the MX-RR's are identical) we can do coincidental 157590792Sgshapiro ** piggybacking. We try hard for coattail piggybacking 157690792Sgshapiro ** with the same mail host when the next recipient has the 157790792Sgshapiro ** same host at lowest preference. It may be that this 157890792Sgshapiro ** won't work out, so 'skip_back' is maintained if a backup 157990792Sgshapiro ** to coincidental piggybacking or full signature must happen. 158090792Sgshapiro */ 158190792Sgshapiro 158290792Sgshapiro ret = firstto == to ? HS_MATCH_FULL : 158390792Sgshapiro coloncmp(to->q_signature, firstsig); 158490792Sgshapiro if (ret == HS_MATCH_FULL) 158590792Sgshapiro skip_back = to; 158690792Sgshapiro else if (ret == HS_MATCH_NO) 158764562Sgshapiro break; 158864562Sgshapiro 158990792Sgshapiro if (!clever) 159090792Sgshapiro { 159190792Sgshapiro /* avoid overflowing tobuf */ 159290792Sgshapiro strsize += strlen(to->q_paddr) + 1; 159390792Sgshapiro if (strsize > TOBUFSIZE) 159490792Sgshapiro break; 159590792Sgshapiro } 159690792Sgshapiro 159764562Sgshapiro if (++rcptcount > to->q_mailer->m_maxrcpt) 159864562Sgshapiro break; 159938032Speter 160038032Speter if (tTd(10, 1)) 160138032Speter { 160290792Sgshapiro sm_dprintf("\nsend to "); 1603132943Sgshapiro printaddr(sm_debug_file(), to, false); 160438032Speter } 160538032Speter 160638032Speter /* compute effective uid/gid when sending */ 160738032Speter if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 1608363466Sgshapiro#if HASSETUSERCONTEXT 160938032Speter contextaddr = ctladdr = getctladdr(to); 1610363466Sgshapiro#else 161190792Sgshapiro ctladdr = getctladdr(to); 1612363466Sgshapiro#endif 161338032Speter 161438032Speter if (tTd(10, 2)) 161538032Speter { 161690792Sgshapiro sm_dprintf("ctladdr="); 1617132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 161838032Speter } 161938032Speter 162038032Speter user = to->q_user; 162138032Speter e->e_to = to->q_paddr; 162238032Speter 162338032Speter /* 162438032Speter ** Check to see that these people are allowed to 162538032Speter ** talk to each other. 162666494Sgshapiro ** Check also for overflow of e_msgsize. 162738032Speter */ 162838032Speter 162966494Sgshapiro if (m->m_maxsize != 0 && 163066494Sgshapiro (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 163138032Speter { 163238032Speter e->e_flags |= EF_NO_BODY_RETN; 163338032Speter if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 163438032Speter to->q_status = "5.2.3"; 163538032Speter else 163638032Speter to->q_status = "5.3.4"; 163790792Sgshapiro 163864562Sgshapiro /* set to->q_rstatus = NULL; or to the following? */ 163964562Sgshapiro usrerrenh(to->q_status, 164064562Sgshapiro "552 Message is too large; %ld bytes max", 164164562Sgshapiro m->m_maxsize); 164290792Sgshapiro markfailure(e, to, NULL, EX_UNAVAILABLE, false); 164364562Sgshapiro giveresponse(EX_UNAVAILABLE, to->q_status, m, 164490792Sgshapiro NULL, ctladdr, xstart, e, to); 164538032Speter continue; 164638032Speter } 164773188Sgshapiro SM_SET_H_ERRNO(0); 164890792Sgshapiro ovr = true; 164938032Speter 165038032Speter /* do config file checking of compatibility */ 165190792Sgshapiro quarantine = (e->e_quarmsg != NULL); 165264562Sgshapiro rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1653102528Sgshapiro e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1654285229Sgshapiro e->e_id, NULL, NULL); 165538032Speter if (rcode == EX_OK) 165638032Speter { 165742575Speter /* do in-code checking if not discarding */ 165842575Speter if (!bitset(EF_DISCARD, e->e_flags)) 165964562Sgshapiro { 166042575Speter rcode = checkcompat(to, e); 166190792Sgshapiro ovr = false; 166264562Sgshapiro } 166338032Speter } 166438032Speter if (rcode != EX_OK) 166538032Speter { 166664562Sgshapiro markfailure(e, to, NULL, rcode, ovr); 166764562Sgshapiro giveresponse(rcode, to->q_status, m, 166890792Sgshapiro NULL, ctladdr, xstart, e, to); 166938032Speter continue; 167038032Speter } 167190792Sgshapiro if (!quarantine && e->e_quarmsg != NULL) 167290792Sgshapiro { 167390792Sgshapiro /* 167490792Sgshapiro ** check_compat or checkcompat() has tried 167590792Sgshapiro ** to quarantine but that isn't supported. 167690792Sgshapiro ** Revert the attempt. 167790792Sgshapiro */ 167890792Sgshapiro 167990792Sgshapiro e->e_quarmsg = NULL; 168090792Sgshapiro macdefine(&e->e_macro, A_PERM, 168190792Sgshapiro macid("{quarantine}"), ""); 168290792Sgshapiro } 168342575Speter if (bitset(EF_DISCARD, e->e_flags)) 168442575Speter { 168542575Speter if (tTd(10, 5)) 168642575Speter { 168790792Sgshapiro sm_dprintf("deliver: discarding recipient "); 1688132943Sgshapiro printaddr(sm_debug_file(), to, false); 168942575Speter } 169038032Speter 169164562Sgshapiro /* pretend the message was sent */ 169264562Sgshapiro /* XXX should we log something here? */ 169364562Sgshapiro to->q_state = QS_DISCARDED; 169464562Sgshapiro 169542575Speter /* 169642575Speter ** Remove discard bit to prevent discard of 169764562Sgshapiro ** future recipients. This is safe because the 169864562Sgshapiro ** true "global discard" has been handled before 169964562Sgshapiro ** we get here. 170042575Speter */ 170164562Sgshapiro 170242575Speter e->e_flags &= ~EF_DISCARD; 170342575Speter continue; 170442575Speter } 170542575Speter 170638032Speter /* 170738032Speter ** Strip quote bits from names if the mailer is dumb 170838032Speter ** about them. 170938032Speter */ 171038032Speter 171138032Speter if (bitnset(M_STRIPQ, m->m_flags)) 171238032Speter { 171338032Speter stripquotes(user); 171438032Speter stripquotes(host); 171538032Speter } 1716132943Sgshapiro 1717110560Sgshapiro /* 1718141858Sgshapiro ** Strip all leading backslashes if requested and the 1719110560Sgshapiro ** next character is alphanumerical (the latter can 1720110560Sgshapiro ** probably relaxed a bit, see RFC2821). 1721110560Sgshapiro */ 172238032Speter 1723110560Sgshapiro if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1724110560Sgshapiro stripbackslash(user); 1725110560Sgshapiro 172638032Speter /* hack attack -- delivermail compatibility */ 172738032Speter if (m == ProgMailer && *user == '|') 172838032Speter user++; 172938032Speter 173038032Speter /* 173138032Speter ** If an error message has already been given, don't 173238032Speter ** bother to send to this address. 173338032Speter ** 173438032Speter ** >>>>>>>>>> This clause assumes that the local mailer 173538032Speter ** >> NOTE >> cannot do any further aliasing; that 173638032Speter ** >>>>>>>>>> function is subsumed by sendmail. 173738032Speter */ 173838032Speter 173964562Sgshapiro if (!QS_IS_OK(to->q_state)) 174038032Speter continue; 174138032Speter 174238032Speter /* 174338032Speter ** See if this user name is "special". 174438032Speter ** If the user name has a slash in it, assume that this 174538032Speter ** is a file -- send it off without further ado. Note 174638032Speter ** that this type of addresses is not processed along 174738032Speter ** with the others, so we fudge on the To person. 174838032Speter */ 174938032Speter 175038032Speter if (strcmp(m->m_mailer, "[FILE]") == 0) 175138032Speter { 175290792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); 175338032Speter p = to->q_home; 175438032Speter if (p == NULL && ctladdr != NULL) 175538032Speter p = ctladdr->q_home; 175690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); 1757168515Sgshapiro expand(m->m_argv[1], buf, sizeof(buf), e); 175838032Speter if (strlen(buf) > 0) 175938032Speter rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 176038032Speter else 176138032Speter { 176238032Speter syserr("empty filename specification for mailer %s", 176338032Speter m->m_name); 176438032Speter rcode = EX_CONFIG; 176538032Speter } 176664562Sgshapiro giveresponse(rcode, to->q_status, m, NULL, 176790792Sgshapiro ctladdr, xstart, e, to); 176890792Sgshapiro markfailure(e, to, NULL, rcode, true); 176938032Speter e->e_nsent++; 177038032Speter if (rcode == EX_OK) 177138032Speter { 177264562Sgshapiro to->q_state = QS_SENT; 177338032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 177438032Speter bitset(QPINGONSUCCESS, to->q_flags)) 177538032Speter { 177638032Speter to->q_flags |= QDELIVERED; 177738032Speter to->q_status = "2.1.5"; 177890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, 177990792Sgshapiro SM_TIME_DEFAULT, 178090792Sgshapiro "%s... Successfully delivered\n", 178190792Sgshapiro to->q_paddr); 178238032Speter } 178338032Speter } 178438032Speter to->q_statdate = curtime(); 178590792Sgshapiro markstats(e, to, STATS_NORMAL); 178638032Speter continue; 178738032Speter } 178838032Speter 178938032Speter /* 179038032Speter ** Address is verified -- add this user to mailer 179138032Speter ** argv, and add it to the print list of recipients. 179238032Speter */ 179338032Speter 179438032Speter /* link together the chain of recipients */ 179538032Speter to->q_tchain = tochain; 179638032Speter tochain = to; 179764562Sgshapiro e->e_to = "[CHAIN]"; 179864562Sgshapiro 179990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 180038032Speter p = to->q_home; 180138032Speter if (p == NULL && ctladdr != NULL) 180238032Speter p = ctladdr->q_home; 180390792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 180438032Speter 180564562Sgshapiro /* set the ${dsn_notify} macro if applicable */ 180664562Sgshapiro if (bitset(QHASNOTIFY, to->q_flags)) 180764562Sgshapiro { 180864562Sgshapiro char notify[MAXLINE]; 180964562Sgshapiro 181064562Sgshapiro notify[0] = '\0'; 181164562Sgshapiro if (bitset(QPINGONSUCCESS, to->q_flags)) 181290792Sgshapiro (void) sm_strlcat(notify, "SUCCESS,", 1813168515Sgshapiro sizeof(notify)); 181464562Sgshapiro if (bitset(QPINGONFAILURE, to->q_flags)) 181590792Sgshapiro (void) sm_strlcat(notify, "FAILURE,", 1816168515Sgshapiro sizeof(notify)); 181764562Sgshapiro if (bitset(QPINGONDELAY, to->q_flags)) 181890792Sgshapiro (void) sm_strlcat(notify, "DELAY,", 1819168515Sgshapiro sizeof(notify)); 182064562Sgshapiro 182164562Sgshapiro /* Set to NEVER or drop trailing comma */ 182264562Sgshapiro if (notify[0] == '\0') 182390792Sgshapiro (void) sm_strlcat(notify, "NEVER", 1824168515Sgshapiro sizeof(notify)); 182564562Sgshapiro else 182664562Sgshapiro notify[strlen(notify) - 1] = '\0'; 182764562Sgshapiro 182890792Sgshapiro macdefine(&e->e_macro, A_TEMP, 182990792Sgshapiro macid("{dsn_notify}"), notify); 183064562Sgshapiro } 183164562Sgshapiro else 183290792Sgshapiro macdefine(&e->e_macro, A_PERM, 183390792Sgshapiro macid("{dsn_notify}"), NULL); 183464562Sgshapiro 183538032Speter /* 183638032Speter ** Expand out this user into argument list. 183738032Speter */ 183838032Speter 183938032Speter if (!clever) 184038032Speter { 1841168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 184290792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 184338032Speter if (pvp >= &pv[MAXPV - 2]) 184438032Speter { 184538032Speter /* allow some space for trailing parms */ 184638032Speter break; 184738032Speter } 184838032Speter } 184938032Speter } 185038032Speter 185138032Speter /* see if any addresses still exist */ 185264562Sgshapiro if (tochain == NULL) 185338032Speter { 185490792Sgshapiro rcode = 0; 185590792Sgshapiro goto cleanup; 185638032Speter } 185738032Speter 185838032Speter /* print out messages as full list */ 185990792Sgshapiro strsize = 1; 186090792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 186190792Sgshapiro strsize += strlen(to->q_paddr) + 1; 186290792Sgshapiro if (strsize < TOBUFSIZE) 186390792Sgshapiro strsize = TOBUFSIZE; 186490792Sgshapiro if (strsize > tobufsize) 186564562Sgshapiro { 1866363466Sgshapiro SM_FREE(tobuf); 186790792Sgshapiro tobuf = sm_pmalloc_x(strsize); 186890792Sgshapiro tobufsize = strsize; 186964562Sgshapiro } 187090792Sgshapiro p = tobuf; 187190792Sgshapiro *p = '\0'; 187290792Sgshapiro for (to = tochain; to != NULL; to = to->q_tchain) 187390792Sgshapiro { 187490792Sgshapiro (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 187590792Sgshapiro ",", to->q_paddr); 187690792Sgshapiro p += strlen(p); 187790792Sgshapiro } 187838032Speter e->e_to = tobuf + 1; 187938032Speter 188038032Speter /* 188138032Speter ** Fill out any parameters after the $u parameter. 188238032Speter */ 188338032Speter 188490792Sgshapiro if (!clever) 188538032Speter { 188690792Sgshapiro while (*++mvp != NULL) 188790792Sgshapiro { 1888168515Sgshapiro expand(*mvp, buf, sizeof(buf), e); 188990792Sgshapiro *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 189090792Sgshapiro if (pvp >= &pv[MAXPV]) 189190792Sgshapiro syserr("554 5.3.0 deliver: pv overflow after $u for %s", 189290792Sgshapiro pv[0]); 189390792Sgshapiro } 189438032Speter } 189538032Speter *pvp++ = NULL; 189638032Speter 189738032Speter /* 189838032Speter ** Call the mailer. 189938032Speter ** The argument vector gets built, pipes 190038032Speter ** are created as necessary, and we fork & exec as 190138032Speter ** appropriate. 190238032Speter ** If we are running SMTP, we just need to clean up. 190338032Speter */ 190438032Speter 1905223067Sgshapiro /* XXX this seems a bit weird */ 190638032Speter if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 190738032Speter bitset(QGOODUID, e->e_from.q_flags)) 190838032Speter ctladdr = &e->e_from; 190938032Speter 191038032Speter#if NAMED_BIND 191138032Speter if (ConfigLevel < 2) 191238032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 1913363466Sgshapiro#endif 191438032Speter 191538032Speter if (tTd(11, 1)) 191638032Speter { 191790792Sgshapiro sm_dprintf("openmailer:"); 1918132943Sgshapiro printav(sm_debug_file(), pv); 191938032Speter } 192038032Speter errno = 0; 192173188Sgshapiro SM_SET_H_ERRNO(0); 192238032Speter CurHostName = NULL; 192338032Speter 192438032Speter /* 192538032Speter ** Deal with the special case of mail handled through an IPC 192638032Speter ** connection. 192738032Speter ** In this case we don't actually fork. We must be 192838032Speter ** running SMTP for this to work. We will return a 192938032Speter ** zero pid to indicate that we are running IPC. 193038032Speter ** We also handle a debug version that just talks to stdin/out. 193138032Speter */ 193238032Speter 193338032Speter curhost = NULL; 193438032Speter SmtpPhase = NULL; 193538032Speter mci = NULL; 193638032Speter 193738032Speter#if XDEBUG 193838032Speter { 193938032Speter char wbuf[MAXLINE]; 194038032Speter 194138032Speter /* make absolutely certain 0, 1, and 2 are in use */ 1942168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)", 194390792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 194490792Sgshapiro m->m_name); 194538032Speter checkfd012(wbuf); 194638032Speter } 194764562Sgshapiro#endif /* XDEBUG */ 194838032Speter 194938032Speter /* check for 8-bit available */ 195038032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 195138032Speter bitnset(M_7BITS, m->m_flags) && 195238032Speter (bitset(EF_DONT_MIME, e->e_flags) || 195338032Speter !(bitset(MM_MIME8BIT, MimeMode) || 195438032Speter (bitset(EF_IS_MIME, e->e_flags) && 195564562Sgshapiro bitset(MM_CVTMIME, MimeMode))))) 195638032Speter { 195764562Sgshapiro e->e_status = "5.6.3"; 195864562Sgshapiro usrerrenh(e->e_status, 195990792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 196038032Speter rcode = EX_DATAERR; 196138032Speter goto give_up; 196238032Speter } 196338032Speter 196438032Speter if (tTd(62, 8)) 196538032Speter checkfds("before delivery"); 196638032Speter 196738032Speter /* check for Local Person Communication -- not for mortals!!! */ 196838032Speter if (strcmp(m->m_mailer, "[LPC]") == 0) 196938032Speter { 197090792Sgshapiro if (clever) 197190792Sgshapiro { 197290792Sgshapiro /* flush any expired connections */ 197390792Sgshapiro (void) mci_scan(NULL); 197490792Sgshapiro 197590792Sgshapiro /* try to get a cached connection or just a slot */ 197690792Sgshapiro mci = mci_get(m->m_name, m); 197790792Sgshapiro if (mci->mci_host == NULL) 197890792Sgshapiro mci->mci_host = m->m_name; 197990792Sgshapiro CurHostName = mci->mci_host; 198090792Sgshapiro if (mci->mci_state != MCIS_CLOSED) 198190792Sgshapiro { 198290792Sgshapiro message("Using cached SMTP/LPC connection for %s...", 198390792Sgshapiro m->m_name); 198490792Sgshapiro mci->mci_deliveries++; 198590792Sgshapiro goto do_transfer; 198690792Sgshapiro } 198790792Sgshapiro } 198890792Sgshapiro else 198990792Sgshapiro { 199090792Sgshapiro mci = mci_new(e->e_rpool); 199190792Sgshapiro } 199290792Sgshapiro mci->mci_in = smioin; 199390792Sgshapiro mci->mci_out = smioout; 199490792Sgshapiro mci->mci_mailer = m; 199590792Sgshapiro mci->mci_host = m->m_name; 199690792Sgshapiro if (clever) 199790792Sgshapiro { 199890792Sgshapiro mci->mci_state = MCIS_OPENING; 199990792Sgshapiro mci_cache(mci); 200090792Sgshapiro } 200190792Sgshapiro else 200290792Sgshapiro mci->mci_state = MCIS_OPEN; 200338032Speter } 200490792Sgshapiro else if (strcmp(m->m_mailer, "[IPC]") == 0) 200538032Speter { 200638032Speter register int i; 200738032Speter 200838032Speter if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 200938032Speter { 201064562Sgshapiro syserr("null destination for %s mailer", m->m_mailer); 201138032Speter rcode = EX_CONFIG; 201238032Speter goto give_up; 201338032Speter } 201438032Speter 2015363466Sgshapiro#if NETUNIX 201664562Sgshapiro if (strcmp(pv[0], "FILE") == 0) 201764562Sgshapiro { 201864562Sgshapiro curhost = CurHostName = "localhost"; 201964562Sgshapiro mux_path = pv[1]; 202064562Sgshapiro } 202164562Sgshapiro else 2022363466Sgshapiro#endif /* NETUNIX */ 202364562Sgshapiro { 202464562Sgshapiro CurHostName = pv[1]; 2025363466Sgshapiro /* XXX ??? */ 2026363466Sgshapiro curhost = hostsignature(m, pv[1], firstto->q_flags & QSECURE); 202764562Sgshapiro } 202838032Speter 202938032Speter if (curhost == NULL || curhost[0] == '\0') 203038032Speter { 203138032Speter syserr("null host signature for %s", pv[1]); 203238032Speter rcode = EX_CONFIG; 203338032Speter goto give_up; 203438032Speter } 203538032Speter 203638032Speter if (!clever) 203738032Speter { 203864562Sgshapiro syserr("554 5.3.5 non-clever IPC"); 203938032Speter rcode = EX_CONFIG; 204038032Speter goto give_up; 204138032Speter } 204264562Sgshapiro if (pv[2] != NULL 2043363466Sgshapiro#if NETUNIX 204464562Sgshapiro && mux_path == NULL 2045363466Sgshapiro#endif 204664562Sgshapiro ) 204738032Speter { 204890792Sgshapiro port = htons((unsigned short) atoi(pv[2])); 204938032Speter if (port == 0) 205038032Speter { 2051363466Sgshapiro#ifdef NO_GETSERVBYNAME 205264562Sgshapiro syserr("Invalid port number: %s", pv[2]); 2053363466Sgshapiro#else /* NO_GETSERVBYNAME */ 205438032Speter struct servent *sp = getservbyname(pv[2], "tcp"); 205538032Speter 205638032Speter if (sp == NULL) 205738032Speter syserr("Service %s unknown", pv[2]); 205838032Speter else 205938032Speter port = sp->s_port; 2060363466Sgshapiro#endif /* NO_GETSERVBYNAME */ 206138032Speter } 206238032Speter } 206364562Sgshapiro 206464562Sgshapiro nummxhosts = parse_hostsignature(curhost, mxhosts, m); 206590792Sgshapiro if (TimeOuts.to_aconnect > 0) 206690792Sgshapiro enough = curtime() + TimeOuts.to_aconnect; 206738032Spetertryhost: 206864562Sgshapiro while (hostnum < nummxhosts) 206938032Speter { 207064562Sgshapiro char sep = ':'; 207164562Sgshapiro char *endp; 207238032Speter static char hostbuf[MAXNAME + 1]; 2073132943Sgshapiro bool tried_fallbacksmarthost = false; 2074363466Sgshapiro#if DANE 2075363466Sgshapiro unsigned long tlsa_flags; 207638032Speter 2077363466Sgshapiro ste = NULL; 2078363466Sgshapiro tlsa_flags = 0; 2079363466Sgshapiro#endif 2080363466Sgshapiro#if NETINET6 208164562Sgshapiro if (*mxhosts[hostnum] == '[') 208238032Speter { 208364562Sgshapiro endp = strchr(mxhosts[hostnum] + 1, ']'); 208464562Sgshapiro if (endp != NULL) 208564562Sgshapiro endp = strpbrk(endp + 1, ":,"); 208664562Sgshapiro } 208764562Sgshapiro else 208864562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 2089363466Sgshapiro#else /* NETINET6 */ 209064562Sgshapiro endp = strpbrk(mxhosts[hostnum], ":,"); 2091363466Sgshapiro#endif /* NETINET6 */ 209264562Sgshapiro if (endp != NULL) 209364562Sgshapiro { 209464562Sgshapiro sep = *endp; 209564562Sgshapiro *endp = '\0'; 209664562Sgshapiro } 209764562Sgshapiro 209890792Sgshapiro if (hostnum == 1 && skip_back != NULL) 209990792Sgshapiro { 210090792Sgshapiro /* 210190792Sgshapiro ** Coattail piggybacking is no longer an 210290792Sgshapiro ** option with the mail host next to be tried 210390792Sgshapiro ** no longer the lowest MX preference 210490792Sgshapiro ** (hostnum == 1 meaning we're on the second 210590792Sgshapiro ** preference). We do not try to coattail 210690792Sgshapiro ** piggyback more than the first MX preference. 210790792Sgshapiro ** Revert 'tochain' to last location for 210890792Sgshapiro ** coincidental piggybacking. This works this 210990792Sgshapiro ** easily because the q_tchain kept getting 211090792Sgshapiro ** added to the top of the linked list. 211190792Sgshapiro */ 211290792Sgshapiro 211390792Sgshapiro tochain = skip_back; 211490792Sgshapiro } 211590792Sgshapiro 211664562Sgshapiro if (*mxhosts[hostnum] == '\0') 211764562Sgshapiro { 211838032Speter syserr("deliver: null host name in signature"); 211964562Sgshapiro hostnum++; 212064562Sgshapiro if (endp != NULL) 212164562Sgshapiro *endp = sep; 212238032Speter continue; 212338032Speter } 212490792Sgshapiro (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 2125168515Sgshapiro sizeof(hostbuf)); 212664562Sgshapiro hostnum++; 212764562Sgshapiro if (endp != NULL) 212864562Sgshapiro *endp = sep; 2129363466Sgshapiro#if STARTTLS 2130363466Sgshapiro tlsstate = 0; 2131363466Sgshapiro#endif 213238032Speter 2133132943Sgshapiro one_last_try: 213438032Speter /* see if we already know that this host is fried */ 213538032Speter CurHostName = hostbuf; 213638032Speter mci = mci_get(hostbuf, m); 213738032Speter if (mci->mci_state != MCIS_CLOSED) 213838032Speter { 213990792Sgshapiro char *type; 214090792Sgshapiro 214138032Speter if (tTd(11, 1)) 214238032Speter { 214390792Sgshapiro sm_dprintf("openmailer: "); 2144132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 214538032Speter } 214638032Speter CurHostName = mci->mci_host; 214790792Sgshapiro if (bitnset(M_LMTP, m->m_flags)) 214890792Sgshapiro type = "L"; 214990792Sgshapiro else if (bitset(MCIF_ESMTP, mci->mci_flags)) 215090792Sgshapiro type = "ES"; 215190792Sgshapiro else 215290792Sgshapiro type = "S"; 215390792Sgshapiro message("Using cached %sMTP connection to %s via %s...", 215490792Sgshapiro type, hostbuf, m->m_name); 215564562Sgshapiro mci->mci_deliveries++; 215638032Speter break; 215738032Speter } 215838032Speter mci->mci_mailer = m; 2159363466Sgshapiro#if DANE 2160363466Sgshapiro tlsa_flags = 0; 2161363466Sgshapiro if (CHK_DANE(Dane)) 2162363466Sgshapiro (void) GETTLSA(hostbuf, &ste, m->m_port); 2163363466Sgshapiro 2164363466Sgshapiro /* XXX: check expiration! */ 2165363466Sgshapiro if (ste != NULL && TLSA_RR_TEMPFAIL(ste->s_tlsa)) 2166363466Sgshapiro { 2167363466Sgshapiro if (tTd(11, 1)) 2168363466Sgshapiro sm_dprintf("skip: host=%s, TLSA_RR_lookup=%d\n" 2169363466Sgshapiro , hostbuf 2170363466Sgshapiro , ste->s_tlsa->dane_tlsa_dnsrc); 2171363466Sgshapiro 2172363466Sgshapiro tlsa_flags |= TLSAFLTEMP; 2173363466Sgshapiro } 2174363466Sgshapiro#endif /* DANE */ 2175363466Sgshapiro 217638032Speter if (mci->mci_exitstat != EX_OK) 217738032Speter { 217838032Speter if (mci->mci_exitstat == EX_TEMPFAIL) 217990792Sgshapiro goodmxfound = true; 2180132943Sgshapiro 2181132943Sgshapiro /* Try FallbackSmartHost? */ 2182132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2183168515Sgshapiro hostbuf, sizeof(hostbuf), 2184132943Sgshapiro mci->mci_exitstat)) 2185132943Sgshapiro goto one_last_try; 2186132943Sgshapiro 218738032Speter continue; 218838032Speter } 218938032Speter 219038032Speter if (mci_lock_host(mci) != EX_OK) 219138032Speter { 219238032Speter mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 219390792Sgshapiro goodmxfound = true; 219438032Speter continue; 219538032Speter } 219638032Speter 219738032Speter /* try the connection */ 219890792Sgshapiro sm_setproctitle(true, e, "%s %s: %s", 219964562Sgshapiro qid_printname(e), 220064562Sgshapiro hostbuf, "user open"); 2201363466Sgshapiro#if NETUNIX 220264562Sgshapiro if (mux_path != NULL) 220364562Sgshapiro { 220438032Speter message("Connecting to %s via %s...", 220564562Sgshapiro mux_path, m->m_name); 220690792Sgshapiro i = makeconnection_ds((char *) mux_path, mci); 220764562Sgshapiro } 220838032Speter else 2209363466Sgshapiro#endif /* NETUNIX */ 221064562Sgshapiro { 221164562Sgshapiro if (port == 0) 221264562Sgshapiro message("Connecting to %s via %s...", 221364562Sgshapiro hostbuf, m->m_name); 221464562Sgshapiro else 221564562Sgshapiro message("Connecting to %s port %d via %s...", 221664562Sgshapiro hostbuf, ntohs(port), 221764562Sgshapiro m->m_name); 2218363466Sgshapiro#if DANE 2219363466Sgshapiro tlsa_flags |= (ste != NULL) ? Dane : DANE_NEVER; 2220363466Sgshapiro dane_vrfy_ctx.dane_vrfy_chk = tlsa_flags; 2221363466Sgshapiro dane_vrfy_ctx.dane_vrfy_port = m->m_port; 2222363466Sgshapiro if (tTd(11, 11)) 2223363466Sgshapiro sm_dprintf("makeconnection: before: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags); 2224363466Sgshapiro#endif 222590792Sgshapiro i = makeconnection(hostbuf, port, mci, e, 2226363466Sgshapiro enough 2227363466Sgshapiro#if DANE 2228363466Sgshapiro , &tlsa_flags 2229363466Sgshapiro#endif 2230363466Sgshapiro ); 2231363466Sgshapiro#if DANE 2232363466Sgshapiro if (tTd(11, 11)) 2233363466Sgshapiro sm_dprintf("makeconnection: after: chk=%d, mode=%lX\n", dane_vrfy_ctx.dane_vrfy_chk, tlsa_flags); 2234363466Sgshapiro if (dane_vrfy_ctx.dane_vrfy_chk != DANE_ALWAYS) 2235363466Sgshapiro dane_vrfy_ctx.dane_vrfy_chk = DANEMODE(tlsa_flags); 2236363466Sgshapiro if (EX_TEMPFAIL == i && 2237363466Sgshapiro ((tlsa_flags & (TLSAFLTEMP|DANE_SECURE)) == 2238363466Sgshapiro (TLSAFLTEMP|DANE_SECURE))) 2239363466Sgshapiro { 2240363466Sgshapiro (void) sm_strlcpy(SmtpError, 2241363466Sgshapiro " for TLSA RR", 2242363466Sgshapiro sizeof(SmtpError)); 2243363466Sgshapiro# if NAMED_BIND 2244363466Sgshapiro SM_SET_H_ERRNO(TRY_AGAIN); 2245363466Sgshapiro# endif 2246363466Sgshapiro } 2247363466Sgshapiro#endif 224864562Sgshapiro } 224977349Sgshapiro mci->mci_errno = errno; 225038032Speter mci->mci_lastuse = curtime(); 225164562Sgshapiro mci->mci_deliveries = 0; 225238032Speter mci->mci_exitstat = i; 2253223067Sgshapiro mci_clr_extensions(mci); 2254363466Sgshapiro#if NAMED_BIND 225538032Speter mci->mci_herrno = h_errno; 2256363466Sgshapiro#endif 225790792Sgshapiro 225890792Sgshapiro /* 225990792Sgshapiro ** Have we tried long enough to get a connection? 226090792Sgshapiro ** If yes, skip to the fallback MX hosts 226190792Sgshapiro ** (if existent). 226290792Sgshapiro */ 226390792Sgshapiro 226490792Sgshapiro if (enough > 0 && mci->mci_lastuse >= enough) 226590792Sgshapiro { 226690792Sgshapiro int h; 2267363466Sgshapiro#if NAMED_BIND 2268132943Sgshapiro extern int NumFallbackMXHosts; 2269363466Sgshapiro#else 2270132943Sgshapiro const int NumFallbackMXHosts = 0; 2271363466Sgshapiro#endif 227290792Sgshapiro 227390792Sgshapiro if (hostnum < nummxhosts && LogLevel > 9) 227490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 227590792Sgshapiro "Timeout.to_aconnect occurred before exhausting all addresses"); 227690792Sgshapiro 227790792Sgshapiro /* turn off timeout if fallback available */ 2278132943Sgshapiro if (NumFallbackMXHosts > 0) 227990792Sgshapiro enough = 0; 228090792Sgshapiro 228190792Sgshapiro /* skip to a fallback MX host */ 2282132943Sgshapiro h = nummxhosts - NumFallbackMXHosts; 228390792Sgshapiro if (hostnum < h) 228490792Sgshapiro hostnum = h; 228590792Sgshapiro } 228638032Speter if (i == EX_OK) 228738032Speter { 228890792Sgshapiro goodmxfound = true; 228994334Sgshapiro markstats(e, firstto, STATS_CONNECT); 229038032Speter mci->mci_state = MCIS_OPENING; 229138032Speter mci_cache(mci); 229238032Speter if (TrafficLogFile != NULL) 229390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 229490792Sgshapiro SM_TIME_DEFAULT, 229590792Sgshapiro "%05d === CONNECT %s\n", 229690792Sgshapiro (int) CurrentPid, 229790792Sgshapiro hostbuf); 229838032Speter break; 229938032Speter } 230038032Speter else 230138032Speter { 2302132943Sgshapiro /* Try FallbackSmartHost? */ 2303132943Sgshapiro if (should_try_fbsh(e, &tried_fallbacksmarthost, 2304168515Sgshapiro hostbuf, sizeof(hostbuf), i)) 2305132943Sgshapiro goto one_last_try; 2306132943Sgshapiro 230764562Sgshapiro if (tTd(11, 1)) 230890792Sgshapiro sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 230990792Sgshapiro i, errno); 231038032Speter if (i == EX_TEMPFAIL) 231190792Sgshapiro goodmxfound = true; 231238032Speter mci_unlock_host(mci); 231338032Speter } 231438032Speter 231538032Speter /* enter status of this host */ 231638032Speter setstat(i); 231738032Speter 231838032Speter /* should print some message here for -v mode */ 231938032Speter } 232038032Speter if (mci == NULL) 232138032Speter { 232238032Speter syserr("deliver: no host name"); 232338032Speter rcode = EX_SOFTWARE; 232438032Speter goto give_up; 232538032Speter } 232638032Speter mci->mci_pid = 0; 232738032Speter } 232838032Speter else 232938032Speter { 233038032Speter /* flush any expired connections */ 233138032Speter (void) mci_scan(NULL); 233238032Speter mci = NULL; 233338032Speter 233438032Speter if (bitnset(M_LMTP, m->m_flags)) 233538032Speter { 233638032Speter /* try to get a cached connection */ 233738032Speter mci = mci_get(m->m_name, m); 233838032Speter if (mci->mci_host == NULL) 233938032Speter mci->mci_host = m->m_name; 234038032Speter CurHostName = mci->mci_host; 234138032Speter if (mci->mci_state != MCIS_CLOSED) 234238032Speter { 234338032Speter message("Using cached LMTP connection for %s...", 234438032Speter m->m_name); 234564562Sgshapiro mci->mci_deliveries++; 234638032Speter goto do_transfer; 234738032Speter } 234838032Speter } 234938032Speter 235038032Speter /* announce the connection to verbose listeners */ 235138032Speter if (host == NULL || host[0] == '\0') 235238032Speter message("Connecting to %s...", m->m_name); 235338032Speter else 235438032Speter message("Connecting to %s via %s...", host, m->m_name); 235538032Speter if (TrafficLogFile != NULL) 235638032Speter { 235738032Speter char **av; 235838032Speter 235990792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 236090792Sgshapiro "%05d === EXEC", (int) CurrentPid); 236138032Speter for (av = pv; *av != NULL; av++) 236290792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 236390792Sgshapiro SM_TIME_DEFAULT, " %s", 236490792Sgshapiro *av); 236590792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 236690792Sgshapiro "\n"); 236738032Speter } 236838032Speter 236938032Speter#if XDEBUG 237038032Speter checkfd012("before creating mail pipe"); 2371363466Sgshapiro#endif 237238032Speter 237338032Speter /* create a pipe to shove the mail through */ 237438032Speter if (pipe(mpvect) < 0) 237538032Speter { 237638032Speter syserr("%s... openmailer(%s): pipe (to mailer)", 237790792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 237838032Speter if (tTd(11, 1)) 237990792Sgshapiro sm_dprintf("openmailer: NULL\n"); 238038032Speter rcode = EX_OSERR; 238138032Speter goto give_up; 238238032Speter } 238338032Speter 238438032Speter#if XDEBUG 238538032Speter /* make sure we didn't get one of the standard I/O files */ 238638032Speter if (mpvect[0] < 3 || mpvect[1] < 3) 238738032Speter { 238838032Speter syserr("%s... openmailer(%s): bogus mpvect %d %d", 238990792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 239090792Sgshapiro mpvect[0], mpvect[1]); 239190792Sgshapiro printopenfds(true); 239238032Speter if (tTd(11, 1)) 239390792Sgshapiro sm_dprintf("openmailer: NULL\n"); 239438032Speter rcode = EX_OSERR; 239538032Speter goto give_up; 239638032Speter } 239738032Speter 239838032Speter /* make sure system call isn't dead meat */ 239938032Speter checkfdopen(mpvect[0], "mpvect[0]"); 240038032Speter checkfdopen(mpvect[1], "mpvect[1]"); 240138032Speter if (mpvect[0] == mpvect[1] || 240238032Speter (e->e_lockfp != NULL && 240390792Sgshapiro (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 240490792Sgshapiro NULL) || 240590792Sgshapiro mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 240690792Sgshapiro NULL)))) 240738032Speter { 240838032Speter if (e->e_lockfp == NULL) 240938032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d", 241090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 241190792Sgshapiro m->m_name, mpvect[0], mpvect[1]); 241238032Speter else 241338032Speter syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 241490792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 241590792Sgshapiro m->m_name, mpvect[0], mpvect[1], 241690792Sgshapiro sm_io_getinfo(e->e_lockfp, 241790792Sgshapiro SM_IO_WHAT_FD, NULL)); 241838032Speter } 241964562Sgshapiro#endif /* XDEBUG */ 242038032Speter 242164562Sgshapiro /* create a return pipe */ 242264562Sgshapiro if (pipe(rpvect) < 0) 242338032Speter { 242464562Sgshapiro syserr("%s... openmailer(%s): pipe (from mailer)", 242590792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 242690792Sgshapiro m->m_name); 242764562Sgshapiro (void) close(mpvect[0]); 242864562Sgshapiro (void) close(mpvect[1]); 242964562Sgshapiro if (tTd(11, 1)) 243090792Sgshapiro sm_dprintf("openmailer: NULL\n"); 243164562Sgshapiro rcode = EX_OSERR; 243264562Sgshapiro goto give_up; 243338032Speter } 243464562Sgshapiro#if XDEBUG 243564562Sgshapiro checkfdopen(rpvect[0], "rpvect[0]"); 243664562Sgshapiro checkfdopen(rpvect[1], "rpvect[1]"); 2437363466Sgshapiro#endif 243838032Speter 243938032Speter /* 244038032Speter ** Actually fork the mailer process. 244138032Speter ** DOFORK is clever about retrying. 244238032Speter ** 244338032Speter ** Dispose of SIGCHLD signal catchers that may be laying 244464562Sgshapiro ** around so that endmailer will get it. 244538032Speter */ 244638032Speter 244790792Sgshapiro if (e->e_xfp != NULL) /* for debugging */ 244890792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 244990792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 245090792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 245164562Sgshapiro 245264562Sgshapiro 245338032Speter DOFORK(FORK); 245438032Speter /* pid is set by DOFORK */ 245564562Sgshapiro 245638032Speter if (pid < 0) 245738032Speter { 245838032Speter /* failure */ 245938032Speter syserr("%s... openmailer(%s): cannot fork", 246090792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 246138032Speter (void) close(mpvect[0]); 246238032Speter (void) close(mpvect[1]); 246364562Sgshapiro (void) close(rpvect[0]); 246464562Sgshapiro (void) close(rpvect[1]); 246538032Speter if (tTd(11, 1)) 246690792Sgshapiro sm_dprintf("openmailer: NULL\n"); 246738032Speter rcode = EX_OSERR; 246838032Speter goto give_up; 246938032Speter } 247038032Speter else if (pid == 0) 247138032Speter { 247264562Sgshapiro int save_errno; 247390792Sgshapiro int sff; 247438032Speter int new_euid = NO_UID; 247538032Speter int new_ruid = NO_UID; 247638032Speter int new_gid = NO_GID; 247790792Sgshapiro char *user = NULL; 247838032Speter struct stat stb; 247938032Speter extern int DtableSize; 248038032Speter 248190792Sgshapiro CurrentPid = getpid(); 248290792Sgshapiro 248380785Sgshapiro /* clear the events to turn off SIGALRMs */ 248490792Sgshapiro sm_clear_events(); 248580785Sgshapiro 248677349Sgshapiro /* Reset global flags */ 248777349Sgshapiro RestartRequest = NULL; 248890792Sgshapiro RestartWorkGroup = false; 248977349Sgshapiro ShutdownRequest = NULL; 249077349Sgshapiro PendingSignal = 0; 249177349Sgshapiro 249238032Speter if (e->e_lockfp != NULL) 249390792Sgshapiro (void) close(sm_io_getinfo(e->e_lockfp, 249490792Sgshapiro SM_IO_WHAT_FD, 249590792Sgshapiro NULL)); 249638032Speter 249738032Speter /* child -- set up input & exec mailer */ 249890792Sgshapiro (void) sm_signal(SIGALRM, sm_signal_noop); 249990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 250090792Sgshapiro (void) sm_signal(SIGHUP, SIG_IGN); 250190792Sgshapiro (void) sm_signal(SIGINT, SIG_IGN); 250290792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 2503363466Sgshapiro#ifdef SIGUSR1 250490792Sgshapiro (void) sm_signal(SIGUSR1, sm_signal_noop); 2505363466Sgshapiro#endif 250638032Speter 250738032Speter if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 250838032Speter stb.st_mode = 0; 250938032Speter 2510363466Sgshapiro#if HASSETUSERCONTEXT 251138032Speter /* 251238032Speter ** Set user resources. 251338032Speter */ 251438032Speter 251538032Speter if (contextaddr != NULL) 251638032Speter { 2517110560Sgshapiro int sucflags; 251838032Speter struct passwd *pwd; 251938032Speter 252038032Speter if (contextaddr->q_ruser != NULL) 252138032Speter pwd = sm_getpwnam(contextaddr->q_ruser); 252238032Speter else 252338032Speter pwd = sm_getpwnam(contextaddr->q_user); 2524110560Sgshapiro sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2525363466Sgshapiro# ifdef LOGIN_SETCPUMASK 2526223701Strasz sucflags |= LOGIN_SETCPUMASK; 2527363466Sgshapiro# endif 2528363466Sgshapiro# ifdef LOGIN_SETLOGINCLASS 2529223701Strasz sucflags |= LOGIN_SETLOGINCLASS; 2530363466Sgshapiro# endif 2531363466Sgshapiro# ifdef LOGIN_SETMAC 2532110560Sgshapiro sucflags |= LOGIN_SETMAC; 2533363466Sgshapiro# endif 2534102528Sgshapiro if (pwd != NULL && 2535102528Sgshapiro setusercontext(NULL, pwd, pwd->pw_uid, 2536110560Sgshapiro sucflags) == -1 && 2537102528Sgshapiro suidwarn) 2538102528Sgshapiro { 2539102528Sgshapiro syserr("openmailer: setusercontext() failed"); 2540102528Sgshapiro exit(EX_TEMPFAIL); 2541102528Sgshapiro } 254238032Speter } 2543363466Sgshapiro#endif /* HASSETUSERCONTEXT */ 254438032Speter 254590792Sgshapiro#if HASNICE 254638032Speter /* tweak niceness */ 254738032Speter if (m->m_nice != 0) 254864562Sgshapiro (void) nice(m->m_nice); 254990792Sgshapiro#endif /* HASNICE */ 255038032Speter 255138032Speter /* reset group id */ 255238032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2553132943Sgshapiro { 2554132943Sgshapiro if (m->m_gid == NO_GID) 2555132943Sgshapiro new_gid = RunAsGid; 2556132943Sgshapiro else 2557132943Sgshapiro new_gid = m->m_gid; 2558132943Sgshapiro } 255938032Speter else if (bitset(S_ISGID, stb.st_mode)) 256038032Speter new_gid = stb.st_gid; 256138032Speter else if (ctladdr != NULL && ctladdr->q_gid != 0) 256238032Speter { 256338032Speter if (!DontInitGroups) 256438032Speter { 256590792Sgshapiro user = ctladdr->q_ruser; 256690792Sgshapiro if (user == NULL) 256790792Sgshapiro user = ctladdr->q_user; 256838032Speter 256990792Sgshapiro if (initgroups(user, 257090792Sgshapiro ctladdr->q_gid) == -1 257190792Sgshapiro && suidwarn) 257264562Sgshapiro { 2573285229Sgshapiro syserr("openmailer: initgroups(%s, %ld) failed", 2574285229Sgshapiro user, (long) ctladdr->q_gid); 257564562Sgshapiro exit(EX_TEMPFAIL); 257664562Sgshapiro } 257738032Speter } 257838032Speter else 257938032Speter { 258038032Speter GIDSET_T gidset[1]; 258138032Speter 258238032Speter gidset[0] = ctladdr->q_gid; 258390792Sgshapiro if (setgroups(1, gidset) == -1 258490792Sgshapiro && suidwarn) 258564562Sgshapiro { 258638032Speter syserr("openmailer: setgroups() failed"); 258764562Sgshapiro exit(EX_TEMPFAIL); 258864562Sgshapiro } 258938032Speter } 259038032Speter new_gid = ctladdr->q_gid; 259138032Speter } 259238032Speter else 259338032Speter { 259438032Speter if (!DontInitGroups) 259538032Speter { 259690792Sgshapiro user = DefUser; 259790792Sgshapiro if (initgroups(DefUser, DefGid) == -1 && 259890792Sgshapiro suidwarn) 259964562Sgshapiro { 2600285229Sgshapiro syserr("openmailer: initgroups(%s, %ld) failed", 2601285229Sgshapiro DefUser, (long) DefGid); 260264562Sgshapiro exit(EX_TEMPFAIL); 260364562Sgshapiro } 260438032Speter } 260538032Speter else 260638032Speter { 260738032Speter GIDSET_T gidset[1]; 260838032Speter 260938032Speter gidset[0] = DefGid; 261090792Sgshapiro if (setgroups(1, gidset) == -1 261190792Sgshapiro && suidwarn) 261264562Sgshapiro { 261338032Speter syserr("openmailer: setgroups() failed"); 261464562Sgshapiro exit(EX_TEMPFAIL); 261564562Sgshapiro } 261638032Speter } 2617132943Sgshapiro if (m->m_gid == NO_GID) 261838032Speter new_gid = DefGid; 261938032Speter else 262038032Speter new_gid = m->m_gid; 262138032Speter } 262264562Sgshapiro if (new_gid != NO_GID) 262364562Sgshapiro { 262464562Sgshapiro if (RunAsUid != 0 && 262564562Sgshapiro bitnset(M_SPECIFIC_UID, m->m_flags) && 262664562Sgshapiro new_gid != getgid() && 262764562Sgshapiro new_gid != getegid()) 262864562Sgshapiro { 262964562Sgshapiro /* Only root can change the gid */ 2630285229Sgshapiro syserr("openmailer: insufficient privileges to change gid, RunAsUid=%ld, new_gid=%ld, gid=%ld, egid=%ld", 2631285229Sgshapiro (long) RunAsUid, (long) new_gid, 2632285229Sgshapiro (long) getgid(), (long) getegid()); 263364562Sgshapiro exit(EX_TEMPFAIL); 263464562Sgshapiro } 263538032Speter 263664562Sgshapiro if (setgid(new_gid) < 0 && suidwarn) 263764562Sgshapiro { 263864562Sgshapiro syserr("openmailer: setgid(%ld) failed", 263964562Sgshapiro (long) new_gid); 264064562Sgshapiro exit(EX_TEMPFAIL); 264164562Sgshapiro } 264264562Sgshapiro } 264364562Sgshapiro 264464562Sgshapiro /* change root to some "safe" directory */ 264564562Sgshapiro if (m->m_rootdir != NULL) 264664562Sgshapiro { 2647168515Sgshapiro expand(m->m_rootdir, cbuf, sizeof(cbuf), e); 264864562Sgshapiro if (tTd(11, 20)) 264990792Sgshapiro sm_dprintf("openmailer: chroot %s\n", 265098121Sgshapiro cbuf); 265198121Sgshapiro if (chroot(cbuf) < 0) 265264562Sgshapiro { 265364562Sgshapiro syserr("openmailer: Cannot chroot(%s)", 265498121Sgshapiro cbuf); 265564562Sgshapiro exit(EX_TEMPFAIL); 265664562Sgshapiro } 265764562Sgshapiro if (chdir("/") < 0) 265864562Sgshapiro { 265964562Sgshapiro syserr("openmailer: cannot chdir(/)"); 266064562Sgshapiro exit(EX_TEMPFAIL); 266164562Sgshapiro } 266264562Sgshapiro } 266364562Sgshapiro 266438032Speter /* reset user id */ 266538032Speter endpwent(); 266690792Sgshapiro sm_mbdb_terminate(); 266738032Speter if (bitnset(M_SPECIFIC_UID, m->m_flags)) 266880785Sgshapiro { 2669132943Sgshapiro if (m->m_uid == NO_UID) 2670132943Sgshapiro new_euid = RunAsUid; 2671132943Sgshapiro else 2672132943Sgshapiro new_euid = m->m_uid; 267380785Sgshapiro 267480785Sgshapiro /* 267580785Sgshapiro ** Undo the effects of the uid change in main 267680785Sgshapiro ** for signal handling. The real uid may 267780785Sgshapiro ** be used by mailer in adding a "From " 267880785Sgshapiro ** line. 267980785Sgshapiro */ 268080785Sgshapiro 268180785Sgshapiro if (RealUid != 0 && RealUid != getuid()) 268290792Sgshapiro { 2683363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETEUID 2684363466Sgshapiro# if HASSETREUID 268590792Sgshapiro if (setreuid(RealUid, geteuid()) < 0) 268690792Sgshapiro { 268790792Sgshapiro syserr("openmailer: setreuid(%d, %d) failed", 268890792Sgshapiro (int) RealUid, (int) geteuid()); 268990792Sgshapiro exit(EX_OSERR); 269090792Sgshapiro } 2691363466Sgshapiro# endif /* HASSETREUID */ 2692363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 2693363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETREUID 269480785Sgshapiro new_ruid = RealUid; 2695363466Sgshapiro#endif 269690792Sgshapiro } 269780785Sgshapiro } 269838032Speter else if (bitset(S_ISUID, stb.st_mode)) 269938032Speter new_ruid = stb.st_uid; 270038032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 270138032Speter new_ruid = ctladdr->q_uid; 2702132943Sgshapiro else if (m->m_uid != NO_UID) 270338032Speter new_ruid = m->m_uid; 270438032Speter else 270538032Speter new_ruid = DefUid; 270694334Sgshapiro 2707363466Sgshapiro#if _FFR_USE_SETLOGIN 270894334Sgshapiro /* run disconnected from terminal and set login name */ 270994334Sgshapiro if (setsid() >= 0 && 271094334Sgshapiro ctladdr != NULL && ctladdr->q_uid != 0 && 271194334Sgshapiro new_euid == ctladdr->q_uid) 271294334Sgshapiro { 271394334Sgshapiro struct passwd *pwd; 271494334Sgshapiro 271594334Sgshapiro pwd = sm_getpwuid(ctladdr->q_uid); 271694334Sgshapiro if (pwd != NULL && suidwarn) 271794334Sgshapiro (void) setlogin(pwd->pw_name); 271894334Sgshapiro endpwent(); 271994334Sgshapiro } 2720363466Sgshapiro#endif /* _FFR_USE_SETLOGIN */ 272194334Sgshapiro 272238032Speter if (new_euid != NO_UID) 272338032Speter { 272464562Sgshapiro if (RunAsUid != 0 && new_euid != RunAsUid) 272564562Sgshapiro { 272664562Sgshapiro /* Only root can change the uid */ 2727285229Sgshapiro syserr("openmailer: insufficient privileges to change uid, new_euid=%ld, RunAsUid=%ld", 2728285229Sgshapiro (long) new_euid, (long) RunAsUid); 272964562Sgshapiro exit(EX_TEMPFAIL); 273064562Sgshapiro } 273164562Sgshapiro 273238032Speter vendor_set_uid(new_euid); 2733363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETEUID 273438032Speter if (seteuid(new_euid) < 0 && suidwarn) 273564562Sgshapiro { 273638032Speter syserr("openmailer: seteuid(%ld) failed", 273790792Sgshapiro (long) new_euid); 273864562Sgshapiro exit(EX_TEMPFAIL); 273964562Sgshapiro } 2740363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 2741363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETREUID 274238032Speter if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 274364562Sgshapiro { 274438032Speter syserr("openmailer: setreuid(%ld, %ld) failed", 274590792Sgshapiro (long) new_ruid, (long) new_euid); 274664562Sgshapiro exit(EX_TEMPFAIL); 274764562Sgshapiro } 2748363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 2749363466Sgshapiro#if MAILER_SETUID_METHOD == USE_SETUID 275038032Speter if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 275164562Sgshapiro { 275238032Speter syserr("openmailer: setuid(%ld) failed", 275390792Sgshapiro (long) new_euid); 275464562Sgshapiro exit(EX_TEMPFAIL); 275564562Sgshapiro } 2756363466Sgshapiro#endif /* MAILER_SETUID_METHOD == USE_SETUID */ 275738032Speter } 275838032Speter else if (new_ruid != NO_UID) 275938032Speter { 276038032Speter vendor_set_uid(new_ruid); 276138032Speter if (setuid(new_ruid) < 0 && suidwarn) 276264562Sgshapiro { 276338032Speter syserr("openmailer: setuid(%ld) failed", 276490792Sgshapiro (long) new_ruid); 276564562Sgshapiro exit(EX_TEMPFAIL); 276664562Sgshapiro } 276738032Speter } 276838032Speter 276938032Speter if (tTd(11, 2)) 2770285229Sgshapiro sm_dprintf("openmailer: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n", 2771285229Sgshapiro (long) getuid(), (long) geteuid(), 2772285229Sgshapiro (long) getgid(), (long) getegid()); 277338032Speter 277438032Speter /* move into some "safe" directory */ 277538032Speter if (m->m_execdir != NULL) 277638032Speter { 277738032Speter char *q; 277838032Speter 277938032Speter for (p = m->m_execdir; p != NULL; p = q) 278038032Speter { 278138032Speter q = strchr(p, ':'); 278238032Speter if (q != NULL) 278338032Speter *q = '\0'; 2784168515Sgshapiro expand(p, cbuf, sizeof(cbuf), e); 278538032Speter if (q != NULL) 278638032Speter *q++ = ':'; 278738032Speter if (tTd(11, 20)) 278890792Sgshapiro sm_dprintf("openmailer: trydir %s\n", 278998121Sgshapiro cbuf); 279098121Sgshapiro if (cbuf[0] != '\0' && 279198121Sgshapiro chdir(cbuf) >= 0) 279238032Speter break; 279338032Speter } 279438032Speter } 279538032Speter 279690792Sgshapiro /* Check safety of program to be run */ 279790792Sgshapiro sff = SFF_ROOTOK|SFF_EXECOK; 279890792Sgshapiro if (!bitnset(DBS_RUNWRITABLEPROGRAM, 279990792Sgshapiro DontBlameSendmail)) 280090792Sgshapiro sff |= SFF_NOGWFILES|SFF_NOWWFILES; 280190792Sgshapiro if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 280290792Sgshapiro DontBlameSendmail)) 280390792Sgshapiro sff |= SFF_NOPATHCHECK; 280490792Sgshapiro else 280590792Sgshapiro sff |= SFF_SAFEDIRPATH; 280690792Sgshapiro ret = safefile(m->m_mailer, getuid(), getgid(), 280790792Sgshapiro user, sff, 0, NULL); 280890792Sgshapiro if (ret != 0) 280990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 281090792Sgshapiro "Warning: program %s unsafe: %s", 281190792Sgshapiro m->m_mailer, sm_errstring(ret)); 281290792Sgshapiro 281338032Speter /* arrange to filter std & diag output of command */ 281464562Sgshapiro (void) close(rpvect[0]); 281564562Sgshapiro if (dup2(rpvect[1], STDOUT_FILENO) < 0) 281638032Speter { 281764562Sgshapiro syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 281864562Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 281964562Sgshapiro m->m_name, rpvect[1]); 282064562Sgshapiro _exit(EX_OSERR); 282138032Speter } 282264562Sgshapiro (void) close(rpvect[1]); 282364562Sgshapiro 282438032Speter if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 282538032Speter { 282638032Speter syserr("%s... openmailer(%s): cannot dup stdout for stderr", 282790792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 282890792Sgshapiro m->m_name); 282938032Speter _exit(EX_OSERR); 283038032Speter } 283138032Speter 283238032Speter /* arrange to get standard input */ 283338032Speter (void) close(mpvect[1]); 283438032Speter if (dup2(mpvect[0], STDIN_FILENO) < 0) 283538032Speter { 283638032Speter syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 283790792Sgshapiro shortenstring(e->e_to, MAXSHORTSTR), 283890792Sgshapiro m->m_name, mpvect[0]); 283938032Speter _exit(EX_OSERR); 284038032Speter } 284138032Speter (void) close(mpvect[0]); 284238032Speter 284338032Speter /* arrange for all the files to be closed */ 2844132943Sgshapiro sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 284538032Speter 2846363466Sgshapiro#if !_FFR_USE_SETLOGIN 284738032Speter /* run disconnected from terminal */ 284838032Speter (void) setsid(); 2849363466Sgshapiro#endif 285038032Speter 285138032Speter /* try to execute the mailer */ 285264562Sgshapiro (void) execve(m->m_mailer, (ARGV_T) pv, 285364562Sgshapiro (ARGV_T) UserEnviron); 285464562Sgshapiro save_errno = errno; 285538032Speter syserr("Cannot exec %s", m->m_mailer); 285638032Speter if (bitnset(M_LOCALMAILER, m->m_flags) || 285764562Sgshapiro transienterror(save_errno)) 285838032Speter _exit(EX_OSERR); 285938032Speter _exit(EX_UNAVAILABLE); 286038032Speter } 286138032Speter 286238032Speter /* 286338032Speter ** Set up return value. 286438032Speter */ 286538032Speter 286638032Speter if (mci == NULL) 286738032Speter { 286890792Sgshapiro if (clever) 286990792Sgshapiro { 287090792Sgshapiro /* 287190792Sgshapiro ** Allocate from general heap, not 287290792Sgshapiro ** envelope rpool, because this mci 287390792Sgshapiro ** is going to be cached. 287490792Sgshapiro */ 287590792Sgshapiro 287690792Sgshapiro mci = mci_new(NULL); 287790792Sgshapiro } 287890792Sgshapiro else 287990792Sgshapiro { 288090792Sgshapiro /* 288190792Sgshapiro ** Prevent a storage leak by allocating 288290792Sgshapiro ** this from the envelope rpool. 288390792Sgshapiro */ 288490792Sgshapiro 288590792Sgshapiro mci = mci_new(e->e_rpool); 288690792Sgshapiro } 288738032Speter } 288838032Speter mci->mci_mailer = m; 288938032Speter if (clever) 289038032Speter { 289138032Speter mci->mci_state = MCIS_OPENING; 289238032Speter mci_cache(mci); 289338032Speter } 289438032Speter else 289538032Speter { 289638032Speter mci->mci_state = MCIS_OPEN; 289738032Speter } 289838032Speter mci->mci_pid = pid; 289938032Speter (void) close(mpvect[0]); 290090792Sgshapiro mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2901132943Sgshapiro (void *) &(mpvect[1]), SM_IO_WRONLY_B, 290290792Sgshapiro NULL); 290338032Speter if (mci->mci_out == NULL) 290438032Speter { 290538032Speter syserr("deliver: cannot create mailer output channel, fd=%d", 290690792Sgshapiro mpvect[1]); 290738032Speter (void) close(mpvect[1]); 290864562Sgshapiro (void) close(rpvect[0]); 290964562Sgshapiro (void) close(rpvect[1]); 291038032Speter rcode = EX_OSERR; 291138032Speter goto give_up; 291238032Speter } 291364562Sgshapiro 291464562Sgshapiro (void) close(rpvect[1]); 291590792Sgshapiro mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2916132943Sgshapiro (void *) &(rpvect[0]), SM_IO_RDONLY_B, 291790792Sgshapiro NULL); 291864562Sgshapiro if (mci->mci_in == NULL) 291938032Speter { 292064562Sgshapiro syserr("deliver: cannot create mailer input channel, fd=%d", 292164562Sgshapiro mpvect[1]); 292264562Sgshapiro (void) close(rpvect[0]); 292390792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 292464562Sgshapiro mci->mci_out = NULL; 292564562Sgshapiro rcode = EX_OSERR; 292664562Sgshapiro goto give_up; 292738032Speter } 292838032Speter } 292938032Speter 293038032Speter /* 293138032Speter ** If we are in SMTP opening state, send initial protocol. 293238032Speter */ 293338032Speter 293438032Speter if (bitnset(M_7BITS, m->m_flags) && 293538032Speter (!clever || mci->mci_state == MCIS_OPENING)) 293638032Speter mci->mci_flags |= MCIF_7BIT; 293738032Speter if (clever && mci->mci_state != MCIS_CLOSED) 293838032Speter { 2939363466Sgshapiro#if STARTTLS || SASL 294090792Sgshapiro char *srvname; 294190792Sgshapiro extern SOCKADDR CurHostAddr; 2942363466Sgshapiro#endif /* STARTTLS || SASL */ 294390792Sgshapiro 2944363466Sgshapiro#if SASL 2945363466Sgshapiro# define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 2946363466Sgshapiro#endif 2947363466Sgshapiro#if STARTTLS 2948363466Sgshapiro# define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 2949363466Sgshapiro#endif 2950363466Sgshapiro#define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 2951363466Sgshapiro#define SET_HELO(f) f |= MCIF_ONLY_EHLO 2952363466Sgshapiro#define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 295338032Speter 2954363466Sgshapiro#if STARTTLS || SASL 295590792Sgshapiro /* don't use CurHostName, it is changed in many places */ 295690792Sgshapiro if (mci->mci_host != NULL) 295790792Sgshapiro { 295890792Sgshapiro srvname = mci->mci_host; 2959363466Sgshapiro RM_TRAIL_DOT(srvname); 296090792Sgshapiro } 296190792Sgshapiro else if (mci->mci_mailer != NULL) 296290792Sgshapiro { 296390792Sgshapiro srvname = mci->mci_mailer->m_name; 296490792Sgshapiro dotpos = -1; 296590792Sgshapiro } 296690792Sgshapiro else 296790792Sgshapiro { 296890792Sgshapiro srvname = "local"; 296990792Sgshapiro dotpos = -1; 297090792Sgshapiro } 297171345Sgshapiro 297290792Sgshapiro /* don't set {server_name} to NULL or "": see getauth() */ 297390792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 297490792Sgshapiro srvname); 297564562Sgshapiro 297690792Sgshapiro /* CurHostAddr is set by makeconnection() and mci_get() */ 297790792Sgshapiro if (CurHostAddr.sa.sa_family != 0) 297890792Sgshapiro { 297990792Sgshapiro macdefine(&mci->mci_macro, A_TEMP, 298090792Sgshapiro macid("{server_addr}"), 298190792Sgshapiro anynet_ntoa(&CurHostAddr)); 298290792Sgshapiro } 298390792Sgshapiro else if (mci->mci_mailer != NULL) 298490792Sgshapiro { 298590792Sgshapiro /* mailer name is unique, use it as address */ 298690792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 298790792Sgshapiro macid("{server_addr}"), 298890792Sgshapiro mci->mci_mailer->m_name); 298990792Sgshapiro } 299090792Sgshapiro else 299190792Sgshapiro { 299290792Sgshapiro /* don't set it to NULL or "": see getauth() */ 299390792Sgshapiro macdefine(&mci->mci_macro, A_PERM, 299490792Sgshapiro macid("{server_addr}"), "0"); 299590792Sgshapiro } 299690792Sgshapiro 2997363466Sgshapiro# if DANE 2998363466Sgshapiro SM_FREE(dane_vrfy_ctx.dane_vrfy_host); 2999363466Sgshapiro SM_FREE(dane_vrfy_ctx.dane_vrfy_sni); 3000363466Sgshapiro dane_vrfy_ctx.dane_vrfy_fp[0] = '\0'; 3001363466Sgshapiro if (ste != NULL && ste->s_tlsa != NULL && 3002363466Sgshapiro ste->s_tlsa->dane_tlsa_sni != NULL) 3003363466Sgshapiro dane_vrfy_ctx.dane_vrfy_sni = sm_strdup(ste->s_tlsa->dane_tlsa_sni); 3004363466Sgshapiro dane_vrfy_ctx.dane_vrfy_host = sm_strdup(srvname); 3005363466Sgshapiro# endif 3006363466Sgshapiro 300790792Sgshapiro /* undo change of srvname (mci->mci_host) */ 3008363466Sgshapiro FIX_TRAIL_DOT(srvname); 300990792Sgshapiro 301090792Sgshapiroreconnect: /* after switching to an encrypted connection */ 3011363466Sgshapiro# if DANE 3012363466Sgshapiro if (DONE_STARTTLS(mci->mci_flags)) 3013363466Sgshapiro { 3014363466Sgshapiro /* use a "reset" function? */ 3015363466Sgshapiro SM_FREE(dane_vrfy_ctx.dane_vrfy_host); 3016363466Sgshapiro SM_FREE(dane_vrfy_ctx.dane_vrfy_sni); 3017363466Sgshapiro dane_vrfy_ctx.dane_vrfy_fp[0] = '\0'; 3018363466Sgshapiro dane_vrfy_ctx.dane_vrfy_res = 0; 3019363466Sgshapiro } 3020363466Sgshapiro# endif 302190792Sgshapiro 3022363466Sgshapiro#endif /* STARTTLS || SASL */ 3023363466Sgshapiro 302490792Sgshapiro /* set the current connection information */ 302590792Sgshapiro e->e_mci = mci; 3026363466Sgshapiro#if SASL 302764562Sgshapiro mci->mci_saslcap = NULL; 3028363466Sgshapiro#endif 302971345Sgshapiro smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 303071345Sgshapiro CLR_HELO(mci->mci_flags); 303164562Sgshapiro 303290792Sgshapiro if (IS_DLVR_RETURN(e)) 303390792Sgshapiro { 303490792Sgshapiro /* 303590792Sgshapiro ** Check whether other side can deliver e-mail 303690792Sgshapiro ** fast enough 303790792Sgshapiro */ 303890792Sgshapiro 303990792Sgshapiro if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 304090792Sgshapiro { 304190792Sgshapiro e->e_status = "5.4.7"; 304290792Sgshapiro usrerrenh(e->e_status, 304390792Sgshapiro "554 Server does not support Deliver By"); 304490792Sgshapiro rcode = EX_UNAVAILABLE; 304590792Sgshapiro goto give_up; 304690792Sgshapiro } 304790792Sgshapiro if (e->e_deliver_by > 0 && 304890792Sgshapiro e->e_deliver_by - (curtime() - e->e_ctime) < 304990792Sgshapiro mci->mci_min_by) 305090792Sgshapiro { 305190792Sgshapiro e->e_status = "5.4.7"; 305290792Sgshapiro usrerrenh(e->e_status, 305390792Sgshapiro "554 Message can't be delivered in time; %ld < %ld", 3054363466Sgshapiro e->e_deliver_by - (long) (curtime() - 3055363466Sgshapiro e->e_ctime), 305690792Sgshapiro mci->mci_min_by); 305790792Sgshapiro rcode = EX_UNAVAILABLE; 305890792Sgshapiro goto give_up; 305990792Sgshapiro } 306090792Sgshapiro } 306190792Sgshapiro 3062363466Sgshapiro#if STARTTLS 306364562Sgshapiro /* first TLS then AUTH to provide a security layer */ 306471345Sgshapiro if (mci->mci_state != MCIS_CLOSED && 306571345Sgshapiro !DONE_STARTTLS(mci->mci_flags)) 306664562Sgshapiro { 306764562Sgshapiro int olderrors; 306864562Sgshapiro bool usetls; 306964562Sgshapiro bool saveQuickAbort = QuickAbort; 307064562Sgshapiro bool saveSuprErrs = SuprErrs; 307171345Sgshapiro char *host = NULL; 307264562Sgshapiro 307364562Sgshapiro rcode = EX_OK; 307464562Sgshapiro usetls = bitset(MCIF_TLS, mci->mci_flags); 307590792Sgshapiro if (usetls) 307690792Sgshapiro usetls = !iscltflgset(e, D_NOTLS); 3077363466Sgshapiro if (usetls) 3078363466Sgshapiro usetls = tlsstate == 0; 307966494Sgshapiro 3080168515Sgshapiro host = macvalue(macid("{server_name}"), e); 308164562Sgshapiro if (usetls) 308264562Sgshapiro { 308364562Sgshapiro olderrors = Errors; 308490792Sgshapiro QuickAbort = false; 308590792Sgshapiro SuprErrs = true; 3086102528Sgshapiro if (rscheck("try_tls", host, NULL, e, 3087285229Sgshapiro RSF_RMCOMM, 7, host, NOQID, NULL, 3088285229Sgshapiro NULL) != EX_OK 308964562Sgshapiro || Errors > olderrors) 3090168515Sgshapiro { 309190792Sgshapiro usetls = false; 3092168515Sgshapiro } 309364562Sgshapiro SuprErrs = saveSuprErrs; 309464562Sgshapiro QuickAbort = saveQuickAbort; 309564562Sgshapiro } 309664562Sgshapiro 309764562Sgshapiro if (usetls) 309864562Sgshapiro { 3099363466Sgshapiro if ((rcode = starttls(m, mci, e 3100363466Sgshapiro# if DANE 3101363466Sgshapiro , &dane_vrfy_ctx 3102363466Sgshapiro# endif 3103363466Sgshapiro )) == EX_OK) 310464562Sgshapiro { 310564562Sgshapiro /* start again without STARTTLS */ 310664562Sgshapiro mci->mci_flags |= MCIF_TLSACT; 310764562Sgshapiro } 310864562Sgshapiro else 310964562Sgshapiro { 311064562Sgshapiro char *s; 311164562Sgshapiro 311264562Sgshapiro /* 3113203004Sgshapiro ** TLS negotiation failed, what to do? 311464562Sgshapiro ** fall back to unencrypted connection 311564562Sgshapiro ** or abort? How to decide? 311664562Sgshapiro ** set a macro and call a ruleset. 311764562Sgshapiro */ 311890792Sgshapiro 311964562Sgshapiro mci->mci_flags &= ~MCIF_TLS; 312064562Sgshapiro switch (rcode) 312164562Sgshapiro { 312264562Sgshapiro case EX_TEMPFAIL: 312364562Sgshapiro s = "TEMP"; 312464562Sgshapiro break; 312564562Sgshapiro case EX_USAGE: 312664562Sgshapiro s = "USAGE"; 312764562Sgshapiro break; 312864562Sgshapiro case EX_PROTOCOL: 312964562Sgshapiro s = "PROTOCOL"; 313064562Sgshapiro break; 313164562Sgshapiro case EX_SOFTWARE: 313264562Sgshapiro s = "SOFTWARE"; 313364562Sgshapiro break; 3134157001Sgshapiro case EX_UNAVAILABLE: 3135157001Sgshapiro s = "NONE"; 3136157001Sgshapiro break; 313764562Sgshapiro 313864562Sgshapiro /* everything else is a failure */ 313964562Sgshapiro default: 314064562Sgshapiro s = "FAILURE"; 314164562Sgshapiro rcode = EX_TEMPFAIL; 314264562Sgshapiro } 314390792Sgshapiro macdefine(&e->e_macro, A_PERM, 314490792Sgshapiro macid("{verify}"), s); 314564562Sgshapiro } 314664562Sgshapiro } 314764562Sgshapiro else 3148363466Sgshapiro { 3149363466Sgshapiro p = tlsstate == 0 ? "NONE": "CLEAR"; 3150363466Sgshapiro# if DANE 3151363466Sgshapiro /* 3152363466Sgshapiro ** TLSA found but STARTTLS not offered? 3153363466Sgshapiro ** What is the best way to "fail"? 3154363466Sgshapiro ** XXX: check expiration! 3155363466Sgshapiro */ 3156363466Sgshapiro 3157363466Sgshapiro if (!bitset(MCIF_TLS, mci->mci_flags) && 3158363466Sgshapiro ste != NULL && 3159363466Sgshapiro ste->s_tlsa != NULL && 3160363466Sgshapiro ste->s_tlsa->dane_tlsa_n > 0) 3161363466Sgshapiro { 3162363466Sgshapiro if (LogLevel > 8) 3163363466Sgshapiro sm_syslog(LOG_NOTICE, NOQID, 3164363466Sgshapiro "STARTTLS=client, relay=%.100s, warning=DANE configured in DNS but no STARTTLS available", 3165363466Sgshapiro host); 3166363466Sgshapiro /* XXX include TLSA RR from DNS? */ 3167363466Sgshapiro 3168363466Sgshapiro p = "DANE_FAIL"; 3169363466Sgshapiro } 3170363466Sgshapiro# endif /* DANE */ 317190792Sgshapiro macdefine(&e->e_macro, A_PERM, 3172363466Sgshapiro macid("{verify}"), p); 3173363466Sgshapiro } 317464562Sgshapiro olderrors = Errors; 317590792Sgshapiro QuickAbort = false; 317690792Sgshapiro SuprErrs = true; 317764562Sgshapiro 317864562Sgshapiro /* 317964562Sgshapiro ** rcode == EX_SOFTWARE is special: 3180203004Sgshapiro ** the TLS negotiation failed 318164562Sgshapiro ** we have to drop the connection no matter what 318264562Sgshapiro ** However, we call tls_server to give it the chance 318364562Sgshapiro ** to log the problem and return an appropriate 318464562Sgshapiro ** error code. 318564562Sgshapiro */ 318690792Sgshapiro 318764562Sgshapiro if (rscheck("tls_server", 318890792Sgshapiro macvalue(macid("{verify}"), e), 3189102528Sgshapiro NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3190285229Sgshapiro host, NOQID, NULL, NULL) != EX_OK || 319164562Sgshapiro Errors > olderrors || 319264562Sgshapiro rcode == EX_SOFTWARE) 319364562Sgshapiro { 319464562Sgshapiro char enhsc[ENHSCLEN]; 319564562Sgshapiro extern char MsgBuf[]; 319664562Sgshapiro 319764562Sgshapiro if (ISSMTPCODE(MsgBuf) && 319864562Sgshapiro extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 319964562Sgshapiro { 320090792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, 320190792Sgshapiro MsgBuf); 320264562Sgshapiro } 320364562Sgshapiro else 320464562Sgshapiro { 320564562Sgshapiro p = "403 4.7.0 server not authenticated."; 320690792Sgshapiro (void) sm_strlcpy(enhsc, "4.7.0", 3207168515Sgshapiro sizeof(enhsc)); 320864562Sgshapiro } 320964562Sgshapiro SuprErrs = saveSuprErrs; 321064562Sgshapiro QuickAbort = saveQuickAbort; 321164562Sgshapiro 321264562Sgshapiro if (rcode == EX_SOFTWARE) 321364562Sgshapiro { 321464562Sgshapiro /* drop the connection */ 321564562Sgshapiro mci->mci_state = MCIS_QUITING; 321664562Sgshapiro if (mci->mci_in != NULL) 321764562Sgshapiro { 321890792Sgshapiro (void) sm_io_close(mci->mci_in, 321990792Sgshapiro SM_TIME_DEFAULT); 322064562Sgshapiro mci->mci_in = NULL; 322164562Sgshapiro } 322264562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 322364562Sgshapiro (void) endmailer(mci, e, pv); 3224363466Sgshapiro 3225363466Sgshapiro if ((TLSFallbacktoClear || 3226363466Sgshapiro SM_TLSI_IS(&(mci->mci_tlsi), 3227363466Sgshapiro TLSI_FL_FB2CLR)) && 3228363466Sgshapiro !SM_TLSI_IS(&(mci->mci_tlsi), 3229363466Sgshapiro TLSI_FL_NOFB2CLR) 3230363466Sgshapiro# if DANE 3231363466Sgshapiro && dane_vrfy_ctx.dane_vrfy_chk != 3232363466Sgshapiro DANE_SECURE 3233363466Sgshapiro# endif 3234363466Sgshapiro ) 3235363466Sgshapiro { 3236363466Sgshapiro ++tlsstate; 3237363466Sgshapiro } 323864562Sgshapiro } 323964562Sgshapiro else 324064562Sgshapiro { 324164562Sgshapiro /* abort transfer */ 324264562Sgshapiro smtpquit(m, mci, e); 324364562Sgshapiro } 324464562Sgshapiro 324571345Sgshapiro /* avoid bogus error msg */ 324671345Sgshapiro mci->mci_errno = 0; 324771345Sgshapiro 324864562Sgshapiro /* temp or permanent failure? */ 324964562Sgshapiro rcode = (*p == '4') ? EX_TEMPFAIL 325064562Sgshapiro : EX_UNAVAILABLE; 325190792Sgshapiro mci_setstat(mci, rcode, enhsc, p); 325264562Sgshapiro 325364562Sgshapiro /* 325464562Sgshapiro ** hack to get the error message into 325564562Sgshapiro ** the envelope (done in giveresponse()) 325664562Sgshapiro */ 325790792Sgshapiro 325890792Sgshapiro (void) sm_strlcpy(SmtpError, p, 3259168515Sgshapiro sizeof(SmtpError)); 326064562Sgshapiro } 3261168515Sgshapiro else if (mci->mci_state == MCIS_CLOSED) 3262168515Sgshapiro { 3263168515Sgshapiro /* connection close caused by 421 */ 3264168515Sgshapiro mci->mci_errno = 0; 3265168515Sgshapiro rcode = EX_TEMPFAIL; 3266168515Sgshapiro mci_setstat(mci, rcode, NULL, "421"); 3267168515Sgshapiro } 3268168515Sgshapiro else 3269168515Sgshapiro rcode = 0; 3270168515Sgshapiro 327164562Sgshapiro QuickAbort = saveQuickAbort; 327264562Sgshapiro SuprErrs = saveSuprErrs; 327371345Sgshapiro if (DONE_STARTTLS(mci->mci_flags) && 327471345Sgshapiro mci->mci_state != MCIS_CLOSED) 327564562Sgshapiro { 327671345Sgshapiro SET_HELO(mci->mci_flags); 3277223067Sgshapiro mci_clr_extensions(mci); 327864562Sgshapiro goto reconnect; 327964562Sgshapiro } 3280363466Sgshapiro if (tlsstate == 1) 3281363466Sgshapiro { 3282363466Sgshapiro if (tTd(11, 1)) 3283363466Sgshapiro { 3284363466Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 3285363466Sgshapiro "STARTTLS=client, relay=%.100s, tlsstate=%d, status=trying_again", 3286363466Sgshapiro mci->mci_host, tlsstate); 3287363466Sgshapiro mci_dump(NULL, mci, true); 3288363466Sgshapiro } 3289363466Sgshapiro ++tlsstate; 3290363466Sgshapiro 3291363466Sgshapiro /* 3292363466Sgshapiro ** Fake the status so a new connection is 3293363466Sgshapiro ** tried, otherwise the TLS error will 3294363466Sgshapiro ** "persist" during this delivery attempt. 3295363466Sgshapiro */ 3296363466Sgshapiro 3297363466Sgshapiro mci->mci_errno = 0; 3298363466Sgshapiro rcode = EX_OK; 3299363466Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 3300363466Sgshapiro goto one_last_try; 3301363466Sgshapiro} 330264562Sgshapiro } 3303363466Sgshapiro#endif /* STARTTLS */ 3304363466Sgshapiro#if SASL 330564562Sgshapiro /* if other server supports authentication let's authenticate */ 330664562Sgshapiro if (mci->mci_state != MCIS_CLOSED && 330764562Sgshapiro mci->mci_saslcap != NULL && 330890792Sgshapiro !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 330964562Sgshapiro { 331090792Sgshapiro /* Should we require some minimum authentication? */ 331190792Sgshapiro if ((ret = smtpauth(m, mci, e)) == EX_OK) 331264562Sgshapiro { 331364562Sgshapiro int result; 331490792Sgshapiro sasl_ssf_t *ssf = NULL; 331564562Sgshapiro 331690792Sgshapiro /* Get security strength (features) */ 331764562Sgshapiro result = sasl_getprop(mci->mci_conn, SASL_SSF, 331898121Sgshapiro# if SASL >= 20000 331998121Sgshapiro (const void **) &ssf); 3320363466Sgshapiro# else 332164562Sgshapiro (void **) &ssf); 3322363466Sgshapiro# endif 332390792Sgshapiro 332490792Sgshapiro /* XXX authid? */ 332564562Sgshapiro if (LogLevel > 9) 332664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 332790792Sgshapiro "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 332864562Sgshapiro mci->mci_host, 332990792Sgshapiro macvalue(macid("{auth_type}"), e), 333090792Sgshapiro result == SASL_OK ? *ssf : 0); 333177349Sgshapiro 333264562Sgshapiro /* 333390792Sgshapiro ** Only switch to encrypted connection 333464562Sgshapiro ** if a security layer has been negotiated 333564562Sgshapiro */ 333690792Sgshapiro 333764562Sgshapiro if (result == SASL_OK && *ssf > 0) 333864562Sgshapiro { 3339159609Sgshapiro int tmo; 3340159609Sgshapiro 334164562Sgshapiro /* 334290792Sgshapiro ** Convert I/O layer to use SASL. 334390792Sgshapiro ** If the call fails, the connection 334490792Sgshapiro ** is aborted. 334564562Sgshapiro */ 334690792Sgshapiro 3347159609Sgshapiro tmo = DATA_PROGRESS_TIMEOUT * 1000; 334890792Sgshapiro if (sfdcsasl(&mci->mci_in, 334990792Sgshapiro &mci->mci_out, 3350159609Sgshapiro mci->mci_conn, tmo) == 0) 335164562Sgshapiro { 3352223067Sgshapiro mci_clr_extensions(mci); 335390792Sgshapiro mci->mci_flags |= MCIF_AUTHACT| 335490792Sgshapiro MCIF_ONLY_EHLO; 335564562Sgshapiro goto reconnect; 335664562Sgshapiro } 335790792Sgshapiro syserr("AUTH TLS switch failed in client"); 335864562Sgshapiro } 335964562Sgshapiro /* else? XXX */ 336064562Sgshapiro mci->mci_flags |= MCIF_AUTHACT; 336164562Sgshapiro 336264562Sgshapiro } 336390792Sgshapiro else if (ret == EX_TEMPFAIL) 336490792Sgshapiro { 336590792Sgshapiro if (LogLevel > 8) 336690792Sgshapiro sm_syslog(LOG_ERR, NOQID, 336790792Sgshapiro "AUTH=client, relay=%.100s, temporary failure, connection abort", 336890792Sgshapiro mci->mci_host); 336990792Sgshapiro smtpquit(m, mci, e); 337090792Sgshapiro 337190792Sgshapiro /* avoid bogus error msg */ 337290792Sgshapiro mci->mci_errno = 0; 337390792Sgshapiro rcode = EX_TEMPFAIL; 3374132943Sgshapiro mci_setstat(mci, rcode, "4.3.0", p); 337590792Sgshapiro 337690792Sgshapiro /* 337790792Sgshapiro ** hack to get the error message into 337890792Sgshapiro ** the envelope (done in giveresponse()) 337990792Sgshapiro */ 338090792Sgshapiro 338190792Sgshapiro (void) sm_strlcpy(SmtpError, 338290792Sgshapiro "Temporary AUTH failure", 3383168515Sgshapiro sizeof(SmtpError)); 338490792Sgshapiro } 338564562Sgshapiro } 3386363466Sgshapiro#endif /* SASL */ 338738032Speter } 338838032Speter 338938032Speterdo_transfer: 339038032Speter /* clear out per-message flags from connection structure */ 339138032Speter mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 339238032Speter 339338032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 339438032Speter !bitset(EF_DONT_MIME, e->e_flags) && 339538032Speter bitnset(M_7BITS, m->m_flags)) 339638032Speter mci->mci_flags |= MCIF_CVT8TO7; 339738032Speter 339838032Speter#if MIME7TO8 339938032Speter if (bitnset(M_MAKE8BIT, m->m_flags) && 340038032Speter !bitset(MCIF_7BIT, mci->mci_flags) && 340138032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 340290792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 340390792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 340438032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 340538032Speter { 340638032Speter /* may want to convert 7 -> 8 */ 340738032Speter /* XXX should really parse it here -- and use a class XXX */ 340890792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 340938032Speter (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 341038032Speter mci->mci_flags |= MCIF_CVT7TO8; 341138032Speter } 341264562Sgshapiro#endif /* MIME7TO8 */ 341338032Speter 341438032Speter if (tTd(11, 1)) 341538032Speter { 341690792Sgshapiro sm_dprintf("openmailer: "); 3417132943Sgshapiro mci_dump(sm_debug_file(), mci, false); 341838032Speter } 341938032Speter 342090792Sgshapiro#if _FFR_CLIENT_SIZE 342190792Sgshapiro /* 342290792Sgshapiro ** See if we know the maximum size and 342390792Sgshapiro ** abort if the message is too big. 342490792Sgshapiro ** 342590792Sgshapiro ** NOTE: _FFR_CLIENT_SIZE is untested. 342690792Sgshapiro */ 342790792Sgshapiro 342890792Sgshapiro if (bitset(MCIF_SIZE, mci->mci_flags) && 342990792Sgshapiro mci->mci_maxsize > 0 && 343090792Sgshapiro e->e_msgsize > mci->mci_maxsize) 343190792Sgshapiro { 343290792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 343390792Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags)) 343490792Sgshapiro e->e_status = "5.2.3"; 343590792Sgshapiro else 343690792Sgshapiro e->e_status = "5.3.4"; 343790792Sgshapiro 343890792Sgshapiro usrerrenh(e->e_status, 343990792Sgshapiro "552 Message is too large; %ld bytes max", 344090792Sgshapiro mci->mci_maxsize); 344190792Sgshapiro rcode = EX_DATAERR; 344290792Sgshapiro 344390792Sgshapiro /* Need an e_message for error */ 3444168515Sgshapiro (void) sm_snprintf(SmtpError, sizeof(SmtpError), 344590792Sgshapiro "Message is too large; %ld bytes max", 344690792Sgshapiro mci->mci_maxsize); 344790792Sgshapiro goto give_up; 344890792Sgshapiro } 344990792Sgshapiro#endif /* _FFR_CLIENT_SIZE */ 345090792Sgshapiro 345138032Speter if (mci->mci_state != MCIS_OPEN) 345238032Speter { 345338032Speter /* couldn't open the mailer */ 345438032Speter rcode = mci->mci_exitstat; 345538032Speter errno = mci->mci_errno; 345673188Sgshapiro SM_SET_H_ERRNO(mci->mci_herrno); 345738032Speter if (rcode == EX_OK) 345838032Speter { 345938032Speter /* shouldn't happen */ 346064562Sgshapiro syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 346190792Sgshapiro (unsigned long) mci, rcode, errno, 346290792Sgshapiro mci->mci_state, firstsig); 3463132943Sgshapiro mci_dump_all(smioout, true); 346438032Speter rcode = EX_SOFTWARE; 346538032Speter } 346664562Sgshapiro else if (nummxhosts > hostnum) 346738032Speter { 346838032Speter /* try next MX site */ 346938032Speter goto tryhost; 347038032Speter } 347138032Speter } 347238032Speter else if (!clever) 347338032Speter { 3474157001Sgshapiro bool ok; 3475157001Sgshapiro 347638032Speter /* 347738032Speter ** Format and send message. 347838032Speter */ 347938032Speter 3480157001Sgshapiro rcode = EX_OK; 3481157001Sgshapiro errno = 0; 3482157001Sgshapiro ok = putfromline(mci, e); 3483157001Sgshapiro if (ok) 3484157001Sgshapiro ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3485157001Sgshapiro if (ok) 3486157001Sgshapiro ok = (*e->e_putbody)(mci, e, NULL); 3487173340Sgshapiro if (ok && bitset(MCIF_INLONGLINE, mci->mci_flags)) 3488173340Sgshapiro ok = putline("", mci); 348938032Speter 3490157001Sgshapiro /* 3491157001Sgshapiro ** Ignore an I/O error that was caused by EPIPE. 3492157001Sgshapiro ** Some broken mailers don't read the entire body 3493157001Sgshapiro ** but just exit() thus causing an I/O error. 3494157001Sgshapiro */ 3495157001Sgshapiro 3496157001Sgshapiro if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE)) 3497157001Sgshapiro ok = true; 3498157001Sgshapiro 3499157001Sgshapiro /* (always) get the exit status */ 350038032Speter rcode = endmailer(mci, e, pv); 3501157001Sgshapiro if (!ok) 3502157001Sgshapiro rcode = EX_TEMPFAIL; 350390792Sgshapiro if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 350473188Sgshapiro { 350573188Sgshapiro /* 350673188Sgshapiro ** Need an e_message for mailq display. 350773188Sgshapiro ** We set SmtpError as 350873188Sgshapiro */ 350973188Sgshapiro 3510168515Sgshapiro (void) sm_snprintf(SmtpError, sizeof(SmtpError), 351190792Sgshapiro "%s mailer (%s) exited with EX_TEMPFAIL", 351290792Sgshapiro m->m_name, m->m_mailer); 351373188Sgshapiro } 351438032Speter } 351538032Speter else 351638032Speter { 351738032Speter /* 351838032Speter ** Send the MAIL FROM: protocol 351938032Speter */ 352038032Speter 352190792Sgshapiro /* XXX this isn't pipelined... */ 352238032Speter rcode = smtpmailfrom(m, mci, e); 352338032Speter if (rcode == EX_OK) 352438032Speter { 352538032Speter register int i; 3526363466Sgshapiro#if PIPELINING 352790792Sgshapiro ADDRESS *volatile pchain; 3528363466Sgshapiro#endif 352938032Speter 353038032Speter /* send the recipient list */ 353138032Speter tobuf[0] = '\0'; 353290792Sgshapiro mci->mci_retryrcpt = false; 353390792Sgshapiro mci->mci_tolist = tobuf; 3534363466Sgshapiro#if PIPELINING 353590792Sgshapiro pchain = NULL; 353690792Sgshapiro mci->mci_nextaddr = NULL; 3537363466Sgshapiro#endif 353864562Sgshapiro 353938032Speter for (to = tochain; to != NULL; to = to->q_tchain) 354038032Speter { 354190792Sgshapiro if (!QS_IS_UNMARKED(to->q_state)) 354238032Speter continue; 354364562Sgshapiro 354490792Sgshapiro /* mark recipient state as "ok so far" */ 354590792Sgshapiro to->q_state = QS_OK; 354690792Sgshapiro e->e_to = to->q_paddr; 3547363466Sgshapiro#if STARTTLS 354864562Sgshapiro i = rscheck("tls_rcpt", to->q_user, NULL, e, 3549102528Sgshapiro RSF_RMCOMM|RSF_COUNT, 3, 3550285229Sgshapiro mci->mci_host, e->e_id, NULL, NULL); 355164562Sgshapiro if (i != EX_OK) 355238032Speter { 355390792Sgshapiro markfailure(e, to, mci, i, false); 355490792Sgshapiro giveresponse(i, to->q_status, m, mci, 355590792Sgshapiro ctladdr, xstart, e, to); 355690792Sgshapiro if (i == EX_TEMPFAIL) 355790792Sgshapiro { 355890792Sgshapiro mci->mci_retryrcpt = true; 355990792Sgshapiro to->q_state = QS_RETRY; 356090792Sgshapiro } 356164562Sgshapiro continue; 356238032Speter } 3563363466Sgshapiro#endif /* STARTTLS */ 356464562Sgshapiro 356590792Sgshapiro i = smtprcpt(to, m, mci, e, ctladdr, xstart); 3566363466Sgshapiro#if PIPELINING 356790792Sgshapiro if (i == EX_OK && 356890792Sgshapiro bitset(MCIF_PIPELINED, mci->mci_flags)) 356964562Sgshapiro { 357090792Sgshapiro /* 357190792Sgshapiro ** Add new element to list of 357290792Sgshapiro ** recipients for pipelining. 357390792Sgshapiro */ 357490792Sgshapiro 357590792Sgshapiro to->q_pchain = NULL; 357690792Sgshapiro if (mci->mci_nextaddr == NULL) 357790792Sgshapiro mci->mci_nextaddr = to; 357890792Sgshapiro if (pchain == NULL) 357990792Sgshapiro pchain = to; 358090792Sgshapiro else 358190792Sgshapiro { 358290792Sgshapiro pchain->q_pchain = to; 358390792Sgshapiro pchain = pchain->q_pchain; 358490792Sgshapiro } 358564562Sgshapiro } 3586363466Sgshapiro#endif /* PIPELINING */ 358790792Sgshapiro if (i != EX_OK) 358838032Speter { 358990792Sgshapiro markfailure(e, to, mci, i, false); 359098841Sgshapiro giveresponse(i, to->q_status, m, mci, 359190792Sgshapiro ctladdr, xstart, e, to); 359290792Sgshapiro if (i == EX_TEMPFAIL) 359390792Sgshapiro to->q_state = QS_RETRY; 359438032Speter } 359538032Speter } 359638032Speter 359790792Sgshapiro /* No recipients in list and no missing responses? */ 359890792Sgshapiro if (tobuf[0] == '\0' 3599363466Sgshapiro#if PIPELINING 3600173340Sgshapiro && bitset(MCIF_PIPELINED, mci->mci_flags) 360190792Sgshapiro && mci->mci_nextaddr == NULL 3602363466Sgshapiro#endif 360390792Sgshapiro ) 360438032Speter { 360538032Speter rcode = EX_OK; 360638032Speter e->e_to = NULL; 360738032Speter if (bitset(MCIF_CACHED, mci->mci_flags)) 360838032Speter smtprset(m, mci, e); 360938032Speter } 361038032Speter else 361138032Speter { 361238032Speter e->e_to = tobuf + 1; 361390792Sgshapiro rcode = smtpdata(m, mci, e, ctladdr, xstart); 361438032Speter } 361538032Speter } 361664562Sgshapiro if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 361738032Speter { 361838032Speter /* try next MX site */ 361938032Speter goto tryhost; 362038032Speter } 362138032Speter } 362238032Speter#if NAMED_BIND 362338032Speter if (ConfigLevel < 2) 362438032Speter _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 3625363466Sgshapiro#endif 362638032Speter 362738032Speter if (tTd(62, 1)) 362838032Speter checkfds("after delivery"); 362938032Speter 363038032Speter /* 363138032Speter ** Do final status disposal. 363238032Speter ** We check for something in tobuf for the SMTP case. 363338032Speter ** If we got a temporary failure, arrange to queue the 363438032Speter ** addressees. 363538032Speter */ 363638032Speter 363738032Speter give_up: 363838032Speter if (bitnset(M_LMTP, m->m_flags)) 363938032Speter { 364038032Speter lmtp_rcode = rcode; 364138032Speter tobuf[0] = '\0'; 364290792Sgshapiro anyok = false; 364390792Sgshapiro strsize = 0; 364438032Speter } 364538032Speter else 364638032Speter anyok = rcode == EX_OK; 364738032Speter 364838032Speter for (to = tochain; to != NULL; to = to->q_tchain) 364938032Speter { 365038032Speter /* see if address already marked */ 365164562Sgshapiro if (!QS_IS_OK(to->q_state)) 365238032Speter continue; 365338032Speter 365438032Speter /* if running LMTP, get the status for each address */ 365538032Speter if (bitnset(M_LMTP, m->m_flags)) 365638032Speter { 365738032Speter if (lmtp_rcode == EX_OK) 365838032Speter rcode = smtpgetstat(m, mci, e); 365938032Speter if (rcode == EX_OK) 366038032Speter { 366190792Sgshapiro strsize += sm_strlcat2(tobuf + strsize, ",", 366290792Sgshapiro to->q_paddr, 366390792Sgshapiro tobufsize - strsize); 366490792Sgshapiro SM_ASSERT(strsize < tobufsize); 366590792Sgshapiro anyok = true; 366638032Speter } 366738032Speter else 366838032Speter { 366938032Speter e->e_to = to->q_paddr; 367090792Sgshapiro markfailure(e, to, mci, rcode, true); 367164562Sgshapiro giveresponse(rcode, to->q_status, m, mci, 367290792Sgshapiro ctladdr, xstart, e, to); 367338032Speter e->e_to = tobuf + 1; 367438032Speter continue; 367538032Speter } 367638032Speter } 367738032Speter else 367838032Speter { 367938032Speter /* mark bad addresses */ 368038032Speter if (rcode != EX_OK) 368138032Speter { 368238032Speter if (goodmxfound && rcode == EX_NOHOST) 368338032Speter rcode = EX_TEMPFAIL; 368490792Sgshapiro markfailure(e, to, mci, rcode, true); 368538032Speter continue; 368638032Speter } 368738032Speter } 368838032Speter 368938032Speter /* successful delivery */ 369064562Sgshapiro to->q_state = QS_SENT; 369138032Speter to->q_statdate = curtime(); 369238032Speter e->e_nsent++; 369364562Sgshapiro 369464562Sgshapiro /* 369564562Sgshapiro ** Checkpoint the send list every few addresses 369664562Sgshapiro */ 369764562Sgshapiro 369866494Sgshapiro if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 369964562Sgshapiro { 370090792Sgshapiro queueup(e, false, false); 370164562Sgshapiro e->e_nsent = 0; 370264562Sgshapiro } 370364562Sgshapiro 370438032Speter if (bitnset(M_LOCALMAILER, m->m_flags) && 370538032Speter bitset(QPINGONSUCCESS, to->q_flags)) 370638032Speter { 370738032Speter to->q_flags |= QDELIVERED; 370838032Speter to->q_status = "2.1.5"; 370990792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 371090792Sgshapiro "%s... Successfully delivered\n", 371190792Sgshapiro to->q_paddr); 371238032Speter } 371338032Speter else if (bitset(QPINGONSUCCESS, to->q_flags) && 371438032Speter bitset(QPRIMARY, to->q_flags) && 371538032Speter !bitset(MCIF_DSN, mci->mci_flags)) 371638032Speter { 371738032Speter to->q_flags |= QRELAYED; 371890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 371990792Sgshapiro "%s... relayed; expect no further notifications\n", 372090792Sgshapiro to->q_paddr); 372138032Speter } 372290792Sgshapiro else if (IS_DLVR_NOTIFY(e) && 372390792Sgshapiro !bitset(MCIF_DLVR_BY, mci->mci_flags) && 372490792Sgshapiro bitset(QPRIMARY, to->q_flags) && 372590792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 372690792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 372790792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 372890792Sgshapiro bitset(QPINGONDELAY, to->q_flags))) 372990792Sgshapiro { 373090792Sgshapiro /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 373190792Sgshapiro to->q_flags |= QBYNRELAY; 373290792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 373390792Sgshapiro "%s... Deliver-by notify: relayed\n", 373490792Sgshapiro to->q_paddr); 373590792Sgshapiro } 373690792Sgshapiro else if (IS_DLVR_TRACE(e) && 373790792Sgshapiro (!bitset(QHASNOTIFY, to->q_flags) || 373890792Sgshapiro bitset(QPINGONSUCCESS, to->q_flags) || 373990792Sgshapiro bitset(QPINGONFAILURE, to->q_flags) || 374090792Sgshapiro bitset(QPINGONDELAY, to->q_flags)) && 374190792Sgshapiro bitset(QPRIMARY, to->q_flags)) 374290792Sgshapiro { 374390792Sgshapiro /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 374490792Sgshapiro to->q_flags |= QBYTRACE; 374590792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 374690792Sgshapiro "%s... Deliver-By trace: relayed\n", 374790792Sgshapiro to->q_paddr); 374890792Sgshapiro } 374938032Speter } 375038032Speter 375138032Speter if (bitnset(M_LMTP, m->m_flags)) 375238032Speter { 375338032Speter /* 375438032Speter ** Global information applies to the last recipient only; 375538032Speter ** clear it out to avoid bogus errors. 375638032Speter */ 375738032Speter 375838032Speter rcode = EX_OK; 375938032Speter e->e_statmsg = NULL; 376038032Speter 376138032Speter /* reset the mci state for the next transaction */ 376290792Sgshapiro if (mci != NULL && 376390792Sgshapiro (mci->mci_state == MCIS_MAIL || 376490792Sgshapiro mci->mci_state == MCIS_RCPT || 376590792Sgshapiro mci->mci_state == MCIS_DATA)) 3766125820Sgshapiro { 376738032Speter mci->mci_state = MCIS_OPEN; 3768125820Sgshapiro SmtpPhase = mci->mci_phase = "idle"; 3769125820Sgshapiro sm_setproctitle(true, e, "%s: %s", CurHostName, 3770125820Sgshapiro mci->mci_phase); 3771125820Sgshapiro } 377238032Speter } 377338032Speter 377438032Speter if (tobuf[0] != '\0') 377590792Sgshapiro { 3776285229Sgshapiro giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, NULL); 377790792Sgshapiro#if 0 377890792Sgshapiro /* 377990792Sgshapiro ** This code is disabled for now because I am not 378090792Sgshapiro ** sure that copying status from the first recipient 378190792Sgshapiro ** to all non-status'ed recipients is a good idea. 378290792Sgshapiro */ 378390792Sgshapiro 378490792Sgshapiro if (tochain->q_message != NULL && 378590792Sgshapiro !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 378690792Sgshapiro { 378790792Sgshapiro for (to = tochain->q_tchain; to != NULL; 378890792Sgshapiro to = to->q_tchain) 378990792Sgshapiro { 379090792Sgshapiro /* see if address already marked */ 379190792Sgshapiro if (QS_IS_QUEUEUP(to->q_state) && 379290792Sgshapiro to->q_message == NULL) 379390792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 379490792Sgshapiro tochain->q_message); 379590792Sgshapiro } 379690792Sgshapiro } 379790792Sgshapiro#endif /* 0 */ 379890792Sgshapiro } 379938032Speter if (anyok) 380090792Sgshapiro markstats(e, tochain, STATS_NORMAL); 380138032Speter mci_store_persistent(mci); 380238032Speter 3803363466Sgshapiro#if _FFR_OCC 3804363466Sgshapiro /* 3805363466Sgshapiro ** HACK: this is NOT the right place to "close" a connection! 3806363466Sgshapiro ** use smtpquit? 3807363466Sgshapiro ** add a flag to mci to indicate that rate/conc. was increased? 3808363466Sgshapiro */ 3809363466Sgshapiro 3810363466Sgshapiro if (clever) 3811363466Sgshapiro { 3812363466Sgshapiro extern SOCKADDR CurHostAddr; 3813363466Sgshapiro 3814363466Sgshapiro /* check family... {} */ 3815363466Sgshapiro /* r = anynet_pton(AF_INET, p, dst); */ 3816363466Sgshapiro occ_close(e, mci, host, &CurHostAddr); 3817363466Sgshapiro } 3818363466Sgshapiro#endif /* _FFR_OCC */ 3819363466Sgshapiro 382090792Sgshapiro /* Some recipients were tempfailed, try them on the next host */ 382190792Sgshapiro if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 382290792Sgshapiro { 382390792Sgshapiro /* try next MX site */ 382490792Sgshapiro goto tryhost; 382590792Sgshapiro } 382690792Sgshapiro 382738032Speter /* now close the connection */ 382838032Speter if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 382938032Speter !bitset(MCIF_CACHED, mci->mci_flags)) 383038032Speter smtpquit(m, mci, e); 383138032Speter 383290792Sgshapirocleanup: ; 383390792Sgshapiro } 383490792Sgshapiro SM_FINALLY 383590792Sgshapiro { 383690792Sgshapiro /* 383790792Sgshapiro ** Restore state and return. 383890792Sgshapiro */ 383938032Speter#if XDEBUG 384038032Speter char wbuf[MAXLINE]; 384138032Speter 384238032Speter /* make absolutely certain 0, 1, and 2 are in use */ 3843168515Sgshapiro (void) sm_snprintf(wbuf, sizeof(wbuf), 384490792Sgshapiro "%s... end of deliver(%s)", 384590792Sgshapiro e->e_to == NULL ? "NO-TO-LIST" 384690792Sgshapiro : shortenstring(e->e_to, 384790792Sgshapiro MAXSHORTSTR), 384890792Sgshapiro m->m_name); 384938032Speter checkfd012(wbuf); 385064562Sgshapiro#endif /* XDEBUG */ 385138032Speter 385290792Sgshapiro errno = 0; 385390792Sgshapiro 385490792Sgshapiro /* 385590792Sgshapiro ** It was originally necessary to set macro 'g' to NULL 385690792Sgshapiro ** because it previously pointed to an auto buffer. 385790792Sgshapiro ** We don't do this any more, so this may be unnecessary. 385890792Sgshapiro */ 385990792Sgshapiro 386090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 386190792Sgshapiro e->e_to = NULL; 386290792Sgshapiro } 386390792Sgshapiro SM_END_TRY 386464562Sgshapiro return rcode; 386538032Speter} 386664562Sgshapiro 386790792Sgshapiro/* 386838032Speter** MARKFAILURE -- mark a failure on a specific address. 386938032Speter** 387038032Speter** Parameters: 387138032Speter** e -- the envelope we are sending. 387238032Speter** q -- the address to mark. 387338032Speter** mci -- mailer connection information. 387438032Speter** rcode -- the code signifying the particular failure. 387564562Sgshapiro** ovr -- override an existing code? 387638032Speter** 387738032Speter** Returns: 387838032Speter** none. 387938032Speter** 388038032Speter** Side Effects: 388138032Speter** marks the address (and possibly the envelope) with the 388238032Speter** failure so that an error will be returned or 388338032Speter** the message will be queued, as appropriate. 388438032Speter*/ 388538032Speter 388690792Sgshapirovoid 388764562Sgshapiromarkfailure(e, q, mci, rcode, ovr) 388838032Speter register ENVELOPE *e; 388938032Speter register ADDRESS *q; 389038032Speter register MCI *mci; 389138032Speter int rcode; 389264562Sgshapiro bool ovr; 389338032Speter{ 389490792Sgshapiro int save_errno = errno; 389564562Sgshapiro char *status = NULL; 389664562Sgshapiro char *rstatus = NULL; 389738032Speter 389838032Speter switch (rcode) 389938032Speter { 390038032Speter case EX_OK: 390138032Speter break; 390238032Speter 390338032Speter case EX_TEMPFAIL: 390438032Speter case EX_IOERR: 390538032Speter case EX_OSERR: 390664562Sgshapiro q->q_state = QS_QUEUEUP; 390738032Speter break; 390838032Speter 390938032Speter default: 391064562Sgshapiro q->q_state = QS_BADADDR; 391138032Speter break; 391238032Speter } 391338032Speter 391438032Speter /* find most specific error code possible */ 391538032Speter if (mci != NULL && mci->mci_status != NULL) 391638032Speter { 391790792Sgshapiro status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 391838032Speter if (mci->mci_rstatus != NULL) 391990792Sgshapiro rstatus = sm_rpool_strdup_x(e->e_rpool, 392090792Sgshapiro mci->mci_rstatus); 392138032Speter } 392238032Speter else if (e->e_status != NULL) 392338032Speter { 392464562Sgshapiro status = e->e_status; 392538032Speter } 392638032Speter else 392738032Speter { 392838032Speter switch (rcode) 392938032Speter { 393038032Speter case EX_USAGE: 393164562Sgshapiro status = "5.5.4"; 393238032Speter break; 393338032Speter 393438032Speter case EX_DATAERR: 393564562Sgshapiro status = "5.5.2"; 393638032Speter break; 393738032Speter 393838032Speter case EX_NOUSER: 393964562Sgshapiro status = "5.1.1"; 394038032Speter break; 394138032Speter 394238032Speter case EX_NOHOST: 394364562Sgshapiro status = "5.1.2"; 394438032Speter break; 394538032Speter 394638032Speter case EX_NOINPUT: 394738032Speter case EX_CANTCREAT: 394838032Speter case EX_NOPERM: 394964562Sgshapiro status = "5.3.0"; 395038032Speter break; 395138032Speter 395238032Speter case EX_UNAVAILABLE: 395338032Speter case EX_SOFTWARE: 395438032Speter case EX_OSFILE: 395538032Speter case EX_PROTOCOL: 395638032Speter case EX_CONFIG: 395764562Sgshapiro status = "5.5.0"; 395838032Speter break; 395938032Speter 396038032Speter case EX_OSERR: 396138032Speter case EX_IOERR: 396264562Sgshapiro status = "4.5.0"; 396338032Speter break; 396438032Speter 396538032Speter case EX_TEMPFAIL: 396664562Sgshapiro status = "4.2.0"; 396738032Speter break; 396838032Speter } 396938032Speter } 397038032Speter 397164562Sgshapiro /* new status? */ 397264562Sgshapiro if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 397364562Sgshapiro *q->q_status == '\0' || *q->q_status < *status)) 397464562Sgshapiro { 397564562Sgshapiro q->q_status = status; 397664562Sgshapiro q->q_rstatus = rstatus; 397764562Sgshapiro } 397838032Speter if (rcode != EX_OK && q->q_rstatus == NULL && 397938032Speter q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 398090792Sgshapiro sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 398138032Speter { 398264562Sgshapiro char buf[16]; 398338032Speter 3984168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%d", rcode); 398590792Sgshapiro q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 398638032Speter } 398764562Sgshapiro 398864562Sgshapiro q->q_statdate = curtime(); 398964562Sgshapiro if (CurHostName != NULL && CurHostName[0] != '\0' && 399064562Sgshapiro mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 399190792Sgshapiro q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 399290792Sgshapiro 399390792Sgshapiro /* restore errno */ 399490792Sgshapiro errno = save_errno; 399538032Speter} 399690792Sgshapiro/* 399738032Speter** ENDMAILER -- Wait for mailer to terminate. 399838032Speter** 399938032Speter** We should never get fatal errors (e.g., segmentation 400038032Speter** violation), so we report those specially. For other 400138032Speter** errors, we choose a status message (into statmsg), 400238032Speter** and if it represents an error, we print it. 400338032Speter** 400438032Speter** Parameters: 400580785Sgshapiro** mci -- the mailer connection info. 400638032Speter** e -- the current envelope. 400738032Speter** pv -- the parameter vector that invoked the mailer 400838032Speter** (for error messages). 400938032Speter** 401038032Speter** Returns: 401138032Speter** exit code of mailer. 401238032Speter** 401338032Speter** Side Effects: 401438032Speter** none. 401538032Speter*/ 401638032Speter 401764562Sgshapirostatic jmp_buf EndWaitTimeout; 401864562Sgshapiro 401964562Sgshapirostatic void 4020141858Sgshapiroendwaittimeout(ignore) 4021141858Sgshapiro int ignore; 402264562Sgshapiro{ 402377349Sgshapiro /* 402477349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 402577349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 402677349Sgshapiro ** DOING. 402777349Sgshapiro */ 402877349Sgshapiro 402964562Sgshapiro errno = ETIMEDOUT; 403064562Sgshapiro longjmp(EndWaitTimeout, 1); 403164562Sgshapiro} 403264562Sgshapiro 403338032Speterint 403438032Speterendmailer(mci, e, pv) 403538032Speter register MCI *mci; 403638032Speter register ENVELOPE *e; 403738032Speter char **pv; 403838032Speter{ 403938032Speter int st; 404064562Sgshapiro int save_errno = errno; 404164562Sgshapiro char buf[MAXLINE]; 404290792Sgshapiro SM_EVENT *ev = NULL; 404338032Speter 404464562Sgshapiro 404538032Speter mci_unlock_host(mci); 404638032Speter 404777349Sgshapiro /* close output to mailer */ 404877349Sgshapiro if (mci->mci_out != NULL) 4049141858Sgshapiro { 405090792Sgshapiro (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 4051141858Sgshapiro mci->mci_out = NULL; 4052141858Sgshapiro } 405377349Sgshapiro 405477349Sgshapiro /* copy any remaining input to transcript */ 405577349Sgshapiro if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 405677349Sgshapiro e->e_xfp != NULL) 405777349Sgshapiro { 4058168515Sgshapiro while (sfgets(buf, sizeof(buf), mci->mci_in, 405990792Sgshapiro TimeOuts.to_quit, "Draining Input") != NULL) 406090792Sgshapiro (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 406177349Sgshapiro } 406277349Sgshapiro 406364562Sgshapiro#if SASL 406490792Sgshapiro /* close SASL connection */ 406564562Sgshapiro if (bitset(MCIF_AUTHACT, mci->mci_flags)) 406664562Sgshapiro { 406764562Sgshapiro sasl_dispose(&mci->mci_conn); 406864562Sgshapiro mci->mci_flags &= ~MCIF_AUTHACT; 406964562Sgshapiro } 407064562Sgshapiro#endif /* SASL */ 407164562Sgshapiro 407264562Sgshapiro#if STARTTLS 407364562Sgshapiro /* shutdown TLS */ 407464562Sgshapiro (void) endtlsclt(mci); 4075363466Sgshapiro#endif 407664562Sgshapiro 407764562Sgshapiro /* now close the input */ 407838032Speter if (mci->mci_in != NULL) 4079141858Sgshapiro { 408090792Sgshapiro (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 4081141858Sgshapiro mci->mci_in = NULL; 4082141858Sgshapiro } 408338032Speter mci->mci_state = MCIS_CLOSED; 408438032Speter 408564562Sgshapiro errno = save_errno; 408664562Sgshapiro 408738032Speter /* in the IPC case there is nothing to wait for */ 408838032Speter if (mci->mci_pid == 0) 408964562Sgshapiro return EX_OK; 409038032Speter 409164562Sgshapiro /* put a timeout around the wait */ 409264562Sgshapiro if (mci->mci_mailer->m_wait > 0) 409364562Sgshapiro { 409464562Sgshapiro if (setjmp(EndWaitTimeout) == 0) 409590792Sgshapiro ev = sm_setevent(mci->mci_mailer->m_wait, 409690792Sgshapiro endwaittimeout, 0); 409764562Sgshapiro else 409864562Sgshapiro { 409966494Sgshapiro syserr("endmailer %s: wait timeout (%ld)", 410064562Sgshapiro mci->mci_mailer->m_name, 410166494Sgshapiro (long) mci->mci_mailer->m_wait); 410264562Sgshapiro return EX_TEMPFAIL; 410364562Sgshapiro } 410464562Sgshapiro } 410538032Speter 410664562Sgshapiro /* wait for the mailer process, collect status */ 410738032Speter st = waitfor(mci->mci_pid); 410864562Sgshapiro save_errno = errno; 410964562Sgshapiro if (ev != NULL) 411090792Sgshapiro sm_clrevent(ev); 411164562Sgshapiro errno = save_errno; 411264562Sgshapiro 411338032Speter if (st == -1) 411438032Speter { 411538032Speter syserr("endmailer %s: wait", mci->mci_mailer->m_name); 411664562Sgshapiro return EX_SOFTWARE; 411738032Speter } 411838032Speter 411938032Speter if (WIFEXITED(st)) 412038032Speter { 412138032Speter /* normal death -- return status */ 412238032Speter return (WEXITSTATUS(st)); 412338032Speter } 412438032Speter 412538032Speter /* it died a horrid death */ 412664562Sgshapiro syserr("451 4.3.0 mailer %s died with signal %d%s", 412764562Sgshapiro mci->mci_mailer->m_name, WTERMSIG(st), 412864562Sgshapiro WCOREDUMP(st) ? " (core dumped)" : 412964562Sgshapiro (WIFSTOPPED(st) ? " (stopped)" : "")); 413038032Speter 413138032Speter /* log the arguments */ 413238032Speter if (pv != NULL && e->e_xfp != NULL) 413338032Speter { 413438032Speter register char **av; 413538032Speter 413690792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 413738032Speter for (av = pv; *av != NULL; av++) 413890792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 413990792Sgshapiro *av); 414090792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 414138032Speter } 414238032Speter 414338032Speter ExitStat = EX_TEMPFAIL; 414464562Sgshapiro return EX_TEMPFAIL; 414538032Speter} 414690792Sgshapiro/* 414738032Speter** GIVERESPONSE -- Interpret an error response from a mailer 414838032Speter** 414938032Speter** Parameters: 415064562Sgshapiro** status -- the status code from the mailer (high byte 415138032Speter** only; core dumps must have been taken care of 415238032Speter** already). 415364562Sgshapiro** dsn -- the DSN associated with the address, if any. 415438032Speter** m -- the mailer info for this mailer. 415538032Speter** mci -- the mailer connection info -- can be NULL if the 415638032Speter** response is given before the connection is made. 415738032Speter** ctladdr -- the controlling address for the recipient 415838032Speter** address(es). 415938032Speter** xstart -- the transaction start time, for computing 416038032Speter** transaction delays. 416138032Speter** e -- the current envelope. 416290792Sgshapiro** to -- the current recipient (NULL if none). 416338032Speter** 416438032Speter** Returns: 416538032Speter** none. 416638032Speter** 416738032Speter** Side Effects: 416838032Speter** Errors may be incremented. 416938032Speter** ExitStat may be set. 417038032Speter*/ 417138032Speter 417238032Spetervoid 417390792Sgshapirogiveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 417464562Sgshapiro int status; 417564562Sgshapiro char *dsn; 417638032Speter register MAILER *m; 417738032Speter register MCI *mci; 417838032Speter ADDRESS *ctladdr; 417938032Speter time_t xstart; 418038032Speter ENVELOPE *e; 418190792Sgshapiro ADDRESS *to; 418238032Speter{ 418338032Speter register const char *statmsg; 418464562Sgshapiro int errnum = errno; 418564562Sgshapiro int off = 4; 418690792Sgshapiro bool usestat = false; 418764562Sgshapiro char dsnbuf[ENHSCLEN]; 418838032Speter char buf[MAXLINE]; 418990792Sgshapiro char *exmsg; 419038032Speter 419138032Speter if (e == NULL) 4192159609Sgshapiro { 419338032Speter syserr("giveresponse: null envelope"); 4194159609Sgshapiro /* NOTREACHED */ 4195159609Sgshapiro SM_ASSERT(0); 4196159609Sgshapiro } 419738032Speter 419838032Speter /* 419938032Speter ** Compute status message from code. 420038032Speter */ 420138032Speter 420290792Sgshapiro exmsg = sm_sysexmsg(status); 420364562Sgshapiro if (status == 0) 420438032Speter { 420564562Sgshapiro statmsg = "250 2.0.0 Sent"; 420638032Speter if (e->e_statmsg != NULL) 420738032Speter { 4208168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", 420990792Sgshapiro statmsg, 421090792Sgshapiro shortenstring(e->e_statmsg, 403)); 421138032Speter statmsg = buf; 421238032Speter } 421338032Speter } 421490792Sgshapiro else if (exmsg == NULL) 421538032Speter { 4216168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 421790792Sgshapiro "554 5.3.0 unknown mailer error %d", 421890792Sgshapiro status); 421964562Sgshapiro status = EX_UNAVAILABLE; 422038032Speter statmsg = buf; 422190792Sgshapiro usestat = true; 422238032Speter } 422364562Sgshapiro else if (status == EX_TEMPFAIL) 422438032Speter { 422538032Speter char *bp = buf; 422638032Speter 422790792Sgshapiro (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 422838032Speter bp += strlen(bp); 422938032Speter#if NAMED_BIND 423038032Speter if (h_errno == TRY_AGAIN) 423190792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 423238032Speter else 423364562Sgshapiro#endif /* NAMED_BIND */ 423438032Speter { 423564562Sgshapiro if (errnum != 0) 423690792Sgshapiro statmsg = sm_errstring(errnum); 423738032Speter else 423838032Speter statmsg = SmtpError; 423938032Speter } 424038032Speter if (statmsg != NULL && statmsg[0] != '\0') 424164562Sgshapiro { 424264562Sgshapiro switch (errnum) 424364562Sgshapiro { 424464562Sgshapiro#ifdef ENETDOWN 424564562Sgshapiro case ENETDOWN: /* Network is down */ 4246363466Sgshapiro#endif 424764562Sgshapiro#ifdef ENETUNREACH 424864562Sgshapiro case ENETUNREACH: /* Network is unreachable */ 4249363466Sgshapiro#endif 425064562Sgshapiro#ifdef ENETRESET 425164562Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 4252363466Sgshapiro#endif 425364562Sgshapiro#ifdef ECONNABORTED 425464562Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 4255363466Sgshapiro#endif 425664562Sgshapiro#ifdef EHOSTDOWN 425764562Sgshapiro case EHOSTDOWN: /* Host is down */ 4258363466Sgshapiro#endif 425964562Sgshapiro#ifdef EHOSTUNREACH 426064562Sgshapiro case EHOSTUNREACH: /* No route to host */ 4261363466Sgshapiro#endif 4262141858Sgshapiro if (mci != NULL && mci->mci_host != NULL) 426364562Sgshapiro { 426490792Sgshapiro (void) sm_strlcpyn(bp, 426590792Sgshapiro SPACELEFT(buf, bp), 426690792Sgshapiro 2, ": ", 426790792Sgshapiro mci->mci_host); 426864562Sgshapiro bp += strlen(bp); 426964562Sgshapiro } 427064562Sgshapiro break; 427164562Sgshapiro } 427290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 427390792Sgshapiro statmsg); 4274363466Sgshapiro#if DANE 4275363466Sgshapiro if (errnum == 0 && SmtpError[0] != '\0' && 4276363466Sgshapiro h_errno == TRY_AGAIN && 4277363466Sgshapiro mci->mci_exitstat == EX_TEMPFAIL) 4278363466Sgshapiro { 4279363466Sgshapiro (void) sm_strlcat(bp, SmtpError, 4280363466Sgshapiro SPACELEFT(buf, bp)); 4281363466Sgshapiro bp += strlen(bp); 4282363466Sgshapiro } 4283363466Sgshapiro#endif /* DANE */ 428490792Sgshapiro usestat = true; 428564562Sgshapiro } 428638032Speter statmsg = buf; 428738032Speter } 428838032Speter#if NAMED_BIND 428964562Sgshapiro else if (status == EX_NOHOST && h_errno != 0) 429038032Speter { 429190792Sgshapiro statmsg = sm_errstring(h_errno + E_DNSBASE); 4292168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1, 429390792Sgshapiro statmsg); 429438032Speter statmsg = buf; 429590792Sgshapiro usestat = true; 429638032Speter } 429764562Sgshapiro#endif /* NAMED_BIND */ 429838032Speter else 429938032Speter { 430090792Sgshapiro statmsg = exmsg; 430164562Sgshapiro if (*statmsg++ == ':' && errnum != 0) 430238032Speter { 4303168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg, 430490792Sgshapiro sm_errstring(errnum)); 430538032Speter statmsg = buf; 430690792Sgshapiro usestat = true; 430738032Speter } 430894334Sgshapiro else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 430994334Sgshapiro { 4310168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg, 431194334Sgshapiro shortenstring(e->e_statmsg, 403)); 431294334Sgshapiro statmsg = buf; 431394334Sgshapiro usestat = true; 431494334Sgshapiro } 431538032Speter } 431638032Speter 431738032Speter /* 431838032Speter ** Print the message as appropriate 431938032Speter */ 432038032Speter 432164562Sgshapiro if (status == EX_OK || status == EX_TEMPFAIL) 432238032Speter { 432338032Speter extern char MsgBuf[]; 432438032Speter 432564562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0) 432664562Sgshapiro { 432764562Sgshapiro if (dsn == NULL) 432864562Sgshapiro { 4329168515Sgshapiro (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 433090792Sgshapiro "%.*s", off, statmsg + 4); 433164562Sgshapiro dsn = dsnbuf; 433264562Sgshapiro } 433364562Sgshapiro off += 5; 433464562Sgshapiro } 433564562Sgshapiro else 433664562Sgshapiro { 433764562Sgshapiro off = 4; 433864562Sgshapiro } 433964562Sgshapiro message("%s", statmsg + off); 434064562Sgshapiro if (status == EX_TEMPFAIL && e->e_xfp != NULL) 434190792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 434290792Sgshapiro &MsgBuf[4]); 434338032Speter } 434438032Speter else 434538032Speter { 434664562Sgshapiro char mbuf[ENHSCLEN + 4]; 434738032Speter 434838032Speter Errors++; 434964562Sgshapiro if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 4350168515Sgshapiro off < sizeof(mbuf) - 4) 435164562Sgshapiro { 435264562Sgshapiro if (dsn == NULL) 435364562Sgshapiro { 4354168515Sgshapiro (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 435590792Sgshapiro "%.*s", off, statmsg + 4); 435664562Sgshapiro dsn = dsnbuf; 435764562Sgshapiro } 435864562Sgshapiro off += 5; 435990792Sgshapiro 436090792Sgshapiro /* copy only part of statmsg to mbuf */ 436190792Sgshapiro (void) sm_strlcpy(mbuf, statmsg, off); 4362168515Sgshapiro (void) sm_strlcat(mbuf, " %s", sizeof(mbuf)); 436364562Sgshapiro } 436464562Sgshapiro else 436564562Sgshapiro { 436664562Sgshapiro dsnbuf[0] = '\0'; 4367168515Sgshapiro (void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s", 436890792Sgshapiro statmsg); 436964562Sgshapiro off = 4; 437064562Sgshapiro } 437164562Sgshapiro usrerr(mbuf, &statmsg[off]); 437238032Speter } 437338032Speter 437438032Speter /* 437538032Speter ** Final cleanup. 4376285229Sgshapiro ** Log a record of the transaction. Compute the new ExitStat 4377285229Sgshapiro ** -- if we already had an error, stick with that. 437838032Speter */ 437938032Speter 438038032Speter if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 438164562Sgshapiro LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 4382285229Sgshapiro logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e, to, status); 438338032Speter 438438032Speter if (tTd(11, 2)) 438590792Sgshapiro sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 438690792Sgshapiro status, 438790792Sgshapiro dsn == NULL ? "<NULL>" : dsn, 438890792Sgshapiro e->e_message == NULL ? "<NULL>" : e->e_message, 438990792Sgshapiro errnum); 439038032Speter 439164562Sgshapiro if (status != EX_TEMPFAIL) 439264562Sgshapiro setstat(status); 439364562Sgshapiro if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 439490792Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 439590792Sgshapiro if (status != EX_OK && to != NULL && to->q_message == NULL) 439638032Speter { 439790792Sgshapiro if (!usestat && e->e_message != NULL) 439890792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 439990792Sgshapiro e->e_message); 440090792Sgshapiro else 440190792Sgshapiro to->q_message = sm_rpool_strdup_x(e->e_rpool, 440290792Sgshapiro statmsg + off); 440338032Speter } 440438032Speter errno = 0; 440573188Sgshapiro SM_SET_H_ERRNO(0); 440638032Speter} 440790792Sgshapiro/* 440838032Speter** LOGDELIVERY -- log the delivery in the system log 440938032Speter** 441038032Speter** Care is taken to avoid logging lines that are too long, because 441138032Speter** some versions of syslog have an unfortunate proclivity for core 441238032Speter** dumping. This is a hack, to be sure, that is at best empirical. 441338032Speter** 441438032Speter** Parameters: 441538032Speter** m -- the mailer info. Can be NULL for initial queue. 441638032Speter** mci -- the mailer connection info -- can be NULL if the 441764562Sgshapiro** log is occurring when no connection is active. 441864562Sgshapiro** dsn -- the DSN attached to the status. 441964562Sgshapiro** status -- the message to print for the status. 442038032Speter** ctladdr -- the controlling address for the to list. 442138032Speter** xstart -- the transaction start time, used for 442238032Speter** computing transaction delay. 442338032Speter** e -- the current envelope. 4424285229Sgshapiro** to -- the current recipient (NULL if none). 4425285229Sgshapiro** rcode -- status code 442638032Speter** 442738032Speter** Returns: 442838032Speter** none 442938032Speter** 443038032Speter** Side Effects: 443138032Speter** none 443238032Speter*/ 443338032Speter 443438032Spetervoid 4435285229Sgshapirologdelivery(m, mci, dsn, status, ctladdr, xstart, e, to, rcode) 443638032Speter MAILER *m; 443738032Speter register MCI *mci; 443864562Sgshapiro char *dsn; 443964562Sgshapiro const char *status; 444038032Speter ADDRESS *ctladdr; 444138032Speter time_t xstart; 444238032Speter register ENVELOPE *e; 4443285229Sgshapiro ADDRESS *to; 4444285229Sgshapiro int rcode; 444538032Speter{ 444638032Speter register char *bp; 444738032Speter register char *p; 444838032Speter int l; 444990792Sgshapiro time_t now = curtime(); 445038032Speter char buf[1024]; 445138032Speter 445264562Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 445338032Speter /* ctladdr: max 106 bytes */ 445438032Speter bp = buf; 445538032Speter if (ctladdr != NULL) 445638032Speter { 445790792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 445890792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 445938032Speter bp += strlen(bp); 446038032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 446138032Speter { 446290792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 446390792Sgshapiro (int) ctladdr->q_uid, 446490792Sgshapiro (int) ctladdr->q_gid); 446538032Speter bp += strlen(bp); 446638032Speter } 446738032Speter } 446838032Speter 446938032Speter /* delay & xdelay: max 41 bytes */ 447090792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 447190792Sgshapiro pintvl(now - e->e_ctime, true)); 447238032Speter bp += strlen(bp); 447338032Speter 447438032Speter if (xstart != (time_t) 0) 447538032Speter { 447690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 447790792Sgshapiro pintvl(now - xstart, true)); 447838032Speter bp += strlen(bp); 447938032Speter } 448038032Speter 448138032Speter /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 448238032Speter if (m != NULL) 448338032Speter { 448490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 448590792Sgshapiro m->m_name); 448638032Speter bp += strlen(bp); 448738032Speter } 448838032Speter 4489285229Sgshapiro# if _FFR_LOG_MORE2 4490363466Sgshapiro LOG_MORE(buf, bp); 4491363466Sgshapiro# endif 4492285229Sgshapiro 449364562Sgshapiro /* pri: changes with each delivery attempt */ 449490792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 4495244833Sgshapiro PRT_NONNEGL(e->e_msgpriority)); 449664562Sgshapiro bp += strlen(bp); 449764562Sgshapiro 449838032Speter /* relay: max 66 bytes for IPv4 addresses */ 449938032Speter if (mci != NULL && mci->mci_host != NULL) 450038032Speter { 450138032Speter extern SOCKADDR CurHostAddr; 450238032Speter 450390792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 450490792Sgshapiro shortenstring(mci->mci_host, 40)); 450538032Speter bp += strlen(bp); 450638032Speter 450738032Speter if (CurHostAddr.sa.sa_family != 0) 450838032Speter { 450990792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 451090792Sgshapiro anynet_ntoa(&CurHostAddr)); 451138032Speter } 451238032Speter } 451390792Sgshapiro else if (strcmp(status, "quarantined") == 0) 451490792Sgshapiro { 451590792Sgshapiro if (e->e_quarmsg != NULL) 451690792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 451790792Sgshapiro ", quarantine=%s", 451890792Sgshapiro shortenstring(e->e_quarmsg, 40)); 451990792Sgshapiro } 452064562Sgshapiro else if (strcmp(status, "queued") != 0) 452138032Speter { 452238032Speter p = macvalue('h', e); 452338032Speter if (p != NULL && p[0] != '\0') 452438032Speter { 452590792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 452690792Sgshapiro ", relay=%s", shortenstring(p, 40)); 452738032Speter } 452838032Speter } 452938032Speter bp += strlen(bp); 453038032Speter 453164562Sgshapiro /* dsn */ 453264562Sgshapiro if (dsn != NULL && *dsn != '\0') 453364562Sgshapiro { 453490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 453590792Sgshapiro shortenstring(dsn, ENHSCLEN)); 453664562Sgshapiro bp += strlen(bp); 453764562Sgshapiro } 453838032Speter 4539363466Sgshapiro# if _FFR_LOG_NTRIES 4540147078Sgshapiro /* ntries */ 4541147078Sgshapiro if (e->e_ntries >= 0) 4542147078Sgshapiro { 4543147078Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4544147078Sgshapiro ", ntries=%d", e->e_ntries + 1); 4545147078Sgshapiro bp += strlen(bp); 4546147078Sgshapiro } 4547363466Sgshapiro# endif /* _FFR_LOG_NTRIES */ 4548147078Sgshapiro 454964562Sgshapiro# define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 455064562Sgshapiro# if (STATLEN) < 63 455164562Sgshapiro# undef STATLEN 455264562Sgshapiro# define STATLEN 63 455364562Sgshapiro# endif /* (STATLEN) < 63 */ 455464562Sgshapiro# if (STATLEN) > 203 455564562Sgshapiro# undef STATLEN 455664562Sgshapiro# define STATLEN 203 455764562Sgshapiro# endif /* (STATLEN) > 203 */ 455864562Sgshapiro 4559285229Sgshapiro /* 4560285229Sgshapiro ** Notes: 4561285229Sgshapiro ** per-rcpt status: to->q_rstatus 4562285229Sgshapiro ** global status: e->e_text 4563285229Sgshapiro ** 4564285229Sgshapiro ** We (re)use STATLEN here, is that a good choice? 4565285229Sgshapiro ** 4566285229Sgshapiro ** stat=Deferred: ... 4567285229Sgshapiro ** has sometimes the same text? 4568285229Sgshapiro ** 4569285229Sgshapiro ** Note: this doesn't show the stage at which the error happened. 4570285229Sgshapiro ** can/should we log that? 4571285229Sgshapiro ** XS_* in reply() basically encodes the state. 4572363466Sgshapiro ** 4573363466Sgshapiro ** Note: in some case the normal logging might show the same server 4574363466Sgshapiro ** reply - how to avoid that? 4575285229Sgshapiro */ 4576285229Sgshapiro 4577285229Sgshapiro /* only show errors */ 4578285229Sgshapiro if (rcode != EX_OK && to != NULL && to->q_rstatus != NULL && 4579285229Sgshapiro *to->q_rstatus != '\0') 4580285229Sgshapiro { 4581285229Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4582285229Sgshapiro ", reply=%s", 4583285229Sgshapiro shortenstring(to->q_rstatus, STATLEN)); 4584285229Sgshapiro bp += strlen(bp); 4585285229Sgshapiro } 4586285229Sgshapiro else if (rcode != EX_OK && e->e_text != NULL) 4587285229Sgshapiro { 4588285229Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4589285229Sgshapiro ", reply=%d %s%s%s", 4590285229Sgshapiro e->e_rcode, 4591285229Sgshapiro e->e_renhsc, 4592285229Sgshapiro (e->e_renhsc[0] != '\0') ? " " : "", 4593285229Sgshapiro shortenstring(e->e_text, STATLEN)); 4594285229Sgshapiro bp += strlen(bp); 4595285229Sgshapiro } 4596285229Sgshapiro 459738032Speter /* stat: max 210 bytes */ 4598168515Sgshapiro if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20))) 459938032Speter { 460038032Speter /* desperation move -- truncate data */ 4601168515Sgshapiro bp = buf + sizeof(buf) - ((STATLEN) + 17); 460290792Sgshapiro (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 460338032Speter bp += 3; 460438032Speter } 460538032Speter 460690792Sgshapiro (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 460738032Speter bp += strlen(bp); 460838032Speter 460990792Sgshapiro (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 461090792Sgshapiro SPACELEFT(buf, bp)); 461138032Speter 461238032Speter /* id, to: max 13 + TOBUFSIZE bytes */ 461338032Speter l = SYSLOG_BUFSIZE - 100 - strlen(buf); 461490792Sgshapiro if (l < 0) 461590792Sgshapiro l = 0; 461664562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 461790792Sgshapiro while (strlen(p) >= l) 461838032Speter { 461964562Sgshapiro register char *q; 462038032Speter 462164562Sgshapiro for (q = p + l; q > p; q--) 462264562Sgshapiro { 4623285229Sgshapiro /* XXX a comma in an address will break this! */ 462464562Sgshapiro if (*q == ',') 462564562Sgshapiro break; 462664562Sgshapiro } 462764562Sgshapiro if (p == q) 462864562Sgshapiro break; 462990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 463066494Sgshapiro (int) (++q - p), p, buf); 463138032Speter p = q; 463238032Speter } 463364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 463438032Speter 463564562Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 463638032Speter 463738032Speter l = SYSLOG_BUFSIZE - 85; 463890792Sgshapiro if (l < 0) 463990792Sgshapiro l = 0; 464064562Sgshapiro p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 464190792Sgshapiro while (strlen(p) >= l) 464238032Speter { 464364562Sgshapiro register char *q; 464438032Speter 464564562Sgshapiro for (q = p + l; q > p; q--) 464664562Sgshapiro { 464764562Sgshapiro if (*q == ',') 464864562Sgshapiro break; 464964562Sgshapiro } 465064562Sgshapiro if (p == q) 465164562Sgshapiro break; 465264562Sgshapiro 465390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 465466494Sgshapiro (int) (++q - p), p); 465538032Speter p = q; 465638032Speter } 465764562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 465838032Speter 465938032Speter if (ctladdr != NULL) 466038032Speter { 466138032Speter bp = buf; 466290792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 466390792Sgshapiro shortenstring(ctladdr->q_paddr, 83)); 466438032Speter bp += strlen(bp); 466538032Speter if (bitset(QGOODUID, ctladdr->q_flags)) 466638032Speter { 466790792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 466890792Sgshapiro ctladdr->q_uid, ctladdr->q_gid); 466938032Speter bp += strlen(bp); 467038032Speter } 467138032Speter sm_syslog(LOG_INFO, e->e_id, "%s", buf); 467238032Speter } 467338032Speter bp = buf; 467490792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 467590792Sgshapiro pintvl(now - e->e_ctime, true)); 467638032Speter bp += strlen(bp); 467738032Speter if (xstart != (time_t) 0) 467838032Speter { 467990792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 468090792Sgshapiro pintvl(now - xstart, true)); 468138032Speter bp += strlen(bp); 468238032Speter } 468338032Speter 468438032Speter if (m != NULL) 468538032Speter { 468690792Sgshapiro (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 468790792Sgshapiro m->m_name); 468838032Speter bp += strlen(bp); 468938032Speter } 469038032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 469138032Speter 469238032Speter buf[0] = '\0'; 469338032Speter bp = buf; 469438032Speter if (mci != NULL && mci->mci_host != NULL) 469538032Speter { 469638032Speter extern SOCKADDR CurHostAddr; 469738032Speter 469890792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 469990792Sgshapiro mci->mci_host); 470038032Speter bp += strlen(bp); 470138032Speter 470238032Speter if (CurHostAddr.sa.sa_family != 0) 470390792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 470490792Sgshapiro " [%.100s]", 470590792Sgshapiro anynet_ntoa(&CurHostAddr)); 470638032Speter } 470790792Sgshapiro else if (strcmp(status, "quarantined") == 0) 470890792Sgshapiro { 470990792Sgshapiro if (e->e_quarmsg != NULL) 471090792Sgshapiro (void) sm_snprintf(bp, SPACELEFT(buf, bp), 471190792Sgshapiro ", quarantine=%.100s", 471290792Sgshapiro e->e_quarmsg); 471390792Sgshapiro } 471464562Sgshapiro else if (strcmp(status, "queued") != 0) 471538032Speter { 471638032Speter p = macvalue('h', e); 471738032Speter if (p != NULL && p[0] != '\0') 4718168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p); 471938032Speter } 472038032Speter if (buf[0] != '\0') 472138032Speter sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 472238032Speter 472364562Sgshapiro sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 472464562Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 472538032Speter} 472690792Sgshapiro/* 472738032Speter** PUTFROMLINE -- output a UNIX-style from line (or whatever) 472838032Speter** 472938032Speter** This can be made an arbitrary message separator by changing $l 473038032Speter** 473138032Speter** One of the ugliest hacks seen by human eyes is contained herein: 473238032Speter** UUCP wants those stupid "remote from <host>" lines. Why oh why 473338032Speter** does a well-meaning programmer such as myself have to deal with 473438032Speter** this kind of antique garbage???? 473538032Speter** 473638032Speter** Parameters: 473738032Speter** mci -- the connection information. 473838032Speter** e -- the envelope. 473938032Speter** 474038032Speter** Returns: 4741157001Sgshapiro** true iff line was written successfully 474238032Speter** 474338032Speter** Side Effects: 474438032Speter** outputs some text to fp. 474538032Speter*/ 474638032Speter 4747157001Sgshapirobool 474838032Speterputfromline(mci, e) 474938032Speter register MCI *mci; 475038032Speter ENVELOPE *e; 475138032Speter{ 475238032Speter char *template = UnixFromLine; 475338032Speter char buf[MAXLINE]; 475438032Speter char xbuf[MAXLINE]; 475538032Speter 475638032Speter if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4757157001Sgshapiro return true; 475838032Speter 475938032Speter mci->mci_flags |= MCIF_INHEADER; 476038032Speter 476138032Speter if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 476238032Speter { 476338032Speter char *bang; 476438032Speter 4765168515Sgshapiro expand("\201g", buf, sizeof(buf), e); 476638032Speter bang = strchr(buf, '!'); 476738032Speter if (bang == NULL) 476838032Speter { 476938032Speter char *at; 477038032Speter char hname[MAXNAME]; 477138032Speter 477264562Sgshapiro /* 477342575Speter ** If we can construct a UUCP path, do so 477438032Speter */ 477538032Speter 477638032Speter at = strrchr(buf, '@'); 477738032Speter if (at == NULL) 477838032Speter { 4779168515Sgshapiro expand("\201k", hname, sizeof(hname), e); 478038032Speter at = hname; 478138032Speter } 478238032Speter else 478338032Speter *at++ = '\0'; 4784168515Sgshapiro (void) sm_snprintf(xbuf, sizeof(xbuf), 478590792Sgshapiro "From %.800s \201d remote from %.100s\n", 478690792Sgshapiro buf, at); 478738032Speter } 478838032Speter else 478938032Speter { 479038032Speter *bang++ = '\0'; 4791168515Sgshapiro (void) sm_snprintf(xbuf, sizeof(xbuf), 479290792Sgshapiro "From %.800s \201d remote from %.100s\n", 479390792Sgshapiro bang, buf); 479438032Speter template = xbuf; 479538032Speter } 479638032Speter } 4797168515Sgshapiro expand(template, buf, sizeof(buf), e); 4798157001Sgshapiro return putxline(buf, strlen(buf), mci, PXLF_HEADER); 479938032Speter} 4800157001Sgshapiro 480190792Sgshapiro/* 480238032Speter** PUTBODY -- put the body of a message. 480338032Speter** 480438032Speter** Parameters: 480538032Speter** mci -- the connection information. 480638032Speter** e -- the envelope to put out. 480738032Speter** separator -- if non-NULL, a message separator that must 480838032Speter** not be permitted in the resulting message. 480938032Speter** 481038032Speter** Returns: 4811157001Sgshapiro** true iff message was written successfully 481238032Speter** 481338032Speter** Side Effects: 481438032Speter** The message is written onto fp. 481538032Speter*/ 481638032Speter 481738032Speter/* values for output state variable */ 4818157001Sgshapiro#define OSTATE_HEAD 0 /* at beginning of line */ 4819157001Sgshapiro#define OSTATE_CR 1 /* read a carriage return */ 4820157001Sgshapiro#define OSTATE_INLINE 2 /* putting rest of line */ 482138032Speter 4822157001Sgshapirobool 482338032Speterputbody(mci, e, separator) 482438032Speter register MCI *mci; 482538032Speter register ENVELOPE *e; 482638032Speter char *separator; 482738032Speter{ 482890792Sgshapiro bool dead = false; 4829157001Sgshapiro bool ioerr = false; 4830157001Sgshapiro int save_errno; 483138032Speter char buf[MAXLINE]; 483290792Sgshapiro#if MIME8TO7 483342575Speter char *boundaries[MAXMIMENESTING + 1]; 4834363466Sgshapiro#endif 483538032Speter 483638032Speter /* 483738032Speter ** Output the body of the message 483838032Speter */ 483938032Speter 484038032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 484138032Speter { 484290792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 484338032Speter 484490792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4845120256Sgshapiro SM_IO_RDONLY_B, NULL); 484638032Speter if (e->e_dfp == NULL) 484764562Sgshapiro { 484864562Sgshapiro char *msg = "!putbody: Cannot open %s for %s from %s"; 484964562Sgshapiro 485064562Sgshapiro if (errno == ENOENT) 485164562Sgshapiro msg++; 485264562Sgshapiro syserr(msg, df, e->e_to, e->e_from.q_paddr); 485364562Sgshapiro } 485490792Sgshapiro 485538032Speter } 485638032Speter if (e->e_dfp == NULL) 485738032Speter { 485838032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 485938032Speter { 4860157001Sgshapiro if (!putline("", mci)) 4861157001Sgshapiro goto writeerr; 486238032Speter mci->mci_flags &= ~MCIF_INHEADER; 486338032Speter } 4864157001Sgshapiro if (!putline("<<< No Message Collected >>>", mci)) 4865157001Sgshapiro goto writeerr; 486638032Speter goto endofmessage; 486738032Speter } 486864562Sgshapiro 486938032Speter if (e->e_dfino == (ino_t) 0) 487038032Speter { 487138032Speter struct stat stbuf; 487238032Speter 487390792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 487490792Sgshapiro < 0) 487538032Speter e->e_dfino = -1; 487638032Speter else 487738032Speter { 487838032Speter e->e_dfdev = stbuf.st_dev; 487938032Speter e->e_dfino = stbuf.st_ino; 488038032Speter } 488138032Speter } 488238032Speter 488390792Sgshapiro /* paranoia: the data file should always be in a rewound state */ 488464562Sgshapiro (void) bfrewind(e->e_dfp); 488564562Sgshapiro 4886157001Sgshapiro /* simulate an I/O timeout when used as source */ 4887157001Sgshapiro if (tTd(84, 101)) 4888157001Sgshapiro sleep(319); 4889157001Sgshapiro 489038032Speter#if MIME8TO7 489138032Speter if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 489238032Speter { 489338032Speter /* 489438032Speter ** Do 8 to 7 bit MIME conversion. 489538032Speter */ 489638032Speter 489738032Speter /* make sure it looks like a MIME message */ 4898157001Sgshapiro if (hvalue("MIME-Version", e->e_header) == NULL && 4899157001Sgshapiro !putline("MIME-Version: 1.0", mci)) 4900157001Sgshapiro goto writeerr; 490138032Speter 490238032Speter if (hvalue("Content-Type", e->e_header) == NULL) 490338032Speter { 4904168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), 490590792Sgshapiro "Content-Type: text/plain; charset=%s", 490690792Sgshapiro defcharset(e)); 4907157001Sgshapiro if (!putline(buf, mci)) 4908157001Sgshapiro goto writeerr; 490938032Speter } 491038032Speter 491138032Speter /* now do the hard work */ 491238032Speter boundaries[0] = NULL; 491338032Speter mci->mci_flags |= MCIF_INHEADER; 4914159609Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) == 4915157001Sgshapiro SM_IO_EOF) 4916157001Sgshapiro goto writeerr; 491738032Speter } 491838032Speter# if MIME7TO8 491938032Speter else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 492038032Speter { 4921157001Sgshapiro if (!mime7to8(mci, e->e_header, e)) 4922157001Sgshapiro goto writeerr; 492338032Speter } 492464562Sgshapiro# endif /* MIME7TO8 */ 492542575Speter else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 492642575Speter { 492764562Sgshapiro bool oldsuprerrs = SuprErrs; 492864562Sgshapiro 492942575Speter /* Use mime8to7 to check multipart for MIME header overflows */ 493042575Speter boundaries[0] = NULL; 493142575Speter mci->mci_flags |= MCIF_INHEADER; 493264562Sgshapiro 493364562Sgshapiro /* 493464562Sgshapiro ** If EF_DONT_MIME is set, we have a broken MIME message 493564562Sgshapiro ** and don't want to generate a new bounce message whose 493664562Sgshapiro ** body propagates the broken MIME. We can't just not call 493764562Sgshapiro ** mime8to7() as is done above since we need the security 493864562Sgshapiro ** checks. The best we can do is suppress the errors. 493964562Sgshapiro */ 494064562Sgshapiro 494164562Sgshapiro if (bitset(EF_DONT_MIME, e->e_flags)) 494290792Sgshapiro SuprErrs = true; 494364562Sgshapiro 4944157001Sgshapiro if (mime8to7(mci, e->e_header, e, boundaries, 4945159609Sgshapiro M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF) 4946157001Sgshapiro goto writeerr; 494764562Sgshapiro 494864562Sgshapiro /* restore SuprErrs */ 494964562Sgshapiro SuprErrs = oldsuprerrs; 495042575Speter } 495138032Speter else 495264562Sgshapiro#endif /* MIME8TO7 */ 495338032Speter { 495438032Speter int ostate; 495538032Speter register char *bp; 495638032Speter register char *pbp; 495738032Speter register int c; 495838032Speter register char *xp; 495938032Speter int padc; 496038032Speter char *buflim; 496138032Speter int pos = 0; 496264562Sgshapiro char peekbuf[12]; 496338032Speter 496438032Speter if (bitset(MCIF_INHEADER, mci->mci_flags)) 496538032Speter { 4966157001Sgshapiro if (!putline("", mci)) 4967157001Sgshapiro goto writeerr; 496838032Speter mci->mci_flags &= ~MCIF_INHEADER; 496938032Speter } 497038032Speter 497138032Speter /* determine end of buffer; allow for short mailer lines */ 4972168515Sgshapiro buflim = &buf[sizeof(buf) - 1]; 497338032Speter if (mci->mci_mailer->m_linelimit > 0 && 4974168515Sgshapiro mci->mci_mailer->m_linelimit < sizeof(buf) - 1) 497538032Speter buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 497638032Speter 497738032Speter /* copy temp file to output with mapping */ 4978157001Sgshapiro ostate = OSTATE_HEAD; 497938032Speter bp = buf; 498038032Speter pbp = peekbuf; 498190792Sgshapiro while (!sm_io_error(mci->mci_out) && !dead) 498238032Speter { 498338032Speter if (pbp > peekbuf) 498438032Speter c = *--pbp; 498590792Sgshapiro else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 498690792Sgshapiro == SM_IO_EOF) 498738032Speter break; 498838032Speter if (bitset(MCIF_7BIT, mci->mci_flags)) 498938032Speter c &= 0x7f; 499038032Speter switch (ostate) 499138032Speter { 4992157001Sgshapiro case OSTATE_HEAD: 499338032Speter if (c == '\0' && 499490792Sgshapiro bitnset(M_NONULLS, 499590792Sgshapiro mci->mci_mailer->m_flags)) 499638032Speter break; 499738032Speter if (c != '\r' && c != '\n' && bp < buflim) 499838032Speter { 499938032Speter *bp++ = c; 500038032Speter break; 500138032Speter } 500238032Speter 500338032Speter /* check beginning of line for special cases */ 500438032Speter *bp = '\0'; 500538032Speter pos = 0; 500690792Sgshapiro padc = SM_IO_EOF; 500738032Speter if (buf[0] == 'F' && 500890792Sgshapiro bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 500990792Sgshapiro && strncmp(buf, "From ", 5) == 0) 501038032Speter { 501138032Speter padc = '>'; 501238032Speter } 501338032Speter if (buf[0] == '-' && buf[1] == '-' && 501438032Speter separator != NULL) 501538032Speter { 501638032Speter /* possible separator */ 501738032Speter int sl = strlen(separator); 501838032Speter 501990792Sgshapiro if (strncmp(&buf[2], separator, sl) 502090792Sgshapiro == 0) 502138032Speter padc = ' '; 502238032Speter } 502338032Speter if (buf[0] == '.' && 502438032Speter bitnset(M_XDOT, mci->mci_mailer->m_flags)) 502538032Speter { 502638032Speter padc = '.'; 502738032Speter } 502838032Speter 502938032Speter /* now copy out saved line */ 503038032Speter if (TrafficLogFile != NULL) 503138032Speter { 503290792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 503390792Sgshapiro SM_TIME_DEFAULT, 503490792Sgshapiro "%05d >>> ", 503590792Sgshapiro (int) CurrentPid); 503690792Sgshapiro if (padc != SM_IO_EOF) 503790792Sgshapiro (void) sm_io_putc(TrafficLogFile, 503890792Sgshapiro SM_TIME_DEFAULT, 503990792Sgshapiro padc); 504038032Speter for (xp = buf; xp < bp; xp++) 504190792Sgshapiro (void) sm_io_putc(TrafficLogFile, 504290792Sgshapiro SM_TIME_DEFAULT, 504390792Sgshapiro (unsigned char) *xp); 504438032Speter if (c == '\n') 504590792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 504690792Sgshapiro SM_TIME_DEFAULT, 504790792Sgshapiro mci->mci_mailer->m_eol); 504838032Speter } 504990792Sgshapiro if (padc != SM_IO_EOF) 505038032Speter { 505190792Sgshapiro if (sm_io_putc(mci->mci_out, 505290792Sgshapiro SM_TIME_DEFAULT, padc) 505390792Sgshapiro == SM_IO_EOF) 505464562Sgshapiro { 505590792Sgshapiro dead = true; 505664562Sgshapiro continue; 505764562Sgshapiro } 505838032Speter pos++; 505938032Speter } 506038032Speter for (xp = buf; xp < bp; xp++) 506138032Speter { 506290792Sgshapiro if (sm_io_putc(mci->mci_out, 506390792Sgshapiro SM_TIME_DEFAULT, 506490792Sgshapiro (unsigned char) *xp) 506590792Sgshapiro == SM_IO_EOF) 506664562Sgshapiro { 506790792Sgshapiro dead = true; 506864562Sgshapiro break; 506964562Sgshapiro } 507038032Speter } 507164562Sgshapiro if (dead) 507264562Sgshapiro continue; 507338032Speter if (c == '\n') 507438032Speter { 507590792Sgshapiro if (sm_io_fputs(mci->mci_out, 507690792Sgshapiro SM_TIME_DEFAULT, 507790792Sgshapiro mci->mci_mailer->m_eol) 507890792Sgshapiro == SM_IO_EOF) 507964562Sgshapiro break; 508038032Speter pos = 0; 508138032Speter } 508238032Speter else 508338032Speter { 508438032Speter pos += bp - buf; 508538032Speter if (c != '\r') 5086112810Sgshapiro { 5087112810Sgshapiro SM_ASSERT(pbp < peekbuf + 5088112810Sgshapiro sizeof(peekbuf)); 508938032Speter *pbp++ = c; 5090112810Sgshapiro } 509138032Speter } 509264562Sgshapiro 509338032Speter bp = buf; 509438032Speter 509538032Speter /* determine next state */ 509638032Speter if (c == '\n') 5097157001Sgshapiro ostate = OSTATE_HEAD; 509838032Speter else if (c == '\r') 5099157001Sgshapiro ostate = OSTATE_CR; 510038032Speter else 5101157001Sgshapiro ostate = OSTATE_INLINE; 510238032Speter continue; 510338032Speter 5104157001Sgshapiro case OSTATE_CR: 510538032Speter if (c == '\n') 510638032Speter { 510738032Speter /* got CRLF */ 510890792Sgshapiro if (sm_io_fputs(mci->mci_out, 510990792Sgshapiro SM_TIME_DEFAULT, 511090792Sgshapiro mci->mci_mailer->m_eol) 511190792Sgshapiro == SM_IO_EOF) 511264562Sgshapiro continue; 511364562Sgshapiro 511438032Speter if (TrafficLogFile != NULL) 511538032Speter { 511690792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 511790792Sgshapiro SM_TIME_DEFAULT, 511890792Sgshapiro mci->mci_mailer->m_eol); 511938032Speter } 5120168515Sgshapiro pos = 0; 5121157001Sgshapiro ostate = OSTATE_HEAD; 512238032Speter continue; 512338032Speter } 512438032Speter 512538032Speter /* had a naked carriage return */ 5126112810Sgshapiro SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 512738032Speter *pbp++ = c; 512838032Speter c = '\r'; 5129157001Sgshapiro ostate = OSTATE_INLINE; 513038032Speter goto putch; 513138032Speter 5132157001Sgshapiro case OSTATE_INLINE: 513338032Speter if (c == '\r') 513438032Speter { 5135157001Sgshapiro ostate = OSTATE_CR; 513638032Speter continue; 513738032Speter } 513838032Speter if (c == '\0' && 513990792Sgshapiro bitnset(M_NONULLS, 514090792Sgshapiro mci->mci_mailer->m_flags)) 514138032Speter break; 514238032Speterputch: 514338032Speter if (mci->mci_mailer->m_linelimit > 0 && 514464562Sgshapiro pos >= mci->mci_mailer->m_linelimit - 1 && 514538032Speter c != '\n') 514638032Speter { 514764562Sgshapiro int d; 514864562Sgshapiro 514964562Sgshapiro /* check next character for EOL */ 515064562Sgshapiro if (pbp > peekbuf) 515164562Sgshapiro d = *(pbp - 1); 515290792Sgshapiro else if ((d = sm_io_getc(e->e_dfp, 515390792Sgshapiro SM_TIME_DEFAULT)) 515490792Sgshapiro != SM_IO_EOF) 5155112810Sgshapiro { 5156112810Sgshapiro SM_ASSERT(pbp < peekbuf + 5157112810Sgshapiro sizeof(peekbuf)); 515864562Sgshapiro *pbp++ = d; 5159112810Sgshapiro } 516064562Sgshapiro 516190792Sgshapiro if (d == '\n' || d == SM_IO_EOF) 516264562Sgshapiro { 516364562Sgshapiro if (TrafficLogFile != NULL) 516490792Sgshapiro (void) sm_io_putc(TrafficLogFile, 516590792Sgshapiro SM_TIME_DEFAULT, 516690792Sgshapiro (unsigned char) c); 516790792Sgshapiro if (sm_io_putc(mci->mci_out, 516890792Sgshapiro SM_TIME_DEFAULT, 516990792Sgshapiro (unsigned char) c) 517090792Sgshapiro == SM_IO_EOF) 517164562Sgshapiro { 517290792Sgshapiro dead = true; 517364562Sgshapiro continue; 517464562Sgshapiro } 517564562Sgshapiro pos++; 517664562Sgshapiro continue; 517764562Sgshapiro } 517864562Sgshapiro 517990792Sgshapiro if (sm_io_putc(mci->mci_out, 518090792Sgshapiro SM_TIME_DEFAULT, '!') 518190792Sgshapiro == SM_IO_EOF || 518290792Sgshapiro sm_io_fputs(mci->mci_out, 518390792Sgshapiro SM_TIME_DEFAULT, 518490792Sgshapiro mci->mci_mailer->m_eol) 518590792Sgshapiro == SM_IO_EOF) 518664562Sgshapiro { 518790792Sgshapiro dead = true; 518864562Sgshapiro continue; 518964562Sgshapiro } 519064562Sgshapiro 519138032Speter if (TrafficLogFile != NULL) 519238032Speter { 519390792Sgshapiro (void) sm_io_fprintf(TrafficLogFile, 519490792Sgshapiro SM_TIME_DEFAULT, 519590792Sgshapiro "!%s", 519690792Sgshapiro mci->mci_mailer->m_eol); 519738032Speter } 5198157001Sgshapiro ostate = OSTATE_HEAD; 5199112810Sgshapiro SM_ASSERT(pbp < peekbuf + 5200112810Sgshapiro sizeof(peekbuf)); 520138032Speter *pbp++ = c; 520238032Speter continue; 520338032Speter } 520438032Speter if (c == '\n') 520538032Speter { 520638032Speter if (TrafficLogFile != NULL) 520790792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 520890792Sgshapiro SM_TIME_DEFAULT, 520990792Sgshapiro mci->mci_mailer->m_eol); 521090792Sgshapiro if (sm_io_fputs(mci->mci_out, 521190792Sgshapiro SM_TIME_DEFAULT, 521290792Sgshapiro mci->mci_mailer->m_eol) 521390792Sgshapiro == SM_IO_EOF) 521464562Sgshapiro continue; 521538032Speter pos = 0; 5216157001Sgshapiro ostate = OSTATE_HEAD; 521738032Speter } 521838032Speter else 521938032Speter { 522038032Speter if (TrafficLogFile != NULL) 522190792Sgshapiro (void) sm_io_putc(TrafficLogFile, 522290792Sgshapiro SM_TIME_DEFAULT, 522390792Sgshapiro (unsigned char) c); 522490792Sgshapiro if (sm_io_putc(mci->mci_out, 522590792Sgshapiro SM_TIME_DEFAULT, 522690792Sgshapiro (unsigned char) c) 522790792Sgshapiro == SM_IO_EOF) 522864562Sgshapiro { 522990792Sgshapiro dead = true; 523064562Sgshapiro continue; 523164562Sgshapiro } 523238032Speter pos++; 5233157001Sgshapiro ostate = OSTATE_INLINE; 523438032Speter } 523538032Speter break; 523638032Speter } 523738032Speter } 523838032Speter 523938032Speter /* make sure we are at the beginning of a line */ 524038032Speter if (bp > buf) 524138032Speter { 524238032Speter if (TrafficLogFile != NULL) 524338032Speter { 524438032Speter for (xp = buf; xp < bp; xp++) 524590792Sgshapiro (void) sm_io_putc(TrafficLogFile, 524690792Sgshapiro SM_TIME_DEFAULT, 524790792Sgshapiro (unsigned char) *xp); 524838032Speter } 524938032Speter for (xp = buf; xp < bp; xp++) 525038032Speter { 525190792Sgshapiro if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 525290792Sgshapiro (unsigned char) *xp) 525390792Sgshapiro == SM_IO_EOF) 525464562Sgshapiro { 525590792Sgshapiro dead = true; 525664562Sgshapiro break; 525764562Sgshapiro } 525838032Speter } 525938032Speter pos += bp - buf; 526038032Speter } 526164562Sgshapiro if (!dead && pos > 0) 526238032Speter { 526338032Speter if (TrafficLogFile != NULL) 526490792Sgshapiro (void) sm_io_fputs(TrafficLogFile, 526590792Sgshapiro SM_TIME_DEFAULT, 526690792Sgshapiro mci->mci_mailer->m_eol); 5267157001Sgshapiro if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 5268157001Sgshapiro mci->mci_mailer->m_eol) == SM_IO_EOF) 5269157001Sgshapiro goto writeerr; 527038032Speter } 527138032Speter } 527238032Speter 527390792Sgshapiro if (sm_io_error(e->e_dfp)) 527438032Speter { 527590792Sgshapiro syserr("putbody: %s/%cf%s: read error", 527690792Sgshapiro qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 527790792Sgshapiro DATAFL_LETTER, e->e_id); 527838032Speter ExitStat = EX_IOERR; 5279157001Sgshapiro ioerr = true; 528038032Speter } 528138032Speter 528238032Speterendofmessage: 528364562Sgshapiro /* 528464562Sgshapiro ** Since mailfile() uses e_dfp in a child process, 528564562Sgshapiro ** the file offset in the stdio library for the 528664562Sgshapiro ** parent process will not agree with the in-kernel 528764562Sgshapiro ** file offset since the file descriptor is shared 528864562Sgshapiro ** between the processes. Therefore, it is vital 528964562Sgshapiro ** that the file always be rewound. This forces the 529064562Sgshapiro ** kernel offset (lseek) and stdio library (ftell) 529164562Sgshapiro ** offset to match. 529264562Sgshapiro */ 529364562Sgshapiro 5294157001Sgshapiro save_errno = errno; 529564562Sgshapiro if (e->e_dfp != NULL) 529664562Sgshapiro (void) bfrewind(e->e_dfp); 529764562Sgshapiro 529838032Speter /* some mailers want extra blank line at end of message */ 529964562Sgshapiro if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 530038032Speter buf[0] != '\0' && buf[0] != '\n') 5301157001Sgshapiro { 5302157001Sgshapiro if (!putline("", mci)) 5303157001Sgshapiro goto writeerr; 5304157001Sgshapiro } 530538032Speter 5306157001Sgshapiro if (!dead && 5307157001Sgshapiro (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF || 5308157001Sgshapiro (sm_io_error(mci->mci_out) && errno != EPIPE))) 530938032Speter { 5310157001Sgshapiro save_errno = errno; 531138032Speter syserr("putbody: write error"); 531238032Speter ExitStat = EX_IOERR; 5313157001Sgshapiro ioerr = true; 531438032Speter } 531564562Sgshapiro 5316157001Sgshapiro errno = save_errno; 5317157001Sgshapiro return !dead && !ioerr; 5318157001Sgshapiro 5319157001Sgshapiro writeerr: 5320157001Sgshapiro return false; 532138032Speter} 5322157001Sgshapiro 532390792Sgshapiro/* 532438032Speter** MAILFILE -- Send a message to a file. 532538032Speter** 532690792Sgshapiro** If the file has the set-user-ID/set-group-ID bits set, but NO 532790792Sgshapiro** execute bits, sendmail will try to become the owner of that file 532838032Speter** rather than the real user. Obviously, this only works if 532938032Speter** sendmail runs as root. 533038032Speter** 533138032Speter** This could be done as a subordinate mailer, except that it 533238032Speter** is used implicitly to save messages in ~/dead.letter. We 533338032Speter** view this as being sufficiently important as to include it 533438032Speter** here. For example, if the system is dying, we shouldn't have 533538032Speter** to create another process plus some pipes to save the message. 533638032Speter** 533738032Speter** Parameters: 533838032Speter** filename -- the name of the file to send to. 533938032Speter** mailer -- mailer definition for recipient -- if NULL, 534038032Speter** use FileMailer. 534138032Speter** ctladdr -- the controlling address header -- includes 534238032Speter** the userid/groupid to be when sending. 534338032Speter** sfflags -- flags for opening. 534438032Speter** e -- the current envelope. 534538032Speter** 534638032Speter** Returns: 534738032Speter** The exit code associated with the operation. 534838032Speter** 534938032Speter** Side Effects: 535038032Speter** none. 535138032Speter*/ 535238032Speter 535390792Sgshapiro# define RETURN(st) exit(st); 535490792Sgshapiro 535538032Speterstatic jmp_buf CtxMailfileTimeout; 535638032Speter 535738032Speterint 535838032Spetermailfile(filename, mailer, ctladdr, sfflags, e) 535938032Speter char *volatile filename; 536038032Speter MAILER *volatile mailer; 536138032Speter ADDRESS *ctladdr; 536264562Sgshapiro volatile long sfflags; 536338032Speter register ENVELOPE *e; 536438032Speter{ 536590792Sgshapiro register SM_FILE_T *f; 536638032Speter register pid_t pid = -1; 536764562Sgshapiro volatile int mode; 536864562Sgshapiro int len; 536964562Sgshapiro off_t curoff; 537038032Speter bool suidwarn = geteuid() == 0; 537138032Speter char *p; 537264562Sgshapiro char *volatile realfile; 537390792Sgshapiro SM_EVENT *ev; 537498121Sgshapiro char buf[MAXPATHLEN]; 537598121Sgshapiro char targetfile[MAXPATHLEN]; 537638032Speter 537738032Speter if (tTd(11, 1)) 537838032Speter { 537990792Sgshapiro sm_dprintf("mailfile %s\n ctladdr=", filename); 5380132943Sgshapiro printaddr(sm_debug_file(), ctladdr, false); 538138032Speter } 538238032Speter 538338032Speter if (mailer == NULL) 538438032Speter mailer = FileMailer; 538538032Speter 538638032Speter if (e->e_xfp != NULL) 538790792Sgshapiro (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 538838032Speter 538938032Speter /* 539038032Speter ** Special case /dev/null. This allows us to restrict file 539138032Speter ** delivery to regular files only. 539238032Speter */ 539338032Speter 539490792Sgshapiro if (sm_path_isdevnull(filename)) 539538032Speter return EX_OK; 539638032Speter 539738032Speter /* check for 8-bit available */ 539838032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 539938032Speter bitnset(M_7BITS, mailer->m_flags) && 540038032Speter (bitset(EF_DONT_MIME, e->e_flags) || 540138032Speter !(bitset(MM_MIME8BIT, MimeMode) || 540238032Speter (bitset(EF_IS_MIME, e->e_flags) && 540338032Speter bitset(MM_CVTMIME, MimeMode))))) 540438032Speter { 540538032Speter e->e_status = "5.6.3"; 540664562Sgshapiro usrerrenh(e->e_status, 540790792Sgshapiro "554 Cannot send 8-bit data to 7-bit destination"); 540890792Sgshapiro errno = 0; 540964562Sgshapiro return EX_DATAERR; 541038032Speter } 541138032Speter 541264562Sgshapiro /* Find the actual file */ 541364562Sgshapiro if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 541464562Sgshapiro { 541564562Sgshapiro len = strlen(SafeFileEnv); 541664562Sgshapiro 541764562Sgshapiro if (strncmp(SafeFileEnv, filename, len) == 0) 541864562Sgshapiro filename += len; 541964562Sgshapiro 5420168515Sgshapiro if (len + strlen(filename) + 1 >= sizeof(targetfile)) 542164562Sgshapiro { 542264562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 542364562Sgshapiro SafeFileEnv, filename); 542464562Sgshapiro return EX_CANTCREAT; 542564562Sgshapiro } 5426168515Sgshapiro (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile)); 542764562Sgshapiro realfile = targetfile + len; 542864562Sgshapiro if (*filename == '/') 542964562Sgshapiro filename++; 543094334Sgshapiro if (*filename != '\0') 543194334Sgshapiro { 543294334Sgshapiro /* paranoia: trailing / should be removed in readcf */ 543394334Sgshapiro if (targetfile[len - 1] != '/') 543494334Sgshapiro (void) sm_strlcat(targetfile, 5435168515Sgshapiro "/", sizeof(targetfile)); 543694334Sgshapiro (void) sm_strlcat(targetfile, filename, 5437168515Sgshapiro sizeof(targetfile)); 543894334Sgshapiro } 543964562Sgshapiro } 544064562Sgshapiro else if (mailer->m_rootdir != NULL) 544164562Sgshapiro { 5442168515Sgshapiro expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e); 544364562Sgshapiro len = strlen(targetfile); 544464562Sgshapiro 544564562Sgshapiro if (strncmp(targetfile, filename, len) == 0) 544664562Sgshapiro filename += len; 544764562Sgshapiro 5448168515Sgshapiro if (len + strlen(filename) + 1 >= sizeof(targetfile)) 544964562Sgshapiro { 545064562Sgshapiro syserr("mailfile: filename too long (%s/%s)", 545164562Sgshapiro targetfile, filename); 545264562Sgshapiro return EX_CANTCREAT; 545364562Sgshapiro } 545464562Sgshapiro realfile = targetfile + len; 545564562Sgshapiro if (targetfile[len - 1] != '/') 5456168515Sgshapiro (void) sm_strlcat(targetfile, "/", sizeof(targetfile)); 545764562Sgshapiro if (*filename == '/') 545890792Sgshapiro (void) sm_strlcat(targetfile, filename + 1, 5459168515Sgshapiro sizeof(targetfile)); 546064562Sgshapiro else 546190792Sgshapiro (void) sm_strlcat(targetfile, filename, 5462168515Sgshapiro sizeof(targetfile)); 546364562Sgshapiro } 546464562Sgshapiro else 546564562Sgshapiro { 5466168515Sgshapiro if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >= 5467168515Sgshapiro sizeof(targetfile)) 546864562Sgshapiro { 546964562Sgshapiro syserr("mailfile: filename too long (%s)", filename); 547064562Sgshapiro return EX_CANTCREAT; 547164562Sgshapiro } 547264562Sgshapiro realfile = targetfile; 547364562Sgshapiro } 547464562Sgshapiro 547538032Speter /* 547638032Speter ** Fork so we can change permissions here. 547738032Speter ** Note that we MUST use fork, not vfork, because of 547838032Speter ** the complications of calling subroutines, etc. 547938032Speter */ 548038032Speter 548194334Sgshapiro 548294334Sgshapiro /* 548394334Sgshapiro ** Dispose of SIGCHLD signal catchers that may be laying 548494334Sgshapiro ** around so that the waitfor() below will get it. 548594334Sgshapiro */ 548694334Sgshapiro 548794334Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 548894334Sgshapiro 548938032Speter DOFORK(fork); 549038032Speter 549138032Speter if (pid < 0) 549264562Sgshapiro return EX_OSERR; 549338032Speter else if (pid == 0) 549438032Speter { 549538032Speter /* child -- actually write to file */ 549638032Speter struct stat stb; 549738032Speter MCI mcibuf; 549842575Speter int err; 549938032Speter volatile int oflags = O_WRONLY|O_APPEND; 550038032Speter 550177349Sgshapiro /* Reset global flags */ 550277349Sgshapiro RestartRequest = NULL; 550390792Sgshapiro RestartWorkGroup = false; 550477349Sgshapiro ShutdownRequest = NULL; 550577349Sgshapiro PendingSignal = 0; 550690792Sgshapiro CurrentPid = getpid(); 550777349Sgshapiro 550838032Speter if (e->e_lockfp != NULL) 5509159609Sgshapiro { 5510159609Sgshapiro int fd; 551138032Speter 5512159609Sgshapiro fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 5513159609Sgshapiro /* SM_ASSERT(fd >= 0); */ 5514159609Sgshapiro if (fd >= 0) 5515159609Sgshapiro (void) close(fd); 5516159609Sgshapiro } 5517159609Sgshapiro 551890792Sgshapiro (void) sm_signal(SIGINT, SIG_DFL); 551990792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 552090792Sgshapiro (void) sm_signal(SIGTERM, SIG_DFL); 552138032Speter (void) umask(OldUmask); 552238032Speter e->e_to = filename; 552338032Speter ExitStat = EX_OK; 552438032Speter 552538032Speter if (setjmp(CtxMailfileTimeout) != 0) 552638032Speter { 552790792Sgshapiro RETURN(EX_TEMPFAIL); 552838032Speter } 552938032Speter 553038032Speter if (TimeOuts.to_fileopen > 0) 553190792Sgshapiro ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 553290792Sgshapiro 0); 553338032Speter else 553438032Speter ev = NULL; 553538032Speter 553690792Sgshapiro /* check file mode to see if set-user-ID */ 553764562Sgshapiro if (stat(targetfile, &stb) < 0) 553864562Sgshapiro mode = FileMode; 553942575Speter else 554038032Speter mode = stb.st_mode; 554138032Speter 554238032Speter /* limit the errors to those actually caused in the child */ 554338032Speter errno = 0; 554438032Speter ExitStat = EX_OK; 554538032Speter 554664562Sgshapiro /* Allow alias expansions to use the S_IS{U,G}ID bits */ 554764562Sgshapiro if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 554864562Sgshapiro bitset(SFF_RUNASREALUID, sfflags)) 554938032Speter { 555090792Sgshapiro /* ignore set-user-ID and set-group-ID bits */ 555138032Speter mode &= ~(S_ISGID|S_ISUID); 555264562Sgshapiro if (tTd(11, 20)) 555390792Sgshapiro sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 555438032Speter } 555538032Speter 555690792Sgshapiro /* we have to open the data file BEFORE setuid() */ 555738032Speter if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 555838032Speter { 555990792Sgshapiro char *df = queuename(e, DATAFL_LETTER); 556038032Speter 556190792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5562120256Sgshapiro SM_IO_RDONLY_B, NULL); 556338032Speter if (e->e_dfp == NULL) 556438032Speter { 556538032Speter syserr("mailfile: Cannot open %s for %s from %s", 556638032Speter df, e->e_to, e->e_from.q_paddr); 556738032Speter } 556838032Speter } 556938032Speter 557038032Speter /* select a new user to run as */ 557138032Speter if (!bitset(SFF_RUNASREALUID, sfflags)) 557238032Speter { 557338032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 557438032Speter { 557538032Speter RealUserName = NULL; 5576132943Sgshapiro if (mailer->m_uid == NO_UID) 5577132943Sgshapiro RealUid = RunAsUid; 5578132943Sgshapiro else 5579132943Sgshapiro RealUid = mailer->m_uid; 558064562Sgshapiro if (RunAsUid != 0 && RealUid != RunAsUid) 558164562Sgshapiro { 558264562Sgshapiro /* Only root can change the uid */ 5583285229Sgshapiro syserr("mailfile: insufficient privileges to change uid, RunAsUid=%ld, RealUid=%ld", 5584285229Sgshapiro (long) RunAsUid, (long) RealUid); 558590792Sgshapiro RETURN(EX_TEMPFAIL); 558664562Sgshapiro } 558738032Speter } 558838032Speter else if (bitset(S_ISUID, mode)) 558938032Speter { 559038032Speter RealUserName = NULL; 559138032Speter RealUid = stb.st_uid; 559238032Speter } 559338032Speter else if (ctladdr != NULL && ctladdr->q_uid != 0) 559438032Speter { 559538032Speter if (ctladdr->q_ruser != NULL) 559638032Speter RealUserName = ctladdr->q_ruser; 559738032Speter else 559838032Speter RealUserName = ctladdr->q_user; 559938032Speter RealUid = ctladdr->q_uid; 560038032Speter } 5601132943Sgshapiro else if (mailer != NULL && mailer->m_uid != NO_UID) 560238032Speter { 560338032Speter RealUserName = DefUser; 560438032Speter RealUid = mailer->m_uid; 560538032Speter } 560638032Speter else 560738032Speter { 560838032Speter RealUserName = DefUser; 560938032Speter RealUid = DefUid; 561038032Speter } 561138032Speter 561238032Speter /* select a new group to run as */ 561338032Speter if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 561464562Sgshapiro { 5615132943Sgshapiro if (mailer->m_gid == NO_GID) 5616132943Sgshapiro RealGid = RunAsGid; 5617132943Sgshapiro else 5618132943Sgshapiro RealGid = mailer->m_gid; 561964562Sgshapiro if (RunAsUid != 0 && 562064562Sgshapiro (RealGid != getgid() || 562164562Sgshapiro RealGid != getegid())) 562264562Sgshapiro { 562364562Sgshapiro /* Only root can change the gid */ 5624285229Sgshapiro syserr("mailfile: insufficient privileges to change gid, RealGid=%ld, RunAsUid=%ld, gid=%ld, egid=%ld", 5625285229Sgshapiro (long) RealGid, (long) RunAsUid, 5626285229Sgshapiro (long) getgid(), (long) getegid()); 562790792Sgshapiro RETURN(EX_TEMPFAIL); 562864562Sgshapiro } 562964562Sgshapiro } 563038032Speter else if (bitset(S_ISGID, mode)) 563138032Speter RealGid = stb.st_gid; 563264562Sgshapiro else if (ctladdr != NULL && 563364562Sgshapiro ctladdr->q_uid == DefUid && 563464562Sgshapiro ctladdr->q_gid == 0) 563571345Sgshapiro { 563671345Sgshapiro /* 563771345Sgshapiro ** Special case: This means it is an 563871345Sgshapiro ** alias and we should act as DefaultUser. 563971345Sgshapiro ** See alias()'s comments. 564071345Sgshapiro */ 564171345Sgshapiro 564264562Sgshapiro RealGid = DefGid; 564371345Sgshapiro RealUserName = DefUser; 564471345Sgshapiro } 564571345Sgshapiro else if (ctladdr != NULL && ctladdr->q_uid != 0) 564671345Sgshapiro RealGid = ctladdr->q_gid; 5647132943Sgshapiro else if (mailer != NULL && mailer->m_gid != NO_GID) 564838032Speter RealGid = mailer->m_gid; 564938032Speter else 565038032Speter RealGid = DefGid; 565138032Speter } 565238032Speter 565338032Speter /* last ditch */ 565438032Speter if (!bitset(SFF_ROOTOK, sfflags)) 565538032Speter { 565638032Speter if (RealUid == 0) 565738032Speter RealUid = DefUid; 565838032Speter if (RealGid == 0) 565938032Speter RealGid = DefGid; 566038032Speter } 566138032Speter 566238032Speter /* set group id list (needs /etc/group access) */ 566338032Speter if (RealUserName != NULL && !DontInitGroups) 566438032Speter { 566538032Speter if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 566664562Sgshapiro { 5667285229Sgshapiro syserr("mailfile: initgroups(%s, %ld) failed", 5668285229Sgshapiro RealUserName, (long) RealGid); 566990792Sgshapiro RETURN(EX_TEMPFAIL); 567064562Sgshapiro } 567138032Speter } 567238032Speter else 567338032Speter { 567438032Speter GIDSET_T gidset[1]; 567538032Speter 567638032Speter gidset[0] = RealGid; 567738032Speter if (setgroups(1, gidset) == -1 && suidwarn) 567864562Sgshapiro { 567938032Speter syserr("mailfile: setgroups() failed"); 568090792Sgshapiro RETURN(EX_TEMPFAIL); 568164562Sgshapiro } 568238032Speter } 568338032Speter 568464562Sgshapiro /* 568564562Sgshapiro ** If you have a safe environment, go into it. 568664562Sgshapiro */ 568764562Sgshapiro 568864562Sgshapiro if (realfile != targetfile) 568938032Speter { 569094334Sgshapiro char save; 569194334Sgshapiro 569294334Sgshapiro save = *realfile; 569364562Sgshapiro *realfile = '\0'; 569464562Sgshapiro if (tTd(11, 20)) 569590792Sgshapiro sm_dprintf("mailfile: chroot %s\n", targetfile); 569664562Sgshapiro if (chroot(targetfile) < 0) 569738032Speter { 569838032Speter syserr("mailfile: Cannot chroot(%s)", 569964562Sgshapiro targetfile); 570090792Sgshapiro RETURN(EX_CANTCREAT); 570138032Speter } 570294334Sgshapiro *realfile = save; 570338032Speter } 570464562Sgshapiro 570564562Sgshapiro if (tTd(11, 40)) 570690792Sgshapiro sm_dprintf("mailfile: deliver to %s\n", realfile); 570764562Sgshapiro 570838032Speter if (chdir("/") < 0) 570964562Sgshapiro { 571038032Speter syserr("mailfile: cannot chdir(/)"); 571190792Sgshapiro RETURN(EX_CANTCREAT); 571264562Sgshapiro } 571338032Speter 571438032Speter /* now reset the group and user ids */ 571538032Speter endpwent(); 571690792Sgshapiro sm_mbdb_terminate(); 571738032Speter if (setgid(RealGid) < 0 && suidwarn) 571864562Sgshapiro { 571938032Speter syserr("mailfile: setgid(%ld) failed", (long) RealGid); 572090792Sgshapiro RETURN(EX_TEMPFAIL); 572164562Sgshapiro } 572238032Speter vendor_set_uid(RealUid); 572338032Speter if (setuid(RealUid) < 0 && suidwarn) 572464562Sgshapiro { 572538032Speter syserr("mailfile: setuid(%ld) failed", (long) RealUid); 572690792Sgshapiro RETURN(EX_TEMPFAIL); 572764562Sgshapiro } 572838032Speter 572964562Sgshapiro if (tTd(11, 2)) 5730285229Sgshapiro sm_dprintf("mailfile: running as r/euid=%ld/%ld, r/egid=%ld/%ld\n", 5731285229Sgshapiro (long) getuid(), (long) geteuid(), 5732285229Sgshapiro (long) getgid(), (long) getegid()); 573364562Sgshapiro 573464562Sgshapiro 573538032Speter /* move into some "safe" directory */ 573638032Speter if (mailer->m_execdir != NULL) 573738032Speter { 573838032Speter char *q; 573938032Speter 574038032Speter for (p = mailer->m_execdir; p != NULL; p = q) 574138032Speter { 574238032Speter q = strchr(p, ':'); 574338032Speter if (q != NULL) 574438032Speter *q = '\0'; 5745168515Sgshapiro expand(p, buf, sizeof(buf), e); 574638032Speter if (q != NULL) 574738032Speter *q++ = ':'; 574838032Speter if (tTd(11, 20)) 574990792Sgshapiro sm_dprintf("mailfile: trydir %s\n", 575090792Sgshapiro buf); 575138032Speter if (buf[0] != '\0' && chdir(buf) >= 0) 575238032Speter break; 575338032Speter } 575438032Speter } 575538032Speter 575664562Sgshapiro /* 575764562Sgshapiro ** Recheck the file after we have assumed the ID of the 575864562Sgshapiro ** delivery user to make sure we can deliver to it as 575964562Sgshapiro ** that user. This is necessary if sendmail is running 576064562Sgshapiro ** as root and the file is on an NFS mount which treats 576164562Sgshapiro ** root as nobody. 576264562Sgshapiro */ 576364562Sgshapiro 576464562Sgshapiro#if HASLSTAT 576564562Sgshapiro if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 576664562Sgshapiro err = stat(realfile, &stb); 576764562Sgshapiro else 576864562Sgshapiro err = lstat(realfile, &stb); 576964562Sgshapiro#else /* HASLSTAT */ 577064562Sgshapiro err = stat(realfile, &stb); 577164562Sgshapiro#endif /* HASLSTAT */ 577264562Sgshapiro 577364562Sgshapiro if (err < 0) 577464562Sgshapiro { 577564562Sgshapiro stb.st_mode = ST_MODE_NOFILE; 577664562Sgshapiro mode = FileMode; 577764562Sgshapiro oflags |= O_CREAT|O_EXCL; 577864562Sgshapiro } 577964562Sgshapiro else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 578064562Sgshapiro (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 578164562Sgshapiro DontBlameSendmail) && 578264562Sgshapiro stb.st_nlink != 1) || 578364562Sgshapiro (realfile != targetfile && !S_ISREG(mode))) 578464562Sgshapiro exit(EX_CANTCREAT); 578564562Sgshapiro else 578664562Sgshapiro mode = stb.st_mode; 578764562Sgshapiro 578864562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 578938032Speter sfflags |= SFF_NOSLINK; 579064562Sgshapiro if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 579138032Speter sfflags |= SFF_NOHLINK; 579238032Speter sfflags &= ~SFF_OPENASROOT; 579364562Sgshapiro f = safefopen(realfile, oflags, mode, sfflags); 579438032Speter if (f == NULL) 579538032Speter { 579664562Sgshapiro if (transienterror(errno)) 579764562Sgshapiro { 579864562Sgshapiro usrerr("454 4.3.0 cannot open %s: %s", 579964562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 580090792Sgshapiro sm_errstring(errno)); 580190792Sgshapiro RETURN(EX_TEMPFAIL); 580264562Sgshapiro } 580364562Sgshapiro else 580464562Sgshapiro { 580564562Sgshapiro usrerr("554 5.3.0 cannot open %s: %s", 580664562Sgshapiro shortenstring(realfile, MAXSHORTSTR), 580790792Sgshapiro sm_errstring(errno)); 580890792Sgshapiro RETURN(EX_CANTCREAT); 580964562Sgshapiro } 581038032Speter } 581190792Sgshapiro if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 581290792Sgshapiro &stb)) 581338032Speter { 581464562Sgshapiro syserr("554 5.3.0 file changed after open"); 581590792Sgshapiro RETURN(EX_CANTCREAT); 581638032Speter } 581790792Sgshapiro if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 581838032Speter { 581990792Sgshapiro syserr("554 5.3.0 cannot fstat %s", 582090792Sgshapiro sm_errstring(errno)); 582190792Sgshapiro RETURN(EX_CANTCREAT); 582238032Speter } 582338032Speter 582464562Sgshapiro curoff = stb.st_size; 582564562Sgshapiro 582638032Speter if (ev != NULL) 582790792Sgshapiro sm_clrevent(ev); 582838032Speter 5829168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 583038032Speter mcibuf.mci_mailer = mailer; 583138032Speter mcibuf.mci_out = f; 583238032Speter if (bitnset(M_7BITS, mailer->m_flags)) 583338032Speter mcibuf.mci_flags |= MCIF_7BIT; 583438032Speter 583538032Speter /* clear out per-message flags from connection structure */ 583638032Speter mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 583738032Speter 583838032Speter if (bitset(EF_HAS8BIT, e->e_flags) && 583938032Speter !bitset(EF_DONT_MIME, e->e_flags) && 584038032Speter bitnset(M_7BITS, mailer->m_flags)) 584138032Speter mcibuf.mci_flags |= MCIF_CVT8TO7; 584238032Speter 584338032Speter#if MIME7TO8 584438032Speter if (bitnset(M_MAKE8BIT, mailer->m_flags) && 584538032Speter !bitset(MCIF_7BIT, mcibuf.mci_flags) && 584638032Speter (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 584790792Sgshapiro (sm_strcasecmp(p, "quoted-printable") == 0 || 584890792Sgshapiro sm_strcasecmp(p, "base64") == 0) && 584938032Speter (p = hvalue("Content-Type", e->e_header)) != NULL) 585038032Speter { 585138032Speter /* may want to convert 7 -> 8 */ 585238032Speter /* XXX should really parse it here -- and use a class XXX */ 585390792Sgshapiro if (sm_strncasecmp(p, "text/plain", 10) == 0 && 585464562Sgshapiro (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 585538032Speter mcibuf.mci_flags |= MCIF_CVT7TO8; 585638032Speter } 585764562Sgshapiro#endif /* MIME7TO8 */ 585838032Speter 5859157001Sgshapiro if (!putfromline(&mcibuf, e) || 5860157001Sgshapiro !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) || 5861157001Sgshapiro !(*e->e_putbody)(&mcibuf, e, NULL) || 5862157001Sgshapiro !putline("\n", &mcibuf) || 5863157001Sgshapiro (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 586490792Sgshapiro (SuperSafe != SAFE_NO && 586590792Sgshapiro fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5866157001Sgshapiro sm_io_error(f))) 586738032Speter { 586838032Speter setstat(EX_IOERR); 586964562Sgshapiro#if !NOFTRUNCATE 587090792Sgshapiro (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 587190792Sgshapiro curoff); 5872363466Sgshapiro#endif 587338032Speter } 587438032Speter 587538032Speter /* reset ISUID & ISGID bits for paranoid systems */ 587638032Speter#if HASFCHMOD 587790792Sgshapiro (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 587890792Sgshapiro (MODE_T) mode); 587964562Sgshapiro#else /* HASFCHMOD */ 588064562Sgshapiro (void) chmod(filename, (MODE_T) mode); 588164562Sgshapiro#endif /* HASFCHMOD */ 588290792Sgshapiro if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 588364562Sgshapiro setstat(EX_IOERR); 588490792Sgshapiro (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 588564562Sgshapiro (void) setuid(RealUid); 588638032Speter exit(ExitStat); 588764562Sgshapiro /* NOTREACHED */ 588838032Speter } 588938032Speter else 589038032Speter { 589138032Speter /* parent -- wait for exit status */ 589238032Speter int st; 589338032Speter 589438032Speter st = waitfor(pid); 589538032Speter if (st == -1) 589638032Speter { 589738032Speter syserr("mailfile: %s: wait", mailer->m_name); 589864562Sgshapiro return EX_SOFTWARE; 589938032Speter } 590038032Speter if (WIFEXITED(st)) 590190792Sgshapiro { 590290792Sgshapiro errno = 0; 590338032Speter return (WEXITSTATUS(st)); 590490792Sgshapiro } 590538032Speter else 590638032Speter { 590738032Speter syserr("mailfile: %s: child died on signal %d", 590838032Speter mailer->m_name, st); 590964562Sgshapiro return EX_UNAVAILABLE; 591038032Speter } 591164562Sgshapiro /* NOTREACHED */ 591238032Speter } 591338032Speter return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 591438032Speter} 591538032Speter 591638032Speterstatic void 5917141858Sgshapiromailfiletimeout(ignore) 5918141858Sgshapiro int ignore; 591938032Speter{ 592077349Sgshapiro /* 592177349Sgshapiro ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 592277349Sgshapiro ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 592377349Sgshapiro ** DOING. 592477349Sgshapiro */ 592577349Sgshapiro 592677349Sgshapiro errno = ETIMEDOUT; 592738032Speter longjmp(CtxMailfileTimeout, 1); 592838032Speter} 5929363466Sgshapiro 5930363466Sgshapiro#if DANE 5931363466Sgshapiro 593290792Sgshapiro/* 5933363466Sgshapiro** GETMPORT -- return the port of a mailer 5934363466Sgshapiro** 5935363466Sgshapiro** Parameters: 5936363466Sgshapiro** m -- the mailer describing this host. 5937363466Sgshapiro** 5938363466Sgshapiro** Returns: 5939363466Sgshapiro** the port of the mailer if defined. 5940363466Sgshapiro** 0 otherwise 5941363466Sgshapiro** <0 error 5942363466Sgshapiro*/ 5943363466Sgshapiro 5944363466Sgshapirostatic int getmport __P((MAILER *)); 5945363466Sgshapiro 5946363466Sgshapirostatic int 5947363466Sgshapirogetmport(m) 5948363466Sgshapiro MAILER *m; 5949363466Sgshapiro{ 5950363466Sgshapiro unsigned long ulval; 5951363466Sgshapiro char *buf, *ep; 5952363466Sgshapiro 5953363466Sgshapiro if (m->m_port > 0) 5954363466Sgshapiro return m->m_port; 5955363466Sgshapiro 5956363466Sgshapiro if (NULL == m->m_argv[0] ||NULL == m->m_argv[1]) 5957363466Sgshapiro return -1; 5958363466Sgshapiro buf = m->m_argv[2]; 5959363466Sgshapiro if (NULL == buf) 5960363466Sgshapiro return 0; 5961363466Sgshapiro 5962363466Sgshapiro errno = 0; 5963363466Sgshapiro ulval = strtoul(buf, &ep, 0); 5964363466Sgshapiro if (buf[0] == '\0' || *ep != '\0') 5965363466Sgshapiro return -1; 5966363466Sgshapiro if (errno == ERANGE && ulval == ULONG_MAX) 5967363466Sgshapiro return -1; 5968363466Sgshapiro if (ulval > USHRT_MAX) 5969363466Sgshapiro return -1; 5970363466Sgshapiro m->m_port = (unsigned short) ulval; 5971363466Sgshapiro if (tTd(17, 30)) 5972363466Sgshapiro sm_dprintf("getmport: mailer=%s, port=%d\n", m->m_name, 5973363466Sgshapiro m->m_port); 5974363466Sgshapiro return m->m_port; 5975363466Sgshapiro} 5976363466Sgshapiro# define GETMPORT(m) getmport(m) 5977363466Sgshapiro#else /* DANE */ 5978363466Sgshapiro# define GETMPORT(m) 25 5979363466Sgshapiro#endif /* DANE */ 5980363466Sgshapiro 5981363466Sgshapiro/* 598238032Speter** HOSTSIGNATURE -- return the "signature" for a host. 598338032Speter** 598438032Speter** The signature describes how we are going to send this -- it 598538032Speter** can be just the hostname (for non-Internet hosts) or can be 598638032Speter** an ordered list of MX hosts. 598738032Speter** 598838032Speter** Parameters: 598938032Speter** m -- the mailer describing this host. 599038032Speter** host -- the host name. 5991363466Sgshapiro** ad -- DNSSEC: ad 599238032Speter** 599338032Speter** Returns: 599438032Speter** The signature for this host. 599538032Speter** 599638032Speter** Side Effects: 599738032Speter** Can tweak the symbol table. 599838032Speter*/ 599990792Sgshapiro 600064562Sgshapiro#define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 600138032Speter 600290792Sgshapirochar * 6003363466Sgshapirohostsignature(m, host, ad) 600438032Speter register MAILER *m; 600538032Speter char *host; 6006363466Sgshapiro bool ad; 600738032Speter{ 600838032Speter register char *p; 600938032Speter register STAB *s; 601090792Sgshapiro time_t now; 601164562Sgshapiro#if NAMED_BIND 601264562Sgshapiro char sep = ':'; 601364562Sgshapiro char prevsep = ':'; 601438032Speter int i; 601538032Speter int len; 601638032Speter int nmx; 601764562Sgshapiro int hl; 601838032Speter char *hp; 601938032Speter char *endp; 602038032Speter int oldoptions = _res.options; 602138032Speter char *mxhosts[MAXMXHOSTS + 1]; 602290792Sgshapiro unsigned short mxprefs[MAXMXHOSTS + 1]; 602364562Sgshapiro#endif /* NAMED_BIND */ 602438032Speter 602564562Sgshapiro if (tTd(17, 3)) 6026363466Sgshapiro sm_dprintf("hostsignature(%s), ad=%d\n", host, ad); 602764562Sgshapiro 602838032Speter /* 602977349Sgshapiro ** If local delivery (and not remote), just return a constant. 603064562Sgshapiro */ 603164562Sgshapiro 603277349Sgshapiro if (bitnset(M_LOCALMAILER, m->m_flags) && 603390792Sgshapiro strcmp(m->m_mailer, "[IPC]") != 0 && 603490792Sgshapiro !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 603564562Sgshapiro return "localhost"; 603664562Sgshapiro 6037147078Sgshapiro /* an empty host does not have MX records */ 6038147078Sgshapiro if (*host == '\0') 6039147078Sgshapiro return "_empty_"; 6040147078Sgshapiro 604164562Sgshapiro /* 604238032Speter ** Check to see if this uses IPC -- if not, it can't have MX records. 604338032Speter */ 604438032Speter 604590792Sgshapiro if (strcmp(m->m_mailer, "[IPC]") != 0 || 604690792Sgshapiro CurEnv->e_sendmode == SM_DEFER) 604738032Speter { 604890792Sgshapiro /* just an ordinary mailer or deferred mode */ 604938032Speter return host; 605038032Speter } 605164562Sgshapiro#if NETUNIX 605264562Sgshapiro else if (m->m_argv[0] != NULL && 605364562Sgshapiro strcmp(m->m_argv[0], "FILE") == 0) 605464562Sgshapiro { 605564562Sgshapiro /* rendezvous in the file system, no MX records */ 605664562Sgshapiro return host; 605764562Sgshapiro } 605864562Sgshapiro#endif /* NETUNIX */ 605938032Speter 606038032Speter /* 606138032Speter ** Look it up in the symbol table. 606238032Speter */ 606338032Speter 606490792Sgshapiro now = curtime(); 606538032Speter s = stab(host, ST_HOSTSIG, ST_ENTER); 606690792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 606764562Sgshapiro { 606890792Sgshapiro if (s->s_hostsig.hs_exp >= now) 606990792Sgshapiro { 607090792Sgshapiro if (tTd(17, 3)) 607190792Sgshapiro sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 607290792Sgshapiro s->s_hostsig.hs_sig); 607390792Sgshapiro return s->s_hostsig.hs_sig; 607490792Sgshapiro } 607590792Sgshapiro 607690792Sgshapiro /* signature is expired: clear it */ 607790792Sgshapiro sm_free(s->s_hostsig.hs_sig); 607890792Sgshapiro s->s_hostsig.hs_sig = NULL; 607964562Sgshapiro } 608038032Speter 608190792Sgshapiro /* set default TTL */ 608290792Sgshapiro s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 608390792Sgshapiro 608438032Speter /* 608590792Sgshapiro ** Not already there or expired -- create a signature. 608638032Speter */ 608738032Speter 608838032Speter#if NAMED_BIND 608938032Speter if (ConfigLevel < 2) 609038032Speter _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 609138032Speter 609238032Speter for (hp = host; hp != NULL; hp = endp) 609338032Speter { 6094363466Sgshapiro# if NETINET6 609564562Sgshapiro if (*hp == '[') 609664562Sgshapiro { 609764562Sgshapiro endp = strchr(hp + 1, ']'); 609864562Sgshapiro if (endp != NULL) 609964562Sgshapiro endp = strpbrk(endp + 1, ":,"); 610064562Sgshapiro } 610164562Sgshapiro else 610264562Sgshapiro endp = strpbrk(hp, ":,"); 6103363466Sgshapiro# else /* NETINET6 */ 610464562Sgshapiro endp = strpbrk(hp, ":,"); 6105363466Sgshapiro# endif /* NETINET6 */ 610638032Speter if (endp != NULL) 610764562Sgshapiro { 610864562Sgshapiro sep = *endp; 610938032Speter *endp = '\0'; 611064562Sgshapiro } 611138032Speter 611238032Speter if (bitnset(M_NOMX, m->m_flags)) 611338032Speter { 611438032Speter /* skip MX lookups */ 611538032Speter nmx = 1; 611638032Speter mxhosts[0] = hp; 611738032Speter } 611838032Speter else 611938032Speter { 612038032Speter auto int rcode; 612190792Sgshapiro int ttl; 612238032Speter 6123363466Sgshapiro GETMPORT(m); 6124363466Sgshapiro nmx = getmxrr(hp, mxhosts, mxprefs, 6125363466Sgshapiro DROPLOCALHOST|TRYFALLBACK|(ad ? ISAD :0), 6126363466Sgshapiro &rcode, &ttl, M_PORT(m)); 612738032Speter if (nmx <= 0) 612838032Speter { 612980785Sgshapiro int save_errno; 613038032Speter register MCI *mci; 613138032Speter 613238032Speter /* update the connection info for this host */ 613380785Sgshapiro save_errno = errno; 613438032Speter mci = mci_get(hp, m); 613580785Sgshapiro mci->mci_errno = save_errno; 613638032Speter mci->mci_herrno = h_errno; 613771345Sgshapiro mci->mci_lastuse = now; 6138363466Sgshapiro if (nmx == NULLMX) 6139363466Sgshapiro mci_setstat(mci, rcode, "5.7.27", 6140363466Sgshapiro "550 Host does not accept mail"); 6141363466Sgshapiro else if (rcode == EX_NOHOST) 614264562Sgshapiro mci_setstat(mci, rcode, "5.1.2", 614390792Sgshapiro "550 Host unknown"); 614464562Sgshapiro else 614564562Sgshapiro mci_setstat(mci, rcode, NULL, NULL); 614638032Speter 614738032Speter /* use the original host name as signature */ 614838032Speter nmx = 1; 614938032Speter mxhosts[0] = hp; 615038032Speter } 615164562Sgshapiro if (tTd(17, 3)) 615290792Sgshapiro sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 615390792Sgshapiro nmx, mxhosts[0]); 615490792Sgshapiro 615590792Sgshapiro /* 615690792Sgshapiro ** Set new TTL: we use only one! 615790792Sgshapiro ** We could try to use the minimum instead. 615890792Sgshapiro */ 615990792Sgshapiro 616090792Sgshapiro s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 616138032Speter } 616238032Speter 616338032Speter len = 0; 616438032Speter for (i = 0; i < nmx; i++) 616538032Speter len += strlen(mxhosts[i]) + 1; 616690792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 616790792Sgshapiro len += strlen(s->s_hostsig.hs_sig) + 1; 616890792Sgshapiro if (len < 0 || len >= MAXHOSTSIGNATURE) 616964562Sgshapiro { 617064562Sgshapiro sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 617164562Sgshapiro host, MAXHOSTSIGNATURE, len); 617264562Sgshapiro len = MAXHOSTSIGNATURE; 617364562Sgshapiro } 617490792Sgshapiro p = sm_pmalloc_x(len); 617590792Sgshapiro if (s->s_hostsig.hs_sig != NULL) 617638032Speter { 617790792Sgshapiro (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 617890792Sgshapiro sm_free(s->s_hostsig.hs_sig); /* XXX */ 617990792Sgshapiro s->s_hostsig.hs_sig = p; 618064562Sgshapiro hl = strlen(p); 618164562Sgshapiro p += hl; 618264562Sgshapiro *p++ = prevsep; 618364562Sgshapiro len -= hl + 1; 618438032Speter } 618538032Speter else 618690792Sgshapiro s->s_hostsig.hs_sig = p; 618738032Speter for (i = 0; i < nmx; i++) 618838032Speter { 618964562Sgshapiro hl = strlen(mxhosts[i]); 619064562Sgshapiro if (len - 1 < hl || len <= 1) 619164562Sgshapiro { 619264562Sgshapiro /* force to drop out of outer loop */ 619364562Sgshapiro len = -1; 619464562Sgshapiro break; 619564562Sgshapiro } 619638032Speter if (i != 0) 619764562Sgshapiro { 619864562Sgshapiro if (mxprefs[i] == mxprefs[i - 1]) 619964562Sgshapiro *p++ = ','; 620064562Sgshapiro else 620164562Sgshapiro *p++ = ':'; 620264562Sgshapiro len--; 620364562Sgshapiro } 620490792Sgshapiro (void) sm_strlcpy(p, mxhosts[i], len); 620564562Sgshapiro p += hl; 620664562Sgshapiro len -= hl; 620738032Speter } 620864562Sgshapiro 620964562Sgshapiro /* 621064562Sgshapiro ** break out of loop if len exceeded MAXHOSTSIGNATURE 621164562Sgshapiro ** because we won't have more space for further hosts 621264562Sgshapiro ** anyway (separated by : in the .cf file). 621364562Sgshapiro */ 621464562Sgshapiro 621564562Sgshapiro if (len < 0) 621664562Sgshapiro break; 621738032Speter if (endp != NULL) 621864562Sgshapiro *endp++ = sep; 621964562Sgshapiro prevsep = sep; 622038032Speter } 622190792Sgshapiro makelower(s->s_hostsig.hs_sig); 622238032Speter if (ConfigLevel < 2) 622338032Speter _res.options = oldoptions; 622464562Sgshapiro#else /* NAMED_BIND */ 622538032Speter /* not using BIND -- the signature is just the host name */ 622690792Sgshapiro /* 622790792Sgshapiro ** 'host' points to storage that will be freed after we are 622890792Sgshapiro ** done processing the current envelope, so we copy it. 622990792Sgshapiro */ 623090792Sgshapiro s->s_hostsig.hs_sig = sm_pstrdup_x(host); 623164562Sgshapiro#endif /* NAMED_BIND */ 623238032Speter if (tTd(17, 1)) 623390792Sgshapiro sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 623490792Sgshapiro return s->s_hostsig.hs_sig; 623538032Speter} 623690792Sgshapiro/* 623764562Sgshapiro** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 623864562Sgshapiro** 623964562Sgshapiro** The signature describes how we are going to send this -- it 624064562Sgshapiro** can be just the hostname (for non-Internet hosts) or can be 624164562Sgshapiro** an ordered list of MX hosts which must be randomized for equal 624264562Sgshapiro** MX preference values. 624364562Sgshapiro** 624464562Sgshapiro** Parameters: 624564562Sgshapiro** sig -- the host signature. 624664562Sgshapiro** mxhosts -- array to populate. 624790792Sgshapiro** mailer -- mailer. 624864562Sgshapiro** 624964562Sgshapiro** Returns: 625064562Sgshapiro** The number of hosts inserted into mxhosts array. 625164562Sgshapiro** 625264562Sgshapiro** Side Effects: 625364562Sgshapiro** Randomizes equal MX preference hosts in mxhosts. 625464562Sgshapiro*/ 625564562Sgshapiro 625664562Sgshapirostatic int 625764562Sgshapiroparse_hostsignature(sig, mxhosts, mailer) 625864562Sgshapiro char *sig; 625964562Sgshapiro char **mxhosts; 626064562Sgshapiro MAILER *mailer; 626164562Sgshapiro{ 626290792Sgshapiro unsigned short curpref = 0; 626390792Sgshapiro int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 626464562Sgshapiro char *hp, *endp; 626590792Sgshapiro unsigned short prefer[MAXMXHOSTS]; 626664562Sgshapiro long rndm[MAXMXHOSTS]; 626764562Sgshapiro 626864562Sgshapiro for (hp = sig; hp != NULL; hp = endp) 626964562Sgshapiro { 627064562Sgshapiro char sep = ':'; 627164562Sgshapiro 627264562Sgshapiro#if NETINET6 627364562Sgshapiro if (*hp == '[') 627464562Sgshapiro { 627564562Sgshapiro endp = strchr(hp + 1, ']'); 627664562Sgshapiro if (endp != NULL) 627764562Sgshapiro endp = strpbrk(endp + 1, ":,"); 627864562Sgshapiro } 627964562Sgshapiro else 628064562Sgshapiro endp = strpbrk(hp, ":,"); 628164562Sgshapiro#else /* NETINET6 */ 628264562Sgshapiro endp = strpbrk(hp, ":,"); 628364562Sgshapiro#endif /* NETINET6 */ 628464562Sgshapiro if (endp != NULL) 628564562Sgshapiro { 628664562Sgshapiro sep = *endp; 628764562Sgshapiro *endp = '\0'; 628864562Sgshapiro } 628964562Sgshapiro 629064562Sgshapiro mxhosts[nmx] = hp; 629164562Sgshapiro prefer[nmx] = curpref; 629264562Sgshapiro if (mci_match(hp, mailer)) 629364562Sgshapiro rndm[nmx] = 0; 629464562Sgshapiro else 629564562Sgshapiro rndm[nmx] = get_random(); 629664562Sgshapiro 629764562Sgshapiro if (endp != NULL) 629864562Sgshapiro { 629964562Sgshapiro /* 630064562Sgshapiro ** Since we don't have the original MX prefs, 630164562Sgshapiro ** make our own. If the separator is a ':', that 630264562Sgshapiro ** means the preference for the next host will be 630364562Sgshapiro ** higher than this one, so simply increment curpref. 630464562Sgshapiro */ 630564562Sgshapiro 630664562Sgshapiro if (sep == ':') 630764562Sgshapiro curpref++; 630864562Sgshapiro 630964562Sgshapiro *endp++ = sep; 631064562Sgshapiro } 631164562Sgshapiro if (++nmx >= MAXMXHOSTS) 631264562Sgshapiro break; 631364562Sgshapiro } 631464562Sgshapiro 631564562Sgshapiro /* sort the records using the random factor for equal preferences */ 631664562Sgshapiro for (i = 0; i < nmx; i++) 631764562Sgshapiro { 631864562Sgshapiro for (j = i + 1; j < nmx; j++) 631964562Sgshapiro { 632064562Sgshapiro /* 632164562Sgshapiro ** List is already sorted by MX preference, only 632264562Sgshapiro ** need to look for equal preference MX records 632364562Sgshapiro */ 632464562Sgshapiro 632564562Sgshapiro if (prefer[i] < prefer[j]) 632664562Sgshapiro break; 632764562Sgshapiro 632864562Sgshapiro if (prefer[i] > prefer[j] || 632964562Sgshapiro (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 633064562Sgshapiro { 633190792Sgshapiro register unsigned short tempp; 633264562Sgshapiro register long tempr; 633364562Sgshapiro register char *temp1; 633464562Sgshapiro 633564562Sgshapiro tempp = prefer[i]; 633664562Sgshapiro prefer[i] = prefer[j]; 633764562Sgshapiro prefer[j] = tempp; 633864562Sgshapiro temp1 = mxhosts[i]; 633964562Sgshapiro mxhosts[i] = mxhosts[j]; 634064562Sgshapiro mxhosts[j] = temp1; 634164562Sgshapiro tempr = rndm[i]; 634264562Sgshapiro rndm[i] = rndm[j]; 634364562Sgshapiro rndm[j] = tempr; 634464562Sgshapiro } 634564562Sgshapiro } 634664562Sgshapiro } 634764562Sgshapiro return nmx; 634864562Sgshapiro} 634964562Sgshapiro 6350363466Sgshapiro#if STARTTLS 635164562Sgshapirostatic SSL_CTX *clt_ctx = NULL; 635290792Sgshapirostatic bool tls_ok_clt = true; 635364562Sgshapiro 635490792Sgshapiro/* 635590792Sgshapiro** SETCLTTLS -- client side TLS: allow/disallow. 635664562Sgshapiro** 635764562Sgshapiro** Parameters: 635890792Sgshapiro** tls_ok -- should tls be done? 635990792Sgshapiro** 636090792Sgshapiro** Returns: 636164562Sgshapiro** none. 636264562Sgshapiro** 636390792Sgshapiro** Side Effects: 636490792Sgshapiro** sets tls_ok_clt (static variable in this module) 636590792Sgshapiro*/ 636690792Sgshapiro 636790792Sgshapirovoid 636890792Sgshapirosetclttls(tls_ok) 636990792Sgshapiro bool tls_ok; 637090792Sgshapiro{ 637190792Sgshapiro tls_ok_clt = tls_ok; 637290792Sgshapiro return; 637390792Sgshapiro} 637490792Sgshapiro/* 637590792Sgshapiro** INITCLTTLS -- initialize client side TLS 637690792Sgshapiro** 637790792Sgshapiro** Parameters: 637890792Sgshapiro** tls_ok -- should tls initialization be done? 637990792Sgshapiro** 638064562Sgshapiro** Returns: 638164562Sgshapiro** succeeded? 638290792Sgshapiro** 638390792Sgshapiro** Side Effects: 638490792Sgshapiro** sets tls_ok_clt (static variable in this module) 638564562Sgshapiro*/ 638664562Sgshapiro 638764562Sgshapirobool 638890792Sgshapiroinitclttls(tls_ok) 638990792Sgshapiro bool tls_ok; 639064562Sgshapiro{ 639190792Sgshapiro if (!tls_ok_clt) 639290792Sgshapiro return false; 639390792Sgshapiro tls_ok_clt = tls_ok; 639490792Sgshapiro if (!tls_ok_clt) 639590792Sgshapiro return false; 639664562Sgshapiro if (clt_ctx != NULL) 639790792Sgshapiro return true; /* already done */ 6398203004Sgshapiro tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, Clt_SSL_Options, false, 6399363466Sgshapiro CltCertFile, CltKeyFile, 6400363466Sgshapiro# if _FFR_CLIENTCA 6401363466Sgshapiro (CltCACertPath != NULL) ? CltCACertPath : 6402363466Sgshapiro# endif 6403363466Sgshapiro CACertPath, 6404363466Sgshapiro# if _FFR_CLIENTCA 6405363466Sgshapiro (CltCACertFile != NULL) ? CltCACertFile : 6406363466Sgshapiro# endif 6407363466Sgshapiro CACertFile, 6408363466Sgshapiro DHParams); 640990792Sgshapiro return tls_ok_clt; 641064562Sgshapiro} 641164562Sgshapiro 641290792Sgshapiro/* 641364562Sgshapiro** STARTTLS -- try to start secure connection (client side) 641464562Sgshapiro** 641564562Sgshapiro** Parameters: 641664562Sgshapiro** m -- the mailer. 641764562Sgshapiro** mci -- the mailer connection info. 641864562Sgshapiro** e -- the envelope. 641964562Sgshapiro** 642064562Sgshapiro** Returns: 642164562Sgshapiro** success? 642264562Sgshapiro** (maybe this should be some other code than EX_ 642364562Sgshapiro** that denotes which stage failed.) 642464562Sgshapiro*/ 642564562Sgshapiro 642664562Sgshapirostatic int 6427363466Sgshapirostarttls(m, mci, e 6428363466Sgshapiro# if DANE 6429363466Sgshapiro , dane_vrfy_ctx 6430363466Sgshapiro# endif 6431363466Sgshapiro ) 643264562Sgshapiro MAILER *m; 643364562Sgshapiro MCI *mci; 643464562Sgshapiro ENVELOPE *e; 6435363466Sgshapiro# if DANE 6436363466Sgshapiro dane_vrfy_ctx_P dane_vrfy_ctx; 6437363466Sgshapiro# endif 643864562Sgshapiro{ 643964562Sgshapiro int smtpresult; 644066494Sgshapiro int result = 0; 644166494Sgshapiro int rfd, wfd; 644264562Sgshapiro SSL *clt_ssl = NULL; 644390792Sgshapiro time_t tlsstart; 6444363466Sgshapiro extern int TLSsslidx; 644564562Sgshapiro 644690792Sgshapiro if (clt_ctx == NULL && !initclttls(true)) 644766494Sgshapiro return EX_TEMPFAIL; 6448203004Sgshapiro 6449363466Sgshapiro if (!TLS_set_engine(SSLEngine, false)) 6450203004Sgshapiro { 6451203004Sgshapiro sm_syslog(LOG_ERR, NOQID, 6452363466Sgshapiro "STARTTLS=client, engine=%s, TLS_set_engine=failed", 6453363466Sgshapiro SSLEngine); 6454203004Sgshapiro return EX_TEMPFAIL; 6455203004Sgshapiro } 6456203004Sgshapiro 645764562Sgshapiro smtpmessage("STARTTLS", m, mci); 645864562Sgshapiro 645964562Sgshapiro /* get the reply */ 6460132943Sgshapiro smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6461132943Sgshapiro XS_STARTTLS); 646264562Sgshapiro 646364562Sgshapiro /* check return code from server */ 6464157001Sgshapiro if (REPLYTYPE(smtpresult) == 4) 646564562Sgshapiro return EX_TEMPFAIL; 646664562Sgshapiro if (smtpresult == 501) 646764562Sgshapiro return EX_USAGE; 646864562Sgshapiro if (smtpresult == -1) 646964562Sgshapiro return smtpresult; 6470157001Sgshapiro 6471157001Sgshapiro /* not an expected reply but we have to deal with it */ 6472157001Sgshapiro if (REPLYTYPE(smtpresult) == 5) 6473157001Sgshapiro return EX_UNAVAILABLE; 647464562Sgshapiro if (smtpresult != 220) 647564562Sgshapiro return EX_PROTOCOL; 647664562Sgshapiro 647764562Sgshapiro if (LogLevel > 13) 647890792Sgshapiro sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 647964562Sgshapiro 648064562Sgshapiro /* start connection */ 648164562Sgshapiro if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 648264562Sgshapiro { 648364562Sgshapiro if (LogLevel > 5) 648464562Sgshapiro { 648590792Sgshapiro sm_syslog(LOG_ERR, NOQID, 648690792Sgshapiro "STARTTLS=client, error: SSL_new failed"); 6487363466Sgshapiro tlslogerr(LOG_WARNING, 9, "client"); 648864562Sgshapiro } 648964562Sgshapiro return EX_SOFTWARE; 649064562Sgshapiro } 6491285229Sgshapiro /* SSL_clear(clt_ssl); ? */ 649264562Sgshapiro 6493363466Sgshapiro if (get_tls_se_options(e, clt_ssl, &mci->mci_tlsi, false) != 0) 6494285229Sgshapiro { 6495285229Sgshapiro sm_syslog(LOG_ERR, NOQID, 6496285229Sgshapiro "STARTTLS=client, get_tls_se_options=fail"); 6497285229Sgshapiro return EX_SOFTWARE; 6498285229Sgshapiro } 6499363466Sgshapiro result = SSL_set_ex_data(clt_ssl, TLSsslidx, &mci->mci_tlsi); 6500363466Sgshapiro if (0 == result) 6501363466Sgshapiro { 6502363466Sgshapiro if (LogLevel > 5) 6503363466Sgshapiro { 6504363466Sgshapiro sm_syslog(LOG_ERR, NOQID, 6505363466Sgshapiro "STARTTLS=client, error: SSL_set_ex_data failed=%d, idx=%d", 6506363466Sgshapiro result, TLSsslidx); 6507363466Sgshapiro tlslogerr(LOG_WARNING, 9, "client"); 6508363466Sgshapiro } 6509363466Sgshapiro return EX_SOFTWARE; 6510363466Sgshapiro } 6511363466Sgshapiro# if DANE 6512363466Sgshapiro if (SM_TLSI_IS(&(mci->mci_tlsi), TLSI_FL_NODANE)) 6513363466Sgshapiro dane_vrfy_ctx->dane_vrfy_chk = DANE_NEVER; 6514363466Sgshapiro else 6515363466Sgshapiro { 6516363466Sgshapiro int r; 6517285229Sgshapiro 6518363466Sgshapiro# define SM_IS_EMPTY(s) (NULL == (s) || '\0' == *(s)) 6519363466Sgshapiro 6520363466Sgshapiro /* set SNI only if there is a TLSA RR */ 6521363466Sgshapiro if (dane_get_tlsa(dane_vrfy_ctx) != NULL && 6522363466Sgshapiro !(SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_host) && 6523363466Sgshapiro SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni)) && 6524363466Sgshapiro (r = SSL_set_tlsext_host_name(clt_ssl, 6525363466Sgshapiro (!SM_IS_EMPTY(dane_vrfy_ctx->dane_vrfy_sni) 6526363466Sgshapiro ? dane_vrfy_ctx->dane_vrfy_sni 6527363466Sgshapiro : dane_vrfy_ctx->dane_vrfy_host))) <= 0) 6528363466Sgshapiro { 6529363466Sgshapiro if (LogLevel > 5) 6530363466Sgshapiro { 6531363466Sgshapiro sm_syslog(LOG_ERR, NOQID, 6532363466Sgshapiro "STARTTLS=client, host=%s, SSL_set_tlsext_host_name=%d", 6533363466Sgshapiro dane_vrfy_ctx->dane_vrfy_host, r); 6534363466Sgshapiro } 6535363466Sgshapiro tlslogerr(LOG_ERR, 5, "client"); 6536363466Sgshapiro /* return EX_SOFTWARE; */ 6537363466Sgshapiro } 6538363466Sgshapiro } 6539363466Sgshapiro memcpy(&mci->mci_tlsi.tlsi_dvc, dane_vrfy_ctx, sizeof(*dane_vrfy_ctx)); 6540363466Sgshapiro# endif /* DANE */ 6541363466Sgshapiro 654290792Sgshapiro rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 654390792Sgshapiro wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 654466494Sgshapiro 654566494Sgshapiro if (rfd < 0 || wfd < 0 || 654690792Sgshapiro (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 654790792Sgshapiro (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 654864562Sgshapiro { 654964562Sgshapiro if (LogLevel > 5) 655064562Sgshapiro { 655190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 655290792Sgshapiro "STARTTLS=client, error: SSL_set_xfd failed=%d", 655390792Sgshapiro result); 6554363466Sgshapiro tlslogerr(LOG_WARNING, 9, "client"); 655564562Sgshapiro } 655664562Sgshapiro return EX_SOFTWARE; 655764562Sgshapiro } 655864562Sgshapiro SSL_set_connect_state(clt_ssl); 655990792Sgshapiro tlsstart = curtime(); 656090792Sgshapiro 656190792Sgshapirossl_retry: 656264562Sgshapiro if ((result = SSL_connect(clt_ssl)) <= 0) 656364562Sgshapiro { 6564157001Sgshapiro int i, ssl_err; 6565285229Sgshapiro int save_errno = errno; 656664562Sgshapiro 6567157001Sgshapiro ssl_err = SSL_get_error(clt_ssl, result); 6568157001Sgshapiro i = tls_retry(clt_ssl, rfd, wfd, tlsstart, 6569157001Sgshapiro TimeOuts.to_starttls, ssl_err, "client"); 6570157001Sgshapiro if (i > 0) 6571157001Sgshapiro goto ssl_retry; 657290792Sgshapiro 6573157001Sgshapiro if (LogLevel > 5) 657490792Sgshapiro { 6575244833Sgshapiro unsigned long l; 6576244833Sgshapiro const char *sr; 6577244833Sgshapiro 6578244833Sgshapiro l = ERR_peek_error(); 6579244833Sgshapiro sr = ERR_reason_error_string(l); 6580157001Sgshapiro sm_syslog(LOG_WARNING, NOQID, 6581244833Sgshapiro "STARTTLS=client, error: connect failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d", 6582244833Sgshapiro result, sr == NULL ? "unknown" : sr, ssl_err, 6583285229Sgshapiro save_errno, i); 6584363466Sgshapiro tlslogerr(LOG_WARNING, 9, "client"); 6585110560Sgshapiro } 658690792Sgshapiro 6587363466Sgshapiro SM_SSL_FREE(clt_ssl); 658864562Sgshapiro return EX_SOFTWARE; 658964562Sgshapiro } 659064562Sgshapiro mci->mci_ssl = clt_ssl; 659190792Sgshapiro result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 659290792Sgshapiro &mci->mci_macro, true); 659364562Sgshapiro 659490792Sgshapiro /* switch to use TLS... */ 659564562Sgshapiro if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 659664562Sgshapiro return EX_OK; 659764562Sgshapiro 659864562Sgshapiro /* failure */ 6599363466Sgshapiro SM_SSL_FREE(clt_ssl); 660064562Sgshapiro return EX_SOFTWARE; 660164562Sgshapiro} 660290792Sgshapiro/* 660364562Sgshapiro** ENDTLSCLT -- shutdown secure connection (client side) 660464562Sgshapiro** 660564562Sgshapiro** Parameters: 660664562Sgshapiro** mci -- the mailer connection info. 660764562Sgshapiro** 660864562Sgshapiro** Returns: 660964562Sgshapiro** success? 661064562Sgshapiro*/ 661190792Sgshapiro 661290792Sgshapirostatic int 661364562Sgshapiroendtlsclt(mci) 661464562Sgshapiro MCI *mci; 661564562Sgshapiro{ 661664562Sgshapiro int r; 661764562Sgshapiro 661864562Sgshapiro if (!bitset(MCIF_TLSACT, mci->mci_flags)) 661964562Sgshapiro return EX_OK; 6620363466Sgshapiro r = endtls(&mci->mci_ssl, "client"); 662164562Sgshapiro mci->mci_flags &= ~MCIF_TLSACT; 662264562Sgshapiro return r; 662364562Sgshapiro} 6624363466Sgshapiro#endif /* STARTTLS */ 6625363466Sgshapiro#if STARTTLS || SASL 662690792Sgshapiro/* 662790792Sgshapiro** ISCLTFLGSET -- check whether client flag is set. 662864562Sgshapiro** 662964562Sgshapiro** Parameters: 663090792Sgshapiro** e -- envelope. 663190792Sgshapiro** flag -- flag to check in {client_flags} 663264562Sgshapiro** 663364562Sgshapiro** Returns: 663490792Sgshapiro** true iff flag is set. 663564562Sgshapiro*/ 663664562Sgshapiro 663790792Sgshapirostatic bool 663890792Sgshapiroiscltflgset(e, flag) 663990792Sgshapiro ENVELOPE *e; 664090792Sgshapiro int flag; 664164562Sgshapiro{ 664290792Sgshapiro char *p; 664373188Sgshapiro 664490792Sgshapiro p = macvalue(macid("{client_flags}"), e); 664590792Sgshapiro if (p == NULL) 664690792Sgshapiro return false; 664790792Sgshapiro for (; *p != '\0'; p++) 664864562Sgshapiro { 664990792Sgshapiro /* look for just this one flag */ 665090792Sgshapiro if (*p == (char) flag) 665190792Sgshapiro return true; 665264562Sgshapiro } 665390792Sgshapiro return false; 665464562Sgshapiro} 6655363466Sgshapiro#endif /* STARTTLS || SASL */ 6656