2 * Copyright (c) 1998-2006, 2008-2010, 2014 Proofpoint, Inc. and its suppliers.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
16 SM_RCSID("@(#)$Id: usersmtp.c,v 8.488 2013-11-22 20:51:57 ca Exp $")
18 #include <sm/sendmail.h>
20 static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
21 static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
22 static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
25 extern void *sm_sasl_malloc __P((unsigned long));
26 extern void sm_sasl_free __P((void *));
30 ** USERSMTP -- run SMTP protocol from the user end.
32 ** This protocol is described in RFC821.
35 #define SMTPCLOSING 421 /* "Service Shutting Down" */
37 #define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
39 #define ENHSCN_RPOOL(e, d, rpool) \
40 ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
42 static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
43 static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
44 static bool SmtpNeedIntro; /* need "while talking" in transcript */
47 ** SMTPCCLRSE -- clear session related data in envelope
62 e->e_renhsc[0] = '\0';
69 ** Reset to avoid access to potentially dangling pointer
77 ** SMTPINIT -- initialize SMTP.
79 ** Opens the connection and sends the initial protocol.
82 ** m -- mailer to create connection to.
83 ** mci -- the mailer connection info.
85 ** onlyhelo -- send only helo command?
91 ** creates connection and sends initial protocol.
95 smtpinit(m, mci, e, onlyhelo)
105 #if _FFR_EXPAND_HELONAME
106 char hnbuf[MAXNAME + 1]; /* EAI:ok:EHLO name must be ASCII */
113 sm_dprintf("smtpinit ");
114 mci_dump(sm_debug_file(), mci, false);
118 ** Open the connection to the mailer.
122 SmtpMsgBuffer[0] = '\0';
123 CurHostName = mci->mci_host; /* XXX UGLY XXX */
124 if (CurHostName == NULL)
125 CurHostName = MyHostName;
126 SmtpNeedIntro = true;
127 state = mci->mci_state;
129 e->e_renhsc[0] = '\0';
131 maps_reset_chged("client:smtpinit");
137 /* need to clear old information */
149 /* shouldn't happen */
154 syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
163 mci->mci_state = MCIS_OPENING;
167 ** Get the greeting message.
168 ** This should appear spontaneously. Give it five minutes to
172 SmtpPhase = mci->mci_phase = "client greeting";
173 sm_setproctitle(true, e, "%s %s: %s",
174 qid_printname(e), CurHostName, mci->mci_phase);
175 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL, XS_GREET,
179 if (REPLYTYPE(r) == 4)
181 if (REPLYTYPE(r) != 2)
185 ** Send the HELO command.
186 ** My mother taught me to always introduce myself.
190 if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
191 mci->mci_flags |= MCIF_ESMTP;
192 if (mci->mci_heloname != NULL)
194 #if _FFR_EXPAND_HELONAME
195 expand(mci->mci_heloname, hnbuf, sizeof(hnbuf), e);
198 hn = mci->mci_heloname;
205 #if _FFR_IGNORE_EXT_ON_HELO
206 mci->mci_flags &= ~MCIF_HELO;
208 if (bitnset(M_LMTP, m->m_flags))
210 smtpmessage("LHLO %s", m, mci, hn);
211 SmtpPhase = mci->mci_phase = "client LHLO";
213 else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
214 !bitnset(M_FSMTP, m->m_flags))
216 smtpmessage("EHLO %s", m, mci, hn);
217 SmtpPhase = mci->mci_phase = "client EHLO";
221 smtpmessage("HELO %s", m, mci, hn);
222 SmtpPhase = mci->mci_phase = "client HELO";
223 #if _FFR_IGNORE_EXT_ON_HELO
224 mci->mci_flags |= MCIF_HELO;
227 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
228 CurHostName, mci->mci_phase);
230 bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
232 helo_options, NULL, XS_EHLO, NULL);
235 else if (REPLYTYPE(r) == 5)
237 if (bitset(MCIF_ESMTP, mci->mci_flags) &&
238 !bitnset(M_LMTP, m->m_flags))
240 /* try old SMTP instead */
241 mci->mci_flags &= ~MCIF_ESMTP;
246 else if (REPLYTYPE(r) != 2)
250 ** Check to see if we actually ended up talking to ourself.
251 ** This means we didn't know about an alias or MX, or we managed
252 ** to connect to an echo server.
255 p = strchr(&SmtpReplyBuffer[4], ' ');
258 if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
259 !bitnset(M_LMTP, m->m_flags) &&
260 SM_STRCASEEQ(&SmtpReplyBuffer[4], MyHostName))
262 syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
264 mci_setstat(mci, EX_CONFIG, "5.3.5",
265 "553 5.3.5 system config error");
272 ** If this is expected to be another sendmail, send some internal
274 ** If we're running as MSP, "propagate" -v flag if possible.
277 if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
278 || bitnset(M_INTERNAL, m->m_flags))
280 /* tell it to be verbose */
281 smtpmessage("VERB", m, mci);
282 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
288 if (mci->mci_state != MCIS_CLOSED)
290 mci->mci_state = MCIS_OPEN;
294 /* got a 421 error code during startup */
297 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
298 if (mci->mci_state != MCIS_CLOSED)
303 /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
304 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
306 if (mci->mci_state != MCIS_CLOSED)
311 mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
316 ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
319 ** line -- the response line.
320 ** firstline -- set if this is the first line of the reply.
322 ** mci -- the mailer connection info.
323 ** e -- the envelope.
330 esmtp_check(line, firstline, m, mci, e)
337 if (strstr(line, "ESMTP") != NULL)
338 mci->mci_flags |= MCIF_ESMTP;
341 ** Dirty hack below. Quoting the author:
342 ** This was a response to people who wanted SMTP transmission to be
343 ** just-send-8 by default. Essentially, you could put this tag into
344 ** your greeting message to behave as though the F=8 flag was set on
348 if (strstr(line, "8BIT-OK") != NULL)
349 mci->mci_flags |= MCIF_8BITOK;
353 /* specify prototype so compiler can check calls */
354 static char *str_union __P((char *, char *, SM_RPOOL_T *));
357 ** STR_UNION -- create the union of two lists
360 ** s1, s2 -- lists of items (separated by single blanks).
361 ** rpool -- resource pool from which result is allocated.
364 ** the union of both lists.
368 str_union(s1, s2, rpool)
372 char *hr, *h1, *h, *res;
384 sm_syslog(LOG_WARNING, NOQID,
385 "str_union: stringlen1=%d, stringlen2=%d, sum=%d, status=overflow",
390 res = (char *) sm_rpool_malloc(rpool, rl + 2);
397 (void) sm_strlcpy(res, s1, rl);
402 /* walk through s2 */
403 while (h != NULL && *h1 != '\0')
405 /* is there something after the current word? */
406 if ((h = strchr(h1, ' ')) != NULL)
410 /* does the current word appear in s1 ? */
411 if (iteminlist(h1, s1, " ") == NULL)
413 /* add space as delimiter */
419 /* advance pointer in result list */
425 /* there are more items */
435 ** HELO_OPTIONS -- process the options on a HELO line.
438 ** line -- the response line.
439 ** firstline -- set if this is the first line of the reply.
441 ** mci -- the mailer connection info.
442 ** e -- the envelope (unused).
449 helo_options(line, firstline, m, mci, e)
457 #if _FFR_IGNORE_EXT_ON_HELO
458 static bool logged = false;
463 mci_clr_extensions(mci);
464 #if _FFR_IGNORE_EXT_ON_HELO
469 #if _FFR_IGNORE_EXT_ON_HELO
470 else if (bitset(MCIF_HELO, mci->mci_flags))
472 if (LogLevel > 8 && !logged)
474 sm_syslog(LOG_WARNING, NOQID,
475 "server=%s [%s] returned extensions despite HELO command",
476 macvalue(macid("{server_name}"), e),
477 macvalue(macid("{server_addr}"), e));
482 #endif /* _FFR_IGNORE_EXT_ON_HELO */
484 if (strlen(line) < 5)
487 p = strpbrk(line, " =");
490 if (SM_STRCASEEQ(line, "size"))
492 mci->mci_flags |= MCIF_SIZE;
494 mci->mci_maxsize = atol(p);
496 else if (SM_STRCASEEQ(line, "8bitmime"))
498 mci->mci_flags |= MCIF_8BITMIME;
499 mci->mci_flags &= ~MCIF_7BIT;
501 else if (SM_STRCASEEQ(line, "expn"))
502 mci->mci_flags |= MCIF_EXPN;
503 else if (SM_STRCASEEQ(line, "dsn"))
504 mci->mci_flags |= MCIF_DSN;
505 else if (SM_STRCASEEQ(line, "enhancedstatuscodes"))
506 mci->mci_flags |= MCIF_ENHSTAT;
507 else if (SM_STRCASEEQ(line, "pipelining"))
508 mci->mci_flags |= MCIF_PIPELINED;
509 else if (SM_STRCASEEQ(line, "verb"))
510 mci->mci_flags |= MCIF_VERB;
512 else if (SM_STRCASEEQ(line, "smtputf8"))
513 mci->mci_flags |= MCIF_EAI;
516 else if (SM_STRCASEEQ(line, "starttls"))
517 mci->mci_flags |= MCIF_TLS;
519 else if (SM_STRCASEEQ(line, "deliverby"))
521 mci->mci_flags |= MCIF_DLVR_BY;
523 mci->mci_min_by = atol(p);
526 else if (SM_STRCASEEQ(line, "auth"))
528 if (p != NULL && *p != '\0' &&
529 !bitset(MCIF_AUTH2, mci->mci_flags))
531 if (mci->mci_saslcap != NULL)
534 ** Create the union with previous auth
535 ** offerings because we recognize "auth "
536 ** and "auth=" (old format).
539 mci->mci_saslcap = str_union(mci->mci_saslcap,
541 mci->mci_flags |= MCIF_AUTH2;
548 mci->mci_saslcap = (char *)
549 sm_rpool_malloc(mci->mci_rpool, l);
550 if (mci->mci_saslcap != NULL)
552 (void) sm_strlcpy(mci->mci_saslcap, p,
554 mci->mci_flags |= MCIF_AUTH;
559 sm_syslog(LOG_DEBUG, NOQID, "AUTH flags=%lx, mechs=%s",
560 mci->mci_flags, mci->mci_saslcap);
566 static int getsimple __P((void *, int, const char **, unsigned *));
567 static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
568 static int saslgetrealm __P((void *, int, const char **, const char **));
569 static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
570 static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *));
571 static char *removemech __P((char *, char *, SM_RPOOL_T *));
572 static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
574 static sasl_callback_t callbacks[] =
576 { SASL_CB_GETREALM, (sasl_callback_ft)&saslgetrealm, NULL },
577 #define CB_GETREALM_IDX 0
578 { SASL_CB_PASS, (sasl_callback_ft)&getsecret, NULL },
579 #define CB_PASS_IDX 1
580 { SASL_CB_USER, (sasl_callback_ft)&getsimple, NULL },
581 #define CB_USER_IDX 2
582 { SASL_CB_AUTHNAME, (sasl_callback_ft)&getsimple, NULL },
583 #define CB_AUTHNAME_IDX 3
584 { SASL_CB_VERIFYFILE, (sasl_callback_ft)&safesaslfile, NULL },
585 #define CB_SAFESASL_IDX 4
586 { SASL_CB_LIST_END, NULL, NULL }
590 ** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
596 ** SASL_OK -- if successful.
597 ** SASL error code -- otherwise.
600 ** checks/sets sasl_clt_init.
603 ** Callbacks are ignored if sasl_client_init() has
604 ** been called before (by a library such as libnss_ldap)
607 static bool sasl_clt_init = false;
616 result = sasl_client_init(callbacks);
618 /* should we retry later again or just remember that it failed? */
619 if (result == SASL_OK)
620 sasl_clt_init = true;
624 ** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
633 ** checks/sets sasl_clt_init.
641 sasl_clt_init = false;
645 ** GETSASLDATA -- process the challenges from the SASL protocol
647 ** This gets the relevant sasl response data out of the reply
651 ** line -- the response line.
652 ** firstline -- set if this is the first line of the reply.
654 ** mci -- the mailer connection info.
655 ** e -- the envelope (unused).
661 static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
664 getsasldata(line, firstline, m, mci, e)
677 /* if not a continue we don't care about it */
681 !isascii(line[1]) || !isdigit(line[1]) ||
682 !isascii(line[2]) || !isdigit(line[2]))
684 SM_FREE(mci->mci_sasl_string);
688 /* forget about "334 " */
692 /* XXX put this into a macro/function? It's duplicated below */
693 if (mci->mci_sasl_string != NULL)
695 if (mci->mci_sasl_string_len <= len)
697 sm_free(mci->mci_sasl_string); /* XXX */
698 mci->mci_sasl_string = xalloc(len + 1);
702 mci->mci_sasl_string = xalloc(len + 1);
704 result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
705 (unsigned int *) &mci->mci_sasl_string_len);
706 if (result != SASL_OK)
708 mci->mci_sasl_string_len = 0;
709 *mci->mci_sasl_string = '\0';
711 # else /* SASL >= 20000 */
712 out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
713 result = sasl_decode64(line, len, out, (unsigned int *) &len);
714 if (result != SASL_OK)
721 ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
722 ** it can't be in an rpool unless we use the same memory
723 ** management mechanism (with same rpool!) for Cyrus SASL.
726 if (mci->mci_sasl_string != NULL)
728 if (mci->mci_sasl_string_len <= len)
730 sm_free(mci->mci_sasl_string); /* XXX */
731 mci->mci_sasl_string = xalloc(len + 1);
735 mci->mci_sasl_string = xalloc(len + 1);
737 memcpy(mci->mci_sasl_string, out, len);
738 mci->mci_sasl_string[len] = '\0';
739 mci->mci_sasl_string_len = len;
740 # endif /* SASL >= 20000 */
744 ** READAUTH -- read auth values from a file
747 ** filename -- name of file to read.
748 ** safe -- if set, this is a safe read.
749 ** sai -- where to store auth_info.
750 ** rpool -- resource pool for sai.
753 ** EX_OK -- data successfully read.
754 ** EX_UNAVAILABLE -- no valid filename.
755 ** EX_TEMPFAIL -- temporary failure.
758 static char *sasl_info_name[] =
767 readauth(filename, safe, sai, rpool)
780 if (SM_IS_EMPTY(filename))
781 return EX_UNAVAILABLE;
783 # if !_FFR_ALLOW_SASLINFO
785 ** make sure we don't use a program that is not
786 ** accessible to the user who specified a different authinfo file.
787 ** However, currently we don't pass this info (authinfo file
788 ** specified by user) around, so we just turn off program access.
791 if (filename[0] == '|')
796 char *argv[MAXPV + 1];
799 for (p = strtok(&filename[1], " \t"); p != NULL;
800 p = strtok(NULL, " \t"))
807 pid = prog_open(argv, &fd, CurEnv);
811 f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
812 (void *) &fd, SM_IO_RDONLY, NULL);
815 # endif /* !_FFR_ALLOW_SASLINFO */
816 /* "else" in #if code above */
819 sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
820 |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
821 if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
822 sff |= SFF_NOGRFILES;
823 if (DontLockReadFiles)
826 # if _FFR_ALLOW_SASLINFO
828 ** XXX: make sure we don't read or open files that are not
829 ** accessible to the user who specified a different authinfo
834 # else /* _FFR_ALLOW_SASLINFO */
836 sff |= SFF_OPENASROOT;
837 # endif /* _FFR_ALLOW_SASLINFO */
839 f = safefopen(filename, O_RDONLY, 0, sff);
844 sm_syslog(LOG_ERR, NOQID,
845 "AUTH=client, error: can't open %s: %s",
846 filename, sm_errstring(errno));
851 while (lc <= SASL_MECHLIST &&
852 sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
856 (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
857 if ((s = strchr((*sai)[lc], '\n')) != NULL)
863 (void) sm_io_close(f, SM_TIME_DEFAULT);
866 if (lc < SASL_PASSWORD)
869 sm_syslog(LOG_ERR, NOQID,
870 "AUTH=client, error: can't read %s from %s",
871 sasl_info_name[lc + 1], filename);
878 ** GETAUTH -- get authinfo from ruleset call
880 ** {server_name}, {server_addr} must be set
883 ** mci -- the mailer connection structure.
884 ** e -- the envelope (including the sender to specify).
885 ** sai -- pointer to authinfo (result).
888 ** EX_OK -- ruleset was successfully called, data may not
889 ** be available, sai must be checked.
890 ** EX_UNAVAILABLE -- ruleset unavailable (or failed).
891 ** EX_TEMPFAIL -- temporary failure (from ruleset).
894 ** Fills in sai if successful.
903 int i, r, l, got, ret;
905 char pvpbuf[PSBUFSIZE];
907 r = rscap("authinfo", macvalue(macid("{server_name}"), e),
908 macvalue(macid("{server_addr}"), e), e,
909 &pvp, pvpbuf, sizeof(pvpbuf));
912 return EX_UNAVAILABLE;
914 /* other than expected return value: ok (i.e., no auth) */
915 if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
917 if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
921 ** parse the data, put it into sai
922 ** format: "TDstring" (including the '"' !)
923 ** where T is a tag: 'U', ...
924 ** D is a delimiter: ':' or '='
927 ret = EX_OK; /* default return value */
930 while (i < SASL_ENTRIES)
932 if (pvp[i + 1] == NULL)
934 if (pvp[i + 1][0] != '"')
936 switch (pvp[i + 1][1])
961 l = strlen(pvp[i + 1]);
964 if (l <= 3 || pvp[i + 1][l - 1] != '"')
967 /* remove closing quote */
968 pvp[i + 1][l - 1] = '\0';
970 /* remove "TD and " */
972 (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
973 if ((*sai)[r] == NULL)
975 if (pvp[i + 1][2] == ':')
977 /* ':text' (just copy) */
978 (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
981 else if (pvp[i + 1][2] == '=')
985 /* '=base64' (decode) */
987 ret = sasl_decode64(pvp[i + 1] + 3,
988 (unsigned int) l, (*sai)[r],
989 (unsigned int) l + 1, &len);
990 # else /* SASL >= 20000 */
991 ret = sasl_decode64(pvp[i + 1] + 3,
992 (unsigned int) l, (*sai)[r], &len);
993 # endif /* SASL >= 20000 */
1001 sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
1002 sasl_info_name[r], (*sai)[r]);
1006 /* did we get the expected data? */
1007 /* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
1008 if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
1009 bitset(SASL_PASSWORD_BIT, got)))
1012 /* no authid? copy uid */
1013 if (!bitset(SASL_AUTHID_BIT, got))
1015 l = strlen((*sai)[SASL_USER]) + 1;
1016 (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
1018 if ((*sai)[SASL_AUTHID] == NULL)
1020 (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
1023 /* no uid? copy authid */
1024 if (!bitset(SASL_USER_BIT, got))
1026 l = strlen((*sai)[SASL_AUTHID]) + 1;
1027 (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
1029 if ((*sai)[SASL_USER] == NULL)
1031 (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
1039 sm_syslog(LOG_WARNING, NOQID,
1040 "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
1041 macvalue(macid("{server_name}"), e),
1042 macvalue(macid("{server_addr}"), e),
1043 ret == EX_TEMPFAIL ? "temp" : "");
1044 for (i = 0; i <= SASL_MECHLIST; i++)
1045 (*sai)[i] = NULL; /* just clear; rpool */
1051 ** GETSIMPLE -- callback to get userid or authid
1056 ** result -- (pointer to) result
1057 ** len -- (pointer to) length of result
1060 ** OK/failure values
1064 getsimple(context, id, result, len)
1067 const char **result;
1072 if (result == NULL || context == NULL)
1073 return SASL_BADPARAM;
1074 sai = (SASL_AI_T *) context;
1079 *result = (*sai)[SASL_USER];
1081 sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1084 *len = *result != NULL ? strlen(*result) : 0;
1087 case SASL_CB_AUTHNAME:
1088 *result = (*sai)[SASL_AUTHID];
1090 sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1093 *len = *result != NULL ? strlen(*result) : 0;
1096 case SASL_CB_LANGUAGE:
1103 return SASL_BADPARAM;
1108 ** GETSECRET -- callback to get password
1111 ** conn -- connection information
1114 ** psecret -- (pointer to) result
1117 ** OK/failure values
1121 getsecret(conn, context, id, psecret)
1123 SM_UNUSED(void *context);
1125 sasl_secret_t **psecret;
1131 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1132 return SASL_BADPARAM;
1134 mci = (MCI *) context;
1135 authpass = mci->mci_sai[SASL_PASSWORD];
1136 len = strlen(authpass);
1139 ** use an rpool because we are responsible for free()ing the secret,
1140 ** but we can't free() it until after the auth completes
1143 *psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
1144 sizeof(sasl_secret_t) +
1146 if (*psecret == NULL)
1148 (void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
1149 (*psecret)->len = (unsigned long) len;
1152 # else /* SASL >= 20000 */
1154 ** GETSIMPLE -- callback to get userid or authid
1159 ** result -- (pointer to) result
1160 ** len -- (pointer to) length of result
1163 ** OK/failure values
1167 getsimple(context, id, result, len)
1170 const char **result;
1179 char *authid = NULL;
1181 if (result == NULL || context == NULL)
1182 return SASL_BADPARAM;
1183 sai = (SASL_AI_T *) context;
1186 ** Unfortunately it is not clear whether this routine should
1187 ** return a copy of a string or just a pointer to a string.
1188 ** The Cyrus-SASL plugins treat these return values differently, e.g.,
1189 ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1190 ** The best solution to this problem is to fix Cyrus-SASL, but it
1191 ** seems there is nobody who creates patches... Hello CMU!?
1192 ** The second best solution is to have flags that tell this routine
1193 ** whether to return an malloc()ed copy.
1194 ** The next best solution is to always return an malloc()ed copy,
1195 ** and suffer from some memory leak, which is ugly for persistent
1197 ** For now we go with the last solution...
1198 ** We can't use rpools (which would avoid this particular problem)
1199 ** as explained in sasl.c.
1205 l = strlen((*sai)[SASL_USER]) + 1;
1206 s = sm_sasl_malloc(l);
1214 (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
1217 sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1220 *len = *result != NULL ? strlen(*result) : 0;
1223 case SASL_CB_AUTHNAME:
1224 h = (*sai)[SASL_AUTHID];
1226 /* XXX maybe other mechanisms too?! */
1227 addrealm = (*sai)[SASL_MECH] != NULL &&
1228 SM_STRCASEEQ((*sai)[SASL_MECH], "cram-md5");
1231 ** Add realm to authentication id unless authid contains
1232 ** '@' (i.e., a realm) or the default realm is empty.
1235 if (addrealm && h != NULL && strchr(h, '@') == NULL)
1237 /* has this been done before? */
1238 if ((*sai)[SASL_ID_REALM] == NULL)
1242 realm = (*sai)[SASL_DEFREALM];
1244 /* do not add an empty realm */
1248 (*sai)[SASL_ID_REALM] = NULL;
1252 l = strlen(h) + strlen(realm) + 2;
1254 /* should use rpool, but from where? */
1255 authid = sm_sasl_malloc(l);
1258 (void) sm_snprintf(authid, l,
1261 (*sai)[SASL_ID_REALM] = authid;
1266 (*sai)[SASL_ID_REALM] = NULL;
1271 authid = (*sai)[SASL_ID_REALM];
1274 # endif /* SASL > 10509 */
1276 l = strlen(authid) + 1;
1277 s = sm_sasl_malloc(l);
1285 (void) sm_strlcpy(s, authid, l);
1288 sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1291 *len = authid ? strlen(authid) : 0;
1294 case SASL_CB_LANGUAGE:
1301 return SASL_BADPARAM;
1306 ** GETSECRET -- callback to get password
1309 ** conn -- connection information
1312 ** psecret -- (pointer to) result
1315 ** OK/failure values
1319 getsecret(conn, context, id, psecret)
1321 SM_UNUSED(void *context);
1323 sasl_secret_t **psecret;
1329 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1330 return SASL_BADPARAM;
1332 sai = (SASL_AI_T *) context;
1333 authpass = (*sai)[SASL_PASSWORD];
1334 len = strlen(authpass);
1335 *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
1337 if (*psecret == NULL)
1339 (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
1340 (*psecret)->len = (unsigned long) len;
1343 # endif /* SASL >= 20000 */
1346 ** SAFESASLFILE -- callback for sasl: is file safe?
1349 ** context -- pointer to context between invocations (unused)
1350 ** file -- name of file to check
1351 ** type -- type of file to check
1354 ** SASL_OK -- file can be used
1355 ** SASL_CONTINUE -- don't use file
1356 ** SASL_FAIL -- failure (not used here)
1362 safesaslfile(context, file, type)
1364 safesaslfile(context, file)
1374 sasl_verify_type_t type;
1387 if (SM_IS_EMPTY(file))
1390 sm_dprintf("safesaslfile=%s\n", file);
1391 sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
1393 if ((p = strrchr(file, '/')) == NULL)
1398 /* everything beside libs and .conf files must not be readable */
1400 if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
1401 (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
1403 if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1404 sff |= SFF_NORFILES;
1405 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1406 sff |= SFF_NOGWFILES;
1408 # else /* SASL <= 10515 */
1409 /* files containing passwords should be not readable */
1410 if (type == SASL_VRFY_PASSWD)
1412 if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1413 sff |= SFF_NOWRFILES;
1415 sff |= SFF_NORFILES;
1416 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1417 sff |= SFF_NOGWFILES;
1419 # endif /* SASL <= 10515 */
1422 if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
1423 S_IRUSR, NULL)) == 0)
1425 if (LogLevel > (r != ENOENT ? 8 : 10))
1426 sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
1427 p, sm_errstring(r));
1428 return SASL_CONTINUE;
1432 ** SASLGETREALM -- return the realm for SASL
1434 ** return the realm for the client
1437 ** context -- context shared between invocations
1438 ** availrealms -- list of available realms
1439 ** {realm, realm, ...}
1440 ** result -- pointer to result
1447 saslgetrealm(context, id, availrealms, result)
1450 const char **availrealms;
1451 const char **result;
1456 sai = (SASL_AI_T *) context;
1459 r = (*sai)[SASL_DEFREALM];
1462 sm_syslog(LOG_INFO, NOQID,
1463 "AUTH=client, realm=%s, available realms=%s",
1464 r == NULL ? "<No Realm>" : r,
1465 (NULL == availrealms || SM_IS_EMPTY(*availrealms))
1466 ? "<No Realms>" : *availrealms);
1468 /* check whether context is in list */
1469 if (availrealms != NULL && *availrealms != NULL)
1471 if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
1475 sm_syslog(LOG_ERR, NOQID,
1476 "AUTH=client, realm=%s not in list=%s",
1485 ** ITEMINLIST -- does item appear in list?
1487 ** Check whether item appears in list (which must be separated by a
1488 ** character in delim) as a "word", i.e. it must appear at the begin
1489 ** of the list or after a space, and it must end with a space or the
1493 ** item -- item to search.
1494 ** list -- list of items.
1495 ** delim -- list of delimiters.
1498 ** pointer to occurrence (NULL if not found).
1502 iteminlist(item, list, delim)
1510 if (SM_IS_EMPTY(list))
1512 if (SM_IS_EMPTY(item))
1516 while (s != NULL && *s != '\0')
1518 if (sm_strncasecmp(s, item, len) == 0 &&
1519 (s[len] == '\0' || strchr(delim, s[len]) != NULL))
1521 s = strpbrk(s, delim);
1529 ** REMOVEMECH -- remove item [rem] from list [list]
1532 ** rem -- item to remove
1533 ** list -- list of items
1534 ** rpool -- resource pool from which result is allocated.
1537 ** pointer to new list (NULL in case of error).
1541 removemech(rem, list, rpool)
1552 if (SM_IS_EMPTY(rem))
1554 /* take out what? */
1558 /* find the item in the list */
1559 if ((needle = iteminlist(rem, list, " ")) == NULL)
1561 /* not in there: return original */
1565 /* length of string without rem */
1566 len = strlen(list) - strlen(rem);
1569 ret = (char *) sm_rpool_malloc_x(rpool, 1);
1573 ret = (char *) sm_rpool_malloc_x(rpool, len);
1574 memset(ret, '\0', len);
1576 /* copy from start to removed item */
1577 memcpy(ret, list, needle - list);
1579 /* length of rest of string past removed item */
1580 len = strlen(needle) - strlen(rem) - 1;
1583 /* not last item -- copy into string */
1584 memcpy(ret + (needle - list),
1585 list + (needle - list) + strlen(rem) + 1,
1589 ret[(needle - list) - 1] = '\0';
1593 ** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1597 ** mci -- the mailer connection structure.
1598 ** e -- the envelope (including the sender to specify).
1599 ** sai - sasl authinfo
1602 ** EX_OK -- authentication was successful.
1603 ** EX_NOPERM -- authentication failed.
1604 ** EX_IOERR -- authentication dialogue failed (I/O problem?).
1605 ** EX_TEMPFAIL -- temporary failure.
1610 attemptauth(m, mci, e, sai)
1616 int saslresult, smtpresult;
1619 const char *auth_id;
1621 # else /* SASL >= 20000 */
1622 sasl_external_properties_t ssf;
1624 # endif /* SASL >= 20000 */
1625 unsigned int outlen;
1626 sasl_interact_t *client_interact = NULL;
1628 sasl_security_properties_t ssp;
1630 /* MUST NOT be a multiple of 4: bug in some sasl_encode64() versions */
1631 char in64[MAXOUTLEN + 1];
1632 # if NETINET || (NETINET6 && SASL >= 20000)
1633 extern SOCKADDR CurHostAddr;
1636 /* no mechanism selected (yet) */
1637 (*sai)[SASL_MECH] = NULL;
1639 /* dispose old connection */
1640 if (mci->mci_conn != NULL)
1641 sasl_dispose(&(mci->mci_conn));
1643 /* make a new client sasl connection */
1646 ** We provide the callbacks again because global callbacks in
1647 ** sasl_client_init() are ignored if SASL has been initialized
1648 ** before, for example, by a library such as libnss-ldap.
1651 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1653 CurHostName, NULL, NULL, callbacks, 0,
1655 # else /* SASL >= 20000 */
1656 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1658 CurHostName, NULL, 0, &mci->mci_conn);
1659 # endif /* SASL >= 20000 */
1660 if (saslresult != SASL_OK)
1663 /* set properties */
1664 (void) memset(&ssp, '\0', sizeof(ssp));
1666 /* XXX should these be options settable via .cf ? */
1667 ssp.max_ssf = MaxSLBits;
1668 ssp.maxbufsize = MAXOUTLEN;
1670 ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1672 saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1673 if (saslresult != SASL_OK)
1677 /* external security strength factor, authentication id */
1681 out = macvalue(macid("{cert_subject}"), e);
1682 if (out != NULL && *out != '\0')
1684 out = macvalue(macid("{cipher_bits}"), e);
1685 if (out != NULL && *out != '\0')
1687 # endif /* STARTTLS */
1688 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1689 if (saslresult != SASL_OK)
1691 saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
1692 if (saslresult != SASL_OK)
1695 # if NETINET || NETINET6
1696 /* set local/remote ipv4 addresses */
1697 if (mci->mci_out != NULL && (
1699 CurHostAddr.sa.sa_family == AF_INET6 ||
1701 CurHostAddr.sa.sa_family == AF_INET))
1703 SOCKADDR_LEN_T addrsize;
1705 char localip[60], remoteip[60];
1707 switch (CurHostAddr.sa.sa_family)
1710 addrsize = sizeof(struct sockaddr_in);
1714 addrsize = sizeof(struct sockaddr_in6);
1720 if (iptostring(&CurHostAddr, addrsize,
1721 remoteip, sizeof(remoteip)))
1723 if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
1724 remoteip) != SASL_OK)
1727 addrsize = sizeof(saddr_l);
1728 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1730 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1732 if (iptostring(&saddr_l, addrsize,
1733 localip, sizeof(localip)))
1735 if (sasl_setprop(mci->mci_conn,
1737 localip) != SASL_OK)
1742 # endif /* NETINET || NETINET6 */
1744 /* start client side of sasl */
1745 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1748 (const char **) &mechusing);
1749 # else /* SASL >= 20000 */
1750 /* external security strength factor, authentication id */
1754 out = macvalue(macid("{cert_subject}"), e);
1755 if (out != NULL && *out != '\0')
1757 out = macvalue(macid("{cipher_bits}"), e);
1758 if (out != NULL && *out != '\0')
1759 ssf.ssf = atoi(out);
1760 # endif /* STARTTLS */
1761 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1762 if (saslresult != SASL_OK)
1766 /* set local/remote ipv4 addresses */
1767 if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1769 SOCKADDR_LEN_T addrsize;
1770 struct sockaddr_in saddr_l;
1772 if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1773 (struct sockaddr_in *) &CurHostAddr)
1776 addrsize = sizeof(struct sockaddr_in);
1777 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1779 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1781 if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1782 &saddr_l) != SASL_OK)
1786 # endif /* NETINET */
1788 /* start client side of sasl */
1789 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1790 NULL, &client_interact,
1792 (const char **) &mechusing);
1793 # endif /* SASL >= 20000 */
1795 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1797 if (saslresult == SASL_NOMECH && LogLevel > 8)
1799 sm_syslog(LOG_NOTICE, e->e_id,
1800 "AUTH=client, available mechanisms=%s do not fulfill requirements", mci->mci_saslcap);
1805 /* just point current mechanism to the data in the sasl library */
1806 (*sai)[SASL_MECH] = mechusing;
1808 /* send the info across the wire */
1810 /* login and digest-md5 up to 1.5.28 set out="" */
1812 (SM_STRCASEEQ(mechusing, "login") ||
1813 SM_STRCASEEQ(mechusing, "digest-md5")))
1816 /* no initial response */
1817 smtpmessage("AUTH %s", m, mci, mechusing);
1819 else if (outlen == 0)
1822 ** zero-length initial response, per RFC 2554 4.:
1823 ** "Unlike a zero-length client answer to a 334 reply, a zero-
1824 ** length initial response is sent as a single equals sign"
1827 smtpmessage("AUTH %s =", m, mci, mechusing);
1831 saslresult = sasl_encode64(out, outlen, in64, sizeof(in64),
1833 if (saslresult != SASL_OK) /* internal error */
1836 sm_syslog(LOG_ERR, e->e_id,
1837 "encode64 for AUTH failed");
1840 smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1843 sm_sasl_free(out); /* XXX only if no rpool is used */
1847 smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
1852 /* check return code from server */
1853 if (smtpresult == 235)
1855 macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
1859 if (smtpresult == -1)
1861 if (REPLYTYPE(smtpresult) == 5)
1862 return EX_NOPERM; /* ugly, but ... */
1863 if (REPLYTYPE(smtpresult) != 3)
1865 /* should we fail deliberately, see RFC 2554 4. ? */
1866 /* smtpmessage("*", m, mci); */
1870 saslresult = sasl_client_step(mci->mci_conn,
1871 mci->mci_sasl_string,
1872 mci->mci_sasl_string_len,
1876 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1879 sm_dprintf("AUTH FAIL=%s (%d)\n",
1880 sasl_errstring(saslresult, NULL, NULL),
1883 /* fail deliberately, see RFC 2554 4. */
1884 smtpmessage("*", m, mci);
1887 ** but we should only fail for this authentication
1888 ** mechanism; how to do that?
1891 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1892 getsasldata, NULL, XS_AUTH, NULL);
1898 saslresult = sasl_encode64(out, outlen, in64,
1899 sizeof(in64), NULL);
1900 if (saslresult != SASL_OK)
1902 /* give an error reply to the other side! */
1903 smtpmessage("*", m, mci);
1910 sm_sasl_free(out); /* XXX only if no rpool is used */
1912 smtpmessage("%s", m, mci, in64);
1913 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1914 getsasldata, NULL, XS_AUTH, NULL);
1919 ** SMTPAUTH -- try to AUTHenticate
1921 ** This will try mechanisms in the order the sasl library decided until:
1922 ** - there are no more mechanisms
1923 ** - a mechanism succeeds
1924 ** - the sasl library fails initializing
1928 ** mci -- the mailer connection info.
1929 ** e -- the envelope.
1932 ** EX_OK -- authentication was successful
1933 ** EX_UNAVAILABLE -- authentication not possible, e.g.,
1934 ** no data available.
1935 ** EX_NOPERM -- authentication failed.
1936 ** EX_TEMPFAIL -- temporary failure.
1938 ** Notice: AuthInfo is used for all connections, hence we must
1939 ** return EX_TEMPFAIL only if we really want to retry, i.e.,
1940 ** iff getauth() tempfailed or getauth() was used and
1941 ** authentication tempfailed.
1954 mci->mci_sasl_auth = false;
1955 for (i = 0; i < SASL_MECH ; i++)
1956 mci->mci_sai[i] = NULL;
1958 result = getauth(mci, e, &(mci->mci_sai));
1959 if (result == EX_TEMPFAIL)
1963 /* no data available: don't try to authenticate */
1964 if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
1966 if (result != EX_OK)
1968 if (SASLInfo == NULL)
1969 return EX_UNAVAILABLE;
1971 /* read authinfo from file */
1972 result = readauth(SASLInfo, true, &(mci->mci_sai),
1974 if (result != EX_OK)
1976 usedgetauth = false;
1979 /* check whether sufficient data is available */
1980 if (mci->mci_sai[SASL_PASSWORD] == NULL ||
1981 *(mci->mci_sai)[SASL_PASSWORD] == '\0')
1982 return EX_UNAVAILABLE;
1983 if ((mci->mci_sai[SASL_AUTHID] == NULL ||
1984 *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
1985 (mci->mci_sai[SASL_USER] == NULL ||
1986 *(mci->mci_sai)[SASL_USER] == '\0'))
1987 return EX_UNAVAILABLE;
1989 /* set the context for the callback function to sai */
1991 callbacks[CB_PASS_IDX].context = (void *) mci;
1993 callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
1995 callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
1996 callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
1997 callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
1999 callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
2002 /* set default value for realm */
2003 if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
2004 (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
2005 macvalue('j', CurEnv));
2007 /* set default value for list of mechanism to use */
2008 if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
2009 *(mci->mci_sai)[SASL_MECHLIST] == '\0')
2010 (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
2012 /* create list of mechanisms to try */
2013 mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
2014 mci->mci_saslcap, mci->mci_rpool);
2016 /* initialize sasl client library */
2017 result = init_sasl_client();
2018 if (result != SASL_OK)
2019 return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
2022 result = attemptauth(m, mci, e, &(mci->mci_sai));
2023 if (result == EX_OK)
2024 mci->mci_sasl_auth = true;
2025 else if (result == EX_TEMPFAIL || result == EX_NOPERM)
2027 mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
2030 if (mci->mci_saslcap == NULL ||
2031 *(mci->mci_saslcap) == '\0')
2032 return usedgetauth ? result
2037 } while (result != EX_OK);
2043 ** SMTPMAILFROM -- send MAIL command
2047 ** mci -- the mailer connection structure.
2048 ** e -- the envelope (including the sender to specify).
2051 ** exit status corresponding to mail status.
2055 smtpmailfrom(m, mci, e)
2064 char buf[MAXNAME_I + 1];
2065 char optbuf[MAXLINE];
2066 #if _FFR_8BITENVADDR
2071 sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
2075 ** Check if connection is gone, if so
2076 ** it's a tempfail and we use mci_errno
2080 if (mci->mci_state == MCIS_CLOSED)
2082 errno = mci->mci_errno;
2087 if (bitset(EF_RESPONSE, e->e_flags) &&
2088 !bitnset(M_NO_NULL_FROM, m->m_flags))
2092 expand("\201g", buf, sizeof(buf), e);
2093 if (!addr_is_ascii(buf) && !e->e_smtputf8)
2094 e->e_smtputf8 = true;
2097 if (e->e_smtputf8 && !SMTP_UTF8)
2099 extern char MsgBuf[];
2101 /* XREF: format must be coordinated with giveresponse() */
2102 usrerrenh("5.6.7", "504 SMTPUTF8 required but not enabled");
2103 mci_setstat(mci, EX_NOTSTICKY, "5.6.7", MsgBuf);
2108 ** Abort right away if the message needs SMTPUTF8
2109 ** but the server does not advertise it.
2112 if (e->e_smtputf8 && !bitset(MCIF_EAI, mci->mci_flags))
2114 extern char MsgBuf[];
2116 /* XREF: format must be coordinated with giveresponse() */
2117 usrerrenh("5.6.7", "504 SMTPUTF8 required but not offered");
2118 mci_setstat(mci, EX_NOTSTICKY, "5.6.7", MsgBuf);
2121 #endif /* USE_EAI */
2123 /* set up appropriate options to include */
2124 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
2126 (void) sm_snprintf(optbuf, sizeof(optbuf), " SIZE=%ld",
2128 bufp = &optbuf[strlen(optbuf)];
2139 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2141 bufp += strlen(bufp);
2143 #endif /* USE_EAI */
2145 bodytype = e->e_bodytype;
2146 if (bitset(MCIF_8BITMIME, mci->mci_flags))
2148 if (bodytype == NULL &&
2149 bitset(MM_MIME8BIT, MimeMode) &&
2150 bitset(EF_HAS8BIT, e->e_flags) &&
2151 !bitset(EF_DONT_MIME, e->e_flags) &&
2152 !bitnset(M_8BITS, m->m_flags))
2153 bodytype = "8BITMIME";
2154 if (bodytype != NULL &&
2155 SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2157 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2158 " BODY=%s", bodytype);
2159 bufp += strlen(bufp);
2162 else if (bitnset(M_8BITS, m->m_flags) ||
2163 !bitset(EF_HAS8BIT, e->e_flags) ||
2164 bitset(MCIF_8BITOK, mci->mci_flags))
2167 /* just pass it through */
2170 else if (bitset(MM_CVTMIME, MimeMode) &&
2171 !bitset(EF_DONT_MIME, e->e_flags) &&
2172 (!bitset(MM_PASS8BIT, MimeMode) ||
2173 bitset(EF_IS_MIME, e->e_flags)))
2175 /* must convert from 8bit MIME format to 7bit encoded */
2176 mci->mci_flags |= MCIF_CVT8TO7;
2178 #endif /* MIME8TO7 */
2179 else if (!bitset(MM_PASS8BIT, MimeMode))
2181 /* cannot just send a 8-bit version */
2182 extern char MsgBuf[];
2184 usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2185 mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2189 if (bitset(MCIF_DSN, mci->mci_flags))
2191 if (e->e_envid != NULL &&
2192 SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2194 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2195 " ENVID=%s", e->e_envid);
2196 bufp += strlen(bufp);
2199 /* RET= parameter */
2200 if (bitset(EF_RET_PARAM, e->e_flags) &&
2201 SPACELEFT(optbuf, bufp) > 9)
2203 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2205 bitset(EF_NO_BODY_RETN, e->e_flags) ?
2207 bufp += strlen(bufp);
2211 if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
2212 SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
2214 && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
2218 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2219 " AUTH=%s", e->e_auth_param);
2220 bufp += strlen(bufp);
2224 ** 17 is the max length required, we could use log() to compute
2225 ** the exact length (and check IS_DLVR_TRACE())
2228 if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
2229 IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
2234 ** Avoid problems with delays (for R) since the check
2235 ** in deliver() whether min-deliver-time is sufficient.
2236 ** Alternatively we could pass the computed time to this
2240 dby = e->e_deliver_by - (curtime() - e->e_ctime);
2241 if (dby <= 0 && IS_DLVR_RETURN(e))
2242 dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
2243 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2246 IS_DLVR_RETURN(e) ? 'R' : 'N',
2247 IS_DLVR_TRACE(e) ? "T" : "");
2248 bufp += strlen(bufp);
2252 ** Send the MAIL command.
2253 ** Designates the sender.
2256 maps_reset_chged("client:MAIL");
2257 mci->mci_state = MCIS_MAIL;
2260 if (bitset(EF_RESPONSE, e->e_flags) &&
2261 !bitnset(M_NO_NULL_FROM, m->m_flags))
2264 expand("\201g", buf, sizeof(buf), e);
2265 #endif /* !USE_EAI */
2266 #if _FFR_8BITENVADDR
2268 sm_dprintf("mail_expand=%s\n", buf);
2270 nlen = dequote_internal_chars(buf, buf, len);
2271 /* check length! but that's a bit late... */
2273 sm_syslog(LOG_ERR, e->e_id, "MAIL too long: %d", nlen);
2275 sm_dprintf("mail2=%s\n", buf);
2276 #endif /* _FFR_8BITENVADDR */
2279 /* strip off <angle brackets> (put back on below) */
2280 bufp = &buf[strlen(buf) - 1];
2287 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2288 !bitnset(M_FROMPATH, m->m_flags))
2290 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2294 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2295 *bufp == '@' ? ',' : ':', bufp, optbuf);
2297 SmtpPhase = mci->mci_phase = "client MAIL";
2298 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2299 CurHostName, mci->mci_phase);
2300 r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_MAIL, NULL);
2303 /* communications failure */
2304 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2307 else if (r == SMTPCLOSING)
2309 /* service shutting down: handled by reply() */
2312 else if (REPLYTYPE(r) == 4)
2314 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
2318 else if (REPLYTYPE(r) == 2)
2324 /* syntax error in arguments */
2325 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
2331 /* mailbox name not allowed */
2332 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
2338 /* exceeded storage allocation */
2339 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
2341 if (bitset(MCIF_SIZE, mci->mci_flags))
2342 e->e_flags |= EF_NO_BODY_RETN;
2343 return EX_UNAVAILABLE;
2345 else if (REPLYTYPE(r) == 5)
2348 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
2350 return EX_UNAVAILABLE;
2355 sm_syslog(LOG_CRIT, e->e_id,
2356 "%.100s: SMTP MAIL protocol error: %s",
2358 shortenstring(SmtpReplyBuffer, 403));
2361 /* protocol error -- close up */
2362 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2364 smtpquit(m, mci, e);
2368 ** SMTPRCPT -- designate recipient.
2371 ** to -- address of recipient.
2372 ** m -- the mailer we are sending to.
2373 ** mci -- the connection info for this transaction.
2374 ** e -- the envelope for this transaction.
2377 ** exit status corresponding to recipient status.
2380 ** Sends the mail via SMTP.
2384 smtprcpt(to, m, mci, e, ctladdr, xstart)
2393 char optbuf[MAXLINE];
2395 #if _FFR_8BITENVADDR
2396 char buf[MAXNAME + 1]; /* EAI:ok */
2405 ** If there is status waiting from the other end, read it.
2406 ** This should normally happen because of SMTP pipelining.
2410 while (mci->mci_nextaddr != NULL &&
2411 sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2415 e->e_to = mci->mci_nextaddr->q_paddr;
2416 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2419 markfailure(e, mci->mci_nextaddr, mci, r, false);
2420 giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
2421 ctladdr, xstart, e, mci->mci_nextaddr);
2423 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2427 #endif /* PIPELINING */
2430 ** Check if connection is gone, if so
2431 ** it's a tempfail and we use mci_errno
2435 if (mci->mci_state == MCIS_CLOSED)
2437 errno = mci->mci_errno;
2445 ** Warning: in the following it is assumed that the free space
2446 ** in bufp is sizeof(optbuf)
2449 if (bitset(MCIF_DSN, mci->mci_flags))
2451 if (IS_DLVR_NOTIFY(e) &&
2452 !bitset(MCIF_DLVR_BY, mci->mci_flags))
2454 /* RFC 2852: 4.1.4.2 */
2455 if (!bitset(QHASNOTIFY, to->q_flags))
2456 to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
2457 else if (bitset(QPINGONSUCCESS, to->q_flags) ||
2458 bitset(QPINGONFAILURE, to->q_flags) ||
2459 bitset(QPINGONDELAY, to->q_flags))
2460 to->q_flags |= QPINGONDELAY;
2463 /* NOTIFY= parameter */
2464 if (bitset(QHASNOTIFY, to->q_flags) &&
2465 bitset(QPRIMARY, to->q_flags) &&
2466 !bitnset(M_LOCALMAILER, m->m_flags))
2468 bool firstone = true;
2470 (void) sm_strlcat(bufp, " NOTIFY=", sizeof(optbuf));
2471 if (bitset(QPINGONSUCCESS, to->q_flags))
2473 (void) sm_strlcat(bufp, "SUCCESS", sizeof(optbuf));
2476 if (bitset(QPINGONFAILURE, to->q_flags))
2479 (void) sm_strlcat(bufp, ",",
2481 (void) sm_strlcat(bufp, "FAILURE", sizeof(optbuf));
2484 if (bitset(QPINGONDELAY, to->q_flags))
2487 (void) sm_strlcat(bufp, ",",
2489 (void) sm_strlcat(bufp, "DELAY", sizeof(optbuf));
2493 (void) sm_strlcat(bufp, "NEVER", sizeof(optbuf));
2494 bufp += strlen(bufp);
2497 /* ORCPT= parameter */
2498 if (to->q_orcpt != NULL &&
2499 SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2501 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2502 " ORCPT=%s", to->q_orcpt);
2503 bufp += strlen(bufp);
2508 #if _FFR_8BITENVADDR
2510 sm_dprintf("rcpt=%s\n", rcpt);
2512 nlen = dequote_internal_chars(rcpt, buf, len);
2514 /* check length! but that's a bit late... */
2516 sm_syslog(LOG_ERR, e->e_id, "RCPT too long: %d", nlen);
2518 sm_dprintf("rcpt2=%s\n", rcpt);
2519 #endif /* _FFR_8BITENVADDR */
2521 smtpmessage("RCPT To:<%s>%s", m, mci, rcpt, optbuf);
2522 mci->mci_state = MCIS_RCPT;
2524 SmtpPhase = mci->mci_phase = "client RCPT";
2525 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2526 CurHostName, mci->mci_phase);
2530 ** If running SMTP pipelining, we will pick up status later
2533 if (bitset(MCIF_PIPELINED, mci->mci_flags))
2535 #endif /* PIPELINING */
2537 return smtprcptstat(to, m, mci, e);
2540 ** SMTPRCPTSTAT -- get recipient status
2542 ** This is only called during SMTP pipelining
2545 ** to -- address of recipient.
2546 ** m -- mailer being sent to.
2547 ** mci -- the mailer connection information.
2548 ** e -- the envelope for this message.
2551 ** EX_* -- protocol status
2555 smtprcptstat(to, m, mci, e)
2559 register ENVELOPE *e;
2566 ** Check if connection is gone, if so
2567 ** it's a tempfail and we use mci_errno
2571 if (mci->mci_state == MCIS_CLOSED)
2573 errno = mci->mci_errno;
2578 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_RCPT,
2581 to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
2582 if (!bitnset(M_LMTP, m->m_flags))
2583 to->q_statmta = mci->mci_host;
2584 if (r < 0 || REPLYTYPE(r) == 4)
2586 mci->mci_retryrcpt = true;
2590 else if (REPLYTYPE(r) == 2)
2594 if ((t = mci->mci_tolist) != NULL)
2599 for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
2602 mci->mci_tolist = t;
2609 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2614 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2619 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2622 else if (REPLYTYPE(r) == 5)
2624 return EX_UNAVAILABLE;
2629 sm_syslog(LOG_CRIT, e->e_id,
2630 "%.100s: SMTP RCPT protocol error: %s",
2632 shortenstring(SmtpReplyBuffer, 403));
2635 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2640 ** SMTPDATA -- send the data and clean up the transaction.
2643 ** m -- mailer being sent to.
2644 ** mci -- the mailer connection information.
2645 ** e -- the envelope for this message.
2648 ** exit status corresponding to DATA command.
2652 smtpdata(m, mci, e, ctladdr, xstart)
2655 register ENVELOPE *e;
2666 ** Check if connection is gone, if so
2667 ** it's a tempfail and we use mci_errno
2671 if (mci->mci_state == MCIS_CLOSED)
2673 errno = mci->mci_errno;
2681 ** First send the command and check that it is ok.
2682 ** Then send the data (if there are valid recipients).
2683 ** Follow it up with a dot to terminate.
2684 ** Finally get the results of the transaction.
2687 /* send the command and check ok to proceed */
2688 smtpmessage("DATA", m, mci);
2691 if (mci->mci_nextaddr != NULL)
2693 char *oldto = e->e_to;
2695 /* pick up any pending RCPT responses for SMTP pipelining */
2696 while (mci->mci_nextaddr != NULL)
2698 e->e_to = mci->mci_nextaddr->q_paddr;
2699 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2702 markfailure(e, mci->mci_nextaddr, mci, r,
2704 giveresponse(r, mci->mci_nextaddr->q_status, m,
2705 mci, ctladdr, xstart, e,
2707 if (r == EX_TEMPFAIL)
2708 mci->mci_nextaddr->q_state = QS_RETRY;
2710 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2715 ** Connection might be closed in response to a RCPT command,
2716 ** i.e., the server responded with 421. In that case (at
2717 ** least) one RCPT has a temporary failure, hence we don't
2718 ** need to check mci_okrcpts (as it is done below) to figure
2719 ** out which error to return.
2722 if (mci->mci_state == MCIS_CLOSED)
2724 errno = mci->mci_errno;
2728 #endif /* PIPELINING */
2730 /* now proceed with DATA phase */
2731 SmtpPhase = mci->mci_phase = "client DATA 354";
2732 mci->mci_state = MCIS_DATA;
2733 sm_setproctitle(true, e, "%s %s: %s",
2734 qid_printname(e), CurHostName, mci->mci_phase);
2735 r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DATA, NULL);
2736 if (r < 0 || REPLYTYPE(r) == 4)
2739 smtpquit(m, mci, e);
2740 errno = mci->mci_errno;
2743 else if (REPLYTYPE(r) == 5)
2745 smtprset(m, mci, e);
2746 if (mci->mci_okrcpts <= 0 && mci->mci_retryrcpt)
2748 return EX_UNAVAILABLE;
2750 else if (REPLYTYPE(r) != 3)
2754 sm_syslog(LOG_CRIT, e->e_id,
2755 "%.100s: SMTP DATA-1 protocol error: %s",
2757 shortenstring(SmtpReplyBuffer, 403));
2759 smtprset(m, mci, e);
2760 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2762 if (mci->mci_okrcpts <= 0 && mci->mci_retryrcpt)
2767 if (mci->mci_okrcpts > 0)
2770 ** Set timeout around data writes. Make it at least
2771 ** large enough for DNS timeouts on all recipients
2772 ** plus some fudge factor. The main thing is
2773 ** that it should not be infinite.
2778 /* simulate a DATA timeout */
2782 timeout = DATA_PROGRESS_TIMEOUT * 1000;
2783 sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
2786 ** Output the actual message.
2789 if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
2794 /* simulate a DATA timeout */
2798 if (!(*e->e_putbody)(mci, e, NULL))
2802 ** Cleanup after sending message.
2806 #if _FFR_CATCH_BROKEN_MTAS
2807 if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2809 /* terminate the message */
2810 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
2812 if (TrafficLogFile != NULL)
2813 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2814 "%05d >>> .\n", (int) CurrentPid);
2818 sm_syslog(LOG_CRIT, e->e_id,
2819 "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2821 mci->mci_errno = EIO;
2822 mci->mci_state = MCIS_ERROR;
2823 mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
2824 smtpquit(m, mci, e);
2827 #endif /* _FFR_CATCH_BROKEN_MTAS */
2829 if (sm_io_error(mci->mci_out))
2831 /* error during processing -- don't send the dot */
2832 mci->mci_errno = EIO;
2833 mci->mci_state = MCIS_ERROR;
2834 mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2835 smtpquit(m, mci, e);
2839 /* terminate the message */
2840 if (sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s.%s",
2841 bitset(MCIF_INLONGLINE, mci->mci_flags) ? m->m_eol : "",
2842 m->m_eol) == SM_IO_EOF)
2844 if (TrafficLogFile != NULL)
2845 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2846 "%05d >>> .\n", (int) CurrentPid);
2850 /* check for the results of the transaction */
2851 SmtpPhase = mci->mci_phase = "client DATA status";
2852 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2853 CurHostName, mci->mci_phase);
2854 if (bitnset(M_LMTP, m->m_flags))
2856 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_EOM, NULL);
2859 if (mci->mci_state == MCIS_DATA)
2860 mci->mci_state = MCIS_OPEN;
2861 xstat = EX_NOTSTICKY;
2863 rstat = EX_TEMPFAIL;
2864 else if (REPLYTYPE(r) == 4)
2865 rstat = xstat = EX_TEMPFAIL;
2866 else if (REPLYTYPE(r) == 2)
2867 rstat = xstat = EX_OK;
2868 else if (REPLYCLASS(r) != 5)
2869 rstat = xstat = EX_PROTOCOL;
2870 else if (REPLYTYPE(r) == 5)
2871 rstat = EX_UNAVAILABLE;
2873 rstat = EX_PROTOCOL;
2874 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
2876 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2877 (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2881 e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2882 SmtpPhase = mci->mci_phase = "idle";
2883 sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2884 if (rstat != EX_PROTOCOL)
2888 sm_syslog(LOG_CRIT, e->e_id,
2889 "%.100s: SMTP DATA-2 protocol error: %s",
2891 shortenstring(SmtpReplyBuffer, 403));
2896 mci->mci_errno = errno;
2897 mci->mci_state = MCIS_ERROR;
2898 mci_setstat(mci, bitset(MCIF_NOTSTICKY, mci->mci_flags)
2899 ? EX_NOTSTICKY: EX_TEMPFAIL,
2901 mci->mci_flags &= ~MCIF_NOTSTICKY;
2904 ** If putbody() couldn't finish due to a timeout,
2905 ** rewind it here in the timeout handler. See
2906 ** comments at the end of putbody() for reasoning.
2909 if (e->e_dfp != NULL)
2910 (void) bfrewind(e->e_dfp);
2912 errno = mci->mci_errno;
2913 syserr("+451 4.4.1 timeout writing message to %s", CurHostName);
2914 smtpquit(m, mci, e);
2919 ** SMTPGETSTAT -- get status code from DATA in LMTP
2922 ** m -- the mailer to which we are sending the message.
2923 ** mci -- the mailer connection structure.
2924 ** e -- the current envelope.
2927 ** The exit status corresponding to the reply code.
2931 smtpgetstat(m, mci, e)
2943 /* check for the results of the transaction */
2944 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DATA2,
2948 xstat = EX_NOTSTICKY;
2949 if (REPLYTYPE(r) == 4)
2950 status = EX_TEMPFAIL;
2951 else if (REPLYTYPE(r) == 2)
2952 status = xstat = EX_OK;
2953 else if (REPLYCLASS(r) != 5)
2954 status = xstat = EX_PROTOCOL;
2955 else if (REPLYTYPE(r) == 5)
2956 status = EX_UNAVAILABLE;
2958 status = EX_PROTOCOL;
2959 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2960 (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2964 e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
2965 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
2966 if (LogLevel > 1 && status == EX_PROTOCOL)
2968 sm_syslog(LOG_CRIT, e->e_id,
2969 "%.100s: SMTP DATA-3 protocol error: %s",
2971 shortenstring(SmtpReplyBuffer, 403));
2976 ** SMTPQUIT -- close the SMTP connection.
2979 ** m -- a pointer to the mailer.
2980 ** mci -- the mailer connection information.
2981 ** e -- the current envelope.
2987 ** sends the final protocol and closes the connection.
2996 bool oldSuprErrs = SuprErrs;
3000 if (mci->mci_state == MCIS_CLOSED)
3002 mci_close(mci, "smtpquit:1");
3006 oldcurhost = CurHostName;
3007 CurHostName = mci->mci_host; /* XXX UGLY XXX */
3008 if (CurHostName == NULL)
3009 CurHostName = MyHostName;
3012 ** Suppress errors here -- we may be processing a different
3013 ** job when we do the quit connection, and we don't want the
3014 ** new job to be penalized for something that isn't it's
3020 /* send the quit message if we haven't gotten I/O error */
3021 if (mci->mci_state != MCIS_ERROR &&
3022 mci->mci_state != MCIS_QUITING)
3024 SmtpPhase = "client QUIT";
3025 mci->mci_state = MCIS_QUITING;
3026 smtpmessage("QUIT", m, mci);
3027 (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL, XS_QUIT,
3029 SuprErrs = oldSuprErrs;
3030 if (mci->mci_state == MCIS_CLOSED)
3034 /* now actually close the connection and pick up the zombie */
3035 rcode = endmailer(mci, e, NULL);
3038 char *mailer = NULL;
3040 if (mci->mci_mailer != NULL &&
3041 mci->mci_mailer->m_name != NULL)
3042 mailer = mci->mci_mailer->m_name;
3044 /* look for naughty mailers */
3045 sm_syslog(LOG_ERR, e->e_id,
3046 "smtpquit: mailer%s%s exited with exit value %d",
3047 mailer == NULL ? "" : " ",
3048 mailer == NULL ? "" : mailer,
3052 SuprErrs = oldSuprErrs;
3055 CurHostName = oldcurhost;
3059 ** SMTPRSET -- send a RSET (reset) command
3062 ** m -- a pointer to the mailer.
3063 ** mci -- the mailer connection information.
3064 ** e -- the current envelope.
3070 ** closes the connection if there is no reply to RSET.
3081 CurHostName = mci->mci_host; /* XXX UGLY XXX */
3082 if (CurHostName == NULL)
3083 CurHostName = MyHostName;
3086 ** Check if connection is gone, if so
3087 ** it's a tempfail and we use mci_errno
3091 if (mci->mci_state == MCIS_CLOSED)
3093 errno = mci->mci_errno;
3097 SmtpPhase = "client RSET";
3098 smtpmessage("RSET", m, mci);
3099 r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT, NULL);
3104 ** Any response is deemed to be acceptable.
3105 ** The standard does not state the proper action
3106 ** to take when a value other than 250 is received.
3108 ** However, if 421 is returned for the RSET, leave
3109 ** mci_state alone (MCIS_SSD can be set in reply()
3110 ** and MCIS_CLOSED can be set in smtpquit() if
3111 ** reply() gets a 421 and calls smtpquit()).
3114 if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
3115 mci->mci_state = MCIS_OPEN;
3116 else if (mci->mci_exitstat == EX_OK)
3117 mci_setstat(mci, EX_TEMPFAIL, "4.5.0", NULL);
3120 ** SMTPPROBE -- check the connection state
3123 ** mci -- the mailer connection information.
3129 ** closes the connection if there is no reply to RSET.
3137 MAILER *m = mci->mci_mailer;
3139 extern ENVELOPE BlankEnvelope;
3141 CurHostName = mci->mci_host; /* XXX UGLY XXX */
3142 if (CurHostName == NULL)
3143 CurHostName = MyHostName;
3146 SmtpPhase = "client probe";
3147 smtpmessage("RSET", m, mci);
3148 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT,
3150 if (REPLYTYPE(r) != 2)
3151 smtpquit(m, mci, e);
3155 ** REPLY -- read arpanet reply
3158 ** m -- the mailer we are reading the reply from.
3159 ** mci -- the mailer connection info structure.
3160 ** e -- the current envelope.
3161 ** timeout -- the timeout for reads.
3162 ** pfunc -- processing function called on each line of response.
3163 ** If null, no special processing is done.
3164 ** enhstat -- optional, returns enhanced error code string (if set)
3165 ** rtype -- type of SmtpMsgBuffer: does it contains secret data?
3166 ** rtext -- pointer to where to save first line of reply (if set)
3169 ** reply code it reads.
3170 ** -1 on I/O errors etc.
3173 ** flushes the mail file.
3177 reply(m, mci, e, timeout, pfunc, enhstat, rtype, rtext)
3182 void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
3187 register char *bufp;
3189 bool firstline = true;
3190 char junkbuf[MAXLINE];
3191 static char enhstatcode[ENHSCLEN];
3195 ** Flush the output before reading response.
3197 ** For SMTP pipelining, it would be better if we didn't do
3198 ** this if there was already data waiting to be read. But
3199 ** to do it properly means pushing it to the I/O library,
3200 ** since it really needs to be done below the buffer layer.
3203 if (mci->mci_out != NULL)
3204 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3210 if (SmtpMsgBuffer[0] != '\0')
3211 what = SmtpMsgBuffer;
3212 else if (SmtpPhase != NULL && SmtpPhase[0] != '\0')
3214 else if (XS_GREET == rtype)
3219 if (mci->mci_flags & MCIF_PIPELINED)
3220 sm_dprintf("reply to %s:%d [but PIPELINED]\n", what, rtype);
3223 /* "else" in #if code above */
3224 sm_dprintf("reply to %s:%d\n", what, rtype);
3228 ** Read the input line, being careful not to hang.
3231 bufp = SmtpReplyBuffer;
3232 (void) set_tls_rd_tmo(timeout);
3237 /* actually do the read */
3238 if (e->e_xfp != NULL) /* for debugging */
3239 (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3241 /* if we are in the process of closing just give the code */
3242 if (mci->mci_state == MCIS_CLOSED)
3245 /* don't try to read from a non-existent fd */
3246 if (mci->mci_in == NULL)
3248 if (mci->mci_errno == 0)
3249 mci->mci_errno = EBADF;
3251 /* errors on QUIT should be ignored */
3252 if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3254 errno = mci->mci_errno;
3255 mci_close(mci, "reply:1");
3258 mci->mci_state = MCIS_ERROR;
3259 smtpquit(m, mci, e);
3260 errno = mci->mci_errno;
3264 if (mci->mci_out != NULL)
3265 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3267 /* get the line from the other side */
3268 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
3270 mci->mci_lastuse = curtime();
3275 extern char MsgBuf[];
3277 /* errors on QUIT should be ignored */
3278 if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3280 mci_close(mci, "reply:2");
3284 /* if the remote end closed early, fake an error */
3288 (void) sm_snprintf(SmtpReplyBuffer,
3289 sizeof(SmtpReplyBuffer),
3290 "421 4.4.1 Connection reset by %s",
3299 mci->mci_errno = errno;
3300 oldholderrs = HoldErrs;
3302 usrerr("451 4.4.2 reply: read error from %s",
3304 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3306 /* if debugging, pause so we can see state */
3309 mci->mci_state = MCIS_ERROR;
3310 smtpquit(m, mci, e);
3316 if (e->e_to != NULL)
3318 (void) sm_snprintf(p,
3321 shortenstring(e->e_to, MAXSHORTSTR));
3324 (void) sm_snprintf(p, SPACELEFT(wbuf, p),
3325 "reply(%.100s) during %s",
3326 CURHOSTNAME, SmtpPhase);
3330 HoldErrs = oldholderrs;
3334 fixcrlf(bufp, true);
3336 sm_dprintf("received=%s\n", bufp);
3338 /* EHLO failure is not a real error */
3339 if (e->e_xfp != NULL && (bufp[0] == '4' ||
3340 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3342 /* serious error -- log the previous command */
3345 /* inform user who we are chatting with */
3346 (void) sm_io_fprintf(CurEnv->e_xfp,
3348 "... while talking to %s:\n",
3350 SmtpNeedIntro = false;
3352 if (SmtpMsgBuffer[0] != '\0')
3354 (void) sm_io_fprintf(e->e_xfp,
3357 (rtype == XS_STARTTLS)
3358 ? "STARTTLS dialogue"
3359 : ((rtype == XS_AUTH)
3362 SmtpMsgBuffer[0] = '\0';
3365 /* now log the message as from the other side */
3366 (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3370 /* display the input for verbose mode */
3372 nmessage("050 %s", bufp);
3374 /* ignore improperly formatted input */
3375 if (!ISSMTPREPLY(bufp))
3378 if (NULL != rtext && firstline)
3379 *rtext = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
3381 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
3383 extenhsc(bufp + 4, ' ', enhstatcode) > 0)
3384 *enhstat = enhstatcode;
3386 /* process the line */
3388 (*pfunc)(bufp, firstline, m, mci, e);
3390 /* decode the reply code */
3393 /* extra semantics: 0xx codes are "informational" */
3399 if (REPLYTYPE(r) > 3 && firstline
3402 (e->e_sendmode != SM_PROXY
3403 || (e->e_sendmode == SM_PROXY
3404 && (e->e_rcode == 0 || REPLYTYPE(e->e_rcode) < 5))
3411 ** ignore error iff: DATA, 5xy error, but we had
3412 ** "retryable" recipients. XREF: smtpdata()
3415 if (!(rtype == XS_DATA && REPLYTYPE(r) == 5 &&
3416 mci->mci_okrcpts <= 0 && mci->mci_retryrcpt))
3418 o = extenhsc(bufp + 4, ' ', enhstatcode);
3421 sm_strlcpy(e->e_renhsc, enhstatcode,
3422 sizeof(e->e_renhsc));
3424 /* skip SMTP reply code, delimiters */
3431 ** Don't use this for reply= logging
3432 ** if it was for QUIT.
3433 ** (Note: use the debug option to
3434 ** reproduce the original error.)
3437 if (rtype != XS_QUIT || tTd(87, 101))
3440 e->e_text = sm_rpool_strdup_x(
3441 e->e_rpool, bufp + o);
3443 e->e_estate = rtype;
3449 sm_dprintf("user: e=%p, offset=%d, bufp=%s, rcode=%d, enhstat=%s, rtype=%d, text=%s\n"
3450 , (void *)e, o, bufp, r, e->e_renhsc
3451 , rtype, e->e_text);
3454 #if _FFR_REPLY_MULTILINE
3455 # if _FFR_REPLY_MULTILINE > 200
3456 # define MLLIMIT _FFR_REPLY_MULTILINE
3458 # define MLLIMIT 1024
3460 if ((REPLYTYPE(r) > 3 && !firstline && e->e_text != NULL &&
3461 rtype != XS_QUIT) || tTd(87, 101))
3466 /* skip the same stuff or use o? */
3467 /* but o is a local variable in the block above */
3468 len = strlen(e->e_text) + strlen(bufp) + 3;
3469 if (len < MLLIMIT &&
3470 (new = (char *) sm_rpool_malloc(e->e_rpool, len))
3473 sm_strlcpyn(new, len, 3, e->e_text, "; ",
3478 #endif /* _FFR_REPLY_MULTILINE */
3482 /* if no continuation lines, return this line */
3486 /* first line of real reply -- ignore rest */
3491 ** Now look at SmtpReplyBuffer -- only care about the first
3492 ** line of the response from here on out.
3495 /* save temporary failure messages for posterity */
3496 if (SmtpReplyBuffer[0] == '4')
3497 (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof(SmtpError));
3499 /* reply code 421 is "Service Shutting Down" */
3500 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3501 mci->mci_state != MCIS_QUITING)
3503 /* send the quit protocol */
3504 mci->mci_state = MCIS_SSD;
3505 smtpquit(m, mci, e);
3511 ** SMTPMESSAGE -- send message to server
3515 ** m -- the mailer to control formatting.
3516 ** a, b, c -- parameters
3522 ** writes message to mci->mci_out.
3528 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
3529 #else /* __STDC__ */
3530 smtpmessage(f, m, mci, va_alist)
3535 #endif /* __STDC__ */
3539 SM_VA_START(ap, mci);
3540 (void) sm_vsnprintf(SmtpMsgBuffer, sizeof(SmtpMsgBuffer), f, ap);
3543 if (tTd(18, 1) || Verbose)
3544 nmessage(">>> %s", SmtpMsgBuffer);
3545 if (TrafficLogFile != NULL)
3546 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
3547 "%05d >>> %s\n", (int) CurrentPid,
3549 if (mci->mci_out != NULL)
3551 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
3552 SmtpMsgBuffer, m == NULL ? "\r\n"
3555 else if (tTd(18, 1))
3556 sm_dprintf("smtpmessage: NULL mci_out\n");