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