]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/usersmtp.c
MFC: Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / usersmtp.c
1 /*
2  * Copyright (c) 1998-2006, 2008-2010, 2014 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
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.
7  *
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.
11  *
12  */
13
14 #include <sendmail.h>
15
16 SM_RCSID("@(#)$Id: usersmtp.c,v 8.488 2013-11-22 20:51:57 ca Exp $")
17
18 #include <sysexits.h>
19
20
21 static void     esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
22 static void     helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
23 static int      smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
24
25 #if SASL
26 extern void     *sm_sasl_malloc __P((unsigned long));
27 extern void     sm_sasl_free __P((void *));
28 #endif
29
30 /*
31 **  USERSMTP -- run SMTP protocol from the user end.
32 **
33 **      This protocol is described in RFC821.
34 */
35
36 #define SMTPCLOSING     421                     /* "Service Shutting Down" */
37
38 #define ENHSCN(e, d)    ((e) == NULL ? (d) : (e))
39
40 #define ENHSCN_RPOOL(e, d, rpool) \
41         ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
42
43 static char     SmtpMsgBuffer[MAXLINE];         /* buffer for commands */
44 static char     SmtpReplyBuffer[MAXLINE];       /* buffer for replies */
45 static bool     SmtpNeedIntro;          /* need "while talking" in transcript */
46 /*
47 **  SMTPINIT -- initialize SMTP.
48 **
49 **      Opens the connection and sends the initial protocol.
50 **
51 **      Parameters:
52 **              m -- mailer to create connection to.
53 **              mci -- the mailer connection info.
54 **              e -- the envelope.
55 **              onlyhelo -- send only helo command?
56 **
57 **      Returns:
58 **              none.
59 **
60 **      Side Effects:
61 **              creates connection and sends initial protocol.
62 */
63
64 void
65 smtpinit(m, mci, e, onlyhelo)
66         MAILER *m;
67         register MCI *mci;
68         ENVELOPE *e;
69         bool onlyhelo;
70 {
71         register int r;
72         int state;
73         register char *p;
74         register char *hn;
75 #if _FFR_EXPAND_HELONAME
76         char hnbuf[MAXNAME + 1];
77 #endif
78         char *enhsc;
79
80         enhsc = NULL;
81         if (tTd(18, 1))
82         {
83                 sm_dprintf("smtpinit ");
84                 mci_dump(sm_debug_file(), mci, false);
85         }
86
87         /*
88         **  Open the connection to the mailer.
89         */
90
91         SmtpError[0] = '\0';
92         SmtpMsgBuffer[0] = '\0';
93         CurHostName = mci->mci_host;            /* XXX UGLY XXX */
94         if (CurHostName == NULL)
95                 CurHostName = MyHostName;
96         SmtpNeedIntro = true;
97         state = mci->mci_state;
98         e->e_rcode = 0;
99         e->e_renhsc[0] = '\0';
100         e->e_text = NULL;
101         switch (state)
102         {
103           case MCIS_MAIL:
104           case MCIS_RCPT:
105           case MCIS_DATA:
106                 /* need to clear old information */
107                 smtprset(m, mci, e);
108                 /* FALLTHROUGH */
109
110           case MCIS_OPEN:
111                 if (!onlyhelo)
112                         return;
113                 break;
114
115           case MCIS_ERROR:
116           case MCIS_QUITING:
117           case MCIS_SSD:
118                 /* shouldn't happen */
119                 smtpquit(m, mci, e);
120                 /* FALLTHROUGH */
121
122           case MCIS_CLOSED:
123                 syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
124                 return;
125
126           case MCIS_OPENING:
127                 break;
128         }
129         if (onlyhelo)
130                 goto helo;
131
132         mci->mci_state = MCIS_OPENING;
133         clrsessenvelope(e);
134
135         /*
136         **  Get the greeting message.
137         **      This should appear spontaneously.  Give it five minutes to
138         **      happen.
139         */
140
141         SmtpPhase = mci->mci_phase = "client greeting";
142         sm_setproctitle(true, e, "%s %s: %s",
143                         qid_printname(e), CurHostName, mci->mci_phase);
144         r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL, XS_GREET);
145         if (r < 0)
146                 goto tempfail1;
147         if (REPLYTYPE(r) == 4)
148                 goto tempfail2;
149         if (REPLYTYPE(r) != 2)
150                 goto unavailable;
151
152         /*
153         **  Send the HELO command.
154         **      My mother taught me to always introduce myself.
155         */
156
157 helo:
158         if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
159                 mci->mci_flags |= MCIF_ESMTP;
160         if (mci->mci_heloname != NULL)
161         {
162 #if _FFR_EXPAND_HELONAME
163                 expand(mci->mci_heloname, hnbuf, sizeof(hnbuf), e);
164                 hn = hnbuf;
165 #else
166                 hn = mci->mci_heloname;
167 #endif
168         }
169         else
170                 hn = MyHostName;
171
172 tryhelo:
173 #if _FFR_IGNORE_EXT_ON_HELO
174         mci->mci_flags &= ~MCIF_HELO;
175 #endif
176         if (bitnset(M_LMTP, m->m_flags))
177         {
178                 smtpmessage("LHLO %s", m, mci, hn);
179                 SmtpPhase = mci->mci_phase = "client LHLO";
180         }
181         else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
182                  !bitnset(M_FSMTP, m->m_flags))
183         {
184                 smtpmessage("EHLO %s", m, mci, hn);
185                 SmtpPhase = mci->mci_phase = "client EHLO";
186         }
187         else
188         {
189                 smtpmessage("HELO %s", m, mci, hn);
190                 SmtpPhase = mci->mci_phase = "client HELO";
191 #if _FFR_IGNORE_EXT_ON_HELO
192                 mci->mci_flags |= MCIF_HELO;
193 #endif
194         }
195         sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
196                         CurHostName, mci->mci_phase);
197         r = reply(m, mci, e,
198                   bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
199                                               : TimeOuts.to_helo,
200                   helo_options, NULL, XS_EHLO);
201         if (r < 0)
202                 goto tempfail1;
203         else if (REPLYTYPE(r) == 5)
204         {
205                 if (bitset(MCIF_ESMTP, mci->mci_flags) &&
206                     !bitnset(M_LMTP, m->m_flags))
207                 {
208                         /* try old SMTP instead */
209                         mci->mci_flags &= ~MCIF_ESMTP;
210                         goto tryhelo;
211                 }
212                 goto unavailable;
213         }
214         else if (REPLYTYPE(r) != 2)
215                 goto tempfail2;
216
217         /*
218         **  Check to see if we actually ended up talking to ourself.
219         **  This means we didn't know about an alias or MX, or we managed
220         **  to connect to an echo server.
221         */
222
223         p = strchr(&SmtpReplyBuffer[4], ' ');
224         if (p != NULL)
225                 *p = '\0';
226         if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
227             !bitnset(M_LMTP, m->m_flags) &&
228             sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
229         {
230                 syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
231                         CurHostName);
232                 mci_setstat(mci, EX_CONFIG, "5.3.5",
233                             "553 5.3.5 system config error");
234                 mci->mci_errno = 0;
235                 smtpquit(m, mci, e);
236                 return;
237         }
238
239         /*
240         **  If this is expected to be another sendmail, send some internal
241         **  commands.
242         **  If we're running as MSP, "propagate" -v flag if possible.
243         */
244
245         if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
246             || bitnset(M_INTERNAL, m->m_flags))
247         {
248                 /* tell it to be verbose */
249                 smtpmessage("VERB", m, mci);
250                 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
251                         XS_DEFAULT);
252                 if (r < 0)
253                         goto tempfail1;
254         }
255
256         if (mci->mci_state != MCIS_CLOSED)
257         {
258                 mci->mci_state = MCIS_OPEN;
259                 return;
260         }
261
262         /* got a 421 error code during startup */
263
264   tempfail1:
265         mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
266         if (mci->mci_state != MCIS_CLOSED)
267                 smtpquit(m, mci, e);
268         return;
269
270   tempfail2:
271         /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
272         mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
273                     SmtpReplyBuffer);
274         if (mci->mci_state != MCIS_CLOSED)
275                 smtpquit(m, mci, e);
276         return;
277
278   unavailable:
279         mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
280         smtpquit(m, mci, e);
281         return;
282 }
283 /*
284 **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
285 **
286 **      Parameters:
287 **              line -- the response line.
288 **              firstline -- set if this is the first line of the reply.
289 **              m -- the mailer.
290 **              mci -- the mailer connection info.
291 **              e -- the envelope.
292 **
293 **      Returns:
294 **              none.
295 */
296
297 static void
298 esmtp_check(line, firstline, m, mci, e)
299         char *line;
300         bool firstline;
301         MAILER *m;
302         register MCI *mci;
303         ENVELOPE *e;
304 {
305         if (strstr(line, "ESMTP") != NULL)
306                 mci->mci_flags |= MCIF_ESMTP;
307
308         /*
309         **  Dirty hack below. Quoting the author:
310         **  This was a response to people who wanted SMTP transmission to be
311         **  just-send-8 by default.  Essentially, you could put this tag into
312         **  your greeting message to behave as though the F=8 flag was set on
313         **  the mailer.
314         */
315
316         if (strstr(line, "8BIT-OK") != NULL)
317                 mci->mci_flags |= MCIF_8BITOK;
318 }
319
320 #if SASL
321 /* specify prototype so compiler can check calls */
322 static char *str_union __P((char *, char *, SM_RPOOL_T *));
323
324 /*
325 **  STR_UNION -- create the union of two lists
326 **
327 **      Parameters:
328 **              s1, s2 -- lists of items (separated by single blanks).
329 **              rpool -- resource pool from which result is allocated.
330 **
331 **      Returns:
332 **              the union of both lists.
333 */
334
335 static char *
336 str_union(s1, s2, rpool)
337         char *s1, *s2;
338         SM_RPOOL_T *rpool;
339 {
340         char *hr, *h1, *h, *res;
341         int l1, l2, rl;
342
343         if (s1 == NULL || *s1 == '\0')
344                 return s2;
345         if (s2 == NULL || *s2 == '\0')
346                 return s1;
347         l1 = strlen(s1);
348         l2 = strlen(s2);
349         rl = l1 + l2;
350         if (rl <= 0)
351         {
352                 sm_syslog(LOG_WARNING, NOQID,
353                           "str_union: stringlen1=%d, stringlen2=%d, sum=%d, status=overflow",
354                           l1, l2, rl);
355                 res = NULL;
356         }
357         else
358                 res = (char *) sm_rpool_malloc(rpool, rl + 2);
359         if (res == NULL)
360         {
361                 if (l1 > l2)
362                         return s1;
363                 return s2;
364         }
365         (void) sm_strlcpy(res, s1, rl);
366         hr = res + l1;
367         h1 = s2;
368         h = s2;
369
370         /* walk through s2 */
371         while (h != NULL && *h1 != '\0')
372         {
373                 /* is there something after the current word? */
374                 if ((h = strchr(h1, ' ')) != NULL)
375                         *h = '\0';
376                 l1 = strlen(h1);
377
378                 /* does the current word appear in s1 ? */
379                 if (iteminlist(h1, s1, " ") == NULL)
380                 {
381                         /* add space as delimiter */
382                         *hr++ = ' ';
383
384                         /* copy the item */
385                         memcpy(hr, h1, l1);
386
387                         /* advance pointer in result list */
388                         hr += l1;
389                         *hr = '\0';
390                 }
391                 if (h != NULL)
392                 {
393                         /* there are more items */
394                         *h = ' ';
395                         h1 = h + 1;
396                 }
397         }
398         return res;
399 }
400 #endif /* SASL */
401
402 /*
403 **  HELO_OPTIONS -- process the options on a HELO line.
404 **
405 **      Parameters:
406 **              line -- the response line.
407 **              firstline -- set if this is the first line of the reply.
408 **              m -- the mailer.
409 **              mci -- the mailer connection info.
410 **              e -- the envelope (unused).
411 **
412 **      Returns:
413 **              none.
414 */
415
416 static void
417 helo_options(line, firstline, m, mci, e)
418         char *line;
419         bool firstline;
420         MAILER *m;
421         register MCI *mci;
422         ENVELOPE *e;
423 {
424         register char *p;
425 #if _FFR_IGNORE_EXT_ON_HELO
426         static bool logged = false;
427 #endif
428
429         if (firstline)
430         {
431                 mci_clr_extensions(mci);
432 #if _FFR_IGNORE_EXT_ON_HELO
433                 logged = false;
434 #endif
435                 return;
436         }
437 #if _FFR_IGNORE_EXT_ON_HELO
438         else if (bitset(MCIF_HELO, mci->mci_flags))
439         {
440                 if (LogLevel > 8 && !logged)
441                 {
442                         sm_syslog(LOG_WARNING, NOQID,
443                                   "server=%s [%s] returned extensions despite HELO command",
444                                   macvalue(macid("{server_name}"), e),
445                                   macvalue(macid("{server_addr}"), e));
446                         logged = true;
447                 }
448                 return;
449         }
450 #endif /* _FFR_IGNORE_EXT_ON_HELO */
451
452         if (strlen(line) < 5)
453                 return;
454         line += 4;
455         p = strpbrk(line, " =");
456         if (p != NULL)
457                 *p++ = '\0';
458         if (sm_strcasecmp(line, "size") == 0)
459         {
460                 mci->mci_flags |= MCIF_SIZE;
461                 if (p != NULL)
462                         mci->mci_maxsize = atol(p);
463         }
464         else if (sm_strcasecmp(line, "8bitmime") == 0)
465         {
466                 mci->mci_flags |= MCIF_8BITMIME;
467                 mci->mci_flags &= ~MCIF_7BIT;
468         }
469         else if (sm_strcasecmp(line, "expn") == 0)
470                 mci->mci_flags |= MCIF_EXPN;
471         else if (sm_strcasecmp(line, "dsn") == 0)
472                 mci->mci_flags |= MCIF_DSN;
473         else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
474                 mci->mci_flags |= MCIF_ENHSTAT;
475         else if (sm_strcasecmp(line, "pipelining") == 0)
476                 mci->mci_flags |= MCIF_PIPELINED;
477         else if (sm_strcasecmp(line, "verb") == 0)
478                 mci->mci_flags |= MCIF_VERB;
479 #if _FFR_EAI
480         else if (sm_strcasecmp(line, "smtputf8") == 0)
481                 mci->mci_flags |= MCIF_EAI;
482 #endif /* _FFR_EAI */
483 #if STARTTLS
484         else if (sm_strcasecmp(line, "starttls") == 0)
485                 mci->mci_flags |= MCIF_TLS;
486 #endif
487         else if (sm_strcasecmp(line, "deliverby") == 0)
488         {
489                 mci->mci_flags |= MCIF_DLVR_BY;
490                 if (p != NULL)
491                         mci->mci_min_by = atol(p);
492         }
493 #if SASL
494         else if (sm_strcasecmp(line, "auth") == 0)
495         {
496                 if (p != NULL && *p != '\0' &&
497                     !bitset(MCIF_AUTH2, mci->mci_flags))
498                 {
499                         if (mci->mci_saslcap != NULL)
500                         {
501                                 /*
502                                 **  Create the union with previous auth
503                                 **  offerings because we recognize "auth "
504                                 **  and "auth=" (old format).
505                                 */
506
507                                 mci->mci_saslcap = str_union(mci->mci_saslcap,
508                                                              p, mci->mci_rpool);
509                                 mci->mci_flags |= MCIF_AUTH2;
510                         }
511                         else
512                         {
513                                 int l;
514
515                                 l = strlen(p) + 1;
516                                 mci->mci_saslcap = (char *)
517                                         sm_rpool_malloc(mci->mci_rpool, l);
518                                 if (mci->mci_saslcap != NULL)
519                                 {
520                                         (void) sm_strlcpy(mci->mci_saslcap, p,
521                                                           l);
522                                         mci->mci_flags |= MCIF_AUTH;
523                                 }
524                         }
525                 }
526                 if (tTd(95, 5))
527                         sm_syslog(LOG_DEBUG, NOQID, "AUTH flags=%lx, mechs=%s",
528                                 mci->mci_flags, mci->mci_saslcap);
529         }
530 #endif /* SASL */
531 }
532 #if SASL
533
534 static int getsimple    __P((void *, int, const char **, unsigned *));
535 static int getsecret    __P((sasl_conn_t *, void *, int, sasl_secret_t **));
536 static int saslgetrealm __P((void *, int, const char **, const char **));
537 static int readauth     __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
538 static int getauth      __P((MCI *, ENVELOPE *, SASL_AI_T *));
539 static char *removemech __P((char *, char *, SM_RPOOL_T *));
540 static int attemptauth  __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
541
542 static sasl_callback_t callbacks[] =
543 {
544         {       SASL_CB_GETREALM,       (sasl_callback_ft)&saslgetrealm,        NULL    },
545 #define CB_GETREALM_IDX 0
546         {       SASL_CB_PASS,           (sasl_callback_ft)&getsecret,   NULL    },
547 #define CB_PASS_IDX     1
548         {       SASL_CB_USER,           (sasl_callback_ft)&getsimple,   NULL    },
549 #define CB_USER_IDX     2
550         {       SASL_CB_AUTHNAME,       (sasl_callback_ft)&getsimple,   NULL    },
551 #define CB_AUTHNAME_IDX 3
552         {       SASL_CB_VERIFYFILE,     (sasl_callback_ft)&safesaslfile,        NULL    },
553 #define CB_SAFESASL_IDX 4
554         {       SASL_CB_LIST_END,       NULL,           NULL    }
555 };
556
557 /*
558 **  INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
559 **
560 **      Parameters:
561 **              none.
562 **
563 **      Returns:
564 **              SASL_OK -- if successful.
565 **              SASL error code -- otherwise.
566 **
567 **      Side Effects:
568 **              checks/sets sasl_clt_init.
569 **
570 **      Note:
571 **      Callbacks are ignored if sasl_client_init() has
572 **      been called before (by a library such as libnss_ldap)
573 */
574
575 static bool sasl_clt_init = false;
576
577 static int
578 init_sasl_client()
579 {
580         int result;
581
582         if (sasl_clt_init)
583                 return SASL_OK;
584         result = sasl_client_init(callbacks);
585
586         /* should we retry later again or just remember that it failed? */
587         if (result == SASL_OK)
588                 sasl_clt_init = true;
589         return result;
590 }
591 /*
592 **  STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
593 **
594 **      Parameters:
595 **              none.
596 **
597 **      Returns:
598 **              none.
599 **
600 **      Side Effects:
601 **              checks/sets sasl_clt_init.
602 */
603
604 void
605 stop_sasl_client()
606 {
607         if (!sasl_clt_init)
608                 return;
609         sasl_clt_init = false;
610         sasl_done();
611 }
612 /*
613 **  GETSASLDATA -- process the challenges from the SASL protocol
614 **
615 **      This gets the relevant sasl response data out of the reply
616 **      from the server.
617 **
618 **      Parameters:
619 **              line -- the response line.
620 **              firstline -- set if this is the first line of the reply.
621 **              m -- the mailer.
622 **              mci -- the mailer connection info.
623 **              e -- the envelope (unused).
624 **
625 **      Returns:
626 **              none.
627 */
628
629 static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
630
631 static void
632 getsasldata(line, firstline, m, mci, e)
633         char *line;
634         bool firstline;
635         MAILER *m;
636         register MCI *mci;
637         ENVELOPE *e;
638 {
639         int len;
640         int result;
641 # if SASL < 20000
642         char *out;
643 # endif
644
645         /* if not a continue we don't care about it */
646         len = strlen(line);
647         if ((len <= 4) ||
648             (line[0] != '3') ||
649              !isascii(line[1]) || !isdigit(line[1]) ||
650              !isascii(line[2]) || !isdigit(line[2]))
651         {
652                 SM_FREE(mci->mci_sasl_string);
653                 return;
654         }
655
656         /* forget about "334 " */
657         line += 4;
658         len -= 4;
659 # if SASL >= 20000
660         /* XXX put this into a macro/function? It's duplicated below */
661         if (mci->mci_sasl_string != NULL)
662         {
663                 if (mci->mci_sasl_string_len <= len)
664                 {
665                         sm_free(mci->mci_sasl_string); /* XXX */
666                         mci->mci_sasl_string = xalloc(len + 1);
667                 }
668         }
669         else
670                 mci->mci_sasl_string = xalloc(len + 1);
671
672         result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
673                                (unsigned int *) &mci->mci_sasl_string_len);
674         if (result != SASL_OK)
675         {
676                 mci->mci_sasl_string_len = 0;
677                 *mci->mci_sasl_string = '\0';
678         }
679 # else /* SASL >= 20000 */
680         out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
681         result = sasl_decode64(line, len, out, (unsigned int *) &len);
682         if (result != SASL_OK)
683         {
684                 len = 0;
685                 *out = '\0';
686         }
687
688         /*
689         **  mci_sasl_string is "shared" with Cyrus-SASL library; hence
690         **      it can't be in an rpool unless we use the same memory
691         **      management mechanism (with same rpool!) for Cyrus SASL.
692         */
693
694         if (mci->mci_sasl_string != NULL)
695         {
696                 if (mci->mci_sasl_string_len <= len)
697                 {
698                         sm_free(mci->mci_sasl_string); /* XXX */
699                         mci->mci_sasl_string = xalloc(len + 1);
700                 }
701         }
702         else
703                 mci->mci_sasl_string = xalloc(len + 1);
704
705         memcpy(mci->mci_sasl_string, out, len);
706         mci->mci_sasl_string[len] = '\0';
707         mci->mci_sasl_string_len = len;
708 # endif /* SASL >= 20000 */
709         return;
710 }
711 /*
712 **  READAUTH -- read auth values from a file
713 **
714 **      Parameters:
715 **              filename -- name of file to read.
716 **              safe -- if set, this is a safe read.
717 **              sai -- where to store auth_info.
718 **              rpool -- resource pool for sai.
719 **
720 **      Returns:
721 **              EX_OK -- data successfully read.
722 **              EX_UNAVAILABLE -- no valid filename.
723 **              EX_TEMPFAIL -- temporary failure.
724 */
725
726 static char *sasl_info_name[] =
727 {
728         "user id",
729         "authentication id",
730         "password",
731         "realm",
732         "mechlist"
733 };
734 static int
735 readauth(filename, safe, sai, rpool)
736         char *filename;
737         bool safe;
738         SASL_AI_T *sai;
739         SM_RPOOL_T *rpool;
740 {
741         SM_FILE_T *f;
742         long sff;
743         pid_t pid;
744         int lc;
745         char *s;
746         char buf[MAXLINE];
747
748         if (filename == NULL || filename[0] == '\0')
749                 return EX_UNAVAILABLE;
750
751 #if !_FFR_ALLOW_SASLINFO
752         /*
753         **  make sure we don't use a program that is not
754         **  accessible to the user who specified a different authinfo file.
755         **  However, currently we don't pass this info (authinfo file
756         **  specified by user) around, so we just turn off program access.
757         */
758
759         if (filename[0] == '|')
760         {
761                 auto int fd;
762                 int i;
763                 char *p;
764                 char *argv[MAXPV + 1];
765
766                 i = 0;
767                 for (p = strtok(&filename[1], " \t"); p != NULL;
768                      p = strtok(NULL, " \t"))
769                 {
770                         if (i >= MAXPV)
771                                 break;
772                         argv[i++] = p;
773                 }
774                 argv[i] = NULL;
775                 pid = prog_open(argv, &fd, CurEnv);
776                 if (pid < 0)
777                         f = NULL;
778                 else
779                         f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
780                                        (void *) &fd, SM_IO_RDONLY, NULL);
781         }
782         else
783 #endif /* !_FFR_ALLOW_SASLINFO */
784         {
785                 pid = -1;
786                 sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
787                       |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
788                 if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
789                         sff |= SFF_NOGRFILES;
790                 if (DontLockReadFiles)
791                         sff |= SFF_NOLOCK;
792
793 #if _FFR_ALLOW_SASLINFO
794                 /*
795                 **  XXX: make sure we don't read or open files that are not
796                 **  accessible to the user who specified a different authinfo
797                 **  file.
798                 */
799
800                 sff |= SFF_MUSTOWN;
801 #else /* _FFR_ALLOW_SASLINFO */
802                 if (safe)
803                         sff |= SFF_OPENASROOT;
804 #endif /* _FFR_ALLOW_SASLINFO */
805
806                 f = safefopen(filename, O_RDONLY, 0, sff);
807         }
808         if (f == NULL)
809         {
810                 if (LogLevel > 5)
811                         sm_syslog(LOG_ERR, NOQID,
812                                   "AUTH=client, error: can't open %s: %s",
813                                   filename, sm_errstring(errno));
814                 return EX_TEMPFAIL;
815         }
816
817         lc = 0;
818         while (lc <= SASL_MECHLIST &&
819                 sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
820         {
821                 if (buf[0] != '#')
822                 {
823                         (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
824                         if ((s = strchr((*sai)[lc], '\n')) != NULL)
825                                 *s = '\0';
826                         lc++;
827                 }
828         }
829
830         (void) sm_io_close(f, SM_TIME_DEFAULT);
831         if (pid > 0)
832                 (void) waitfor(pid);
833         if (lc < SASL_PASSWORD)
834         {
835                 if (LogLevel > 8)
836                         sm_syslog(LOG_ERR, NOQID,
837                                   "AUTH=client, error: can't read %s from %s",
838                                   sasl_info_name[lc + 1], filename);
839                 return EX_TEMPFAIL;
840         }
841         return EX_OK;
842 }
843
844 /*
845 **  GETAUTH -- get authinfo from ruleset call
846 **
847 **      {server_name}, {server_addr} must be set
848 **
849 **      Parameters:
850 **              mci -- the mailer connection structure.
851 **              e -- the envelope (including the sender to specify).
852 **              sai -- pointer to authinfo (result).
853 **
854 **      Returns:
855 **              EX_OK -- ruleset was successfully called, data may not
856 **                      be available, sai must be checked.
857 **              EX_UNAVAILABLE -- ruleset unavailable (or failed).
858 **              EX_TEMPFAIL -- temporary failure (from ruleset).
859 **
860 **      Side Effects:
861 **              Fills in sai if successful.
862 */
863
864 static int
865 getauth(mci, e, sai)
866         MCI *mci;
867         ENVELOPE *e;
868         SASL_AI_T *sai;
869 {
870         int i, r, l, got, ret;
871         char **pvp;
872         char pvpbuf[PSBUFSIZE];
873
874         r = rscap("authinfo", macvalue(macid("{server_name}"), e),
875                    macvalue(macid("{server_addr}"), e), e,
876                    &pvp, pvpbuf, sizeof(pvpbuf));
877
878         if (r != EX_OK)
879                 return EX_UNAVAILABLE;
880
881         /* other than expected return value: ok (i.e., no auth) */
882         if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
883                 return EX_OK;
884         if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
885                 return EX_TEMPFAIL;
886
887         /*
888         **  parse the data, put it into sai
889         **  format: "TDstring" (including the '"' !)
890         **  where T is a tag: 'U', ...
891         **  D is a delimiter: ':' or '='
892         */
893
894         ret = EX_OK;    /* default return value */
895         i = 0;
896         got = 0;
897         while (i < SASL_ENTRIES)
898         {
899                 if (pvp[i + 1] == NULL)
900                         break;
901                 if (pvp[i + 1][0] != '"')
902                         break;
903                 switch (pvp[i + 1][1])
904                 {
905                   case 'U':
906                   case 'u':
907                         r = SASL_USER;
908                         break;
909                   case 'I':
910                   case 'i':
911                         r = SASL_AUTHID;
912                         break;
913                   case 'P':
914                   case 'p':
915                         r = SASL_PASSWORD;
916                         break;
917                   case 'R':
918                   case 'r':
919                         r = SASL_DEFREALM;
920                         break;
921                   case 'M':
922                   case 'm':
923                         r = SASL_MECHLIST;
924                         break;
925                   default:
926                         goto fail;
927                 }
928                 l = strlen(pvp[i + 1]);
929
930                 /* check syntax */
931                 if (l <= 3 || pvp[i + 1][l - 1] != '"')
932                         goto fail;
933
934                 /* remove closing quote */
935                 pvp[i + 1][l - 1] = '\0';
936
937                 /* remove "TD and " */
938                 l -= 4;
939                 (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
940                 if ((*sai)[r] == NULL)
941                         goto tempfail;
942                 if (pvp[i + 1][2] == ':')
943                 {
944                         /* ':text' (just copy) */
945                         (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
946                         got |= 1 << r;
947                 }
948                 else if (pvp[i + 1][2] == '=')
949                 {
950                         unsigned int len;
951
952                         /* '=base64' (decode) */
953 # if SASL >= 20000
954                         ret = sasl_decode64(pvp[i + 1] + 3,
955                                           (unsigned int) l, (*sai)[r],
956                                           (unsigned int) l + 1, &len);
957 # else /* SASL >= 20000 */
958                         ret = sasl_decode64(pvp[i + 1] + 3,
959                                           (unsigned int) l, (*sai)[r], &len);
960 # endif /* SASL >= 20000 */
961                         if (ret != SASL_OK)
962                                 goto fail;
963                         got |= 1 << r;
964                 }
965                 else
966                         goto fail;
967                 if (tTd(95, 5))
968                         sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
969                                   sasl_info_name[r], (*sai)[r]);
970                 ++i;
971         }
972
973         /* did we get the expected data? */
974         /* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
975         if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
976               bitset(SASL_PASSWORD_BIT, got)))
977                 goto fail;
978
979         /* no authid? copy uid */
980         if (!bitset(SASL_AUTHID_BIT, got))
981         {
982                 l = strlen((*sai)[SASL_USER]) + 1;
983                 (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
984                                                                l + 1);
985                 if ((*sai)[SASL_AUTHID] == NULL)
986                         goto tempfail;
987                 (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
988         }
989
990         /* no uid? copy authid */
991         if (!bitset(SASL_USER_BIT, got))
992         {
993                 l = strlen((*sai)[SASL_AUTHID]) + 1;
994                 (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
995                                                              l + 1);
996                 if ((*sai)[SASL_USER] == NULL)
997                         goto tempfail;
998                 (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
999         }
1000         return EX_OK;
1001
1002   tempfail:
1003         ret = EX_TEMPFAIL;
1004   fail:
1005         if (LogLevel > 8)
1006                 sm_syslog(LOG_WARNING, NOQID,
1007                           "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
1008                           macvalue(macid("{server_name}"), e),
1009                           macvalue(macid("{server_addr}"), e),
1010                           ret == EX_TEMPFAIL ? "temp" : "");
1011         for (i = 0; i <= SASL_MECHLIST; i++)
1012                 (*sai)[i] = NULL;       /* just clear; rpool */
1013         return ret;
1014 }
1015
1016 # if SASL >= 20000
1017 /*
1018 **  GETSIMPLE -- callback to get userid or authid
1019 **
1020 **      Parameters:
1021 **              context -- sai
1022 **              id -- what to do
1023 **              result -- (pointer to) result
1024 **              len -- (pointer to) length of result
1025 **
1026 **      Returns:
1027 **              OK/failure values
1028 */
1029
1030 static int
1031 getsimple(context, id, result, len)
1032         void *context;
1033         int id;
1034         const char **result;
1035         unsigned *len;
1036 {
1037         SASL_AI_T *sai;
1038
1039         if (result == NULL || context == NULL)
1040                 return SASL_BADPARAM;
1041         sai = (SASL_AI_T *) context;
1042
1043         switch (id)
1044         {
1045           case SASL_CB_USER:
1046                 *result = (*sai)[SASL_USER];
1047                 if (tTd(95, 5))
1048                         sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1049                                   *result);
1050                 if (len != NULL)
1051                         *len = *result != NULL ? strlen(*result) : 0;
1052                 break;
1053
1054           case SASL_CB_AUTHNAME:
1055                 *result = (*sai)[SASL_AUTHID];
1056                 if (tTd(95, 5))
1057                         sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1058                                   *result);
1059                 if (len != NULL)
1060                         *len = *result != NULL ? strlen(*result) : 0;
1061                 break;
1062
1063           case SASL_CB_LANGUAGE:
1064                 *result = NULL;
1065                 if (len != NULL)
1066                         *len = 0;
1067                 break;
1068
1069           default:
1070                 return SASL_BADPARAM;
1071         }
1072         return SASL_OK;
1073 }
1074 /*
1075 **  GETSECRET -- callback to get password
1076 **
1077 **      Parameters:
1078 **              conn -- connection information
1079 **              context -- sai
1080 **              id -- what to do
1081 **              psecret -- (pointer to) result
1082 **
1083 **      Returns:
1084 **              OK/failure values
1085 */
1086
1087 static int
1088 getsecret(conn, context, id, psecret)
1089         sasl_conn_t *conn;
1090         SM_UNUSED(void *context);
1091         int id;
1092         sasl_secret_t **psecret;
1093 {
1094         int len;
1095         char *authpass;
1096         MCI *mci;
1097
1098         if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1099                 return SASL_BADPARAM;
1100
1101         mci = (MCI *) context;
1102         authpass = mci->mci_sai[SASL_PASSWORD];
1103         len = strlen(authpass);
1104
1105         /*
1106         **  use an rpool because we are responsible for free()ing the secret,
1107         **  but we can't free() it until after the auth completes
1108         */
1109
1110         *psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
1111                                                      sizeof(sasl_secret_t) +
1112                                                      len + 1);
1113         if (*psecret == NULL)
1114                 return SASL_FAIL;
1115         (void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
1116         (*psecret)->len = (unsigned long) len;
1117         return SASL_OK;
1118 }
1119 # else /* SASL >= 20000 */
1120 /*
1121 **  GETSIMPLE -- callback to get userid or authid
1122 **
1123 **      Parameters:
1124 **              context -- sai
1125 **              id -- what to do
1126 **              result -- (pointer to) result
1127 **              len -- (pointer to) length of result
1128 **
1129 **      Returns:
1130 **              OK/failure values
1131 */
1132
1133 static int
1134 getsimple(context, id, result, len)
1135         void *context;
1136         int id;
1137         const char **result;
1138         unsigned *len;
1139 {
1140         char *h, *s;
1141 # if SASL > 10509
1142         bool addrealm;
1143 # endif
1144         size_t l;
1145         SASL_AI_T *sai;
1146         char *authid = NULL;
1147
1148         if (result == NULL || context == NULL)
1149                 return SASL_BADPARAM;
1150         sai = (SASL_AI_T *) context;
1151
1152         /*
1153         **  Unfortunately it is not clear whether this routine should
1154         **  return a copy of a string or just a pointer to a string.
1155         **  The Cyrus-SASL plugins treat these return values differently, e.g.,
1156         **  plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1157         **  The best solution to this problem is to fix Cyrus-SASL, but it
1158         **  seems there is nobody who creates patches... Hello CMU!?
1159         **  The second best solution is to have flags that tell this routine
1160         **  whether to return an malloc()ed copy.
1161         **  The next best solution is to always return an malloc()ed copy,
1162         **  and suffer from some memory leak, which is ugly for persistent
1163         **  queue runners.
1164         **  For now we go with the last solution...
1165         **  We can't use rpools (which would avoid this particular problem)
1166         **  as explained in sasl.c.
1167         */
1168
1169         switch (id)
1170         {
1171           case SASL_CB_USER:
1172                 l = strlen((*sai)[SASL_USER]) + 1;
1173                 s = sm_sasl_malloc(l);
1174                 if (s == NULL)
1175                 {
1176                         if (len != NULL)
1177                                 *len = 0;
1178                         *result = NULL;
1179                         return SASL_NOMEM;
1180                 }
1181                 (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
1182                 *result = s;
1183                 if (tTd(95, 5))
1184                         sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1185                                   *result);
1186                 if (len != NULL)
1187                         *len = *result != NULL ? strlen(*result) : 0;
1188                 break;
1189
1190           case SASL_CB_AUTHNAME:
1191                 h = (*sai)[SASL_AUTHID];
1192 # if SASL > 10509
1193                 /* XXX maybe other mechanisms too?! */
1194                 addrealm = (*sai)[SASL_MECH] != NULL &&
1195                            sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
1196
1197                 /*
1198                 **  Add realm to authentication id unless authid contains
1199                 **  '@' (i.e., a realm) or the default realm is empty.
1200                 */
1201
1202                 if (addrealm && h != NULL && strchr(h, '@') == NULL)
1203                 {
1204                         /* has this been done before? */
1205                         if ((*sai)[SASL_ID_REALM] == NULL)
1206                         {
1207                                 char *realm;
1208
1209                                 realm = (*sai)[SASL_DEFREALM];
1210
1211                                 /* do not add an empty realm */
1212                                 if (*realm == '\0')
1213                                 {
1214                                         authid = h;
1215                                         (*sai)[SASL_ID_REALM] = NULL;
1216                                 }
1217                                 else
1218                                 {
1219                                         l = strlen(h) + strlen(realm) + 2;
1220
1221                                         /* should use rpool, but from where? */
1222                                         authid = sm_sasl_malloc(l);
1223                                         if (authid != NULL)
1224                                         {
1225                                                 (void) sm_snprintf(authid, l,
1226                                                                   "%s@%s",
1227                                                                    h, realm);
1228                                                 (*sai)[SASL_ID_REALM] = authid;
1229                                         }
1230                                         else
1231                                         {
1232                                                 authid = h;
1233                                                 (*sai)[SASL_ID_REALM] = NULL;
1234                                         }
1235                                 }
1236                         }
1237                         else
1238                                 authid = (*sai)[SASL_ID_REALM];
1239                 }
1240                 else
1241 # endif /* SASL > 10509 */
1242                         authid = h;
1243                 l = strlen(authid) + 1;
1244                 s = sm_sasl_malloc(l);
1245                 if (s == NULL)
1246                 {
1247                         if (len != NULL)
1248                                 *len = 0;
1249                         *result = NULL;
1250                         return SASL_NOMEM;
1251                 }
1252                 (void) sm_strlcpy(s, authid, l);
1253                 *result = s;
1254                 if (tTd(95, 5))
1255                         sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1256                                   *result);
1257                 if (len != NULL)
1258                         *len = authid ? strlen(authid) : 0;
1259                 break;
1260
1261           case SASL_CB_LANGUAGE:
1262                 *result = NULL;
1263                 if (len != NULL)
1264                         *len = 0;
1265                 break;
1266
1267           default:
1268                 return SASL_BADPARAM;
1269         }
1270         return SASL_OK;
1271 }
1272 /*
1273 **  GETSECRET -- callback to get password
1274 **
1275 **      Parameters:
1276 **              conn -- connection information
1277 **              context -- sai
1278 **              id -- what to do
1279 **              psecret -- (pointer to) result
1280 **
1281 **      Returns:
1282 **              OK/failure values
1283 */
1284
1285 static int
1286 getsecret(conn, context, id, psecret)
1287         sasl_conn_t *conn;
1288         SM_UNUSED(void *context);
1289         int id;
1290         sasl_secret_t **psecret;
1291 {
1292         int len;
1293         char *authpass;
1294         SASL_AI_T *sai;
1295
1296         if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1297                 return SASL_BADPARAM;
1298
1299         sai = (SASL_AI_T *) context;
1300         authpass = (*sai)[SASL_PASSWORD];
1301         len = strlen(authpass);
1302         *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
1303                                                     len + 1);
1304         if (*psecret == NULL)
1305                 return SASL_FAIL;
1306         (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
1307         (*psecret)->len = (unsigned long) len;
1308         return SASL_OK;
1309 }
1310 # endif /* SASL >= 20000 */
1311
1312 /*
1313 **  SAFESASLFILE -- callback for sasl: is file safe?
1314 **
1315 **      Parameters:
1316 **              context -- pointer to context between invocations (unused)
1317 **              file -- name of file to check
1318 **              type -- type of file to check
1319 **
1320 **      Returns:
1321 **              SASL_OK -- file can be used
1322 **              SASL_CONTINUE -- don't use file
1323 **              SASL_FAIL -- failure (not used here)
1324 **
1325 */
1326
1327 int
1328 #if SASL > 10515
1329 safesaslfile(context, file, type)
1330 #else
1331 safesaslfile(context, file)
1332 #endif
1333         void *context;
1334 # if SASL >= 20000
1335         const char *file;
1336 # else
1337         char *file;
1338 # endif
1339 #if SASL > 10515
1340 # if SASL >= 20000
1341         sasl_verify_type_t type;
1342 # else
1343         int type;
1344 # endif
1345 #endif
1346 {
1347         long sff;
1348         int r;
1349 #if SASL <= 10515
1350         size_t len;
1351 #endif
1352         char *p;
1353
1354         if (file == NULL || *file == '\0')
1355                 return SASL_OK;
1356         if (tTd(95, 16))
1357                 sm_dprintf("safesaslfile=%s\n", file);
1358         sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
1359 #if SASL <= 10515
1360         if ((p = strrchr(file, '/')) == NULL)
1361                 p = file;
1362         else
1363                 ++p;
1364
1365         /* everything beside libs and .conf files must not be readable */
1366         len = strlen(p);
1367         if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
1368             (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
1369         {
1370                 if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1371                         sff |= SFF_NORFILES;
1372                 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1373                         sff |= SFF_NOGWFILES;
1374         }
1375 #else /* SASL <= 10515 */
1376         /* files containing passwords should be not readable */
1377         if (type == SASL_VRFY_PASSWD)
1378         {
1379                 if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1380                         sff |= SFF_NOWRFILES;
1381                 else
1382                         sff |= SFF_NORFILES;
1383                 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1384                         sff |= SFF_NOGWFILES;
1385         }
1386 #endif /* SASL <= 10515 */
1387
1388         p = (char *) file;
1389         if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
1390                           S_IRUSR, NULL)) == 0)
1391                 return SASL_OK;
1392         if (LogLevel > (r != ENOENT ? 8 : 10))
1393                 sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
1394                           p, sm_errstring(r));
1395         return SASL_CONTINUE;
1396 }
1397
1398 /*
1399 **  SASLGETREALM -- return the realm for SASL
1400 **
1401 **      return the realm for the client
1402 **
1403 **      Parameters:
1404 **              context -- context shared between invocations
1405 **              availrealms -- list of available realms
1406 **                      {realm, realm, ...}
1407 **              result -- pointer to result
1408 **
1409 **      Returns:
1410 **              failure/success
1411 */
1412
1413 static int
1414 saslgetrealm(context, id, availrealms, result)
1415         void *context;
1416         int id;
1417         const char **availrealms;
1418         const char **result;
1419 {
1420         char *r;
1421         SASL_AI_T *sai;
1422
1423         sai = (SASL_AI_T *) context;
1424         if (sai == NULL)
1425                 return SASL_FAIL;
1426         r = (*sai)[SASL_DEFREALM];
1427
1428         if (LogLevel > 12)
1429                 sm_syslog(LOG_INFO, NOQID,
1430                           "AUTH=client, realm=%s, available realms=%s",
1431                           r == NULL ? "<No Realm>" : r,
1432                           (availrealms == NULL || *availrealms == NULL)
1433                                 ? "<No Realms>" : *availrealms);
1434
1435         /* check whether context is in list */
1436         if (availrealms != NULL && *availrealms != NULL)
1437         {
1438                 if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
1439                     NULL)
1440                 {
1441                         if (LogLevel > 8)
1442                                 sm_syslog(LOG_ERR, NOQID,
1443                                           "AUTH=client, realm=%s not in list=%s",
1444                                           r, *availrealms);
1445                         return SASL_FAIL;
1446                 }
1447         }
1448         *result = r;
1449         return SASL_OK;
1450 }
1451 /*
1452 **  ITEMINLIST -- does item appear in list?
1453 **
1454 **      Check whether item appears in list (which must be separated by a
1455 **      character in delim) as a "word", i.e. it must appear at the begin
1456 **      of the list or after a space, and it must end with a space or the
1457 **      end of the list.
1458 **
1459 **      Parameters:
1460 **              item -- item to search.
1461 **              list -- list of items.
1462 **              delim -- list of delimiters.
1463 **
1464 **      Returns:
1465 **              pointer to occurrence (NULL if not found).
1466 */
1467
1468 char *
1469 iteminlist(item, list, delim)
1470         char *item;
1471         char *list;
1472         char *delim;
1473 {
1474         char *s;
1475         int len;
1476
1477         if (list == NULL || *list == '\0')
1478                 return NULL;
1479         if (item == NULL || *item == '\0')
1480                 return NULL;
1481         s = list;
1482         len = strlen(item);
1483         while (s != NULL && *s != '\0')
1484         {
1485                 if (sm_strncasecmp(s, item, len) == 0 &&
1486                     (s[len] == '\0' || strchr(delim, s[len]) != NULL))
1487                         return s;
1488                 s = strpbrk(s, delim);
1489                 if (s != NULL)
1490                         while (*++s == ' ')
1491                                 continue;
1492         }
1493         return NULL;
1494 }
1495 /*
1496 **  REMOVEMECH -- remove item [rem] from list [list]
1497 **
1498 **      Parameters:
1499 **              rem -- item to remove
1500 **              list -- list of items
1501 **              rpool -- resource pool from which result is allocated.
1502 **
1503 **      Returns:
1504 **              pointer to new list (NULL in case of error).
1505 */
1506
1507 static char *
1508 removemech(rem, list, rpool)
1509         char *rem;
1510         char *list;
1511         SM_RPOOL_T *rpool;
1512 {
1513         char *ret;
1514         char *needle;
1515         int len;
1516
1517         if (list == NULL)
1518                 return NULL;
1519         if (rem == NULL || *rem == '\0')
1520         {
1521                 /* take out what? */
1522                 return NULL;
1523         }
1524
1525         /* find the item in the list */
1526         if ((needle = iteminlist(rem, list, " ")) == NULL)
1527         {
1528                 /* not in there: return original */
1529                 return list;
1530         }
1531
1532         /* length of string without rem */
1533         len = strlen(list) - strlen(rem);
1534         if (len <= 0)
1535         {
1536                 ret = (char *) sm_rpool_malloc_x(rpool, 1);
1537                 *ret = '\0';
1538                 return ret;
1539         }
1540         ret = (char *) sm_rpool_malloc_x(rpool, len);
1541         memset(ret, '\0', len);
1542
1543         /* copy from start to removed item */
1544         memcpy(ret, list, needle - list);
1545
1546         /* length of rest of string past removed item */
1547         len = strlen(needle) - strlen(rem) - 1;
1548         if (len > 0)
1549         {
1550                 /* not last item -- copy into string */
1551                 memcpy(ret + (needle - list),
1552                        list + (needle - list) + strlen(rem) + 1,
1553                        len);
1554         }
1555         else
1556                 ret[(needle - list) - 1] = '\0';
1557         return ret;
1558 }
1559 /*
1560 **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1561 **
1562 **      Parameters:
1563 **              m -- the mailer.
1564 **              mci -- the mailer connection structure.
1565 **              e -- the envelope (including the sender to specify).
1566 **              sai - sasl authinfo
1567 **
1568 **      Returns:
1569 **              EX_OK -- authentication was successful.
1570 **              EX_NOPERM -- authentication failed.
1571 **              EX_IOERR -- authentication dialogue failed (I/O problem?).
1572 **              EX_TEMPFAIL -- temporary failure.
1573 **
1574 */
1575
1576 static int
1577 attemptauth(m, mci, e, sai)
1578         MAILER *m;
1579         MCI *mci;
1580         ENVELOPE *e;
1581         SASL_AI_T *sai;
1582 {
1583         int saslresult, smtpresult;
1584 # if SASL >= 20000
1585         sasl_ssf_t ssf;
1586         const char *auth_id;
1587         const char *out;
1588 # else /* SASL >= 20000 */
1589         sasl_external_properties_t ssf;
1590         char *out;
1591 # endif /* SASL >= 20000 */
1592         unsigned int outlen;
1593         sasl_interact_t *client_interact = NULL;
1594         char *mechusing;
1595         sasl_security_properties_t ssp;
1596
1597         /* MUST NOT be a multiple of 4: bug in some sasl_encode64() versions */
1598         char in64[MAXOUTLEN + 1];
1599 #if NETINET || (NETINET6 && SASL >= 20000)
1600         extern SOCKADDR CurHostAddr;
1601 #endif
1602
1603         /* no mechanism selected (yet) */
1604         (*sai)[SASL_MECH] = NULL;
1605
1606         /* dispose old connection */
1607         if (mci->mci_conn != NULL)
1608                 sasl_dispose(&(mci->mci_conn));
1609
1610         /* make a new client sasl connection */
1611 # if SASL >= 20000
1612         /*
1613         **  We provide the callbacks again because global callbacks in
1614         **  sasl_client_init() are ignored if SASL has been initialized
1615         **  before, for example, by a library such as libnss-ldap.
1616         */
1617
1618         saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1619                                                                  : "smtp",
1620                                      CurHostName, NULL, NULL, callbacks, 0,
1621                                      &mci->mci_conn);
1622 # else /* SASL >= 20000 */
1623         saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1624                                                                  : "smtp",
1625                                      CurHostName, NULL, 0, &mci->mci_conn);
1626 # endif /* SASL >= 20000 */
1627         if (saslresult != SASL_OK)
1628                 return EX_TEMPFAIL;
1629
1630         /* set properties */
1631         (void) memset(&ssp, '\0', sizeof(ssp));
1632
1633         /* XXX should these be options settable via .cf ? */
1634         ssp.max_ssf = MaxSLBits;
1635         ssp.maxbufsize = MAXOUTLEN;
1636 #  if 0
1637         ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1638 #  endif
1639         saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1640         if (saslresult != SASL_OK)
1641                 return EX_TEMPFAIL;
1642
1643 # if SASL >= 20000
1644         /* external security strength factor, authentication id */
1645         ssf = 0;
1646         auth_id = NULL;
1647 #  if STARTTLS
1648         out = macvalue(macid("{cert_subject}"), e);
1649         if (out != NULL && *out != '\0')
1650                 auth_id = out;
1651         out = macvalue(macid("{cipher_bits}"), e);
1652         if (out != NULL && *out != '\0')
1653                 ssf = atoi(out);
1654 #  endif /* STARTTLS */
1655         saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1656         if (saslresult != SASL_OK)
1657                 return EX_TEMPFAIL;
1658         saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
1659         if (saslresult != SASL_OK)
1660                 return EX_TEMPFAIL;
1661
1662 #  if NETINET || NETINET6
1663         /* set local/remote ipv4 addresses */
1664         if (mci->mci_out != NULL && (
1665 #   if NETINET6
1666                 CurHostAddr.sa.sa_family == AF_INET6 ||
1667 #   endif
1668                 CurHostAddr.sa.sa_family == AF_INET))
1669         {
1670                 SOCKADDR_LEN_T addrsize;
1671                 SOCKADDR saddr_l;
1672                 char localip[60], remoteip[60];
1673
1674                 switch (CurHostAddr.sa.sa_family)
1675                 {
1676                   case AF_INET:
1677                         addrsize = sizeof(struct sockaddr_in);
1678                         break;
1679 #   if NETINET6
1680                   case AF_INET6:
1681                         addrsize = sizeof(struct sockaddr_in6);
1682                         break;
1683 #   endif
1684                   default:
1685                         break;
1686                 }
1687                 if (iptostring(&CurHostAddr, addrsize,
1688                                remoteip, sizeof(remoteip)))
1689                 {
1690                         if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
1691                                          remoteip) != SASL_OK)
1692                                 return EX_TEMPFAIL;
1693                 }
1694                 addrsize = sizeof(saddr_l);
1695                 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1696                                               NULL),
1697                                 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1698                 {
1699                         if (iptostring(&saddr_l, addrsize,
1700                                        localip, sizeof(localip)))
1701                         {
1702                                 if (sasl_setprop(mci->mci_conn,
1703                                                  SASL_IPLOCALPORT,
1704                                                  localip) != SASL_OK)
1705                                         return EX_TEMPFAIL;
1706                         }
1707                 }
1708         }
1709 #  endif /* NETINET || NETINET6 */
1710
1711         /* start client side of sasl */
1712         saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1713                                        &client_interact,
1714                                        &out, &outlen,
1715                                        (const char **) &mechusing);
1716 # else /* SASL >= 20000 */
1717         /* external security strength factor, authentication id */
1718         ssf.ssf = 0;
1719         ssf.auth_id = NULL;
1720 #  if STARTTLS
1721         out = macvalue(macid("{cert_subject}"), e);
1722         if (out != NULL && *out != '\0')
1723                 ssf.auth_id = out;
1724         out = macvalue(macid("{cipher_bits}"), e);
1725         if (out != NULL && *out != '\0')
1726                 ssf.ssf = atoi(out);
1727 #  endif /* STARTTLS */
1728         saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1729         if (saslresult != SASL_OK)
1730                 return EX_TEMPFAIL;
1731
1732 #  if NETINET
1733         /* set local/remote ipv4 addresses */
1734         if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1735         {
1736                 SOCKADDR_LEN_T addrsize;
1737                 struct sockaddr_in saddr_l;
1738
1739                 if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1740                                  (struct sockaddr_in *) &CurHostAddr)
1741                     != SASL_OK)
1742                         return EX_TEMPFAIL;
1743                 addrsize = sizeof(struct sockaddr_in);
1744                 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1745                                               NULL),
1746                                 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1747                 {
1748                         if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1749                                          &saddr_l) != SASL_OK)
1750                                 return EX_TEMPFAIL;
1751                 }
1752         }
1753 #  endif /* NETINET */
1754
1755         /* start client side of sasl */
1756         saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1757                                        NULL, &client_interact,
1758                                        &out, &outlen,
1759                                        (const char **) &mechusing);
1760 # endif /* SASL >= 20000 */
1761
1762         if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1763         {
1764                 if (saslresult == SASL_NOMECH && LogLevel > 8)
1765                 {
1766                         sm_syslog(LOG_NOTICE, e->e_id,
1767                                   "AUTH=client, available mechanisms=%s do not fulfill requirements", mci->mci_saslcap);
1768                 }
1769                 return EX_TEMPFAIL;
1770         }
1771
1772         /* just point current mechanism to the data in the sasl library */
1773         (*sai)[SASL_MECH] = mechusing;
1774
1775         /* send the info across the wire */
1776         if (out == NULL
1777                 /* login and digest-md5 up to 1.5.28 set out="" */
1778             || (outlen == 0 &&
1779                 (sm_strcasecmp(mechusing, "LOGIN") == 0 ||
1780                  sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
1781            )
1782         {
1783                 /* no initial response */
1784                 smtpmessage("AUTH %s", m, mci, mechusing);
1785         }
1786         else if (outlen == 0)
1787         {
1788                 /*
1789                 **  zero-length initial response, per RFC 2554 4.:
1790                 **  "Unlike a zero-length client answer to a 334 reply, a zero-
1791                 **  length initial response is sent as a single equals sign"
1792                 */
1793
1794                 smtpmessage("AUTH %s =", m, mci, mechusing);
1795         }
1796         else
1797         {
1798                 saslresult = sasl_encode64(out, outlen, in64, sizeof(in64),
1799                                            NULL);
1800                 if (saslresult != SASL_OK) /* internal error */
1801                 {
1802                         if (LogLevel > 8)
1803                                 sm_syslog(LOG_ERR, e->e_id,
1804                                         "encode64 for AUTH failed");
1805                         return EX_TEMPFAIL;
1806                 }
1807                 smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1808         }
1809 # if SASL < 20000
1810         sm_sasl_free(out); /* XXX only if no rpool is used */
1811 # endif
1812
1813         /* get the reply */
1814         smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
1815                         XS_AUTH);
1816
1817         for (;;)
1818         {
1819                 /* check return code from server */
1820                 if (smtpresult == 235)
1821                 {
1822                         macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
1823                                   mechusing);
1824                         return EX_OK;
1825                 }
1826                 if (smtpresult == -1)
1827                         return EX_IOERR;
1828                 if (REPLYTYPE(smtpresult) == 5)
1829                         return EX_NOPERM;       /* ugly, but ... */
1830                 if (REPLYTYPE(smtpresult) != 3)
1831                 {
1832                         /* should we fail deliberately, see RFC 2554 4. ? */
1833                         /* smtpmessage("*", m, mci); */
1834                         return EX_TEMPFAIL;
1835                 }
1836
1837                 saslresult = sasl_client_step(mci->mci_conn,
1838                                               mci->mci_sasl_string,
1839                                               mci->mci_sasl_string_len,
1840                                               &client_interact,
1841                                               &out, &outlen);
1842
1843                 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1844                 {
1845                         if (tTd(95, 5))
1846                                 sm_dprintf("AUTH FAIL=%s (%d)\n",
1847                                         sasl_errstring(saslresult, NULL, NULL),
1848                                         saslresult);
1849
1850                         /* fail deliberately, see RFC 2554 4. */
1851                         smtpmessage("*", m, mci);
1852
1853                         /*
1854                         **  but we should only fail for this authentication
1855                         **  mechanism; how to do that?
1856                         */
1857
1858                         smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1859                                            getsasldata, NULL, XS_AUTH);
1860                         return EX_NOPERM;
1861                 }
1862
1863                 if (outlen > 0)
1864                 {
1865                         saslresult = sasl_encode64(out, outlen, in64,
1866                                                    sizeof(in64), NULL);
1867                         if (saslresult != SASL_OK)
1868                         {
1869                                 /* give an error reply to the other side! */
1870                                 smtpmessage("*", m, mci);
1871                                 return EX_TEMPFAIL;
1872                         }
1873                 }
1874                 else
1875                         in64[0] = '\0';
1876 # if SASL < 20000
1877                 sm_sasl_free(out); /* XXX only if no rpool is used */
1878 # endif
1879                 smtpmessage("%s", m, mci, in64);
1880                 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1881                                    getsasldata, NULL, XS_AUTH);
1882         }
1883         /* NOTREACHED */
1884 }
1885 /*
1886 **  SMTPAUTH -- try to AUTHenticate
1887 **
1888 **      This will try mechanisms in the order the sasl library decided until:
1889 **      - there are no more mechanisms
1890 **      - a mechanism succeeds
1891 **      - the sasl library fails initializing
1892 **
1893 **      Parameters:
1894 **              m -- the mailer.
1895 **              mci -- the mailer connection info.
1896 **              e -- the envelope.
1897 **
1898 **      Returns:
1899 **              EX_OK -- authentication was successful
1900 **              EX_UNAVAILABLE -- authentication not possible, e.g.,
1901 **                      no data available.
1902 **              EX_NOPERM -- authentication failed.
1903 **              EX_TEMPFAIL -- temporary failure.
1904 **
1905 **      Notice: AuthInfo is used for all connections, hence we must
1906 **              return EX_TEMPFAIL only if we really want to retry, i.e.,
1907 **              iff getauth() tempfailed or getauth() was used and
1908 **              authentication tempfailed.
1909 */
1910
1911 int
1912 smtpauth(m, mci, e)
1913         MAILER *m;
1914         MCI *mci;
1915         ENVELOPE *e;
1916 {
1917         int result;
1918         int i;
1919         bool usedgetauth;
1920
1921         mci->mci_sasl_auth = false;
1922         for (i = 0; i < SASL_MECH ; i++)
1923                 mci->mci_sai[i] = NULL;
1924
1925         result = getauth(mci, e, &(mci->mci_sai));
1926         if (result == EX_TEMPFAIL)
1927                 return result;
1928         usedgetauth = true;
1929
1930         /* no data available: don't try to authenticate */
1931         if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
1932                 return result;
1933         if (result != EX_OK)
1934         {
1935                 if (SASLInfo == NULL)
1936                         return EX_UNAVAILABLE;
1937
1938                 /* read authinfo from file */
1939                 result = readauth(SASLInfo, true, &(mci->mci_sai),
1940                                   mci->mci_rpool);
1941                 if (result != EX_OK)
1942                         return result;
1943                 usedgetauth = false;
1944         }
1945
1946         /* check whether sufficient data is available */
1947         if (mci->mci_sai[SASL_PASSWORD] == NULL ||
1948             *(mci->mci_sai)[SASL_PASSWORD] == '\0')
1949                 return EX_UNAVAILABLE;
1950         if ((mci->mci_sai[SASL_AUTHID] == NULL ||
1951              *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
1952             (mci->mci_sai[SASL_USER] == NULL ||
1953              *(mci->mci_sai)[SASL_USER] == '\0'))
1954                 return EX_UNAVAILABLE;
1955
1956         /* set the context for the callback function to sai */
1957 # if SASL >= 20000
1958         callbacks[CB_PASS_IDX].context = (void *) mci;
1959 # else
1960         callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
1961 # endif
1962         callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
1963         callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
1964         callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
1965 #if 0
1966         callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
1967 #endif
1968
1969         /* set default value for realm */
1970         if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
1971                 (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
1972                                                         macvalue('j', CurEnv));
1973
1974         /* set default value for list of mechanism to use */
1975         if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
1976             *(mci->mci_sai)[SASL_MECHLIST] == '\0')
1977                 (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
1978
1979         /* create list of mechanisms to try */
1980         mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
1981                                      mci->mci_saslcap, mci->mci_rpool);
1982
1983         /* initialize sasl client library */
1984         result = init_sasl_client();
1985         if (result != SASL_OK)
1986                 return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
1987         do
1988         {
1989                 result = attemptauth(m, mci, e, &(mci->mci_sai));
1990                 if (result == EX_OK)
1991                         mci->mci_sasl_auth = true;
1992                 else if (result == EX_TEMPFAIL || result == EX_NOPERM)
1993                 {
1994                         mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
1995                                                       mci->mci_saslcap,
1996                                                       mci->mci_rpool);
1997                         if (mci->mci_saslcap == NULL ||
1998                             *(mci->mci_saslcap) == '\0')
1999                                 return usedgetauth ? result
2000                                                    : EX_UNAVAILABLE;
2001                 }
2002                 else
2003                         return result;
2004         } while (result != EX_OK);
2005         return result;
2006 }
2007 #endif /* SASL */
2008
2009 /*
2010 **  SMTPMAILFROM -- send MAIL command
2011 **
2012 **      Parameters:
2013 **              m -- the mailer.
2014 **              mci -- the mailer connection structure.
2015 **              e -- the envelope (including the sender to specify).
2016 */
2017
2018 int
2019 smtpmailfrom(m, mci, e)
2020         MAILER *m;
2021         MCI *mci;
2022         ENVELOPE *e;
2023 {
2024         int r;
2025         char *bufp;
2026         char *bodytype;
2027         char *enhsc;
2028         char buf[MAXNAME + 1];
2029         char optbuf[MAXLINE];
2030
2031         if (tTd(18, 2))
2032                 sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
2033         enhsc = NULL;
2034
2035         /*
2036         **  Check if connection is gone, if so
2037         **  it's a tempfail and we use mci_errno
2038         **  for the reason.
2039         */
2040
2041         if (mci->mci_state == MCIS_CLOSED)
2042         {
2043                 errno = mci->mci_errno;
2044                 return EX_TEMPFAIL;
2045         }
2046
2047 #if _FFR_EAI
2048         /*
2049         **  Abort right away if the message needs SMTPUTF8 and the
2050         **  server does not advertise SMTPUTF8.
2051         */
2052
2053         if (e->e_smtputf8 && !bitset(MCIF_EAI, mci->mci_flags)) {
2054                 usrerrenh("5.6.7", "%s does not support SMTPUTF8", CurHostName);
2055                 mci_setstat(mci, EX_NOTSTICKY, "5.6.7", NULL);
2056                 return EX_DATAERR;
2057         }
2058 #endif /* _FFR_EAI */
2059
2060         /* set up appropriate options to include */
2061         if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
2062         {
2063                 (void) sm_snprintf(optbuf, sizeof(optbuf), " SIZE=%ld",
2064                         e->e_msgsize);
2065                 bufp = &optbuf[strlen(optbuf)];
2066         }
2067         else
2068         {
2069                 optbuf[0] = '\0';
2070                 bufp = optbuf;
2071         }
2072
2073 #if _FFR_EAI
2074         if (e->e_smtputf8) {
2075                 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2076                                  " SMTPUTF8");
2077                 bufp += strlen(bufp);
2078         }
2079 #endif /* _FFR_EAI */
2080
2081         bodytype = e->e_bodytype;
2082         if (bitset(MCIF_8BITMIME, mci->mci_flags))
2083         {
2084                 if (bodytype == NULL &&
2085                     bitset(MM_MIME8BIT, MimeMode) &&
2086                     bitset(EF_HAS8BIT, e->e_flags) &&
2087                     !bitset(EF_DONT_MIME, e->e_flags) &&
2088                     !bitnset(M_8BITS, m->m_flags))
2089                         bodytype = "8BITMIME";
2090                 if (bodytype != NULL &&
2091                     SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2092                 {
2093                         (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2094                                  " BODY=%s", bodytype);
2095                         bufp += strlen(bufp);
2096                 }
2097         }
2098         else if (bitnset(M_8BITS, m->m_flags) ||
2099                  !bitset(EF_HAS8BIT, e->e_flags) ||
2100                  bitset(MCIF_8BITOK, mci->mci_flags))
2101         {
2102                 /* EMPTY */
2103                 /* just pass it through */
2104         }
2105 #if MIME8TO7
2106         else if (bitset(MM_CVTMIME, MimeMode) &&
2107                  !bitset(EF_DONT_MIME, e->e_flags) &&
2108                  (!bitset(MM_PASS8BIT, MimeMode) ||
2109                   bitset(EF_IS_MIME, e->e_flags)))
2110         {
2111                 /* must convert from 8bit MIME format to 7bit encoded */
2112                 mci->mci_flags |= MCIF_CVT8TO7;
2113         }
2114 #endif /* MIME8TO7 */
2115         else if (!bitset(MM_PASS8BIT, MimeMode))
2116         {
2117                 /* cannot just send a 8-bit version */
2118                 extern char MsgBuf[];
2119
2120                 usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2121                 mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2122                 return EX_DATAERR;
2123         }
2124
2125         if (bitset(MCIF_DSN, mci->mci_flags))
2126         {
2127                 if (e->e_envid != NULL &&
2128                     SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2129                 {
2130                         (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2131                                  " ENVID=%s", e->e_envid);
2132                         bufp += strlen(bufp);
2133                 }
2134
2135                 /* RET= parameter */
2136                 if (bitset(EF_RET_PARAM, e->e_flags) &&
2137                     SPACELEFT(optbuf, bufp) > 9)
2138                 {
2139                         (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2140                                  " RET=%s",
2141                                  bitset(EF_NO_BODY_RETN, e->e_flags) ?
2142                                         "HDRS" : "FULL");
2143                         bufp += strlen(bufp);
2144                 }
2145         }
2146
2147         if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
2148             SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
2149 #if SASL
2150              && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
2151 #endif
2152             )
2153         {
2154                 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2155                          " AUTH=%s", e->e_auth_param);
2156                 bufp += strlen(bufp);
2157         }
2158
2159         /*
2160         **  17 is the max length required, we could use log() to compute
2161         **  the exact length (and check IS_DLVR_TRACE())
2162         */
2163
2164         if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
2165             IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
2166         {
2167                 long dby;
2168
2169                 /*
2170                 **  Avoid problems with delays (for R) since the check
2171                 **  in deliver() whether min-deliver-time is sufficient.
2172                 **  Alternatively we could pass the computed time to this
2173                 **  function.
2174                 */
2175
2176                 dby = e->e_deliver_by - (curtime() - e->e_ctime);
2177                 if (dby <= 0 && IS_DLVR_RETURN(e))
2178                         dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
2179                 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2180                         " BY=%ld;%c%s",
2181                         dby,
2182                         IS_DLVR_RETURN(e) ? 'R' : 'N',
2183                         IS_DLVR_TRACE(e) ? "T" : "");
2184                 bufp += strlen(bufp);
2185         }
2186
2187         /*
2188         **  Send the MAIL command.
2189         **      Designates the sender.
2190         */
2191
2192         mci->mci_state = MCIS_MAIL;
2193
2194         if (bitset(EF_RESPONSE, e->e_flags) &&
2195             !bitnset(M_NO_NULL_FROM, m->m_flags))
2196                 buf[0] = '\0';
2197         else
2198                 expand("\201g", buf, sizeof(buf), e);
2199         if (buf[0] == '<')
2200         {
2201                 /* strip off <angle brackets> (put back on below) */
2202                 bufp = &buf[strlen(buf) - 1];
2203                 if (*bufp == '>')
2204                         *bufp = '\0';
2205                 bufp = &buf[1];
2206         }
2207         else
2208                 bufp = buf;
2209         if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2210             !bitnset(M_FROMPATH, m->m_flags))
2211         {
2212                 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2213         }
2214         else
2215         {
2216                 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2217                             *bufp == '@' ? ',' : ':', bufp, optbuf);
2218         }
2219         SmtpPhase = mci->mci_phase = "client MAIL";
2220         sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2221                         CurHostName, mci->mci_phase);
2222         r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_MAIL);
2223         if (r < 0)
2224         {
2225                 /* communications failure */
2226                 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2227                 return EX_TEMPFAIL;
2228         }
2229         else if (r == SMTPCLOSING)
2230         {
2231                 /* service shutting down: handled by reply() */
2232                 return EX_TEMPFAIL;
2233         }
2234         else if (REPLYTYPE(r) == 4)
2235         {
2236                 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
2237                             SmtpReplyBuffer);
2238                 return EX_TEMPFAIL;
2239         }
2240         else if (REPLYTYPE(r) == 2)
2241         {
2242                 return EX_OK;
2243         }
2244         else if (r == 501)
2245         {
2246                 /* syntax error in arguments */
2247                 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
2248                             SmtpReplyBuffer);
2249                 return EX_DATAERR;
2250         }
2251         else if (r == 553)
2252         {
2253                 /* mailbox name not allowed */
2254                 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
2255                             SmtpReplyBuffer);
2256                 return EX_DATAERR;
2257         }
2258         else if (r == 552)
2259         {
2260                 /* exceeded storage allocation */
2261                 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
2262                             SmtpReplyBuffer);
2263                 if (bitset(MCIF_SIZE, mci->mci_flags))
2264                         e->e_flags |= EF_NO_BODY_RETN;
2265                 return EX_UNAVAILABLE;
2266         }
2267         else if (REPLYTYPE(r) == 5)
2268         {
2269                 /* unknown error */
2270                 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
2271                             SmtpReplyBuffer);
2272                 return EX_UNAVAILABLE;
2273         }
2274
2275         if (LogLevel > 1)
2276         {
2277                 sm_syslog(LOG_CRIT, e->e_id,
2278                           "%.100s: SMTP MAIL protocol error: %s",
2279                           CurHostName,
2280                           shortenstring(SmtpReplyBuffer, 403));
2281         }
2282
2283         /* protocol error -- close up */
2284         mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2285                     SmtpReplyBuffer);
2286         smtpquit(m, mci, e);
2287         return EX_PROTOCOL;
2288 }
2289 /*
2290 **  SMTPRCPT -- designate recipient.
2291 **
2292 **      Parameters:
2293 **              to -- address of recipient.
2294 **              m -- the mailer we are sending to.
2295 **              mci -- the connection info for this transaction.
2296 **              e -- the envelope for this transaction.
2297 **
2298 **      Returns:
2299 **              exit status corresponding to recipient status.
2300 **
2301 **      Side Effects:
2302 **              Sends the mail via SMTP.
2303 */
2304
2305 int
2306 smtprcpt(to, m, mci, e, ctladdr, xstart)
2307         ADDRESS *to;
2308         register MAILER *m;
2309         MCI *mci;
2310         ENVELOPE *e;
2311         ADDRESS *ctladdr;
2312         time_t xstart;
2313 {
2314         char *bufp;
2315         char optbuf[MAXLINE];
2316
2317 #if PIPELINING
2318         /*
2319         **  If there is status waiting from the other end, read it.
2320         **  This should normally happen because of SMTP pipelining.
2321         */
2322
2323         while (mci->mci_nextaddr != NULL &&
2324                sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2325         {
2326                 int r;
2327
2328                 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2329                 if (r != EX_OK)
2330                 {
2331                         markfailure(e, mci->mci_nextaddr, mci, r, false);
2332                         giveresponse(r, mci->mci_nextaddr->q_status,  m, mci,
2333                                      ctladdr, xstart, e, to);
2334                 }
2335                 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2336         }
2337 #endif /* PIPELINING */
2338
2339         /*
2340         **  Check if connection is gone, if so
2341         **  it's a tempfail and we use mci_errno
2342         **  for the reason.
2343         */
2344
2345         if (mci->mci_state == MCIS_CLOSED)
2346         {
2347                 errno = mci->mci_errno;
2348                 return EX_TEMPFAIL;
2349         }
2350
2351         optbuf[0] = '\0';
2352         bufp = optbuf;
2353
2354         /*
2355         **  Warning: in the following it is assumed that the free space
2356         **  in bufp is sizeof(optbuf)
2357         */
2358
2359         if (bitset(MCIF_DSN, mci->mci_flags))
2360         {
2361                 if (IS_DLVR_NOTIFY(e) &&
2362                     !bitset(MCIF_DLVR_BY, mci->mci_flags))
2363                 {
2364                         /* RFC 2852: 4.1.4.2 */
2365                         if (!bitset(QHASNOTIFY, to->q_flags))
2366                                 to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
2367                         else if (bitset(QPINGONSUCCESS, to->q_flags) ||
2368                                  bitset(QPINGONFAILURE, to->q_flags) ||
2369                                  bitset(QPINGONDELAY, to->q_flags))
2370                                 to->q_flags |= QPINGONDELAY;
2371                 }
2372
2373                 /* NOTIFY= parameter */
2374                 if (bitset(QHASNOTIFY, to->q_flags) &&
2375                     bitset(QPRIMARY, to->q_flags) &&
2376                     !bitnset(M_LOCALMAILER, m->m_flags))
2377                 {
2378                         bool firstone = true;
2379
2380                         (void) sm_strlcat(bufp, " NOTIFY=", sizeof(optbuf));
2381                         if (bitset(QPINGONSUCCESS, to->q_flags))
2382                         {
2383                                 (void) sm_strlcat(bufp, "SUCCESS", sizeof(optbuf));
2384                                 firstone = false;
2385                         }
2386                         if (bitset(QPINGONFAILURE, to->q_flags))
2387                         {
2388                                 if (!firstone)
2389                                         (void) sm_strlcat(bufp, ",",
2390                                                        sizeof(optbuf));
2391                                 (void) sm_strlcat(bufp, "FAILURE", sizeof(optbuf));
2392                                 firstone = false;
2393                         }
2394                         if (bitset(QPINGONDELAY, to->q_flags))
2395                         {
2396                                 if (!firstone)
2397                                         (void) sm_strlcat(bufp, ",",
2398                                                        sizeof(optbuf));
2399                                 (void) sm_strlcat(bufp, "DELAY", sizeof(optbuf));
2400                                 firstone = false;
2401                         }
2402                         if (firstone)
2403                                 (void) sm_strlcat(bufp, "NEVER", sizeof(optbuf));
2404                         bufp += strlen(bufp);
2405                 }
2406
2407                 /* ORCPT= parameter */
2408                 if (to->q_orcpt != NULL &&
2409                     SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2410                 {
2411                         (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2412                                  " ORCPT=%s", to->q_orcpt);
2413                         bufp += strlen(bufp);
2414                 }
2415         }
2416
2417         smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
2418         mci->mci_state = MCIS_RCPT;
2419
2420         SmtpPhase = mci->mci_phase = "client RCPT";
2421         sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2422                         CurHostName, mci->mci_phase);
2423
2424 #if PIPELINING
2425         /*
2426         **  If running SMTP pipelining, we will pick up status later
2427         */
2428
2429         if (bitset(MCIF_PIPELINED, mci->mci_flags))
2430                 return EX_OK;
2431 #endif /* PIPELINING */
2432
2433         return smtprcptstat(to, m, mci, e);
2434 }
2435 /*
2436 **  SMTPRCPTSTAT -- get recipient status
2437 **
2438 **      This is only called during SMTP pipelining
2439 **
2440 **      Parameters:
2441 **              to -- address of recipient.
2442 **              m -- mailer being sent to.
2443 **              mci -- the mailer connection information.
2444 **              e -- the envelope for this message.
2445 **
2446 **      Returns:
2447 **              EX_* -- protocol status
2448 */
2449
2450 static int
2451 smtprcptstat(to, m, mci, e)
2452         ADDRESS *to;
2453         MAILER *m;
2454         register MCI *mci;
2455         register ENVELOPE *e;
2456 {
2457         int r;
2458         int save_errno;
2459         char *enhsc;
2460
2461         /*
2462         **  Check if connection is gone, if so
2463         **  it's a tempfail and we use mci_errno
2464         **  for the reason.
2465         */
2466
2467         if (mci->mci_state == MCIS_CLOSED)
2468         {
2469                 errno = mci->mci_errno;
2470                 return EX_TEMPFAIL;
2471         }
2472
2473         enhsc = NULL;
2474         r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_RCPT);
2475         save_errno = errno;
2476         to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
2477         to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
2478         if (!bitnset(M_LMTP, m->m_flags))
2479                 to->q_statmta = mci->mci_host;
2480         if (r < 0 || REPLYTYPE(r) == 4)
2481         {
2482                 mci->mci_retryrcpt = true;
2483                 errno = save_errno;
2484                 return EX_TEMPFAIL;
2485         }
2486         else if (REPLYTYPE(r) == 2)
2487         {
2488                 char *t;
2489
2490                 if ((t = mci->mci_tolist) != NULL)
2491                 {
2492                         char *p;
2493
2494                         *t++ = ',';
2495                         for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
2496                                 continue;
2497                         *t = '\0';
2498                         mci->mci_tolist = t;
2499                 }
2500 #if PIPELINING
2501                 mci->mci_okrcpts++;
2502 #endif
2503                 return EX_OK;
2504         }
2505         else if (r == 550)
2506         {
2507                 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2508                 return EX_NOUSER;
2509         }
2510         else if (r == 551)
2511         {
2512                 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2513                 return EX_NOUSER;
2514         }
2515         else if (r == 553)
2516         {
2517                 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2518                 return EX_NOUSER;
2519         }
2520         else if (REPLYTYPE(r) == 5)
2521         {
2522                 return EX_UNAVAILABLE;
2523         }
2524
2525         if (LogLevel > 1)
2526         {
2527                 sm_syslog(LOG_CRIT, e->e_id,
2528                           "%.100s: SMTP RCPT protocol error: %s",
2529                           CurHostName,
2530                           shortenstring(SmtpReplyBuffer, 403));
2531         }
2532
2533         mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2534                     SmtpReplyBuffer);
2535         return EX_PROTOCOL;
2536 }
2537 /*
2538 **  SMTPDATA -- send the data and clean up the transaction.
2539 **
2540 **      Parameters:
2541 **              m -- mailer being sent to.
2542 **              mci -- the mailer connection information.
2543 **              e -- the envelope for this message.
2544 **
2545 **      Returns:
2546 **              exit status corresponding to DATA command.
2547 */
2548
2549 int
2550 smtpdata(m, mci, e, ctladdr, xstart)
2551         MAILER *m;
2552         register MCI *mci;
2553         register ENVELOPE *e;
2554         ADDRESS *ctladdr;
2555         time_t xstart;
2556 {
2557         register int r;
2558         int rstat;
2559         int xstat;
2560         int timeout;
2561         char *enhsc;
2562
2563         /*
2564         **  Check if connection is gone, if so
2565         **  it's a tempfail and we use mci_errno
2566         **  for the reason.
2567         */
2568
2569         if (mci->mci_state == MCIS_CLOSED)
2570         {
2571                 errno = mci->mci_errno;
2572                 return EX_TEMPFAIL;
2573         }
2574
2575         enhsc = NULL;
2576
2577         /*
2578         **  Send the data.
2579         **      First send the command and check that it is ok.
2580         **      Then send the data (if there are valid recipients).
2581         **      Follow it up with a dot to terminate.
2582         **      Finally get the results of the transaction.
2583         */
2584
2585         /* send the command and check ok to proceed */
2586         smtpmessage("DATA", m, mci);
2587
2588 #if PIPELINING
2589         if (mci->mci_nextaddr != NULL)
2590         {
2591                 char *oldto = e->e_to;
2592
2593                 /* pick up any pending RCPT responses for SMTP pipelining */
2594                 while (mci->mci_nextaddr != NULL)
2595                 {
2596                         e->e_to = mci->mci_nextaddr->q_paddr;
2597                         r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2598                         if (r != EX_OK)
2599                         {
2600                                 markfailure(e, mci->mci_nextaddr, mci, r,
2601                                             false);
2602                                 giveresponse(r, mci->mci_nextaddr->q_status, m,
2603                                              mci, ctladdr, xstart, e,
2604                                              mci->mci_nextaddr);
2605                                 if (r == EX_TEMPFAIL)
2606                                         mci->mci_nextaddr->q_state = QS_RETRY;
2607                         }
2608                         mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2609                 }
2610                 e->e_to = oldto;
2611
2612                 /*
2613                 **  Connection might be closed in response to a RCPT command,
2614                 **  i.e., the server responded with 421. In that case (at
2615                 **  least) one RCPT has a temporary failure, hence we don't
2616                 **  need to check mci_okrcpts (as it is done below) to figure
2617                 **  out which error to return.
2618                 */
2619
2620                 if (mci->mci_state == MCIS_CLOSED)
2621                 {
2622                         errno = mci->mci_errno;
2623                         return EX_TEMPFAIL;
2624                 }
2625         }
2626 #endif /* PIPELINING */
2627
2628         /* now proceed with DATA phase */
2629         SmtpPhase = mci->mci_phase = "client DATA 354";
2630         mci->mci_state = MCIS_DATA;
2631         sm_setproctitle(true, e, "%s %s: %s",
2632                         qid_printname(e), CurHostName, mci->mci_phase);
2633         r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DATA);
2634         if (r < 0 || REPLYTYPE(r) == 4)
2635         {
2636                 if (r >= 0)
2637                         smtpquit(m, mci, e);
2638                 errno = mci->mci_errno;
2639                 return EX_TEMPFAIL;
2640         }
2641         else if (REPLYTYPE(r) == 5)
2642         {
2643                 smtprset(m, mci, e);
2644 #if PIPELINING
2645                 if (mci->mci_okrcpts <= 0)
2646                         return mci->mci_retryrcpt ? EX_TEMPFAIL
2647                                                   : EX_UNAVAILABLE;
2648 #endif
2649                 return EX_UNAVAILABLE;
2650         }
2651         else if (REPLYTYPE(r) != 3)
2652         {
2653                 if (LogLevel > 1)
2654                 {
2655                         sm_syslog(LOG_CRIT, e->e_id,
2656                                   "%.100s: SMTP DATA-1 protocol error: %s",
2657                                   CurHostName,
2658                                   shortenstring(SmtpReplyBuffer, 403));
2659                 }
2660                 smtprset(m, mci, e);
2661                 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2662                             SmtpReplyBuffer);
2663 #if PIPELINING
2664                 if (mci->mci_okrcpts <= 0)
2665                         return mci->mci_retryrcpt ? EX_TEMPFAIL
2666                                                   : EX_PROTOCOL;
2667 #endif
2668                 return EX_PROTOCOL;
2669         }
2670
2671 #if PIPELINING
2672         if (mci->mci_okrcpts > 0)
2673         {
2674 #endif
2675
2676         /*
2677         **  Set timeout around data writes.  Make it at least large
2678         **  enough for DNS timeouts on all recipients plus some fudge
2679         **  factor.  The main thing is that it should not be infinite.
2680         */
2681
2682         if (tTd(18, 101))
2683         {
2684                 /* simulate a DATA timeout */
2685                 timeout = 10;
2686         }
2687         else
2688                 timeout = DATA_PROGRESS_TIMEOUT * 1000;
2689         sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
2690
2691
2692         /*
2693         **  Output the actual message.
2694         */
2695
2696         if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
2697                 goto writeerr;
2698
2699         if (tTd(18, 101))
2700         {
2701                 /* simulate a DATA timeout */
2702                 (void) sleep(2);
2703         }
2704
2705         if (!(*e->e_putbody)(mci, e, NULL))
2706                 goto writeerr;
2707
2708         /*
2709         **  Cleanup after sending message.
2710         */
2711
2712
2713 #if PIPELINING
2714         }
2715 #endif
2716
2717 #if _FFR_CATCH_BROKEN_MTAS
2718         if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2719         {
2720                 /* terminate the message */
2721                 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
2722                                      m->m_eol);
2723                 if (TrafficLogFile != NULL)
2724                         (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2725                                              "%05d >>> .\n", (int) CurrentPid);
2726                 if (Verbose)
2727                         nmessage(">>> .");
2728
2729                 sm_syslog(LOG_CRIT, e->e_id,
2730                           "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2731                           CurHostName);
2732                 mci->mci_errno = EIO;
2733                 mci->mci_state = MCIS_ERROR;
2734                 mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
2735                 smtpquit(m, mci, e);
2736                 return EX_PROTOCOL;
2737         }
2738 #endif /* _FFR_CATCH_BROKEN_MTAS */
2739
2740         if (sm_io_error(mci->mci_out))
2741         {
2742                 /* error during processing -- don't send the dot */
2743                 mci->mci_errno = EIO;
2744                 mci->mci_state = MCIS_ERROR;
2745                 mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2746                 smtpquit(m, mci, e);
2747                 return EX_IOERR;
2748         }
2749
2750         /* terminate the message */
2751         if (sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s.%s",
2752                         bitset(MCIF_INLONGLINE, mci->mci_flags) ? m->m_eol : "",
2753                         m->m_eol) == SM_IO_EOF)
2754                 goto writeerr;
2755         if (TrafficLogFile != NULL)
2756                 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2757                                      "%05d >>> .\n", (int) CurrentPid);
2758         if (Verbose)
2759                 nmessage(">>> .");
2760
2761         /* check for the results of the transaction */
2762         SmtpPhase = mci->mci_phase = "client DATA status";
2763         sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2764                         CurHostName, mci->mci_phase);
2765         if (bitnset(M_LMTP, m->m_flags))
2766                 return EX_OK;
2767         r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_EOM);
2768         if (r < 0)
2769                 return EX_TEMPFAIL;
2770         if (mci->mci_state == MCIS_DATA)
2771                 mci->mci_state = MCIS_OPEN;
2772         xstat = EX_NOTSTICKY;
2773         if (r == 452)
2774                 rstat = EX_TEMPFAIL;
2775         else if (REPLYTYPE(r) == 4)
2776                 rstat = xstat = EX_TEMPFAIL;
2777         else if (REPLYTYPE(r) == 2)
2778                 rstat = xstat = EX_OK;
2779         else if (REPLYCLASS(r) != 5)
2780                 rstat = xstat = EX_PROTOCOL;
2781         else if (REPLYTYPE(r) == 5)
2782                 rstat = EX_UNAVAILABLE;
2783         else
2784                 rstat = EX_PROTOCOL;
2785         mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
2786                     SmtpReplyBuffer);
2787         if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2788             (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2789                 r += 5;
2790         else
2791                 r = 4;
2792         e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2793         SmtpPhase = mci->mci_phase = "idle";
2794         sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2795         if (rstat != EX_PROTOCOL)
2796                 return rstat;
2797         if (LogLevel > 1)
2798         {
2799                 sm_syslog(LOG_CRIT, e->e_id,
2800                           "%.100s: SMTP DATA-2 protocol error: %s",
2801                           CurHostName,
2802                           shortenstring(SmtpReplyBuffer, 403));
2803         }
2804         return rstat;
2805
2806   writeerr:
2807         mci->mci_errno = errno;
2808         mci->mci_state = MCIS_ERROR;
2809         mci_setstat(mci, bitset(MCIF_NOTSTICKY, mci->mci_flags)
2810                          ? EX_NOTSTICKY: EX_TEMPFAIL,
2811                     "4.4.2", NULL);
2812         mci->mci_flags &= ~MCIF_NOTSTICKY;
2813
2814         /*
2815         **  If putbody() couldn't finish due to a timeout,
2816         **  rewind it here in the timeout handler.  See
2817         **  comments at the end of putbody() for reasoning.
2818         */
2819
2820         if (e->e_dfp != NULL)
2821                 (void) bfrewind(e->e_dfp);
2822
2823         errno = mci->mci_errno;
2824         syserr("+451 4.4.1 timeout writing message to %s", CurHostName);
2825         smtpquit(m, mci, e);
2826         return EX_TEMPFAIL;
2827 }
2828
2829 /*
2830 **  SMTPGETSTAT -- get status code from DATA in LMTP
2831 **
2832 **      Parameters:
2833 **              m -- the mailer to which we are sending the message.
2834 **              mci -- the mailer connection structure.
2835 **              e -- the current envelope.
2836 **
2837 **      Returns:
2838 **              The exit status corresponding to the reply code.
2839 */
2840
2841 int
2842 smtpgetstat(m, mci, e)
2843         MAILER *m;
2844         MCI *mci;
2845         ENVELOPE *e;
2846 {
2847         int r;
2848         int off;
2849         int status, xstat;
2850         char *enhsc;
2851
2852         enhsc = NULL;
2853
2854         /* check for the results of the transaction */
2855         r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DATA2);
2856         if (r < 0)
2857                 return EX_TEMPFAIL;
2858         xstat = EX_NOTSTICKY;
2859         if (REPLYTYPE(r) == 4)
2860                 status = EX_TEMPFAIL;
2861         else if (REPLYTYPE(r) == 2)
2862                 status = xstat = EX_OK;
2863         else if (REPLYCLASS(r) != 5)
2864                 status = xstat = EX_PROTOCOL;
2865         else if (REPLYTYPE(r) == 5)
2866                 status = EX_UNAVAILABLE;
2867         else
2868                 status = EX_PROTOCOL;
2869         if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2870             (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2871                 off += 5;
2872         else
2873                 off = 4;
2874         e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
2875         mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
2876         if (LogLevel > 1 && status == EX_PROTOCOL)
2877         {
2878                 sm_syslog(LOG_CRIT, e->e_id,
2879                           "%.100s: SMTP DATA-3 protocol error: %s",
2880                           CurHostName,
2881                           shortenstring(SmtpReplyBuffer, 403));
2882         }
2883         return status;
2884 }
2885 /*
2886 **  SMTPQUIT -- close the SMTP connection.
2887 **
2888 **      Parameters:
2889 **              m -- a pointer to the mailer.
2890 **              mci -- the mailer connection information.
2891 **              e -- the current envelope.
2892 **
2893 **      Returns:
2894 **              none.
2895 **
2896 **      Side Effects:
2897 **              sends the final protocol and closes the connection.
2898 */
2899
2900 void
2901 smtpquit(m, mci, e)
2902         register MAILER *m;
2903         register MCI *mci;
2904         ENVELOPE *e;
2905 {
2906         bool oldSuprErrs = SuprErrs;
2907         int rcode;
2908         char *oldcurhost;
2909
2910         if (mci->mci_state == MCIS_CLOSED)
2911         {
2912                 mci_close(mci, "smtpquit:1");
2913                 return;
2914         }
2915
2916         oldcurhost = CurHostName;
2917         CurHostName = mci->mci_host;            /* XXX UGLY XXX */
2918         if (CurHostName == NULL)
2919                 CurHostName = MyHostName;
2920
2921 #if PIPELINING
2922         mci->mci_okrcpts = 0;
2923 #endif
2924
2925         /*
2926         **      Suppress errors here -- we may be processing a different
2927         **      job when we do the quit connection, and we don't want the
2928         **      new job to be penalized for something that isn't it's
2929         **      problem.
2930         */
2931
2932         SuprErrs = true;
2933
2934         /* send the quit message if we haven't gotten I/O error */
2935         if (mci->mci_state != MCIS_ERROR &&
2936             mci->mci_state != MCIS_QUITING)
2937         {
2938                 SmtpPhase = "client QUIT";
2939                 mci->mci_state = MCIS_QUITING;
2940                 smtpmessage("QUIT", m, mci);
2941                 (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL, XS_QUIT);
2942                 SuprErrs = oldSuprErrs;
2943                 if (mci->mci_state == MCIS_CLOSED)
2944                         goto end;
2945         }
2946
2947         /* now actually close the connection and pick up the zombie */
2948         rcode = endmailer(mci, e, NULL);
2949         if (rcode != EX_OK)
2950         {
2951                 char *mailer = NULL;
2952
2953                 if (mci->mci_mailer != NULL &&
2954                     mci->mci_mailer->m_name != NULL)
2955                         mailer = mci->mci_mailer->m_name;
2956
2957                 /* look for naughty mailers */
2958                 sm_syslog(LOG_ERR, e->e_id,
2959                           "smtpquit: mailer%s%s exited with exit value %d",
2960                           mailer == NULL ? "" : " ",
2961                           mailer == NULL ? "" : mailer,
2962                           rcode);
2963         }
2964
2965         SuprErrs = oldSuprErrs;
2966
2967   end:
2968         CurHostName = oldcurhost;
2969         return;
2970 }
2971 /*
2972 **  SMTPRSET -- send a RSET (reset) command
2973 **
2974 **      Parameters:
2975 **              m -- a pointer to the mailer.
2976 **              mci -- the mailer connection information.
2977 **              e -- the current envelope.
2978 **
2979 **      Returns:
2980 **              none.
2981 **
2982 **      Side Effects:
2983 **              closes the connection if there is no reply to RSET.
2984 */
2985
2986 void
2987 smtprset(m, mci, e)
2988         register MAILER *m;
2989         register MCI *mci;
2990         ENVELOPE *e;
2991 {
2992         int r;
2993
2994         CurHostName = mci->mci_host;            /* XXX UGLY XXX */
2995         if (CurHostName == NULL)
2996                 CurHostName = MyHostName;
2997
2998 #if PIPELINING
2999         mci->mci_okrcpts = 0;
3000 #endif
3001
3002         /*
3003         **  Check if connection is gone, if so
3004         **  it's a tempfail and we use mci_errno
3005         **  for the reason.
3006         */
3007
3008         if (mci->mci_state == MCIS_CLOSED)
3009         {
3010                 errno = mci->mci_errno;
3011                 return;
3012         }
3013
3014         SmtpPhase = "client RSET";
3015         smtpmessage("RSET", m, mci);
3016         r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT);
3017         if (r < 0)
3018                 return;
3019
3020         /*
3021         **  Any response is deemed to be acceptable.
3022         **  The standard does not state the proper action
3023         **  to take when a value other than 250 is received.
3024         **
3025         **  However, if 421 is returned for the RSET, leave
3026         **  mci_state alone (MCIS_SSD can be set in reply()
3027         **  and MCIS_CLOSED can be set in smtpquit() if
3028         **  reply() gets a 421 and calls smtpquit()).
3029         */
3030
3031         if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
3032                 mci->mci_state = MCIS_OPEN;
3033         else if (mci->mci_exitstat == EX_OK)
3034                 mci_setstat(mci, EX_TEMPFAIL, "4.5.0", NULL);
3035 }
3036 /*
3037 **  SMTPPROBE -- check the connection state
3038 **
3039 **      Parameters:
3040 **              mci -- the mailer connection information.
3041 **
3042 **      Returns:
3043 **              none.
3044 **
3045 **      Side Effects:
3046 **              closes the connection if there is no reply to RSET.
3047 */
3048
3049 int
3050 smtpprobe(mci)
3051         register MCI *mci;
3052 {
3053         int r;
3054         MAILER *m = mci->mci_mailer;
3055         ENVELOPE *e;
3056         extern ENVELOPE BlankEnvelope;
3057
3058         CurHostName = mci->mci_host;            /* XXX UGLY XXX */
3059         if (CurHostName == NULL)
3060                 CurHostName = MyHostName;
3061
3062         e = &BlankEnvelope;
3063         SmtpPhase = "client probe";
3064         smtpmessage("RSET", m, mci);
3065         r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT);
3066         if (REPLYTYPE(r) != 2)
3067                 smtpquit(m, mci, e);
3068         return r;
3069 }
3070 /*
3071 **  REPLY -- read arpanet reply
3072 **
3073 **      Parameters:
3074 **              m -- the mailer we are reading the reply from.
3075 **              mci -- the mailer connection info structure.
3076 **              e -- the current envelope.
3077 **              timeout -- the timeout for reads.
3078 **              pfunc -- processing function called on each line of response.
3079 **                      If null, no special processing is done.
3080 **              enhstat -- optional, returns enhanced error code string (if set)
3081 **              rtype -- type of SmtpMsgBuffer: does it contains secret data?
3082 **
3083 **      Returns:
3084 **              reply code it reads.
3085 **
3086 **      Side Effects:
3087 **              flushes the mail file.
3088 */
3089
3090 int
3091 reply(m, mci, e, timeout, pfunc, enhstat, rtype)
3092         MAILER *m;
3093         MCI *mci;
3094         ENVELOPE *e;
3095         time_t timeout;
3096         void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
3097         char **enhstat;
3098         int rtype;
3099 {
3100         register char *bufp;
3101         register int r;
3102         bool firstline = true;
3103         char junkbuf[MAXLINE];
3104         static char enhstatcode[ENHSCLEN];
3105         int save_errno;
3106
3107         /*
3108         **  Flush the output before reading response.
3109         **
3110         **      For SMTP pipelining, it would be better if we didn't do
3111         **      this if there was already data waiting to be read.  But
3112         **      to do it properly means pushing it to the I/O library,
3113         **      since it really needs to be done below the buffer layer.
3114         */
3115
3116         if (mci->mci_out != NULL)
3117                 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3118
3119         if (tTd(18, 1))
3120         {
3121                 char *what;
3122
3123                 if (SmtpMsgBuffer[0] != '\0')
3124                         what = SmtpMsgBuffer;
3125                 else if (SmtpPhase != NULL && SmtpPhase[0] != '\0')
3126                         what = SmtpPhase;
3127                 else if (XS_GREET == rtype)
3128                         what = "greeting";
3129                 else
3130                         what = "unknown";
3131                 sm_dprintf("reply to %s\n", what);
3132         }
3133
3134         /*
3135         **  Read the input line, being careful not to hang.
3136         */
3137
3138         bufp = SmtpReplyBuffer;
3139         (void) set_tls_rd_tmo(timeout);
3140         for (;;)
3141         {
3142                 register char *p;
3143
3144                 /* actually do the read */
3145                 if (e->e_xfp != NULL)   /* for debugging */
3146                         (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3147
3148                 /* if we are in the process of closing just give the code */
3149                 if (mci->mci_state == MCIS_CLOSED)
3150                         return SMTPCLOSING;
3151
3152                 /* don't try to read from a non-existent fd */
3153                 if (mci->mci_in == NULL)
3154                 {
3155                         if (mci->mci_errno == 0)
3156                                 mci->mci_errno = EBADF;
3157
3158                         /* errors on QUIT should be ignored */
3159                         if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3160                         {
3161                                 errno = mci->mci_errno;
3162                                 mci_close(mci, "reply:1");
3163                                 return -1;
3164                         }
3165                         mci->mci_state = MCIS_ERROR;
3166                         smtpquit(m, mci, e);
3167                         errno = mci->mci_errno;
3168                         return -1;
3169                 }
3170
3171                 if (mci->mci_out != NULL)
3172                         (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3173
3174                 /* get the line from the other side */
3175                 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
3176                 save_errno = errno;
3177                 mci->mci_lastuse = curtime();
3178
3179                 if (p == NULL)
3180                 {
3181                         bool oldholderrs;
3182                         extern char MsgBuf[];
3183
3184                         /* errors on QUIT should be ignored */
3185                         if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3186                         {
3187                                 mci_close(mci, "reply:2");
3188                                 return -1;
3189                         }
3190
3191                         /* if the remote end closed early, fake an error */
3192                         errno = save_errno;
3193                         if (errno == 0)
3194                         {
3195                                 (void) sm_snprintf(SmtpReplyBuffer,
3196                                                    sizeof(SmtpReplyBuffer),
3197                                                    "421 4.4.1 Connection reset by %s",
3198                                                    CURHOSTNAME);
3199 #ifdef ECONNRESET
3200                                 errno = ECONNRESET;
3201 #else
3202                                 errno = EPIPE;
3203 #endif
3204                         }
3205
3206                         mci->mci_errno = errno;
3207                         oldholderrs = HoldErrs;
3208                         HoldErrs = true;
3209                         usrerr("451 4.4.1 reply: read error from %s",
3210                                CURHOSTNAME);
3211                         mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3212
3213                         /* if debugging, pause so we can see state */
3214                         if (tTd(18, 100))
3215                                 (void) pause();
3216                         mci->mci_state = MCIS_ERROR;
3217                         smtpquit(m, mci, e);
3218 #if XDEBUG
3219                         {
3220                                 char wbuf[MAXLINE];
3221
3222                                 p = wbuf;
3223                                 if (e->e_to != NULL)
3224                                 {
3225                                         (void) sm_snprintf(p,
3226                                                            SPACELEFT(wbuf, p),
3227                                                            "%s... ",
3228                                                            shortenstring(e->e_to, MAXSHORTSTR));
3229                                         p += strlen(p);
3230                                 }
3231                                 (void) sm_snprintf(p, SPACELEFT(wbuf, p),
3232                                                    "reply(%.100s) during %s",
3233                                                    CURHOSTNAME, SmtpPhase);
3234                                 checkfd012(wbuf);
3235                         }
3236 #endif /* XDEBUG */
3237                         HoldErrs = oldholderrs;
3238                         errno = save_errno;
3239                         return -1;
3240                 }
3241                 fixcrlf(bufp, true);
3242
3243                 /* EHLO failure is not a real error */
3244                 if (e->e_xfp != NULL && (bufp[0] == '4' ||
3245                     (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3246                 {
3247                         /* serious error -- log the previous command */
3248                         if (SmtpNeedIntro)
3249                         {
3250                                 /* inform user who we are chatting with */
3251                                 (void) sm_io_fprintf(CurEnv->e_xfp,
3252                                                      SM_TIME_DEFAULT,
3253                                                      "... while talking to %s:\n",
3254                                                      CURHOSTNAME);
3255                                 SmtpNeedIntro = false;
3256                         }
3257                         if (SmtpMsgBuffer[0] != '\0')
3258                         {
3259                                 (void) sm_io_fprintf(e->e_xfp,
3260                                         SM_TIME_DEFAULT,
3261                                         ">>> %s\n",
3262                                         (rtype == XS_STARTTLS)
3263                                         ? "STARTTLS dialogue"
3264                                         : ((rtype == XS_AUTH)
3265                                            ? "AUTH dialogue"
3266                                            : SmtpMsgBuffer));
3267                                 SmtpMsgBuffer[0] = '\0';
3268                         }
3269
3270                         /* now log the message as from the other side */
3271                         (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3272                                              "<<< %s\n", bufp);
3273                 }
3274
3275                 /* display the input for verbose mode */
3276                 if (Verbose)
3277                         nmessage("050 %s", bufp);
3278
3279                 /* ignore improperly formatted input */
3280                 if (!ISSMTPREPLY(bufp))
3281                         continue;
3282
3283                 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
3284                     enhstat != NULL &&
3285                     extenhsc(bufp + 4, ' ', enhstatcode) > 0)
3286                         *enhstat = enhstatcode;
3287
3288                 /* process the line */
3289                 if (pfunc != NULL)
3290                         (*pfunc)(bufp, firstline, m, mci, e);
3291
3292                 /* decode the reply code */
3293                 r = atoi(bufp);
3294
3295                 /* extra semantics: 0xx codes are "informational" */
3296                 if (r < 100)
3297                 {
3298                         firstline = false;
3299                         continue;
3300                 }
3301                 if (REPLYTYPE(r) > 3 && firstline
3302 # if _FFR_PROXY
3303                     &&
3304                     (e->e_sendmode != SM_PROXY
3305                      || (e->e_sendmode == SM_PROXY
3306                          && (e->e_rcode == 0 || REPLYTYPE(e->e_rcode) < 5))
3307                     )
3308 # endif
3309                    )
3310                 {
3311                         int o = -1;
3312 # if PIPELINING
3313                         /*
3314                         **  ignore error iff: DATA, 5xy error, but we had
3315                         **  "retryable" recipients. XREF: smtpdata()
3316                         */
3317
3318                         if (!(rtype == XS_DATA && REPLYTYPE(r) == 5 &&
3319                               mci->mci_okrcpts <= 0 && mci->mci_retryrcpt))
3320 # endif /* PIPELINING */
3321                         {
3322                                 o = extenhsc(bufp + 4, ' ', enhstatcode);
3323                                 if (o > 0)
3324                                 {
3325                                         sm_strlcpy(e->e_renhsc, enhstatcode,
3326                                                 sizeof(e->e_renhsc));
3327
3328                                         /* skip SMTP reply code, delimiters */
3329                                         o += 5;
3330                                 }
3331                                 else
3332                                         o = 4;
3333
3334                                 /*
3335                                 **  Don't use this for reply= logging
3336                                 **  if it was for QUIT.
3337                                 **  (Note: use the debug option to
3338                                 **  reproduce the original error.)
3339                                 */
3340
3341                                 if (rtype != XS_QUIT || tTd(87, 101))
3342                                 {
3343                                         e->e_rcode = r;
3344                                         e->e_text = sm_rpool_strdup_x(
3345                                                         e->e_rpool, bufp + o);
3346                                 }
3347                         }
3348                         if (tTd(87, 2))
3349                         {
3350                                 sm_dprintf("user: e=%p, offset=%d, bufp=%s, rcode=%d, enhstat=%s, rtype=%d, text=%s\n"
3351                                         , (void *)e, o, bufp, r, e->e_renhsc
3352                                         , rtype, e->e_text);
3353                         }
3354                 }
3355
3356                 firstline = false;
3357
3358                 /* if no continuation lines, return this line */
3359                 if (bufp[3] != '-')
3360                         break;
3361
3362                 /* first line of real reply -- ignore rest */
3363                 bufp = junkbuf;
3364         }
3365
3366         /*
3367         **  Now look at SmtpReplyBuffer -- only care about the first
3368         **  line of the response from here on out.
3369         */
3370
3371         /* save temporary failure messages for posterity */
3372         if (SmtpReplyBuffer[0] == '4')
3373                 (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof(SmtpError));
3374
3375         /* reply code 421 is "Service Shutting Down" */
3376         if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3377             mci->mci_state != MCIS_QUITING)
3378         {
3379                 /* send the quit protocol */
3380                 mci->mci_state = MCIS_SSD;
3381                 smtpquit(m, mci, e);
3382         }
3383
3384         return r;
3385 }
3386 /*
3387 **  SMTPMESSAGE -- send message to server
3388 **
3389 **      Parameters:
3390 **              f -- format
3391 **              m -- the mailer to control formatting.
3392 **              a, b, c -- parameters
3393 **
3394 **      Returns:
3395 **              none.
3396 **
3397 **      Side Effects:
3398 **              writes message to mci->mci_out.
3399 */
3400
3401 /*VARARGS1*/
3402 void
3403 #ifdef __STDC__
3404 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
3405 #else /* __STDC__ */
3406 smtpmessage(f, m, mci, va_alist)
3407         char *f;
3408         MAILER *m;
3409         MCI *mci;
3410         va_dcl
3411 #endif /* __STDC__ */
3412 {
3413         SM_VA_LOCAL_DECL
3414
3415         SM_VA_START(ap, mci);
3416         (void) sm_vsnprintf(SmtpMsgBuffer, sizeof(SmtpMsgBuffer), f, ap);
3417         SM_VA_END(ap);
3418
3419         if (tTd(18, 1) || Verbose)
3420                 nmessage(">>> %s", SmtpMsgBuffer);
3421         if (TrafficLogFile != NULL)
3422                 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
3423                                      "%05d >>> %s\n", (int) CurrentPid,
3424                                      SmtpMsgBuffer);
3425         if (mci->mci_out != NULL)
3426         {
3427                 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
3428                                      SmtpMsgBuffer, m == NULL ? "\r\n"
3429                                                               : m->m_eol);
3430         }
3431         else if (tTd(18, 1))
3432         {
3433                 sm_dprintf("smtpmessage: NULL mci_out\n");
3434         }
3435 }