]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
fdcompare.c: Move interface to own file.
[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 "sbr/error.h"
10 #include "smtp.h"
11 #include "h/mts.h"
12 #include "h/signals.h"
13 #include "h/utils.h"
14 #include "h/netsec.h"
15
16 #include <sys/socket.h>
17 #include "sbr/base64.h"
18
19 /*
20 * This module implements an interface to SendMail very similar
21 * to the MMDF mm_(3) routines. The sm_() routines herein talk
22 * SMTP to a sendmail process, mapping SMTP reply codes into
23 * RP_-style codes.
24 */
25
26 #define NBITS ((sizeof (int)) * 8)
27
28 /* Timeout in seconds for SMTP commands.
29 * Lore has it they must be distinct. */
30 #define SM_OPEN 300 /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
31 #define SM_HELO 20
32 #define SM_RSET 15
33 #define SM_MAIL 301 /* changed to 5 minutes and a second (for uniqueness), see above */
34 #define SM_RCPT 302 /* see above */
35 #define SM_DATA 120 /* see above */
36 #define SM_DOT 600 /* see above */
37 #define SM_QUIT 30
38
39 static int sm_addrs = 0;
40 static int sm_child = NOTOK;
41 static int sm_debug = 0;
42 static bool sm_nl = true;
43 static int sm_verbose = 0;
44 static netsec_context *nsc = NULL;
45
46 static char *sm_noreply = "No reply text given";
47 static char *sm_moreply = "; ";
48 static struct smtp sm_reply;
49
50 #define MAXEHLO 20
51
52 static int doingEHLO;
53 static char *EHLOkeys[MAXEHLO + 1];
54
55 /*
56 * static prototypes
57 */
58 static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
59 const char *, const char *, int);
60 static int sendmail_init (char *, int, int, int, int, const char *,
61 const char *);
62
63 static int rclient (char *, char *, char **);
64 static int sm_ierror (const char *fmt, ...) CHECK_PRINTF(1, 2);
65 static int sm_nerror (char *);
66 static int smtalk (int time, char *fmt, ...) CHECK_PRINTF(2, 3);
67 static int sm_wstream (char *, int);
68 static int smhear (void);
69 static char *EHLOset (char *) PURE;
70 static int sm_sasl_callback(enum sasl_message_type, unsigned const char *,
71 unsigned int, unsigned char **, unsigned int *,
72 void *, char **);
73
74 int
75 sm_init (char *client, char *server, char *port, int watch, int verbose,
76 int debug, int sasl, const char *saslmech, const char *user,
77 const char *oauth_svc, int tls)
78 {
79 if (sm_mts == MTS_SMTP)
80 return smtp_init (client, server, port, watch, verbose,
81 debug, sasl, saslmech, user, oauth_svc, tls);
82
83 return sendmail_init (client, watch, verbose, debug, sasl,
84 saslmech, user);
85 }
86
87 static int
88 smtp_init (char *client, char *server, char *port, int watch, int verbose,
89 int debug, int sasl, const char *saslmech, const char *user,
90 const char *oauth_svc, int tls)
91 {
92 int result, sd1;
93 char *errstr, *chosen_server;
94
95 if (watch)
96 verbose = true;
97
98 sm_verbose = verbose;
99 sm_debug = debug;
100
101 if (nsc != NULL)
102 goto send_options;
103
104 if (client == NULL || *client == '\0') {
105 if (clientname) {
106 client = clientname;
107 } else {
108 client = LocalName(1); /* no clientname -> LocalName */
109 }
110
111 /*
112 * Last-ditch check just in case client still isn't set to anything
113 */
114 if (client == NULL || *client == '\0')
115 client = "localhost";
116 }
117
118 nsc = netsec_init();
119
120 if (user)
121 netsec_set_userid(nsc, user);
122
123 if ((sd1 = rclient (server, port, &chosen_server)) == NOTOK)
124 return RP_BHST;
125
126 SIGNAL (SIGPIPE, SIG_IGN);
127
128 netsec_set_fd(nsc, sd1, sd1);
129
130 netsec_set_hostname(nsc, chosen_server);
131
132 if (sm_debug)
133 netsec_set_snoop(nsc, 1);
134
135 if (sasl) {
136 if (netsec_set_sasl_params(nsc, "smtp", saslmech, sm_sasl_callback,
137 NULL, &errstr) != OK)
138 return sm_nerror(errstr);
139 }
140
141 if (oauth_svc) {
142 if (netsec_set_oauth_service(nsc, oauth_svc) != OK)
143 return sm_ierror("OAuth2 not supported");
144 }
145
146 if (tls & S_TLSENABLEMASK) {
147 if (netsec_set_tls(nsc, 1, tls & S_NOVERIFY, &errstr) != OK)
148 return sm_nerror(errstr);
149 }
150
151 /*
152 * If tls == S_INITTLS, that means that the user requested
153 * "initial" TLS, which happens right after the connection has
154 * opened. Do that negotiation now
155 */
156
157 if (tls & S_INITTLS) {
158 if (netsec_negotiate_tls(nsc, &errstr) != OK) {
159 sm_end(NOTOK);
160 return sm_nerror(errstr);
161 }
162 }
163
164 netsec_set_timeout(nsc, SM_OPEN);
165 result = smhear ();
166
167 switch (result) {
168 case 220:
169 break;
170
171 default:
172 sm_end (NOTOK);
173 return RP_RPLY;
174 }
175
176 /*
177 * Give EHLO or HELO command
178 */
179
180 doingEHLO = 1;
181 result = smtalk (SM_HELO, "EHLO %s", client);
182 doingEHLO = 0;
183
184 if (result >= 500 && result <= 599)
185 result = smtalk (SM_HELO, "HELO %s", client);
186
187 if (result != 250) {
188 sm_end (NOTOK);
189 return RP_RPLY;
190 }
191
192 /*
193 * If the user requested TLS support, then try to do the STARTTLS command
194 * as part of the initial dialog. Assuming this works, we then need to
195 * restart the EHLO dialog after TLS negotiation is complete.
196 */
197
198 if (tls & S_STARTTLS) {
199 if (! EHLOset("STARTTLS")) {
200 sm_end(NOTOK);
201 return sm_ierror("SMTP server does not support TLS");
202 }
203
204 result = smtalk(SM_HELO, "STARTTLS");
205
206 if (result != 220) {
207 sm_end(NOTOK);
208 return RP_RPLY;
209 }
210
211 /*
212 * Okay, the other side should be waiting for us to start TLS
213 * negotiation. Oblige them.
214 */
215
216 if (netsec_negotiate_tls(nsc, &errstr) != OK) {
217 sm_end(NOTOK);
218 return sm_nerror(errstr);
219 }
220
221 doingEHLO = 1;
222 result = smtalk (SM_HELO, "EHLO %s", client);
223 doingEHLO = 0;
224
225 if (result != 250) {
226 sm_end (NOTOK);
227 return RP_RPLY;
228 }
229 }
230
231 /*
232 * If the user asked for SASL, then check to see if the SMTP server
233 * supports it. Otherwise, error out (because the SMTP server
234 * might have been spoofed; we don't want to just silently not
235 * do authentication
236 */
237
238 if (sasl) {
239 char *server_mechs;
240 if (! (server_mechs = EHLOset("AUTH"))) {
241 sm_end(NOTOK);
242 return sm_ierror("SMTP server does not support SASL");
243 }
244
245 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
246 sm_end(NOTOK);
247 return sm_nerror(errstr);
248 }
249 }
250
251 send_options: ;
252 if (watch && EHLOset ("XVRB"))
253 smtalk (SM_HELO, "VERB on");
254
255 return RP_OK;
256 }
257
258 int
259 sendmail_init (char *client, int watch, int verbose, int debug, int sasl,
260 const char *saslmech, const char *user)
261 {
262 unsigned int i, result, vecp;
263 int pdi[2], pdo[2];
264 char *vec[15], *errstr;
265
266 if (watch)
267 verbose = true;
268
269 sm_verbose = verbose;
270 sm_debug = debug;
271 if (nsc)
272 return RP_OK;
273
274 if (client == NULL || *client == '\0') {
275 if (clientname)
276 client = clientname;
277 else
278 client = LocalName(1); /* no clientname -> LocalName */
279
280 /*
281 * Last-ditch check just in case client still isn't set to anything
282 */
283 if (client == NULL || *client == '\0')
284 client = "localhost";
285 }
286
287 nsc = netsec_init();
288
289 if (user)
290 netsec_set_userid(nsc, user);
291
292 netsec_set_hostname(nsc, client);
293
294 if (sm_debug)
295 netsec_set_snoop(nsc, 1);
296
297 if (sasl) {
298 if (netsec_set_sasl_params(nsc, "smtp", saslmech, sm_sasl_callback,
299 NULL, &errstr) != OK)
300 return sm_nerror(errstr);
301 }
302
303 if (pipe (pdi) == NOTOK)
304 return sm_ierror ("no pipes");
305 if (pipe (pdo) == NOTOK) {
306 close (pdi[0]);
307 close (pdi[1]);
308 return sm_ierror ("no pipes");
309 }
310
311 sm_child = fork();
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[0], 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, snoopstate;
534
535 if ((snoopstate = netsec_get_snoop(nsc)))
536 netsec_set_snoop(nsc, 0);
537
538 result = sm_wstream (buffer, len);
539
540 netsec_set_snoop(nsc, snoopstate);
541 return result == NOTOK ? RP_BHST : RP_OK;
542 }
543
544
545 int
546 sm_wtend (void)
547 {
548 int snoopstate;
549
550 if ((snoopstate = netsec_get_snoop(nsc)))
551 netsec_set_snoop(nsc, 0);
552
553 if (sm_wstream(NULL, 0) == NOTOK)
554 return RP_BHST;
555
556 /*
557 * Because snoop output now happens at flush time, if we are using snoop
558 * we force an extra flush here. While this introduces some extra network
559 * traffic, we're only doing it when snoop is in effect so I think it's
560 * reasonable.
561 */
562
563 if (snoopstate) {
564 char *errstr;
565 if (netsec_flush(nsc, &errstr) != OK) {
566 sm_nerror(errstr);
567 return RP_BHST;
568 }
569 netsec_set_snoop(nsc, snoopstate);
570 }
571
572 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
573 case 250:
574 case 251:
575 return RP_OK;
576
577 case 451:
578 case 452:
579 default:
580 return RP_NO;
581
582 case 552:
583 case 554:
584 return RP_NDEL;
585 }
586 }
587
588
589 int
590 sm_end (int type)
591 {
592 int status;
593 struct smtp sm_note;
594
595 if (sm_mts == MTS_SENDMAIL_SMTP) {
596 switch (sm_child) {
597 case NOTOK:
598 case OK:
599 return RP_OK;
600
601 default:
602 break;
603 }
604 }
605
606 if (nsc == NULL)
607 return RP_OK;
608
609 switch (type) {
610 case OK:
611 smtalk (SM_QUIT, "QUIT");
612 break;
613
614 case NOTOK:
615 sm_note.code = sm_reply.code;
616 sm_note.length = sm_reply.length;
617 memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);
618 /* FALLTHRU */
619 case DONE:
620 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
621 return RP_OK;
622 if (sm_mts == MTS_SMTP)
623 smtalk (SM_QUIT, "QUIT");
624 else {
625 /* The SIGPIPE block replaces old calls to discard ().
626 We're not sure what the discard () calls were for,
627 maybe to prevent deadlock on old systems. In any
628 case, blocking SIGPIPE should be harmless.
629 Because the file handles are closed below, leave it
630 blocked. */
631 sigset_t set, oset;
632 sigemptyset (&set);
633 sigaddset (&set, SIGPIPE);
634 sigprocmask (SIG_BLOCK, &set, &oset);
635
636 kill (sm_child, SIGKILL);
637 sm_child = NOTOK;
638 }
639 if (type == NOTOK) {
640 sm_reply.code = sm_note.code;
641 sm_reply.length = sm_note.length;
642 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
643 }
644 break;
645 }
646
647 if (nsc != NULL) {
648 netsec_shutdown(nsc);
649 nsc = NULL;
650 }
651
652 if (sm_mts == MTS_SMTP) {
653 status = 0;
654 } else if (sm_child != NOTOK) {
655 status = pidwait (sm_child, OK);
656 sm_child = NOTOK;
657 } else {
658 status = OK;
659 }
660
661 return status ? RP_BHST : RP_OK;
662 }
663
664
665 static int
666 sm_ierror (const char *fmt, ...)
667 {
668 va_list ap;
669
670 va_start(ap, fmt);
671 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
672 va_end(ap);
673
674 sm_reply.length = strlen (sm_reply.text);
675 sm_reply.code = NOTOK;
676
677 return RP_BHST;
678 }
679
680 /*
681 * Like sm_ierror, but assume it's an allocated error string we need to free.
682 */
683
684 static int
685 sm_nerror (char *str)
686 {
687 strncpy(sm_reply.text, str, sizeof(sm_reply.text));
688 sm_reply.text[sizeof(sm_reply.text) - 1] = '\0';
689 sm_reply.length = strlen(sm_reply.text);
690 sm_reply.code = NOTOK;
691 free(str);
692
693 return RP_BHST;
694 }
695
696 static int
697 smtalk (int time, char *fmt, ...)
698 {
699 va_list ap;
700 char *errstr;
701 int result;
702
703 va_start(ap, fmt);
704 result = netsec_vprintf (nsc, &errstr, fmt, ap);
705 va_end(ap);
706
707 if (result != OK)
708 return sm_nerror(errstr);
709
710 if (netsec_printf (nsc, &errstr, "\r\n") != OK)
711 return sm_nerror(errstr);
712
713 if (netsec_flush (nsc, &errstr) != OK)
714 return sm_nerror(errstr);
715
716 netsec_set_timeout(nsc, time);
717
718 return smhear ();
719 }
720
721
722 static int
723 sm_wstream (char *buffer, int len)
724 {
725 char *bp, *errstr;
726 static char lc = '\0';
727 int rc;
728
729 if (nsc == NULL) {
730 sm_ierror("No socket opened");
731 return NOTOK;
732 }
733
734 if (buffer == NULL && len == 0) {
735 rc = OK;
736 if (lc != '\n')
737 rc = netsec_write(nsc, "\r\n", 2, &errstr);
738 lc = '\0';
739 if (rc != OK)
740 sm_nerror(errstr);
741 return rc;
742 }
743
744 for (bp = buffer; bp && len > 0; bp++, len--) {
745 switch (*bp) {
746 case '\n':
747 sm_nl = true;
748 if (netsec_write(nsc, "\r", 1, &errstr) != OK) {
749 sm_nerror(errstr);
750 return NOTOK;
751 }
752 break;
753
754 case '.':
755 if (sm_nl)
756 if (netsec_write(nsc, ".", 1, &errstr) != OK) {
757 sm_nerror(errstr);
758 return NOTOK;
759 }
760 /* FALLTHRU */
761
762 default:
763 sm_nl = false;
764 }
765 if (netsec_write(nsc, bp, 1, &errstr) != OK) {
766 sm_nerror(errstr);
767 return NOTOK;
768 }
769 }
770
771 if (bp > buffer)
772 lc = *--bp;
773 return OK;
774 }
775
776
777 static int
778 smhear (void)
779 {
780 int i, code;
781 bool cont, more;
782 size_t buflen, rc;
783 unsigned char *bp;
784 char *rp;
785 char *errstr;
786 char **ehlo = EHLOkeys, *buffer;
787
788 if (doingEHLO) {
789 static bool at_least_once;
790
791 if (at_least_once) {
792 char *ep;
793
794 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
795 ep = *ehlo;
796 free (ep);
797 }
798 } else {
799 at_least_once = true;
800 }
801
802 ehlo = EHLOkeys;
803 *ehlo = NULL;
804 }
805
806 again: ;
807
808 sm_reply.length = 0;
809 sm_reply.text[0] = 0;
810 rp = sm_reply.text;
811 rc = sizeof(sm_reply.text) - 1;
812
813 for (more = false; (buffer = netsec_readline(nsc, &buflen,
814 &errstr)) != NULL ; ) {
815
816 if (doingEHLO
817 && has_prefix(buffer, "250")
818 && (buffer[3] == '-' || doingEHLO == 2)
819 && buffer[4]) {
820 if (doingEHLO == 2) {
821 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
822 strcpy (*ehlo++, buffer + 4);
823 *ehlo = NULL;
824 if (ehlo >= EHLOkeys + MAXEHLO)
825 doingEHLO = 0;
826 }
827 else
828 doingEHLO = 0;
829 }
830 else
831 doingEHLO = 2;
832 }
833
834 bp = (unsigned char *) buffer;
835
836 for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
837 continue;
838
839 cont = false;
840 code = atoi ((char *) bp);
841 bp += 3, buflen -= 3;
842 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
843 continue;
844 if (buflen > 0 && *bp == '-') {
845 cont = true;
846 bp++, buflen--;
847 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
848 continue;
849 }
850
851 if (more) {
852 if (code != sm_reply.code || cont)
853 continue;
854 more = false;
855 } else {
856 sm_reply.code = code;
857 more = cont;
858 if (buflen <= 0) {
859 bp = (unsigned char *) sm_noreply;
860 buflen = strlen (sm_noreply);
861 }
862 }
863
864 if ((i = min (buflen, rc)) > 0) {
865 memcpy (rp, bp, i);
866 rp += i;
867 rc -= i;
868 i = strlen(sm_moreply);
869 if (more && (int) rc > i + 1) {
870 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
871 rp += i;
872 rc -= i;
873 }
874 }
875 if (more)
876 continue;
877 if (sm_reply.code < 100) {
878 if (sm_verbose) {
879 puts(sm_reply.text);
880 fflush (stdout);
881 }
882 goto again;
883 }
884
885 sm_reply.length = rp - sm_reply.text;
886 sm_reply.text[sm_reply.length] = 0;
887 return sm_reply.code;
888 }
889
890 sm_nerror(errstr);
891 return NOTOK;
892 }
893
894
895 char *
896 rp_string (int code)
897 {
898 char *text;
899 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
900 static char buffer[BUFSIZ + 19];
901
902 switch (sm_reply.code != NOTOK ? code : NOTOK) {
903 case RP_AOK:
904 text = "AOK";
905 break;
906
907 case RP_MOK:
908 text = "MOK";
909 break;
910
911 case RP_OK:
912 text = "OK";
913 break;
914
915 case RP_RPLY:
916 text = "RPLY";
917 break;
918
919 case RP_BHST:
920 default:
921 text = "BHST";
922 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
923 return buffer;
924
925 case RP_PARM:
926 text = "PARM";
927 break;
928
929 case RP_NO:
930 text = "NO";
931 break;
932
933 case RP_USER:
934 text = "USER";
935 break;
936
937 case RP_NDEL:
938 text = "NDEL";
939 break;
940 }
941
942 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
943 text, sm_reply.code, sm_reply.text);
944 return buffer;
945 }
946
947 static char *
948 EHLOset (char *s)
949 {
950 size_t len;
951 char *ep, **ehlo;
952
953 len = strlen (s);
954
955 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
956 ep = *ehlo;
957 if (has_prefix(ep, s)) {
958 for (ep += len; *ep == ' '; ep++)
959 continue;
960 return ep;
961 }
962 }
963
964 return NULL;
965 }
966
967 /*
968 * Our SASL callback; we are either given SASL tokens to generate network
969 * protocols messages for, or we decode incoming protocol messages and
970 * convert them to binary SASL tokens to pass up into the SASL library.
971 */
972
973 static int
974 sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
975 unsigned int indatalen, unsigned char **outdata,
976 unsigned int *outdatalen, void *context, char **errstr)
977 {
978 int rc, snoopoffset;
979 char *mech, *line;
980 size_t len;
981 NMH_UNUSED(context);
982
983 switch (mtype) {
984 case NETSEC_SASL_START:
985 /*
986 * Generate an AUTH message; if we were given an input token
987 * then generate a an AUTH message that includes the initial
988 * response.
989 */
990
991 mech = netsec_get_sasl_mechanism(nsc);
992
993 if (indatalen) {
994 char *b64data;
995
996 b64data = mh_xmalloc(BASE64SIZE(indatalen));
997 writeBase64raw(indata, indatalen, (unsigned char *) b64data);
998
999 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
1000 &snoopoffset);
1001 snoopoffset = 6 + strlen(mech);
1002 rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
1003 free(b64data);
1004 } else {
1005 rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
1006 }
1007
1008 if (rc != OK) {
1009 netsec_set_snoop_callback(nsc, NULL, NULL);
1010 return NOTOK;
1011 }
1012
1013 rc = netsec_flush(nsc, errstr);
1014 netsec_set_snoop_callback(nsc, NULL, NULL);
1015
1016 if (rc != OK)
1017 return NOTOK;
1018
1019 break;
1020
1021 case NETSEC_SASL_READ:
1022 /*
1023 * Read in a line that should contain a 334 response code, followed
1024 * by base64 response data.
1025 */
1026
1027 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
1028 snoopoffset = 4;
1029 line = netsec_readline(nsc, &len, errstr);
1030 netsec_set_snoop_callback(nsc, NULL, NULL);
1031
1032 if (line == NULL)
1033 return NOTOK;
1034
1035 if (len < 4) {
1036 netsec_err(errstr, "Invalid format for SASL response");
1037 return NOTOK;
1038 }
1039
1040 if (!has_prefix(line, "334 ")) {
1041 netsec_err(errstr, "Improper SASL protocol response: %s", line);
1042 return NOTOK;
1043 }
1044
1045 if (len == 4) {
1046 *outdata = NULL;
1047 *outdatalen = 0;
1048 } else {
1049 rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
1050 if (rc != OK) {
1051 netsec_err(errstr, "Unable to decode base64 response");
1052 return NOTOK;
1053 }
1054 *outdatalen = len;
1055 }
1056 break;
1057
1058 case NETSEC_SASL_WRITE:
1059 /*
1060 * The output encoding is pretty simple, so this is easy.
1061 */
1062 if (indatalen == 0) {
1063 rc = netsec_printf(nsc, errstr, "\r\n");
1064 } else {
1065 unsigned char *b64data;
1066 b64data = mh_xmalloc(BASE64SIZE(indatalen));
1067 writeBase64raw(indata, indatalen, b64data);
1068 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
1069 rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
1070 free(b64data);
1071 }
1072
1073 if (rc != OK)
1074 return NOTOK;
1075
1076 rc = netsec_flush(nsc, errstr);
1077 netsec_set_snoop_callback(nsc, NULL, NULL);
1078
1079 if (rc != OK)
1080 return NOTOK;
1081 break;
1082
1083 case NETSEC_SASL_FINISH:
1084 /*
1085 * Finish the protocol; we're looking for a 235 message.
1086 */
1087 line = netsec_readline(nsc, &len, errstr);
1088 if (line == NULL)
1089 return NOTOK;
1090
1091 if (!has_prefix(line, "235 ")) {
1092 if (len > 4)
1093 netsec_err(errstr, "Authentication failed: %s", line + 4);
1094 else
1095 netsec_err(errstr, "Authentication failed: %s", line);
1096 return NOTOK;
1097 }
1098 break;
1099
1100 case NETSEC_SASL_CANCEL:
1101 /*
1102 * Cancel the SASL exchange; this is done by sending a single "*".
1103 */
1104 rc = netsec_printf(nsc, errstr, "*\r\n");
1105 if (rc == OK)
1106 rc = netsec_flush(nsc, errstr);
1107 if (rc != OK)
1108 return NOTOK;
1109 break;
1110 }
1111
1112 return OK;
1113 }