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