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