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