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