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 */
75 char *oauth_service
; /* OAuth2 service name */
76 #endif /* OAUTH_SUPPORT */
78 char *sasl_hostname
; /* Hostname we've connected to */
79 sasl_conn_t
*sasl_conn
; /* SASL connection context */
80 sasl_ssf_t sasl_ssf
; /* SASL Security Strength Factor */
81 netsec_sasl_callback sasl_proto_cb
; /* SASL callback we use */
82 sasl_callback_t
*sasl_cbs
; /* Callbacks used by SASL */
83 nmh_creds_t sasl_creds
; /* Credentials (username/password) */
84 sasl_secret_t
*sasl_secret
; /* SASL password structure */
85 char *sasl_chosen_mech
; /* Mechanism chosen by SASL */
86 int sasl_seclayer
; /* If true, SASL security layer is enabled */
87 char *sasl_tmpbuf
; /* Temporary read buffer for decodes */
88 size_t sasl_maxbufsize
; /* Maximum negotiated SASL buffer size */
89 #endif /* CYRUS_SASL */
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 * How this code works, in general.
105 * _If_ we are using no encryption or SASL encryption, then we buffer the
106 * network data through ns_inbuffer and ns_outbuffer. That should be
107 * relatively self-explanatory.
109 * If we are using SSL for encryption, then use a buffering BIO for output
110 * (that just easier). Still do buffering for reads; when we need more
111 * data we call the BIO_read() function to fill our local buffer.
113 * For SASL, we make use of (for now) the Cyrus-SASL library. For some
114 * mechanisms, we implement those mechanisms directly since the Cyrus SASL
115 * library doesn't support them (like OAuth).
119 * Allocate and initialize our security context
125 netsec_context
*nsc
= mh_xmalloc(sizeof(*nsc
));
128 nsc
->ns_writefd
= -1;
130 nsc
->ns_snoop_noend
= 0;
131 nsc
->ns_snoop_cb
= NULL
;
132 nsc
->ns_snoop_context
= NULL
;
133 nsc
->ns_userid
= NULL
;
134 nsc
->ns_timeout
= 60; /* Our default */
135 nsc
->ns_inbufsize
= NETSEC_BUFSIZE
;
136 nsc
->ns_inbuffer
= mh_xmalloc(nsc
->ns_inbufsize
);
137 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
138 nsc
->ns_inbuflen
= 0;
139 nsc
->ns_outbufsize
= NETSEC_BUFSIZE
;
140 nsc
->ns_outbuffer
= mh_xmalloc(nsc
->ns_outbufsize
);
141 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
142 nsc
->ns_outbuflen
= 0;
143 nsc
->sasl_mech
= NULL
;
145 nsc
->oauth_service
= NULL
;
146 #endif /* OAUTH_SUPPORT */
148 nsc
->sasl_conn
= NULL
;
149 nsc
->sasl_hostname
= NULL
;
150 nsc
->sasl_cbs
= NULL
;
151 nsc
->sasl_creds
= NULL
;
152 nsc
->sasl_secret
= NULL
;
153 nsc
->sasl_chosen_mech
= NULL
;
155 nsc
->sasl_seclayer
= 0;
156 nsc
->sasl_tmpbuf
= NULL
;
157 nsc
->sasl_maxbufsize
= 0;
158 #endif /* CYRUS_SASL */
162 #endif /* TLS_SUPPORT */
167 * Shutdown the connection completely and free all resources.
168 * The connection is only closed if the flag is given.
172 netsec_shutdown(netsec_context
*nsc
, int closeflag
)
175 free(nsc
->ns_userid
);
176 if (nsc
->ns_inbuffer
)
177 free(nsc
->ns_inbuffer
);
178 if (nsc
->ns_outbuffer
)
179 free(nsc
->ns_outbuffer
);
181 free(nsc
->sasl_mech
);
183 if (nsc
->oauth_service
)
184 free(nsc
->oauth_service
);
185 #endif /* OAUTH_SERVICE */
188 sasl_dispose(&nsc
->sasl_conn
);
189 if (nsc
->sasl_hostname
)
190 free(nsc
->sasl_hostname
);
193 if (nsc
->sasl_creds
) {
194 if (nsc
->sasl_creds
->password
)
195 memset(nsc
->sasl_creds
->password
, 0,
196 strlen(nsc
->sasl_creds
->password
));
197 free(nsc
->sasl_creds
);
199 if (nsc
->sasl_secret
) {
200 if (nsc
->sasl_secret
->len
> 0) {
201 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
203 free(nsc
->sasl_secret
);
205 if (nsc
->sasl_chosen_mech
)
206 free(nsc
->sasl_chosen_mech
);
207 if (nsc
->sasl_tmpbuf
)
208 free(nsc
->sasl_tmpbuf
);
209 #endif /* CYRUS_SASL */
213 * I checked; BIO_free_all() will cause SSL_shutdown to be called
214 * on the SSL object in the chain.
216 BIO_free_all(nsc
->ssl_io
);
217 #endif /* TLS_SUPPORT */
220 if (nsc
->ns_readfd
!= -1)
221 close(nsc
->ns_readfd
);
222 if (nsc
->ns_writefd
!= -1 && nsc
->ns_writefd
!= nsc
->ns_readfd
)
223 close(nsc
->ns_writefd
);
230 * Set the file descriptor for our context
234 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
236 nsc
->ns_readfd
= readfd
;
237 nsc
->ns_writefd
= writefd
;
241 * Set the userid used for authentication for this context
245 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
247 nsc
->ns_userid
= getcpy(userid
);
251 * Get the snoop flag for this connection
255 netsec_get_snoop(netsec_context
*nsc
)
257 return nsc
->ns_snoop
;
261 * Set the snoop flag for this connection
265 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
267 nsc
->ns_snoop
= snoop
;
271 * Set the snoop callback for this connection.
274 void netsec_set_snoop_callback(netsec_context
*nsc
,
275 netsec_snoop_callback callback
, void *context
)
277 nsc
->ns_snoop_cb
= callback
;
278 nsc
->ns_snoop_context
= context
;
282 * A base64-decoding snoop callback
286 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
294 offset
= context
? *((int *) context
) : 0;
298 * Output non-base64 data first.
300 fprintf(stderr
, "%.*s", offset
, string
);
305 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
307 hexify((const unsigned char *) decoded
, decodedlen
, &hexified
);
308 fprintf(stderr
, "b64<%s>\n", hexified
);
310 free((char *) decoded
);
312 fprintf(stderr
, "%.*s\n", (int) len
, string
);
317 * Set the read timeout for this connection
321 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
323 nsc
->ns_timeout
= timeout
;
327 * Read data from the network. Basically, return anything in our buffer,
328 * otherwise fill from the network.
332 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
337 * If our buffer is empty, then we should fill it now
340 if (nsc
->ns_inbuflen
== 0) {
341 if (netsec_fillread(nsc
, errstr
) != OK
)
346 * netsec_fillread only returns if the buffer is full, so we can
347 * assume here that this has something in it.
350 retlen
= size
> nsc
->ns_inbuflen
? nsc
->ns_inbuflen
: size
;
352 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
354 if (retlen
== (int) nsc
->ns_inbuflen
) {
356 * We've emptied our buffer, so reset everything.
358 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
359 nsc
->ns_inbuflen
= 0;
361 nsc
->ns_inptr
+= size
;
362 nsc
->ns_inbuflen
-= size
;
369 * Get a "line" (CR/LF) terminated from the network.
371 * Okay, we play some games here, so pay attention:
373 * - Unlike every other function, we return a pointer to the
374 * existing buffer. This pointer is valid until you call another
375 * read functiona again.
376 * - We NUL-terminated the buffer right at the end, before the terminator.
377 * - Technically we look for a LF; if we find a CR right before it, then
379 * - If your data may contain embedded NULs, this won't work.
383 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
385 unsigned char *ptr
= nsc
->ns_inptr
;
386 size_t count
= 0, offset
;
390 * Search through our existing buffer for a LF
393 while (count
< nsc
->ns_inbuflen
) {
395 if (*ptr
++ == '\n') {
396 char *sptr
= (char *) nsc
->ns_inptr
;
397 if (count
> 1 && *(ptr
- 2) == '\r')
401 *len
= ptr
- nsc
->ns_inptr
;
402 nsc
->ns_inptr
+= count
;
403 nsc
->ns_inbuflen
-= count
;
406 if (nsc
->sasl_seclayer
)
407 fprintf(stderr
, "(sasl-decrypted) ");
408 #endif /* CYRUS_SASL */
411 fprintf(stderr
, "(tls-decrypted) ");
412 #endif /* TLS_SUPPORT */
413 fprintf(stderr
, "<= ");
414 if (nsc
->ns_snoop_cb
)
415 nsc
->ns_snoop_cb(nsc
, sptr
, strlen(sptr
),
416 nsc
->ns_snoop_context
);
418 fprintf(stderr
, "%s\n", sptr
);
425 * Hm, we didn't find a \n. If we've already searched half of the input
426 * buffer, return an error.
429 if (count
>= nsc
->ns_inbufsize
/ 2) {
430 netsec_err(errstr
, "Unable to find a line terminator after %d bytes",
436 * Okay, get some more network data. This may move inptr, so regenerate
440 offset
= ptr
- nsc
->ns_inptr
;
442 if (netsec_fillread(nsc
, errstr
) != OK
)
445 ptr
= nsc
->ns_inptr
+ offset
;
449 return NULL
; /* Should never reach this */
453 * Fill our read buffer with some data from the network.
457 netsec_fillread(netsec_context
*nsc
, char **errstr
)
461 size_t readbufsize
, remaining
, startoffset
;
465 * If inbuflen is zero, that means the buffer has been emptied
466 * completely. In that case move inptr back to the start.
469 if (nsc
->ns_inbuflen
== 0) {
470 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
475 * If we are using TLS and there's anything pending, then skip the
479 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
480 #endif /* TLS_SUPPORT */
486 FD_SET(nsc
->ns_readfd
, &rfds
);
488 tv
.tv_sec
= nsc
->ns_timeout
;
491 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
494 netsec_err(errstr
, "select() while reading failed: %s",
500 netsec_err(errstr
, "read() timed out after %d seconds",
506 * At this point, we know that rc is 1, so there's not even any
507 * point to check to see if our descriptor is set in rfds.
511 startoffset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
512 remaining
= nsc
->ns_inbufsize
- (startoffset
+ nsc
->ns_inbuflen
);
513 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
516 * If we are using TLS, then just read via the BIO. But we still
517 * use our local buffer.
520 if (nsc
->tls_active
) {
521 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
524 * Either EOF, or possibly an error. Either way, it was probably
525 * unexpected, so treat as error.
527 netsec_err(errstr
, "TLS peer aborted connection");
530 /* Definitely an error */
531 netsec_err(errstr
, "Read on TLS connection failed: %s",
532 ERR_error_string(ERR_get_error(), NULL
));
536 nsc
->ns_inbuflen
+= rc
;
540 #endif /* TLS_SUPPORT */
543 * Okay, time to read some data. Either we're just doing it straight
544 * or we're passing it through sasl_decode() first.
548 if (nsc
->sasl_seclayer
) {
549 readbuf
= nsc
->sasl_tmpbuf
;
550 readbufsize
= nsc
->sasl_maxbufsize
;
552 #endif /* CYRUS_SASL */
554 readbuf
= (char *) end
;
555 readbufsize
= remaining
;
559 * At this point, we should have active data on the connection (see
560 * select() above) so this read SHOULDN'T block. Hopefully.
563 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
566 netsec_err(errstr
, "Received EOF on network read");
571 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
576 * Okay, so we've had a successful read. If we are doing SASL security
577 * layers, pass this through sasl_decode(). sasl_decode() can return
578 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
579 * we can just update our length pointer.
583 if (nsc
->sasl_seclayer
) {
585 unsigned int tmpoutlen
;
587 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
588 &tmpout
, &tmpoutlen
);
591 netsec_err(errstr
, "Unable to decode SASL network data: %s",
592 sasl_errdetail(nsc
->sasl_conn
));
603 if (tmpoutlen
> remaining
) {
604 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
608 memcpy(end
, tmpout
, tmpoutlen
);
610 nsc
->ns_inbuflen
+= tmpoutlen
;
612 #endif /* CYRUS_SASL */
613 nsc
->ns_inbuflen
+= rc
;
616 * If we're past the halfway point in our read buffers, shuffle everything
617 * back to the beginning.
620 if (startoffset
> nsc
->ns_inbufsize
/ 2) {
621 memmove(nsc
->ns_inbuffer
, nsc
->ns_inptr
, nsc
->ns_inbuflen
);
622 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
629 * Write data to our network connection. Really, fill up the buffer as
630 * much as we can, and flush it out if necessary. netsec_flush() does
635 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
638 const unsigned char *bufptr
= buffer
;
647 * Run a loop copying in data to our local buffer; when we're done with
648 * any buffer overflows then just copy any remaining data in.
651 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
652 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
655 * In theory I should increment outptr, but netsec_flush just resets
658 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
660 rc
= netsec_flush(nsc
, errstr
);
670 * Copy any leftover data into the buffer.
674 memcpy(nsc
->ns_outptr
, bufptr
, size
);
675 nsc
->ns_outptr
+= size
;
676 nsc
->ns_outbuflen
+= size
;
683 * Our network printf() routine, which really just calls netsec_vprintf().
687 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
692 va_start(ap
, format
);
693 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
700 * Write bytes to the network using printf()-style formatting.
702 * Again, for the most part copy stuff into our buffer to be flushed
707 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
713 * Cheat a little. If we can fit the data into our outgoing buffer,
714 * great! If not, generate a flush and retry once.
718 rc
= vsnprintf((char *) nsc
->ns_outptr
,
719 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, ap
);
721 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
723 * This means we have an overflow. Note that we don't actually
724 * make use of the terminating NUL, but according to the spec
725 * vsnprintf() won't write to the last byte in the string; that's
726 * why we have to use >= in the comparison above.
728 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
730 * Whoops, if the buffer pointer was the same as the start of the
731 * buffer, that means we overflowed the internal buffer.
732 * At that point, just give up.
734 netsec_err(errstr
, "Internal error: wanted to printf() a total of "
735 "%d bytes, but our buffer size was only %d bytes",
736 rc
, nsc
->ns_outbufsize
);
740 * Generate a flush (which may be inefficient, but hopefully
741 * it isn't) and then try again.
743 if (netsec_flush(nsc
, errstr
) != OK
)
746 * After this, outbuffer should == outptr, so we shouldn't
747 * hit this next time around.
755 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\n') {
757 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\r')
760 nsc
->ns_snoop_noend
= 1;
762 if (outlen
> 0 || nsc
->ns_snoop_noend
== 0) {
764 if (nsc
->sasl_seclayer
)
765 fprintf(stderr
, "(sasl-encrypted) ");
766 #endif /* CYRUS_SASL */
769 fprintf(stderr
, "(tls-encrypted) ");
770 #endif /* TLS_SUPPORT */
771 fprintf(stderr
, "=> ");
772 if (nsc
->ns_snoop_cb
)
773 nsc
->ns_snoop_cb(nsc
, (char *) nsc
->ns_outptr
, outlen
,
774 nsc
->ns_snoop_context
);
776 fprintf(stderr
, "%.*s\n", outlen
, nsc
->ns_outptr
);
778 nsc
->ns_snoop_noend
= 0;
782 nsc
->ns_outptr
+= rc
;
783 nsc
->ns_outbuflen
+= rc
;
789 * Flush out any buffered data in our output buffers. This routine is
790 * actually where the real network writes take place.
794 netsec_flush(netsec_context
*nsc
, char **errstr
)
796 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
797 unsigned int netoutlen
= nsc
->ns_outbuflen
;
808 * If SASL security layers are in effect, run the data through
809 * sasl_encode() first.
812 if (nsc
->sasl_seclayer
) {
813 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
814 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
817 netsec_err(errstr
, "SASL data encoding failed: %s",
818 sasl_errdetail(nsc
->sasl_conn
));
823 #endif /* CYRUS_SASL */
826 * If TLS is active, then use those functions to write out the
830 if (nsc
->tls_active
) {
831 if (BIO_write(nsc
->ssl_io
, netoutbuf
, netoutlen
) <= 0) {
832 netsec_err(errstr
, "Error writing to TLS connection: %s",
833 ERR_error_string(ERR_get_error(), NULL
));
837 #endif /* TLS_SUPPORT */
839 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
842 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
847 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
848 nsc
->ns_outbuflen
= 0;
854 * Set various SASL protocol parameters
858 netsec_set_sasl_params(netsec_context
*nsc
, const char *hostname
,
859 const char *service
, const char *mechanism
,
860 netsec_sasl_callback callback
, char **errstr
)
863 sasl_callback_t
*sasl_cbs
;
866 if (! sasl_initialized
) {
867 retval
= sasl_client_init(NULL
);
868 if (retval
!= SASL_OK
) {
869 netsec_err(errstr
, "SASL client initialization failed: %s",
870 sasl_errstring(retval
, NULL
, NULL
));
877 * Allocate an array of SASL callbacks for this connection.
878 * Right now we just allocate an array of four callbacks.
881 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
883 sasl_cbs
[0].id
= SASL_CB_USER
;
884 sasl_cbs
[0].proc
= (sasl_callback_ft
) netsec_get_user
;
885 sasl_cbs
[0].context
= nsc
;
887 sasl_cbs
[1].id
= SASL_CB_AUTHNAME
;
888 sasl_cbs
[1].proc
= (sasl_callback_ft
) netsec_get_user
;
889 sasl_cbs
[1].context
= nsc
;
891 sasl_cbs
[2].id
= SASL_CB_PASS
;
892 sasl_cbs
[2].proc
= (sasl_callback_ft
) netsec_get_password
;
893 sasl_cbs
[2].context
= nsc
;
895 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
896 sasl_cbs
[3].proc
= NULL
;
897 sasl_cbs
[3].context
= NULL
;
899 nsc
->sasl_cbs
= sasl_cbs
;
901 retval
= sasl_client_new(service
, hostname
, NULL
, NULL
, nsc
->sasl_cbs
, 0,
905 netsec_err(errstr
, "SASL new client allocation failed: %s",
906 sasl_errstring(retval
, NULL
, NULL
));
911 * According to the RFC, mechanisms can only be uppercase letter, numbers,
912 * and a hypen or underscore. So make sure we uppercase any letters
913 * in case the user passed in lowercase.
918 nsc
->sasl_mech
= getcpy(mechanism
);
920 for (p
= nsc
->sasl_mech
; *p
; p
++)
921 if (isascii((unsigned char) *p
)) /* Just in case */
922 *p
= toupper((unsigned char) *p
);
925 nsc
->sasl_proto_cb
= callback
;
926 nsc
->sasl_hostname
= getcpy(hostname
);
929 #else /* CYRUS_SASL */
930 netsec_err(errstr
, "SASL is not supported");
933 #endif /* CYRUS_SASL */
938 * Our userid callback; return the specified username to the SASL
939 * library when asked.
942 int netsec_get_user(void *context
, int id
, const char **result
,
945 netsec_context
*nsc
= (netsec_context
*) context
;
947 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
948 return SASL_BADPARAM
;
950 if (nsc
->ns_userid
== NULL
) {
952 * Pass the 1 third argument to nmh_get_credentials() so that
953 * a defauly user if the -user switch wasn't supplied, and so
954 * that a default password will be supplied. That's used when
955 * those values really don't matter, and only with legacy/.netrc,
956 * i.e., with a credentials profile entry.
959 if (nsc
->sasl_creds
== NULL
) {
960 nsc
->sasl_creds
= mh_xmalloc(sizeof(*nsc
->sasl_creds
));
961 nsc
->sasl_creds
->user
= NULL
;
962 nsc
->sasl_creds
->password
= NULL
;
965 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 1,
966 nsc
->sasl_creds
) != OK
)
967 return SASL_BADPARAM
;
969 if (nsc
->ns_userid
!= nsc
->sasl_creds
->user
) {
971 free(nsc
->ns_userid
);
972 nsc
->ns_userid
= getcpy(nsc
->sasl_creds
->user
);
976 *result
= nsc
->ns_userid
;
978 *len
= strlen(nsc
->ns_userid
);
984 * Retrieve a password and return it to SASL
988 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
989 sasl_secret_t
**psecret
)
991 netsec_context
*nsc
= (netsec_context
*) context
;
996 if (! psecret
|| id
!= SASL_CB_PASS
)
997 return SASL_BADPARAM
;
999 if (nsc
->sasl_creds
== NULL
) {
1000 nsc
->sasl_creds
= mh_xmalloc(sizeof(*nsc
->sasl_creds
));
1001 nsc
->sasl_creds
->user
= NULL
;
1002 nsc
->sasl_creds
->password
= NULL
;
1005 if (nsc
->sasl_creds
->password
== NULL
) {
1007 * Pass the 0 third argument to nmh_get_credentials() so
1008 * that the default password isn't used. With legacy/.netrc
1009 * credentials support, we'll only get here if the -user
1010 * switch to send(1)/post(8) wasn't used.
1013 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 0,
1014 nsc
->sasl_creds
) != OK
) {
1015 return SASL_BADPARAM
;
1019 len
= strlen(nsc
->sasl_creds
->password
);
1022 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1023 * us room for a terminating NUL
1026 *psecret
= (sasl_secret_t
*) malloc(sizeof(sasl_secret_t
) + len
);
1031 (*psecret
)->len
= len
;
1032 strcpy((char *) (*psecret
)->data
, nsc
->sasl_creds
->password
);
1034 nsc
->sasl_secret
= *psecret
;
1038 #endif /* CYRUS_SASL */
1041 * Negotiate SASL on this connection
1045 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
1048 sasl_security_properties_t secprops
;
1049 const char *chosen_mech
;
1050 const unsigned char *saslbuf
;
1051 unsigned char *outbuf
;
1052 unsigned int saslbuflen
, outbuflen
;
1056 #ifdef OAUTH_SUPPORT
1057 unsigned char *xoauth_client_res
;
1058 size_t xoauth_client_res_len
;
1059 #endif /* OAUTH_SUPPORT */
1063 * If we've been passed a requested mechanism, check our mechanism
1064 * list from the protocol. If it's not supported, return an error.
1067 if (nsc
->sasl_mech
) {
1068 char **str
, *mlist
= getcpy(mechlist
);
1071 str
= brkstring(mlist
, " ", NULL
);
1073 for (i
= 0; str
[i
] != NULL
; i
++) {
1074 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1079 i
= (str
[i
] == NULL
);
1084 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1090 #ifdef OAUTH_SUPPORT
1091 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
1093 * This should be relatively straightforward, but requires some
1094 * help from the plugin. Basically, if XOAUTH2 is a success,
1095 * the callback has to return success, but no output data. If
1096 * there is output data, it will be assumed that it is the JSON
1100 if (! nsc
->oauth_service
) {
1101 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1105 nsc
->sasl_chosen_mech
= getcpy(nsc
->sasl_mech
);
1107 if (mh_oauth_do_xoauth(nsc
->ns_userid
, nsc
->oauth_service
,
1108 &xoauth_client_res
, &xoauth_client_res_len
,
1109 nsc
->ns_snoop
? stderr
: NULL
) != OK
) {
1110 netsec_err(errstr
, "Internal error: Unable to get OAuth2 "
1115 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_START
, xoauth_client_res
,
1116 xoauth_client_res_len
, NULL
, 0, errstr
);
1117 free(xoauth_client_res
);
1123 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1124 * success, we indicate that with no output data. But if we
1125 * fail, then send a blank message and get the resulting
1129 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0, errstr
);
1133 * We're going to assume the error here is a JSON response;
1134 * we ignore it and send a blank message in response. We should
1135 * then get either an +OK or -ERR
1138 nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, NULL
, 0, NULL
, 0, NULL
);
1139 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1142 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1148 #endif /* OAUTH_SUPPORT */
1152 * In netsec_set_sasl_params, we've already done all of our setup with
1153 * sasl_client_init() and sasl_client_new(). So time to set security
1154 * properties, call sasl_client_start(), and generate the protocol
1158 memset(&secprops
, 0, sizeof(secprops
));
1159 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1162 * If we're using TLS, do not negotiate a security layer
1167 nsc
->tls_active
? 0 :
1168 #endif /* TLS_SUPPORT */
1171 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1173 if (rc
!= SASL_OK
) {
1174 netsec_err(errstr
, "SASL security property initialization failed: %s",
1175 sasl_errstring(rc
, NULL
, NULL
));
1180 * Start the actual protocol negotiation, and go through the
1181 * sasl_client_step() loop (after sasl_client_start, of course).
1184 rc
= sasl_client_start(nsc
->sasl_conn
,
1185 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1186 (const char **) &saslbuf
, &saslbuflen
,
1189 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1190 netsec_err(errstr
, "SASL client start failed: %s",
1191 sasl_errdetail(nsc
->sasl_conn
));
1195 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1197 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1202 * We've written out our first message; enter in the step loop
1205 while (rc
== SASL_CONTINUE
) {
1207 * Call our SASL callback, which will handle the details of
1208 * reading data from the network.
1211 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1213 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1217 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1218 (const char **) &saslbuf
, &saslbuflen
);
1223 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1224 netsec_err(errstr
, "SASL client negotiation failed: %s",
1225 sasl_errdetail(nsc
->sasl_conn
));
1226 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1230 if (nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, saslbuf
, saslbuflen
,
1231 NULL
, 0, errstr
) != OK
) {
1232 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1238 * SASL exchanges should be complete, process the final response message
1242 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1245 * At this point we can't really send an abort since the SASL dialog
1246 * has completed, so just bubble back up the error message.
1253 * At this point, SASL should be complete. Get a few properties
1254 * from the authentication exchange.
1257 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1259 if (rc
!= SASL_OK
) {
1260 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1261 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1265 nsc
->sasl_ssf
= *ssf
;
1267 if (nsc
->sasl_ssf
> 0) {
1268 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1269 (const void **) &outbufmax
);
1271 if (rc
!= SASL_OK
) {
1272 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1273 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
1278 * If our output buffer isn't the same size as the input buffer,
1279 * reallocate it and set the new size (since we won't encode any
1280 * data larger than that).
1283 nsc
->sasl_maxbufsize
= *outbufmax
;
1285 if (nsc
->ns_outbufsize
!= nsc
->sasl_maxbufsize
) {
1286 nsc
->ns_outbufsize
= nsc
->sasl_maxbufsize
;
1287 nsc
->ns_outbuffer
= mh_xrealloc(nsc
->ns_outbuffer
,
1288 nsc
->ns_outbufsize
);
1290 * There shouldn't be any data in the buffer, but for
1291 * consistency's sake discard it.
1293 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1294 nsc
->ns_outbuflen
= 0;
1298 * Allocate a buffer to do temporary reads into, before we
1299 * call sasl_decode()
1302 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
1305 * Okay, this is a bit weird. Make sure that the input buffer
1306 * is at least TWICE the size of the max buffer size. That's
1307 * because if we're consuming data but want to extend the current
1308 * buffer, we want to be sure there's room for another full buffer's
1312 if (nsc
->ns_inbufsize
< nsc
->sasl_maxbufsize
* 2) {
1313 size_t offset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
1314 nsc
->ns_inbufsize
= nsc
->sasl_maxbufsize
* 2;
1315 nsc
->ns_inbuffer
= mh_xrealloc(nsc
->ns_inbuffer
, nsc
->ns_inbufsize
);
1316 nsc
->ns_inptr
= nsc
->ns_inbuffer
+ offset
;
1319 nsc
->sasl_seclayer
= 1;
1324 netsec_err(errstr
, "SASL not supported");
1327 #endif /* CYRUS_SASL */
1331 * Retrieve our chosen SASL mechanism
1335 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1338 return nsc
->sasl_chosen_mech
;
1339 #else /* CYRUS_SASL */
1341 #endif /* CYRUS_SASL */
1345 * Set an OAuth2 service name, if we support it.
1349 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1351 #ifdef OAUTH_SUPPORT
1352 nsc
->oauth_service
= getcpy(service
);
1354 #else /* OAUTH_SUPPORT */
1356 #endif /* OAUTH_SUPPORT */
1360 * Initialize (and enable) TLS for this connection
1364 netsec_set_tls(netsec_context
*nsc
, int tls
, char **errstr
)
1369 BIO
*rbio
, *wbio
, *ssl_bio
;;
1371 if (! tls_initialized
) {
1373 SSL_load_error_strings();
1376 * Create the SSL context; this has the properties for all
1377 * SSL connections (we are only doing one), though. Make sure
1378 * we only support secure (known as of now) TLS protocols.
1381 sslctx
= SSL_CTX_new(SSLv23_client_method());
1384 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1385 ERR_error_string(ERR_get_error(), NULL
));
1389 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1395 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1396 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1401 * Create the SSL structure which holds the data for a single
1405 ssl
= SSL_new(sslctx
);
1408 netsec_err(errstr
, "Unable to create SSL connection: %s",
1409 ERR_error_string(ERR_get_error(), NULL
));
1414 * Never bother us, since we are using blocking sockets.
1417 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1420 * This is a bit weird, so pay attention.
1422 * We create a socket BIO, and bind it to our SSL connection.
1423 * That means reads and writes to the SSL connection will use our
1426 * Then we create an SSL BIO, and assign our current SSL connection
1427 * to it. We then create a buffer BIO and push it in front of our
1428 * SSL BIO. So the chain looks like:
1430 * buffer BIO -> SSL BIO -> socket BIO.
1432 * So writes and reads are buffered (we mostly care about writes).
1435 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_NOCLOSE
);
1438 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1439 ERR_error_string(ERR_get_error(), NULL
));
1444 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_NOCLOSE
);
1447 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1448 ERR_error_string(ERR_get_error(), NULL
));
1454 SSL_set_bio(ssl
, rbio
, wbio
);
1455 SSL_set_connect_state(ssl
);
1457 ssl_bio
= BIO_new(BIO_f_ssl());
1460 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1461 ERR_error_string(ERR_get_error(), NULL
));
1466 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1467 nsc
->ssl_io
= ssl_bio
;
1471 BIO_free_all(nsc
->ssl_io
);
1476 #else /* TLS_SUPPORT */
1477 netsec_err(errstr
, "TLS is not supported");
1481 #endif /* TLS_SUPPORT */
1485 * Start TLS negotiation on this connection
1489 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1492 if (! nsc
->ssl_io
) {
1493 netsec_err(errstr
, "TLS has not been configured for this connection");
1497 if (BIO_do_handshake(nsc
->ssl_io
) < 1) {
1498 netsec_err(errstr
, "TLS negotiation failed: %s",
1499 ERR_error_string(ERR_get_error(), NULL
));
1503 if (nsc
->ns_snoop
) {
1506 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1507 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
1509 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
1510 fprintf(stderr
, "TLS negotiation successful: %s(%d) %s\n",
1511 SSL_CIPHER_get_name(cipher
),
1512 SSL_CIPHER_get_bits(cipher
, NULL
),
1513 SSL_CIPHER_get_version(cipher
));
1517 nsc
->tls_active
= 1;
1520 #else /* TLS_SUPPORT */
1521 netsec_err(errstr
, "TLS not supported");
1524 #endif /* TLS_SUPPORT */
1528 * Generate an (allocated) error string
1532 netsec_err(char **errstr
, const char *fmt
, ...)
1536 char *errbuf
= NULL
;
1543 errbufsize
= rc
+ 1;
1544 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1546 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1548 } while (rc
>= (int) errbufsize
);