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