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