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