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