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