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