]> diplodocus.org Git - nmh/blob - sbr/netsec.c
75be76a3f183e6592d361121edb2f605c6e5dfa6
[nmh] / sbr / netsec.c
1 /* netsec.c -- Network security routines for handling protocols that
2 * require SASL and/or TLS.
3 *
4 * This code is Copyright (c) 2016, 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 "credentials.h"
11 #include "getcpy.h"
12 #include "brkstring.h"
13 #include "h/utils.h"
14 #include "h/netsec.h"
15 #include "h/oauth.h"
16 #include <stdarg.h>
17 #include <sys/select.h>
18 #include "base64.h"
19
20 #ifdef CYRUS_SASL
21 #include <sasl/sasl.h>
22 #include <sasl/saslutil.h>
23 # if SASL_VERSION_FULL < 0x020125
24 /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
25 which has an explicit void parameter list, according to best
26 practice. So we need to cast to avoid compile warnings.
27 Provide this prototype for earlier versions. */
28 typedef int (*sasl_callback_ft)();
29 # endif /* SASL_VERSION_FULL < 0x020125 */
30
31 static int netsec_get_user(void *context, int id, const char **result,
32 unsigned int *len);
33 static int netsec_get_password(sasl_conn_t *conn, void *context, int id,
34 sasl_secret_t **psecret);
35
36 static bool sasl_initialized;
37
38 #define SASL_MAXRECVBUF 65536
39 #endif /* CYRUS_SASL */
40
41 #ifdef TLS_SUPPORT
42 #include <openssl/ssl.h>
43 #include <openssl/err.h>
44
45 static bool tls_initialized;
46 static SSL_CTX *sslctx = NULL; /* SSL Context */
47
48 #endif /* TLS_SUPPORT */
49
50 /* I'm going to hardcode this for now; maybe make it adjustable later? */
51 #define NETSEC_BUFSIZE 65536
52
53 /*
54 * Our context structure, which holds all of the relevant information
55 * about a connection.
56 */
57
58 struct _netsec_context {
59 int ns_readfd; /* Read descriptor for network connection */
60 int ns_writefd; /* Write descriptor for network connection */
61 int ns_noclose; /* Do not close file descriptors if set */
62 int ns_snoop; /* If true, display network data */
63 netsec_snoop_callback *ns_snoop_cb; /* Snoop output callback */
64 void *ns_snoop_context; /* Context data for snoop function */
65 char *ns_snoop_savebuf; /* Save buffer for snoop data */
66 int ns_timeout; /* Network read timeout, in seconds */
67 char *ns_userid; /* Userid for authentication */
68 char *ns_hostname; /* Hostname we've connected to */
69 unsigned char *ns_inbuffer; /* Our read input buffer */
70 unsigned char *ns_inptr; /* Our read buffer input pointer */
71 unsigned int ns_inbuflen; /* Length of data in input buffer */
72 unsigned int ns_inbufsize; /* Size of input buffer */
73 unsigned char *ns_outbuffer;/* Output buffer */
74 unsigned char *ns_outptr; /* Output buffer pointer */
75 unsigned int ns_outbuflen; /* Output buffer data length */
76 unsigned int ns_outbufsize; /* Output buffer size */
77 char *sasl_mech; /* User-requested mechanism */
78 char *sasl_chosen_mech; /* Mechanism chosen by SASL */
79 netsec_sasl_callback sasl_proto_cb; /* SASL callback we use */
80 void *sasl_proto_context; /* Context to be used by SASL callback */
81 #ifdef OAUTH_SUPPORT
82 char *oauth_service; /* OAuth2 service name */
83 #endif /* OAUTH_SUPPORT */
84 #ifdef CYRUS_SASL
85 sasl_conn_t *sasl_conn; /* SASL connection context */
86 sasl_ssf_t sasl_ssf; /* SASL Security Strength Factor */
87 sasl_callback_t *sasl_cbs; /* Callbacks used by SASL */
88 nmh_creds_t sasl_creds; /* Credentials (username/password) */
89 sasl_secret_t *sasl_secret; /* SASL password structure */
90 int sasl_seclayer; /* If true, SASL security layer is enabled */
91 char *sasl_tmpbuf; /* Temporary read buffer for decodes */
92 size_t sasl_maxbufsize; /* Maximum negotiated SASL buffer size */
93 #endif /* CYRUS_SASL */
94 #ifdef TLS_SUPPORT
95 BIO *ssl_io; /* BIO used for connection I/O */
96 int tls_active; /* If true, TLS is running */
97 #endif /* TLS_SUPPORT */
98 };
99
100 /*
101 * Function to read data from the actual network socket
102 */
103
104 static int netsec_fillread(netsec_context *ns_context, char **errstr);
105
106 /*
107 * Code to check the ASCII content of a byte array.
108 */
109
110 static int checkascii(const unsigned char *byte, size_t len);
111
112 /*
113 * How this code works, in general.
114 *
115 * _If_ we are using no encryption then we buffer the network data
116 * through ns_inbuffer and ns_outbuffer. That should be relatively
117 * self-explanatory.
118 *
119 * If we use encryption, then ns_inbuffer and ns_outbuffer contain the
120 * cleartext data. When it comes time to send the encrypted data on the
121 * (either from a flush or the buffer is full) we either use BIO_write()
122 * for TLS or sasl_encode() (followed by a write() for Cyrus-SASL. For
123 * reads we either use BIO_read() (TLS) or do a network read into a
124 * temporary buffer and use sasl_decode() (Cyrus-SASL). Note that if
125 * negotiate TLS then we disable SASL encryption.
126 *
127 * We used to use a buffering BIO for the reads/writes for TLS, but it
128 * ended up being complicated to special-case the buffering for everything
129 * except TLS, so the buffering is now unified, no matter which encryption
130 * method is being used (even none).
131 *
132 * For SASL authentication, we make use of (for now) the Cyrus-SASL
133 * library. For some mechanisms, we implement those mechanisms directly
134 * since the Cyrus SASL library doesn't support them (like OAuth).
135 */
136
137 /*
138 * Allocate and initialize our security context
139 */
140
141 netsec_context *
142 netsec_init(void)
143 {
144 netsec_context *nsc;
145
146 NEW(nsc);
147 nsc->ns_readfd = -1;
148 nsc->ns_writefd = -1;
149 nsc->ns_noclose = 0;
150 nsc->ns_snoop = 0;
151 nsc->ns_snoop_cb = NULL;
152 nsc->ns_snoop_context = NULL;
153 nsc->ns_snoop_savebuf = NULL;
154 nsc->ns_userid = NULL;
155 nsc->ns_hostname = NULL;
156 nsc->ns_timeout = 60; /* Our default */
157 nsc->ns_inbufsize = NETSEC_BUFSIZE;
158 nsc->ns_inbuffer = mh_xmalloc(nsc->ns_inbufsize);
159 nsc->ns_inptr = nsc->ns_inbuffer;
160 nsc->ns_inbuflen = 0;
161 nsc->ns_outbufsize = NETSEC_BUFSIZE;
162 nsc->ns_outbuffer = mh_xmalloc(nsc->ns_outbufsize);
163 nsc->ns_outptr = nsc->ns_outbuffer;
164 nsc->ns_outbuflen = 0;
165 nsc->sasl_mech = NULL;
166 nsc->sasl_chosen_mech = NULL;
167 nsc->sasl_proto_cb = NULL;
168 nsc->sasl_proto_context = NULL;
169 #ifdef OAUTH_SUPPORT
170 nsc->oauth_service = NULL;
171 #endif /* OAUTH_SUPPORT */
172 #ifdef CYRUS_SASL
173 nsc->sasl_conn = NULL;
174 nsc->sasl_cbs = NULL;
175 nsc->sasl_creds = NULL;
176 nsc->sasl_secret = NULL;
177 nsc->sasl_ssf = 0;
178 nsc->sasl_seclayer = 0;
179 nsc->sasl_tmpbuf = NULL;
180 nsc->sasl_maxbufsize = 0;
181 #endif /* CYRUS_SASL */
182 #ifdef TLS_SUPPORT
183 nsc->ssl_io = NULL;
184 nsc->tls_active = 0;
185 #endif /* TLS_SUPPORT */
186 return nsc;
187 }
188
189 /*
190 * Shutdown the connection completely and free all resources.
191 */
192
193 void
194 netsec_shutdown(netsec_context *nsc)
195 {
196 free(nsc->ns_userid);
197 free(nsc->ns_hostname);
198 free(nsc->ns_inbuffer);
199 free(nsc->ns_outbuffer);
200 free(nsc->sasl_mech);
201 free(nsc->sasl_chosen_mech);
202 free(nsc->ns_snoop_savebuf);
203 #ifdef OAUTH_SERVICE
204 free(nsc->oauth_service);
205 #endif /* OAUTH_SERVICE */
206 #ifdef CYRUS_SASL
207 if (nsc->sasl_conn)
208 sasl_dispose(&nsc->sasl_conn);
209 free(nsc->sasl_cbs);
210 if (nsc->sasl_creds)
211 nmh_credentials_free(nsc->sasl_creds);
212 if (nsc->sasl_secret) {
213 if (nsc->sasl_secret->len > 0) {
214 memset(nsc->sasl_secret->data, 0, nsc->sasl_secret->len);
215 }
216 free(nsc->sasl_secret);
217 }
218 free(nsc->sasl_tmpbuf);
219 #endif /* CYRUS_SASL */
220 #ifdef TLS_SUPPORT
221 if (nsc->ssl_io)
222 /*
223 * I checked; BIO_free_all() will cause SSL_shutdown to be called
224 * on the SSL object in the chain.
225 */
226 BIO_free_all(nsc->ssl_io);
227 #endif /* TLS_SUPPORT */
228
229 if (! nsc->ns_noclose) {
230 if (nsc->ns_readfd != -1)
231 close(nsc->ns_readfd);
232 if (nsc->ns_writefd != -1 && nsc->ns_writefd != nsc->ns_readfd)
233 close(nsc->ns_writefd);
234 }
235
236 free(nsc);
237 }
238
239 /*
240 * Set the file descriptor for our context
241 */
242
243 void
244 netsec_set_fd(netsec_context *nsc, int readfd, int writefd)
245 {
246 nsc->ns_readfd = readfd;
247 nsc->ns_writefd = writefd;
248 }
249
250 /*
251 * Set the userid used for authentication for this context
252 */
253
254 void
255 netsec_set_userid(netsec_context *nsc, const char *userid)
256 {
257 nsc->ns_userid = getcpy(userid);
258 }
259
260 /*
261 * Set the hostname of the remote host we're connecting to.
262 */
263
264 void
265 netsec_set_hostname(netsec_context *nsc, const char *hostname)
266 {
267 nsc->ns_hostname = mh_xstrdup(hostname);
268 }
269
270 /*
271 * Get the snoop flag for this connection
272 */
273
274 int
275 netsec_get_snoop(netsec_context *nsc)
276 {
277 return nsc->ns_snoop;
278 }
279
280 /*
281 * Set the snoop flag for this connection
282 */
283
284 void
285 netsec_set_snoop(netsec_context *nsc, int snoop)
286 {
287 nsc->ns_snoop = snoop;
288 }
289
290 /*
291 * Set the snoop callback for this connection.
292 */
293
294 void
295 netsec_set_snoop_callback(netsec_context *nsc,
296 netsec_snoop_callback callback, void *context)
297 {
298 nsc->ns_snoop_cb = callback;
299 nsc->ns_snoop_context = context;
300 }
301
302 /*
303 * A base64-decoding snoop callback
304 */
305
306 void
307 netsec_b64_snoop_decoder(netsec_context *nsc, const char *string, size_t len,
308 void *context)
309 {
310 unsigned char *decoded;
311 size_t decodedlen;
312 int offset;
313 NMH_UNUSED(nsc);
314
315 offset = context ? *((int *) context) : 0;
316
317 if (offset > 0) {
318 /*
319 * Output non-base64 data first.
320 */
321 fprintf(stderr, "%.*s", offset, string);
322 string += offset;
323 len -= offset;
324 }
325
326 if (decodeBase64(string, &decoded, &decodedlen, 1) == OK) {
327 /*
328 * Some mechanisms produce large binary tokens, which aren't really
329 * readable. So let's do a simple heuristic. If the token is greater
330 * than 100 characters _and_ the first 100 bytes are more than 50%
331 * non-ASCII, then don't print the decoded buffer, just the
332 * base64 text.
333 */
334 if (decodedlen > 100 && !checkascii(decoded, 100)) {
335 fprintf(stderr, "%.*s\n", (int) len, string);
336 } else {
337 char *hexified;
338 hexify(decoded, decodedlen, &hexified);
339 fprintf(stderr, "b64<%s>\n", hexified);
340 free(hexified);
341 }
342 free(decoded);
343 } else {
344 fprintf(stderr, "%.*s\n", (int) len, string);
345 }
346 }
347
348 /*
349 * If the ASCII content is > 50%, return 1
350 */
351
352 static int
353 checkascii(const unsigned char *bytes, size_t len)
354 {
355 size_t count = 0, half = len / 2;
356
357 while (len-- > 0) {
358 if (isascii(*bytes) && isprint(*bytes) && ++count > half)
359 return 1;
360 bytes++;
361 /* No chance by this point */
362 if (count + len < half)
363 return 0;
364 }
365
366 return 0;
367 }
368
369 /*
370 * Set the read timeout for this connection
371 */
372
373 void
374 netsec_set_timeout(netsec_context *nsc, int timeout)
375 {
376 nsc->ns_timeout = timeout;
377 }
378
379 /*
380 * Read data from the network. Basically, return anything in our buffer,
381 * otherwise fill from the network.
382 */
383
384 ssize_t
385 netsec_read(netsec_context *nsc, void *buffer, size_t size, char **errstr)
386 {
387 int retlen;
388
389 /*
390 * If our buffer is empty, then we should fill it now
391 */
392
393 if (nsc->ns_inbuflen == 0) {
394 if (netsec_fillread(nsc, errstr) != OK)
395 return NOTOK;
396 }
397
398 /*
399 * netsec_fillread only returns if the buffer is full, so we can
400 * assume here that this has something in it.
401 */
402
403 retlen = min(size, nsc->ns_inbuflen);
404
405 memcpy(buffer, nsc->ns_inptr, retlen);
406
407 if (retlen == (int) nsc->ns_inbuflen) {
408 /*
409 * We've emptied our buffer, so reset everything.
410 */
411 nsc->ns_inptr = nsc->ns_inbuffer;
412 nsc->ns_inbuflen = 0;
413 } else {
414 nsc->ns_inptr += size;
415 nsc->ns_inbuflen -= size;
416 }
417
418 return OK;
419 }
420
421 /*
422 * Get a "line" (CR/LF) terminated from the network.
423 *
424 * Okay, we play some games here, so pay attention:
425 *
426 * - Unlike every other function, we return a pointer to the
427 * existing buffer. This pointer is valid until you call another
428 * read function again.
429 * - We NUL-terminate the buffer right at the end, before the CR-LF terminator.
430 * - Technically we look for a LF; if we find a CR right before it, then
431 * we back up one.
432 * - If your data may contain embedded NULs, this won't work. You should
433 * be using netsec_read() in that case.
434 */
435
436 char *
437 netsec_readline(netsec_context *nsc, size_t *len, char **errstr)
438 {
439 unsigned char *ptr = nsc->ns_inptr;
440 size_t count = 0, offset;
441
442 retry:
443 /*
444 * Search through our existing buffer for a LF
445 */
446
447 while (count < nsc->ns_inbuflen) {
448 count++;
449 if (*ptr++ == '\n') {
450 char *sptr = (char *) nsc->ns_inptr;
451 if (count > 1 && *(ptr - 2) == '\r')
452 ptr--;
453 *--ptr = '\0';
454 if (len)
455 *len = ptr - nsc->ns_inptr;
456 nsc->ns_inptr += count;
457 nsc->ns_inbuflen -= count;
458 if (nsc->ns_snoop) {
459 #ifdef CYRUS_SASL
460 if (nsc->sasl_seclayer)
461 fprintf(stderr, "(sasl-decrypted) ");
462 #endif /* CYRUS_SASL */
463 #ifdef TLS_SUPPORT
464 if (nsc->tls_active)
465 fprintf(stderr, "(tls-decrypted) ");
466 #endif /* TLS_SUPPORT */
467 fprintf(stderr, "<= ");
468 if (nsc->ns_snoop_cb)
469 nsc->ns_snoop_cb(nsc, sptr, strlen(sptr),
470 nsc->ns_snoop_context);
471 else {
472 fputs(sptr, stderr);
473 putc('\n', stderr);
474 }
475 }
476 return sptr;
477 }
478 }
479
480 /*
481 * Hm, we didn't find a \n. If we've already searched half of the input
482 * buffer, return an error.
483 */
484
485 if (count >= nsc->ns_inbufsize / 2) {
486 netsec_err(errstr, "Unable to find a line terminator after %zu bytes",
487 count);
488 return NULL;
489 }
490
491 /*
492 * Okay, get some more network data. This may move inptr, so regenerate
493 * our ptr value;
494 */
495
496 offset = ptr - nsc->ns_inptr;
497
498 if (netsec_fillread(nsc, errstr) != OK)
499 return NULL;
500
501 ptr = nsc->ns_inptr + offset;
502
503 goto retry;
504
505 return NULL; /* Should never reach this */
506 }
507
508 /*
509 * Fill our read buffer with some data from the network.
510 */
511
512 static int
513 netsec_fillread(netsec_context *nsc, char **errstr)
514 {
515 unsigned char *end;
516 char *readbuf;
517 size_t readbufsize, remaining, startoffset;
518 int rc;
519
520 /*
521 * If inbuflen is zero, that means the buffer has been emptied
522 * completely. In that case move inptr back to the start.
523 */
524
525 if (nsc->ns_inbuflen == 0) {
526 nsc->ns_inptr = nsc->ns_inbuffer;
527 }
528
529 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
530 retry:
531 #endif /* CYRUS_SASL || TLS_SUPPORT */
532 /*
533 * If we are using TLS and there's anything pending, then skip the
534 * select call
535 */
536 #ifdef TLS_SUPPORT
537 if (!nsc->tls_active || BIO_pending(nsc->ssl_io) == 0)
538 #endif /* TLS_SUPPORT */
539 {
540 struct timeval tv;
541 fd_set rfds;
542
543 FD_ZERO(&rfds);
544 FD_SET(nsc->ns_readfd, &rfds);
545
546 tv.tv_sec = nsc->ns_timeout;
547 tv.tv_usec = 0;
548
549 rc = select(nsc->ns_readfd + 1, &rfds, NULL, NULL, &tv);
550
551 if (rc == -1) {
552 netsec_err(errstr, "select() while reading failed: %s",
553 strerror(errno));
554 return NOTOK;
555 }
556
557 if (rc == 0) {
558 netsec_err(errstr, "read() timed out after %d seconds",
559 nsc->ns_timeout);
560 return NOTOK;
561 }
562
563 /*
564 * At this point, we know that rc is 1, so there's not even any
565 * point to check to see if our descriptor is set in rfds.
566 */
567 }
568
569 /*
570 * Some explanation:
571 *
572 * startoffset is the offset from the beginning of the input
573 * buffer to data that is in our input buffer, but has not yet
574 * been consumed. This can be non-zero if functions like
575 * netsec_readline() leave leftover data.
576 *
577 * remaining is the remaining amount of unconsumed data in the input
578 * buffer.
579 *
580 * end is a pointer to the end of the valid data + 1; it's where
581 * the next read should go.
582 */
583
584 startoffset = nsc->ns_inptr - nsc->ns_inbuffer;
585 remaining = nsc->ns_inbufsize - (startoffset + nsc->ns_inbuflen);
586 end = nsc->ns_inptr + nsc->ns_inbuflen;
587
588 /*
589 * If we're past the halfway point in our read buffers, shuffle everything
590 * back to the beginning.
591 */
592
593 if (startoffset > nsc->ns_inbufsize / 2) {
594 memmove(nsc->ns_inbuffer, nsc->ns_inptr, nsc->ns_inbuflen);
595 nsc->ns_inptr = nsc->ns_inbuffer;
596 startoffset = 0;
597 remaining = nsc->ns_inbufsize - nsc->ns_inbuflen;
598 end = nsc->ns_inptr + nsc->ns_inbuflen;
599 }
600
601 /*
602 * If we are using TLS, then just read via the BIO. But we still
603 * use our local buffer.
604 */
605 #ifdef TLS_SUPPORT
606 if (nsc->tls_active) {
607 rc = BIO_read(nsc->ssl_io, end, remaining);
608 if (rc == 0) {
609 SSL *ssl;
610 int errcode;
611
612 /*
613 * Check to see if we're supposed to retry; if so,
614 * then go back and read again.
615 */
616
617 if (BIO_should_retry(nsc->ssl_io))
618 goto retry;
619
620 /*
621 * Okay, fine. Get the real error out of the SSL context.
622 */
623
624 if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
625 netsec_err(errstr, "SSL_read() returned 0, but cannot "
626 "retrieve SSL context");
627 return NOTOK;
628 }
629
630 errcode = SSL_get_error(ssl, rc);
631 if (errcode == SSL_ERROR_ZERO_RETURN) {
632 netsec_err(errstr, "TLS peer closed remote connection");
633 } else {
634 netsec_err(errstr, "TLS network read failed: %s",
635 ERR_error_string(ERR_peek_last_error(), NULL));
636 }
637 if (nsc->ns_snoop)
638 ERR_print_errors_fp(stderr);
639 return NOTOK;
640 }
641 if (rc < 0) {
642 /* Definitely an error */
643 netsec_err(errstr, "Read on TLS connection failed: %s",
644 ERR_error_string(ERR_get_error(), NULL));
645 return NOTOK;
646 }
647
648 nsc->ns_inbuflen += rc;
649
650 return OK;
651 }
652 #endif /* TLS_SUPPORT */
653
654 /*
655 * Okay, time to read some data. Either we're just doing it straight
656 * or we're passing it through sasl_decode() first.
657 */
658
659 #ifdef CYRUS_SASL
660 if (nsc->sasl_seclayer) {
661 readbuf = nsc->sasl_tmpbuf;
662 readbufsize = nsc->sasl_maxbufsize;
663 } else
664 #endif /* CYRUS_SASL */
665 {
666 readbuf = (char *) end;
667 readbufsize = remaining;
668 }
669
670 /*
671 * At this point, we should have active data on the connection (see
672 * select() above) so this read SHOULDN'T block. Hopefully.
673 */
674
675 rc = read(nsc->ns_readfd, readbuf, readbufsize);
676
677 if (rc == 0) {
678 netsec_err(errstr, "Received EOF on network read");
679 return NOTOK;
680 }
681
682 if (rc < 0) {
683 netsec_err(errstr, "Network read failed: %s", strerror(errno));
684 return NOTOK;
685 }
686
687 /*
688 * Okay, so we've had a successful read. If we are doing SASL security
689 * layers, pass this through sasl_decode(). sasl_decode() can return
690 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
691 * we can just update our length pointer.
692 */
693
694 #ifdef CYRUS_SASL
695 if (nsc->sasl_seclayer) {
696 const char *tmpout;
697 unsigned int tmpoutlen;
698
699 rc = sasl_decode(nsc->sasl_conn, nsc->sasl_tmpbuf, rc,
700 &tmpout, &tmpoutlen);
701
702 if (rc != SASL_OK) {
703 netsec_err(errstr, "Unable to decode SASL network data: %s",
704 sasl_errdetail(nsc->sasl_conn));
705 return NOTOK;
706 }
707
708 if (tmpoutlen == 0)
709 goto retry;
710
711 /*
712 * Just in case ...
713 */
714
715 if (tmpoutlen > remaining) {
716 netsec_err(errstr, "Internal error: SASL decode buffer overflow!");
717 return NOTOK;
718 }
719
720 memcpy(end, tmpout, tmpoutlen);
721
722 nsc->ns_inbuflen += tmpoutlen;
723 } else
724 #endif /* CYRUS_SASL */
725 nsc->ns_inbuflen += rc;
726
727 return OK;
728 }
729
730 /*
731 * Write data to our network connection. Really, fill up the buffer as
732 * much as we can, and flush it out if necessary. netsec_flush() does
733 * the real work.
734 */
735
736 int
737 netsec_write(netsec_context *nsc, const void *buffer, size_t size,
738 char **errstr)
739 {
740 const unsigned char *bufptr = buffer;
741 int rc, remaining;
742
743 /* Just in case */
744
745 if (size == 0)
746 return OK;
747
748 /*
749 * Run a loop copying in data to our local buffer; when we're done with
750 * any buffer overflows then just copy any remaining data in.
751 */
752
753 while ((int) size >= (remaining = nsc->ns_outbufsize - nsc->ns_outbuflen)) {
754 memcpy(nsc->ns_outptr, bufptr, remaining);
755
756 /*
757 * In theory I should increment outptr, but netsec_flush just resets
758 * it anyway.
759 */
760 nsc->ns_outbuflen = nsc->ns_outbufsize;
761
762 rc = netsec_flush(nsc, errstr);
763
764 if (rc != OK)
765 return NOTOK;
766
767 bufptr += remaining;
768 size -= remaining;
769 }
770
771 /*
772 * Copy any leftover data into the buffer.
773 */
774
775 if (size > 0) {
776 memcpy(nsc->ns_outptr, bufptr, size);
777 nsc->ns_outptr += size;
778 nsc->ns_outbuflen += size;
779 }
780
781 return OK;
782 }
783
784 /*
785 * Our network printf() routine, which really just calls netsec_vprintf().
786 */
787
788 int
789 netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...)
790 {
791 va_list ap;
792 int rc;
793
794 va_start(ap, format);
795 rc = netsec_vprintf(nsc, errstr, format, ap);
796 va_end(ap);
797
798 return rc;
799 }
800
801 /*
802 * Write bytes to the network using printf()-style formatting.
803 *
804 * Again, for the most part copy stuff into our buffer to be flushed
805 * out later.
806 */
807
808 int
809 netsec_vprintf(netsec_context *nsc, char **errstr, const char *format,
810 va_list ap)
811 {
812 int rc;
813 va_list apcopy;
814
815 /*
816 * Cheat a little. If we can fit the data into our outgoing buffer,
817 * great! If not, generate a flush and retry once.
818 */
819
820 retry:
821 va_copy(apcopy, ap);
822 rc = vsnprintf((char *) nsc->ns_outptr,
823 nsc->ns_outbufsize - nsc->ns_outbuflen, format, apcopy);
824 va_end(apcopy);
825
826 if (rc >= (int) (nsc->ns_outbufsize - nsc->ns_outbuflen)) {
827 /*
828 * This means we have an overflow. Note that we don't actually
829 * make use of the terminating NUL, but according to the spec
830 * vsnprintf() won't write to the last byte in the string; that's
831 * why we have to use >= in the comparison above.
832 */
833 if (nsc->ns_outbuffer == nsc->ns_outptr) {
834 /*
835 * Whoops, if the buffer pointer was the same as the start of the
836 * buffer, that means we overflowed the internal buffer.
837 * At that point, just give up.
838 */
839 netsec_err(errstr, "Internal error: wanted to printf() a total of "
840 "%d bytes, but our buffer size was only %d bytes",
841 rc, nsc->ns_outbufsize);
842 return NOTOK;
843 }
844 /*
845 * Generate a flush (which may be inefficient, but hopefully
846 * it isn't) and then try again.
847 */
848 if (netsec_flush(nsc, errstr) != OK)
849 return NOTOK;
850 /*
851 * After this, outbuffer should == outptr, so we shouldn't
852 * hit this next time around.
853 */
854 goto retry;
855 }
856
857 nsc->ns_outptr += rc;
858 nsc->ns_outbuflen += rc;
859
860 return OK;
861 }
862
863 /*
864 * Flush out any buffered data in our output buffers. This routine is
865 * actually where the real network writes take place.
866 */
867
868 int
869 netsec_flush(netsec_context *nsc, char **errstr)
870 {
871 const char *netoutbuf = (const char *) nsc->ns_outbuffer;
872 unsigned int netoutlen = nsc->ns_outbuflen;
873 int rc;
874
875 /*
876 * Small optimization
877 */
878
879 if (netoutlen == 0)
880 return OK;
881
882 /*
883 * If we have snoop turned on, output the data.
884 *
885 * Note here; if we don't have a CR or LF at the end, save the data
886 * in ns_snoop_savebuf for later and print it next time.
887 */
888
889 if (nsc->ns_snoop) {
890 unsigned int snoopoutlen = netoutlen;
891 const char *snoopoutbuf = (const char *) nsc->ns_outbuffer;
892
893 while (snoopoutlen > 0) {
894 const char *end = strpbrk(snoopoutbuf, "\r\n");
895 unsigned int outlen;
896
897 if (! end) {
898 if (nsc->ns_snoop_savebuf) {
899 nsc->ns_snoop_savebuf = mh_xrealloc(nsc->ns_snoop_savebuf,
900 strlen(nsc->ns_snoop_savebuf) +
901 snoopoutlen + 1);
902 strncat(nsc->ns_snoop_savebuf, snoopoutbuf, snoopoutlen);
903 } else {
904 nsc->ns_snoop_savebuf = mh_xmalloc(snoopoutlen + 1);
905 strncpy(nsc->ns_snoop_savebuf, snoopoutbuf, snoopoutlen);
906 nsc->ns_snoop_savebuf[snoopoutlen] = '\0';
907 }
908 break;
909 }
910
911 outlen = end - snoopoutbuf;
912
913 #ifdef CYRUS_SASL
914 if (nsc->sasl_seclayer)
915 fprintf(stderr, "(sasl-encrypted) ");
916 #endif /* CYRUS_SASL */
917 #ifdef TLS_SUPPORT
918 if (nsc->tls_active)
919 fprintf(stderr, "(tls-encrypted) ");
920 #endif /* TLS_SUPPORT */
921 fprintf(stderr, "=> ");
922 if (nsc->ns_snoop_cb) {
923 const char *ptr;
924 unsigned int cb_len = outlen;
925
926 if (nsc->ns_snoop_savebuf) {
927 cb_len += strlen(nsc->ns_snoop_savebuf);
928 nsc->ns_snoop_savebuf = mh_xrealloc(nsc->ns_snoop_savebuf,
929 outlen);
930 ptr = nsc->ns_snoop_savebuf;
931 } else {
932 ptr = snoopoutbuf;
933 }
934
935 nsc->ns_snoop_cb(nsc, ptr, cb_len, nsc->ns_snoop_context);
936
937 if (nsc->ns_snoop_savebuf) {
938 free(nsc->ns_snoop_savebuf);
939 nsc->ns_snoop_savebuf = NULL;
940 }
941 } else {
942 if (nsc->ns_snoop_savebuf) {
943 fprintf(stderr, "%s", nsc->ns_snoop_savebuf);
944 free(nsc->ns_snoop_savebuf);
945 nsc->ns_snoop_savebuf = NULL;
946 }
947 fprintf(stderr, "%.*s\n", outlen, snoopoutbuf);
948 }
949
950 /*
951 * Alright, hopefully any previous leftover data is done,
952 * and we have the current line output. Move things past the
953 * next CR/LF.
954 */
955
956 snoopoutlen -= outlen;
957 snoopoutbuf += outlen;
958
959 if (snoopoutlen > 0 && *snoopoutbuf == '\r') {
960 snoopoutlen--;
961 snoopoutbuf++;
962 }
963
964 if (snoopoutlen > 0 && *snoopoutbuf == '\n') {
965 snoopoutlen--;
966 snoopoutbuf++;
967 }
968 }
969 }
970
971 /*
972 * If SASL security layers are in effect, run the data through
973 * sasl_encode() first.
974 */
975 #ifdef CYRUS_SASL
976 if (nsc->sasl_seclayer) {
977 rc = sasl_encode(nsc->sasl_conn, (const char *) nsc->ns_outbuffer,
978 nsc->ns_outbuflen, &netoutbuf, &netoutlen);
979
980 if (rc != SASL_OK) {
981 netsec_err(errstr, "SASL data encoding failed: %s",
982 sasl_errdetail(nsc->sasl_conn));
983 return NOTOK;
984 }
985
986 }
987 #endif /* CYRUS_SASL */
988
989 /*
990 * If TLS is active, then use those functions to write out the
991 * data.
992 */
993 #ifdef TLS_SUPPORT
994 if (nsc->tls_active) {
995 if (BIO_write(nsc->ssl_io, netoutbuf, netoutlen) <= 0) {
996 netsec_err(errstr, "Error writing to TLS connection: %s",
997 ERR_error_string(ERR_get_error(), NULL));
998 return NOTOK;
999 }
1000 } else
1001 #endif /* TLS_SUPPORT */
1002 {
1003 rc = write(nsc->ns_writefd, netoutbuf, netoutlen);
1004
1005 if (rc < 0) {
1006 netsec_err(errstr, "write() failed: %s", strerror(errno));
1007 return NOTOK;
1008 }
1009 }
1010
1011 nsc->ns_outptr = nsc->ns_outbuffer;
1012 nsc->ns_outbuflen = 0;
1013
1014 return OK;
1015 }
1016
1017 /*
1018 * Set various SASL protocol parameters
1019 */
1020
1021 int
1022 netsec_set_sasl_params(netsec_context *nsc, const char *service,
1023 const char *mechanism, netsec_sasl_callback callback,
1024 void *context, char **errstr)
1025 {
1026 #ifdef CYRUS_SASL
1027 sasl_callback_t *sasl_cbs;
1028 int retval;
1029
1030 if (!nsc->ns_hostname) {
1031 netsec_err(errstr, "Internal error: ns_hostname is NULL");
1032 return NOTOK;
1033 }
1034
1035 if (! sasl_initialized) {
1036 retval = sasl_client_init(NULL);
1037 if (retval != SASL_OK) {
1038 netsec_err(errstr, "SASL client initialization failed: %s",
1039 sasl_errstring(retval, NULL, NULL));
1040 return NOTOK;
1041 }
1042 sasl_initialized = true;
1043 }
1044
1045 /*
1046 * Allocate an array of SASL callbacks for this connection.
1047 * Right now we just allocate an array of four callbacks.
1048 */
1049
1050 sasl_cbs = mh_xmalloc(sizeof(*sasl_cbs) * 4);
1051
1052 sasl_cbs[0].id = SASL_CB_USER;
1053 sasl_cbs[0].proc = (sasl_callback_ft) netsec_get_user;
1054 sasl_cbs[0].context = nsc;
1055
1056 sasl_cbs[1].id = SASL_CB_AUTHNAME;
1057 sasl_cbs[1].proc = (sasl_callback_ft) netsec_get_user;
1058 sasl_cbs[1].context = nsc;
1059
1060 sasl_cbs[2].id = SASL_CB_PASS;
1061 sasl_cbs[2].proc = (sasl_callback_ft) netsec_get_password;
1062 sasl_cbs[2].context = nsc;
1063
1064 sasl_cbs[3].id = SASL_CB_LIST_END;
1065 sasl_cbs[3].proc = NULL;
1066 sasl_cbs[3].context = NULL;
1067
1068 nsc->sasl_cbs = sasl_cbs;
1069
1070 retval = sasl_client_new(service, nsc->ns_hostname, NULL, NULL,
1071 nsc->sasl_cbs, 0, &nsc->sasl_conn);
1072
1073 if (retval) {
1074 netsec_err(errstr, "SASL new client allocation failed: %s",
1075 sasl_errstring(retval, NULL, NULL));
1076 return NOTOK;
1077 }
1078
1079 /*
1080 * Set up our credentials
1081 */
1082
1083 nsc->sasl_creds = nmh_get_credentials(nsc->ns_hostname, nsc->ns_userid);
1084
1085 #else /* CYRUS_SASL */
1086 NMH_UNUSED(service);
1087 NMH_UNUSED(errstr);
1088 #endif /* CYRUS_SASL */
1089
1090 /*
1091 * According to the RFC, mechanisms can only be uppercase letter, numbers,
1092 * and a hyphen or underscore. So make sure we uppercase any letters
1093 * in case the user passed in lowercase.
1094 */
1095
1096 if (mechanism) {
1097 nsc->sasl_mech = mh_xstrdup(mechanism);
1098 to_upper(nsc->sasl_mech);
1099 }
1100
1101 nsc->sasl_proto_cb = callback;
1102 nsc->sasl_proto_context = context;
1103
1104 return OK;
1105 }
1106
1107 #ifdef CYRUS_SASL
1108 /*
1109 * Our userid callback; return the specified username to the SASL
1110 * library when asked.
1111 */
1112
1113 int
1114 netsec_get_user(void *context, int id, const char **result,
1115 unsigned int *len)
1116 {
1117 netsec_context *nsc = (netsec_context *) context;
1118
1119 if (! result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME))
1120 return SASL_BADPARAM;
1121
1122 *result = nmh_cred_get_user(nsc->sasl_creds);
1123
1124 if (len)
1125 *len = strlen(*result);
1126
1127 return SASL_OK;
1128 }
1129
1130 /*
1131 * Retrieve a password and return it to SASL
1132 */
1133
1134 static int
1135 netsec_get_password(sasl_conn_t *conn, void *context, int id,
1136 sasl_secret_t **psecret)
1137 {
1138 netsec_context *nsc = (netsec_context *) context;
1139 const char *password;
1140 int len;
1141
1142 NMH_UNUSED(conn);
1143
1144 if (! psecret || id != SASL_CB_PASS)
1145 return SASL_BADPARAM;
1146
1147 password = nmh_cred_get_password(nsc->sasl_creds);
1148
1149 len = strlen(password);
1150
1151 /*
1152 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1153 * us room for a terminating NUL
1154 */
1155
1156 *psecret = malloc(sizeof(sasl_secret_t) + len);
1157
1158 if (! *psecret)
1159 return SASL_NOMEM;
1160
1161 (*psecret)->len = len;
1162 strcpy((char *) (*psecret)->data, password);
1163
1164 nsc->sasl_secret = *psecret;
1165
1166 return SASL_OK;
1167 }
1168 #endif /* CYRUS_SASL */
1169
1170 /*
1171 * Negotiate SASL on this connection
1172 */
1173
1174 int
1175 netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr)
1176 {
1177 #ifdef CYRUS_SASL
1178 sasl_security_properties_t secprops;
1179 const char *chosen_mech;
1180 const unsigned char *saslbuf;
1181 unsigned char *outbuf;
1182 unsigned int saslbuflen, outbuflen;
1183 sasl_ssf_t *ssf;
1184 int *outbufmax;
1185 #endif
1186 #ifdef OAUTH_SUPPORT
1187 unsigned char *xoauth_client_res;
1188 size_t xoauth_client_res_len;
1189 #endif /* OAUTH_SUPPORT */
1190 #if defined CYRUS_SASL || defined OAUTH_SUPPORT
1191 int rc;
1192 #endif /* CYRUS_SASL || OAUTH_SUPPORT */
1193
1194 /*
1195 * Output some SASL information if snoop is turned on
1196 */
1197
1198 if (nsc->ns_snoop) {
1199 fprintf(stderr, "SASL mechanisms supported by server: %s\n", mechlist);
1200
1201 if (nsc->sasl_mech) {
1202 fprintf(stderr, "User has requested SASL mechanism: %s\n",
1203 nsc->sasl_mech);
1204 } else {
1205 fprintf(stderr, "No SASL mech selected, will pick "
1206 "the best mech supported by SASL library\n");
1207 }
1208 }
1209
1210 /*
1211 * If we've been passed a requested mechanism, check our mechanism
1212 * list from the protocol. If it's not supported, return an error.
1213 */
1214
1215 if (nsc->sasl_mech) {
1216 char **str, *mlist = getcpy(mechlist);
1217 int i;
1218
1219 str = brkstring(mlist, " ", NULL);
1220
1221 for (i = 0; str[i] != NULL; i++) {
1222 if (strcasecmp(nsc->sasl_mech, str[i]) == 0) {
1223 break;
1224 }
1225 }
1226
1227 i = (str[i] == NULL);
1228
1229 free(mlist);
1230
1231 if (i) {
1232 netsec_err(errstr, "Chosen mechanism %s not supported by server",
1233 nsc->sasl_mech);
1234 return NOTOK;
1235 }
1236 }
1237
1238 #ifdef OAUTH_SUPPORT
1239 if (nsc->sasl_mech && strcasecmp(nsc->sasl_mech, "XOAUTH2") == 0) {
1240 /*
1241 * This should be relatively straightforward, but requires some
1242 * help from the plugin. Basically, if XOAUTH2 is a success,
1243 * the callback has to return success, but no output data. If
1244 * there is output data, it will be assumed that it is the JSON
1245 * error message.
1246 */
1247
1248 if (nsc->ns_snoop) {
1249 fprintf(stderr, "Using internal XOAUTH2 mechanism\n");
1250 }
1251
1252 if (! nsc->oauth_service) {
1253 netsec_err(errstr, "Internal error: OAuth2 service name not given");
1254 return NOTOK;
1255 }
1256
1257 nsc->sasl_chosen_mech = mh_xstrdup(nsc->sasl_mech);
1258
1259 if (mh_oauth_do_xoauth(nsc->ns_userid, nsc->oauth_service,
1260 &xoauth_client_res, &xoauth_client_res_len,
1261 nsc->ns_snoop ? stderr : NULL) != OK) {
1262 netsec_err(errstr, "Internal error: Unable to get OAuth2 "
1263 "bearer token");
1264 return NOTOK;
1265 }
1266
1267 rc = nsc->sasl_proto_cb(NETSEC_SASL_START, xoauth_client_res,
1268 xoauth_client_res_len, NULL, 0,
1269 nsc->sasl_proto_context, errstr);
1270 free(xoauth_client_res);
1271
1272 if (rc != OK)
1273 return NOTOK;
1274
1275 /*
1276 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1277 * success, we indicate that with no output data. But if we
1278 * fail, then send a blank message and get the resulting
1279 * error.
1280 */
1281
1282 rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
1283 nsc->sasl_proto_context, errstr);
1284
1285 if (rc != OK) {
1286 /*
1287 * We're going to assume the error here is a JSON response;
1288 * we ignore it and send a blank message in response. We should
1289 * then get a failure messages with a useful error. We should
1290 * NOT get a success message at this point.
1291 */
1292 free(*errstr);
1293 nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0,
1294 nsc->sasl_proto_context, NULL);
1295 rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
1296 nsc->sasl_proto_context, errstr);
1297 if (rc == 0) {
1298 netsec_err(errstr, "Unexpected success after OAuth failure!");
1299 }
1300 return NOTOK;
1301 }
1302 return OK;
1303 }
1304 #endif /* OAUTH_SUPPORT */
1305
1306 #ifdef CYRUS_SASL
1307 /*
1308 * In netsec_set_sasl_params, we've already done all of our setup with
1309 * sasl_client_init() and sasl_client_new(). So time to set security
1310 * properties, call sasl_client_start(), and generate the protocol
1311 * messages.
1312 */
1313
1314 if (nsc->ns_snoop) {
1315 const char *client_mechlist;
1316
1317 rc = sasl_listmech(nsc->sasl_conn, NULL, NULL, " ", NULL,
1318 &client_mechlist, NULL, NULL);
1319
1320 if (rc != SASL_OK) {
1321 fprintf(stderr, "Unable to get client mechlist: %s\n",
1322 sasl_errstring(rc, NULL, NULL));
1323 } else {
1324 fprintf(stderr, "Client supported SASL mechanisms: %s\n",
1325 client_mechlist);
1326 }
1327 }
1328
1329 ZERO(&secprops);
1330 secprops.maxbufsize = SASL_MAXRECVBUF;
1331
1332 /*
1333 * If we're using TLS, do not negotiate a security layer
1334 */
1335
1336 secprops.max_ssf =
1337 #ifdef TLS_SUPPORT
1338 nsc->tls_active ? 0 :
1339 #endif /* TLS_SUPPORT */
1340 UINT_MAX;
1341
1342 #ifdef TLS_SUPPORT
1343 if (nsc->ns_snoop && nsc->tls_active)
1344 fprintf(stderr, "SASL security layers disabled due to the use "
1345 "of TLS\n");
1346 #endif /* TLS_SUPPORT */
1347
1348 rc = sasl_setprop(nsc->sasl_conn, SASL_SEC_PROPS, &secprops);
1349
1350 if (rc != SASL_OK) {
1351 netsec_err(errstr, "SASL security property initialization failed: %s",
1352 sasl_errstring(rc, NULL, NULL));
1353 return NOTOK;
1354 }
1355
1356 /*
1357 * Start the actual protocol negotiation, and go through the
1358 * sasl_client_step() loop (after sasl_client_start, of course).
1359 */
1360
1361 rc = sasl_client_start(nsc->sasl_conn,
1362 nsc->sasl_mech ? nsc->sasl_mech : mechlist, NULL,
1363 (const char **) &saslbuf, &saslbuflen,
1364 &chosen_mech);
1365
1366 if (rc != SASL_OK && rc != SASL_CONTINUE) {
1367 netsec_err(errstr, "SASL client start failed: %s",
1368 sasl_errdetail(nsc->sasl_conn));
1369 return NOTOK;
1370 }
1371
1372 nsc->sasl_chosen_mech = getcpy(chosen_mech);
1373
1374 if (nsc->ns_snoop)
1375 fprintf(stderr, "Chosen sasl mechanism: %s\n", chosen_mech);
1376
1377 if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, NULL, 0,
1378 nsc->sasl_proto_context, errstr) != OK)
1379 return NOTOK;
1380
1381 /*
1382 * We've written out our first message; enter in the step loop
1383 */
1384
1385 while (rc == SASL_CONTINUE) {
1386 /*
1387 * Call our SASL callback, which will handle the details of
1388 * reading data from the network.
1389 */
1390
1391 if (nsc->sasl_proto_cb(NETSEC_SASL_READ, NULL, 0, &outbuf, &outbuflen,
1392 nsc->sasl_proto_context, errstr) != OK) {
1393 nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0,
1394 nsc->sasl_proto_context, NULL);
1395 return NOTOK;
1396 }
1397
1398 rc = sasl_client_step(nsc->sasl_conn, (char *) outbuf, outbuflen, NULL,
1399 (const char **) &saslbuf, &saslbuflen);
1400
1401 free(outbuf);
1402
1403 if (rc != SASL_OK && rc != SASL_CONTINUE) {
1404 netsec_err(errstr, "SASL client negotiation failed: %s",
1405 sasl_errdetail(nsc->sasl_conn));
1406 nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0,
1407 nsc->sasl_proto_context, NULL);
1408 return NOTOK;
1409 }
1410
1411 if (nsc->sasl_proto_cb(NETSEC_SASL_WRITE, saslbuf, saslbuflen,
1412 NULL, 0, nsc->sasl_proto_context,
1413 errstr) != OK) {
1414 nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0,
1415 nsc->sasl_proto_context, NULL);
1416 return NOTOK;
1417 }
1418 }
1419
1420 /*
1421 * SASL exchanges should be complete, process the final response message
1422 * from the server.
1423 */
1424
1425 if (nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0,
1426 nsc->sasl_proto_context, errstr) != OK) {
1427 /*
1428 * At this point we can't really send an abort since the SASL dialog
1429 * has completed, so just bubble back up the error message.
1430 */
1431
1432 return NOTOK;
1433 }
1434
1435 /*
1436 * At this point, SASL should be complete. Get a few properties
1437 * from the authentication exchange.
1438 */
1439
1440 rc = sasl_getprop(nsc->sasl_conn, SASL_SSF, (const void **) &ssf);
1441
1442 if (rc != SASL_OK) {
1443 netsec_err(errstr, "Cannot retrieve SASL negotiated security "
1444 "strength factor: %s", sasl_errstring(rc, NULL, NULL));
1445 return NOTOK;
1446 }
1447
1448 nsc->sasl_ssf = *ssf;
1449
1450 if (nsc->sasl_ssf > 0) {
1451 if (nsc->ns_snoop)
1452 fprintf(stderr, "SASL security layer negotiated, SASL will "
1453 "perform encryption\n");
1454
1455 rc = sasl_getprop(nsc->sasl_conn, SASL_MAXOUTBUF,
1456 (const void **) &outbufmax);
1457
1458 if (rc != SASL_OK) {
1459 netsec_err(errstr, "Cannot retrieve SASL negotiated output "
1460 "buffer size: %s", sasl_errstring(rc, NULL, NULL));
1461 return NOTOK;
1462 }
1463
1464 /*
1465 * If our output buffer isn't the same size as the input buffer,
1466 * reallocate it and set the new size (since we won't encode any
1467 * data larger than that).
1468 */
1469
1470 nsc->sasl_maxbufsize = *outbufmax;
1471
1472 if (nsc->ns_outbufsize != nsc->sasl_maxbufsize) {
1473 nsc->ns_outbufsize = nsc->sasl_maxbufsize;
1474 nsc->ns_outbuffer = mh_xrealloc(nsc->ns_outbuffer,
1475 nsc->ns_outbufsize);
1476 /*
1477 * There shouldn't be any data in the buffer, but for
1478 * consistency's sake discard it.
1479 */
1480 nsc->ns_outptr = nsc->ns_outbuffer;
1481 nsc->ns_outbuflen = 0;
1482 }
1483
1484 /*
1485 * Allocate a buffer to do temporary reads into, before we
1486 * call sasl_decode()
1487 */
1488
1489 nsc->sasl_tmpbuf = mh_xmalloc(nsc->sasl_maxbufsize);
1490
1491 /*
1492 * Okay, this is a bit weird. Make sure that the input buffer
1493 * is at least TWICE the size of the max buffer size. That's
1494 * because if we're consuming data but want to extend the current
1495 * buffer, we want to be sure there's room for another full buffer's
1496 * worth of data.
1497 */
1498
1499 if (nsc->ns_inbufsize < nsc->sasl_maxbufsize * 2) {
1500 size_t offset = nsc->ns_inptr - nsc->ns_inbuffer;
1501 nsc->ns_inbufsize = nsc->sasl_maxbufsize * 2;
1502 nsc->ns_inbuffer = mh_xrealloc(nsc->ns_inbuffer, nsc->ns_inbufsize);
1503 nsc->ns_inptr = nsc->ns_inbuffer + offset;
1504 }
1505
1506 nsc->sasl_seclayer = 1;
1507 } else if (nsc->ns_snoop) {
1508 fprintf(stderr, "SASL Security layer NOT negotiated, SASL will NOT "
1509 "perform encryption\n");
1510 #ifdef TLS_SUPPORT
1511 if (nsc->tls_active) {
1512 fprintf(stderr, "Encryption will be handled by TLS\n");
1513 } else
1514 #endif /* TLS_SUPPORT */
1515 fprintf(stderr, "Connection will NOT be encrypted, use caution\n");
1516 }
1517
1518 return OK;
1519 #else
1520 /*
1521 * If we're at this point, then either we have NEITHER OAuth2 or
1522 * Cyrus-SASL compiled in, or have OAuth2 but didn't give the XOAUTH2
1523 * mechanism on the command line.
1524 */
1525
1526 if (! nsc->sasl_mech)
1527 netsec_err(errstr, "SASL library support not available; please "
1528 "specify a SASL mechanism to use");
1529 else
1530 netsec_err(errstr, "No support for the %s SASL mechanism",
1531 nsc->sasl_mech);
1532
1533 return NOTOK;
1534 #endif /* CYRUS_SASL */
1535 }
1536
1537 /*
1538 * Retrieve our chosen SASL mechanism
1539 */
1540
1541 char *
1542 netsec_get_sasl_mechanism(netsec_context *nsc)
1543 {
1544 return nsc->sasl_chosen_mech;
1545 }
1546
1547 /*
1548 * Return the negotiated SASL strength security factor (SSF)
1549 */
1550
1551 int
1552 netsec_get_sasl_ssf(netsec_context *nsc)
1553 {
1554 #ifdef CYRUS_SASL
1555 return nsc->sasl_ssf;
1556 #else /* CYRUS_SASL */
1557 NMH_UNUSED(nsc);
1558 return 0;
1559 #endif /* CYRUS_SASL */
1560 }
1561
1562 /*
1563 * Set an OAuth2 service name, if we support it.
1564 */
1565
1566 int
1567 netsec_set_oauth_service(netsec_context *nsc, const char *service)
1568 {
1569 #ifdef OAUTH_SUPPORT
1570 nsc->oauth_service = getcpy(service);
1571 return OK;
1572 #else /* OAUTH_SUPPORT */
1573 NMH_UNUSED(nsc);
1574 NMH_UNUSED(service);
1575 return NOTOK;
1576 #endif /* OAUTH_SUPPORT */
1577 }
1578
1579 /*
1580 * Initialize (and enable) TLS for this connection
1581 */
1582
1583 int
1584 netsec_set_tls(netsec_context *nsc, int tls, int noverify, char **errstr)
1585 {
1586 #ifdef TLS_SUPPORT
1587 if (tls) {
1588 SSL *ssl;
1589 BIO *rbio, *wbio, *ssl_bio;
1590
1591 if (! tls_initialized) {
1592 SSL_library_init();
1593 SSL_load_error_strings();
1594
1595 /*
1596 * Create the SSL context; this has the properties for all
1597 * SSL connections (we are only doing one), though. Make sure
1598 * we only support secure (known as of now) TLS protocols.
1599 */
1600
1601 sslctx = SSL_CTX_new(SSLv23_client_method());
1602
1603 if (! sslctx) {
1604 netsec_err(errstr, "Unable to initialize OpenSSL context: %s",
1605 ERR_error_string(ERR_get_error(), NULL));
1606 return NOTOK;
1607 }
1608
1609 SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
1610 SSL_OP_NO_TLSv1);
1611
1612 if (!SSL_CTX_set_default_verify_paths(sslctx)) {
1613 netsec_err(errstr, "Unable to set default certificate "
1614 "verification paths: %s",
1615 ERR_error_string(ERR_get_error(), NULL));
1616 return NOTOK;
1617 }
1618
1619 tls_initialized = true;
1620 }
1621
1622 if (nsc->ns_readfd == -1 || nsc->ns_writefd == -1) {
1623 netsec_err(errstr, "Invalid file descriptor in netsec context");
1624 return NOTOK;
1625 }
1626
1627 /*
1628 * Create the SSL structure which holds the data for a single
1629 * TLS connection.
1630 */
1631
1632 ssl = SSL_new(sslctx);
1633
1634 if (! ssl) {
1635 netsec_err(errstr, "Unable to create SSL connection: %s",
1636 ERR_error_string(ERR_get_error(), NULL));
1637 return NOTOK;
1638 }
1639
1640 /*
1641 * Never bother us, since we are using blocking sockets.
1642 */
1643
1644 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
1645
1646 /*
1647 * This is a bit weird, so pay attention.
1648 *
1649 * We create a socket BIO, and bind it to our SSL connection.
1650 * That means reads and writes to the SSL connection will use our
1651 * supplied socket.
1652 *
1653 * Then we create an SSL BIO, and assign our current SSL connection
1654 * to it. This is done so our code stays simple if we want to use
1655 * any buffering BIOs (right now we do our own buffering).
1656 * So the chain looks like:
1657 *
1658 * SSL BIO -> socket BIO.
1659 */
1660
1661 rbio = BIO_new_socket(nsc->ns_readfd, BIO_CLOSE);
1662
1663 if (! rbio) {
1664 netsec_err(errstr, "Unable to create a read socket BIO: %s",
1665 ERR_error_string(ERR_get_error(), NULL));
1666 SSL_free(ssl);
1667 return NOTOK;
1668 }
1669
1670 wbio = BIO_new_socket(nsc->ns_writefd, BIO_CLOSE);
1671
1672 if (! wbio) {
1673 netsec_err(errstr, "Unable to create a write socket BIO: %s",
1674 ERR_error_string(ERR_get_error(), NULL));
1675 SSL_free(ssl);
1676 BIO_free(rbio);
1677 return NOTOK;
1678 }
1679
1680 SSL_set_bio(ssl, rbio, wbio);
1681 SSL_set_connect_state(ssl);
1682
1683 /*
1684 * If noverify is NOT set, then do certificate validation.
1685 * Turning on SSL_VERIFY_PEER will verify the certificate chain
1686 * against locally stored root certificates (the locations are
1687 * set using SSL_CTX_set_default_verify_paths()), and we put
1688 * the hostname in the X509 verification parameters so the OpenSSL
1689 * code will verify that the hostname appears in the server
1690 * certificate.
1691 */
1692
1693 if (! noverify) {
1694 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1695 X509_VERIFY_PARAM *param;
1696 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1697
1698 SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
1699 if (! nsc->ns_hostname) {
1700 netsec_err(errstr, "Internal error: hostname not set and "
1701 "certification verification enabled");
1702 SSL_free(ssl);
1703 return NOTOK;
1704 }
1705
1706 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1707 param = SSL_get0_param(ssl);
1708
1709 if (! X509_VERIFY_PARAM_set1_host(param, nsc->ns_hostname, 0)) {
1710 netsec_err(errstr, "Unable to add hostname %s to cert "
1711 "verification parameters: %s", nsc->ns_hostname,
1712 ERR_error_string(ERR_get_error(), NULL));
1713 SSL_free(ssl);
1714 return NOTOK;
1715 }
1716 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1717 }
1718
1719 ssl_bio = BIO_new(BIO_f_ssl());
1720
1721 if (! ssl_bio) {
1722 netsec_err(errstr, "Unable to create a SSL BIO: %s",
1723 ERR_error_string(ERR_get_error(), NULL));
1724 SSL_free(ssl);
1725 return NOTOK;
1726 }
1727
1728 BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
1729 nsc->ssl_io = ssl_bio;
1730
1731 /*
1732 * Since SSL now owns these file descriptors, have it handle the
1733 * closing of them instead of netsec_shutdown().
1734 */
1735
1736 nsc->ns_noclose = 1;
1737
1738 return OK;
1739 }
1740 BIO_free_all(nsc->ssl_io);
1741 nsc->ssl_io = NULL;
1742
1743 #else /* TLS_SUPPORT */
1744 NMH_UNUSED(nsc);
1745 NMH_UNUSED(noverify);
1746
1747 if (tls) {
1748 netsec_err(errstr, "TLS is not supported");
1749 return NOTOK;
1750 }
1751 #endif /* TLS_SUPPORT */
1752
1753 return OK;
1754 }
1755
1756 /*
1757 * Start TLS negotiation on this connection
1758 */
1759
1760 int
1761 netsec_negotiate_tls(netsec_context *nsc, char **errstr)
1762 {
1763 #ifdef TLS_SUPPORT
1764 if (! nsc->ssl_io) {
1765 netsec_err(errstr, "TLS has not been configured for this connection");
1766 return NOTOK;
1767 }
1768
1769 if (BIO_do_handshake(nsc->ssl_io) < 1) {
1770 unsigned long errcode = ERR_get_error();
1771
1772 /*
1773 * Print a more detailed message if it was certificate verification
1774 * failure.
1775 */
1776
1777 if (ERR_GET_LIB(errcode) == ERR_LIB_SSL &&
1778 ERR_GET_REASON(errcode) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
1779 SSL *ssl;
1780
1781 if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
1782 netsec_err(errstr, "Certificate verification failed, but "
1783 "cannot retrieve SSL handle: %s",
1784 ERR_error_string(ERR_get_error(), NULL));
1785 } else {
1786 netsec_err(errstr, "Server certificate verification failed: %s",
1787 X509_verify_cert_error_string(
1788 SSL_get_verify_result(ssl)));
1789 }
1790 } else {
1791 netsec_err(errstr, "TLS negotiation failed: %s",
1792 ERR_error_string(errcode, NULL));
1793 }
1794
1795 /*
1796 * Because negotiation failed, shut down TLS so we don't get any
1797 * garbage on the connection. Because of weirdness with SSL_shutdown,
1798 * we end up calling it twice: once explicitly, once as part of
1799 * BIO_free_all().
1800 */
1801
1802 BIO_ssl_shutdown(nsc->ssl_io);
1803 BIO_free_all(nsc->ssl_io);
1804 nsc->ssl_io = NULL;
1805
1806 return NOTOK;
1807 }
1808
1809 if (nsc->ns_snoop) {
1810 SSL *ssl;
1811
1812 if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
1813 fprintf(stderr, "WARNING: cannot determine SSL ciphers\n");
1814 } else {
1815 const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
1816 fprintf(stderr, "TLS negotiation successful: %s(%d) %s\n",
1817 SSL_CIPHER_get_name(cipher),
1818 SSL_CIPHER_get_bits(cipher, NULL),
1819 SSL_CIPHER_get_version(cipher));
1820 SSL_SESSION_print_fp(stderr, SSL_get_session(ssl));
1821 }
1822 }
1823
1824 nsc->tls_active = 1;
1825
1826 return OK;
1827 #else /* TLS_SUPPORT */
1828 NMH_UNUSED(nsc);
1829 netsec_err(errstr, "TLS not supported");
1830
1831 return NOTOK;
1832 #endif /* TLS_SUPPORT */
1833 }
1834
1835 /*
1836 * Generate an (allocated) error string
1837 */
1838
1839 void
1840 netsec_err(char **errstr, const char *fmt, ...)
1841 {
1842 va_list ap;
1843 size_t errbufsize;
1844 char *errbuf = NULL;
1845 int rc = 127;
1846
1847 if (! errstr)
1848 return;
1849
1850 do {
1851 errbufsize = rc + 1;
1852 errbuf = mh_xrealloc(errbuf, errbufsize);
1853 va_start(ap, fmt);
1854 rc = vsnprintf(errbuf, errbufsize, fmt, ap);
1855 va_end(ap);
1856 } while (rc >= (int) errbufsize);
1857
1858 *errstr = errbuf;
1859 }