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