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