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