1 /* netsec.c -- Network security routines for handling protocols that
2 * require SASL and/or TLS.
4 * This code is Copyright (c) 2016, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
14 #include <sys/select.h>
17 #include <sasl/sasl.h>
18 #include <sasl/saslutil.h>
19 # if SASL_VERSION_FULL < 0x020125
20 /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
21 which has an explicit void parameter list, according to best
22 practice. So we need to cast to avoid compile warnings.
23 Provide this prototype for earlier versions. */
24 typedef int (*sasl_callback_ft
)();
25 # endif /* SASL_VERSION_FULL < 0x020125 */
27 static int netsec_get_user(void *context
, int id
, const char **result
,
29 static int netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
30 sasl_secret_t
**psecret
);
32 static int sasl_initialized
= 0;
34 #define SASL_MAXRECVBUF 65536
35 #endif /* CYRUS_SASL */
38 #include <openssl/ssl.h>
39 #include <openssl/err.h>
41 static int tls_initialized
= 0;
42 static SSL_CTX
*sslctx
= NULL
; /* SSL Context */
44 #endif /* TLS_SUPPORT */
46 /* I'm going to hardcode this for now; maybe make it adjustable later? */
47 #define NETSEC_BUFSIZE 65536
50 * Our context structure, which holds all of the relevant information
54 struct _netsec_context
{
55 int ns_readfd
; /* Read descriptor for network connection */
56 int ns_writefd
; /* Write descriptor for network connection */
57 int ns_snoop
; /* If true, display network data */
58 int ns_snoop_noend
; /* If true, didn't get a CR/LF on last line */
59 netsec_snoop_callback
*ns_snoop_cb
; /* Snoop output callback */
60 void *ns_snoop_context
; /* Context data for snoop function */
61 int ns_timeout
; /* Network read timeout, in seconds */
62 char *ns_userid
; /* Userid for authentication */
63 char *ns_hostname
; /* Hostname we've connected to */
64 unsigned char *ns_inbuffer
; /* Our read input buffer */
65 unsigned char *ns_inptr
; /* Our read buffer input pointer */
66 unsigned int ns_inbuflen
; /* Length of data in input buffer */
67 unsigned int ns_inbufsize
; /* Size of input buffer */
68 unsigned char *ns_outbuffer
;/* Output buffer */
69 unsigned char *ns_outptr
; /* Output buffer pointer */
70 unsigned int ns_outbuflen
; /* Output buffer data length */
71 unsigned int ns_outbufsize
; /* Output buffer size */
72 char *sasl_mech
; /* User-requested mechanism */
73 char *sasl_chosen_mech
; /* Mechanism chosen by SASL */
74 netsec_sasl_callback sasl_proto_cb
; /* SASL callback we use */
76 char *oauth_service
; /* OAuth2 service name */
77 #endif /* OAUTH_SUPPORT */
79 sasl_conn_t
*sasl_conn
; /* SASL connection context */
80 sasl_ssf_t sasl_ssf
; /* SASL Security Strength Factor */
81 sasl_callback_t
*sasl_cbs
; /* Callbacks used by SASL */
82 nmh_creds_t sasl_creds
; /* Credentials (username/password) */
83 sasl_secret_t
*sasl_secret
; /* SASL password structure */
84 int sasl_seclayer
; /* If true, SASL security layer is enabled */
85 char *sasl_tmpbuf
; /* Temporary read buffer for decodes */
86 size_t sasl_maxbufsize
; /* Maximum negotiated SASL buffer size */
87 #endif /* CYRUS_SASL */
89 BIO
*ssl_io
; /* BIO used for connection I/O */
90 int tls_active
; /* If true, TLS is running */
91 #endif /* TLS_SUPPORT */
95 * Function to read data from the actual network socket
98 static int netsec_fillread(netsec_context
*ns_context
, char **errstr
);
101 * Code to check the ASCII content of a byte array.
104 static int checkascii(const unsigned char *byte
, size_t len
);
107 * How this code works, in general.
109 * _If_ we are using no encryption then we buffer the network data
110 * through ns_inbuffer and ns_outbuffer. That should be relatively
113 * If we use encryption, then ns_inbuffer and ns_outbuffer contain the
114 * cleartext data. When it comes time to send the encrypted data on the
115 * (either from a flush or the buffer is full) we either use BIO_write()
116 * for TLS or sasl_encode() (followed by a write() for Cyrus-SASL. For
117 * reads we either use BIO_read() (TLS) or do a network read into a
118 * temporary buffer and use sasl_decode() (Cyrus-SASL). Note that if
119 * negotiate TLS then we disable SASL encryption.
121 * We used to use a buffering BIO for the reads/writes for TLS, but it
122 * ended up being complicated to special-case the buffering for everything
123 * except TLS, so the buffering is now unified, no matter which encryption
124 * method is being used (even none).
126 * For SASL authentication, we make use of (for now) the Cyrus-SASL
127 * library. For some mechanisms, we implement those mechanisms directly
128 * since the Cyrus SASL library doesn't support them (like OAuth).
132 * Allocate and initialize our security context
142 nsc
->ns_writefd
= -1;
144 nsc
->ns_snoop_noend
= 0;
145 nsc
->ns_snoop_cb
= NULL
;
146 nsc
->ns_snoop_context
= NULL
;
147 nsc
->ns_userid
= NULL
;
148 nsc
->ns_hostname
= 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
;
162 nsc
->oauth_service
= NULL
;
163 #endif /* OAUTH_SUPPORT */
165 nsc
->sasl_conn
= NULL
;
166 nsc
->sasl_cbs
= NULL
;
167 nsc
->sasl_creds
= NULL
;
168 nsc
->sasl_secret
= NULL
;
170 nsc
->sasl_seclayer
= 0;
171 nsc
->sasl_tmpbuf
= NULL
;
172 nsc
->sasl_maxbufsize
= 0;
173 #endif /* CYRUS_SASL */
177 #endif /* TLS_SUPPORT */
182 * Shutdown the connection completely and free all resources.
183 * The connection is only closed if the flag is given.
187 netsec_shutdown(netsec_context
*nsc
, int closeflag
)
189 mh_xfree(nsc
->ns_userid
);
190 mh_xfree(nsc
->ns_hostname
);
191 mh_xfree(nsc
->ns_inbuffer
);
192 mh_xfree(nsc
->ns_outbuffer
);
193 mh_xfree(nsc
->sasl_mech
);
194 mh_xfree(nsc
->sasl_chosen_mech
);
196 mh_xfree(nsc
->oauth_service
);
197 #endif /* OAUTH_SERVICE */
200 sasl_dispose(&nsc
->sasl_conn
);
201 mh_xfree(nsc
->sasl_cbs
);
203 nmh_credentials_free(nsc
->sasl_creds
);
204 if (nsc
->sasl_secret
) {
205 if (nsc
->sasl_secret
->len
> 0) {
206 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
208 free(nsc
->sasl_secret
);
210 mh_xfree(nsc
->sasl_tmpbuf
);
211 #endif /* CYRUS_SASL */
215 * I checked; BIO_free_all() will cause SSL_shutdown to be called
216 * on the SSL object in the chain.
218 BIO_free_all(nsc
->ssl_io
);
219 #endif /* TLS_SUPPORT */
222 if (nsc
->ns_readfd
!= -1)
223 close(nsc
->ns_readfd
);
224 if (nsc
->ns_writefd
!= -1 && nsc
->ns_writefd
!= nsc
->ns_readfd
)
225 close(nsc
->ns_writefd
);
232 * Set the file descriptor for our context
236 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
238 nsc
->ns_readfd
= readfd
;
239 nsc
->ns_writefd
= writefd
;
243 * Set the userid used for authentication for this context
247 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
249 nsc
->ns_userid
= getcpy(userid
);
253 * Set the hostname of the remote host we're connecting to.
257 netsec_set_hostname(netsec_context
*nsc
, const char *hostname
)
259 nsc
->ns_hostname
= mh_xstrdup(hostname
);
263 * Get the snoop flag for this connection
267 netsec_get_snoop(netsec_context
*nsc
)
269 return nsc
->ns_snoop
;
273 * Set the snoop flag for this connection
277 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
279 nsc
->ns_snoop
= snoop
;
283 * Set the snoop callback for this connection.
286 void netsec_set_snoop_callback(netsec_context
*nsc
,
287 netsec_snoop_callback callback
, void *context
)
289 nsc
->ns_snoop_cb
= callback
;
290 nsc
->ns_snoop_context
= context
;
294 * A base64-decoding snoop callback
298 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
301 unsigned char *decoded
;
306 offset
= context
? *((int *) context
) : 0;
310 * Output non-base64 data first.
312 fprintf(stderr
, "%.*s", offset
, string
);
317 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
319 * Some mechanisms produce large binary tokens, which aren't really
320 * readable. So let's do a simple heuristic. If the token is greater
321 * than 100 characters _and_ the first 100 bytes are more than 50%
322 * non-ASCII, then don't print the decoded buffer, just the
325 if (decodedlen
> 100 && !checkascii(decoded
, 100)) {
326 fprintf(stderr
, "%.*s\n", (int) len
, string
);
329 hexify(decoded
, decodedlen
, &hexified
);
330 fprintf(stderr
, "b64<%s>\n", hexified
);
335 fprintf(stderr
, "%.*s\n", (int) len
, string
);
340 * If the ASCII content is > 50%, return 1
344 checkascii(const unsigned char *bytes
, size_t len
)
346 size_t count
= 0, half
= len
/ 2;
349 if (isascii(*bytes
) && isprint(*bytes
) && ++count
> half
)
352 /* No chance by this point */
353 if (count
+ len
< half
)
361 * Set the read timeout for this connection
365 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
367 nsc
->ns_timeout
= timeout
;
371 * Read data from the network. Basically, return anything in our buffer,
372 * otherwise fill from the network.
376 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
381 * If our buffer is empty, then we should fill it now
384 if (nsc
->ns_inbuflen
== 0) {
385 if (netsec_fillread(nsc
, errstr
) != OK
)
390 * netsec_fillread only returns if the buffer is full, so we can
391 * assume here that this has something in it.
394 retlen
= min(size
, nsc
->ns_inbuflen
);
396 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
398 if (retlen
== (int) nsc
->ns_inbuflen
) {
400 * We've emptied our buffer, so reset everything.
402 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
403 nsc
->ns_inbuflen
= 0;
405 nsc
->ns_inptr
+= size
;
406 nsc
->ns_inbuflen
-= size
;
413 * Get a "line" (CR/LF) terminated from the network.
415 * Okay, we play some games here, so pay attention:
417 * - Unlike every other function, we return a pointer to the
418 * existing buffer. This pointer is valid until you call another
419 * read function again.
420 * - We NUL-terminate the buffer right at the end, before the CR-LF terminator.
421 * - Technically we look for a LF; if we find a CR right before it, then
423 * - If your data may contain embedded NULs, this won't work. You should
424 * be using netsec_read() in that case.
428 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
430 unsigned char *ptr
= nsc
->ns_inptr
;
431 size_t count
= 0, offset
;
435 * Search through our existing buffer for a LF
438 while (count
< nsc
->ns_inbuflen
) {
440 if (*ptr
++ == '\n') {
441 char *sptr
= (char *) nsc
->ns_inptr
;
442 if (count
> 1 && *(ptr
- 2) == '\r')
446 *len
= ptr
- nsc
->ns_inptr
;
447 nsc
->ns_inptr
+= count
;
448 nsc
->ns_inbuflen
-= count
;
451 if (nsc
->sasl_seclayer
)
452 fprintf(stderr
, "(sasl-decrypted) ");
453 #endif /* CYRUS_SASL */
456 fprintf(stderr
, "(tls-decrypted) ");
457 #endif /* TLS_SUPPORT */
458 fprintf(stderr
, "<= ");
459 if (nsc
->ns_snoop_cb
)
460 nsc
->ns_snoop_cb(nsc
, sptr
, strlen(sptr
),
461 nsc
->ns_snoop_context
);
463 fprintf(stderr
, "%s\n", sptr
);
470 * Hm, we didn't find a \n. If we've already searched half of the input
471 * buffer, return an error.
474 if (count
>= nsc
->ns_inbufsize
/ 2) {
475 netsec_err(errstr
, "Unable to find a line terminator after %d bytes",
481 * Okay, get some more network data. This may move inptr, so regenerate
485 offset
= ptr
- nsc
->ns_inptr
;
487 if (netsec_fillread(nsc
, errstr
) != OK
)
490 ptr
= nsc
->ns_inptr
+ offset
;
494 return NULL
; /* Should never reach this */
498 * Fill our read buffer with some data from the network.
502 netsec_fillread(netsec_context
*nsc
, char **errstr
)
506 size_t readbufsize
, remaining
, startoffset
;
510 * If inbuflen is zero, that means the buffer has been emptied
511 * completely. In that case move inptr back to the start.
514 if (nsc
->ns_inbuflen
== 0) {
515 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
518 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
520 #endif /* CYRUS_SASL || TLS_SUPPORT */
522 * If we are using TLS and there's anything pending, then skip the
526 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
527 #endif /* TLS_SUPPORT */
533 FD_SET(nsc
->ns_readfd
, &rfds
);
535 tv
.tv_sec
= nsc
->ns_timeout
;
538 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
541 netsec_err(errstr
, "select() while reading failed: %s",
547 netsec_err(errstr
, "read() timed out after %d seconds",
553 * At this point, we know that rc is 1, so there's not even any
554 * point to check to see if our descriptor is set in rfds.
561 * startoffset is the offset from the beginning of the input
562 * buffer to data that is in our input buffer, but has not yet
563 * been consumed. This can be non-zero if functions like
564 * netsec_readline() leave leftover data.
566 * remaining is the remaining amount of unconsumed data in the input
569 * end is a pointer to the end of the valid data + 1; it's where
570 * the next read should go.
573 startoffset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
574 remaining
= nsc
->ns_inbufsize
- (startoffset
+ nsc
->ns_inbuflen
);
575 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
578 * If we're past the halfway point in our read buffers, shuffle everything
579 * back to the beginning.
582 if (startoffset
> nsc
->ns_inbufsize
/ 2) {
583 memmove(nsc
->ns_inbuffer
, nsc
->ns_inptr
, nsc
->ns_inbuflen
);
584 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
586 remaining
= nsc
->ns_inbufsize
- nsc
->ns_inbuflen
;
587 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
591 * If we are using TLS, then just read via the BIO. But we still
592 * use our local buffer.
595 if (nsc
->tls_active
) {
596 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
602 * Check to see if we're supposed to retry; if so,
603 * then go back and read again.
606 if (BIO_should_retry(nsc
->ssl_io
))
610 * Okay, fine. Get the real error out of the SSL context.
613 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
614 netsec_err(errstr
, "SSL_read() returned 0, but cannot "
615 "retrieve SSL context");
619 errcode
= SSL_get_error(ssl
, rc
);
620 if (errcode
== SSL_ERROR_ZERO_RETURN
) {
621 netsec_err(errstr
, "TLS peer closed remote connection");
623 netsec_err(errstr
, "TLS network read failed: %s",
624 ERR_error_string(ERR_peek_last_error(), NULL
));
627 ERR_print_errors_fp(stderr
);
631 /* Definitely an error */
632 netsec_err(errstr
, "Read on TLS connection failed: %s",
633 ERR_error_string(ERR_get_error(), NULL
));
637 nsc
->ns_inbuflen
+= rc
;
641 #endif /* TLS_SUPPORT */
644 * Okay, time to read some data. Either we're just doing it straight
645 * or we're passing it through sasl_decode() first.
649 if (nsc
->sasl_seclayer
) {
650 readbuf
= nsc
->sasl_tmpbuf
;
651 readbufsize
= nsc
->sasl_maxbufsize
;
653 #endif /* CYRUS_SASL */
655 readbuf
= (char *) end
;
656 readbufsize
= remaining
;
660 * At this point, we should have active data on the connection (see
661 * select() above) so this read SHOULDN'T block. Hopefully.
664 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
667 netsec_err(errstr
, "Received EOF on network read");
672 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
677 * Okay, so we've had a successful read. If we are doing SASL security
678 * layers, pass this through sasl_decode(). sasl_decode() can return
679 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
680 * we can just update our length pointer.
684 if (nsc
->sasl_seclayer
) {
686 unsigned int tmpoutlen
;
688 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
689 &tmpout
, &tmpoutlen
);
692 netsec_err(errstr
, "Unable to decode SASL network data: %s",
693 sasl_errdetail(nsc
->sasl_conn
));
704 if (tmpoutlen
> remaining
) {
705 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
709 memcpy(end
, tmpout
, tmpoutlen
);
711 nsc
->ns_inbuflen
+= tmpoutlen
;
713 #endif /* CYRUS_SASL */
714 nsc
->ns_inbuflen
+= rc
;
720 * Write data to our network connection. Really, fill up the buffer as
721 * much as we can, and flush it out if necessary. netsec_flush() does
726 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
729 const unsigned char *bufptr
= buffer
;
738 * Run a loop copying in data to our local buffer; when we're done with
739 * any buffer overflows then just copy any remaining data in.
742 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
743 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
746 * In theory I should increment outptr, but netsec_flush just resets
749 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
751 rc
= netsec_flush(nsc
, errstr
);
761 * Copy any leftover data into the buffer.
765 memcpy(nsc
->ns_outptr
, bufptr
, size
);
766 nsc
->ns_outptr
+= size
;
767 nsc
->ns_outbuflen
+= size
;
774 * Our network printf() routine, which really just calls netsec_vprintf().
778 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
783 va_start(ap
, format
);
784 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
791 * Write bytes to the network using printf()-style formatting.
793 * Again, for the most part copy stuff into our buffer to be flushed
798 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
804 * Cheat a little. If we can fit the data into our outgoing buffer,
805 * great! If not, generate a flush and retry once.
809 rc
= vsnprintf((char *) nsc
->ns_outptr
,
810 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, ap
);
812 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
814 * This means we have an overflow. Note that we don't actually
815 * make use of the terminating NUL, but according to the spec
816 * vsnprintf() won't write to the last byte in the string; that's
817 * why we have to use >= in the comparison above.
819 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
821 * Whoops, if the buffer pointer was the same as the start of the
822 * buffer, that means we overflowed the internal buffer.
823 * At that point, just give up.
825 netsec_err(errstr
, "Internal error: wanted to printf() a total of "
826 "%d bytes, but our buffer size was only %d bytes",
827 rc
, nsc
->ns_outbufsize
);
831 * Generate a flush (which may be inefficient, but hopefully
832 * it isn't) and then try again.
834 if (netsec_flush(nsc
, errstr
) != OK
)
837 * After this, outbuffer should == outptr, so we shouldn't
838 * hit this next time around.
845 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\n') {
847 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\r')
850 nsc
->ns_snoop_noend
= 1;
852 if (outlen
> 0 || nsc
->ns_snoop_noend
== 0) {
854 if (nsc
->sasl_seclayer
)
855 fprintf(stderr
, "(sasl-encrypted) ");
856 #endif /* CYRUS_SASL */
859 fprintf(stderr
, "(tls-encrypted) ");
860 #endif /* TLS_SUPPORT */
861 fprintf(stderr
, "=> ");
862 if (nsc
->ns_snoop_cb
)
863 nsc
->ns_snoop_cb(nsc
, (char *) nsc
->ns_outptr
, outlen
,
864 nsc
->ns_snoop_context
);
866 fprintf(stderr
, "%.*s\n", outlen
, nsc
->ns_outptr
);
868 nsc
->ns_snoop_noend
= 0;
872 nsc
->ns_outptr
+= rc
;
873 nsc
->ns_outbuflen
+= rc
;
879 * Flush out any buffered data in our output buffers. This routine is
880 * actually where the real network writes take place.
884 netsec_flush(netsec_context
*nsc
, char **errstr
)
886 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
887 unsigned int netoutlen
= nsc
->ns_outbuflen
;
898 * If SASL security layers are in effect, run the data through
899 * sasl_encode() first.
902 if (nsc
->sasl_seclayer
) {
903 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
904 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
907 netsec_err(errstr
, "SASL data encoding failed: %s",
908 sasl_errdetail(nsc
->sasl_conn
));
913 #endif /* CYRUS_SASL */
916 * If TLS is active, then use those functions to write out the
920 if (nsc
->tls_active
) {
921 if (BIO_write(nsc
->ssl_io
, netoutbuf
, netoutlen
) <= 0) {
922 netsec_err(errstr
, "Error writing to TLS connection: %s",
923 ERR_error_string(ERR_get_error(), NULL
));
927 #endif /* TLS_SUPPORT */
929 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
932 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
937 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
938 nsc
->ns_outbuflen
= 0;
944 * Set various SASL protocol parameters
948 netsec_set_sasl_params(netsec_context
*nsc
, const char *service
,
949 const char *mechanism
, netsec_sasl_callback callback
,
953 sasl_callback_t
*sasl_cbs
;
956 if (!nsc
->ns_hostname
) {
957 netsec_err(errstr
, "Internal error: ns_hostname is NULL");
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
));
972 * Allocate an array of SASL callbacks for this connection.
973 * Right now we just allocate an array of four callbacks.
976 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
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
;
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
;
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
;
990 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
991 sasl_cbs
[3].proc
= NULL
;
992 sasl_cbs
[3].context
= NULL
;
994 nsc
->sasl_cbs
= sasl_cbs
;
996 retval
= sasl_client_new(service
, nsc
->ns_hostname
, NULL
, NULL
,
997 nsc
->sasl_cbs
, 0, &nsc
->sasl_conn
);
1000 netsec_err(errstr
, "SASL new client allocation failed: %s",
1001 sasl_errstring(retval
, NULL
, NULL
));
1006 * Set up our credentials
1009 nsc
->sasl_creds
= nmh_get_credentials(nsc
->ns_hostname
, nsc
->ns_userid
);
1011 #else /* CYRUS_SASL */
1012 NMH_UNUSED(service
);
1014 #endif /* CYRUS_SASL */
1017 * According to the RFC, mechanisms can only be uppercase letter, numbers,
1018 * and a hyphen or underscore. So make sure we uppercase any letters
1019 * in case the user passed in lowercase.
1024 nsc
->sasl_mech
= mh_xstrdup(mechanism
);
1026 for (p
= nsc
->sasl_mech
; *p
; p
++)
1027 if (isascii((unsigned char) *p
)) /* Leave non-ASCII lower alone. */
1028 *p
= toupper((unsigned char) *p
);
1031 nsc
->sasl_proto_cb
= callback
;
1038 * Our userid callback; return the specified username to the SASL
1039 * library when asked.
1042 int netsec_get_user(void *context
, int id
, const char **result
,
1045 netsec_context
*nsc
= (netsec_context
*) context
;
1047 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
1048 return SASL_BADPARAM
;
1050 *result
= nmh_cred_get_user(nsc
->sasl_creds
);
1053 *len
= strlen(*result
);
1059 * Retrieve a password and return it to SASL
1063 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
1064 sasl_secret_t
**psecret
)
1066 netsec_context
*nsc
= (netsec_context
*) context
;
1067 const char *password
;
1072 if (! psecret
|| id
!= SASL_CB_PASS
)
1073 return SASL_BADPARAM
;
1075 password
= nmh_cred_get_password(nsc
->sasl_creds
);
1077 len
= strlen(password
);
1080 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1081 * us room for a terminating NUL
1084 *psecret
= (sasl_secret_t
*) malloc(sizeof(sasl_secret_t
) + len
);
1089 (*psecret
)->len
= len
;
1090 strcpy((char *) (*psecret
)->data
, password
);
1092 nsc
->sasl_secret
= *psecret
;
1096 #endif /* CYRUS_SASL */
1099 * Negotiate SASL on this connection
1103 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
1106 sasl_security_properties_t secprops
;
1107 const char *chosen_mech
;
1108 const unsigned char *saslbuf
;
1109 unsigned char *outbuf
;
1110 unsigned int saslbuflen
, outbuflen
;
1114 #ifdef OAUTH_SUPPORT
1115 unsigned char *xoauth_client_res
;
1116 size_t xoauth_client_res_len
;
1117 #endif /* OAUTH_SUPPORT */
1118 #if defined CYRUS_SASL || defined OAUTH_SUPPORT
1120 #endif /* CYRUS_SASL || OAUTH_SUPPORT */
1123 * If we've been passed a requested mechanism, check our mechanism
1124 * list from the protocol. If it's not supported, return an error.
1127 if (nsc
->sasl_mech
) {
1128 char **str
, *mlist
= getcpy(mechlist
);
1131 str
= brkstring(mlist
, " ", NULL
);
1133 for (i
= 0; str
[i
] != NULL
; i
++) {
1134 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1139 i
= (str
[i
] == NULL
);
1144 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1150 #ifdef OAUTH_SUPPORT
1151 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
1153 * This should be relatively straightforward, but requires some
1154 * help from the plugin. Basically, if XOAUTH2 is a success,
1155 * the callback has to return success, but no output data. If
1156 * there is output data, it will be assumed that it is the JSON
1160 if (! nsc
->oauth_service
) {
1161 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1165 nsc
->sasl_chosen_mech
= mh_xstrdup(nsc
->sasl_mech
);
1167 if (mh_oauth_do_xoauth(nsc
->ns_userid
, nsc
->oauth_service
,
1168 &xoauth_client_res
, &xoauth_client_res_len
,
1169 nsc
->ns_snoop
? stderr
: NULL
) != OK
) {
1170 netsec_err(errstr
, "Internal error: Unable to get OAuth2 "
1175 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_START
, xoauth_client_res
,
1176 xoauth_client_res_len
, NULL
, 0, errstr
);
1177 free(xoauth_client_res
);
1183 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1184 * success, we indicate that with no output data. But if we
1185 * fail, then send a blank message and get the resulting
1189 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0, errstr
);
1193 * We're going to assume the error here is a JSON response;
1194 * we ignore it and send a blank message in response. We should
1195 * then get a failure messages with a useful error. We should
1196 * NOT get a success message at this point.
1199 nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, NULL
, 0, NULL
, 0, NULL
);
1200 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1203 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1209 #endif /* OAUTH_SUPPORT */
1213 * In netsec_set_sasl_params, we've already done all of our setup with
1214 * sasl_client_init() and sasl_client_new(). So time to set security
1215 * properties, call sasl_client_start(), and generate the protocol
1219 memset(&secprops
, 0, sizeof(secprops
));
1220 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1223 * If we're using TLS, do not negotiate a security layer
1228 nsc
->tls_active
? 0 :
1229 #endif /* TLS_SUPPORT */
1232 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1234 if (rc
!= SASL_OK
) {
1235 netsec_err(errstr
, "SASL security property initialization failed: %s",
1236 sasl_errstring(rc
, NULL
, NULL
));
1241 * Start the actual protocol negotiation, and go through the
1242 * sasl_client_step() loop (after sasl_client_start, of course).
1245 rc
= sasl_client_start(nsc
->sasl_conn
,
1246 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1247 (const char **) &saslbuf
, &saslbuflen
,
1250 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1251 netsec_err(errstr
, "SASL client start failed: %s",
1252 sasl_errdetail(nsc
->sasl_conn
));
1256 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1258 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1263 * We've written out our first message; enter in the step loop
1266 while (rc
== SASL_CONTINUE
) {
1268 * Call our SASL callback, which will handle the details of
1269 * reading data from the network.
1272 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1274 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1278 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1279 (const char **) &saslbuf
, &saslbuflen
);
1283 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1284 netsec_err(errstr
, "SASL client negotiation failed: %s",
1285 sasl_errdetail(nsc
->sasl_conn
));
1286 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1290 if (nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, saslbuf
, saslbuflen
,
1291 NULL
, 0, errstr
) != OK
) {
1292 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1298 * SASL exchanges should be complete, process the final response message
1302 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1305 * At this point we can't really send an abort since the SASL dialog
1306 * has completed, so just bubble back up the error message.
1313 * At this point, SASL should be complete. Get a few properties
1314 * from the authentication exchange.
1317 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1319 if (rc
!= SASL_OK
) {
1320 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1321 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1325 nsc
->sasl_ssf
= *ssf
;
1327 if (nsc
->sasl_ssf
> 0) {
1328 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1329 (const void **) &outbufmax
);
1331 if (rc
!= SASL_OK
) {
1332 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1333 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
1338 * If our output buffer isn't the same size as the input buffer,
1339 * reallocate it and set the new size (since we won't encode any
1340 * data larger than that).
1343 nsc
->sasl_maxbufsize
= *outbufmax
;
1345 if (nsc
->ns_outbufsize
!= nsc
->sasl_maxbufsize
) {
1346 nsc
->ns_outbufsize
= nsc
->sasl_maxbufsize
;
1347 nsc
->ns_outbuffer
= mh_xrealloc(nsc
->ns_outbuffer
,
1348 nsc
->ns_outbufsize
);
1350 * There shouldn't be any data in the buffer, but for
1351 * consistency's sake discard it.
1353 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1354 nsc
->ns_outbuflen
= 0;
1358 * Allocate a buffer to do temporary reads into, before we
1359 * call sasl_decode()
1362 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
1365 * Okay, this is a bit weird. Make sure that the input buffer
1366 * is at least TWICE the size of the max buffer size. That's
1367 * because if we're consuming data but want to extend the current
1368 * buffer, we want to be sure there's room for another full buffer's
1372 if (nsc
->ns_inbufsize
< nsc
->sasl_maxbufsize
* 2) {
1373 size_t offset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
1374 nsc
->ns_inbufsize
= nsc
->sasl_maxbufsize
* 2;
1375 nsc
->ns_inbuffer
= mh_xrealloc(nsc
->ns_inbuffer
, nsc
->ns_inbufsize
);
1376 nsc
->ns_inptr
= nsc
->ns_inbuffer
+ offset
;
1379 nsc
->sasl_seclayer
= 1;
1385 * If we're at this point, then either we have NEITHER OAuth2 or
1386 * Cyrus-SASL compiled in, or have OAuth2 but didn't give the XOAUTH2
1387 * mechanism on the command line.
1390 if (! nsc
->sasl_mech
)
1391 netsec_err(errstr
, "SASL library support not available; please "
1392 "specify a SASL mechanism to use");
1394 netsec_err(errstr
, "No support for the %s SASL mechanism",
1398 #endif /* CYRUS_SASL */
1402 * Retrieve our chosen SASL mechanism
1406 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1408 return nsc
->sasl_chosen_mech
;
1412 * Set an OAuth2 service name, if we support it.
1416 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1418 #ifdef OAUTH_SUPPORT
1419 nsc
->oauth_service
= getcpy(service
);
1421 #else /* OAUTH_SUPPORT */
1423 NMH_UNUSED(service
);
1425 #endif /* OAUTH_SUPPORT */
1429 * Initialize (and enable) TLS for this connection
1433 netsec_set_tls(netsec_context
*nsc
, int tls
, int noverify
, char **errstr
)
1438 BIO
*rbio
, *wbio
, *ssl_bio
;
1440 if (! tls_initialized
) {
1442 SSL_load_error_strings();
1445 * Create the SSL context; this has the properties for all
1446 * SSL connections (we are only doing one), though. Make sure
1447 * we only support secure (known as of now) TLS protocols.
1450 sslctx
= SSL_CTX_new(SSLv23_client_method());
1453 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1454 ERR_error_string(ERR_get_error(), NULL
));
1458 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1461 if (!SSL_CTX_set_default_verify_paths(sslctx
)) {
1462 netsec_err(errstr
, "Unable to set default certificate "
1463 "verification paths: %s",
1464 ERR_error_string(ERR_get_error(), NULL
));
1471 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1472 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1477 * Create the SSL structure which holds the data for a single
1481 ssl
= SSL_new(sslctx
);
1484 netsec_err(errstr
, "Unable to create SSL connection: %s",
1485 ERR_error_string(ERR_get_error(), NULL
));
1490 * Never bother us, since we are using blocking sockets.
1493 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1496 * This is a bit weird, so pay attention.
1498 * We create a socket BIO, and bind it to our SSL connection.
1499 * That means reads and writes to the SSL connection will use our
1502 * Then we create an SSL BIO, and assign our current SSL connection
1503 * to it. This is done so our code stays simple if we want to use
1504 * any buffering BIOs (right now we do our own buffering).
1505 * So the chain looks like:
1507 * SSL BIO -> socket BIO.
1510 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_NOCLOSE
);
1513 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1514 ERR_error_string(ERR_get_error(), NULL
));
1519 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_NOCLOSE
);
1522 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1523 ERR_error_string(ERR_get_error(), NULL
));
1529 SSL_set_bio(ssl
, rbio
, wbio
);
1530 SSL_set_connect_state(ssl
);
1533 * If noverify is NOT set, then do certificate validation.
1534 * Turning on SSL_VERIFY_PEER will verify the certificate chain
1535 * against locally stored root certificates (the locations are
1536 * set using SSL_CTX_set_default_verify_paths()), and we put
1537 * the hostname in the X509 verification parameters so the OpenSSL
1538 * code will verify that the hostname appears in the server
1543 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1544 X509_VERIFY_PARAM
*param
;
1545 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1547 SSL_set_verify(ssl
, SSL_VERIFY_PEER
, NULL
);
1548 if (! nsc
->ns_hostname
) {
1549 netsec_err(errstr
, "Internal error: hostname not set and "
1550 "certification verification enabled");
1555 #ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
1556 param
= SSL_get0_param(ssl
);
1558 if (! X509_VERIFY_PARAM_set1_host(param
, nsc
->ns_hostname
, 0)) {
1559 netsec_err(errstr
, "Unable to add hostname %s to cert "
1560 "verification parameters: %s", nsc
->ns_hostname
,
1561 ERR_error_string(ERR_get_error(), NULL
));
1565 #endif /* HAVE_X509_VERIFY_PARAM_SET1_HOST */
1568 ssl_bio
= BIO_new(BIO_f_ssl());
1571 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1572 ERR_error_string(ERR_get_error(), NULL
));
1577 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1578 nsc
->ssl_io
= ssl_bio
;
1582 BIO_free_all(nsc
->ssl_io
);
1585 #else /* TLS_SUPPORT */
1587 NMH_UNUSED(noverify
);
1590 netsec_err(errstr
, "TLS is not supported");
1593 #endif /* TLS_SUPPORT */
1599 * Start TLS negotiation on this connection
1603 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1606 if (! nsc
->ssl_io
) {
1607 netsec_err(errstr
, "TLS has not been configured for this connection");
1611 if (BIO_do_handshake(nsc
->ssl_io
) < 1) {
1612 unsigned long errcode
= ERR_get_error();
1615 * Print a more detailed message if it was certificate verification
1619 if (ERR_GET_LIB(errcode
) == ERR_LIB_SSL
&&
1620 ERR_GET_REASON(errcode
) == SSL_R_CERTIFICATE_VERIFY_FAILED
) {
1623 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1624 netsec_err(errstr
, "Certificate verification failed, but "
1625 "cannot retrieve SSL handle: %s",
1626 ERR_error_string(ERR_get_error(), NULL
));
1628 netsec_err(errstr
, "Server certificate verification failed: %s",
1629 X509_verify_cert_error_string(
1630 SSL_get_verify_result(ssl
)));
1633 netsec_err(errstr
, "TLS negotiation failed: %s",
1634 ERR_error_string(ERR_get_error(), NULL
));
1638 * Because negotiation failed, shut down TLS so we don't get any
1639 * garbage on the connection. Because of weirdness with SSL_shutdown,
1640 * we end up calling it twice: once explicitly, once as part of
1644 BIO_ssl_shutdown(nsc
->ssl_io
);
1645 BIO_free_all(nsc
->ssl_io
);
1651 if (nsc
->ns_snoop
) {
1654 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1655 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
1657 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
1658 fprintf(stderr
, "TLS negotiation successful: %s(%d) %s\n",
1659 SSL_CIPHER_get_name(cipher
),
1660 SSL_CIPHER_get_bits(cipher
, NULL
),
1661 SSL_CIPHER_get_version(cipher
));
1662 SSL_SESSION_print_fp(stderr
, SSL_get_session(ssl
));
1666 nsc
->tls_active
= 1;
1669 #else /* TLS_SUPPORT */
1671 netsec_err(errstr
, "TLS not supported");
1674 #endif /* TLS_SUPPORT */
1678 * Generate an (allocated) error string
1682 netsec_err(char **errstr
, const char *fmt
, ...)
1686 char *errbuf
= NULL
;
1693 errbufsize
= rc
+ 1;
1694 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1696 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1698 } while (rc
>= (int) errbufsize
);