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