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