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