source: trunk/third/sendmail/sendmail/usersmtp.c @ 22421

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