]> diplodocus.org Git - nmh/blob - uip/popsbr.c
Save this for now.
[nmh] / uip / popsbr.c
1 /*
2 * popsbr.c -- POP client subroutines
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 <h/utils.h>
11 #include <h/oauth.h>
12
13 #ifdef CYRUS_SASL
14 # include <sasl/sasl.h>
15 # include <sasl/saslutil.h>
16 # if SASL_VERSION_FULL < 0x020125
17 /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
18 which has an explicit void parameter list, according to best
19 practice. So we need to cast to avoid compile warnings.
20 Provide this prototype for earlier versions. */
21 typedef int (*sasl_callback_ft)();
22 # endif /* SASL_VERSION_FULL < 0x020125 */
23 #endif /* CYRUS_SASL */
24
25 #ifdef TLS_SUPPORT
26 #include <openssl/ssl.h>
27 #include <openssl/err.h>
28 #endif /* TLS_SUPPORT */
29
30 #include <h/popsbr.h>
31 #include <h/signals.h>
32
33 #define TRM "."
34 #define TRMLEN (sizeof TRM - 1)
35
36 static int poprint = 0;
37 static int pophack = 0;
38
39 char response[BUFSIZ];
40
41 static FILE *input;
42 static FILE *output;
43
44 #ifdef CYRUS_SASL
45 static sasl_conn_t *conn; /* SASL connection state */
46 static int sasl_complete = 0; /* Has sasl authentication succeeded? */
47 static int maxoutbuf; /* Maximum output buffer size */
48 static sasl_ssf_t sasl_ssf = 0; /* Security strength factor */
49 static int sasl_get_user(void *, int, const char **, unsigned *);
50 static int sasl_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
51 struct pass_context {
52 char *user;
53 char *password;
54 char *host;
55 };
56
57 static sasl_callback_t callbacks[] = {
58 { SASL_CB_USER, (sasl_callback_ft) sasl_get_user, NULL },
59 #define POP_SASL_CB_N_USER 0
60 { SASL_CB_PASS, (sasl_callback_ft) sasl_get_pass, NULL },
61 #define POP_SASL_CB_N_PASS 1
62 { SASL_CB_LOG, NULL, NULL },
63 { SASL_CB_LIST_END, NULL, NULL },
64
65 #define SASL_BUFFER_SIZE 262144
66 };
67 #endif /* CYRUS_SASL */
68
69 /*
70 * static prototypes
71 */
72
73 static int command(const char *, ...);
74 static int multiline(void);
75
76 #ifdef CYRUS_SASL
77 static int pop_auth_sasl(char *, char *, char *);
78 #endif /* CYRUS_SASL */
79
80 #ifdef TLS_SUPPORT
81 static SSL_CTX *sslctx = NULL;
82 static SSL *ssl = NULL;
83 static BIO *sbior = NULL;
84 static BIO *sbiow = NULL;
85 static BIO *io = NULL;
86
87 static int tls_negotiate(void);
88 #endif /* TLS_SUPPORT */
89
90 static int tls_active = 0;
91
92 static int traverse (int (*)(char *), const char *, ...);
93 static int vcommand(const char *, va_list);
94 static int sasl_getline (char *, int, FILE *);
95 static int sasl_fgetc(FILE *);
96 static int putline (char *, FILE *);
97
98
99 int
100 check_mech(char *server_mechs, size_t server_mechs_size, char *mech)
101 {
102 int status, sasl_capability = 0;
103
104 /*
105 * First off, we're going to send the CAPA command to see if we can
106 * even support the AUTH command, and if we do, then we'll get a
107 * list of mechanisms the server supports. If we don't support
108 * the CAPA command, then it's unlikely that we will support
109 * SASL
110 */
111
112 if (command("CAPA") == NOTOK) {
113 snprintf(response, sizeof(response),
114 "The POP CAPA command failed; POP server does not "
115 "support SASL");
116 return NOTOK;
117 }
118
119 while ((status = multiline()) != DONE)
120 switch (status) {
121 case NOTOK:
122 return NOTOK;
123 break;
124 case DONE: /* Shouldn't be possible, but just in case */
125 break;
126 case OK:
127 if (strncasecmp(response, "SASL ", 5) == 0) {
128 /*
129 * We've seen the SASL capability. Grab the mech list
130 */
131 sasl_capability++;
132 strncpy(server_mechs, response + 5, server_mechs_size);
133 }
134 break;
135 }
136
137 if (!sasl_capability) {
138 snprintf(response, sizeof(response), "POP server does not support "
139 "SASL");
140 return NOTOK;
141 }
142
143 /*
144 * If we received a preferred mechanism, see if the server supports it.
145 */
146
147 if (mech && stringdex(mech, server_mechs) == -1) {
148 snprintf(response, sizeof(response), "Requested SASL mech \"%s\" is "
149 "not in list of supported mechanisms:\n%s",
150 mech, server_mechs);
151 return NOTOK;
152 }
153
154 return OK;
155 }
156
157 #ifdef CYRUS_SASL
158 /*
159 * This function implements the AUTH command for various SASL mechanisms
160 *
161 * We do the whole SASL dialog here. If this completes, then we've
162 * authenticated successfully and have (possibly) negotiated a security
163 * layer.
164 */
165
166 #define CHECKB64SIZE(insize, outbuf, outsize) \
167 { size_t wantout = (((insize + 2) / 3) * 4) + 32; \
168 if (wantout > outsize) { \
169 outbuf = mh_xrealloc(outbuf, outsize = wantout); \
170 } \
171 }
172
173 int
174 pop_auth_sasl(char *user, char *host, char *mech)
175 {
176 int result, status;
177 unsigned int buflen, outlen;
178 char server_mechs[256], *buf, *outbuf = NULL;
179 size_t outbufsize = 0;
180 const char *chosen_mech;
181 sasl_security_properties_t secprops;
182 struct pass_context p_context;
183 sasl_ssf_t *ssf;
184 int *moutbuf;
185
186 if ((status = check_mech(server_mechs, sizeof(server_mechs), mech)) != OK) {
187 return status;
188 }
189
190 /*
191 * Start the SASL process. First off, initialize the SASL library.
192 */
193
194 callbacks[POP_SASL_CB_N_USER].context = user;
195 p_context.user = user;
196 p_context.host = host;
197 callbacks[POP_SASL_CB_N_PASS].context = &p_context;
198
199 result = sasl_client_init(callbacks);
200
201 if (result != SASL_OK) {
202 snprintf(response, sizeof(response), "SASL library initialization "
203 "failed: %s", sasl_errstring(result, NULL, NULL));
204 return NOTOK;
205 }
206
207 result = sasl_client_new("pop", host, NULL, NULL, NULL, 0, &conn);
208
209 if (result != SASL_OK) {
210 snprintf(response, sizeof(response), "SASL client initialization "
211 "failed: %s", sasl_errstring(result, NULL, NULL));
212 return NOTOK;
213 }
214
215 /*
216 * Initialize the security properties
217 */
218
219 memset(&secprops, 0, sizeof(secprops));
220 secprops.maxbufsize = SASL_BUFFER_SIZE;
221 secprops.max_ssf = tls_active ? 0 : UINT_MAX;
222
223 result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
224
225 if (result != SASL_OK) {
226 snprintf(response, sizeof(response), "SASL security property "
227 "initialization failed: %s", sasl_errdetail(conn));
228 return NOTOK;
229 }
230
231 /*
232 * Start the actual protocol. Feed the mech list into the library
233 * and get out a possible initial challenge
234 */
235
236 result = sasl_client_start(conn,
237 (const char *) (mech ? mech : server_mechs),
238 NULL, (const char **) &buf,
239 &buflen, &chosen_mech);
240
241 if (result != SASL_OK && result != SASL_CONTINUE) {
242 snprintf(response, sizeof(response), "SASL client start failed: %s",
243 sasl_errdetail(conn));
244 return NOTOK;
245 }
246
247 if (buflen) {
248 CHECKB64SIZE(buflen, outbuf, outbufsize);
249 status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
250 if (status != SASL_OK) {
251 snprintf(response, sizeof(response), "SASL base64 encode "
252 "failed: %s", sasl_errstring(status, NULL, NULL));
253 if (outbuf)
254 free(outbuf);
255 return NOTOK;
256 }
257
258 status = command("AUTH %s %s", chosen_mech, outbuf);
259 } else
260 status = command("AUTH %s", chosen_mech);
261
262 while (result == SASL_CONTINUE) {
263 size_t inlen;
264
265 if (status == NOTOK) {
266 if (outbuf)
267 free(outbuf);
268 return NOTOK;
269 }
270
271 /*
272 * If we get a "+OK" prefix to our response, then we should
273 * exit out of this exchange now (because authenticated should
274 * have succeeded)
275 */
276
277 if (strncmp(response, "+OK", 3) == 0)
278 break;
279
280 /*
281 * Otherwise, make sure the server challenge is correctly formatted
282 */
283
284 if (strncmp(response, "+ ", 2) != 0) {
285 command("*");
286 snprintf(response, sizeof(response),
287 "Malformed authentication message from server");
288 if (outbuf)
289 free(outbuf);
290 return NOTOK;
291 }
292
293 /*
294 * For decode, it will always be shorter, so just make sure
295 * that outbuf is as at least as big as the encoded response.
296 */
297
298 inlen = strlen(response + 2);
299
300 if (inlen > outbufsize) {
301 outbuf = mh_xrealloc(outbuf, outbufsize = inlen);
302 }
303
304 result = sasl_decode64(response + 2, strlen(response + 2),
305 outbuf, outbufsize, &outlen);
306
307 if (result != SASL_OK) {
308 command("*");
309 snprintf(response, sizeof(response), "SASL base64 decode "
310 "failed: %s", sasl_errstring(result, NULL, NULL));
311 if (outbuf)
312 free(outbuf);
313 return NOTOK;
314 }
315
316 result = sasl_client_step(conn, outbuf, outlen, NULL,
317 (const char **) &buf, &buflen);
318
319 if (result != SASL_OK && result != SASL_CONTINUE) {
320 command("*");
321 snprintf(response, sizeof(response), "SASL client negotiaton "
322 "failed: %s", sasl_errdetail(conn));
323 if (outbuf)
324 free(outbuf);
325 return NOTOK;
326 }
327
328 CHECKB64SIZE(buflen, outbuf, outbufsize);
329
330 status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
331
332 if (status != SASL_OK) {
333 command("*");
334 snprintf(response, sizeof(response), "SASL base64 encode "
335 "failed: %s", sasl_errstring(status, NULL, NULL));
336 if (outbuf)
337 free(outbuf);
338 return NOTOK;
339 }
340
341 status = command(outbuf);
342 }
343
344 if (outbuf)
345 free(outbuf);
346
347 /*
348 * If we didn't get a positive final response, then error out
349 * (that probably means we failed an authorization check).
350 */
351
352 if (status != OK)
353 return NOTOK;
354
355 /*
356 * We _should_ be okay now. Get a few properties now that negotiation
357 * has completed.
358 */
359
360 result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &moutbuf);
361
362 if (result != SASL_OK) {
363 snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
364 "output buffer size: %s", sasl_errdetail(conn));
365 return NOTOK;
366 }
367
368 maxoutbuf = *moutbuf;
369
370 result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
371
372 sasl_ssf = *ssf;
373
374 if (result != SASL_OK) {
375 snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
376 "security strength factor: %s", sasl_errdetail(conn));
377 return NOTOK;
378 }
379
380 /*
381 * Limit this to what we can deal with. Shouldn't matter much because
382 * this is only outgoing data (which should be small)
383 */
384
385 if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
386 maxoutbuf = BUFSIZ;
387
388 sasl_complete = 1;
389
390 return status;
391 }
392
393 /*
394 * Callback to return the userid sent down via the user parameter
395 */
396
397 static int
398 sasl_get_user(void *context, int id, const char **result, unsigned *len)
399 {
400 char *user = (char *) context;
401
402 if (! result || id != SASL_CB_USER)
403 return SASL_BADPARAM;
404
405 *result = user;
406 if (len)
407 *len = strlen(user);
408
409 return SASL_OK;
410 }
411
412 /*
413 * Callback to return the password (we call ruserpass, which can get it
414 * out of the .netrc
415 */
416
417 static int
418 sasl_get_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
419 {
420 struct pass_context *p_context = (struct pass_context *) context;
421 struct nmh_creds creds = { 0, 0, 0 };
422 int len;
423
424 NMH_UNUSED (conn);
425
426 if (! psecret || id != SASL_CB_PASS)
427 return SASL_BADPARAM;
428
429 if (creds.password == NULL) {
430 /*
431 * Pass the 0 third argument to nmh_get_credentials() so
432 * that the default password isn't used. With legacy/.netrc
433 * credentials support, we'll only get here if the -user
434 * switch to send(1)/post(8) wasn't used.
435 */
436 if (nmh_get_credentials (p_context->host, p_context->user, 0, &creds)
437 != OK) {
438 return SASL_BADPARAM;
439 }
440 }
441
442 len = strlen (creds.password);
443
444 *psecret = (sasl_secret_t *) mh_xmalloc(sizeof(sasl_secret_t) + len);
445
446 (*psecret)->len = len;
447 strcpy((char *) (*psecret)->data, creds.password);
448
449 return SASL_OK;
450 }
451 #endif /* CYRUS_SASL */
452
453 int
454 pop_auth_xoauth(const char *client_res)
455 {
456 char server_mechs[256];
457 int status = check_mech(server_mechs, sizeof(server_mechs), "XOAUTH");
458
459 if (status != OK) return status;
460
461 if ((status = command("AUTH XOAUTH2 %s", client_res)) != OK) {
462 return status;
463 }
464 if (strncmp(response, "+OK", 3) == 0) {
465 return OK;
466 }
467
468 /* response contains base64-encoded JSON, which is always the same.
469 * See mts/smtp/smtp.c for more notes on that. */
470 /* Then we're supposed to send an empty response ("\r\n"). */
471 return command("");
472 }
473
474 /*
475 * Split string containing proxy command into an array of arguments
476 * suitable for passing to exec. Returned array must be freed. Shouldn't
477 * be possible to call this with host set to NULL.
478 */
479 char **
480 parse_proxy(char *proxy, char *host)
481 {
482 char **pargv, **p;
483 int pargc = 2;
484 int hlen = strlen(host);
485 int plen = 1;
486 unsigned char *cur, *pro;
487 char *c;
488
489 /* skip any initial space */
490 for (pro = (unsigned char *) proxy; isspace(*pro); pro++)
491 continue;
492
493 /* calculate required size for argument array */
494 for (cur = pro; *cur; cur++) {
495 if (isspace(*cur) && cur[1] && !isspace(cur[1]))
496 plen++, pargc++;
497 else if (*cur == '%' && cur[1] == 'h') {
498 plen += hlen;
499 cur++;
500 } else if (!isspace(*cur))
501 plen++;
502 }
503
504 /* put together list of arguments */
505 p = pargv = mh_xmalloc(pargc * sizeof(char *));
506 c = *pargv = mh_xmalloc(plen * sizeof(char));
507 for (cur = pro; *cur; cur++) {
508 if (isspace(*cur) && cur[1] && !isspace(cur[1])) {
509 *c++ = '\0';
510 *++p = c;
511 } else if (*cur == '%' && cur[1] == 'h') {
512 strcpy (c, host);
513 c += hlen;
514 cur++;
515 } else if (!isspace(*cur))
516 *c++ = *cur;
517 }
518 *c = '\0';
519 *++p = NULL;
520 return pargv;
521 }
522
523 int
524 pop_init (char *host, char *port, char *user, char *pass, char *proxy,
525 int snoop, int sasl, char *mech, int tls, const char *oauth_svc)
526 {
527 int fd1, fd2;
528 char buffer[BUFSIZ];
529 const char *xoauth_client_res = NULL;
530 #ifndef CYRUS_SASL
531 NMH_UNUSED (sasl);
532 NMH_UNUSED (mech);
533 #endif /* ! CYRUS_SASL */
534
535 #ifdef OAUTH_SUPPORT
536 if (oauth_svc != NULL) {
537 xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc,
538 snoop ? stderr : NULL);
539 }
540 #else
541 NMH_UNUSED (oauth_svc);
542 NMH_UNUSED (xoauth_client_res);
543 #endif /* OAUTH_SUPPORT */
544
545 if (proxy && *proxy) {
546 int pid;
547 int inpipe[2]; /* for reading from the server */
548 int outpipe[2]; /* for sending to the server */
549
550 if (pipe(inpipe) < 0) {
551 adios ("inpipe", "pipe");
552 }
553 if (pipe(outpipe) < 0) {
554 adios ("outpipe", "pipe");
555 }
556
557 pid=fork();
558 if (pid==0) {
559 char **argv;
560
561 /* in child */
562 close(0);
563 close(1);
564 dup2(outpipe[0],0); /* connect read end of connection */
565 dup2(inpipe[1], 1); /* connect write end of connection */
566 if(inpipe[0]>1) close(inpipe[0]);
567 if(inpipe[1]>1) close(inpipe[1]);
568 if(outpipe[0]>1) close(outpipe[0]);
569 if(outpipe[1]>1) close(outpipe[1]);
570
571 /* run the proxy command */
572 argv=parse_proxy(proxy, host);
573 execvp(argv[0],argv);
574
575 perror(argv[0]);
576 close(0);
577 close(1);
578 free(*argv);
579 free(argv);
580 exit(10);
581 }
582
583 /* okay in the parent we do some stuff */
584 close(inpipe[1]); /* child uses this */
585 close(outpipe[0]); /* child uses this */
586
587 /* we read on fd1 */
588 fd1=inpipe[0];
589 /* and write on fd2 */
590 fd2=outpipe[1];
591
592 } else {
593
594 if ((fd1 = client (host, port ? port : "pop3", response,
595 sizeof(response), snoop)) == NOTOK) {
596 return NOTOK;
597 }
598
599 if ((fd2 = dup (fd1)) == NOTOK) {
600 char *s;
601
602 if ((s = strerror(errno)))
603 snprintf (response, sizeof(response),
604 "unable to dup connection descriptor: %s", s);
605 else
606 snprintf (response, sizeof(response),
607 "unable to dup connection descriptor: unknown error");
608 close (fd1);
609 return NOTOK;
610 }
611 }
612 if (pop_set (fd1, fd2, snoop) == NOTOK)
613 return NOTOK;
614
615 SIGNAL (SIGPIPE, SIG_IGN);
616
617 if (tls && tls_negotiate())
618 return NOTOK;
619
620 switch (sasl_getline (response, sizeof response, input)) {
621 case OK:
622 if (poprint)
623 fprintf (stderr, "<--- %s\n", response);
624 if (*response == '+') {
625 # ifdef CYRUS_SASL
626 if (sasl) {
627 if (pop_auth_sasl(user, host, mech) != NOTOK)
628 return OK;
629 } else
630 # endif /* CYRUS_SASL */
631 # if OAUTH_SUPPORT
632 if (xoauth_client_res != NULL) {
633 if (pop_auth_xoauth(xoauth_client_res) != NOTOK)
634 return OK;
635 } else
636 # endif /* OAUTH_SUPPORT */
637 if (command ("USER %s", user) != NOTOK
638 && command ("%s %s", (pophack++, "PASS"),
639 pass) != NOTOK)
640 return OK;
641 }
642 strncpy (buffer, response, sizeof(buffer));
643 command ("QUIT");
644 strncpy (response, buffer, sizeof(response));
645 /* and fall */
646
647 case NOTOK:
648 case DONE:
649 if (poprint)
650 fprintf (stderr, "%s\n", response);
651 fclose (input);
652 fclose (output);
653 return NOTOK;
654 }
655
656 return NOTOK; /* NOTREACHED */
657 }
658
659 int
660 pop_set (int in, int out, int snoop)
661 {
662
663 if ((input = fdopen (in, "r")) == NULL
664 || (output = fdopen (out, "w")) == NULL) {
665 strncpy (response, "fdopen failed on connection descriptor", sizeof(response));
666 if (input)
667 fclose (input);
668 else
669 close (in);
670 close (out);
671 return NOTOK;
672 }
673
674 poprint = snoop;
675
676 return OK;
677 }
678
679
680 int
681 pop_fd (char *in, int inlen, char *out, int outlen)
682 {
683 snprintf (in, inlen, "%d", fileno (input));
684 snprintf (out, outlen, "%d", fileno (output));
685 return OK;
686 }
687
688
689 /*
690 * Find out number of messages available
691 * and their total size.
692 */
693
694 int
695 pop_stat (int *nmsgs, int *nbytes)
696 {
697
698 if (command ("STAT") == NOTOK)
699 return NOTOK;
700
701 *nmsgs = *nbytes = 0;
702 sscanf (response, "+OK %d %d", nmsgs, nbytes);
703
704 return OK;
705 }
706
707
708 int
709 pop_list (int msgno, int *nmsgs, int *msgs, int *bytes)
710 {
711 int i;
712 int *ids = NULL;
713
714 if (msgno) {
715 if (command ("LIST %d", msgno) == NOTOK)
716 return NOTOK;
717 *msgs = *bytes = 0;
718 if (ids) {
719 *ids = 0;
720 sscanf (response, "+OK %d %d %d", msgs, bytes, ids);
721 }
722 else
723 sscanf (response, "+OK %d %d", msgs, bytes);
724 return OK;
725 }
726
727 if (command ("LIST") == NOTOK)
728 return NOTOK;
729
730 for (i = 0; i < *nmsgs; i++)
731 switch (multiline ()) {
732 case NOTOK:
733 return NOTOK;
734 case DONE:
735 *nmsgs = ++i;
736 return OK;
737 case OK:
738 *msgs = *bytes = 0;
739 if (ids) {
740 *ids = 0;
741 sscanf (response, "%d %d %d",
742 msgs++, bytes++, ids++);
743 }
744 else
745 sscanf (response, "%d %d", msgs++, bytes++);
746 break;
747 }
748 for (;;)
749 switch (multiline ()) {
750 case NOTOK:
751 return NOTOK;
752 case DONE:
753 return OK;
754 case OK:
755 break;
756 }
757 }
758
759
760 int
761 pop_retr (int msgno, int (*action)(char *))
762 {
763 return traverse (action, "RETR %d", msgno);
764 }
765
766
767 static int
768 traverse (int (*action)(char *), const char *fmt, ...)
769 {
770 int result;
771 va_list ap;
772 char buffer[sizeof(response)];
773
774 va_start(ap, fmt);
775 result = vcommand (fmt, ap);
776 va_end(ap);
777
778 if (result == NOTOK)
779 return NOTOK;
780 strncpy (buffer, response, sizeof(buffer));
781
782 for (;;)
783 switch (multiline ()) {
784 case NOTOK:
785 return NOTOK;
786
787 case DONE:
788 strncpy (response, buffer, sizeof(response));
789 return OK;
790
791 case OK:
792 (*action) (response);
793 break;
794 }
795 }
796
797
798 int
799 pop_dele (int msgno)
800 {
801 return command ("DELE %d", msgno);
802 }
803
804
805 int
806 pop_noop (void)
807 {
808 return command ("NOOP");
809 }
810
811
812 int
813 pop_rset (void)
814 {
815 return command ("RSET");
816 }
817
818
819 int
820 pop_top (int msgno, int lines, int (*action)(char *))
821 {
822 return traverse (action, "TOP %d %d", msgno, lines);
823 }
824
825
826 int
827 pop_quit (void)
828 {
829 int i;
830
831 i = command ("QUIT");
832 pop_done ();
833
834 return i;
835 }
836
837
838 int
839 pop_done (void)
840 {
841 #ifdef CYRUS_SASL
842 if (conn)
843 sasl_dispose(&conn);
844 #endif /* CYRUS_SASL */
845 fclose (input);
846 fclose (output);
847
848 return OK;
849 }
850
851
852 int
853 command(const char *fmt, ...)
854 {
855 va_list ap;
856 int result;
857
858 va_start(ap, fmt);
859 result = vcommand(fmt, ap);
860 va_end(ap);
861
862 return result;
863 }
864
865
866 static int
867 vcommand (const char *fmt, va_list ap)
868 {
869 char *cp, buffer[65536];
870
871 vsnprintf (buffer, sizeof(buffer), fmt, ap);
872
873 if (poprint) {
874 #ifdef CYRUS_SASL
875 if (sasl_ssf)
876 fprintf(stderr, "(sasl-encrypted) ");
877 #endif /* CYRUS_SASL */
878 #ifdef TLS_SUPPORT
879 if (tls_active)
880 fprintf(stderr, "(tls-encrypted) ");
881 #endif /* TLS_SUPPORT */
882 if (pophack) {
883 if ((cp = strchr (buffer, ' ')))
884 *cp = 0;
885 fprintf (stderr, "---> %s ********\n", buffer);
886 if (cp)
887 *cp = ' ';
888 pophack = 0;
889 }
890 else
891 fprintf (stderr, "---> %s\n", buffer);
892 }
893
894 if (putline (buffer, output) == NOTOK)
895 return NOTOK;
896
897 #ifdef CYRUS_SASL
898 if (poprint && sasl_ssf)
899 fprintf(stderr, "(decrypted) ");
900 #endif /* CYRUS_SASL */
901
902 switch (sasl_getline (response, sizeof response, input)) {
903 case OK:
904 if (poprint)
905 fprintf (stderr, "<--- %s\n", response);
906 return (*response == '+' ? OK : NOTOK);
907
908 case NOTOK:
909 case DONE:
910 if (poprint)
911 fprintf (stderr, "%s\n", response);
912 return NOTOK;
913 }
914
915 return NOTOK; /* NOTREACHED */
916 }
917
918
919 int
920 multiline (void)
921 {
922 char buffer[BUFSIZ + TRMLEN];
923
924 if (sasl_getline (buffer, sizeof buffer, input) != OK)
925 return NOTOK;
926 #ifdef DEBUG
927 if (poprint) {
928 #ifdef CYRUS_SASL
929 if (sasl_ssf)
930 fprintf(stderr, "(decrypted) ");
931 #endif /* CYRUS_SASL */
932 fprintf (stderr, "<--- %s\n", response);
933 }
934 #endif /* DEBUG */
935 if (strncmp (buffer, TRM, TRMLEN) == 0) {
936 if (buffer[TRMLEN] == 0)
937 return DONE;
938 else
939 strncpy (response, buffer + TRMLEN, sizeof(response));
940 }
941 else
942 strncpy (response, buffer, sizeof(response));
943
944 return OK;
945 }
946
947 /*
948 * Note that these functions have been modified to deal with layer encryption
949 * in the SASL case
950 */
951
952 static int
953 sasl_getline (char *s, int n, FILE *iop)
954 {
955 int c = -2;
956 char *p;
957
958 p = s;
959 while (--n > 0 && (c = sasl_fgetc (iop)) != EOF && c != -2)
960 if ((*p++ = c) == '\n')
961 break;
962 if (c == -2)
963 return NOTOK;
964 if (ferror (iop) && c != EOF) {
965 strncpy (response, "error on connection", sizeof(response));
966 return NOTOK;
967 }
968 if (c == EOF && p == s) {
969 strncpy (response, "connection closed by foreign host", sizeof(response));
970 return DONE;
971 }
972 *p = 0;
973 if (*--p == '\n')
974 *p = 0;
975 if (p > s && *--p == '\r')
976 *p = 0;
977
978 return OK;
979 }
980
981
982 static int
983 putline (char *s, FILE *iop)
984 {
985 #ifdef CYRUS_SASL
986 char outbuf[BUFSIZ], *buf;
987 int result;
988 unsigned int buflen;
989
990 if (sasl_complete == 0 || sasl_ssf == 0) {
991 #endif /* CYRUS_SASL */
992 #ifdef TLS_SUPPORT
993 if (tls_active) {
994 int ret;
995
996 BIO_printf(io, "%s\r\n");
997 ret = BIO_flush(io);
998
999 if (ret != 1) {
1000 strncpy(response, "lost connection", sizeof(response));
1001 return NOTOK;
1002 else {
1003 return OK;
1004 }
1005 } else
1006 #endif /* TLS_SUPPORT */
1007 fprintf (iop, "%s\r\n", s);
1008 #ifdef CYRUS_SASL
1009 } else {
1010 /*
1011 * Build an output buffer, encrypt it using sasl_encode, and
1012 * squirt out the results.
1013 */
1014 strncpy(outbuf, s, sizeof(outbuf) - 3);
1015 outbuf[sizeof(outbuf) - 3] = '\0'; /* Just in case */
1016 strcat(outbuf, "\r\n");
1017
1018 result = sasl_encode(conn, outbuf, strlen(outbuf),
1019 (const char **) &buf, &buflen);
1020
1021 if (result != SASL_OK) {
1022 snprintf(response, sizeof(response), "SASL encoding error: %s",
1023 sasl_errdetail(conn));
1024 return NOTOK;
1025 }
1026
1027 if (fwrite(buf, buflen, 1, iop) < 1) {
1028 advise ("putline", "fwrite");
1029 }
1030 }
1031 #endif /* CYRUS_SASL */
1032
1033 fflush (iop);
1034 if (ferror (iop)) {
1035 strncpy (response, "lost connection", sizeof(response));
1036 return NOTOK;
1037 }
1038
1039 return OK;
1040 }
1041
1042 #ifdef CYRUS_SASL
1043 /*
1044 * Okay, our little fgetc replacement. Hopefully this is a little more
1045 * efficient than the last one.
1046 */
1047 static int
1048 sasl_fgetc(FILE *f)
1049 {
1050 static unsigned char *buffer = NULL, *ptr;
1051 static unsigned int size = 0;
1052 static int cnt = 0;
1053 unsigned int retbufsize = 0;
1054 int cc, result;
1055 char *retbuf, tmpbuf[SASL_BUFFER_SIZE];
1056
1057 /*
1058 * If we have some leftover data, return that
1059 */
1060
1061 if (cnt) {
1062 cnt--;
1063 return (int) *ptr++;
1064 }
1065
1066 /*
1067 * Otherwise, fill our buffer until we have some data to return.
1068 */
1069
1070 while (retbufsize == 0) {
1071
1072 #ifdef TLS_SUPPORT
1073 if (tls_active) {
1074
1075 cc = read(fileno(f), tmpbuf, sizeof(tmpbuf));
1076
1077 if (cc == 0)
1078 return EOF;
1079
1080 if (cc < 0) {
1081 snprintf(response, sizeof(response), "Error during read from "
1082 "network: %s", strerror(errno));
1083 return -2;
1084 }
1085
1086 /*
1087 * We're not allowed to call sasl_decode until sasl_complete is
1088 * true, so we do these gyrations ...
1089 */
1090
1091 if (!sasl_complete) {
1092
1093 retbuf = tmpbuf;
1094 retbufsize = cc;
1095
1096 } else {
1097
1098 result = sasl_decode(conn, tmpbuf, cc,
1099 (const char **) &retbuf, &retbufsize);
1100
1101 if (result != SASL_OK) {
1102 snprintf(response, sizeof(response), "Error during SASL "
1103 "decoding: %s", sasl_errdetail(conn));
1104 return -2;
1105 }
1106 }
1107 }
1108
1109 if (retbufsize > size) {
1110 buffer = mh_xrealloc(buffer, retbufsize);
1111 size = retbufsize;
1112 }
1113
1114 memcpy(buffer, retbuf, retbufsize);
1115 ptr = buffer + 1;
1116 cnt = retbufsize - 1;
1117
1118 return (int) buffer[0];
1119 }
1120 #endif /* CYRUS_SASL */
1121
1122 #ifdef TLS_SUPPORT
1123 /*
1124 * Negotiate TLS at this point. Will call pop_done() on failure.
1125 */
1126
1127 static int
1128 tls_negotiate(void)
1129 {
1130 BIO *ssl_bio;
1131
1132 if (! sslctx) {
1133 SSL_library_init();
1134 SSL_load_error_strings();
1135
1136 sslctx = SSL_CTX_new(SSLv23_client_method());
1137
1138 if (! sslctx) {
1139 pop_done();
1140 advise(NULL, "Unable to initialize OpenSSL context: %s",
1141 ERR_error_string(ERR_get_error(), NULL));
1142 return NOTOK;
1143 }
1144
1145 SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
1146 SSL_OP_NO_TLSv1);
1147 }
1148
1149 ssl = SSL_new(sslctx);
1150
1151 if (! ssl) {
1152 pop_done();
1153 advise(NULL, "Unable to create SSL connection: %s",
1154 ERR_error_string(ERR_get_error(), NULL));
1155 return NOTOK;
1156 }
1157
1158 sbior = BIO_new_socket(fileno(input), BIO_NOCLOSE);
1159 sbiow = BIO_new_socket(fileno(output), BIO_NOCLOSE);
1160
1161 if (sbior == NULL || sbiow == NULL) {
1162 pop_done();
1163 advise(NULL, "Unable to create BIO endpoints: %s",
1164 ERR_error_string(ERR_get_error(), NULL));
1165 return NOTOK;
1166 }
1167
1168 SSL_set_bio(ssl, sbior, sbiow);
1169 SSL_set_connect_state(ssl);
1170
1171 /*
1172 * Set up a BIO to do buffering for us
1173 */
1174
1175 io = BIO_new(BIO_f_buffer());
1176
1177 if (! io) {
1178 pop_done();
1179 advise(NULL, "Unable to create a buffer BIO: %s",
1180 ERR_error_string(ERR_get_error(), NULL));
1181 return NOTOK;
1182 }
1183
1184 ssl_bio = BIO_new(BIO_f_ssl());
1185
1186 if (! ssl_bio) {
1187 pop_done();
1188 advise(NULL, "Unable to create a SSL BIO: %s",
1189 ERR_error_string(ERR_get_error(), NULL));
1190 return NOTOK;
1191 }
1192
1193 BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
1194 BIO_push(io, ssl_bio);
1195
1196 /*
1197 * Try doing the handshake now
1198 */
1199
1200 if (BIO_do_handshake(io) < 1) {
1201 pop_done();
1202 advise(NULL, "Unable to negotiate SSL connection: %s",
1203 ERR_error_string(ERR_get_error(), NULL));
1204 return NOTOK;
1205 }
1206
1207 if (poprint) {
1208 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1209 printf("SSL negotiation successful: %s(%d) %s\n",
1210 SSL_CIPHER_get_name(cipher),
1211 SSL_CIPHER_get_bits(cipher, NULL),
1212 SSL_CIPHER_get_version(cipher));
1213 }
1214
1215 tls_active = 1;
1216
1217 return OK;
1218 }
1219 #endif /* TLS_SUPPORT */