2 * Copyright (c) 1998-2005 Sendmail, 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.463 2005/03/16 00:36:09 ca Exp $")
21 static void datatimeout __P((int));
22 static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
23 static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
24 static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
27 extern void *sm_sasl_malloc __P((unsigned long));
28 extern void sm_sasl_free __P((void *));
32 ** USERSMTP -- run SMTP protocol from the user end.
34 ** This protocol is described in RFC821.
37 #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
38 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
39 #define SMTPCLOSING 421 /* "Service Shutting Down" */
41 #define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
43 #define ENHSCN_RPOOL(e, d, rpool) \
44 ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
46 static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
47 static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
48 static bool SmtpNeedIntro; /* need "while talking" in transcript */
50 ** SMTPINIT -- initialize SMTP.
52 ** Opens the connection and sends the initial protocol.
55 ** m -- mailer to create connection to.
56 ** mci -- the mailer connection info.
58 ** onlyhelo -- send only helo command?
64 ** creates connection and sends initial protocol.
68 smtpinit(m, mci, e, onlyhelo)
83 sm_dprintf("smtpinit ");
84 mci_dump(sm_debug_file(), mci, false);
88 ** Open the connection to the mailer.
92 SmtpMsgBuffer[0] = '\0';
93 CurHostName = mci->mci_host; /* XXX UGLY XXX */
94 if (CurHostName == NULL)
95 CurHostName = MyHostName;
97 state = mci->mci_state;
103 /* need to clear old information */
115 /* shouldn't happen */
120 syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
129 mci->mci_state = MCIS_OPENING;
133 ** Get the greeting message.
134 ** This should appear spontaneously. Give it five minutes to
138 SmtpPhase = mci->mci_phase = "client greeting";
139 sm_setproctitle(true, e, "%s %s: %s",
140 qid_printname(e), CurHostName, mci->mci_phase);
141 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL,
145 if (REPLYTYPE(r) == 4)
147 if (REPLYTYPE(r) != 2)
151 ** Send the HELO command.
152 ** My mother taught me to always introduce myself.
156 if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
157 mci->mci_flags |= MCIF_ESMTP;
158 hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
161 #if _FFR_IGNORE_EXT_ON_HELO
162 mci->mci_flags &= ~MCIF_HELO;
163 #endif /* _FFR_IGNORE_EXT_ON_HELO */
164 if (bitnset(M_LMTP, m->m_flags))
166 smtpmessage("LHLO %s", m, mci, hn);
167 SmtpPhase = mci->mci_phase = "client LHLO";
169 else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
170 !bitnset(M_FSMTP, m->m_flags))
172 smtpmessage("EHLO %s", m, mci, hn);
173 SmtpPhase = mci->mci_phase = "client EHLO";
177 smtpmessage("HELO %s", m, mci, hn);
178 SmtpPhase = mci->mci_phase = "client HELO";
179 #if _FFR_IGNORE_EXT_ON_HELO
180 mci->mci_flags |= MCIF_HELO;
181 #endif /* _FFR_IGNORE_EXT_ON_HELO */
183 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
184 CurHostName, mci->mci_phase);
186 bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
188 helo_options, NULL, XS_DEFAULT);
191 else if (REPLYTYPE(r) == 5)
193 if (bitset(MCIF_ESMTP, mci->mci_flags) &&
194 !bitnset(M_LMTP, m->m_flags))
196 /* try old SMTP instead */
197 mci->mci_flags &= ~MCIF_ESMTP;
202 else if (REPLYTYPE(r) != 2)
206 ** Check to see if we actually ended up talking to ourself.
207 ** This means we didn't know about an alias or MX, or we managed
208 ** to connect to an echo server.
211 p = strchr(&SmtpReplyBuffer[4], ' ');
214 if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
215 !bitnset(M_LMTP, m->m_flags) &&
216 sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
218 syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
220 mci_setstat(mci, EX_CONFIG, "5.3.5",
221 "553 5.3.5 system config error");
228 ** If this is expected to be another sendmail, send some internal
230 ** If we're running as MSP, "propagate" -v flag if possible.
233 if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
234 # if !_FFR_DEPRECATE_MAILER_FLAG_I
235 || bitnset(M_INTERNAL, m->m_flags)
236 # endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
239 /* tell it to be verbose */
240 smtpmessage("VERB", m, mci);
241 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
247 if (mci->mci_state != MCIS_CLOSED)
249 mci->mci_state = MCIS_OPEN;
253 /* got a 421 error code during startup */
256 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
257 if (mci->mci_state != MCIS_CLOSED)
262 /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
263 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
265 if (mci->mci_state != MCIS_CLOSED)
270 mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
275 ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
278 ** line -- the response line.
279 ** firstline -- set if this is the first line of the reply.
281 ** mci -- the mailer connection info.
282 ** e -- the envelope.
289 esmtp_check(line, firstline, m, mci, e)
296 if (strstr(line, "ESMTP") != NULL)
297 mci->mci_flags |= MCIF_ESMTP;
300 ** Dirty hack below. Quoting the author:
301 ** This was a response to people who wanted SMTP transmission to be
302 ** just-send-8 by default. Essentially, you could put this tag into
303 ** your greeting message to behave as though the F=8 flag was set on
307 if (strstr(line, "8BIT-OK") != NULL)
308 mci->mci_flags |= MCIF_8BITOK;
312 /* specify prototype so compiler can check calls */
313 static char *str_union __P((char *, char *, SM_RPOOL_T *));
316 ** STR_UNION -- create the union of two lists
319 ** s1, s2 -- lists of items (separated by single blanks).
320 ** rpool -- resource pool from which result is allocated.
323 ** the union of both lists.
327 str_union(s1, s2, rpool)
331 char *hr, *h1, *h, *res;
334 if (s1 == NULL || *s1 == '\0')
336 if (s2 == NULL || *s2 == '\0')
341 res = (char *) sm_rpool_malloc(rpool, rl + 2);
348 (void) sm_strlcpy(res, s1, rl);
353 /* walk through s2 */
354 while (h != NULL && *h1 != '\0')
356 /* is there something after the current word? */
357 if ((h = strchr(h1, ' ')) != NULL)
361 /* does the current word appear in s1 ? */
362 if (iteminlist(h1, s1, " ") == NULL)
364 /* add space as delimiter */
370 /* advance pointer in result list */
376 /* there are more items */
386 ** HELO_OPTIONS -- process the options on a HELO line.
389 ** line -- the response line.
390 ** firstline -- set if this is the first line of the reply.
392 ** mci -- the mailer connection info.
393 ** e -- the envelope (unused).
400 helo_options(line, firstline, m, mci, e)
408 #if _FFR_IGNORE_EXT_ON_HELO
409 static bool logged = false;
410 #endif /* _FFR_IGNORE_EXT_ON_HELO */
415 mci->mci_saslcap = NULL;
417 #if _FFR_IGNORE_EXT_ON_HELO
419 #endif /* _FFR_IGNORE_EXT_ON_HELO */
422 #if _FFR_IGNORE_EXT_ON_HELO
423 else if (bitset(MCIF_HELO, mci->mci_flags))
425 if (LogLevel > 8 && !logged)
427 sm_syslog(LOG_WARNING, NOQID,
428 "server=%s [%s] returned extensions despite HELO command",
429 macvalue(macid("{server_name}"), e),
430 macvalue(macid("{server_addr}"), e));
435 #endif /* _FFR_IGNORE_EXT_ON_HELO */
437 if (strlen(line) < 5)
440 p = strpbrk(line, " =");
443 if (sm_strcasecmp(line, "size") == 0)
445 mci->mci_flags |= MCIF_SIZE;
447 mci->mci_maxsize = atol(p);
449 else if (sm_strcasecmp(line, "8bitmime") == 0)
451 mci->mci_flags |= MCIF_8BITMIME;
452 mci->mci_flags &= ~MCIF_7BIT;
454 else if (sm_strcasecmp(line, "expn") == 0)
455 mci->mci_flags |= MCIF_EXPN;
456 else if (sm_strcasecmp(line, "dsn") == 0)
457 mci->mci_flags |= MCIF_DSN;
458 else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
459 mci->mci_flags |= MCIF_ENHSTAT;
460 else if (sm_strcasecmp(line, "pipelining") == 0)
461 mci->mci_flags |= MCIF_PIPELINED;
462 else if (sm_strcasecmp(line, "verb") == 0)
463 mci->mci_flags |= MCIF_VERB;
465 else if (sm_strcasecmp(line, "starttls") == 0)
466 mci->mci_flags |= MCIF_TLS;
467 #endif /* STARTTLS */
468 else if (sm_strcasecmp(line, "deliverby") == 0)
470 mci->mci_flags |= MCIF_DLVR_BY;
472 mci->mci_min_by = atol(p);
475 else if (sm_strcasecmp(line, "auth") == 0)
477 if (p != NULL && *p != '\0')
479 if (mci->mci_saslcap != NULL)
482 ** Create the union with previous auth
483 ** offerings because we recognize "auth "
484 ** and "auth=" (old format).
487 mci->mci_saslcap = str_union(mci->mci_saslcap,
489 mci->mci_flags |= MCIF_AUTH;
496 mci->mci_saslcap = (char *)
497 sm_rpool_malloc(mci->mci_rpool, l);
498 if (mci->mci_saslcap != NULL)
500 (void) sm_strlcpy(mci->mci_saslcap, p,
502 mci->mci_flags |= MCIF_AUTH;
511 static int getsimple __P((void *, int, const char **, unsigned *));
512 static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
513 static int saslgetrealm __P((void *, int, const char **, const char **));
514 static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
515 static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *));
516 static char *removemech __P((char *, char *, SM_RPOOL_T *));
517 static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
519 static sasl_callback_t callbacks[] =
521 { SASL_CB_GETREALM, &saslgetrealm, NULL },
522 #define CB_GETREALM_IDX 0
523 { SASL_CB_PASS, &getsecret, NULL },
524 #define CB_PASS_IDX 1
525 { SASL_CB_USER, &getsimple, NULL },
526 #define CB_USER_IDX 2
527 { SASL_CB_AUTHNAME, &getsimple, NULL },
528 #define CB_AUTHNAME_IDX 3
529 { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
530 #define CB_SAFESASL_IDX 4
531 { SASL_CB_LIST_END, NULL, NULL }
535 ** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
541 ** SASL_OK -- if successful.
542 ** SASL error code -- otherwise.
545 ** checks/sets sasl_clt_init.
548 static bool sasl_clt_init = false;
557 result = sasl_client_init(callbacks);
559 /* should we retry later again or just remember that it failed? */
560 if (result == SASL_OK)
561 sasl_clt_init = true;
565 ** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
574 ** checks/sets sasl_clt_init.
582 sasl_clt_init = false;
586 ** GETSASLDATA -- process the challenges from the SASL protocol
588 ** This gets the relevant sasl response data out of the reply
592 ** line -- the response line.
593 ** firstline -- set if this is the first line of the reply.
595 ** mci -- the mailer connection info.
596 ** e -- the envelope (unused).
602 static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
605 getsasldata(line, firstline, m, mci, e)
616 # endif /* SASL < 20000 */
618 /* if not a continue we don't care about it */
622 !isascii(line[1]) || !isdigit(line[1]) ||
623 !isascii(line[2]) || !isdigit(line[2]))
625 SM_FREE_CLR(mci->mci_sasl_string);
629 /* forget about "334 " */
633 /* XXX put this into a macro/function? It's duplicated below */
634 if (mci->mci_sasl_string != NULL)
636 if (mci->mci_sasl_string_len <= len)
638 sm_free(mci->mci_sasl_string); /* XXX */
639 mci->mci_sasl_string = xalloc(len + 1);
643 mci->mci_sasl_string = xalloc(len + 1);
645 result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
646 (unsigned int *) &mci->mci_sasl_string_len);
647 if (result != SASL_OK)
649 mci->mci_sasl_string_len = 0;
650 *mci->mci_sasl_string = '\0';
652 # else /* SASL >= 20000 */
653 out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
654 result = sasl_decode64(line, len, out, (unsigned int *) &len);
655 if (result != SASL_OK)
662 ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
663 ** it can't be in an rpool unless we use the same memory
664 ** management mechanism (with same rpool!) for Cyrus SASL.
667 if (mci->mci_sasl_string != NULL)
669 if (mci->mci_sasl_string_len <= len)
671 sm_free(mci->mci_sasl_string); /* XXX */
672 mci->mci_sasl_string = xalloc(len + 1);
676 mci->mci_sasl_string = xalloc(len + 1);
678 memcpy(mci->mci_sasl_string, out, len);
679 mci->mci_sasl_string[len] = '\0';
680 mci->mci_sasl_string_len = len;
681 # endif /* SASL >= 20000 */
685 ** READAUTH -- read auth values from a file
688 ** filename -- name of file to read.
689 ** safe -- if set, this is a safe read.
690 ** sai -- where to store auth_info.
691 ** rpool -- resource pool for sai.
694 ** EX_OK -- data succesfully read.
695 ** EX_UNAVAILABLE -- no valid filename.
696 ** EX_TEMPFAIL -- temporary failure.
699 static char *sasl_info_name[] =
708 readauth(filename, safe, sai, rpool)
721 if (filename == NULL || filename[0] == '\0')
722 return EX_UNAVAILABLE;
724 #if !_FFR_ALLOW_SASLINFO
726 ** make sure we don't use a program that is not
727 ** accesible to the user who specified a different authinfo file.
728 ** However, currently we don't pass this info (authinfo file
729 ** specified by user) around, so we just turn off program access.
732 if (filename[0] == '|')
737 char *argv[MAXPV + 1];
740 for (p = strtok(&filename[1], " \t"); p != NULL;
741 p = strtok(NULL, " \t"))
748 pid = prog_open(argv, &fd, CurEnv);
752 f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
753 (void *) &fd, SM_IO_RDONLY, NULL);
756 #endif /* !_FFR_ALLOW_SASLINFO */
759 sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
760 |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
761 # if _FFR_GROUPREADABLEAUTHINFOFILE
762 if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
763 # endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
764 sff |= SFF_NOGRFILES;
765 if (DontLockReadFiles)
768 #if _FFR_ALLOW_SASLINFO
770 ** XXX: make sure we don't read or open files that are not
771 ** accesible to the user who specified a different authinfo
776 #else /* _FFR_ALLOW_SASLINFO */
778 sff |= SFF_OPENASROOT;
779 #endif /* _FFR_ALLOW_SASLINFO */
781 f = safefopen(filename, O_RDONLY, 0, sff);
786 sm_syslog(LOG_ERR, NOQID,
787 "AUTH=client, error: can't open %s: %s",
788 filename, sm_errstring(errno));
793 while (lc <= SASL_MECHLIST &&
794 sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
798 (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
799 if ((s = strchr((*sai)[lc], '\n')) != NULL)
805 (void) sm_io_close(f, SM_TIME_DEFAULT);
808 if (lc < SASL_PASSWORD)
811 sm_syslog(LOG_ERR, NOQID,
812 "AUTH=client, error: can't read %s from %s",
813 sasl_info_name[lc + 1], filename);
820 ** GETAUTH -- get authinfo from ruleset call
822 ** {server_name}, {server_addr} must be set
825 ** mci -- the mailer connection structure.
826 ** e -- the envelope (including the sender to specify).
827 ** sai -- pointer to authinfo (result).
830 ** EX_OK -- ruleset was succesfully called, data may not
831 ** be available, sai must be checked.
832 ** EX_UNAVAILABLE -- ruleset unavailable (or failed).
833 ** EX_TEMPFAIL -- temporary failure (from ruleset).
836 ** Fills in sai if successful.
845 int i, r, l, got, ret;
847 char pvpbuf[PSBUFSIZE];
849 r = rscap("authinfo", macvalue(macid("{server_name}"), e),
850 macvalue(macid("{server_addr}"), e), e,
851 &pvp, pvpbuf, sizeof(pvpbuf));
854 return EX_UNAVAILABLE;
856 /* other than expected return value: ok (i.e., no auth) */
857 if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
859 if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
863 ** parse the data, put it into sai
864 ** format: "TDstring" (including the '"' !)
865 ** where T is a tag: 'U', ...
866 ** D is a delimiter: ':' or '='
869 ret = EX_OK; /* default return value */
872 while (i < SASL_ENTRIES)
874 if (pvp[i + 1] == NULL)
876 if (pvp[i + 1][0] != '"')
878 switch (pvp[i + 1][1])
903 l = strlen(pvp[i + 1]);
906 if (l <= 3 || pvp[i + 1][l - 1] != '"')
909 /* remove closing quote */
910 pvp[i + 1][l - 1] = '\0';
912 /* remove "TD and " */
914 (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
915 if ((*sai)[r] == NULL)
917 if (pvp[i + 1][2] == ':')
919 /* ':text' (just copy) */
920 (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
923 else if (pvp[i + 1][2] == '=')
927 /* '=base64' (decode) */
929 ret = sasl_decode64(pvp[i + 1] + 3,
930 (unsigned int) l, (*sai)[r],
931 (unsigned int) l + 1, &len);
932 # else /* SASL >= 20000 */
933 ret = sasl_decode64(pvp[i + 1] + 3,
934 (unsigned int) l, (*sai)[r], &len);
935 # endif /* SASL >= 20000 */
943 sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
944 sasl_info_name[r], (*sai)[r]);
948 /* did we get the expected data? */
949 /* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
950 if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
951 bitset(SASL_PASSWORD_BIT, got)))
954 /* no authid? copy uid */
955 if (!bitset(SASL_AUTHID_BIT, got))
957 l = strlen((*sai)[SASL_USER]) + 1;
958 (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
960 if ((*sai)[SASL_AUTHID] == NULL)
962 (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
965 /* no uid? copy authid */
966 if (!bitset(SASL_USER_BIT, got))
968 l = strlen((*sai)[SASL_AUTHID]) + 1;
969 (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
971 if ((*sai)[SASL_USER] == NULL)
973 (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
981 sm_syslog(LOG_WARNING, NOQID,
982 "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
983 macvalue(macid("{server_name}"), e),
984 macvalue(macid("{server_addr}"), e),
985 ret == EX_TEMPFAIL ? "temp" : "");
986 for (i = 0; i <= SASL_MECHLIST; i++)
987 (*sai)[i] = NULL; /* just clear; rpool */
993 ** GETSIMPLE -- callback to get userid or authid
998 ** result -- (pointer to) result
999 ** len -- (pointer to) length of result
1002 ** OK/failure values
1006 getsimple(context, id, result, len)
1009 const char **result;
1014 if (result == NULL || context == NULL)
1015 return SASL_BADPARAM;
1016 sai = (SASL_AI_T *) context;
1021 *result = (*sai)[SASL_USER];
1023 sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1026 *len = *result != NULL ? strlen(*result) : 0;
1029 case SASL_CB_AUTHNAME:
1030 *result = (*sai)[SASL_AUTHID];
1032 sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1035 *len = *result != NULL ? strlen(*result) : 0;
1038 case SASL_CB_LANGUAGE:
1045 return SASL_BADPARAM;
1050 ** GETSECRET -- callback to get password
1053 ** conn -- connection information
1056 ** psecret -- (pointer to) result
1059 ** OK/failure values
1063 getsecret(conn, context, id, psecret)
1065 SM_UNUSED(void *context);
1067 sasl_secret_t **psecret;
1073 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1074 return SASL_BADPARAM;
1076 mci = (MCI *) context;
1077 authpass = mci->mci_sai[SASL_PASSWORD];
1078 len = strlen(authpass);
1081 ** use an rpool because we are responsible for free()ing the secret,
1082 ** but we can't free() it until after the auth completes
1085 *psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
1086 sizeof(sasl_secret_t) +
1088 if (*psecret == NULL)
1090 (void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
1091 (*psecret)->len = (unsigned long) len;
1094 # else /* SASL >= 20000 */
1096 ** GETSIMPLE -- callback to get userid or authid
1101 ** result -- (pointer to) result
1102 ** len -- (pointer to) length of result
1105 ** OK/failure values
1109 getsimple(context, id, result, len)
1112 const char **result;
1118 # endif /* SASL > 10509 */
1121 char *authid = NULL;
1123 if (result == NULL || context == NULL)
1124 return SASL_BADPARAM;
1125 sai = (SASL_AI_T *) context;
1128 ** Unfortunately it is not clear whether this routine should
1129 ** return a copy of a string or just a pointer to a string.
1130 ** The Cyrus-SASL plugins treat these return values differently, e.g.,
1131 ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1132 ** The best solution to this problem is to fix Cyrus-SASL, but it
1133 ** seems there is nobody who creates patches... Hello CMU!?
1134 ** The second best solution is to have flags that tell this routine
1135 ** whether to return an malloc()ed copy.
1136 ** The next best solution is to always return an malloc()ed copy,
1137 ** and suffer from some memory leak, which is ugly for persistent
1139 ** For now we go with the last solution...
1140 ** We can't use rpools (which would avoid this particular problem)
1141 ** as explained in sasl.c.
1147 l = strlen((*sai)[SASL_USER]) + 1;
1148 s = sm_sasl_malloc(l);
1156 (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
1159 sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1162 *len = *result != NULL ? strlen(*result) : 0;
1165 case SASL_CB_AUTHNAME:
1166 h = (*sai)[SASL_AUTHID];
1168 /* XXX maybe other mechanisms too?! */
1169 addrealm = (*sai)[SASL_MECH] != NULL &&
1170 sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
1173 ** Add realm to authentication id unless authid contains
1174 ** '@' (i.e., a realm) or the default realm is empty.
1177 if (addrealm && h != NULL && strchr(h, '@') == NULL)
1179 /* has this been done before? */
1180 if ((*sai)[SASL_ID_REALM] == NULL)
1184 realm = (*sai)[SASL_DEFREALM];
1186 /* do not add an empty realm */
1190 (*sai)[SASL_ID_REALM] = NULL;
1194 l = strlen(h) + strlen(realm) + 2;
1196 /* should use rpool, but from where? */
1197 authid = sm_sasl_malloc(l);
1200 (void) sm_snprintf(authid, l,
1203 (*sai)[SASL_ID_REALM] = authid;
1208 (*sai)[SASL_ID_REALM] = NULL;
1213 authid = (*sai)[SASL_ID_REALM];
1216 # endif /* SASL > 10509 */
1218 l = strlen(authid) + 1;
1219 s = sm_sasl_malloc(l);
1227 (void) sm_strlcpy(s, authid, l);
1230 sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1233 *len = authid ? strlen(authid) : 0;
1236 case SASL_CB_LANGUAGE:
1243 return SASL_BADPARAM;
1248 ** GETSECRET -- callback to get password
1251 ** conn -- connection information
1254 ** psecret -- (pointer to) result
1257 ** OK/failure values
1261 getsecret(conn, context, id, psecret)
1263 SM_UNUSED(void *context);
1265 sasl_secret_t **psecret;
1271 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1272 return SASL_BADPARAM;
1274 sai = (SASL_AI_T *) context;
1275 authpass = (*sai)[SASL_PASSWORD];
1276 len = strlen(authpass);
1277 *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
1279 if (*psecret == NULL)
1281 (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
1282 (*psecret)->len = (unsigned long) len;
1285 # endif /* SASL >= 20000 */
1288 ** SAFESASLFILE -- callback for sasl: is file safe?
1291 ** context -- pointer to context between invocations (unused)
1292 ** file -- name of file to check
1293 ** type -- type of file to check
1296 ** SASL_OK -- file can be used
1297 ** SASL_CONTINUE -- don't use file
1298 ** SASL_FAIL -- failure (not used here)
1304 safesaslfile(context, file, type)
1305 #else /* SASL > 10515 */
1306 safesaslfile(context, file)
1307 #endif /* SASL > 10515 */
1311 # else /* SASL >= 20000 */
1313 # endif /* SASL >= 20000 */
1316 sasl_verify_type_t type;
1317 # else /* SASL >= 20000 */
1319 # endif /* SASL >= 20000 */
1320 #endif /* SASL > 10515 */
1326 #endif /* SASL <= 10515 */
1329 if (file == NULL || *file == '\0')
1331 sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
1333 if ((p = strrchr(file, '/')) == NULL)
1338 /* everything beside libs and .conf files must not be readable */
1340 if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
1341 (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
1343 if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1344 sff |= SFF_NORFILES;
1345 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1346 sff |= SFF_NOGWFILES;
1348 #else /* SASL <= 10515 */
1349 /* files containing passwords should be not readable */
1350 if (type == SASL_VRFY_PASSWD)
1352 if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1353 sff |= SFF_NOWRFILES;
1355 sff |= SFF_NORFILES;
1356 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1357 sff |= SFF_NOGWFILES;
1359 #endif /* SASL <= 10515 */
1362 if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
1363 S_IRUSR, NULL)) == 0)
1365 if (LogLevel > (r != ENOENT ? 8 : 10))
1366 sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
1367 p, sm_errstring(r));
1368 return SASL_CONTINUE;
1372 ** SASLGETREALM -- return the realm for SASL
1374 ** return the realm for the client
1377 ** context -- context shared between invocations
1378 ** availrealms -- list of available realms
1379 ** {realm, realm, ...}
1380 ** result -- pointer to result
1387 saslgetrealm(context, id, availrealms, result)
1390 const char **availrealms;
1391 const char **result;
1396 sai = (SASL_AI_T *) context;
1399 r = (*sai)[SASL_DEFREALM];
1402 sm_syslog(LOG_INFO, NOQID,
1403 "AUTH=client, realm=%s, available realms=%s",
1404 r == NULL ? "<No Realm>" : r,
1405 (availrealms == NULL || *availrealms == NULL)
1406 ? "<No Realms>" : *availrealms);
1408 /* check whether context is in list */
1409 if (availrealms != NULL && *availrealms != NULL)
1411 if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
1415 sm_syslog(LOG_ERR, NOQID,
1416 "AUTH=client, realm=%s not in list=%s",
1425 ** ITEMINLIST -- does item appear in list?
1427 ** Check whether item appears in list (which must be separated by a
1428 ** character in delim) as a "word", i.e. it must appear at the begin
1429 ** of the list or after a space, and it must end with a space or the
1433 ** item -- item to search.
1434 ** list -- list of items.
1435 ** delim -- list of delimiters.
1438 ** pointer to occurrence (NULL if not found).
1442 iteminlist(item, list, delim)
1450 if (list == NULL || *list == '\0')
1452 if (item == NULL || *item == '\0')
1456 while (s != NULL && *s != '\0')
1458 if (sm_strncasecmp(s, item, len) == 0 &&
1459 (s[len] == '\0' || strchr(delim, s[len]) != NULL))
1461 s = strpbrk(s, delim);
1469 ** REMOVEMECH -- remove item [rem] from list [list]
1472 ** rem -- item to remove
1473 ** list -- list of items
1474 ** rpool -- resource pool from which result is allocated.
1477 ** pointer to new list (NULL in case of error).
1481 removemech(rem, list, rpool)
1492 if (rem == NULL || *rem == '\0')
1494 /* take out what? */
1498 /* find the item in the list */
1499 if ((needle = iteminlist(rem, list, " ")) == NULL)
1501 /* not in there: return original */
1505 /* length of string without rem */
1506 len = strlen(list) - strlen(rem);
1509 ret = (char *) sm_rpool_malloc_x(rpool, 1);
1513 ret = (char *) sm_rpool_malloc_x(rpool, len);
1514 memset(ret, '\0', len);
1516 /* copy from start to removed item */
1517 memcpy(ret, list, needle - list);
1519 /* length of rest of string past removed item */
1520 len = strlen(needle) - strlen(rem) - 1;
1523 /* not last item -- copy into string */
1524 memcpy(ret + (needle - list),
1525 list + (needle - list) + strlen(rem) + 1,
1529 ret[(needle - list) - 1] = '\0';
1533 ** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1537 ** mci -- the mailer connection structure.
1538 ** e -- the envelope (including the sender to specify).
1539 ** sai - sasl authinfo
1542 ** EX_OK -- authentication was successful.
1543 ** EX_NOPERM -- authentication failed.
1544 ** EX_IOERR -- authentication dialogue failed (I/O problem?).
1545 ** EX_TEMPFAIL -- temporary failure.
1550 attemptauth(m, mci, e, sai)
1556 int saslresult, smtpresult;
1559 const char *auth_id;
1561 # else /* SASL >= 20000 */
1562 sasl_external_properties_t ssf;
1564 # endif /* SASL >= 20000 */
1565 unsigned int outlen;
1566 sasl_interact_t *client_interact = NULL;
1568 sasl_security_properties_t ssp;
1569 char in64[MAXOUTLEN];
1570 #if NETINET || (NETINET6 && SASL >= 20000)
1571 extern SOCKADDR CurHostAddr;
1572 #endif /* NETINET || (NETINET6 && SASL >= 20000) */
1574 /* no mechanism selected (yet) */
1575 (*sai)[SASL_MECH] = NULL;
1577 /* dispose old connection */
1578 if (mci->mci_conn != NULL)
1579 sasl_dispose(&(mci->mci_conn));
1581 /* make a new client sasl connection */
1583 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1585 CurHostName, NULL, NULL, NULL, 0,
1587 # else /* SASL >= 20000 */
1588 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1590 CurHostName, NULL, 0, &mci->mci_conn);
1591 # endif /* SASL >= 20000 */
1592 if (saslresult != SASL_OK)
1595 /* set properties */
1596 (void) memset(&ssp, '\0', sizeof ssp);
1598 /* XXX should these be options settable via .cf ? */
1600 ssp.max_ssf = MaxSLBits;
1601 ssp.maxbufsize = MAXOUTLEN;
1603 ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1606 saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1607 if (saslresult != SASL_OK)
1611 /* external security strength factor, authentication id */
1615 out = macvalue(macid("{cert_subject}"), e);
1616 if (out != NULL && *out != '\0')
1618 out = macvalue(macid("{cipher_bits}"), e);
1619 if (out != NULL && *out != '\0')
1621 # endif /* STARTTLS */
1622 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1623 if (saslresult != SASL_OK)
1625 saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
1626 if (saslresult != SASL_OK)
1629 # if NETINET || NETINET6
1630 /* set local/remote ipv4 addresses */
1631 if (mci->mci_out != NULL && (
1633 CurHostAddr.sa.sa_family == AF_INET6 ||
1634 # endif /* NETINET6 */
1635 CurHostAddr.sa.sa_family == AF_INET))
1637 SOCKADDR_LEN_T addrsize;
1639 char localip[60], remoteip[60];
1641 switch (CurHostAddr.sa.sa_family)
1644 addrsize = sizeof(struct sockaddr_in);
1648 addrsize = sizeof(struct sockaddr_in6);
1650 # endif /* NETINET6 */
1654 if (iptostring(&CurHostAddr, addrsize,
1655 remoteip, sizeof remoteip))
1657 if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
1658 remoteip) != SASL_OK)
1661 addrsize = sizeof(saddr_l);
1662 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1664 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1666 if (iptostring(&saddr_l, addrsize,
1667 localip, sizeof localip))
1669 if (sasl_setprop(mci->mci_conn,
1671 localip) != SASL_OK)
1676 # endif /* NETINET || NETINET6 */
1678 /* start client side of sasl */
1679 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1682 (const char **) &mechusing);
1683 # else /* SASL >= 20000 */
1684 /* external security strength factor, authentication id */
1688 out = macvalue(macid("{cert_subject}"), e);
1689 if (out != NULL && *out != '\0')
1691 out = macvalue(macid("{cipher_bits}"), e);
1692 if (out != NULL && *out != '\0')
1693 ssf.ssf = atoi(out);
1694 # endif /* STARTTLS */
1695 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1696 if (saslresult != SASL_OK)
1700 /* set local/remote ipv4 addresses */
1701 if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1703 SOCKADDR_LEN_T addrsize;
1704 struct sockaddr_in saddr_l;
1706 if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1707 (struct sockaddr_in *) &CurHostAddr)
1710 addrsize = sizeof(struct sockaddr_in);
1711 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1713 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1715 if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1716 &saddr_l) != SASL_OK)
1720 # endif /* NETINET */
1722 /* start client side of sasl */
1723 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1724 NULL, &client_interact,
1726 (const char **) &mechusing);
1727 # endif /* SASL >= 20000 */
1729 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1731 if (saslresult == SASL_NOMECH && LogLevel > 8)
1733 sm_syslog(LOG_NOTICE, e->e_id,
1734 "AUTH=client, available mechanisms do not fulfill requirements");
1739 /* just point current mechanism to the data in the sasl library */
1740 (*sai)[SASL_MECH] = mechusing;
1742 /* send the info across the wire */
1744 /* login and digest-md5 up to 1.5.28 set out="" */
1746 (sm_strcasecmp(mechusing, "LOGIN") == 0 ||
1747 sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
1750 /* no initial response */
1751 smtpmessage("AUTH %s", m, mci, mechusing);
1753 else if (outlen == 0)
1756 ** zero-length initial response, per RFC 2554 4.:
1757 ** "Unlike a zero-length client answer to a 334 reply, a zero-
1758 ** length initial response is sent as a single equals sign"
1761 smtpmessage("AUTH %s =", m, mci, mechusing);
1765 saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1766 if (saslresult != SASL_OK) /* internal error */
1769 sm_syslog(LOG_ERR, e->e_id,
1770 "encode64 for AUTH failed");
1773 smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1776 sm_sasl_free(out); /* XXX only if no rpool is used */
1777 # endif /* SASL < 20000 */
1780 smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
1785 /* check return code from server */
1786 if (smtpresult == 235)
1788 macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
1792 if (smtpresult == -1)
1794 if (REPLYTYPE(smtpresult) == 5)
1795 return EX_NOPERM; /* ugly, but ... */
1796 if (REPLYTYPE(smtpresult) != 3)
1798 /* should we fail deliberately, see RFC 2554 4. ? */
1799 /* smtpmessage("*", m, mci); */
1803 saslresult = sasl_client_step(mci->mci_conn,
1804 mci->mci_sasl_string,
1805 mci->mci_sasl_string_len,
1809 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1812 sm_dprintf("AUTH FAIL=%s (%d)\n",
1813 sasl_errstring(saslresult, NULL, NULL),
1816 /* fail deliberately, see RFC 2554 4. */
1817 smtpmessage("*", m, mci);
1820 ** but we should only fail for this authentication
1821 ** mechanism; how to do that?
1824 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1825 getsasldata, NULL, XS_AUTH);
1831 saslresult = sasl_encode64(out, outlen, in64,
1833 if (saslresult != SASL_OK)
1835 /* give an error reply to the other side! */
1836 smtpmessage("*", m, mci);
1843 sm_sasl_free(out); /* XXX only if no rpool is used */
1844 # endif /* SASL < 20000 */
1845 smtpmessage("%s", m, mci, in64);
1846 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1847 getsasldata, NULL, XS_AUTH);
1852 ** SMTPAUTH -- try to AUTHenticate
1854 ** This will try mechanisms in the order the sasl library decided until:
1855 ** - there are no more mechanisms
1856 ** - a mechanism succeeds
1857 ** - the sasl library fails initializing
1861 ** mci -- the mailer connection info.
1862 ** e -- the envelope.
1865 ** EX_OK -- authentication was successful
1866 ** EX_UNAVAILABLE -- authentication not possible, e.g.,
1867 ** no data available.
1868 ** EX_NOPERM -- authentication failed.
1869 ** EX_TEMPFAIL -- temporary failure.
1871 ** Notice: AuthInfo is used for all connections, hence we must
1872 ** return EX_TEMPFAIL only if we really want to retry, i.e.,
1873 ** iff getauth() tempfailed or getauth() was used and
1874 ** authentication tempfailed.
1887 mci->mci_sasl_auth = false;
1888 for (i = 0; i < SASL_MECH ; i++)
1889 mci->mci_sai[i] = NULL;
1891 result = getauth(mci, e, &(mci->mci_sai));
1892 if (result == EX_TEMPFAIL)
1896 /* no data available: don't try to authenticate */
1897 if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
1899 if (result != EX_OK)
1901 if (SASLInfo == NULL)
1902 return EX_UNAVAILABLE;
1904 /* read authinfo from file */
1905 result = readauth(SASLInfo, true, &(mci->mci_sai),
1907 if (result != EX_OK)
1909 usedgetauth = false;
1912 /* check whether sufficient data is available */
1913 if (mci->mci_sai[SASL_PASSWORD] == NULL ||
1914 *(mci->mci_sai)[SASL_PASSWORD] == '\0')
1915 return EX_UNAVAILABLE;
1916 if ((mci->mci_sai[SASL_AUTHID] == NULL ||
1917 *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
1918 (mci->mci_sai[SASL_USER] == NULL ||
1919 *(mci->mci_sai)[SASL_USER] == '\0'))
1920 return EX_UNAVAILABLE;
1922 /* set the context for the callback function to sai */
1924 callbacks[CB_PASS_IDX].context = (void *) mci;
1925 # else /* SASL >= 20000 */
1926 callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
1927 # endif /* SASL >= 20000 */
1928 callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
1929 callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
1930 callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
1932 callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
1935 /* set default value for realm */
1936 if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
1937 (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
1938 macvalue('j', CurEnv));
1940 /* set default value for list of mechanism to use */
1941 if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
1942 *(mci->mci_sai)[SASL_MECHLIST] == '\0')
1943 (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
1945 /* create list of mechanisms to try */
1946 mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
1947 mci->mci_saslcap, mci->mci_rpool);
1949 /* initialize sasl client library */
1950 result = init_sasl_client();
1951 if (result != SASL_OK)
1952 return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
1955 result = attemptauth(m, mci, e, &(mci->mci_sai));
1956 if (result == EX_OK)
1957 mci->mci_sasl_auth = true;
1958 else if (result == EX_TEMPFAIL || result == EX_NOPERM)
1960 mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
1963 if (mci->mci_saslcap == NULL ||
1964 *(mci->mci_saslcap) == '\0')
1965 return usedgetauth ? result
1970 } while (result != EX_OK);
1976 ** SMTPMAILFROM -- send MAIL command
1980 ** mci -- the mailer connection structure.
1981 ** e -- the envelope (including the sender to specify).
1985 smtpmailfrom(m, mci, e)
1994 char buf[MAXNAME + 1];
1995 char optbuf[MAXLINE];
1998 sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
2002 ** Check if connection is gone, if so
2003 ** it's a tempfail and we use mci_errno
2007 if (mci->mci_state == MCIS_CLOSED)
2009 errno = mci->mci_errno;
2013 /* set up appropriate options to include */
2014 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
2016 (void) sm_snprintf(optbuf, sizeof optbuf, " SIZE=%ld",
2018 bufp = &optbuf[strlen(optbuf)];
2026 bodytype = e->e_bodytype;
2027 if (bitset(MCIF_8BITMIME, mci->mci_flags))
2029 if (bodytype == NULL &&
2030 bitset(MM_MIME8BIT, MimeMode) &&
2031 bitset(EF_HAS8BIT, e->e_flags) &&
2032 !bitset(EF_DONT_MIME, e->e_flags) &&
2033 !bitnset(M_8BITS, m->m_flags))
2034 bodytype = "8BITMIME";
2035 if (bodytype != NULL &&
2036 SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2038 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2039 " BODY=%s", bodytype);
2040 bufp += strlen(bufp);
2043 else if (bitnset(M_8BITS, m->m_flags) ||
2044 !bitset(EF_HAS8BIT, e->e_flags) ||
2045 bitset(MCIF_8BITOK, mci->mci_flags))
2048 /* just pass it through */
2051 else if (bitset(MM_CVTMIME, MimeMode) &&
2052 !bitset(EF_DONT_MIME, e->e_flags) &&
2053 (!bitset(MM_PASS8BIT, MimeMode) ||
2054 bitset(EF_IS_MIME, e->e_flags)))
2056 /* must convert from 8bit MIME format to 7bit encoded */
2057 mci->mci_flags |= MCIF_CVT8TO7;
2059 #endif /* MIME8TO7 */
2060 else if (!bitset(MM_PASS8BIT, MimeMode))
2062 /* cannot just send a 8-bit version */
2063 extern char MsgBuf[];
2065 usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2066 mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2070 if (bitset(MCIF_DSN, mci->mci_flags))
2072 if (e->e_envid != NULL &&
2073 SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2075 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2076 " ENVID=%s", e->e_envid);
2077 bufp += strlen(bufp);
2080 /* RET= parameter */
2081 if (bitset(EF_RET_PARAM, e->e_flags) &&
2082 SPACELEFT(optbuf, bufp) > 9)
2084 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2086 bitset(EF_NO_BODY_RETN, e->e_flags) ?
2088 bufp += strlen(bufp);
2092 if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
2093 SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
2095 && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
2099 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2100 " AUTH=%s", e->e_auth_param);
2101 bufp += strlen(bufp);
2105 ** 17 is the max length required, we could use log() to compute
2106 ** the exact length (and check IS_DLVR_TRACE())
2109 if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
2110 IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
2115 ** Avoid problems with delays (for R) since the check
2116 ** in deliver() whether min-deliver-time is sufficient.
2117 ** Alternatively we could pass the computed time to this
2121 dby = e->e_deliver_by - (curtime() - e->e_ctime);
2122 if (dby <= 0 && IS_DLVR_RETURN(e))
2123 dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
2124 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2127 IS_DLVR_RETURN(e) ? 'R' : 'N',
2128 IS_DLVR_TRACE(e) ? "T" : "");
2129 bufp += strlen(bufp);
2133 ** Send the MAIL command.
2134 ** Designates the sender.
2137 mci->mci_state = MCIS_MAIL;
2139 if (bitset(EF_RESPONSE, e->e_flags) &&
2140 !bitnset(M_NO_NULL_FROM, m->m_flags))
2143 expand("\201g", buf, sizeof buf, e);
2146 /* strip off <angle brackets> (put back on below) */
2147 bufp = &buf[strlen(buf) - 1];
2154 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2155 !bitnset(M_FROMPATH, m->m_flags))
2157 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2161 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2162 *bufp == '@' ? ',' : ':', bufp, optbuf);
2164 SmtpPhase = mci->mci_phase = "client MAIL";
2165 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2166 CurHostName, mci->mci_phase);
2167 r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_DEFAULT);
2170 /* communications failure */
2171 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2174 else if (r == SMTPCLOSING)
2176 /* service shutting down: handled by reply() */
2179 else if (REPLYTYPE(r) == 4)
2181 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
2185 else if (REPLYTYPE(r) == 2)
2191 /* syntax error in arguments */
2192 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
2198 /* mailbox name not allowed */
2199 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
2205 /* exceeded storage allocation */
2206 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
2208 if (bitset(MCIF_SIZE, mci->mci_flags))
2209 e->e_flags |= EF_NO_BODY_RETN;
2210 return EX_UNAVAILABLE;
2212 else if (REPLYTYPE(r) == 5)
2215 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
2217 return EX_UNAVAILABLE;
2222 sm_syslog(LOG_CRIT, e->e_id,
2223 "%.100s: SMTP MAIL protocol error: %s",
2225 shortenstring(SmtpReplyBuffer, 403));
2228 /* protocol error -- close up */
2229 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2231 smtpquit(m, mci, e);
2235 ** SMTPRCPT -- designate recipient.
2238 ** to -- address of recipient.
2239 ** m -- the mailer we are sending to.
2240 ** mci -- the connection info for this transaction.
2241 ** e -- the envelope for this transaction.
2244 ** exit status corresponding to recipient status.
2247 ** Sends the mail via SMTP.
2251 smtprcpt(to, m, mci, e, ctladdr, xstart)
2260 char optbuf[MAXLINE];
2264 ** If there is status waiting from the other end, read it.
2265 ** This should normally happen because of SMTP pipelining.
2268 while (mci->mci_nextaddr != NULL &&
2269 sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2273 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2276 markfailure(e, mci->mci_nextaddr, mci, r, false);
2277 giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
2278 ctladdr, xstart, e, to);
2280 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2282 #endif /* PIPELINING */
2285 ** Check if connection is gone, if so
2286 ** it's a tempfail and we use mci_errno
2290 if (mci->mci_state == MCIS_CLOSED)
2292 errno = mci->mci_errno;
2300 ** Warning: in the following it is assumed that the free space
2301 ** in bufp is sizeof optbuf
2304 if (bitset(MCIF_DSN, mci->mci_flags))
2306 if (IS_DLVR_NOTIFY(e) &&
2307 !bitset(MCIF_DLVR_BY, mci->mci_flags))
2309 /* RFC 2852: 4.1.4.2 */
2310 if (!bitset(QHASNOTIFY, to->q_flags))
2311 to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
2312 else if (bitset(QPINGONSUCCESS, to->q_flags) ||
2313 bitset(QPINGONFAILURE, to->q_flags) ||
2314 bitset(QPINGONDELAY, to->q_flags))
2315 to->q_flags |= QPINGONDELAY;
2318 /* NOTIFY= parameter */
2319 if (bitset(QHASNOTIFY, to->q_flags) &&
2320 bitset(QPRIMARY, to->q_flags) &&
2321 !bitnset(M_LOCALMAILER, m->m_flags))
2323 bool firstone = true;
2325 (void) sm_strlcat(bufp, " NOTIFY=", sizeof optbuf);
2326 if (bitset(QPINGONSUCCESS, to->q_flags))
2328 (void) sm_strlcat(bufp, "SUCCESS", sizeof optbuf);
2331 if (bitset(QPINGONFAILURE, to->q_flags))
2334 (void) sm_strlcat(bufp, ",",
2336 (void) sm_strlcat(bufp, "FAILURE", sizeof optbuf);
2339 if (bitset(QPINGONDELAY, to->q_flags))
2342 (void) sm_strlcat(bufp, ",",
2344 (void) sm_strlcat(bufp, "DELAY", sizeof optbuf);
2348 (void) sm_strlcat(bufp, "NEVER", sizeof optbuf);
2349 bufp += strlen(bufp);
2352 /* ORCPT= parameter */
2353 if (to->q_orcpt != NULL &&
2354 SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2356 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2357 " ORCPT=%s", to->q_orcpt);
2358 bufp += strlen(bufp);
2362 smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
2363 mci->mci_state = MCIS_RCPT;
2365 SmtpPhase = mci->mci_phase = "client RCPT";
2366 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2367 CurHostName, mci->mci_phase);
2371 ** If running SMTP pipelining, we will pick up status later
2374 if (bitset(MCIF_PIPELINED, mci->mci_flags))
2376 #endif /* PIPELINING */
2378 return smtprcptstat(to, m, mci, e);
2381 ** SMTPRCPTSTAT -- get recipient status
2383 ** This is only called during SMTP pipelining
2386 ** to -- address of recipient.
2387 ** m -- mailer being sent to.
2388 ** mci -- the mailer connection information.
2389 ** e -- the envelope for this message.
2392 ** EX_* -- protocol status
2396 smtprcptstat(to, m, mci, e)
2400 register ENVELOPE *e;
2407 ** Check if connection is gone, if so
2408 ** it's a tempfail and we use mci_errno
2412 if (mci->mci_state == MCIS_CLOSED)
2414 errno = mci->mci_errno;
2419 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_DEFAULT);
2421 to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
2422 to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
2423 if (!bitnset(M_LMTP, m->m_flags))
2424 to->q_statmta = mci->mci_host;
2425 if (r < 0 || REPLYTYPE(r) == 4)
2427 mci->mci_retryrcpt = true;
2431 else if (REPLYTYPE(r) == 2)
2435 if ((t = mci->mci_tolist) != NULL)
2440 for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
2443 mci->mci_tolist = t;
2447 #endif /* PIPELINING */
2452 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2457 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2462 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2465 else if (REPLYTYPE(r) == 5)
2467 return EX_UNAVAILABLE;
2472 sm_syslog(LOG_CRIT, e->e_id,
2473 "%.100s: SMTP RCPT protocol error: %s",
2475 shortenstring(SmtpReplyBuffer, 403));
2478 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2483 ** SMTPDATA -- send the data and clean up the transaction.
2486 ** m -- mailer being sent to.
2487 ** mci -- the mailer connection information.
2488 ** e -- the envelope for this message.
2491 ** exit status corresponding to DATA command.
2494 static jmp_buf CtxDataTimeout;
2495 static SM_EVENT *volatile DataTimeout = NULL;
2498 smtpdata(m, mci, e, ctladdr, xstart)
2501 register ENVELOPE *e;
2512 ** Check if connection is gone, if so
2513 ** it's a tempfail and we use mci_errno
2517 if (mci->mci_state == MCIS_CLOSED)
2519 errno = mci->mci_errno;
2527 ** First send the command and check that it is ok.
2528 ** Then send the data (if there are valid recipients).
2529 ** Follow it up with a dot to terminate.
2530 ** Finally get the results of the transaction.
2533 /* send the command and check ok to proceed */
2534 smtpmessage("DATA", m, mci);
2537 if (mci->mci_nextaddr != NULL)
2539 char *oldto = e->e_to;
2541 /* pick up any pending RCPT responses for SMTP pipelining */
2542 while (mci->mci_nextaddr != NULL)
2546 e->e_to = mci->mci_nextaddr->q_paddr;
2547 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2550 markfailure(e, mci->mci_nextaddr, mci, r,
2552 giveresponse(r, mci->mci_nextaddr->q_status, m,
2553 mci, ctladdr, xstart, e,
2555 if (r == EX_TEMPFAIL)
2556 mci->mci_nextaddr->q_state = QS_RETRY;
2558 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2563 ** Connection might be closed in response to a RCPT command,
2564 ** i.e., the server responded with 421. In that case (at
2565 ** least) one RCPT has a temporary failure, hence we don't
2566 ** need to check mci_okrcpts (as it is done below) to figure
2567 ** out which error to return.
2570 if (mci->mci_state == MCIS_CLOSED)
2572 errno = mci->mci_errno;
2576 #endif /* PIPELINING */
2578 /* now proceed with DATA phase */
2579 SmtpPhase = mci->mci_phase = "client DATA 354";
2580 mci->mci_state = MCIS_DATA;
2581 sm_setproctitle(true, e, "%s %s: %s",
2582 qid_printname(e), CurHostName, mci->mci_phase);
2583 r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DEFAULT);
2584 if (r < 0 || REPLYTYPE(r) == 4)
2587 smtpquit(m, mci, e);
2588 errno = mci->mci_errno;
2591 else if (REPLYTYPE(r) == 5)
2593 smtprset(m, mci, e);
2595 if (mci->mci_okrcpts <= 0)
2596 return mci->mci_retryrcpt ? EX_TEMPFAIL
2598 #endif /* PIPELINING */
2599 return EX_UNAVAILABLE;
2601 else if (REPLYTYPE(r) != 3)
2605 sm_syslog(LOG_CRIT, e->e_id,
2606 "%.100s: SMTP DATA-1 protocol error: %s",
2608 shortenstring(SmtpReplyBuffer, 403));
2610 smtprset(m, mci, e);
2611 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2614 if (mci->mci_okrcpts <= 0)
2615 return mci->mci_retryrcpt ? EX_TEMPFAIL
2617 #endif /* PIPELINING */
2622 if (mci->mci_okrcpts > 0)
2624 #endif /* PIPELINING */
2627 ** Set timeout around data writes. Make it at least large
2628 ** enough for DNS timeouts on all recipients plus some fudge
2629 ** factor. The main thing is that it should not be infinite.
2632 if (setjmp(CtxDataTimeout) != 0)
2634 mci->mci_errno = errno;
2635 mci->mci_state = MCIS_ERROR;
2636 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2639 ** If putbody() couldn't finish due to a timeout,
2640 ** rewind it here in the timeout handler. See
2641 ** comments at the end of putbody() for reasoning.
2644 if (e->e_dfp != NULL)
2645 (void) bfrewind(e->e_dfp);
2647 errno = mci->mci_errno;
2648 syserr("451 4.4.1 timeout writing message to %s", CurHostName);
2649 smtpquit(m, mci, e);
2655 /* simulate a DATA timeout */
2659 timeout = DATA_PROGRESS_TIMEOUT;
2661 DataTimeout = sm_setevent(timeout, datatimeout, 0);
2665 ** Output the actual message.
2668 (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
2672 /* simulate a DATA timeout */
2676 (*e->e_putbody)(mci, e, NULL);
2679 ** Cleanup after sending message.
2682 if (DataTimeout != NULL)
2683 sm_clrevent(DataTimeout);
2687 #endif /* PIPELINING */
2689 #if _FFR_CATCH_BROKEN_MTAS
2690 if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2692 /* terminate the message */
2693 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
2695 if (TrafficLogFile != NULL)
2696 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2697 "%05d >>> .\n", (int) CurrentPid);
2701 sm_syslog(LOG_CRIT, e->e_id,
2702 "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2704 mci->mci_errno = EIO;
2705 mci->mci_state = MCIS_ERROR;
2706 mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
2707 smtpquit(m, mci, e);
2710 #endif /* _FFR_CATCH_BROKEN_MTAS */
2712 if (sm_io_error(mci->mci_out))
2714 /* error during processing -- don't send the dot */
2715 mci->mci_errno = EIO;
2716 mci->mci_state = MCIS_ERROR;
2717 mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2718 smtpquit(m, mci, e);
2722 /* terminate the message */
2723 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", m->m_eol);
2724 if (TrafficLogFile != NULL)
2725 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2726 "%05d >>> .\n", (int) CurrentPid);
2730 /* check for the results of the transaction */
2731 SmtpPhase = mci->mci_phase = "client DATA status";
2732 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2733 CurHostName, mci->mci_phase);
2734 if (bitnset(M_LMTP, m->m_flags))
2736 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
2739 if (mci->mci_state == MCIS_DATA)
2740 mci->mci_state = MCIS_OPEN;
2741 xstat = EX_NOTSTICKY;
2743 rstat = EX_TEMPFAIL;
2744 else if (REPLYTYPE(r) == 4)
2745 rstat = xstat = EX_TEMPFAIL;
2746 else if (REPLYTYPE(r) == 2)
2747 rstat = xstat = EX_OK;
2748 else if (REPLYCLASS(r) != 5)
2749 rstat = xstat = EX_PROTOCOL;
2750 else if (REPLYTYPE(r) == 5)
2751 rstat = EX_UNAVAILABLE;
2753 rstat = EX_PROTOCOL;
2754 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
2756 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2757 (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2761 e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2762 SmtpPhase = mci->mci_phase = "idle";
2763 sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2764 if (rstat != EX_PROTOCOL)
2768 sm_syslog(LOG_CRIT, e->e_id,
2769 "%.100s: SMTP DATA-2 protocol error: %s",
2771 shortenstring(SmtpReplyBuffer, 403));
2780 int save_errno = errno;
2783 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2784 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2792 /* check back again later */
2795 /* simulate a DATA timeout */
2799 timeout = DATA_PROGRESS_TIMEOUT;
2801 /* reset the timeout */
2802 DataTimeout = sm_sigsafe_setevent(timeout, datatimeout, 0);
2803 DataProgress = false;
2811 /* if no progress was made or problem resetting event, die now */
2812 if (DataTimeout == NULL)
2815 longjmp(CtxDataTimeout, 1);
2820 ** SMTPGETSTAT -- get status code from DATA in LMTP
2823 ** m -- the mailer to which we are sending the message.
2824 ** mci -- the mailer connection structure.
2825 ** e -- the current envelope.
2828 ** The exit status corresponding to the reply code.
2832 smtpgetstat(m, mci, e)
2844 /* check for the results of the transaction */
2845 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
2848 xstat = EX_NOTSTICKY;
2849 if (REPLYTYPE(r) == 4)
2850 status = EX_TEMPFAIL;
2851 else if (REPLYTYPE(r) == 2)
2852 status = xstat = EX_OK;
2853 else if (REPLYCLASS(r) != 5)
2854 status = xstat = EX_PROTOCOL;
2855 else if (REPLYTYPE(r) == 5)
2856 status = EX_UNAVAILABLE;
2858 status = EX_PROTOCOL;
2859 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2860 (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2864 e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
2865 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
2866 if (LogLevel > 1 && status == EX_PROTOCOL)
2868 sm_syslog(LOG_CRIT, e->e_id,
2869 "%.100s: SMTP DATA-3 protocol error: %s",
2871 shortenstring(SmtpReplyBuffer, 403));
2876 ** SMTPQUIT -- close the SMTP connection.
2879 ** m -- a pointer to the mailer.
2880 ** mci -- the mailer connection information.
2881 ** e -- the current envelope.
2887 ** sends the final protocol and closes the connection.
2896 bool oldSuprErrs = SuprErrs;
2900 if (mci->mci_state == MCIS_CLOSED)
2902 mci_close(mci, "smtpquit:1");
2906 oldcurhost = CurHostName;
2907 CurHostName = mci->mci_host; /* XXX UGLY XXX */
2908 if (CurHostName == NULL)
2909 CurHostName = MyHostName;
2912 mci->mci_okrcpts = 0;
2913 #endif /* PIPELINING */
2916 ** Suppress errors here -- we may be processing a different
2917 ** job when we do the quit connection, and we don't want the
2918 ** new job to be penalized for something that isn't it's
2924 /* send the quit message if we haven't gotten I/O error */
2925 if (mci->mci_state != MCIS_ERROR &&
2926 mci->mci_state != MCIS_QUITING)
2928 SmtpPhase = "client QUIT";
2929 mci->mci_state = MCIS_QUITING;
2930 smtpmessage("QUIT", m, mci);
2931 (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL,
2933 SuprErrs = oldSuprErrs;
2934 if (mci->mci_state == MCIS_CLOSED)
2938 /* now actually close the connection and pick up the zombie */
2939 rcode = endmailer(mci, e, NULL);
2942 char *mailer = NULL;
2944 if (mci->mci_mailer != NULL &&
2945 mci->mci_mailer->m_name != NULL)
2946 mailer = mci->mci_mailer->m_name;
2948 /* look for naughty mailers */
2949 sm_syslog(LOG_ERR, e->e_id,
2950 "smtpquit: mailer%s%s exited with exit value %d",
2951 mailer == NULL ? "" : " ",
2952 mailer == NULL ? "" : mailer,
2956 SuprErrs = oldSuprErrs;
2959 CurHostName = oldcurhost;
2963 ** SMTPRSET -- send a RSET (reset) command
2966 ** m -- a pointer to the mailer.
2967 ** mci -- the mailer connection information.
2968 ** e -- the current envelope.
2974 ** closes the connection if there is no reply to RSET.
2985 CurHostName = mci->mci_host; /* XXX UGLY XXX */
2986 if (CurHostName == NULL)
2987 CurHostName = MyHostName;
2990 mci->mci_okrcpts = 0;
2991 #endif /* PIPELINING */
2994 ** Check if connection is gone, if so
2995 ** it's a tempfail and we use mci_errno
2999 if (mci->mci_state == MCIS_CLOSED)
3001 errno = mci->mci_errno;
3005 SmtpPhase = "client RSET";
3006 smtpmessage("RSET", m, mci);
3007 r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT);
3012 ** Any response is deemed to be acceptable.
3013 ** The standard does not state the proper action
3014 ** to take when a value other than 250 is received.
3016 ** However, if 421 is returned for the RSET, leave
3017 ** mci_state alone (MCIS_SSD can be set in reply()
3018 ** and MCIS_CLOSED can be set in smtpquit() if
3019 ** reply() gets a 421 and calls smtpquit()).
3022 if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
3023 mci->mci_state = MCIS_OPEN;
3026 ** SMTPPROBE -- check the connection state
3029 ** mci -- the mailer connection information.
3035 ** closes the connection if there is no reply to RSET.
3043 MAILER *m = mci->mci_mailer;
3045 extern ENVELOPE BlankEnvelope;
3047 CurHostName = mci->mci_host; /* XXX UGLY XXX */
3048 if (CurHostName == NULL)
3049 CurHostName = MyHostName;
3052 SmtpPhase = "client probe";
3053 smtpmessage("RSET", m, mci);
3054 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT);
3055 if (REPLYTYPE(r) != 2)
3056 smtpquit(m, mci, e);
3060 ** REPLY -- read arpanet reply
3063 ** m -- the mailer we are reading the reply from.
3064 ** mci -- the mailer connection info structure.
3065 ** e -- the current envelope.
3066 ** timeout -- the timeout for reads.
3067 ** pfunc -- processing function called on each line of response.
3068 ** If null, no special processing is done.
3069 ** enhstat -- optional, returns enhanced error code string (if set)
3070 ** rtype -- type of SmtpMsgBuffer: does it contains secret data?
3073 ** reply code it reads.
3076 ** flushes the mail file.
3080 reply(m, mci, e, timeout, pfunc, enhstat, rtype)
3085 void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
3089 register char *bufp;
3091 bool firstline = true;
3092 char junkbuf[MAXLINE];
3093 static char enhstatcode[ENHSCLEN];
3097 ** Flush the output before reading response.
3099 ** For SMTP pipelining, it would be better if we didn't do
3100 ** this if there was already data waiting to be read. But
3101 ** to do it properly means pushing it to the I/O library,
3102 ** since it really needs to be done below the buffer layer.
3105 if (mci->mci_out != NULL)
3106 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3109 sm_dprintf("reply\n");
3112 ** Read the input line, being careful not to hang.
3115 bufp = SmtpReplyBuffer;
3120 /* actually do the read */
3121 if (e->e_xfp != NULL) /* for debugging */
3122 (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3124 /* if we are in the process of closing just give the code */
3125 if (mci->mci_state == MCIS_CLOSED)
3128 /* don't try to read from a non-existent fd */
3129 if (mci->mci_in == NULL)
3131 if (mci->mci_errno == 0)
3132 mci->mci_errno = EBADF;
3134 /* errors on QUIT should be ignored */
3135 if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3137 errno = mci->mci_errno;
3138 mci_close(mci, "reply:1");
3141 mci->mci_state = MCIS_ERROR;
3142 smtpquit(m, mci, e);
3143 errno = mci->mci_errno;
3147 if (mci->mci_out != NULL)
3148 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3150 /* get the line from the other side */
3151 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
3153 mci->mci_lastuse = curtime();
3158 extern char MsgBuf[];
3160 /* errors on QUIT should be ignored */
3161 if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3163 mci_close(mci, "reply:2");
3167 /* if the remote end closed early, fake an error */
3171 (void) sm_snprintf(SmtpReplyBuffer,
3172 sizeof SmtpReplyBuffer,
3173 "421 4.4.1 Connection reset by %s",
3177 #else /* ECONNRESET */
3179 #endif /* ECONNRESET */
3182 mci->mci_errno = errno;
3183 oldholderrs = HoldErrs;
3185 usrerr("451 4.4.1 reply: read error from %s",
3187 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3189 /* if debugging, pause so we can see state */
3192 mci->mci_state = MCIS_ERROR;
3193 smtpquit(m, mci, e);
3199 if (e->e_to != NULL)
3201 (void) sm_snprintf(p,
3204 shortenstring(e->e_to, MAXSHORTSTR));
3207 (void) sm_snprintf(p, SPACELEFT(wbuf, p),
3208 "reply(%.100s) during %s",
3209 CURHOSTNAME, SmtpPhase);
3213 HoldErrs = oldholderrs;
3217 fixcrlf(bufp, true);
3219 /* EHLO failure is not a real error */
3220 if (e->e_xfp != NULL && (bufp[0] == '4' ||
3221 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3223 /* serious error -- log the previous command */
3226 /* inform user who we are chatting with */
3227 (void) sm_io_fprintf(CurEnv->e_xfp,
3229 "... while talking to %s:\n",
3231 SmtpNeedIntro = false;
3233 if (SmtpMsgBuffer[0] != '\0')
3235 (void) sm_io_fprintf(e->e_xfp,
3238 (rtype == XS_STARTTLS)
3239 ? "STARTTLS dialogue"
3240 : ((rtype == XS_AUTH)
3243 SmtpMsgBuffer[0] = '\0';
3246 /* now log the message as from the other side */
3247 (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3251 /* display the input for verbose mode */
3253 nmessage("050 %s", bufp);
3255 /* ignore improperly formatted input */
3256 if (!ISSMTPREPLY(bufp))
3259 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
3261 extenhsc(bufp + 4, ' ', enhstatcode) > 0)
3262 *enhstat = enhstatcode;
3264 /* process the line */
3266 (*pfunc)(bufp, firstline, m, mci, e);
3270 /* decode the reply code */
3273 /* extra semantics: 0xx codes are "informational" */
3277 /* if no continuation lines, return this line */
3281 /* first line of real reply -- ignore rest */
3286 ** Now look at SmtpReplyBuffer -- only care about the first
3287 ** line of the response from here on out.
3290 /* save temporary failure messages for posterity */
3291 if (SmtpReplyBuffer[0] == '4')
3292 (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof SmtpError);
3294 /* reply code 421 is "Service Shutting Down" */
3295 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3296 mci->mci_state != MCIS_QUITING)
3298 /* send the quit protocol */
3299 mci->mci_state = MCIS_SSD;
3300 smtpquit(m, mci, e);
3306 ** SMTPMESSAGE -- send message to server
3310 ** m -- the mailer to control formatting.
3311 ** a, b, c -- parameters
3317 ** writes message to mci->mci_out.
3323 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
3324 #else /* __STDC__ */
3325 smtpmessage(f, m, mci, va_alist)
3330 #endif /* __STDC__ */
3334 SM_VA_START(ap, mci);
3335 (void) sm_vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
3338 if (tTd(18, 1) || Verbose)
3339 nmessage(">>> %s", SmtpMsgBuffer);
3340 if (TrafficLogFile != NULL)
3341 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
3342 "%05d >>> %s\n", (int) CurrentPid,
3344 if (mci->mci_out != NULL)
3346 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
3347 SmtpMsgBuffer, m == NULL ? "\r\n"
3350 else if (tTd(18, 1))
3352 sm_dprintf("smtpmessage: NULL mci_out\n");