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