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