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