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