]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
new.c: Order two return statements to match comment.
[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 sm_child = fork();
311 switch (sm_child) {
312 case NOTOK:
313 close (pdo[0]);
314 close (pdo[1]);
315 close (pdi[0]);
316 close (pdi[1]);
317 return sm_ierror ("unable to fork");
318
319 case OK:
320 if (pdo[0] != fileno (stdin))
321 dup2 (pdo[0], fileno (stdin));
322 if (pdi[1] != fileno (stdout))
323 dup2 (pdi[1], fileno (stdout));
324 if (pdi[1] != fileno (stderr))
325 dup2 (pdi[1], fileno (stderr));
326 for (i = fileno (stderr) + 1; i < NBITS; i++)
327 close (i);
328
329 vecp = 0;
330 vec[vecp++] = r1bindex (sendmail, '/');
331 vec[vecp++] = "-bs";
332 vec[vecp++] = watch ? "-odi" : "-odb";
333 vec[vecp++] = "-oem";
334 vec[vecp++] = "-om";
335 if (verbose)
336 vec[vecp++] = "-ov";
337 vec[vecp++] = NULL;
338
339 execvp (sendmail, vec);
340 fprintf (stderr, "unable to exec ");
341 perror (sendmail);
342 _exit (-1); /* NOTREACHED */
343
344 default:
345 SIGNAL (SIGPIPE, SIG_IGN);
346
347 close (pdi[1]);
348 close (pdo[0]);
349
350 netsec_set_fd(nsc, pdi[0], pdo[1]);
351 netsec_set_timeout(nsc, SM_OPEN);
352 result = smhear ();
353 switch (result) {
354 case 220:
355 break;
356
357 default:
358 sm_end (NOTOK);
359 return RP_RPLY;
360 }
361
362 doingEHLO = 1;
363 result = smtalk (SM_HELO, "EHLO %s", client);
364 doingEHLO = 0;
365
366 if (500 <= result && result <= 599)
367 result = smtalk (SM_HELO, "HELO %s", client);
368
369 switch (result) {
370 case 250:
371 break;
372
373 default:
374 sm_end (NOTOK);
375 return RP_RPLY;
376 }
377
378 /*
379 * If the user asked for SASL, then check to see if the SMTP server
380 * supports it. Otherwise, error out (because the SMTP server
381 * might have been spoofed; we don't want to just silently not
382 * do authentication
383 */
384
385 if (sasl) {
386 char *server_mechs;
387 if (! (server_mechs = EHLOset("AUTH"))) {
388 sm_end(NOTOK);
389 return sm_ierror("SMTP server does not support SASL");
390 }
391 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
392 sm_end(NOTOK);
393 return sm_nerror(errstr);
394 }
395 }
396
397 if (watch)
398 smtalk (SM_HELO, "VERB on");
399
400 return RP_OK;
401 }
402 }
403
404 static int
405 rclient (char *server, char *service, char **chosen_server)
406 {
407 int sd;
408 char response[BUFSIZ];
409
410 if (server == NULL)
411 server = servers;
412
413 *chosen_server = server;
414
415 if ((sd = client (server, service, response, sizeof(response),
416 sm_debug)) != NOTOK)
417 return sd;
418
419 sm_ierror ("%s", response);
420 return NOTOK;
421 }
422
423 int
424 sm_winit (char *from, int smtputf8, int eightbit)
425 {
426 const char *mail_parameters = "";
427
428 if (smtputf8) {
429 /* Just for information, if an attempt is made to send to an 8-bit
430 address without specifying SMTPUTF8, Gmail responds with
431 555 5.5.2 Syntax error.
432 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
433 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
434 mail_parameters = " BODY=8BITMIME SMTPUTF8";
435 } else {
436 inform("SMTP server does not support %s, not sending.\n"
437 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
438 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
439 sm_end (NOTOK);
440 return RP_UCMD;
441 }
442 } else if (eightbit) {
443 /* Comply with RFC 6152, for messages that have any 8-bit characters
444 in their body. */
445 if (EHLOset ("8BITMIME")) {
446 mail_parameters = " BODY=8BITMIME";
447 } else {
448 inform("SMTP server does not support 8BITMIME, not sending.\n"
449 "Suggest encoding message for 7-bit transport by setting your\n"
450 "locale to C, and/or specifying *b64 in mhbuild directives.");
451 sm_end (NOTOK);
452 return RP_UCMD;
453 }
454 }
455
456 switch (smtalk (SM_MAIL, "MAIL FROM:<%s>%s", from, mail_parameters)) {
457 case 250:
458 sm_addrs = 0;
459 return RP_OK;
460
461 case 500:
462 case 501:
463 case 552:
464 return RP_PARM;
465
466 default:
467 return RP_RPLY;
468 }
469 }
470
471
472 int
473 sm_wadr (char *mbox, char *host, char *path)
474 {
475 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
476 : "RCPT TO:<%s%s>",
477 FENDNULL(path), mbox, host)) {
478 case 250:
479 case 251:
480 sm_addrs++;
481 return RP_OK;
482
483 case 451:
484 case 421:
485 case 450:
486 case 452:
487 return RP_NO;
488
489 case 500:
490 case 501:
491 return RP_PARM;
492
493 case 550:
494 case 551:
495 case 552:
496 case 553:
497 return RP_USER;
498
499 default:
500 return RP_RPLY;
501 }
502 }
503
504
505 int
506 sm_waend (void)
507 {
508 switch (smtalk (SM_DATA, "DATA")) {
509 case 354:
510 sm_nl = true;
511 return RP_OK;
512
513 case 451:
514 case 421:
515 return RP_NO;
516
517 case 500:
518 case 501:
519 case 503:
520 case 554:
521 return RP_NDEL;
522
523 default:
524 return RP_RPLY;
525 }
526 }
527
528
529 int
530 sm_wtxt (char *buffer, int len)
531 {
532 int result;
533
534 result = sm_wstream (buffer, len);
535
536 return result == NOTOK ? RP_BHST : RP_OK;
537 }
538
539
540 int
541 sm_wtend (void)
542 {
543 if (sm_wstream(NULL, 0) == NOTOK)
544 return RP_BHST;
545
546 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
547 case 250:
548 case 251:
549 return RP_OK;
550
551 case 451:
552 case 452:
553 default:
554 return RP_NO;
555
556 case 552:
557 case 554:
558 return RP_NDEL;
559 }
560 }
561
562
563 int
564 sm_end (int type)
565 {
566 int status;
567 struct smtp sm_note;
568
569 if (sm_mts == MTS_SENDMAIL_SMTP) {
570 switch (sm_child) {
571 case NOTOK:
572 case OK:
573 return RP_OK;
574
575 default:
576 break;
577 }
578 }
579
580 if (nsc == NULL)
581 return RP_OK;
582
583 switch (type) {
584 case OK:
585 smtalk (SM_QUIT, "QUIT");
586 break;
587
588 case NOTOK:
589 sm_note.code = sm_reply.code;
590 sm_note.length = sm_reply.length;
591 memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);
592 /* FALLTHRU */
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);
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 }
734 /* FALLTHRU */
735
736 default:
737 sm_nl = false;
738 }
739 if (netsec_write(nsc, bp, 1, &errstr) != OK) {
740 sm_nerror(errstr);
741 return NOTOK;
742 }
743 }
744
745 if (bp > buffer)
746 lc = *--bp;
747 return OK;
748 }
749
750
751 static int
752 smhear (void)
753 {
754 int i, code;
755 bool 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 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
874 static char buffer[BUFSIZ + 19];
875
876 switch (sm_reply.code != NOTOK ? code : NOTOK) {
877 case RP_AOK:
878 text = "AOK";
879 break;
880
881 case RP_MOK:
882 text = "MOK";
883 break;
884
885 case RP_OK:
886 text = "OK";
887 break;
888
889 case RP_RPLY:
890 text = "RPLY";
891 break;
892
893 case RP_BHST:
894 default:
895 text = "BHST";
896 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
897 return buffer;
898
899 case RP_PARM:
900 text = "PARM";
901 break;
902
903 case RP_NO:
904 text = "NO";
905 break;
906
907 case RP_USER:
908 text = "USER";
909 break;
910
911 case RP_NDEL:
912 text = "NDEL";
913 break;
914 }
915
916 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
917 text, sm_reply.code, sm_reply.text);
918 return buffer;
919 }
920
921 static char *
922 EHLOset (char *s)
923 {
924 size_t len;
925 char *ep, **ehlo;
926
927 len = strlen (s);
928
929 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
930 ep = *ehlo;
931 if (has_prefix(ep, s)) {
932 for (ep += len; *ep == ' '; ep++)
933 continue;
934 return ep;
935 }
936 }
937
938 return 0;
939 }
940
941 /*
942 * Our SASL callback; we are either given SASL tokens to generate network
943 * protocols messages for, or we decode incoming protocol messages and
944 * convert them to binary SASL tokens to pass up into the SASL library.
945 */
946
947 static int
948 sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
949 unsigned int indatalen, unsigned char **outdata,
950 unsigned int *outdatalen, char **errstr)
951 {
952 int rc, snoopoffset;
953 char *mech, *line;
954 size_t len;
955
956 switch (mtype) {
957 case NETSEC_SASL_START:
958 /*
959 * Generate an AUTH message; if we were given an input token
960 * then generate a an AUTH message that includes the initial
961 * response.
962 */
963
964 mech = netsec_get_sasl_mechanism(nsc);
965
966 if (indatalen) {
967 char *b64data;
968
969 b64data = mh_xmalloc(BASE64SIZE(indatalen));
970 writeBase64raw(indata, indatalen, (unsigned char *) b64data);
971
972 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
973 &snoopoffset);
974 snoopoffset = 6 + strlen(mech);
975 rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
976 free(b64data);
977 netsec_set_snoop_callback(nsc, NULL, NULL);
978 } else {
979 rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
980 }
981
982 if (rc != OK)
983 return NOTOK;
984
985 if (netsec_flush(nsc, errstr) != OK)
986 return NOTOK;
987
988 break;
989
990 case NETSEC_SASL_READ:
991 /*
992 * Read in a line that should contain a 334 response code, followed
993 * by base64 response data.
994 */
995
996 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
997 snoopoffset = 4;
998 line = netsec_readline(nsc, &len, errstr);
999 netsec_set_snoop_callback(nsc, NULL, NULL);
1000
1001 if (line == NULL)
1002 return NOTOK;
1003
1004 if (len < 4) {
1005 netsec_err(errstr, "Invalid format for SASL response");
1006 return NOTOK;
1007 }
1008
1009 if (!has_prefix(line, "334 ")) {
1010 netsec_err(errstr, "Improper SASL protocol response: %s", line);
1011 return NOTOK;
1012 }
1013
1014 if (len == 4) {
1015 *outdata = NULL;
1016 *outdatalen = 0;
1017 } else {
1018 rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
1019 if (rc != OK) {
1020 netsec_err(errstr, "Unable to decode base64 response");
1021 return NOTOK;
1022 }
1023 *outdatalen = len;
1024 }
1025 break;
1026
1027 case NETSEC_SASL_WRITE:
1028 /*
1029 * The output encoding is pretty simple, so this is easy.
1030 */
1031 if (indatalen == 0) {
1032 rc = netsec_printf(nsc, errstr, "\r\n");
1033 } else {
1034 unsigned char *b64data;
1035 b64data = mh_xmalloc(BASE64SIZE(indatalen));
1036 writeBase64raw(indata, indatalen, b64data);
1037 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
1038 rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
1039 netsec_set_snoop_callback(nsc, NULL, NULL);
1040 free(b64data);
1041 }
1042
1043 if (rc != OK)
1044 return NOTOK;
1045
1046 if (netsec_flush(nsc, errstr) != OK)
1047 return NOTOK;
1048 break;
1049
1050 case NETSEC_SASL_FINISH:
1051 /*
1052 * Finish the protocol; we're looking for a 235 message.
1053 */
1054 line = netsec_readline(nsc, &len, errstr);
1055 if (line == NULL)
1056 return NOTOK;
1057
1058 if (!has_prefix(line, "235 ")) {
1059 if (len > 4)
1060 netsec_err(errstr, "Authentication failed: %s", line + 4);
1061 else
1062 netsec_err(errstr, "Authentication failed: %s", line);
1063 return NOTOK;
1064 }
1065 break;
1066
1067 case NETSEC_SASL_CANCEL:
1068 /*
1069 * Cancel the SASL exchange; this is done by sending a single "*".
1070 */
1071 rc = netsec_printf(nsc, errstr, "*\r\n");
1072 if (rc == OK)
1073 rc = netsec_flush(nsc, errstr);
1074 if (rc != OK)
1075 return NOTOK;
1076 break;
1077 }
1078
1079 return OK;
1080 }