3 * netsec.c -- Network security routines for handling protocols that
4 * require SASL and/or TLS.
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.
16 #include <sys/select.h>
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 */
29 static int netsec_get_user(void *context
, int id
, const char **result
,
31 static int netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
32 sasl_secret_t
**psecret
);
34 static int sasl_initialized
= 0;
36 #define SASL_MAXRECVBUF 65536
37 #endif /* CYRUS_SASL */
40 #include <openssl/ssl.h>
41 #include <openssl/err.h>
43 static int tls_initialized
= 0;
44 static SSL_CTX
*sslctx
= NULL
; /* SSL Context */
46 #endif /* TLS_SUPPORT */
48 /* I'm going to hardcode this for now; maybe make it adjustable later? */
49 #define NETSEC_BUFSIZE 65536
52 * Our context structure, which holds all of the relevant information
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 */
77 char *oauth_service
; /* OAuth2 service name */
78 #endif /* OAUTH_SUPPORT */
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 */
91 BIO
*ssl_io
; /* BIO used for connection I/O */
92 int tls_active
; /* If true, TLS is running */
93 #endif /* TLS_SUPPORT */
97 * Function to read data from the actual network socket
100 static int netsec_fillread(netsec_context
*ns_context
, char **errstr
);
103 * Code to check the ASCII content of a byte array.
106 static int checkascii(const unsigned char *byte
, size_t len
);
109 * How this code works, in general.
111 * _If_ we are using no encryption then we buffer the network data
112 * through ns_inbuffer and ns_outbuffer. That should be relatively
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.
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).
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).
134 * Allocate and initialize our security context
140 netsec_context
*nsc
= mh_xmalloc(sizeof(*nsc
));
143 nsc
->ns_writefd
= -1;
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
;
162 nsc
->oauth_service
= NULL
;
163 #endif /* OAUTH_SUPPORT */
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
;
171 nsc
->sasl_seclayer
= 0;
172 nsc
->sasl_tmpbuf
= NULL
;
173 nsc
->sasl_maxbufsize
= 0;
174 #endif /* CYRUS_SASL */
178 #endif /* TLS_SUPPORT */
183 * Shutdown the connection completely and free all resources.
184 * The connection is only closed if the flag is given.
188 netsec_shutdown(netsec_context
*nsc
, int closeflag
)
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
);
197 free(nsc
->sasl_mech
);
198 if (nsc
->sasl_chosen_mech
)
199 free(nsc
->sasl_chosen_mech
);
201 if (nsc
->oauth_service
)
202 free(nsc
->oauth_service
);
203 #endif /* OAUTH_SERVICE */
206 sasl_dispose(&nsc
->sasl_conn
);
207 if (nsc
->sasl_hostname
)
208 free(nsc
->sasl_hostname
);
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
);
217 if (nsc
->sasl_secret
) {
218 if (nsc
->sasl_secret
->len
> 0) {
219 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
221 free(nsc
->sasl_secret
);
223 if (nsc
->sasl_tmpbuf
)
224 free(nsc
->sasl_tmpbuf
);
225 #endif /* CYRUS_SASL */
229 * I checked; BIO_free_all() will cause SSL_shutdown to be called
230 * on the SSL object in the chain.
232 BIO_free_all(nsc
->ssl_io
);
233 #endif /* TLS_SUPPORT */
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
);
246 * Set the file descriptor for our context
250 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
252 nsc
->ns_readfd
= readfd
;
253 nsc
->ns_writefd
= writefd
;
257 * Set the userid used for authentication for this context
261 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
263 nsc
->ns_userid
= getcpy(userid
);
267 * Get the snoop flag for this connection
271 netsec_get_snoop(netsec_context
*nsc
)
273 return nsc
->ns_snoop
;
277 * Set the snoop flag for this connection
281 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
283 nsc
->ns_snoop
= snoop
;
287 * Set the snoop callback for this connection.
290 void netsec_set_snoop_callback(netsec_context
*nsc
,
291 netsec_snoop_callback callback
, void *context
)
293 nsc
->ns_snoop_cb
= callback
;
294 nsc
->ns_snoop_context
= context
;
298 * A base64-decoding snoop callback
302 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
305 unsigned char *decoded
;
310 offset
= context
? *((int *) context
) : 0;
314 * Output non-base64 data first.
316 fprintf(stderr
, "%.*s", offset
, string
);
321 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
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
329 if (decodedlen
> 100 && !checkascii(decoded
, 100)) {
330 fprintf(stderr
, "%.*s\n", (int) len
, string
);
333 hexify(decoded
, decodedlen
, &hexified
);
334 fprintf(stderr
, "b64<%s>\n", hexified
);
339 fprintf(stderr
, "%.*s\n", (int) len
, string
);
344 * If the ASCII content is > 50%, return 1
348 checkascii(const unsigned char *bytes
, size_t len
)
350 size_t count
= 0, half
= len
/ 2;
353 if (isascii(*bytes
) && isprint(*bytes
) && ++count
> half
)
356 /* No chance by this point */
357 if (count
+ len
< half
)
365 * Set the read timeout for this connection
369 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
371 nsc
->ns_timeout
= timeout
;
375 * Read data from the network. Basically, return anything in our buffer,
376 * otherwise fill from the network.
380 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
385 * If our buffer is empty, then we should fill it now
388 if (nsc
->ns_inbuflen
== 0) {
389 if (netsec_fillread(nsc
, errstr
) != OK
)
394 * netsec_fillread only returns if the buffer is full, so we can
395 * assume here that this has something in it.
398 retlen
= size
> nsc
->ns_inbuflen
? nsc
->ns_inbuflen
: size
;
400 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
402 if (retlen
== (int) nsc
->ns_inbuflen
) {
404 * We've emptied our buffer, so reset everything.
406 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
407 nsc
->ns_inbuflen
= 0;
409 nsc
->ns_inptr
+= size
;
410 nsc
->ns_inbuflen
-= size
;
417 * Get a "line" (CR/LF) terminated from the network.
419 * Okay, we play some games here, so pay attention:
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
427 * - If your data may contain embedded NULs, this won't work. You should
428 * be using netsec_read() in that case.
432 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
434 unsigned char *ptr
= nsc
->ns_inptr
;
435 size_t count
= 0, offset
;
439 * Search through our existing buffer for a LF
442 while (count
< nsc
->ns_inbuflen
) {
444 if (*ptr
++ == '\n') {
445 char *sptr
= (char *) nsc
->ns_inptr
;
446 if (count
> 1 && *(ptr
- 2) == '\r')
450 *len
= ptr
- nsc
->ns_inptr
;
451 nsc
->ns_inptr
+= count
;
452 nsc
->ns_inbuflen
-= count
;
455 if (nsc
->sasl_seclayer
)
456 fprintf(stderr
, "(sasl-decrypted) ");
457 #endif /* CYRUS_SASL */
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
);
467 fprintf(stderr
, "%s\n", sptr
);
474 * Hm, we didn't find a \n. If we've already searched half of the input
475 * buffer, return an error.
478 if (count
>= nsc
->ns_inbufsize
/ 2) {
479 netsec_err(errstr
, "Unable to find a line terminator after %d bytes",
485 * Okay, get some more network data. This may move inptr, so regenerate
489 offset
= ptr
- nsc
->ns_inptr
;
491 if (netsec_fillread(nsc
, errstr
) != OK
)
494 ptr
= nsc
->ns_inptr
+ offset
;
498 return NULL
; /* Should never reach this */
502 * Fill our read buffer with some data from the network.
506 netsec_fillread(netsec_context
*nsc
, char **errstr
)
510 size_t readbufsize
, remaining
, startoffset
;
514 * If inbuflen is zero, that means the buffer has been emptied
515 * completely. In that case move inptr back to the start.
518 if (nsc
->ns_inbuflen
== 0) {
519 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
522 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
524 #endif /* CYRUS_SASL || TLS_SUPPORT */
526 * If we are using TLS and there's anything pending, then skip the
530 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
531 #endif /* TLS_SUPPORT */
537 FD_SET(nsc
->ns_readfd
, &rfds
);
539 tv
.tv_sec
= nsc
->ns_timeout
;
542 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
545 netsec_err(errstr
, "select() while reading failed: %s",
551 netsec_err(errstr
, "read() timed out after %d seconds",
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.
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.
570 * remaining is the remaining amount of unconsumed data in the input
573 * end is a pointer to the end of the valid data + 1; it's where
574 * the next read should go.
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
;
582 * If we're past the halfway point in our read buffers, shuffle everything
583 * back to the beginning.
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
;
590 remaining
= nsc
->ns_inbufsize
- nsc
->ns_inbuflen
;
591 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
595 * If we are using TLS, then just read via the BIO. But we still
596 * use our local buffer.
599 if (nsc
->tls_active
) {
600 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
606 * Check to see if we're supposed to retry; if so,
607 * then go back and read again.
610 if (BIO_should_retry(nsc
->ssl_io
))
614 * Okay, fine. Get the real error out of the SSL context.
617 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
618 netsec_err(errstr
, "SSL_read() returned 0, but cannot "
619 "retrieve SSL context");
623 errcode
= SSL_get_error(ssl
, rc
);
624 if (errcode
== SSL_ERROR_ZERO_RETURN
) {
625 netsec_err(errstr
, "TLS peer closed remote connection");
627 netsec_err(errstr
, "TLS network read failed: %s",
628 ERR_error_string(ERR_peek_last_error(), NULL
));
631 ERR_print_errors_fp(stderr
);
634 /* Definitely an error */
635 netsec_err(errstr
, "Read on TLS connection failed: %s",
636 ERR_error_string(ERR_get_error(), NULL
));
640 nsc
->ns_inbuflen
+= rc
;
644 #endif /* TLS_SUPPORT */
647 * Okay, time to read some data. Either we're just doing it straight
648 * or we're passing it through sasl_decode() first.
652 if (nsc
->sasl_seclayer
) {
653 readbuf
= nsc
->sasl_tmpbuf
;
654 readbufsize
= nsc
->sasl_maxbufsize
;
656 #endif /* CYRUS_SASL */
658 readbuf
= (char *) end
;
659 readbufsize
= remaining
;
663 * At this point, we should have active data on the connection (see
664 * select() above) so this read SHOULDN'T block. Hopefully.
667 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
670 netsec_err(errstr
, "Received EOF on network read");
675 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
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.
687 if (nsc
->sasl_seclayer
) {
689 unsigned int tmpoutlen
;
691 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
692 &tmpout
, &tmpoutlen
);
695 netsec_err(errstr
, "Unable to decode SASL network data: %s",
696 sasl_errdetail(nsc
->sasl_conn
));
707 if (tmpoutlen
> remaining
) {
708 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
712 memcpy(end
, tmpout
, tmpoutlen
);
714 nsc
->ns_inbuflen
+= tmpoutlen
;
716 #endif /* CYRUS_SASL */
717 nsc
->ns_inbuflen
+= rc
;
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
729 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
732 const unsigned char *bufptr
= buffer
;
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.
745 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
746 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
749 * In theory I should increment outptr, but netsec_flush just resets
752 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
754 rc
= netsec_flush(nsc
, errstr
);
764 * Copy any leftover data into the buffer.
768 memcpy(nsc
->ns_outptr
, bufptr
, size
);
769 nsc
->ns_outptr
+= size
;
770 nsc
->ns_outbuflen
+= size
;
777 * Our network printf() routine, which really just calls netsec_vprintf().
781 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
786 va_start(ap
, format
);
787 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
794 * Write bytes to the network using printf()-style formatting.
796 * Again, for the most part copy stuff into our buffer to be flushed
801 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
807 * Cheat a little. If we can fit the data into our outgoing buffer,
808 * great! If not, generate a flush and retry once.
812 rc
= vsnprintf((char *) nsc
->ns_outptr
,
813 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, ap
);
815 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
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.
822 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
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.
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
);
834 * Generate a flush (which may be inefficient, but hopefully
835 * it isn't) and then try again.
837 if (netsec_flush(nsc
, errstr
) != OK
)
840 * After this, outbuffer should == outptr, so we shouldn't
841 * hit this next time around.
849 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\n') {
851 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\r')
854 nsc
->ns_snoop_noend
= 1;
856 if (outlen
> 0 || nsc
->ns_snoop_noend
== 0) {
858 if (nsc
->sasl_seclayer
)
859 fprintf(stderr
, "(sasl-encrypted) ");
860 #endif /* CYRUS_SASL */
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
);
870 fprintf(stderr
, "%.*s\n", outlen
, nsc
->ns_outptr
);
872 nsc
->ns_snoop_noend
= 0;
876 nsc
->ns_outptr
+= rc
;
877 nsc
->ns_outbuflen
+= rc
;
883 * Flush out any buffered data in our output buffers. This routine is
884 * actually where the real network writes take place.
888 netsec_flush(netsec_context
*nsc
, char **errstr
)
890 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
891 unsigned int netoutlen
= nsc
->ns_outbuflen
;
902 * If SASL security layers are in effect, run the data through
903 * sasl_encode() first.
906 if (nsc
->sasl_seclayer
) {
907 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
908 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
911 netsec_err(errstr
, "SASL data encoding failed: %s",
912 sasl_errdetail(nsc
->sasl_conn
));
917 #endif /* CYRUS_SASL */
920 * If TLS is active, then use those functions to write out the
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
));
931 #endif /* TLS_SUPPORT */
933 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
936 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
941 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
942 nsc
->ns_outbuflen
= 0;
948 * Set various SASL protocol parameters
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
)
957 sasl_callback_t
*sasl_cbs
;
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
));
971 * Allocate an array of SASL callbacks for this connection.
972 * Right now we just allocate an array of four callbacks.
975 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
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
;
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
;
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
;
989 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
990 sasl_cbs
[3].proc
= NULL
;
991 sasl_cbs
[3].context
= NULL
;
993 nsc
->sasl_cbs
= sasl_cbs
;
995 retval
= sasl_client_new(service
, hostname
, NULL
, NULL
, nsc
->sasl_cbs
, 0,
999 netsec_err(errstr
, "SASL new client allocation failed: %s",
1000 sasl_errstring(retval
, NULL
, NULL
));
1004 nsc
->sasl_hostname
= getcpy(hostname
);
1005 #else /* CYRUS_SASL */
1006 NMH_UNUSED(hostname
);
1007 NMH_UNUSED(service
);
1009 #endif /* CYRUS_SASL */
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.
1019 nsc
->sasl_mech
= getcpy(mechanism
);
1021 for (p
= nsc
->sasl_mech
; *p
; p
++)
1022 if (isascii((unsigned char) *p
)) /* Just in case */
1023 *p
= toupper((unsigned char) *p
);
1026 nsc
->sasl_proto_cb
= callback
;
1033 * Our userid callback; return the specified username to the SASL
1034 * library when asked.
1037 int netsec_get_user(void *context
, int id
, const char **result
,
1040 netsec_context
*nsc
= (netsec_context
*) context
;
1042 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
1043 return SASL_BADPARAM
;
1045 if (nsc
->ns_userid
== NULL
) {
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.
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
;
1060 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 1,
1061 nsc
->sasl_creds
) != OK
)
1062 return SASL_BADPARAM
;
1064 if (nsc
->ns_userid
!= nsc
->sasl_creds
->user
) {
1066 free(nsc
->ns_userid
);
1067 nsc
->ns_userid
= getcpy(nsc
->sasl_creds
->user
);
1071 *result
= nsc
->ns_userid
;
1073 *len
= strlen(nsc
->ns_userid
);
1079 * Retrieve a password and return it to SASL
1083 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
1084 sasl_secret_t
**psecret
)
1086 netsec_context
*nsc
= (netsec_context
*) context
;
1091 if (! psecret
|| id
!= SASL_CB_PASS
)
1092 return SASL_BADPARAM
;
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
;
1100 if (nsc
->sasl_creds
->password
== NULL
) {
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.
1108 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 0,
1109 nsc
->sasl_creds
) != OK
) {
1110 return SASL_BADPARAM
;
1114 len
= strlen(nsc
->sasl_creds
->password
);
1117 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1118 * us room for a terminating NUL
1121 *psecret
= (sasl_secret_t
*) malloc(sizeof(sasl_secret_t
) + len
);
1126 (*psecret
)->len
= len
;
1127 strcpy((char *) (*psecret
)->data
, nsc
->sasl_creds
->password
);
1129 nsc
->sasl_secret
= *psecret
;
1133 #endif /* CYRUS_SASL */
1136 * Negotiate SASL on this connection
1140 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
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
;
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
1157 #endif /* CYRUS_SASL || OAUTH_SUPPORT */
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.
1164 if (nsc
->sasl_mech
) {
1165 char **str
, *mlist
= getcpy(mechlist
);
1168 str
= brkstring(mlist
, " ", NULL
);
1170 for (i
= 0; str
[i
] != NULL
; i
++) {
1171 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1176 i
= (str
[i
] == NULL
);
1181 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1187 #ifdef OAUTH_SUPPORT
1188 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
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
1197 if (! nsc
->oauth_service
) {
1198 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1202 nsc
->sasl_chosen_mech
= getcpy(nsc
->sasl_mech
);
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 "
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
);
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
1226 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0, errstr
);
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.
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,
1240 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1246 #endif /* OAUTH_SUPPORT */
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
1256 memset(&secprops
, 0, sizeof(secprops
));
1257 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1260 * If we're using TLS, do not negotiate a security layer
1265 nsc
->tls_active
? 0 :
1266 #endif /* TLS_SUPPORT */
1269 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1271 if (rc
!= SASL_OK
) {
1272 netsec_err(errstr
, "SASL security property initialization failed: %s",
1273 sasl_errstring(rc
, NULL
, NULL
));
1278 * Start the actual protocol negotiation, and go through the
1279 * sasl_client_step() loop (after sasl_client_start, of course).
1282 rc
= sasl_client_start(nsc
->sasl_conn
,
1283 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1284 (const char **) &saslbuf
, &saslbuflen
,
1287 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1288 netsec_err(errstr
, "SASL client start failed: %s",
1289 sasl_errdetail(nsc
->sasl_conn
));
1293 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1295 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1300 * We've written out our first message; enter in the step loop
1303 while (rc
== SASL_CONTINUE
) {
1305 * Call our SASL callback, which will handle the details of
1306 * reading data from the network.
1309 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1311 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1315 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1316 (const char **) &saslbuf
, &saslbuflen
);
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
);
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
);
1336 * SASL exchanges should be complete, process the final response message
1340 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
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.
1351 * At this point, SASL should be complete. Get a few properties
1352 * from the authentication exchange.
1355 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1357 if (rc
!= SASL_OK
) {
1358 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1359 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1363 nsc
->sasl_ssf
= *ssf
;
1365 if (nsc
->sasl_ssf
> 0) {
1366 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1367 (const void **) &outbufmax
);
1369 if (rc
!= SASL_OK
) {
1370 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1371 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
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).
1381 nsc
->sasl_maxbufsize
= *outbufmax
;
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
);
1388 * There shouldn't be any data in the buffer, but for
1389 * consistency's sake discard it.
1391 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1392 nsc
->ns_outbuflen
= 0;
1396 * Allocate a buffer to do temporary reads into, before we
1397 * call sasl_decode()
1400 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
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
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
;
1417 nsc
->sasl_seclayer
= 1;
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.
1428 if (! nsc
->sasl_mech
)
1429 netsec_err(errstr
, "SASL library support not available; please "
1430 "specify a SASL mechanism to use");
1432 netsec_err(errstr
, "No support for the %s SASL mechanism",
1436 #endif /* CYRUS_SASL */
1440 * Retrieve our chosen SASL mechanism
1444 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1446 return nsc
->sasl_chosen_mech
;
1450 * Set an OAuth2 service name, if we support it.
1454 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1456 #ifdef OAUTH_SUPPORT
1457 nsc
->oauth_service
= getcpy(service
);
1459 #else /* OAUTH_SUPPORT */
1461 NMH_UNUSED(service
);
1463 #endif /* OAUTH_SUPPORT */
1467 * Initialize (and enable) TLS for this connection
1471 netsec_set_tls(netsec_context
*nsc
, int tls
, char **errstr
)
1476 BIO
*rbio
, *wbio
, *ssl_bio
;;
1478 if (! tls_initialized
) {
1480 SSL_load_error_strings();
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.
1488 sslctx
= SSL_CTX_new(SSLv23_client_method());
1491 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1492 ERR_error_string(ERR_get_error(), NULL
));
1496 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1502 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1503 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1508 * Create the SSL structure which holds the data for a single
1512 ssl
= SSL_new(sslctx
);
1515 netsec_err(errstr
, "Unable to create SSL connection: %s",
1516 ERR_error_string(ERR_get_error(), NULL
));
1521 * Never bother us, since we are using blocking sockets.
1524 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1527 * This is a bit weird, so pay attention.
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
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:
1538 * SSL BIO -> socket BIO.
1541 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_NOCLOSE
);
1544 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1545 ERR_error_string(ERR_get_error(), NULL
));
1550 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_NOCLOSE
);
1553 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1554 ERR_error_string(ERR_get_error(), NULL
));
1560 SSL_set_bio(ssl
, rbio
, wbio
);
1561 SSL_set_connect_state(ssl
);
1563 ssl_bio
= BIO_new(BIO_f_ssl());
1566 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1567 ERR_error_string(ERR_get_error(), NULL
));
1572 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1573 nsc
->ssl_io
= ssl_bio
;
1577 BIO_free_all(nsc
->ssl_io
);
1582 #else /* TLS_SUPPORT */
1583 netsec_err(errstr
, "TLS is not supported");
1587 #endif /* TLS_SUPPORT */
1591 * Start TLS negotiation on this connection
1595 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1598 if (! nsc
->ssl_io
) {
1599 netsec_err(errstr
, "TLS has not been configured for this connection");
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
));
1609 if (nsc
->ns_snoop
) {
1612 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1613 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
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
));
1623 nsc
->tls_active
= 1;
1626 #else /* TLS_SUPPORT */
1627 netsec_err(errstr
, "TLS not supported");
1630 #endif /* TLS_SUPPORT */
1634 * Generate an (allocated) error string
1638 netsec_err(char **errstr
, const char *fmt
, ...)
1642 char *errbuf
= NULL
;
1649 errbufsize
= rc
+ 1;
1650 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1652 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1654 } while (rc
>= (int) errbufsize
);