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