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