]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
7808e1a70871454de78e9e306e4ad147f7383bdd
[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 /* 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 }