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