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