]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
Fix invalid pointer arithmetic.
[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 bool 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 void *, 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 NULL, &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 NULL, &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 sm_child = fork();
311 switch (sm_child) {
312 case NOTOK:
313 close (pdo[0]);
314 close (pdo[1]);
315 close (pdi[0]);
316 close (pdi[1]);
317 return sm_ierror ("unable to fork");
318
319 case OK:
320 if (pdo[0] != fileno (stdin))
321 dup2 (pdo[0], fileno (stdin));
322 if (pdi[1] != fileno (stdout))
323 dup2 (pdi[1], fileno (stdout));
324 if (pdi[1] != fileno (stderr))
325 dup2 (pdi[1], fileno (stderr));
326 for (i = fileno (stderr) + 1; i < NBITS; i++)
327 close (i);
328
329 vecp = 0;
330 vec[vecp++] = r1bindex (sendmail, '/');
331 vec[vecp++] = "-bs";
332 vec[vecp++] = watch ? "-odi" : "-odb";
333 vec[vecp++] = "-oem";
334 vec[vecp++] = "-om";
335 if (verbose)
336 vec[vecp++] = "-ov";
337 vec[vecp++] = NULL;
338
339 execvp (sendmail, vec);
340 fprintf (stderr, "unable to exec ");
341 perror (sendmail);
342 _exit(1); /* NOTREACHED */
343
344 default:
345 SIGNAL (SIGPIPE, SIG_IGN);
346
347 close (pdi[1]);
348 close (pdo[0]);
349
350 netsec_set_fd(nsc, pdi[0], pdo[1]);
351 netsec_set_timeout(nsc, SM_OPEN);
352 result = smhear ();
353 switch (result) {
354 case 220:
355 break;
356
357 default:
358 sm_end (NOTOK);
359 return RP_RPLY;
360 }
361
362 doingEHLO = 1;
363 result = smtalk (SM_HELO, "EHLO %s", client);
364 doingEHLO = 0;
365
366 if (500 <= result && result <= 599)
367 result = smtalk (SM_HELO, "HELO %s", client);
368
369 switch (result) {
370 case 250:
371 break;
372
373 default:
374 sm_end (NOTOK);
375 return RP_RPLY;
376 }
377
378 /*
379 * If the user asked for SASL, then check to see if the SMTP server
380 * supports it. Otherwise, error out (because the SMTP server
381 * might have been spoofed; we don't want to just silently not
382 * do authentication
383 */
384
385 if (sasl) {
386 char *server_mechs;
387 if (! (server_mechs = EHLOset("AUTH"))) {
388 sm_end(NOTOK);
389 return sm_ierror("SMTP server does not support SASL");
390 }
391 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
392 sm_end(NOTOK);
393 return sm_nerror(errstr);
394 }
395 }
396
397 if (watch)
398 smtalk (SM_HELO, "VERB on");
399
400 return RP_OK;
401 }
402 }
403
404 static int
405 rclient (char *server, char *service, char **chosen_server)
406 {
407 int sd;
408 char response[BUFSIZ];
409
410 if (server == NULL)
411 server = servers;
412
413 *chosen_server = server;
414
415 if ((sd = client (server, service, response, sizeof(response),
416 sm_debug)) != NOTOK)
417 return sd;
418
419 sm_ierror ("%s", response);
420 return NOTOK;
421 }
422
423 int
424 sm_winit (char *from, int smtputf8, int eightbit)
425 {
426 const char *mail_parameters = "";
427
428 if (smtputf8) {
429 /* Just for information, if an attempt is made to send to an 8-bit
430 address without specifying SMTPUTF8, Gmail responds with
431 555 5.5.2 Syntax error.
432 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
433 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
434 mail_parameters = " BODY=8BITMIME SMTPUTF8";
435 } else {
436 inform("SMTP server does not support %s, not sending.\n"
437 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
438 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
439 sm_end (NOTOK);
440 return RP_UCMD;
441 }
442 } else if (eightbit) {
443 /* Comply with RFC 6152, for messages that have any 8-bit characters
444 in their body. */
445 if (EHLOset ("8BITMIME")) {
446 mail_parameters = " BODY=8BITMIME";
447 } else {
448 inform("SMTP server does not support 8BITMIME, not sending.\n"
449 "Suggest encoding message for 7-bit transport by setting your\n"
450 "locale to C, and/or specifying *b64 in mhbuild directives.");
451 sm_end (NOTOK);
452 return RP_UCMD;
453 }
454 }
455
456 switch (smtalk (SM_MAIL, "MAIL FROM:<%s>%s", from, mail_parameters)) {
457 case 250:
458 sm_addrs = 0;
459 return RP_OK;
460
461 case 500:
462 case 501:
463 case 552:
464 return RP_PARM;
465
466 default:
467 return RP_RPLY;
468 }
469 }
470
471
472 int
473 sm_wadr (char *mbox, char *host, char *path)
474 {
475 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
476 : "RCPT TO:<%s%s>",
477 FENDNULL(path), mbox, host)) {
478 case 250:
479 case 251:
480 sm_addrs++;
481 return RP_OK;
482
483 case 451:
484 case 421:
485 case 450:
486 case 452:
487 return RP_NO;
488
489 case 500:
490 case 501:
491 return RP_PARM;
492
493 case 550:
494 case 551:
495 case 552:
496 case 553:
497 return RP_USER;
498
499 default:
500 return RP_RPLY;
501 }
502 }
503
504
505 int
506 sm_waend (void)
507 {
508 switch (smtalk (SM_DATA, "DATA")) {
509 case 354:
510 sm_nl = true;
511 return RP_OK;
512
513 case 451:
514 case 421:
515 return RP_NO;
516
517 case 500:
518 case 501:
519 case 503:
520 case 554:
521 return RP_NDEL;
522
523 default:
524 return RP_RPLY;
525 }
526 }
527
528
529 int
530 sm_wtxt (char *buffer, int len)
531 {
532 int result, snoopstate;
533
534 if ((snoopstate = netsec_get_snoop(nsc)))
535 netsec_set_snoop(nsc, 0);
536
537 result = sm_wstream (buffer, len);
538
539 netsec_set_snoop(nsc, snoopstate);
540 return result == NOTOK ? RP_BHST : RP_OK;
541 }
542
543
544 int
545 sm_wtend (void)
546 {
547 int snoopstate;
548
549 if ((snoopstate = netsec_get_snoop(nsc)))
550 netsec_set_snoop(nsc, 0);
551
552 if (sm_wstream(NULL, 0) == NOTOK)
553 return RP_BHST;
554
555 /*
556 * Because snoop output now happens at flush time, if we are using snoop
557 * we force an extra flush here. While this introduces some extra network
558 * traffic, we're only doing it when snoop is in effect so I think it's
559 * reasonable.
560 */
561
562 if (snoopstate) {
563 char *errstr;
564 if (netsec_flush(nsc, &errstr) != OK) {
565 sm_nerror(errstr);
566 return RP_BHST;
567 }
568 netsec_set_snoop(nsc, snoopstate);
569 }
570
571 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
572 case 250:
573 case 251:
574 return RP_OK;
575
576 case 451:
577 case 452:
578 default:
579 return RP_NO;
580
581 case 552:
582 case 554:
583 return RP_NDEL;
584 }
585 }
586
587
588 int
589 sm_end (int type)
590 {
591 int status;
592 struct smtp sm_note;
593
594 if (sm_mts == MTS_SENDMAIL_SMTP) {
595 switch (sm_child) {
596 case NOTOK:
597 case OK:
598 return RP_OK;
599
600 default:
601 break;
602 }
603 }
604
605 if (nsc == NULL)
606 return RP_OK;
607
608 switch (type) {
609 case OK:
610 smtalk (SM_QUIT, "QUIT");
611 break;
612
613 case NOTOK:
614 sm_note.code = sm_reply.code;
615 sm_note.length = sm_reply.length;
616 memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);
617 /* FALLTHRU */
618 case DONE:
619 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
620 return RP_OK;
621 if (sm_mts == MTS_SMTP)
622 smtalk (SM_QUIT, "QUIT");
623 else {
624 /* The SIGPIPE block replaces old calls to discard ().
625 We're not sure what the discard () calls were for,
626 maybe to prevent deadlock on old systems. In any
627 case, blocking SIGPIPE should be harmless.
628 Because the file handles are closed below, leave it
629 blocked. */
630 sigset_t set, oset;
631 sigemptyset (&set);
632 sigaddset (&set, SIGPIPE);
633 sigprocmask (SIG_BLOCK, &set, &oset);
634
635 kill (sm_child, SIGKILL);
636 sm_child = NOTOK;
637 }
638 if (type == NOTOK) {
639 sm_reply.code = sm_note.code;
640 sm_reply.length = sm_note.length;
641 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
642 }
643 break;
644 }
645
646 if (nsc != NULL) {
647 netsec_shutdown(nsc);
648 nsc = NULL;
649 }
650
651 if (sm_mts == MTS_SMTP) {
652 status = 0;
653 } else if (sm_child != NOTOK) {
654 status = pidwait (sm_child, OK);
655 sm_child = NOTOK;
656 } else {
657 status = OK;
658 }
659
660 return status ? RP_BHST : RP_OK;
661 }
662
663
664 static int
665 sm_ierror (const char *fmt, ...)
666 {
667 va_list ap;
668
669 va_start(ap, fmt);
670 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
671 va_end(ap);
672
673 sm_reply.length = strlen (sm_reply.text);
674 sm_reply.code = NOTOK;
675
676 return RP_BHST;
677 }
678
679 /*
680 * Like sm_ierror, but assume it's an allocated error string we need to free.
681 */
682
683 static int
684 sm_nerror (char *str)
685 {
686 strncpy(sm_reply.text, str, sizeof(sm_reply.text));
687 sm_reply.text[sizeof(sm_reply.text) - 1] = '\0';
688 sm_reply.length = strlen(sm_reply.text);
689 sm_reply.code = NOTOK;
690 free(str);
691
692 return RP_BHST;
693 }
694
695 static int
696 smtalk (int time, char *fmt, ...)
697 {
698 va_list ap;
699 char *errstr;
700 int result;
701
702 va_start(ap, fmt);
703 result = netsec_vprintf (nsc, &errstr, fmt, ap);
704 va_end(ap);
705
706 if (result != OK)
707 return sm_nerror(errstr);
708
709 if (netsec_printf (nsc, &errstr, "\r\n") != OK)
710 return sm_nerror(errstr);
711
712 if (netsec_flush (nsc, &errstr) != OK)
713 return sm_nerror(errstr);
714
715 netsec_set_timeout(nsc, time);
716
717 return smhear ();
718 }
719
720
721 static int
722 sm_wstream (char *buffer, int len)
723 {
724 char *bp, *errstr;
725 static char lc = '\0';
726 int rc;
727
728 if (nsc == NULL) {
729 sm_ierror("No socket opened");
730 return NOTOK;
731 }
732
733 if (buffer == NULL && len == 0) {
734 rc = OK;
735 if (lc != '\n')
736 rc = netsec_write(nsc, "\r\n", 2, &errstr);
737 lc = '\0';
738 if (rc != OK)
739 sm_nerror(errstr);
740 return rc;
741 }
742
743 for (bp = buffer; bp && len > 0; bp++, len--) {
744 switch (*bp) {
745 case '\n':
746 sm_nl = true;
747 if (netsec_write(nsc, "\r", 1, &errstr) != OK) {
748 sm_nerror(errstr);
749 return NOTOK;
750 }
751 break;
752
753 case '.':
754 if (sm_nl)
755 if (netsec_write(nsc, ".", 1, &errstr) != OK) {
756 sm_nerror(errstr);
757 return NOTOK;
758 }
759 /* FALLTHRU */
760
761 default:
762 sm_nl = false;
763 }
764 if (netsec_write(nsc, bp, 1, &errstr) != OK) {
765 sm_nerror(errstr);
766 return NOTOK;
767 }
768 }
769
770 if (bp > buffer)
771 lc = *--bp;
772 return OK;
773 }
774
775
776 static int
777 smhear (void)
778 {
779 int i, code;
780 bool cont, more;
781 size_t buflen, rc;
782 unsigned char *bp;
783 char *rp;
784 char *errstr;
785 char **ehlo = EHLOkeys, *buffer;
786
787 if (doingEHLO) {
788 static int at_least_once = 0;
789
790 if (at_least_once) {
791 char *ep;
792
793 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
794 ep = *ehlo;
795 free (ep);
796 }
797 } else {
798 at_least_once = 1;
799 }
800
801 ehlo = EHLOkeys;
802 *ehlo = NULL;
803 }
804
805 again: ;
806
807 sm_reply.length = 0;
808 sm_reply.text[0] = 0;
809 rp = sm_reply.text;
810 rc = sizeof(sm_reply.text) - 1;
811
812 for (more = false; (buffer = netsec_readline(nsc, &buflen,
813 &errstr)) != NULL ; ) {
814
815 if (doingEHLO
816 && has_prefix(buffer, "250")
817 && (buffer[3] == '-' || doingEHLO == 2)
818 && buffer[4]) {
819 if (doingEHLO == 2) {
820 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
821 strcpy (*ehlo++, buffer + 4);
822 *ehlo = NULL;
823 if (ehlo >= EHLOkeys + MAXEHLO)
824 doingEHLO = 0;
825 }
826 else
827 doingEHLO = 0;
828 }
829 else
830 doingEHLO = 2;
831 }
832
833 bp = (unsigned char *) buffer;
834
835 for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
836 continue;
837
838 cont = false;
839 code = atoi ((char *) bp);
840 bp += 3, buflen -= 3;
841 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
842 continue;
843 if (buflen > 0 && *bp == '-') {
844 cont = true;
845 bp++, buflen--;
846 for (; buflen > 0 && isspace (*bp); bp++, buflen--)
847 continue;
848 }
849
850 if (more) {
851 if (code != sm_reply.code || cont)
852 continue;
853 more = false;
854 } else {
855 sm_reply.code = code;
856 more = cont;
857 if (buflen <= 0) {
858 bp = (unsigned char *) sm_noreply;
859 buflen = strlen (sm_noreply);
860 }
861 }
862
863 if ((i = min (buflen, rc)) > 0) {
864 memcpy (rp, bp, i);
865 rp += i;
866 rc -= i;
867 i = strlen(sm_moreply);
868 if (more && (int) rc > i + 1) {
869 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
870 rp += i;
871 rc -= i;
872 }
873 }
874 if (more)
875 continue;
876 if (sm_reply.code < 100) {
877 if (sm_verbose) {
878 puts(sm_reply.text);
879 fflush (stdout);
880 }
881 goto again;
882 }
883
884 sm_reply.length = rp - sm_reply.text;
885 sm_reply.text[sm_reply.length] = 0;
886 return sm_reply.code;
887 }
888
889 sm_nerror(errstr);
890 return NOTOK;
891 }
892
893
894 char *
895 rp_string (int code)
896 {
897 char *text;
898 /* The additional space is to avoid warning from gcc -Wformat-truncation. */
899 static char buffer[BUFSIZ + 19];
900
901 switch (sm_reply.code != NOTOK ? code : NOTOK) {
902 case RP_AOK:
903 text = "AOK";
904 break;
905
906 case RP_MOK:
907 text = "MOK";
908 break;
909
910 case RP_OK:
911 text = "OK";
912 break;
913
914 case RP_RPLY:
915 text = "RPLY";
916 break;
917
918 case RP_BHST:
919 default:
920 text = "BHST";
921 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
922 return buffer;
923
924 case RP_PARM:
925 text = "PARM";
926 break;
927
928 case RP_NO:
929 text = "NO";
930 break;
931
932 case RP_USER:
933 text = "USER";
934 break;
935
936 case RP_NDEL:
937 text = "NDEL";
938 break;
939 }
940
941 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
942 text, sm_reply.code, sm_reply.text);
943 return buffer;
944 }
945
946 static char *
947 EHLOset (char *s)
948 {
949 size_t len;
950 char *ep, **ehlo;
951
952 len = strlen (s);
953
954 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
955 ep = *ehlo;
956 if (has_prefix(ep, s)) {
957 for (ep += len; *ep == ' '; ep++)
958 continue;
959 return ep;
960 }
961 }
962
963 return NULL;
964 }
965
966 /*
967 * Our SASL callback; we are either given SASL tokens to generate network
968 * protocols messages for, or we decode incoming protocol messages and
969 * convert them to binary SASL tokens to pass up into the SASL library.
970 */
971
972 static int
973 sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
974 unsigned int indatalen, unsigned char **outdata,
975 unsigned int *outdatalen, void *context, char **errstr)
976 {
977 int rc, snoopoffset;
978 char *mech, *line;
979 size_t len;
980 NMH_UNUSED(context);
981
982 switch (mtype) {
983 case NETSEC_SASL_START:
984 /*
985 * Generate an AUTH message; if we were given an input token
986 * then generate a an AUTH message that includes the initial
987 * response.
988 */
989
990 mech = netsec_get_sasl_mechanism(nsc);
991
992 if (indatalen) {
993 char *b64data;
994
995 b64data = mh_xmalloc(BASE64SIZE(indatalen));
996 writeBase64raw(indata, indatalen, (unsigned char *) b64data);
997
998 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
999 &snoopoffset);
1000 snoopoffset = 6 + strlen(mech);
1001 rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
1002 free(b64data);
1003 } else {
1004 rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
1005 }
1006
1007 if (rc != OK) {
1008 netsec_set_snoop_callback(nsc, NULL, NULL);
1009 return NOTOK;
1010 }
1011
1012 rc = netsec_flush(nsc, errstr);
1013 netsec_set_snoop_callback(nsc, NULL, NULL);
1014
1015 if (rc != OK)
1016 return NOTOK;
1017
1018 break;
1019
1020 case NETSEC_SASL_READ:
1021 /*
1022 * Read in a line that should contain a 334 response code, followed
1023 * by base64 response data.
1024 */
1025
1026 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
1027 snoopoffset = 4;
1028 line = netsec_readline(nsc, &len, errstr);
1029 netsec_set_snoop_callback(nsc, NULL, NULL);
1030
1031 if (line == NULL)
1032 return NOTOK;
1033
1034 if (len < 4) {
1035 netsec_err(errstr, "Invalid format for SASL response");
1036 return NOTOK;
1037 }
1038
1039 if (!has_prefix(line, "334 ")) {
1040 netsec_err(errstr, "Improper SASL protocol response: %s", line);
1041 return NOTOK;
1042 }
1043
1044 if (len == 4) {
1045 *outdata = NULL;
1046 *outdatalen = 0;
1047 } else {
1048 rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
1049 if (rc != OK) {
1050 netsec_err(errstr, "Unable to decode base64 response");
1051 return NOTOK;
1052 }
1053 *outdatalen = len;
1054 }
1055 break;
1056
1057 case NETSEC_SASL_WRITE:
1058 /*
1059 * The output encoding is pretty simple, so this is easy.
1060 */
1061 if (indatalen == 0) {
1062 rc = netsec_printf(nsc, errstr, "\r\n");
1063 } else {
1064 unsigned char *b64data;
1065 b64data = mh_xmalloc(BASE64SIZE(indatalen));
1066 writeBase64raw(indata, indatalen, b64data);
1067 netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
1068 rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
1069 free(b64data);
1070 }
1071
1072 if (rc != OK)
1073 return NOTOK;
1074
1075 rc = netsec_flush(nsc, errstr);
1076 netsec_set_snoop_callback(nsc, NULL, NULL);
1077
1078 if (rc != OK)
1079 return NOTOK;
1080 break;
1081
1082 case NETSEC_SASL_FINISH:
1083 /*
1084 * Finish the protocol; we're looking for a 235 message.
1085 */
1086 line = netsec_readline(nsc, &len, errstr);
1087 if (line == NULL)
1088 return NOTOK;
1089
1090 if (!has_prefix(line, "235 ")) {
1091 if (len > 4)
1092 netsec_err(errstr, "Authentication failed: %s", line + 4);
1093 else
1094 netsec_err(errstr, "Authentication failed: %s", line);
1095 return NOTOK;
1096 }
1097 break;
1098
1099 case NETSEC_SASL_CANCEL:
1100 /*
1101 * Cancel the SASL exchange; this is done by sending a single "*".
1102 */
1103 rc = netsec_printf(nsc, errstr, "*\r\n");
1104 if (rc == OK)
1105 rc = netsec_flush(nsc, errstr);
1106 if (rc != OK)
1107 return NOTOK;
1108 break;
1109 }
1110
1111 return OK;
1112 }