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