]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
Reworked BUGS section of nmh(7) man page, and added excerpt
[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, int smtputf8)
445 {
446 const char *const mail_parameters = smtputf8
447 ? " BODY=8BITMIME SMTPUTF8"
448 : "";
449
450 /* Just for information, if an attempt is made to send to an 8-bit
451 address without specifying SMTPUTF8, Gmail responds with
452 555 5.5.2 Syntax error.
453 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
454 if (smtputf8 && (! EHLOset("8BITMIME") || ! EHLOset("SMTPUTF8"))) {
455 sm_end (NOTOK);
456 return RP_UCMD;
457 }
458
459 switch (smtalk (SM_MAIL, "MAIL FROM:<%s>%s", from, mail_parameters)) {
460 case 250:
461 sm_addrs = 0;
462 return RP_OK;
463
464 case 500:
465 case 501:
466 case 552:
467 return RP_PARM;
468
469 default:
470 return RP_RPLY;
471 }
472 }
473
474
475 int
476 sm_wadr (char *mbox, char *host, char *path)
477 {
478 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
479 : "RCPT TO:<%s%s>",
480 path ? path : "", mbox, host)) {
481 case 250:
482 case 251:
483 sm_addrs++;
484 return RP_OK;
485
486 case 451:
487 #ifdef SENDMAILBUG
488 sm_addrs++;
489 return RP_OK;
490 #endif /* SENDMAILBUG */
491 case 421:
492 case 450:
493 case 452:
494 return RP_NO;
495
496 case 500:
497 case 501:
498 return RP_PARM;
499
500 case 550:
501 case 551:
502 case 552:
503 case 553:
504 return RP_USER;
505
506 default:
507 return RP_RPLY;
508 }
509 }
510
511
512 int
513 sm_waend (void)
514 {
515 switch (smtalk (SM_DATA, "DATA")) {
516 case 354:
517 sm_nl = TRUE;
518 return RP_OK;
519
520 case 451:
521 #ifdef SENDMAILBUG
522 sm_nl = TRUE;
523 return RP_OK;
524 #endif /* SENDMAILBUG */
525 case 421:
526 return RP_NO;
527
528 case 500:
529 case 501:
530 case 503:
531 case 554:
532 return RP_NDEL;
533
534 default:
535 return RP_RPLY;
536 }
537 }
538
539
540 int
541 sm_wtxt (char *buffer, int len)
542 {
543 int result;
544
545 result = sm_wstream (buffer, len);
546
547 return (result == NOTOK ? RP_BHST : RP_OK);
548 }
549
550
551 int
552 sm_wtend (void)
553 {
554 if (sm_wstream ((char *) NULL, 0) == NOTOK)
555 return RP_BHST;
556
557 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
558 case 250:
559 case 251:
560 return RP_OK;
561
562 case 451:
563 #ifdef SENDMAILBUG
564 return RP_OK;
565 #endif /* SENDMAILBUG */
566 case 452:
567 default:
568 return RP_NO;
569
570 case 552:
571 case 554:
572 return RP_NDEL;
573 }
574 }
575
576
577 int
578 sm_end (int type)
579 {
580 int status;
581 struct smtp sm_note;
582
583 if (sm_mts == MTS_SENDMAIL_SMTP) {
584 switch (sm_child) {
585 case NOTOK:
586 case OK:
587 return RP_OK;
588
589 default:
590 break;
591 }
592 }
593
594 if (nsc == NULL)
595 return RP_OK;
596
597 switch (type) {
598 case OK:
599 smtalk (SM_QUIT, "QUIT");
600 break;
601
602 case NOTOK:
603 sm_note.code = sm_reply.code;
604 sm_note.length = sm_reply.length;
605 memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */
606 case DONE:
607 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
608 return RP_OK;
609 if (sm_mts == MTS_SMTP)
610 smtalk (SM_QUIT, "QUIT");
611 else {
612 /* The SIGPIPE block replaces old calls to discard ().
613 We're not sure what the discard () calls were for,
614 maybe to prevent deadlock on old systems. In any
615 case, blocking SIGPIPE should be harmless.
616 Because the file handles are closed below, leave it
617 blocked. */
618 sigset_t set, oset;
619 sigemptyset (&set);
620 sigaddset (&set, SIGPIPE);
621 sigprocmask (SIG_BLOCK, &set, &oset);
622
623 kill (sm_child, SIGKILL);
624 sm_child = NOTOK;
625 }
626 if (type == NOTOK) {
627 sm_reply.code = sm_note.code;
628 sm_reply.length = sm_note.length;
629 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
630 }
631 break;
632 }
633
634 if (nsc != NULL) {
635 netsec_shutdown(nsc, 1);
636 nsc = NULL;
637 }
638
639 if (sm_mts == MTS_SMTP) {
640 status = 0;
641 } else if (sm_child != NOTOK) {
642 status = pidwait (sm_child, OK);
643 sm_child = NOTOK;
644 } else {
645 status = OK;
646 }
647
648 return (status ? RP_BHST : RP_OK);
649 }
650
651
652 static int
653 sm_ierror (const char *fmt, ...)
654 {
655 va_list ap;
656
657 va_start(ap, fmt);
658 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
659 va_end(ap);
660
661 sm_reply.length = strlen (sm_reply.text);
662 sm_reply.code = NOTOK;
663
664 return RP_BHST;
665 }
666
667 /*
668 * Like sm_ierror, but assume it's an allocated error string we need to free.
669 */
670
671 static int
672 sm_nerror (char *str)
673 {
674 strncpy(sm_reply.text, str, sizeof(sm_reply.text));
675 sm_reply.text[sizeof(sm_reply.text) - 1] = '\0';
676 sm_reply.length = strlen(sm_reply.text);
677 sm_reply.code = NOTOK;
678 free(str);
679
680 return RP_BHST;
681 }
682
683 static int
684 smtalk (int time, char *fmt, ...)
685 {
686 va_list ap;
687 char *errstr;
688 int result;
689
690 va_start(ap, fmt);
691 result = netsec_vprintf (nsc, &errstr, fmt, ap);
692 va_end(ap);
693
694 if (result != OK)
695 return sm_nerror(errstr);
696
697 if (netsec_printf (nsc, &errstr, "\r\n") != OK)
698 return sm_nerror(errstr);
699
700 if (netsec_flush (nsc, &errstr) != OK)
701 return sm_nerror(errstr);
702
703 netsec_set_timeout(nsc, time);
704
705 return smhear ();
706 }
707
708
709 static int
710 sm_wstream (char *buffer, int len)
711 {
712 char *bp, *errstr;
713 static char lc = '\0';
714 int rc;
715
716 if (nsc == NULL) {
717 sm_ierror("No socket opened");
718 return NOTOK;
719 }
720
721 if (buffer == NULL && len == 0) {
722 rc = OK;
723 if (lc != '\n')
724 rc = netsec_write(nsc, "\r\n", 2, &errstr);
725 lc = '\0';
726 if (rc != OK)
727 sm_nerror(errstr);
728 return rc;
729 }
730
731 for (bp = buffer; bp && len > 0; bp++, len--) {
732 switch (*bp) {
733 case '\n':
734 sm_nl = TRUE;
735 if (netsec_write(nsc, "\r", 1, &errstr) != OK) {
736 sm_nerror(errstr);
737 return NOTOK;
738 }
739 break;
740
741 case '.':
742 if (sm_nl)
743 if (netsec_write(nsc, ".", 1, &errstr) != OK) {
744 sm_nerror(errstr);
745 return NOTOK;
746 } /* FALL THROUGH */
747
748 default:
749 sm_nl = FALSE;
750 }
751 if (netsec_write(nsc, bp, 1, &errstr) != OK) {
752 sm_nerror(errstr);
753 return NOTOK;
754 }
755 }
756
757 if (bp > buffer)
758 lc = *--bp;
759 return OK;
760 }
761
762
763 static int
764 smhear (void)
765 {
766 int i, code, cont, more;
767 size_t buflen, rc;
768 unsigned char *bp;
769 char *rp;
770 char *errstr;
771 char **ehlo = EHLOkeys, *buffer;
772
773 if (doingEHLO) {
774 static int at_least_once = 0;
775
776 if (at_least_once) {
777 char *ep;
778
779 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
780 ep = *ehlo;
781 free (ep);
782 }
783 } else {
784 at_least_once = 1;
785 }
786
787 ehlo = EHLOkeys;
788 *ehlo = NULL;
789 }
790
791 again: ;
792
793 sm_reply.length = 0;
794 sm_reply.text[0] = 0;
795 rp = sm_reply.text;
796 rc = sizeof(sm_reply.text) - 1;
797
798 for (more = FALSE; (buffer = netsec_readline(nsc, &buflen,
799 &errstr)) != NULL ; ) {
800
801 if (doingEHLO
802 && strncmp (buffer, "250", sizeof("250") - 1) == 0
803 && (buffer[3] == '-' || doingEHLO == 2)
804 && buffer[4]) {
805 if (doingEHLO == 2) {
806 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
807 strcpy (*ehlo++, buffer + 4);
808 *ehlo = NULL;
809 if (ehlo >= EHLOkeys + MAXEHLO)
810 doingEHLO = 0;
811 }
812 else
813 doingEHLO = 0;
814 }
815 else
816 doingEHLO = 2;
817 }
818
819 bp = (unsigned char *) buffer;
820
821 for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
822 continue;
823
824 cont = FALSE;
825 code = atoi ((char *) bp);
826 bp += 3, buflen -= 3;
827 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
828 continue;
829 if (buflen > 0 && *bp == '-') {
830 cont = TRUE;
831 bp++, buflen--;
832 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
833 continue;
834 }
835
836 if (more) {
837 if (code != sm_reply.code || cont)
838 continue;
839 more = FALSE;
840 } else {
841 sm_reply.code = code;
842 more = cont;
843 if (buflen <= 0) {
844 bp = (unsigned char *) sm_noreply;
845 buflen = strlen (sm_noreply);
846 }
847 }
848
849 if ((i = min (buflen, rc)) > 0) {
850 memcpy (rp, bp, i);
851 rp += i;
852 rc -= i;
853 i = strlen(sm_moreply);
854 if (more && (int) rc > i + 1) {
855 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
856 rp += i;
857 rc -= i;
858 }
859 }
860 if (more)
861 continue;
862 if (sm_reply.code < 100) {
863 if (sm_verbose) {
864 printf ("%s\n", sm_reply.text);
865 fflush (stdout);
866 }
867 goto again;
868 }
869
870 sm_reply.length = rp - sm_reply.text;
871 sm_reply.text[sm_reply.length] = 0;
872 return sm_reply.code;
873 }
874
875 sm_nerror(errstr);
876 return NOTOK;
877 }
878
879
880 char *
881 rp_string (int code)
882 {
883 char *text;
884 static char buffer[BUFSIZ];
885
886 switch (sm_reply.code != NOTOK ? code : NOTOK) {
887 case RP_AOK:
888 text = "AOK";
889 break;
890
891 case RP_MOK:
892 text = "MOK";
893 break;
894
895 case RP_OK:
896 text = "OK";
897 break;
898
899 case RP_RPLY:
900 text = "RPLY";
901 break;
902
903 case RP_BHST:
904 default:
905 text = "BHST";
906 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
907 return buffer;
908
909 case RP_PARM:
910 text = "PARM";
911 break;
912
913 case RP_NO:
914 text = "NO";
915 break;
916
917 case RP_USER:
918 text = "USER";
919 break;
920
921 case RP_NDEL:
922 text = "NDEL";
923 break;
924 }
925
926 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
927 text, sm_reply.code, sm_reply.text);
928 return buffer;
929 }
930
931 static char *
932 EHLOset (char *s)
933 {
934 size_t len;
935 char *ep, **ehlo;
936
937 len = strlen (s);
938
939 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
940 ep = *ehlo;
941 if (strncmp (ep, s, len) == 0) {
942 for (ep += len; *ep == ' '; ep++)
943 continue;
944 return ep;
945 }
946 }
947
948 return 0;
949 }
950
951 /*
952 * Our SASL callback; we are either given SASL tokens to generate network
953 * protocols messages for, or we decode incoming protocol messages and
954 * convert them to binary SASL tokens to pass up into the SASL library.
955 */
956
957 static int
958 sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
959 unsigned int indatalen, unsigned char **outdata,
960 unsigned int *outdatalen, char **errstr)
961 {
962 int rc, snoopoffset;
963 char *mech, *line;
964 size_t len;
965
966 switch (mtype) {
967 case NETSEC_SASL_START:
968 /*
969 * Generate an AUTH message; if we were given an input token
970 * then generate a an AUTH message that includes the initial
971 * response.
972 */
973
974 mech = netsec_get_sasl_mechanism(nsc);
975
976 if (indatalen) {
977 char *b64data;
978
979 b64data = mh_xmalloc(BASE64SIZE(indatalen));
980 writeBase64raw(indata, indatalen, (unsigned char *) b64data);
981
982 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
983 &snoopoffset);
984 snoopoffset = 6 + strlen(mech);
985 rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
986 free(b64data);
987 netsec_set_snoop_callback(nsc, NULL, NULL);
988 } else {
989 rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
990 }
991
992 if (rc != OK)
993 return NOTOK;
994
995 if (netsec_flush(nsc, errstr) != OK)
996 return NOTOK;
997
998 break;
999
1000 case NETSEC_SASL_READ:
1001 /*
1002 * Read in a line that should contain a 334 response code, followed
1003 * by base64 response data.
1004 */
1005
1006 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
1007 snoopoffset = 4;
1008 line = netsec_readline(nsc, &len, errstr);
1009 netsec_set_snoop_callback(nsc, NULL, NULL);
1010
1011 if (line == NULL)
1012 return NOTOK;
1013
1014 if (len < 4) {
1015 netsec_err(errstr, "Invalid format for SASL response");
1016 return NOTOK;
1017 }
1018
1019 if (strncmp(line, "334 ", 4) != 0) {
1020 netsec_err(errstr, "Improper SASL protocol response: %s", line);
1021 return NOTOK;
1022 }
1023
1024 if (len == 4) {
1025 *outdata = NULL;
1026 *outdatalen = 0;
1027 } else {
1028 rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
1029 if (rc != OK) {
1030 netsec_err(errstr, "Unable to decode base64 response");
1031 return NOTOK;
1032 }
1033 *outdatalen = len;
1034 }
1035 break;
1036
1037 case NETSEC_SASL_WRITE:
1038 /*
1039 * The output encoding is pretty simple, so this is easy.
1040 */
1041 if (indatalen == 0) {
1042 rc = netsec_printf(nsc, errstr, "\r\n");
1043 } else {
1044 unsigned char *b64data;
1045 b64data = mh_xmalloc(BASE64SIZE(indatalen));
1046 writeBase64raw(indata, indatalen, b64data);
1047 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
1048 rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
1049 netsec_set_snoop_callback(nsc, NULL, NULL);
1050 free(b64data);
1051 }
1052
1053 if (rc != OK)
1054 return NOTOK;
1055
1056 if (netsec_flush(nsc, errstr) != OK)
1057 return NOTOK;
1058 break;
1059
1060 case NETSEC_SASL_FINISH:
1061 /*
1062 * Finish the protocol; we're looking for a 235 message.
1063 */
1064 line = netsec_readline(nsc, &len, errstr);
1065 if (line == NULL)
1066 return NOTOK;
1067
1068 if (strncmp(line, "235 ", 4) != 0) {
1069 if (len > 4)
1070 netsec_err(errstr, "Authentication failed: %s", line + 4);
1071 else
1072 netsec_err(errstr, "Authentication failed: %s", line);
1073 return NOTOK;
1074 }
1075 break;
1076
1077 case NETSEC_SASL_CANCEL:
1078 /*
1079 * Cancel the SASL exchange; this is done by sending a single "*".
1080 */
1081 rc = netsec_printf(nsc, errstr, "*\r\n");
1082 if (rc == OK)
1083 rc = netsec_flush(nsc, errstr);
1084 if (rc != OK)
1085 return NOTOK;
1086 break;
1087 }
1088
1089 return OK;
1090 }