Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
[mmh] / mts / smtp / smtp.c
1 /*
2  * smtp.c -- nmh SMTP interface
3  *
4  * This code is Copyright (c) 2002, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mh.h>
10 #include "smtp.h"
11 #include <h/mts.h>
12 #include <signal.h>
13 #include <h/signals.h>
14
15 #ifdef CYRUS_SASL
16 #include <sasl/sasl.h>
17 #include <sasl/saslutil.h>
18 # if SASL_VERSION_FULL < 0x020125
19     /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
20        which has an explicit void parameter list, according to best
21        practice.  So we need to cast to avoid compile warnings.
22        Provide this prototype for earlier versions. */
23     typedef int (*sasl_callback_ft)();
24 # endif /* SASL_VERSION_FULL < 0x020125 */
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <netdb.h>
29 #include <errno.h>
30 #endif /* CYRUS_SASL */
31
32 #ifdef TLS_SUPPORT
33 #include <openssl/ssl.h>
34 #include <openssl/err.h>
35 #endif /* TLS_SUPPORT */
36
37 /*
38  * This module implements an interface to SendMail very similar
39  * to the MMDF mm_(3) routines.  The sm_() routines herein talk
40  * SMTP to a sendmail process, mapping SMTP reply codes into
41  * RP_-style codes.
42  */
43
44 /*
45  * On older 4.2BSD machines without the POSIX function `sigaction',
46  * the alarm handing stuff for time-outs will NOT work due to the way
47  * syscalls get restarted.  This is not really crucial, since SendMail
48  * is generally well-behaved in this area.
49  */
50
51 #ifdef SENDMAILBUG
52 /*
53  * It appears that some versions of Sendmail will return Code 451
54  * when they don't really want to indicate a failure.
55  * "Code 451 almost always means sendmail has deferred; we don't
56  * really want bomb out at this point since sendmail will rectify
57  * things later."  So, if you define SENDMAILBUG, Code 451 is
58  * considered the same as Code 250.  Yuck!
59  */
60 #endif
61
62 #define TRUE    1
63 #define FALSE   0
64
65 #define NBITS ((sizeof (int)) * 8)
66
67 /*
68  * these codes must all be different!
69  */
70 #define SM_OPEN  300      /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
71 #define SM_HELO  20
72 #define SM_RSET  15
73 #define SM_MAIL  301      /* changed to 5 minutes and a second (for uniqueness), see above */
74 #define SM_RCPT  302      /* see above */
75 #define SM_DATA  120      /* see above */
76 #define SM_TEXT 180     /* see above */
77 #define SM_DOT  600     /* see above */
78 #define SM_QUIT  30
79 #define SM_CLOS  10
80 #ifdef CYRUS_SASL
81 #define SM_AUTH  45
82 #endif /* CYRUS_SASL */
83
84 static int sm_addrs = 0;
85 static int sm_alarmed = 0;
86 static int sm_child = NOTOK;
87 static int sm_debug = 0;
88 static int sm_nl = TRUE;
89 static int sm_verbose = 0;
90
91 static FILE *sm_rfp = NULL;
92 static FILE *sm_wfp = NULL;
93
94 #ifdef CYRUS_SASL
95 /*
96  * Some globals needed by SASL
97  */
98
99 static sasl_conn_t *conn = NULL;        /* SASL connection state */
100 static int sasl_complete = 0;           /* Has authentication succeded? */
101 static sasl_ssf_t sasl_ssf;             /* Our security strength factor */
102 static char *sasl_pw_context[2];        /* Context to pass into sm_get_pass */
103 static int maxoutbuf;                   /* Maximum crypto output buffer */
104 static char *sasl_outbuffer;            /* SASL output buffer for encryption */
105 static int sasl_outbuflen;              /* Current length of data in outbuf */
106 static int sm_get_user(void *, int, const char **, unsigned *);
107 static int sm_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
108
109 static sasl_callback_t callbacks[] = {
110     { SASL_CB_USER, (sasl_callback_ft) sm_get_user, NULL },
111 #define SM_SASL_N_CB_USER 0
112     { SASL_CB_PASS, (sasl_callback_ft) sm_get_pass, NULL },
113 #define SM_SASL_N_CB_PASS 1
114     { SASL_CB_AUTHNAME, (sasl_callback_ft) sm_get_user, NULL },
115 #define SM_SASL_N_CB_AUTHNAME 2
116     { SASL_CB_LIST_END, NULL, NULL },
117 };
118
119 #else /* CYRUS_SASL */
120 int sasl_ssf = 0;
121 #endif /* CYRUS_SASL */
122
123 #ifdef TLS_SUPPORT
124 static SSL_CTX *sslctx = NULL;
125 static SSL *ssl = NULL;
126 static BIO *sbior = NULL;
127 static BIO *sbiow = NULL;
128 static BIO *io = NULL;
129 #endif /* TLS_SUPPORT */
130
131 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
132 #define SASL_MAXRECVBUF 65536
133 static int sm_fgetc(FILE *);
134 static char *sasl_inbuffer;             /* SASL input buffer for encryption */
135 static char *sasl_inptr;                /* Pointer to current inbuf position */
136 static int sasl_inbuflen;               /* Current length of data in inbuf */
137 #else
138 #define sm_fgetc fgetc
139 #endif
140
141 static int tls_active = 0;
142
143 static char *sm_noreply = "No reply text given";
144 static char *sm_moreply = "; ";
145
146 struct smtp sm_reply;           /* global... */
147
148 #define MAXEHLO 20
149
150 static int doingEHLO;
151 char *EHLOkeys[MAXEHLO + 1];
152
153 /*
154  * static prototypes
155  */
156 static int smtp_init (char *, char *, char *, int, int, int, int, int, int,
157                       char *, char *, int);
158 static int sendmail_init (char *, char *, int, int, int, int, int, int,
159                           char *, char *);
160
161 static int rclient (char *, char *);
162 static int sm_ierror (char *fmt, ...);
163 static int smtalk (int time, char *fmt, ...);
164 static int sm_wrecord (char *, int);
165 static int sm_wstream (char *, int);
166 static int sm_werror (void);
167 static int smhear (void);
168 static int sm_rrecord (char *, int *);
169 static int sm_rerror (int);
170 static void alrmser (int);
171 static char *EHLOset (char *);
172 static int sm_fwrite(char *, int);
173 static int sm_fputs(char *);
174 static int sm_fputc(int);
175 static void sm_fflush(void);
176 static int sm_fgets(char *, int, FILE *);
177
178 #ifdef CYRUS_SASL
179 /*
180  * Function prototypes needed for SASL
181  */
182
183 static int sm_auth_sasl(char *, int, char *, char *);
184 #endif /* CYRUS_SASL */
185
186 int
187 sm_init (char *client, char *server, char *port, int watch, int verbose,
188          int debug, int queued, int sasl, int saslssf,
189          char *saslmech, char *user, int tls)
190 {
191     if (sm_mts == MTS_SMTP)
192         return smtp_init (client, server, port, watch, verbose,
193                           debug, queued, sasl, saslssf, saslmech,
194                           user, tls);
195     else
196         return sendmail_init (client, server, watch, verbose,
197                               debug, queued, sasl, saslssf, saslmech,
198                               user);
199 }
200
201 static int
202 smtp_init (char *client, char *server, char *port, int watch, int verbose,
203            int debug, int queued,
204            int sasl, int saslssf, char *saslmech, char *user, int tls)
205 {
206     int result, sd1, sd2;
207 #ifdef CYRUS_SASL
208     char *server_mechs;
209 #else  /* CYRUS_SASL */
210     NMH_UNUSED (sasl);
211     NMH_UNUSED (saslssf);
212     NMH_UNUSED (saslmech);
213     NMH_UNUSED (user);
214 #endif /* CYRUS_SASL */
215
216     if (watch)
217         verbose = TRUE;
218
219     sm_verbose = verbose;
220     sm_debug = debug;
221
222     if (sm_rfp != NULL && sm_wfp != NULL)
223         goto send_options;
224
225     if (client == NULL || *client == '\0') {
226         if (clientname) {
227             client = clientname;
228         } else {
229             client = LocalName(1);      /* no clientname -> LocalName */
230         }
231     }
232
233     /*
234      * Last-ditch check just in case client still isn't set to anything
235      */
236
237     if (client == NULL || *client == '\0')
238         client = "localhost";
239
240 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
241     sasl_inbuffer = malloc(SASL_MAXRECVBUF);
242     if (!sasl_inbuffer)
243         return sm_ierror("Unable to allocate %d bytes for read buffer",
244                          SASL_MAXRECVBUF);
245 #endif /* CYRUS_SASL || TLS_SUPPORT */
246
247     if ((sd1 = rclient (server, port)) == NOTOK)
248         return RP_BHST;
249
250     if ((sd2 = dup (sd1)) == NOTOK) {
251         close (sd1);
252         return sm_ierror ("unable to dup");
253     }
254
255     SIGNAL (SIGALRM, alrmser);
256     SIGNAL (SIGPIPE, SIG_IGN);
257
258     if ((sm_rfp = fdopen (sd1, "r")) == NULL
259             || (sm_wfp = fdopen (sd2, "w")) == NULL) {
260         close (sd1);
261         close (sd2);
262         sm_rfp = sm_wfp = NULL;
263         return sm_ierror ("unable to fdopen");
264     }
265
266     tls_active = 0;
267
268     sm_alarmed = 0;
269     alarm (SM_OPEN);
270     result = smhear ();
271     alarm (0);
272
273     switch (result) {
274         case 220: 
275             break;
276
277         default: 
278             sm_end (NOTOK);
279             return RP_RPLY;
280     }
281
282     /*
283      * Give EHLO or HELO command
284      */
285
286     doingEHLO = 1;
287     result = smtalk (SM_HELO, "EHLO %s", client);
288     doingEHLO = 0;
289
290     if (result >= 500 && result <= 599)
291         result = smtalk (SM_HELO, "HELO %s", client);
292
293     if (result != 250) {
294         sm_end (NOTOK);
295         return RP_RPLY;
296     }
297
298 #ifdef TLS_SUPPORT
299     /*
300      * If the user requested TLS support, then try to do the STARTTLS command
301      * as part of the initial dialog.  Assuming this works, we then need to
302      * restart the EHLO dialog after TLS negotiation is complete.
303      */
304
305     if (tls) {
306         BIO *ssl_bio;
307
308         if (! EHLOset("STARTTLS")) {
309             sm_end(NOTOK);
310             return sm_ierror("SMTP server does not support TLS");
311         }
312
313         result = smtalk(SM_HELO, "STARTTLS");
314
315         if (result != 220) {
316             sm_end(NOTOK);
317             return RP_RPLY;
318         }
319
320         /*
321          * Okay, the other side should be waiting for us to start TLS
322          * negotiation.  Oblige them.
323          */
324
325         if (! sslctx) {
326             const SSL_METHOD *method;
327
328             SSL_library_init();
329             SSL_load_error_strings();
330
331             method = TLSv1_client_method();     /* Not sure about this */
332
333             sslctx = SSL_CTX_new(method);
334
335             if (! sslctx) {
336                 sm_end(NOTOK);
337                 return sm_ierror("Unable to initialize OpenSSL context: %s",
338                                  ERR_error_string(ERR_get_error(), NULL));
339             }
340         }
341
342         ssl = SSL_new(sslctx);
343
344         if (! ssl) {
345             sm_end(NOTOK);
346             return sm_ierror("Unable to create SSL connection: %s",
347                              ERR_error_string(ERR_get_error(), NULL));
348         }
349
350         sbior = BIO_new_socket(fileno(sm_rfp), BIO_NOCLOSE);
351         sbiow = BIO_new_socket(fileno(sm_wfp), BIO_NOCLOSE);
352
353         if (sbior == NULL || sbiow == NULL) {
354             sm_end(NOTOK);
355             return sm_ierror("Unable to create BIO endpoints: %s",
356                              ERR_error_string(ERR_get_error(), NULL));
357         }
358
359         SSL_set_bio(ssl, sbior, sbiow);
360         SSL_set_connect_state(ssl);
361
362         /*
363          * Set up a BIO to handle buffering for us
364          */
365
366         io = BIO_new(BIO_f_buffer());
367
368         if (! io) {
369             sm_end(NOTOK);
370             return sm_ierror("Unable to create a buffer BIO: %s",
371                              ERR_error_string(ERR_get_error(), NULL));
372         }
373
374         ssl_bio = BIO_new(BIO_f_ssl());
375
376         if (! ssl_bio) {
377             sm_end(NOTOK);
378             return sm_ierror("Unable to create a SSL BIO: %s",
379                              ERR_error_string(ERR_get_error(), NULL));
380         }
381
382         BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
383         BIO_push(io, ssl_bio);
384
385         /*
386          * Try doing the handshake now
387          */
388
389         if (BIO_do_handshake(io) < 1) {
390             sm_end(NOTOK);
391             return sm_ierror("Unable to negotiate SSL connection: %s",
392                              ERR_error_string(ERR_get_error(), NULL));
393         }
394
395         if (sm_debug) {
396             const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
397             printf("SSL negotiation successful: %s(%d) %s\n",
398                    SSL_CIPHER_get_name(cipher),
399                    SSL_CIPHER_get_bits(cipher, NULL),
400                    SSL_CIPHER_get_version(cipher));
401
402         }
403
404         tls_active = 1;
405
406         doingEHLO = 1;
407         result = smtalk (SM_HELO, "EHLO %s", client);
408         doingEHLO = 0;
409
410         if (result != 250) {
411             sm_end (NOTOK);
412             return RP_RPLY;
413         }
414     }
415 #else  /* TLS_SUPPORT */
416     NMH_UNUSED (tls);
417 #endif /* TLS_SUPPORT */
418
419 #ifdef CYRUS_SASL
420     /*
421      * If the user asked for SASL, then check to see if the SMTP server
422      * supports it.  Otherwise, error out (because the SMTP server
423      * might have been spoofed; we don't want to just silently not
424      * do authentication
425      */
426
427     if (sasl) {
428         if (! (server_mechs = EHLOset("AUTH"))) {
429             sm_end(NOTOK);
430             return sm_ierror("SMTP server does not support SASL");
431         }
432
433         if (saslmech && stringdex(saslmech, server_mechs) == -1) {
434             sm_end(NOTOK);
435             return sm_ierror("Requested SASL mech \"%s\" is not in the "
436                              "list of supported mechanisms:\n%s",
437                              saslmech, server_mechs);
438         }
439
440         if (sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs,
441                          server) != RP_OK) {
442             sm_end(NOTOK);
443             return NOTOK;
444         }
445     }
446 #endif /* CYRUS_SASL */
447
448 send_options: ;
449     if (watch && EHLOset ("XVRB"))
450         smtalk (SM_HELO, "VERB on");
451     if (queued && EHLOset ("XQUE"))
452         smtalk (SM_HELO, "QUED");
453
454     return RP_OK;
455 }
456
457 int
458 sendmail_init (char *client, char *server, int watch, int verbose,
459                int debug, int queued,
460                int sasl, int saslssf, char *saslmech, char *user)
461 {
462     unsigned int i, result, vecp;
463     int pdi[2], pdo[2];
464     char *vec[15];
465 #ifdef CYRUS_SASL
466     char *server_mechs;
467 #else  /* CYRUS_SASL */
468     NMH_UNUSED (server);
469     NMH_UNUSED (sasl);
470     NMH_UNUSED (saslssf);
471     NMH_UNUSED (saslmech);
472     NMH_UNUSED (user);
473 #endif /* CYRUS_SASL */
474
475     if (watch)
476         verbose = TRUE;
477
478     sm_verbose = verbose;
479     sm_debug = debug;
480     if (sm_rfp != NULL && sm_wfp != NULL)
481         return RP_OK;
482
483     if (client == NULL || *client == '\0') {
484         if (clientname)
485             client = clientname;
486         else
487             client = LocalName(1);      /* no clientname -> LocalName */
488     }
489
490     /*
491      * Last-ditch check just in case client still isn't set to anything
492      */
493
494     if (client == NULL || *client == '\0')
495         client = "localhost";
496
497 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
498     sasl_inbuffer = malloc(SASL_MAXRECVBUF);
499     if (!sasl_inbuffer)
500         return sm_ierror("Unable to allocate %d bytes for read buffer",
501                          SASL_MAXRECVBUF);
502 #endif /* CYRUS_SASL || TLS_SUPPORT */
503
504     if (pipe (pdi) == NOTOK)
505         return sm_ierror ("no pipes");
506     if (pipe (pdo) == NOTOK) {
507         close (pdi[0]);
508         close (pdi[1]);
509         return sm_ierror ("no pipes");
510     }
511
512     for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
513         sleep (5);
514
515     switch (sm_child) {
516         case NOTOK: 
517             close (pdo[0]);
518             close (pdo[1]);
519             close (pdi[0]);
520             close (pdi[1]);
521             return sm_ierror ("unable to fork");
522
523         case OK: 
524             if (pdo[0] != fileno (stdin))
525                 dup2 (pdo[0], fileno (stdin));
526             if (pdi[1] != fileno (stdout))
527                 dup2 (pdi[1], fileno (stdout));
528             if (pdi[1] != fileno (stderr))
529                 dup2 (pdi[1], fileno (stderr));
530             for (i = fileno (stderr) + 1; i < NBITS; i++)
531                 close (i);
532
533             vecp = 0;
534             vec[vecp++] = r1bindex (sendmail, '/');
535             vec[vecp++] = "-bs";
536             vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
537             vec[vecp++] = "-oem";
538             vec[vecp++] = "-om";
539             if (verbose)
540                 vec[vecp++] = "-ov";
541             vec[vecp++] = NULL;
542
543             setgid (getegid ());
544             setuid (geteuid ());
545             execvp (sendmail, vec);
546             fprintf (stderr, "unable to exec ");
547             perror (sendmail);
548             _exit (-1);         /* NOTREACHED */
549
550         default: 
551             SIGNAL (SIGALRM, alrmser);
552             SIGNAL (SIGPIPE, SIG_IGN);
553
554             close (pdi[1]);
555             close (pdo[0]);
556             if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
557                     || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
558                 close (pdi[0]);
559                 close (pdo[1]);
560                 sm_rfp = sm_wfp = NULL;
561                 return sm_ierror ("unable to fdopen");
562             }
563             sm_alarmed = 0;
564             alarm (SM_OPEN);
565             result = smhear ();
566             alarm (0);
567             switch (result) {
568                 case 220: 
569                     break;
570
571                 default: 
572                     sm_end (NOTOK);
573                     return RP_RPLY;
574             }
575
576             doingEHLO = 1;
577             result = smtalk (SM_HELO, "EHLO %s", client);
578             doingEHLO = 0;
579
580             if (500 <= result && result <= 599)
581                 result = smtalk (SM_HELO, "HELO %s", client);
582
583             switch (result) {
584                 case 250:
585                     break;
586
587                 default:
588                     sm_end (NOTOK);
589                     return RP_RPLY;
590             }
591
592 #ifdef CYRUS_SASL
593     /*
594      * If the user asked for SASL, then check to see if the SMTP server
595      * supports it.  Otherwise, error out (because the SMTP server
596      * might have been spoofed; we don't want to just silently not
597      * do authentication
598      */
599
600     if (sasl) {
601         if (! (server_mechs = EHLOset("AUTH"))) {
602             sm_end(NOTOK);
603             return sm_ierror("SMTP server does not support SASL");
604         }
605
606         if (saslmech && stringdex(saslmech, server_mechs) == -1) {
607             sm_end(NOTOK);
608             return sm_ierror("Requested SASL mech \"%s\" is not in the "
609                              "list of supported mechanisms:\n%s",
610                              saslmech, server_mechs);
611         }
612
613         if (sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs,
614                          server) != RP_OK) {
615             sm_end(NOTOK);
616             return NOTOK;
617         }
618     }
619 #endif /* CYRUS_SASL */
620
621             if (watch)
622                 smtalk (SM_HELO, "VERB on");
623
624             return RP_OK;
625     }
626 }
627
628 static int
629 rclient (char *server, char *service)
630 {
631     int sd;
632     char response[BUFSIZ];
633
634     if ((sd = client (server, service, response, sizeof(response),
635                       sm_debug)) != NOTOK)
636         return sd;
637
638     sm_ierror ("%s", response);
639     return NOTOK;
640 }
641
642 int
643 sm_winit (char *from)
644 {
645     switch (smtalk (SM_MAIL, "MAIL FROM:<%s>", from)) {
646         case 250: 
647             sm_addrs = 0;
648             return RP_OK;
649
650         case 500: 
651         case 501: 
652         case 552: 
653             return RP_PARM;
654
655         default: 
656             return RP_RPLY;
657     }
658 }
659
660
661 int
662 sm_wadr (char *mbox, char *host, char *path)
663 {
664     switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
665                                            : "RCPT TO:<%s%s>",
666                              path ? path : "", mbox, host)) {
667         case 250: 
668         case 251: 
669             sm_addrs++;
670             return RP_OK;
671
672         case 451: 
673 #ifdef SENDMAILBUG
674             sm_addrs++;
675             return RP_OK;
676 #endif /* SENDMAILBUG */
677         case 421: 
678         case 450: 
679         case 452: 
680             return RP_NO;
681
682         case 500: 
683         case 501: 
684             return RP_PARM;
685
686         case 550: 
687         case 551: 
688         case 552: 
689         case 553: 
690             return RP_USER;
691
692         default: 
693             return RP_RPLY;
694     }
695 }
696
697
698 int
699 sm_waend (void)
700 {
701     switch (smtalk (SM_DATA, "DATA")) {
702         case 354: 
703             sm_nl = TRUE;
704             return RP_OK;
705
706         case 451: 
707 #ifdef SENDMAILBUG
708             sm_nl = TRUE;
709             return RP_OK;
710 #endif /* SENDMAILBUG */
711         case 421: 
712             return RP_NO;
713
714         case 500: 
715         case 501: 
716         case 503: 
717         case 554: 
718             return RP_NDEL;
719
720         default: 
721             return RP_RPLY;
722     }
723 }
724
725
726 int
727 sm_wtxt (char *buffer, int len)
728 {
729     int result;
730
731     sm_alarmed = 0;
732     alarm (SM_TEXT);
733     result = sm_wstream (buffer, len);
734     alarm (0);
735
736     return (result == NOTOK ? RP_BHST : RP_OK);
737 }
738
739
740 int
741 sm_wtend (void)
742 {
743     if (sm_wstream ((char *) NULL, 0) == NOTOK)
744         return RP_BHST;
745
746     switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
747         case 250: 
748         case 251: 
749             return RP_OK;
750
751         case 451: 
752 #ifdef SENDMAILBUG
753             return RP_OK;
754 #endif /* SENDMAILBUG */
755         case 452: 
756         default: 
757             return RP_NO;
758
759         case 552: 
760         case 554: 
761             return RP_NDEL;
762     }
763 }
764
765
766 int
767 sm_end (int type)
768 {
769     int status;
770     struct smtp sm_note;
771
772     if (sm_mts == MTS_SENDMAIL_SMTP) {
773         switch (sm_child) {
774             case NOTOK: 
775             case OK: 
776                 return RP_OK;
777
778             default: 
779                 break;
780         }
781     }
782
783     if (sm_rfp == NULL && sm_wfp == NULL)
784         return RP_OK;
785
786     switch (type) {
787         case OK: 
788             smtalk (SM_QUIT, "QUIT");
789             break;
790
791         case NOTOK: 
792             sm_note.code = sm_reply.code;
793             sm_note.length = sm_reply.length;
794             memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */
795         case DONE: 
796             if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
797                 return RP_OK;
798             if (sm_mts == MTS_SMTP)
799                 smtalk (SM_QUIT, "QUIT");
800             else {
801                 kill (sm_child, SIGKILL);
802                 discard (sm_rfp);
803                 discard (sm_wfp);
804             }
805             if (type == NOTOK) {
806                 sm_reply.code = sm_note.code;
807                 sm_reply.length = sm_note.length;
808                 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
809             }
810             break;
811     }
812
813 #ifdef TLS_SUPPORT
814     if (tls_active) {
815         BIO_ssl_shutdown(io);
816         BIO_free_all(io);
817     }
818 #endif /* TLS_SUPPORT */
819
820     if (sm_rfp != NULL) {
821         alarm (SM_CLOS);
822         fclose (sm_rfp);
823         alarm (0);
824     }
825     if (sm_wfp != NULL) {
826         alarm (SM_CLOS);
827         fclose (sm_wfp);
828         alarm (0);
829     }
830
831     if (sm_mts == MTS_SMTP) {
832         status = 0;
833 #ifdef CYRUS_SASL
834         if (conn) {
835             sasl_dispose(&conn);
836             if (sasl_outbuffer) {
837                 free(sasl_outbuffer);
838             }
839         }
840         if (sasl_inbuffer)
841             free(sasl_inbuffer);
842 #endif /* CYRUS_SASL */
843     } else {
844         status = pidwait (sm_child, OK);
845         sm_child = NOTOK;
846     }
847
848     sm_rfp = sm_wfp = NULL;
849     return (status ? RP_BHST : RP_OK);
850 }
851
852 #ifdef CYRUS_SASL
853 /*
854  * This function implements SASL authentication for SMTP.  If this function
855  * completes successfully, then authentication is successful and we've
856  * (optionally) negotiated a security layer.
857  */
858 static int
859 sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost)
860 {
861     int result, status;
862     unsigned int buflen, outlen;
863     char *buf, outbuf[BUFSIZ], host[NI_MAXHOST];
864     const char *chosen_mech;
865     sasl_security_properties_t secprops;
866     sasl_ssf_t *ssf;
867     int *outbufmax;
868
869     /*
870      * Initialize the callback contexts
871      */
872
873     if (user == NULL)
874         user = getusername();
875
876     callbacks[SM_SASL_N_CB_USER].context = user;
877     callbacks[SM_SASL_N_CB_AUTHNAME].context = user;
878
879     /*
880      * This is a _bit_ of a hack ... but if the hostname wasn't supplied
881      * to us on the command line, then call getpeername and do a
882      * reverse-address lookup on the IP address to get the name.
883      */
884
885     memset(host, 0, sizeof(host));
886
887     if (!inhost) {
888         struct sockaddr_storage sin;
889         socklen_t len = sizeof(sin);
890         int result;
891
892         if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
893             sm_ierror("getpeername on SMTP socket failed: %s",
894                       strerror(errno));
895             return NOTOK;
896         }
897
898         result = getnameinfo((struct sockaddr *) &sin, len, host, sizeof(host),
899                              NULL, 0, NI_NAMEREQD);
900         if (result != 0) {
901             sm_ierror("Unable to look up name of connected host: %s",
902                       gai_strerror(result));
903             return NOTOK;
904         }
905     } else {
906         strncpy(host, inhost, sizeof(host) - 1);
907     }
908
909     sasl_pw_context[0] = host;
910     sasl_pw_context[1] = user;
911
912     callbacks[SM_SASL_N_CB_PASS].context = sasl_pw_context;
913
914     result = sasl_client_init(callbacks);
915
916     if (result != SASL_OK) {
917         sm_ierror("SASL library initialization failed: %s",
918                   sasl_errstring(result, NULL, NULL));
919         return NOTOK;
920     }
921
922     result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
923
924     if (result != SASL_OK) {
925         sm_ierror("SASL client initialization failed: %s",
926                   sasl_errstring(result, NULL, NULL));
927         return NOTOK;
928     }
929
930     /*
931      * Initialize the security properties.  But if TLS is active, then
932      * don't negotiate encryption here.
933      */
934
935     memset(&secprops, 0, sizeof(secprops));
936     secprops.maxbufsize = SASL_MAXRECVBUF;
937     secprops.max_ssf =
938       tls_active ? 0 : (saslssf != -1 ? (unsigned int) saslssf : UINT_MAX);
939
940     result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
941
942     if (result != SASL_OK) {
943         sm_ierror("SASL security property initialization failed: %s",
944                   sasl_errstring(result, NULL, NULL));
945         return NOTOK;
946     }
947
948     /*
949      * Start the actual protocol.  Feed the mech list into the library
950      * and get out a possible initial challenge
951      */
952
953     result = sasl_client_start(conn, mechlist, NULL, (const char **) &buf,
954                                &buflen, (const char **) &chosen_mech);
955
956     if (result != SASL_OK && result != SASL_CONTINUE) {
957         sm_ierror("SASL client start failed: %s", sasl_errdetail(conn));
958         return NOTOK;
959     }
960
961     /*
962      * If we got an initial challenge, send it as part of the AUTH
963      * command; otherwise, just send a plain AUTH command.
964      */
965
966     if (buflen) {
967         status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
968         if (status != SASL_OK) {
969             sm_ierror("SASL base64 encode failed: %s",
970                       sasl_errstring(status, NULL, NULL));
971             return NOTOK;
972         }
973
974         status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
975     } else
976         status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
977
978     /*
979      * Now we loop until we either fail, get a SASL_OK, or a 235
980      * response code.  Receive the challenges and process them until
981      * we're all done.
982      */
983
984     while (result == SASL_CONTINUE) {
985
986         /*
987          * If we get a 235 response, that means authentication has
988          * succeeded and we need to break out of the loop (yes, even if
989          * we still get SASL_CONTINUE from sasl_client_step()).
990          *
991          * Otherwise, if we get a message that doesn't seem to be a
992          * valid response, then abort
993          */
994
995         if (status == 235)
996             break;
997         else if (status < 300 || status > 399)
998             return RP_BHST;
999         
1000         /*
1001          * Special case; a zero-length response from the SMTP server
1002          * is returned as a single =.  If we get that, then set buflen
1003          * to be zero.  Otherwise, just decode the response.
1004          */
1005         
1006         if (strcmp("=", sm_reply.text) == 0) {
1007             outlen = 0;
1008         } else {
1009             result = sasl_decode64(sm_reply.text, sm_reply.length,
1010                                    outbuf, sizeof(outbuf), &outlen);
1011         
1012             if (result != SASL_OK) {
1013                 smtalk(SM_AUTH, "*");
1014                 sm_ierror("SASL base64 decode failed: %s",
1015                           sasl_errstring(result, NULL, NULL));
1016                 return NOTOK;
1017             }
1018         }
1019
1020         result = sasl_client_step(conn, outbuf, outlen, NULL,
1021                                   (const char **) &buf, &buflen);
1022
1023         if (result != SASL_OK && result != SASL_CONTINUE) {
1024             smtalk(SM_AUTH, "*");
1025             sm_ierror("SASL client negotiation failed: %s",
1026                       sasl_errstring(result, NULL, NULL));
1027             return NOTOK;
1028         }
1029
1030         status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1031
1032         if (status != SASL_OK) {
1033             smtalk(SM_AUTH, "*");
1034             sm_ierror("SASL base64 encode failed: %s",
1035                       sasl_errstring(status, NULL, NULL));
1036             return NOTOK;
1037         }
1038         
1039         status = smtalk(SM_AUTH, outbuf);
1040     }
1041
1042     /*
1043      * Make sure that we got the correct response
1044      */
1045
1046     if (status < 200 || status > 299)
1047         return RP_BHST;
1048
1049     /*
1050      * We _should_ have completed the authentication successfully.
1051      * Get a few properties from the authentication exchange.
1052      */
1053
1054     result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
1055
1056     if (result != SASL_OK) {
1057         sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
1058                   sasl_errstring(result, NULL, NULL));
1059         return NOTOK;
1060     }
1061
1062     maxoutbuf = *outbufmax;
1063
1064     result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
1065
1066     sasl_ssf = *ssf;
1067
1068     if (result != SASL_OK) {
1069         sm_ierror("Cannot retrieve SASL negotiated security strength "
1070                   "factor: %s", sasl_errstring(result, NULL, NULL));
1071         return NOTOK;
1072     }
1073
1074     if (sasl_ssf > 0) {
1075         sasl_outbuffer = malloc(maxoutbuf);
1076
1077         if (sasl_outbuffer == NULL) {
1078                 sm_ierror("Unable to allocate %d bytes for SASL output "
1079                           "buffer", maxoutbuf);
1080                 return NOTOK;
1081         }
1082         sasl_outbuflen = 0;
1083         sasl_inbuflen = 0;
1084         sasl_inptr = sasl_inbuffer;
1085     } else {
1086         sasl_outbuffer = NULL;
1087         /* Don't NULL out sasl_inbuffer because it could be used in
1088            sm_fgetc (). */
1089     }
1090
1091     sasl_complete = 1;
1092
1093     return RP_OK;
1094 }
1095
1096 /*
1097  * Our callback functions to feed data to the SASL library
1098  */
1099
1100 static int
1101 sm_get_user(void *context, int id, const char **result, unsigned *len)
1102 {
1103     char *user = (char *) context;
1104
1105     if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
1106         return SASL_BADPARAM;
1107
1108     *result = user;
1109     if (len)
1110         *len = strlen(user);
1111
1112     return SASL_OK;
1113 }
1114
1115 static int
1116 sm_get_pass(sasl_conn_t *conn, void *context, int id,
1117             sasl_secret_t **psecret)
1118 {
1119     char **pw_context = (char **) context;
1120     char *pass = NULL;
1121     int len;
1122
1123     NMH_UNUSED (conn);
1124
1125     if (! psecret || id != SASL_CB_PASS)
1126         return SASL_BADPARAM;
1127
1128     ruserpass(pw_context[0], &(pw_context[1]), &pass);
1129
1130     len = strlen(pass);
1131
1132     *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
1133
1134     if (! *psecret) {
1135         free(pass);
1136         return SASL_NOMEM;
1137     }
1138
1139     (*psecret)->len = len;
1140     strcpy((char *) (*psecret)->data, pass);
1141 /*    free(pass); */
1142
1143     return SASL_OK;
1144 }
1145 #endif /* CYRUS_SASL */
1146
1147 static int
1148 sm_ierror (char *fmt, ...)
1149 {
1150     va_list ap;
1151
1152     va_start(ap, fmt);
1153     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1154     va_end(ap);
1155
1156     sm_reply.length = strlen (sm_reply.text);
1157     sm_reply.code = NOTOK;
1158
1159     return RP_BHST;
1160 }
1161
1162 static int
1163 smtalk (int time, char *fmt, ...)
1164 {
1165     va_list ap;
1166     int result;
1167     char buffer[BUFSIZ];
1168
1169     va_start(ap, fmt);
1170     vsnprintf (buffer, sizeof(buffer), fmt, ap);
1171     va_end(ap);
1172
1173     if (sm_debug) {
1174         if (sasl_ssf)
1175                 printf("(sasl-encrypted) ");
1176         if (tls_active)
1177                 printf("(tls-encrypted) ");
1178         printf ("=> %s\n", buffer);
1179         fflush (stdout);
1180     }
1181
1182     sm_alarmed = 0;
1183     alarm ((unsigned) time);
1184     if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
1185         result = smhear ();
1186     alarm (0);
1187
1188     return result;
1189 }
1190
1191
1192 /*
1193  * write the buffer to the open SMTP channel
1194  */
1195
1196 static int
1197 sm_wrecord (char *buffer, int len)
1198 {
1199     if (sm_wfp == NULL)
1200         return sm_werror ();
1201
1202     sm_fwrite (buffer, len);
1203     sm_fputs ("\r\n");
1204     sm_fflush ();
1205
1206     return (ferror (sm_wfp) ? sm_werror () : OK);
1207 }
1208
1209
1210 static int
1211 sm_wstream (char *buffer, int len)
1212 {
1213     char  *bp;
1214     static char lc = '\0';
1215
1216     if (sm_wfp == NULL)
1217         return sm_werror ();
1218
1219     if (buffer == NULL && len == 0) {
1220         if (lc != '\n')
1221             sm_fputs ("\r\n");
1222         lc = '\0';
1223         return (ferror (sm_wfp) ? sm_werror () : OK);
1224     }
1225
1226     for (bp = buffer; len > 0; bp++, len--) {
1227         switch (*bp) {
1228             case '\n': 
1229                 sm_nl = TRUE;
1230                 sm_fputc ('\r');
1231                 break;
1232
1233             case '.': 
1234                 if (sm_nl)
1235                     sm_fputc ('.');/* FALL THROUGH */
1236             default: 
1237                 sm_nl = FALSE;
1238         }
1239         sm_fputc (*bp);
1240         if (ferror (sm_wfp))
1241             return sm_werror ();
1242     }
1243
1244     if (bp > buffer)
1245         lc = *--bp;
1246     return (ferror (sm_wfp) ? sm_werror () : OK);
1247 }
1248
1249 /*
1250  * Write out to the network, but do buffering for SASL (if enabled)
1251  */
1252
1253 static int
1254 sm_fwrite(char *buffer, int len)
1255 {
1256 #ifdef CYRUS_SASL
1257     const char *output;
1258     unsigned int outputlen;
1259
1260     if (sasl_complete == 0 || sasl_ssf == 0) {
1261 #endif /* CYRUS_SASL */
1262 #ifdef TLS_SUPPORT
1263         if (tls_active) {
1264             int ret;
1265
1266             ret = BIO_write(io, buffer, len);
1267
1268             if (ret <= 0) {
1269                 sm_ierror("TLS error during write: %s",
1270                           ERR_error_string(ERR_get_error(), NULL));
1271                 return NOTOK;
1272             }
1273         } else
1274 #endif /* TLS_SUPPORT */
1275         fwrite(buffer, sizeof(*buffer), len, sm_wfp);
1276 #ifdef CYRUS_SASL
1277     } else {
1278         while (len >= maxoutbuf - sasl_outbuflen) {
1279             memcpy(sasl_outbuffer + sasl_outbuflen, buffer,
1280                    maxoutbuf - sasl_outbuflen);
1281             len -= maxoutbuf - sasl_outbuflen;
1282             sasl_outbuflen = 0;
1283
1284             if (sasl_encode(conn, sasl_outbuffer, maxoutbuf,
1285                             &output, &outputlen) != SASL_OK) {
1286                 sm_ierror("Unable to SASL encode connection data: %s",
1287                           sasl_errdetail(conn));
1288                 return NOTOK;
1289             }
1290
1291             fwrite(output, sizeof(*output), outputlen, sm_wfp);
1292         }
1293
1294         if (len > 0) {
1295             memcpy(sasl_outbuffer + sasl_outbuflen, buffer, len);
1296             sasl_outbuflen += len;
1297         }
1298     }
1299 #endif /* CYRUS_SASL */
1300     return ferror(sm_wfp) ? NOTOK : RP_OK;
1301 }
1302
1303 /*
1304  * Convenience functions to replace occurences of fputs() and fputc()
1305  */
1306
1307 static int
1308 sm_fputs(char *buffer)
1309 {
1310     return sm_fwrite(buffer, strlen(buffer));
1311 }
1312
1313 static int
1314 sm_fputc(int c)
1315 {
1316     char h = c;
1317
1318     return sm_fwrite(&h, 1);
1319 }
1320
1321 /*
1322  * Flush out any pending data on the connection
1323  */
1324
1325 static void
1326 sm_fflush(void)
1327 {
1328 #ifdef CYRUS_SASL
1329     const char *output;
1330     unsigned int outputlen;
1331     int result;
1332
1333     if (sasl_complete == 1 && sasl_ssf > 0 && sasl_outbuflen > 0) {
1334         result = sasl_encode(conn, sasl_outbuffer, sasl_outbuflen,
1335                              &output, &outputlen);
1336         if (result != SASL_OK) {
1337             sm_ierror("Unable to SASL encode connection data: %s",
1338                       sasl_errdetail(conn));
1339             return;
1340         }
1341
1342         fwrite(output, sizeof(*output), outputlen, sm_wfp);
1343         sasl_outbuflen = 0;
1344     }
1345 #endif /* CYRUS_SASL */
1346
1347 #ifdef TLS_SUPPORT
1348     if (tls_active) {
1349         (void) BIO_flush(io);
1350     }
1351 #endif /* TLS_SUPPORT */
1352
1353     fflush(sm_wfp);
1354 }
1355
1356 static int
1357 sm_werror (void)
1358 {
1359     sm_reply.length =
1360         strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1361             : sm_alarmed ? "write to socket timed out"
1362             : "error writing to socket"));
1363
1364     return (sm_reply.code = NOTOK);
1365 }
1366
1367
1368 static int
1369 smhear (void)
1370 {
1371     int i, code, cont, bc = 0, rc, more;
1372     unsigned char *bp;
1373     char *rp;
1374     char **ehlo = NULL, buffer[BUFSIZ];
1375
1376     if (doingEHLO) {
1377         static int at_least_once = 0;
1378
1379         if (at_least_once) {
1380             char *ep;
1381
1382             for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1383                 ep = *ehlo;
1384                 free (ep);
1385             }
1386         } else {
1387             at_least_once = 1;
1388         }
1389
1390         ehlo = EHLOkeys;
1391         *ehlo = NULL;
1392     }
1393
1394 again: ;
1395
1396     sm_reply.length = 0;
1397     sm_reply.text[0] = 0;
1398     rp = sm_reply.text;
1399     rc = sizeof(sm_reply.text) - 1;
1400
1401     for (more = FALSE; sm_rrecord ((char *) (bp = (unsigned char *) buffer),
1402                                    &bc) != NOTOK ; ) {
1403         if (sm_debug) {
1404             if (sasl_ssf > 0)
1405                 printf("(sasl-decrypted) ");
1406             if (tls_active)
1407                 printf("(tls-decrypted) ");
1408             printf ("<= %s\n", buffer);
1409             fflush (stdout);
1410         }
1411
1412         if (doingEHLO
1413                 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1414                 && (buffer[3] == '-' || doingEHLO == 2)
1415                 && buffer[4]) {
1416             if (doingEHLO == 2) {
1417                 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1418                     strcpy (*ehlo++, buffer + 4);
1419                     *ehlo = NULL;
1420                     if (ehlo >= EHLOkeys + MAXEHLO)
1421                         doingEHLO = 0;
1422                 }
1423                 else
1424                     doingEHLO = 0;
1425             }
1426             else
1427                 doingEHLO = 2;
1428         }
1429
1430         for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1431             continue;
1432
1433         cont = FALSE;
1434         code = atoi ((char *) bp);
1435         bp += 3, bc -= 3;
1436         for (; bc > 0 && isspace (*bp); bp++, bc--)
1437             continue;
1438         if (bc > 0 && *bp == '-') {
1439             cont = TRUE;
1440             bp++, bc--;
1441             for (; bc > 0 && isspace (*bp); bp++, bc--)
1442                 continue;
1443         }
1444
1445         if (more) {
1446             if (code != sm_reply.code || cont)
1447                 continue;
1448             more = FALSE;
1449         } else {
1450             sm_reply.code = code;
1451             more = cont;
1452             if (bc <= 0) {
1453                 /* can never fail to 0-terminate because of size of buffer vs fixed string */
1454                 strncpy (buffer, sm_noreply, sizeof(buffer));
1455                 bp = (unsigned char *) buffer;
1456                 bc = strlen (sm_noreply);
1457             }
1458         }
1459
1460         if ((i = min (bc, rc)) > 0) {
1461             memcpy (rp, bp, i);
1462             rp += i;
1463             rc -= i;
1464             i = strlen(sm_moreply);
1465             if (more && rc > i + 1) {
1466                 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
1467                 rp += i;
1468                 rc -= i;
1469             }
1470         }
1471         if (more)
1472             continue;
1473         if (sm_reply.code < 100) {
1474             if (sm_verbose) {
1475                 printf ("%s\n", sm_reply.text);
1476                 fflush (stdout);
1477             }
1478             goto again;
1479         }
1480
1481         sm_reply.length = rp - sm_reply.text;
1482         sm_reply.text[sm_reply.length] = 0;
1483         return sm_reply.code;
1484     }
1485     return NOTOK;
1486 }
1487
1488
1489 static int
1490 sm_rrecord (char *buffer, int *len)
1491 {
1492     int retval;
1493
1494     if (sm_rfp == NULL)
1495         return sm_rerror(0);
1496
1497     buffer[*len = 0] = 0;
1498
1499     if ((retval = sm_fgets (buffer, BUFSIZ, sm_rfp)) != RP_OK)
1500         return sm_rerror (retval);
1501     *len = strlen (buffer);
1502     /* *len should be >0 except on EOF, but check for safety's sake */
1503     if (*len == 0)
1504         return sm_rerror (RP_EOF);
1505     if (buffer[*len - 1] != '\n')
1506         while ((retval = sm_fgetc (sm_rfp)) != '\n' && retval != EOF &&
1507                retval != -2)
1508             continue;
1509     else
1510         if ((*len > 1) && (buffer[*len - 2] == '\r'))
1511             *len -= 1;
1512     *len -= 1;
1513     buffer[*len] = 0;
1514
1515     return OK;
1516 }
1517
1518 /*
1519  * Our version of fgets, which calls our private fgetc function
1520  */
1521
1522 static int
1523 sm_fgets(char *buffer, int size, FILE *f)
1524 {
1525     int c;
1526
1527      do {
1528         c = sm_fgetc(f);
1529
1530         if (c == EOF)
1531             return RP_EOF;
1532
1533         if (c == -2)
1534             return NOTOK;
1535
1536         *buffer++ = c;
1537      } while (size > 1 && c != '\n');
1538
1539      *buffer = '\0';
1540
1541      return RP_OK;
1542 }
1543
1544
1545 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
1546 /*
1547  * Read from the network, but do SASL or TLS encryption
1548  */
1549
1550 static int
1551 sm_fgetc(FILE *f)
1552 {
1553     char tmpbuf[BUFSIZ], *retbuf;
1554     unsigned int retbufsize = 0;
1555     int cc, result;
1556
1557     /*
1558      * If we have leftover data, return it
1559      */
1560
1561     if (sasl_inbuflen) {
1562         sasl_inbuflen--;
1563         return (int) *sasl_inptr++;
1564     }
1565
1566     /*
1567      * If not, read from the network until we have some data to return
1568      */
1569
1570     while (retbufsize == 0) {
1571
1572 #ifdef TLS_SUPPORT
1573         if (tls_active) {
1574             cc = SSL_read(ssl, tmpbuf, sizeof(tmpbuf));
1575
1576             if (cc == 0) {
1577                 result = SSL_get_error(ssl, cc);
1578
1579                 if (result != SSL_ERROR_ZERO_RETURN) {
1580                     sm_ierror("TLS peer aborted connection");
1581                 }
1582
1583                 return EOF;
1584             }
1585
1586             if (cc < 0) {
1587                 sm_ierror("SSL_read failed: %s",
1588                           ERR_error_string(ERR_get_error(), NULL));
1589                 return -2;
1590             }
1591         } else
1592 #endif /* TLS_SUPPORT */
1593
1594         cc = read(fileno(f), tmpbuf, sizeof(tmpbuf));
1595
1596         if (cc == 0)
1597             return EOF;
1598
1599         if (cc < 0) {
1600             sm_ierror("Unable to read from network: %s", strerror(errno));
1601             return -2;
1602         }
1603
1604         /*
1605          * Don't call sasl_decode unless sasl is complete and we have
1606          * encryption working
1607          */
1608
1609 #ifdef CYRUS_SASL
1610         if (sasl_complete == 0 || sasl_ssf == 0) {
1611             retbuf = tmpbuf;
1612             retbufsize = cc;
1613         } else {
1614             result = sasl_decode(conn, tmpbuf, cc, (const char **) &retbuf,
1615                                  &retbufsize);
1616
1617             if (result != SASL_OK) {
1618                 sm_ierror("Unable to decode SASL network data: %s",
1619                           sasl_errdetail(conn));
1620                 return -2;
1621             }
1622         }
1623 #else /* ! CYRUS_SASL */
1624         retbuf = tmpbuf;
1625         retbufsize = cc;
1626 #endif /* CYRUS_SASL */
1627     }
1628
1629     if (retbufsize > SASL_MAXRECVBUF) {
1630         sm_ierror("Received data (%d bytes) is larger than the buffer "
1631                   "size (%d bytes)", retbufsize, SASL_MAXRECVBUF);
1632         return -2;
1633     }
1634
1635     memcpy(sasl_inbuffer, retbuf, retbufsize);
1636     sasl_inptr = sasl_inbuffer + 1;
1637     sasl_inbuflen = retbufsize - 1;
1638
1639     return (int) sasl_inbuffer[0];
1640 }
1641 #endif /* CYRUS_SASL || TLS_SUPPORT */
1642
1643 static int
1644 sm_rerror (int rc)
1645 {
1646     if (sm_mts == MTS_SMTP)
1647         sm_reply.length =
1648             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1649                 : sm_alarmed ? "read from socket timed out"
1650                 : rc == RP_EOF ? "premature end-of-file on socket"
1651                 : "error reading from socket"));
1652     else
1653         sm_reply.length =
1654             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
1655                 : sm_alarmed ? "read from pipe timed out"
1656                 : rc == RP_EOF ? "premature end-of-file on pipe"
1657                 : "error reading from pipe"));
1658
1659     return (sm_reply.code = NOTOK);
1660 }
1661
1662
1663 static void
1664 alrmser (int i)
1665 {
1666     NMH_UNUSED (i);
1667
1668 #ifndef RELIABLE_SIGNALS
1669     SIGNAL (SIGALRM, alrmser);
1670 #endif
1671
1672     sm_alarmed++;
1673     if (sm_debug) {
1674         printf ("timed out...\n");
1675         fflush (stdout);
1676     }
1677 }
1678
1679
1680 char *
1681 rp_string (int code)
1682 {
1683     char *text;
1684     static char buffer[BUFSIZ];
1685
1686     switch (sm_reply.code != NOTOK ? code : NOTOK) {
1687         case RP_AOK:
1688             text = "AOK";
1689             break;
1690
1691         case RP_MOK:
1692             text = "MOK";
1693             break;
1694
1695         case RP_OK: 
1696             text = "OK";
1697             break;
1698
1699         case RP_RPLY: 
1700             text = "RPLY";
1701             break;
1702
1703         case RP_BHST: 
1704         default: 
1705             text = "BHST";
1706             snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1707             return buffer;
1708
1709         case RP_PARM: 
1710             text = "PARM";
1711             break;
1712
1713         case RP_NO: 
1714             text = "NO";
1715             break;
1716
1717         case RP_USER: 
1718             text = "USER";
1719             break;
1720
1721         case RP_NDEL: 
1722             text = "NDEL";
1723             break;
1724     }
1725
1726     snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1727                 text, sm_reply.code, sm_reply.text);
1728     return buffer;
1729 }
1730
1731 static char *
1732 EHLOset (char *s)
1733 {
1734     size_t len;
1735     char *ep, **ehlo;
1736
1737     len = strlen (s);
1738
1739     for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1740         ep = *ehlo;
1741         if (strncmp (ep, s, len) == 0) {
1742             for (ep += len; *ep == ' '; ep++)
1743                 continue;
1744             return ep;
1745         }
1746     }
1747
1748     return 0;
1749 }