]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
Added MH(7) man page, as an alias of nmh(7).
[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_child = NOTOK;
65 static int sm_debug = 0;
66 static int sm_nl = TRUE;
67 static int sm_verbose = 0;
68 static netsec_context *nsc = NULL;
69
70 static char *sm_noreply = "No reply text given";
71 static char *sm_moreply = "; ";
72 static struct smtp sm_reply;
73
74 #define MAXEHLO 20
75
76 static int doingEHLO;
77 static char *EHLOkeys[MAXEHLO + 1];
78
79 /*
80 * static prototypes
81 */
82 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
83 const char *, const char *, int);
84 static int sendmail_init (char *, int, int, int, int, const char *,
85 const char *);
86
87 static int rclient (char *, char *);
88 static int sm_ierror (const char *fmt, ...);
89 static int sm_nerror (char *);
90 static int smtalk (int time, char *fmt, ...);
91 static int sm_wstream (char *, int);
92 static int smhear (void);
93 static char *EHLOset (char *);
94 static int sm_sasl_callback(enum sasl_message_type, unsigned const char *,
95 unsigned int, unsigned char **, unsigned int *,
96 char **);
97
98 int
99 sm_init (char *client, char *server, char *port, int watch, int verbose,
100 int debug, int sasl, const char *saslmech, const char *user,
101 const char *oauth_svc, int tls)
102 {
103 if (sm_mts == MTS_SMTP)
104 return smtp_init (client, server, port, watch, verbose,
105 debug, sasl, saslmech, user, oauth_svc, tls);
106 else
107 return sendmail_init (client, watch, verbose, debug, sasl,
108 saslmech, user);
109 }
110
111 static int
112 smtp_init (char *client, char *server, char *port, int watch, int verbose,
113 int debug, int sasl, const char *saslmech, const char *user,
114 const char *oauth_svc, int tls)
115 {
116 int result, sd1;
117 char *errstr;
118
119 if (watch)
120 verbose = TRUE;
121
122 sm_verbose = verbose;
123 sm_debug = debug;
124
125 if (nsc != NULL)
126 goto send_options;
127
128 if (client == NULL || *client == '\0') {
129 if (clientname) {
130 client = clientname;
131 } else {
132 client = LocalName(1); /* no clientname -> LocalName */
133 }
134 }
135
136 /*
137 * Last-ditch check just in case client still isn't set to anything
138 */
139
140 if (client == NULL || *client == '\0')
141 client = "localhost";
142
143 nsc = netsec_init();
144
145 if (user)
146 netsec_set_userid(nsc, user);
147
148 if (sm_debug)
149 netsec_set_snoop(nsc, 1);
150
151 if (sasl) {
152 if (netsec_set_sasl_params(nsc, server, "smtp", saslmech,
153 sm_sasl_callback, &errstr) != OK)
154 return sm_nerror(errstr);
155 }
156
157 if (oauth_svc) {
158 if (netsec_set_oauth_service(nsc, oauth_svc) != OK)
159 return sm_ierror("OAuth2 not supported");
160 }
161
162 if ((sd1 = rclient (server, port)) == NOTOK)
163 return RP_BHST;
164
165 SIGNAL (SIGPIPE, SIG_IGN);
166
167 netsec_set_fd(nsc, sd1, sd1);
168
169 if (tls) {
170 if (netsec_set_tls(nsc, 1, &errstr) != OK)
171 return sm_nerror(errstr);
172 }
173
174 /*
175 * If tls == 2, that means that the user requested "initial" TLS,
176 * which happens right after the connection has opened. Do that
177 * negotiation now
178 */
179
180 if (tls == 2) {
181 if (netsec_negotiate_tls(nsc, &errstr) != OK) {
182 sm_end(NOTOK);
183 return sm_nerror(errstr);
184 }
185 }
186
187 netsec_set_timeout(nsc, SM_OPEN);
188 result = smhear ();
189
190 switch (result) {
191 case 220:
192 break;
193
194 default:
195 sm_end (NOTOK);
196 return RP_RPLY;
197 }
198
199 /*
200 * Give EHLO or HELO command
201 */
202
203 doingEHLO = 1;
204 result = smtalk (SM_HELO, "EHLO %s", client);
205 doingEHLO = 0;
206
207 if (result >= 500 && result <= 599)
208 result = smtalk (SM_HELO, "HELO %s", client);
209
210 if (result != 250) {
211 sm_end (NOTOK);
212 return RP_RPLY;
213 }
214
215 /*
216 * If the user requested TLS support, then try to do the STARTTLS command
217 * as part of the initial dialog. Assuming this works, we then need to
218 * restart the EHLO dialog after TLS negotiation is complete.
219 */
220
221 if (tls == 1) {
222 if (! EHLOset("STARTTLS")) {
223 sm_end(NOTOK);
224 return sm_ierror("SMTP server does not support TLS");
225 }
226
227 result = smtalk(SM_HELO, "STARTTLS");
228
229 if (result != 220) {
230 sm_end(NOTOK);
231 return RP_RPLY;
232 }
233
234 /*
235 * Okay, the other side should be waiting for us to start TLS
236 * negotiation. Oblige them.
237 */
238
239 if (netsec_negotiate_tls(nsc, &errstr) != OK) {
240 sm_end(NOTOK);
241 return sm_nerror(errstr);
242 }
243
244 doingEHLO = 1;
245 result = smtalk (SM_HELO, "EHLO %s", client);
246 doingEHLO = 0;
247
248 if (result != 250) {
249 sm_end (NOTOK);
250 return RP_RPLY;
251 }
252 }
253
254 /*
255 * If the user asked for SASL, then check to see if the SMTP server
256 * supports it. Otherwise, error out (because the SMTP server
257 * might have been spoofed; we don't want to just silently not
258 * do authentication
259 */
260
261 if (sasl) {
262 char *server_mechs;
263 if (! (server_mechs = EHLOset("AUTH"))) {
264 sm_end(NOTOK);
265 return sm_ierror("SMTP server does not support SASL");
266 }
267
268 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
269 sm_end(NOTOK);
270 return sm_nerror(errstr);
271 }
272 }
273
274 send_options: ;
275 if (watch && EHLOset ("XVRB"))
276 smtalk (SM_HELO, "VERB on");
277
278 return RP_OK;
279 }
280
281 int
282 sendmail_init (char *client, int watch, int verbose, int debug, int sasl,
283 const char *saslmech, const char *user)
284 {
285 unsigned int i, result, vecp;
286 int pdi[2], pdo[2];
287 char *vec[15], *errstr;
288
289 if (watch)
290 verbose = TRUE;
291
292 sm_verbose = verbose;
293 sm_debug = debug;
294 if (nsc)
295 return RP_OK;
296
297 if (client == NULL || *client == '\0') {
298 if (clientname)
299 client = clientname;
300 else
301 client = LocalName(1); /* no clientname -> LocalName */
302 }
303
304 /*
305 * Last-ditch check just in case client still isn't set to anything
306 */
307
308 if (client == NULL || *client == '\0')
309 client = "localhost";
310
311 nsc = netsec_init();
312
313 if (user)
314 netsec_set_userid(nsc, user);
315
316 if (sm_debug)
317 netsec_set_snoop(nsc, 1);
318
319 if (sasl) {
320 if (netsec_set_sasl_params(nsc, client, "smtp", saslmech,
321 sm_sasl_callback, &errstr) != OK)
322 return sm_nerror(errstr);
323 }
324
325 if (pipe (pdi) == NOTOK)
326 return sm_ierror ("no pipes");
327 if (pipe (pdo) == NOTOK) {
328 close (pdi[0]);
329 close (pdi[1]);
330 return sm_ierror ("no pipes");
331 }
332
333 for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
334 sleep (5);
335
336 switch (sm_child) {
337 case NOTOK:
338 close (pdo[0]);
339 close (pdo[1]);
340 close (pdi[0]);
341 close (pdi[1]);
342 return sm_ierror ("unable to fork");
343
344 case OK:
345 if (pdo[0] != fileno (stdin))
346 dup2 (pdo[0], fileno (stdin));
347 if (pdi[1] != fileno (stdout))
348 dup2 (pdi[1], fileno (stdout));
349 if (pdi[1] != fileno (stderr))
350 dup2 (pdi[1], fileno (stderr));
351 for (i = fileno (stderr) + 1; i < NBITS; i++)
352 close (i);
353
354 vecp = 0;
355 vec[vecp++] = r1bindex (sendmail, '/');
356 vec[vecp++] = "-bs";
357 vec[vecp++] = watch ? "-odi" : "-odb";
358 vec[vecp++] = "-oem";
359 vec[vecp++] = "-om";
360 if (verbose)
361 vec[vecp++] = "-ov";
362 vec[vecp++] = NULL;
363
364 execvp (sendmail, vec);
365 fprintf (stderr, "unable to exec ");
366 perror (sendmail);
367 _exit (-1); /* NOTREACHED */
368
369 default:
370 SIGNAL (SIGPIPE, SIG_IGN);
371
372 close (pdi[1]);
373 close (pdo[0]);
374
375 netsec_set_fd(nsc, pdi[i], pdo[1]);
376 netsec_set_timeout(nsc, SM_OPEN);
377 result = smhear ();
378 switch (result) {
379 case 220:
380 break;
381
382 default:
383 sm_end (NOTOK);
384 return RP_RPLY;
385 }
386
387 doingEHLO = 1;
388 result = smtalk (SM_HELO, "EHLO %s", client);
389 doingEHLO = 0;
390
391 if (500 <= result && result <= 599)
392 result = smtalk (SM_HELO, "HELO %s", client);
393
394 switch (result) {
395 case 250:
396 break;
397
398 default:
399 sm_end (NOTOK);
400 return RP_RPLY;
401 }
402
403 /*
404 * If the user asked for SASL, then check to see if the SMTP server
405 * supports it. Otherwise, error out (because the SMTP server
406 * might have been spoofed; we don't want to just silently not
407 * do authentication
408 */
409
410 if (sasl) {
411 char *server_mechs;
412 if (! (server_mechs = EHLOset("AUTH"))) {
413 sm_end(NOTOK);
414 return sm_ierror("SMTP server does not support SASL");
415 }
416 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
417 sm_end(NOTOK);
418 return sm_nerror(errstr);
419 }
420 }
421
422 if (watch)
423 smtalk (SM_HELO, "VERB on");
424
425 return RP_OK;
426 }
427 }
428
429 static int
430 rclient (char *server, char *service)
431 {
432 int sd;
433 char response[BUFSIZ];
434
435 if ((sd = client (server, service, response, sizeof(response),
436 sm_debug)) != NOTOK)
437 return sd;
438
439 sm_ierror ("%s", response);
440 return NOTOK;
441 }
442
443 int
444 sm_winit (char *from)
445 {
446 switch (smtalk (SM_MAIL, "MAIL FROM:<%s>", from)) {
447 case 250:
448 sm_addrs = 0;
449 return RP_OK;
450
451 case 500:
452 case 501:
453 case 552:
454 return RP_PARM;
455
456 default:
457 return RP_RPLY;
458 }
459 }
460
461
462 int
463 sm_wadr (char *mbox, char *host, char *path)
464 {
465 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
466 : "RCPT TO:<%s%s>",
467 path ? path : "", mbox, host)) {
468 case 250:
469 case 251:
470 sm_addrs++;
471 return RP_OK;
472
473 case 451:
474 #ifdef SENDMAILBUG
475 sm_addrs++;
476 return RP_OK;
477 #endif /* SENDMAILBUG */
478 case 421:
479 case 450:
480 case 452:
481 return RP_NO;
482
483 case 500:
484 case 501:
485 return RP_PARM;
486
487 case 550:
488 case 551:
489 case 552:
490 case 553:
491 return RP_USER;
492
493 default:
494 return RP_RPLY;
495 }
496 }
497
498
499 int
500 sm_waend (void)
501 {
502 switch (smtalk (SM_DATA, "DATA")) {
503 case 354:
504 sm_nl = TRUE;
505 return RP_OK;
506
507 case 451:
508 #ifdef SENDMAILBUG
509 sm_nl = TRUE;
510 return RP_OK;
511 #endif /* SENDMAILBUG */
512 case 421:
513 return RP_NO;
514
515 case 500:
516 case 501:
517 case 503:
518 case 554:
519 return RP_NDEL;
520
521 default:
522 return RP_RPLY;
523 }
524 }
525
526
527 int
528 sm_wtxt (char *buffer, int len)
529 {
530 int result;
531
532 result = sm_wstream (buffer, len);
533
534 return (result == NOTOK ? RP_BHST : RP_OK);
535 }
536
537
538 int
539 sm_wtend (void)
540 {
541 if (sm_wstream ((char *) NULL, 0) == NOTOK)
542 return RP_BHST;
543
544 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
545 case 250:
546 case 251:
547 return RP_OK;
548
549 case 451:
550 #ifdef SENDMAILBUG
551 return RP_OK;
552 #endif /* SENDMAILBUG */
553 case 452:
554 default:
555 return RP_NO;
556
557 case 552:
558 case 554:
559 return RP_NDEL;
560 }
561 }
562
563
564 int
565 sm_end (int type)
566 {
567 int status;
568 struct smtp sm_note;
569
570 if (sm_mts == MTS_SENDMAIL_SMTP) {
571 switch (sm_child) {
572 case NOTOK:
573 case OK:
574 return RP_OK;
575
576 default:
577 break;
578 }
579 }
580
581 if (nsc == NULL)
582 return RP_OK;
583
584 switch (type) {
585 case OK:
586 smtalk (SM_QUIT, "QUIT");
587 break;
588
589 case NOTOK:
590 sm_note.code = sm_reply.code;
591 sm_note.length = sm_reply.length;
592 memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */
593 case DONE:
594 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
595 return RP_OK;
596 if (sm_mts == MTS_SMTP)
597 smtalk (SM_QUIT, "QUIT");
598 else {
599 /* The SIGPIPE block replaces old calls to discard ().
600 We're not sure what the discard () calls were for,
601 maybe to prevent deadlock on old systems. In any
602 case, blocking SIGPIPE should be harmless.
603 Because the file handles are closed below, leave it
604 blocked. */
605 sigset_t set, oset;
606 sigemptyset (&set);
607 sigaddset (&set, SIGPIPE);
608 sigprocmask (SIG_BLOCK, &set, &oset);
609
610 kill (sm_child, SIGKILL);
611 sm_child = NOTOK;
612 }
613 if (type == NOTOK) {
614 sm_reply.code = sm_note.code;
615 sm_reply.length = sm_note.length;
616 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
617 }
618 break;
619 }
620
621 if (nsc != NULL) {
622 netsec_shutdown(nsc, 1);
623 nsc = NULL;
624 }
625
626 if (sm_mts == MTS_SMTP) {
627 status = 0;
628 } else if (sm_child != NOTOK) {
629 status = pidwait (sm_child, OK);
630 sm_child = NOTOK;
631 } else {
632 status = OK;
633 }
634
635 return (status ? RP_BHST : RP_OK);
636 }
637
638
639 static int
640 sm_ierror (const char *fmt, ...)
641 {
642 va_list ap;
643
644 va_start(ap, fmt);
645 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
646 va_end(ap);
647
648 sm_reply.length = strlen (sm_reply.text);
649 sm_reply.code = NOTOK;
650
651 return RP_BHST;
652 }
653
654 /*
655 * Like sm_ierror, but assume it's an allocated error string we need to free.
656 */
657
658 static int
659 sm_nerror (char *str)
660 {
661 strncpy(sm_reply.text, str, sizeof(sm_reply.text));
662 sm_reply.text[sizeof(sm_reply.text) - 1] = '\0';
663 sm_reply.length = strlen(sm_reply.text);
664 sm_reply.code = NOTOK;
665 free(str);
666
667 return RP_BHST;
668 }
669
670 static int
671 smtalk (int time, char *fmt, ...)
672 {
673 va_list ap;
674 char *errstr;
675 int result;
676
677 va_start(ap, fmt);
678 result = netsec_vprintf (nsc, &errstr, fmt, ap);
679 va_end(ap);
680
681 if (result != OK)
682 return sm_nerror(errstr);
683
684 if (netsec_printf (nsc, &errstr, "\r\n") != OK)
685 return sm_nerror(errstr);
686
687 if (netsec_flush (nsc, &errstr) != OK)
688 return sm_nerror(errstr);
689
690 netsec_set_timeout(nsc, time);
691
692 return smhear ();
693 }
694
695
696 static int
697 sm_wstream (char *buffer, int len)
698 {
699 char *bp, *errstr;
700 static char lc = '\0';
701 int rc;
702
703 if (nsc == NULL) {
704 sm_ierror("No socket opened");
705 return NOTOK;
706 }
707
708 if (buffer == NULL && len == 0) {
709 rc = OK;
710 if (lc != '\n')
711 rc = netsec_write(nsc, "\r\n", 2, &errstr);
712 lc = '\0';
713 if (rc != OK)
714 sm_nerror(errstr);
715 return rc;
716 }
717
718 for (bp = buffer; bp && len > 0; bp++, len--) {
719 switch (*bp) {
720 case '\n':
721 sm_nl = TRUE;
722 if (netsec_write(nsc, "\r", 1, &errstr) != OK) {
723 sm_nerror(errstr);
724 return NOTOK;
725 }
726 break;
727
728 case '.':
729 if (sm_nl)
730 if (netsec_write(nsc, ".", 1, &errstr) != OK) {
731 sm_nerror(errstr);
732 return NOTOK;
733 } /* FALL THROUGH */
734
735 default:
736 sm_nl = FALSE;
737 }
738 if (netsec_write(nsc, bp, 1, &errstr) != OK) {
739 sm_nerror(errstr);
740 return NOTOK;
741 }
742 }
743
744 if (bp > buffer)
745 lc = *--bp;
746 return OK;
747 }
748
749
750 static int
751 smhear (void)
752 {
753 int i, code, cont, more;
754 size_t buflen, rc;
755 unsigned char *bp;
756 char *rp;
757 char *errstr;
758 char **ehlo = EHLOkeys, *buffer;
759
760 if (doingEHLO) {
761 static int at_least_once = 0;
762
763 if (at_least_once) {
764 char *ep;
765
766 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
767 ep = *ehlo;
768 free (ep);
769 }
770 } else {
771 at_least_once = 1;
772 }
773
774 ehlo = EHLOkeys;
775 *ehlo = NULL;
776 }
777
778 again: ;
779
780 sm_reply.length = 0;
781 sm_reply.text[0] = 0;
782 rp = sm_reply.text;
783 rc = sizeof(sm_reply.text) - 1;
784
785 for (more = FALSE; (buffer = netsec_readline(nsc, &buflen,
786 &errstr)) != NULL ; ) {
787
788 if (doingEHLO
789 && strncmp (buffer, "250", sizeof("250") - 1) == 0
790 && (buffer[3] == '-' || doingEHLO == 2)
791 && buffer[4]) {
792 if (doingEHLO == 2) {
793 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
794 strcpy (*ehlo++, buffer + 4);
795 *ehlo = NULL;
796 if (ehlo >= EHLOkeys + MAXEHLO)
797 doingEHLO = 0;
798 }
799 else
800 doingEHLO = 0;
801 }
802 else
803 doingEHLO = 2;
804 }
805
806 bp = (unsigned char *) buffer;
807
808 for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
809 continue;
810
811 cont = FALSE;
812 code = atoi ((char *) bp);
813 bp += 3, buflen -= 3;
814 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
815 continue;
816 if (buflen > 0 && *bp == '-') {
817 cont = TRUE;
818 bp++, buflen--;
819 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
820 continue;
821 }
822
823 if (more) {
824 if (code != sm_reply.code || cont)
825 continue;
826 more = FALSE;
827 } else {
828 sm_reply.code = code;
829 more = cont;
830 if (buflen <= 0) {
831 bp = (unsigned char *) sm_noreply;
832 buflen = strlen (sm_noreply);
833 }
834 }
835
836 if ((i = min (buflen, rc)) > 0) {
837 memcpy (rp, bp, i);
838 rp += i;
839 rc -= i;
840 i = strlen(sm_moreply);
841 if (more && (int) rc > i + 1) {
842 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
843 rp += i;
844 rc -= i;
845 }
846 }
847 if (more)
848 continue;
849 if (sm_reply.code < 100) {
850 if (sm_verbose) {
851 printf ("%s\n", sm_reply.text);
852 fflush (stdout);
853 }
854 goto again;
855 }
856
857 sm_reply.length = rp - sm_reply.text;
858 sm_reply.text[sm_reply.length] = 0;
859 return sm_reply.code;
860 }
861
862 sm_nerror(errstr);
863 return NOTOK;
864 }
865
866
867 char *
868 rp_string (int code)
869 {
870 char *text;
871 static char buffer[BUFSIZ];
872
873 switch (sm_reply.code != NOTOK ? code : NOTOK) {
874 case RP_AOK:
875 text = "AOK";
876 break;
877
878 case RP_MOK:
879 text = "MOK";
880 break;
881
882 case RP_OK:
883 text = "OK";
884 break;
885
886 case RP_RPLY:
887 text = "RPLY";
888 break;
889
890 case RP_BHST:
891 default:
892 text = "BHST";
893 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
894 return buffer;
895
896 case RP_PARM:
897 text = "PARM";
898 break;
899
900 case RP_NO:
901 text = "NO";
902 break;
903
904 case RP_USER:
905 text = "USER";
906 break;
907
908 case RP_NDEL:
909 text = "NDEL";
910 break;
911 }
912
913 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
914 text, sm_reply.code, sm_reply.text);
915 return buffer;
916 }
917
918 static char *
919 EHLOset (char *s)
920 {
921 size_t len;
922 char *ep, **ehlo;
923
924 len = strlen (s);
925
926 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
927 ep = *ehlo;
928 if (strncmp (ep, s, len) == 0) {
929 for (ep += len; *ep == ' '; ep++)
930 continue;
931 return ep;
932 }
933 }
934
935 return 0;
936 }
937
938 /*
939 * Our SASL callback; we are either given SASL tokens to generate network
940 * protocols messages for, or we decode incoming protocol messages and
941 * convert them to binary SASL tokens to pass up into the SASL library.
942 */
943
944 static int
945 sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
946 unsigned int indatalen, unsigned char **outdata,
947 unsigned int *outdatalen, char **errstr)
948 {
949 int rc, snoopoffset;
950 char *mech, *line;
951 size_t len;
952
953 switch (mtype) {
954 case NETSEC_SASL_START:
955 /*
956 * Generate an AUTH message; if we were given an input token
957 * then generate a an AUTH message that includes the initial
958 * response.
959 */
960
961 mech = netsec_get_sasl_mechanism(nsc);
962
963 if (indatalen) {
964 char *b64data;
965
966 b64data = mh_xmalloc(BASE64SIZE(indatalen));
967 writeBase64raw(indata, indatalen, (unsigned char *) b64data);
968
969 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
970 &snoopoffset);
971 snoopoffset = 6 + strlen(mech);
972 rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
973 free(b64data);
974 netsec_set_snoop_callback(nsc, NULL, NULL);
975 } else {
976 rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
977 }
978
979 if (rc != OK)
980 return NOTOK;
981
982 if (netsec_flush(nsc, errstr) != OK)
983 return NOTOK;
984
985 break;
986
987 case NETSEC_SASL_READ:
988 /*
989 * Read in a line that should contain a 334 response code, followed
990 * by base64 response data.
991 */
992
993 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
994 snoopoffset = 4;
995 line = netsec_readline(nsc, &len, errstr);
996 netsec_set_snoop_callback(nsc, NULL, NULL);
997
998 if (line == NULL)
999 return NOTOK;
1000
1001 if (len < 4) {
1002 netsec_err(errstr, "Invalid format for SASL response");
1003 return NOTOK;
1004 }
1005
1006 if (strncmp(line, "334 ", 4) != 0) {
1007 netsec_err(errstr, "Improper SASL protocol response: %s", line);
1008 return NOTOK;
1009 }
1010
1011 if (len == 4) {
1012 *outdata = NULL;
1013 *outdatalen = 0;
1014 } else {
1015 rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
1016 if (rc != OK) {
1017 netsec_err(errstr, "Unable to decode base64 response");
1018 return NOTOK;
1019 }
1020 *outdatalen = len;
1021 }
1022 break;
1023
1024 case NETSEC_SASL_WRITE:
1025 /*
1026 * The output encoding is pretty simple, so this is easy.
1027 */
1028 if (indatalen == 0) {
1029 rc = netsec_printf(nsc, errstr, "\r\n");
1030 } else {
1031 unsigned char *b64data;
1032 b64data = mh_xmalloc(BASE64SIZE(indatalen));
1033 writeBase64raw(indata, indatalen, b64data);
1034 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
1035 rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
1036 netsec_set_snoop_callback(nsc, NULL, NULL);
1037 free(b64data);
1038 }
1039
1040 if (rc != OK)
1041 return NOTOK;
1042
1043 if (netsec_flush(nsc, errstr) != OK)
1044 return NOTOK;
1045 break;
1046
1047 case NETSEC_SASL_FINISH:
1048 /*
1049 * Finish the protocol; we're looking for a 235 message.
1050 */
1051 line = netsec_readline(nsc, &len, errstr);
1052 if (line == NULL)
1053 return NOTOK;
1054
1055 if (strncmp(line, "235 ", 4) != 0) {
1056 if (len > 4)
1057 netsec_err(errstr, "Authentication failed: %s", line + 4);
1058 else
1059 netsec_err(errstr, "Authentication failed: %s", line);
1060 return NOTOK;
1061 }
1062 break;
1063
1064 case NETSEC_SASL_CANCEL:
1065 /*
1066 * Cancel the SASL exchange; this is done by sending a single "*".
1067 */
1068 rc = netsec_printf(nsc, errstr, "*\r\n");
1069 if (rc == OK)
1070 rc = netsec_flush(nsc, errstr);
1071 if (rc != OK)
1072 return NOTOK;
1073 break;
1074 }
1075
1076 return OK;
1077 }