]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
Don't `else' after return. Simplify control flow.
[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 else
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 /*
134 * Last-ditch check just in case client still isn't set to anything
135 */
136
137 if (client == NULL || *client == '\0')
138 client = "localhost";
139
140 nsc = netsec_init();
141
142 if (user)
143 netsec_set_userid(nsc, user);
144
145 if (sm_debug)
146 netsec_set_snoop(nsc, 1);
147
148 if (sasl) {
149 if (netsec_set_sasl_params(nsc, server, "smtp", saslmech,
150 sm_sasl_callback, &errstr) != OK)
151 return sm_nerror(errstr);
152 }
153
154 if (oauth_svc) {
155 if (netsec_set_oauth_service(nsc, oauth_svc) != OK)
156 return sm_ierror("OAuth2 not supported");
157 }
158
159 if ((sd1 = rclient (server, port)) == NOTOK)
160 return RP_BHST;
161
162 SIGNAL (SIGPIPE, SIG_IGN);
163
164 netsec_set_fd(nsc, sd1, sd1);
165
166 if (tls) {
167 if (netsec_set_tls(nsc, 1, &errstr) != OK)
168 return sm_nerror(errstr);
169 }
170
171 /*
172 * If tls == 2, that means that the user requested "initial" TLS,
173 * which happens right after the connection has opened. Do that
174 * negotiation now
175 */
176
177 if (tls == 2) {
178 if (netsec_negotiate_tls(nsc, &errstr) != OK) {
179 sm_end(NOTOK);
180 return sm_nerror(errstr);
181 }
182 }
183
184 netsec_set_timeout(nsc, SM_OPEN);
185 result = smhear ();
186
187 switch (result) {
188 case 220:
189 break;
190
191 default:
192 sm_end (NOTOK);
193 return RP_RPLY;
194 }
195
196 /*
197 * Give EHLO or HELO command
198 */
199
200 doingEHLO = 1;
201 result = smtalk (SM_HELO, "EHLO %s", client);
202 doingEHLO = 0;
203
204 if (result >= 500 && result <= 599)
205 result = smtalk (SM_HELO, "HELO %s", client);
206
207 if (result != 250) {
208 sm_end (NOTOK);
209 return RP_RPLY;
210 }
211
212 /*
213 * If the user requested TLS support, then try to do the STARTTLS command
214 * as part of the initial dialog. Assuming this works, we then need to
215 * restart the EHLO dialog after TLS negotiation is complete.
216 */
217
218 if (tls == 1) {
219 if (! EHLOset("STARTTLS")) {
220 sm_end(NOTOK);
221 return sm_ierror("SMTP server does not support TLS");
222 }
223
224 result = smtalk(SM_HELO, "STARTTLS");
225
226 if (result != 220) {
227 sm_end(NOTOK);
228 return RP_RPLY;
229 }
230
231 /*
232 * Okay, the other side should be waiting for us to start TLS
233 * negotiation. Oblige them.
234 */
235
236 if (netsec_negotiate_tls(nsc, &errstr) != OK) {
237 sm_end(NOTOK);
238 return sm_nerror(errstr);
239 }
240
241 doingEHLO = 1;
242 result = smtalk (SM_HELO, "EHLO %s", client);
243 doingEHLO = 0;
244
245 if (result != 250) {
246 sm_end (NOTOK);
247 return RP_RPLY;
248 }
249 }
250
251 /*
252 * If the user asked for SASL, then check to see if the SMTP server
253 * supports it. Otherwise, error out (because the SMTP server
254 * might have been spoofed; we don't want to just silently not
255 * do authentication
256 */
257
258 if (sasl) {
259 char *server_mechs;
260 if (! (server_mechs = EHLOset("AUTH"))) {
261 sm_end(NOTOK);
262 return sm_ierror("SMTP server does not support SASL");
263 }
264
265 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
266 sm_end(NOTOK);
267 return sm_nerror(errstr);
268 }
269 }
270
271 send_options: ;
272 if (watch && EHLOset ("XVRB"))
273 smtalk (SM_HELO, "VERB on");
274
275 return RP_OK;
276 }
277
278 int
279 sendmail_init (char *client, int watch, int verbose, int debug, int sasl,
280 const char *saslmech, const char *user)
281 {
282 unsigned int i, result, vecp;
283 int pdi[2], pdo[2];
284 char *vec[15], *errstr;
285
286 if (watch)
287 verbose = TRUE;
288
289 sm_verbose = verbose;
290 sm_debug = debug;
291 if (nsc)
292 return RP_OK;
293
294 if (client == NULL || *client == '\0') {
295 if (clientname)
296 client = clientname;
297 else
298 client = LocalName(1); /* no clientname -> LocalName */
299 }
300
301 /*
302 * Last-ditch check just in case client still isn't set to anything
303 */
304
305 if (client == NULL || *client == '\0')
306 client = "localhost";
307
308 nsc = netsec_init();
309
310 if (user)
311 netsec_set_userid(nsc, user);
312
313 if (sm_debug)
314 netsec_set_snoop(nsc, 1);
315
316 if (sasl) {
317 if (netsec_set_sasl_params(nsc, client, "smtp", saslmech,
318 sm_sasl_callback, &errstr) != OK)
319 return sm_nerror(errstr);
320 }
321
322 if (pipe (pdi) == NOTOK)
323 return sm_ierror ("no pipes");
324 if (pipe (pdo) == NOTOK) {
325 close (pdi[0]);
326 close (pdi[1]);
327 return sm_ierror ("no pipes");
328 }
329
330 for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
331 sleep (5);
332
333 switch (sm_child) {
334 case NOTOK:
335 close (pdo[0]);
336 close (pdo[1]);
337 close (pdi[0]);
338 close (pdi[1]);
339 return sm_ierror ("unable to fork");
340
341 case OK:
342 if (pdo[0] != fileno (stdin))
343 dup2 (pdo[0], fileno (stdin));
344 if (pdi[1] != fileno (stdout))
345 dup2 (pdi[1], fileno (stdout));
346 if (pdi[1] != fileno (stderr))
347 dup2 (pdi[1], fileno (stderr));
348 for (i = fileno (stderr) + 1; i < NBITS; i++)
349 close (i);
350
351 vecp = 0;
352 vec[vecp++] = r1bindex (sendmail, '/');
353 vec[vecp++] = "-bs";
354 vec[vecp++] = watch ? "-odi" : "-odb";
355 vec[vecp++] = "-oem";
356 vec[vecp++] = "-om";
357 if (verbose)
358 vec[vecp++] = "-ov";
359 vec[vecp++] = NULL;
360
361 execvp (sendmail, vec);
362 fprintf (stderr, "unable to exec ");
363 perror (sendmail);
364 _exit (-1); /* NOTREACHED */
365
366 default:
367 SIGNAL (SIGPIPE, SIG_IGN);
368
369 close (pdi[1]);
370 close (pdo[0]);
371
372 netsec_set_fd(nsc, pdi[i], pdo[1]);
373 netsec_set_timeout(nsc, SM_OPEN);
374 result = smhear ();
375 switch (result) {
376 case 220:
377 break;
378
379 default:
380 sm_end (NOTOK);
381 return RP_RPLY;
382 }
383
384 doingEHLO = 1;
385 result = smtalk (SM_HELO, "EHLO %s", client);
386 doingEHLO = 0;
387
388 if (500 <= result && result <= 599)
389 result = smtalk (SM_HELO, "HELO %s", client);
390
391 switch (result) {
392 case 250:
393 break;
394
395 default:
396 sm_end (NOTOK);
397 return RP_RPLY;
398 }
399
400 /*
401 * If the user asked for SASL, then check to see if the SMTP server
402 * supports it. Otherwise, error out (because the SMTP server
403 * might have been spoofed; we don't want to just silently not
404 * do authentication
405 */
406
407 if (sasl) {
408 char *server_mechs;
409 if (! (server_mechs = EHLOset("AUTH"))) {
410 sm_end(NOTOK);
411 return sm_ierror("SMTP server does not support SASL");
412 }
413 if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
414 sm_end(NOTOK);
415 return sm_nerror(errstr);
416 }
417 }
418
419 if (watch)
420 smtalk (SM_HELO, "VERB on");
421
422 return RP_OK;
423 }
424 }
425
426 static int
427 rclient (char *server, char *service)
428 {
429 int sd;
430 char response[BUFSIZ];
431
432 if ((sd = client (server, service, response, sizeof(response),
433 sm_debug)) != NOTOK)
434 return sd;
435
436 sm_ierror ("%s", response);
437 return NOTOK;
438 }
439
440 int
441 sm_winit (char *from, int smtputf8, int eightbit)
442 {
443 const char *mail_parameters = "";
444
445 if (smtputf8) {
446 /* Just for information, if an attempt is made to send to an 8-bit
447 address without specifying SMTPUTF8, Gmail responds with
448 555 5.5.2 Syntax error.
449 Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
450 if (EHLOset ("8BITMIME") && EHLOset ("SMTPUTF8")) {
451 mail_parameters = " BODY=8BITMIME SMTPUTF8";
452 } else {
453 advise (NULL, "SMTP server does not support %s, not sending.\n"
454 "Rebuild message with 7-bit headers, WITHOUT -headerencoding utf-8.",
455 EHLOset ("SMTPUTF8") ? "8BITMIME" : "SMTPUTF8");
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.");
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", LEN("250")) == 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 }