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