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