]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
2f32877cf87e5d6782755a5b7565e2c4a279466a
[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 int 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[i], 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, cont, more;
757 size_t buflen, rc;
758 unsigned char *bp;
759 char *rp;
760 char *errstr;
761 char **ehlo = EHLOkeys, *buffer;
762
763 if (doingEHLO) {
764 static int at_least_once = 0;
765
766 if (at_least_once) {
767 char *ep;
768
769 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
770 ep = *ehlo;
771 free (ep);
772 }
773 } else {
774 at_least_once = 1;
775 }
776
777 ehlo = EHLOkeys;
778 *ehlo = NULL;
779 }
780
781 again: ;
782
783 sm_reply.length = 0;
784 sm_reply.text[0] = 0;
785 rp = sm_reply.text;
786 rc = sizeof(sm_reply.text) - 1;
787
788 for (more = FALSE; (buffer = netsec_readline(nsc, &buflen,
789 &errstr)) != NULL ; ) {
790
791 if (doingEHLO
792 && has_prefix(buffer, "250")
793 && (buffer[3] == '-' || doingEHLO == 2)
794 && buffer[4]) {
795 if (doingEHLO == 2) {
796 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
797 strcpy (*ehlo++, buffer + 4);
798 *ehlo = NULL;
799 if (ehlo >= EHLOkeys + MAXEHLO)
800 doingEHLO = 0;
801 }
802 else
803 doingEHLO = 0;
804 }
805 else
806 doingEHLO = 2;
807 }
808
809 bp = (unsigned char *) buffer;
810
811 for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
812 continue;
813
814 cont = FALSE;
815 code = atoi ((char *) bp);
816 bp += 3, buflen -= 3;
817 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
818 continue;
819 if (buflen > 0 && *bp == '-') {
820 cont = TRUE;
821 bp++, buflen--;
822 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
823 continue;
824 }
825
826 if (more) {
827 if (code != sm_reply.code || cont)
828 continue;
829 more = FALSE;
830 } else {
831 sm_reply.code = code;
832 more = cont;
833 if (buflen <= 0) {
834 bp = (unsigned char *) sm_noreply;
835 buflen = strlen (sm_noreply);
836 }
837 }
838
839 if ((i = min (buflen, rc)) > 0) {
840 memcpy (rp, bp, i);
841 rp += i;
842 rc -= i;
843 i = strlen(sm_moreply);
844 if (more && (int) rc > i + 1) {
845 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
846 rp += i;
847 rc -= i;
848 }
849 }
850 if (more)
851 continue;
852 if (sm_reply.code < 100) {
853 if (sm_verbose) {
854 puts(sm_reply.text);
855 fflush (stdout);
856 }
857 goto again;
858 }
859
860 sm_reply.length = rp - sm_reply.text;
861 sm_reply.text[sm_reply.length] = 0;
862 return sm_reply.code;
863 }
864
865 sm_nerror(errstr);
866 return NOTOK;
867 }
868
869
870 char *
871 rp_string (int code)
872 {
873 char *text;
874 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
875 static char buffer[BUFSIZ + 19];
876
877 switch (sm_reply.code != NOTOK ? code : NOTOK) {
878 case RP_AOK:
879 text = "AOK";
880 break;
881
882 case RP_MOK:
883 text = "MOK";
884 break;
885
886 case RP_OK:
887 text = "OK";
888 break;
889
890 case RP_RPLY:
891 text = "RPLY";
892 break;
893
894 case RP_BHST:
895 default:
896 text = "BHST";
897 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
898 return buffer;
899
900 case RP_PARM:
901 text = "PARM";
902 break;
903
904 case RP_NO:
905 text = "NO";
906 break;
907
908 case RP_USER:
909 text = "USER";
910 break;
911
912 case RP_NDEL:
913 text = "NDEL";
914 break;
915 }
916
917 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
918 text, sm_reply.code, sm_reply.text);
919 return buffer;
920 }
921
922 static char *
923 EHLOset (char *s)
924 {
925 size_t len;
926 char *ep, **ehlo;
927
928 len = strlen (s);
929
930 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
931 ep = *ehlo;
932 if (has_prefix(ep, s)) {
933 for (ep += len; *ep == ' '; ep++)
934 continue;
935 return ep;
936 }
937 }
938
939 return 0;
940 }
941
942 /*
943 * Our SASL callback; we are either given SASL tokens to generate network
944 * protocols messages for, or we decode incoming protocol messages and
945 * convert them to binary SASL tokens to pass up into the SASL library.
946 */
947
948 static int
949 sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
950 unsigned int indatalen, unsigned char **outdata,
951 unsigned int *outdatalen, char **errstr)
952 {
953 int rc, snoopoffset;
954 char *mech, *line;
955 size_t len;
956
957 switch (mtype) {
958 case NETSEC_SASL_START:
959 /*
960 * Generate an AUTH message; if we were given an input token
961 * then generate a an AUTH message that includes the initial
962 * response.
963 */
964
965 mech = netsec_get_sasl_mechanism(nsc);
966
967 if (indatalen) {
968 char *b64data;
969
970 b64data = mh_xmalloc(BASE64SIZE(indatalen));
971 writeBase64raw(indata, indatalen, (unsigned char *) b64data);
972
973 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
974 &snoopoffset);
975 snoopoffset = 6 + strlen(mech);
976 rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
977 free(b64data);
978 netsec_set_snoop_callback(nsc, NULL, NULL);
979 } else {
980 rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
981 }
982
983 if (rc != OK)
984 return NOTOK;
985
986 if (netsec_flush(nsc, errstr) != OK)
987 return NOTOK;
988
989 break;
990
991 case NETSEC_SASL_READ:
992 /*
993 * Read in a line that should contain a 334 response code, followed
994 * by base64 response data.
995 */
996
997 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
998 snoopoffset = 4;
999 line = netsec_readline(nsc, &len, errstr);
1000 netsec_set_snoop_callback(nsc, NULL, NULL);
1001
1002 if (line == NULL)
1003 return NOTOK;
1004
1005 if (len < 4) {
1006 netsec_err(errstr, "Invalid format for SASL response");
1007 return NOTOK;
1008 }
1009
1010 if (!has_prefix(line, "334 ")) {
1011 netsec_err(errstr, "Improper SASL protocol response: %s", line);
1012 return NOTOK;
1013 }
1014
1015 if (len == 4) {
1016 *outdata = NULL;
1017 *outdatalen = 0;
1018 } else {
1019 rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
1020 if (rc != OK) {
1021 netsec_err(errstr, "Unable to decode base64 response");
1022 return NOTOK;
1023 }
1024 *outdatalen = len;
1025 }
1026 break;
1027
1028 case NETSEC_SASL_WRITE:
1029 /*
1030 * The output encoding is pretty simple, so this is easy.
1031 */
1032 if (indatalen == 0) {
1033 rc = netsec_printf(nsc, errstr, "\r\n");
1034 } else {
1035 unsigned char *b64data;
1036 b64data = mh_xmalloc(BASE64SIZE(indatalen));
1037 writeBase64raw(indata, indatalen, b64data);
1038 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
1039 rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
1040 netsec_set_snoop_callback(nsc, NULL, NULL);
1041 free(b64data);
1042 }
1043
1044 if (rc != OK)
1045 return NOTOK;
1046
1047 if (netsec_flush(nsc, errstr) != OK)
1048 return NOTOK;
1049 break;
1050
1051 case NETSEC_SASL_FINISH:
1052 /*
1053 * Finish the protocol; we're looking for a 235 message.
1054 */
1055 line = netsec_readline(nsc, &len, errstr);
1056 if (line == NULL)
1057 return NOTOK;
1058
1059 if (!has_prefix(line, "235 ")) {
1060 if (len > 4)
1061 netsec_err(errstr, "Authentication failed: %s", line + 4);
1062 else
1063 netsec_err(errstr, "Authentication failed: %s", line);
1064 return NOTOK;
1065 }
1066 break;
1067
1068 case NETSEC_SASL_CANCEL:
1069 /*
1070 * Cancel the SASL exchange; this is done by sending a single "*".
1071 */
1072 rc = netsec_printf(nsc, errstr, "*\r\n");
1073 if (rc == OK)
1074 rc = netsec_flush(nsc, errstr);
1075 if (rc != OK)
1076 return NOTOK;
1077 break;
1078 }
1079
1080 return OK;
1081 }