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