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 or SASL encryption, then we buffer the
112 * network data through ns_inbuffer and ns_outbuffer. That should be
113 * relatively self-explanatory.
115 * If we are using SSL for encryption, then use a buffering BIO for output
116 * (that just easier). Still do buffering for reads; when we need more
117 * data we call the BIO_read() function to fill our local buffer.
119 * For SASL, we make use of (for now) the Cyrus-SASL library. For some
120 * mechanisms, we implement those mechanisms directly since the Cyrus SASL
121 * library doesn't support them (like OAuth).
125 * Allocate and initialize our security context
131 netsec_context
*nsc
= mh_xmalloc(sizeof(*nsc
));
134 nsc
->ns_writefd
= -1;
136 nsc
->ns_snoop_noend
= 0;
137 nsc
->ns_snoop_cb
= NULL
;
138 nsc
->ns_snoop_context
= NULL
;
139 nsc
->ns_userid
= NULL
;
140 nsc
->ns_timeout
= 60; /* Our default */
141 nsc
->ns_inbufsize
= NETSEC_BUFSIZE
;
142 nsc
->ns_inbuffer
= mh_xmalloc(nsc
->ns_inbufsize
);
143 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
144 nsc
->ns_inbuflen
= 0;
145 nsc
->ns_outbufsize
= NETSEC_BUFSIZE
;
146 nsc
->ns_outbuffer
= mh_xmalloc(nsc
->ns_outbufsize
);
147 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
148 nsc
->ns_outbuflen
= 0;
149 nsc
->sasl_mech
= NULL
;
150 nsc
->sasl_chosen_mech
= NULL
;
151 nsc
->sasl_proto_cb
= NULL
;
153 nsc
->oauth_service
= NULL
;
154 #endif /* OAUTH_SUPPORT */
156 nsc
->sasl_conn
= NULL
;
157 nsc
->sasl_hostname
= NULL
;
158 nsc
->sasl_cbs
= NULL
;
159 nsc
->sasl_creds
= NULL
;
160 nsc
->sasl_secret
= NULL
;
162 nsc
->sasl_seclayer
= 0;
163 nsc
->sasl_tmpbuf
= NULL
;
164 nsc
->sasl_maxbufsize
= 0;
165 #endif /* CYRUS_SASL */
169 #endif /* TLS_SUPPORT */
174 * Shutdown the connection completely and free all resources.
175 * The connection is only closed if the flag is given.
179 netsec_shutdown(netsec_context
*nsc
, int closeflag
)
182 free(nsc
->ns_userid
);
183 if (nsc
->ns_inbuffer
)
184 free(nsc
->ns_inbuffer
);
185 if (nsc
->ns_outbuffer
)
186 free(nsc
->ns_outbuffer
);
188 free(nsc
->sasl_mech
);
189 if (nsc
->sasl_chosen_mech
)
190 free(nsc
->sasl_chosen_mech
);
192 if (nsc
->oauth_service
)
193 free(nsc
->oauth_service
);
194 #endif /* OAUTH_SERVICE */
197 sasl_dispose(&nsc
->sasl_conn
);
198 if (nsc
->sasl_hostname
)
199 free(nsc
->sasl_hostname
);
202 if (nsc
->sasl_creds
) {
203 if (nsc
->sasl_creds
->password
)
204 memset(nsc
->sasl_creds
->password
, 0,
205 strlen(nsc
->sasl_creds
->password
));
206 free(nsc
->sasl_creds
);
208 if (nsc
->sasl_secret
) {
209 if (nsc
->sasl_secret
->len
> 0) {
210 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
212 free(nsc
->sasl_secret
);
214 if (nsc
->sasl_tmpbuf
)
215 free(nsc
->sasl_tmpbuf
);
216 #endif /* CYRUS_SASL */
220 * I checked; BIO_free_all() will cause SSL_shutdown to be called
221 * on the SSL object in the chain.
223 BIO_free_all(nsc
->ssl_io
);
224 #endif /* TLS_SUPPORT */
227 if (nsc
->ns_readfd
!= -1)
228 close(nsc
->ns_readfd
);
229 if (nsc
->ns_writefd
!= -1 && nsc
->ns_writefd
!= nsc
->ns_readfd
)
230 close(nsc
->ns_writefd
);
237 * Set the file descriptor for our context
241 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
243 nsc
->ns_readfd
= readfd
;
244 nsc
->ns_writefd
= writefd
;
248 * Set the userid used for authentication for this context
252 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
254 nsc
->ns_userid
= getcpy(userid
);
258 * Get the snoop flag for this connection
262 netsec_get_snoop(netsec_context
*nsc
)
264 return nsc
->ns_snoop
;
268 * Set the snoop flag for this connection
272 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
274 nsc
->ns_snoop
= snoop
;
278 * Set the snoop callback for this connection.
281 void netsec_set_snoop_callback(netsec_context
*nsc
,
282 netsec_snoop_callback callback
, void *context
)
284 nsc
->ns_snoop_cb
= callback
;
285 nsc
->ns_snoop_context
= context
;
289 * A base64-decoding snoop callback
293 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
296 unsigned char *decoded
;
301 offset
= context
? *((int *) context
) : 0;
305 * Output non-base64 data first.
307 fprintf(stderr
, "%.*s", offset
, string
);
312 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
314 * Some mechanisms preoduce large binary tokens, which aren't really
315 * readable. So let's do a simple heuristic. If the token is greater
316 * than 100 characters _and_ the first 100 bytes are more than 50%
317 * non-ASCII, then don't print the decoded buffer, just the
320 if (decodedlen
> 100 && !checkascii(decoded
, 100)) {
321 fprintf(stderr
, "%.*s\n", (int) len
, string
);
324 hexify(decoded
, decodedlen
, &hexified
);
325 fprintf(stderr
, "b64<%s>\n", hexified
);
330 fprintf(stderr
, "%.*s\n", (int) len
, string
);
335 * If the ASCII content is > 50%, return 1
339 checkascii(const unsigned char *bytes
, size_t len
)
341 size_t count
= 0, half
= len
/ 2;
344 if (isascii(*bytes
) && isprint(*bytes
) && ++count
> half
)
347 /* No chance by this point */
348 if (count
+ len
< half
)
356 * Set the read timeout for this connection
360 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
362 nsc
->ns_timeout
= timeout
;
366 * Read data from the network. Basically, return anything in our buffer,
367 * otherwise fill from the network.
371 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
376 * If our buffer is empty, then we should fill it now
379 if (nsc
->ns_inbuflen
== 0) {
380 if (netsec_fillread(nsc
, errstr
) != OK
)
385 * netsec_fillread only returns if the buffer is full, so we can
386 * assume here that this has something in it.
389 retlen
= size
> nsc
->ns_inbuflen
? nsc
->ns_inbuflen
: size
;
391 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
393 if (retlen
== (int) nsc
->ns_inbuflen
) {
395 * We've emptied our buffer, so reset everything.
397 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
398 nsc
->ns_inbuflen
= 0;
400 nsc
->ns_inptr
+= size
;
401 nsc
->ns_inbuflen
-= size
;
408 * Get a "line" (CR/LF) terminated from the network.
410 * Okay, we play some games here, so pay attention:
412 * - Unlike every other function, we return a pointer to the
413 * existing buffer. This pointer is valid until you call another
414 * read functiona again.
415 * - We NUL-terminated the buffer right at the end, before the terminator.
416 * - Technically we look for a LF; if we find a CR right before it, then
418 * - If your data may contain embedded NULs, this won't work.
422 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
424 unsigned char *ptr
= nsc
->ns_inptr
;
425 size_t count
= 0, offset
;
429 * Search through our existing buffer for a LF
432 while (count
< nsc
->ns_inbuflen
) {
434 if (*ptr
++ == '\n') {
435 char *sptr
= (char *) nsc
->ns_inptr
;
436 if (count
> 1 && *(ptr
- 2) == '\r')
440 *len
= ptr
- nsc
->ns_inptr
;
441 nsc
->ns_inptr
+= count
;
442 nsc
->ns_inbuflen
-= count
;
445 if (nsc
->sasl_seclayer
)
446 fprintf(stderr
, "(sasl-decrypted) ");
447 #endif /* CYRUS_SASL */
450 fprintf(stderr
, "(tls-decrypted) ");
451 #endif /* TLS_SUPPORT */
452 fprintf(stderr
, "<= ");
453 if (nsc
->ns_snoop_cb
)
454 nsc
->ns_snoop_cb(nsc
, sptr
, strlen(sptr
),
455 nsc
->ns_snoop_context
);
457 fprintf(stderr
, "%s\n", sptr
);
464 * Hm, we didn't find a \n. If we've already searched half of the input
465 * buffer, return an error.
468 if (count
>= nsc
->ns_inbufsize
/ 2) {
469 netsec_err(errstr
, "Unable to find a line terminator after %d bytes",
475 * Okay, get some more network data. This may move inptr, so regenerate
479 offset
= ptr
- nsc
->ns_inptr
;
481 if (netsec_fillread(nsc
, errstr
) != OK
)
484 ptr
= nsc
->ns_inptr
+ offset
;
488 return NULL
; /* Should never reach this */
492 * Fill our read buffer with some data from the network.
496 netsec_fillread(netsec_context
*nsc
, char **errstr
)
500 size_t readbufsize
, remaining
, startoffset
;
504 * If inbuflen is zero, that means the buffer has been emptied
505 * completely. In that case move inptr back to the start.
508 if (nsc
->ns_inbuflen
== 0) {
509 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
514 #endif /* CYRUS_SASL */
516 * If we are using TLS and there's anything pending, then skip the
520 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
521 #endif /* TLS_SUPPORT */
527 FD_SET(nsc
->ns_readfd
, &rfds
);
529 tv
.tv_sec
= nsc
->ns_timeout
;
532 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
535 netsec_err(errstr
, "select() while reading failed: %s",
541 netsec_err(errstr
, "read() timed out after %d seconds",
547 * At this point, we know that rc is 1, so there's not even any
548 * point to check to see if our descriptor is set in rfds.
552 startoffset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
553 remaining
= nsc
->ns_inbufsize
- (startoffset
+ nsc
->ns_inbuflen
);
554 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
557 * If we are using TLS, then just read via the BIO. But we still
558 * use our local buffer.
561 if (nsc
->tls_active
) {
562 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
565 * Either EOF, or possibly an error. Either way, it was probably
566 * unexpected, so treat as error.
568 netsec_err(errstr
, "TLS peer aborted connection");
571 /* Definitely an error */
572 netsec_err(errstr
, "Read on TLS connection failed: %s",
573 ERR_error_string(ERR_get_error(), NULL
));
577 nsc
->ns_inbuflen
+= rc
;
581 #endif /* TLS_SUPPORT */
584 * Okay, time to read some data. Either we're just doing it straight
585 * or we're passing it through sasl_decode() first.
589 if (nsc
->sasl_seclayer
) {
590 readbuf
= nsc
->sasl_tmpbuf
;
591 readbufsize
= nsc
->sasl_maxbufsize
;
593 #endif /* CYRUS_SASL */
595 readbuf
= (char *) end
;
596 readbufsize
= remaining
;
600 * At this point, we should have active data on the connection (see
601 * select() above) so this read SHOULDN'T block. Hopefully.
604 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
607 netsec_err(errstr
, "Received EOF on network read");
612 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
617 * Okay, so we've had a successful read. If we are doing SASL security
618 * layers, pass this through sasl_decode(). sasl_decode() can return
619 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
620 * we can just update our length pointer.
624 if (nsc
->sasl_seclayer
) {
626 unsigned int tmpoutlen
;
628 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
629 &tmpout
, &tmpoutlen
);
632 netsec_err(errstr
, "Unable to decode SASL network data: %s",
633 sasl_errdetail(nsc
->sasl_conn
));
644 if (tmpoutlen
> remaining
) {
645 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
649 memcpy(end
, tmpout
, tmpoutlen
);
651 nsc
->ns_inbuflen
+= tmpoutlen
;
653 #endif /* CYRUS_SASL */
654 nsc
->ns_inbuflen
+= rc
;
657 * If we're past the halfway point in our read buffers, shuffle everything
658 * back to the beginning.
661 if (startoffset
> nsc
->ns_inbufsize
/ 2) {
662 memmove(nsc
->ns_inbuffer
, nsc
->ns_inptr
, nsc
->ns_inbuflen
);
663 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
670 * Write data to our network connection. Really, fill up the buffer as
671 * much as we can, and flush it out if necessary. netsec_flush() does
676 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
679 const unsigned char *bufptr
= buffer
;
688 * Run a loop copying in data to our local buffer; when we're done with
689 * any buffer overflows then just copy any remaining data in.
692 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
693 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
696 * In theory I should increment outptr, but netsec_flush just resets
699 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
701 rc
= netsec_flush(nsc
, errstr
);
711 * Copy any leftover data into the buffer.
715 memcpy(nsc
->ns_outptr
, bufptr
, size
);
716 nsc
->ns_outptr
+= size
;
717 nsc
->ns_outbuflen
+= size
;
724 * Our network printf() routine, which really just calls netsec_vprintf().
728 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
733 va_start(ap
, format
);
734 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
741 * Write bytes to the network using printf()-style formatting.
743 * Again, for the most part copy stuff into our buffer to be flushed
748 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
754 * Cheat a little. If we can fit the data into our outgoing buffer,
755 * great! If not, generate a flush and retry once.
759 rc
= vsnprintf((char *) nsc
->ns_outptr
,
760 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, ap
);
762 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
764 * This means we have an overflow. Note that we don't actually
765 * make use of the terminating NUL, but according to the spec
766 * vsnprintf() won't write to the last byte in the string; that's
767 * why we have to use >= in the comparison above.
769 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
771 * Whoops, if the buffer pointer was the same as the start of the
772 * buffer, that means we overflowed the internal buffer.
773 * At that point, just give up.
775 netsec_err(errstr
, "Internal error: wanted to printf() a total of "
776 "%d bytes, but our buffer size was only %d bytes",
777 rc
, nsc
->ns_outbufsize
);
781 * Generate a flush (which may be inefficient, but hopefully
782 * it isn't) and then try again.
784 if (netsec_flush(nsc
, errstr
) != OK
)
787 * After this, outbuffer should == outptr, so we shouldn't
788 * hit this next time around.
796 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\n') {
798 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\r')
801 nsc
->ns_snoop_noend
= 1;
803 if (outlen
> 0 || nsc
->ns_snoop_noend
== 0) {
805 if (nsc
->sasl_seclayer
)
806 fprintf(stderr
, "(sasl-encrypted) ");
807 #endif /* CYRUS_SASL */
810 fprintf(stderr
, "(tls-encrypted) ");
811 #endif /* TLS_SUPPORT */
812 fprintf(stderr
, "=> ");
813 if (nsc
->ns_snoop_cb
)
814 nsc
->ns_snoop_cb(nsc
, (char *) nsc
->ns_outptr
, outlen
,
815 nsc
->ns_snoop_context
);
817 fprintf(stderr
, "%.*s\n", outlen
, nsc
->ns_outptr
);
819 nsc
->ns_snoop_noend
= 0;
823 nsc
->ns_outptr
+= rc
;
824 nsc
->ns_outbuflen
+= rc
;
830 * Flush out any buffered data in our output buffers. This routine is
831 * actually where the real network writes take place.
835 netsec_flush(netsec_context
*nsc
, char **errstr
)
837 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
838 unsigned int netoutlen
= nsc
->ns_outbuflen
;
849 * If SASL security layers are in effect, run the data through
850 * sasl_encode() first.
853 if (nsc
->sasl_seclayer
) {
854 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
855 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
858 netsec_err(errstr
, "SASL data encoding failed: %s",
859 sasl_errdetail(nsc
->sasl_conn
));
864 #endif /* CYRUS_SASL */
867 * If TLS is active, then use those functions to write out the
871 if (nsc
->tls_active
) {
872 if (BIO_write(nsc
->ssl_io
, netoutbuf
, netoutlen
) <= 0) {
873 netsec_err(errstr
, "Error writing to TLS connection: %s",
874 ERR_error_string(ERR_get_error(), NULL
));
878 #endif /* TLS_SUPPORT */
880 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
883 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
888 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
889 nsc
->ns_outbuflen
= 0;
895 * Set various SASL protocol parameters
899 netsec_set_sasl_params(netsec_context
*nsc
, const char *hostname
,
900 const char *service
, const char *mechanism
,
901 netsec_sasl_callback callback
, char **errstr
)
904 sasl_callback_t
*sasl_cbs
;
907 if (! sasl_initialized
) {
908 retval
= sasl_client_init(NULL
);
909 if (retval
!= SASL_OK
) {
910 netsec_err(errstr
, "SASL client initialization failed: %s",
911 sasl_errstring(retval
, NULL
, NULL
));
918 * Allocate an array of SASL callbacks for this connection.
919 * Right now we just allocate an array of four callbacks.
922 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
924 sasl_cbs
[0].id
= SASL_CB_USER
;
925 sasl_cbs
[0].proc
= (sasl_callback_ft
) netsec_get_user
;
926 sasl_cbs
[0].context
= nsc
;
928 sasl_cbs
[1].id
= SASL_CB_AUTHNAME
;
929 sasl_cbs
[1].proc
= (sasl_callback_ft
) netsec_get_user
;
930 sasl_cbs
[1].context
= nsc
;
932 sasl_cbs
[2].id
= SASL_CB_PASS
;
933 sasl_cbs
[2].proc
= (sasl_callback_ft
) netsec_get_password
;
934 sasl_cbs
[2].context
= nsc
;
936 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
937 sasl_cbs
[3].proc
= NULL
;
938 sasl_cbs
[3].context
= NULL
;
940 nsc
->sasl_cbs
= sasl_cbs
;
942 retval
= sasl_client_new(service
, hostname
, NULL
, NULL
, nsc
->sasl_cbs
, 0,
946 netsec_err(errstr
, "SASL new client allocation failed: %s",
947 sasl_errstring(retval
, NULL
, NULL
));
951 nsc
->sasl_hostname
= getcpy(hostname
);
952 #else /* CYRUS_SASL */
953 NMH_UNUSED(hostname
);
956 #endif /* CYRUS_SASL */
959 * According to the RFC, mechanisms can only be uppercase letter, numbers,
960 * and a hypen or underscore. So make sure we uppercase any letters
961 * in case the user passed in lowercase.
966 nsc
->sasl_mech
= getcpy(mechanism
);
968 for (p
= nsc
->sasl_mech
; *p
; p
++)
969 if (isascii((unsigned char) *p
)) /* Just in case */
970 *p
= toupper((unsigned char) *p
);
973 nsc
->sasl_proto_cb
= callback
;
980 * Our userid callback; return the specified username to the SASL
981 * library when asked.
984 int netsec_get_user(void *context
, int id
, const char **result
,
987 netsec_context
*nsc
= (netsec_context
*) context
;
989 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
990 return SASL_BADPARAM
;
992 if (nsc
->ns_userid
== NULL
) {
994 * Pass the 1 third argument to nmh_get_credentials() so that
995 * a defauly user if the -user switch wasn't supplied, and so
996 * that a default password will be supplied. That's used when
997 * those values really don't matter, and only with legacy/.netrc,
998 * i.e., with a credentials profile entry.
1001 if (nsc
->sasl_creds
== NULL
) {
1002 nsc
->sasl_creds
= mh_xmalloc(sizeof(*nsc
->sasl_creds
));
1003 nsc
->sasl_creds
->user
= NULL
;
1004 nsc
->sasl_creds
->password
= NULL
;
1007 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 1,
1008 nsc
->sasl_creds
) != OK
)
1009 return SASL_BADPARAM
;
1011 if (nsc
->ns_userid
!= nsc
->sasl_creds
->user
) {
1013 free(nsc
->ns_userid
);
1014 nsc
->ns_userid
= getcpy(nsc
->sasl_creds
->user
);
1018 *result
= nsc
->ns_userid
;
1020 *len
= strlen(nsc
->ns_userid
);
1026 * Retrieve a password and return it to SASL
1030 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
1031 sasl_secret_t
**psecret
)
1033 netsec_context
*nsc
= (netsec_context
*) context
;
1038 if (! psecret
|| id
!= SASL_CB_PASS
)
1039 return SASL_BADPARAM
;
1041 if (nsc
->sasl_creds
== NULL
) {
1042 nsc
->sasl_creds
= mh_xmalloc(sizeof(*nsc
->sasl_creds
));
1043 nsc
->sasl_creds
->user
= NULL
;
1044 nsc
->sasl_creds
->password
= NULL
;
1047 if (nsc
->sasl_creds
->password
== NULL
) {
1049 * Pass the 0 third argument to nmh_get_credentials() so
1050 * that the default password isn't used. With legacy/.netrc
1051 * credentials support, we'll only get here if the -user
1052 * switch to send(1)/post(8) wasn't used.
1055 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 0,
1056 nsc
->sasl_creds
) != OK
) {
1057 return SASL_BADPARAM
;
1061 len
= strlen(nsc
->sasl_creds
->password
);
1064 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1065 * us room for a terminating NUL
1068 *psecret
= (sasl_secret_t
*) malloc(sizeof(sasl_secret_t
) + len
);
1073 (*psecret
)->len
= len
;
1074 strcpy((char *) (*psecret
)->data
, nsc
->sasl_creds
->password
);
1076 nsc
->sasl_secret
= *psecret
;
1080 #endif /* CYRUS_SASL */
1083 * Negotiate SASL on this connection
1087 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
1090 sasl_security_properties_t secprops
;
1091 const char *chosen_mech
;
1092 const unsigned char *saslbuf
;
1093 unsigned char *outbuf
;
1094 unsigned int saslbuflen
, outbuflen
;
1098 #ifdef OAUTH_SUPPORT
1099 unsigned char *xoauth_client_res
;
1100 size_t xoauth_client_res_len
;
1101 #endif /* OAUTH_SUPPORT */
1105 * If we've been passed a requested mechanism, check our mechanism
1106 * list from the protocol. If it's not supported, return an error.
1109 if (nsc
->sasl_mech
) {
1110 char **str
, *mlist
= getcpy(mechlist
);
1113 str
= brkstring(mlist
, " ", NULL
);
1115 for (i
= 0; str
[i
] != NULL
; i
++) {
1116 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1121 i
= (str
[i
] == NULL
);
1126 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1132 #ifdef OAUTH_SUPPORT
1133 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
1135 * This should be relatively straightforward, but requires some
1136 * help from the plugin. Basically, if XOAUTH2 is a success,
1137 * the callback has to return success, but no output data. If
1138 * there is output data, it will be assumed that it is the JSON
1142 if (! nsc
->oauth_service
) {
1143 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1147 nsc
->sasl_chosen_mech
= getcpy(nsc
->sasl_mech
);
1149 if (mh_oauth_do_xoauth(nsc
->ns_userid
, nsc
->oauth_service
,
1150 &xoauth_client_res
, &xoauth_client_res_len
,
1151 nsc
->ns_snoop
? stderr
: NULL
) != OK
) {
1152 netsec_err(errstr
, "Internal error: Unable to get OAuth2 "
1157 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_START
, xoauth_client_res
,
1158 xoauth_client_res_len
, NULL
, 0, errstr
);
1159 free(xoauth_client_res
);
1165 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1166 * success, we indicate that with no output data. But if we
1167 * fail, then send a blank message and get the resulting
1171 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0, errstr
);
1175 * We're going to assume the error here is a JSON response;
1176 * we ignore it and send a blank message in response. We should
1177 * then get either an +OK or -ERR
1180 nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, NULL
, 0, NULL
, 0, NULL
);
1181 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1184 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1190 #endif /* OAUTH_SUPPORT */
1194 * In netsec_set_sasl_params, we've already done all of our setup with
1195 * sasl_client_init() and sasl_client_new(). So time to set security
1196 * properties, call sasl_client_start(), and generate the protocol
1200 memset(&secprops
, 0, sizeof(secprops
));
1201 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1204 * If we're using TLS, do not negotiate a security layer
1209 nsc
->tls_active
? 0 :
1210 #endif /* TLS_SUPPORT */
1213 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1215 if (rc
!= SASL_OK
) {
1216 netsec_err(errstr
, "SASL security property initialization failed: %s",
1217 sasl_errstring(rc
, NULL
, NULL
));
1222 * Start the actual protocol negotiation, and go through the
1223 * sasl_client_step() loop (after sasl_client_start, of course).
1226 rc
= sasl_client_start(nsc
->sasl_conn
,
1227 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1228 (const char **) &saslbuf
, &saslbuflen
,
1231 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1232 netsec_err(errstr
, "SASL client start failed: %s",
1233 sasl_errdetail(nsc
->sasl_conn
));
1237 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1239 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1244 * We've written out our first message; enter in the step loop
1247 while (rc
== SASL_CONTINUE
) {
1249 * Call our SASL callback, which will handle the details of
1250 * reading data from the network.
1253 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1255 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1259 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1260 (const char **) &saslbuf
, &saslbuflen
);
1265 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1266 netsec_err(errstr
, "SASL client negotiation failed: %s",
1267 sasl_errdetail(nsc
->sasl_conn
));
1268 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1272 if (nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, saslbuf
, saslbuflen
,
1273 NULL
, 0, errstr
) != OK
) {
1274 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1280 * SASL exchanges should be complete, process the final response message
1284 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1287 * At this point we can't really send an abort since the SASL dialog
1288 * has completed, so just bubble back up the error message.
1295 * At this point, SASL should be complete. Get a few properties
1296 * from the authentication exchange.
1299 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1301 if (rc
!= SASL_OK
) {
1302 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1303 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1307 nsc
->sasl_ssf
= *ssf
;
1309 if (nsc
->sasl_ssf
> 0) {
1310 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1311 (const void **) &outbufmax
);
1313 if (rc
!= SASL_OK
) {
1314 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1315 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
1320 * If our output buffer isn't the same size as the input buffer,
1321 * reallocate it and set the new size (since we won't encode any
1322 * data larger than that).
1325 nsc
->sasl_maxbufsize
= *outbufmax
;
1327 if (nsc
->ns_outbufsize
!= nsc
->sasl_maxbufsize
) {
1328 nsc
->ns_outbufsize
= nsc
->sasl_maxbufsize
;
1329 nsc
->ns_outbuffer
= mh_xrealloc(nsc
->ns_outbuffer
,
1330 nsc
->ns_outbufsize
);
1332 * There shouldn't be any data in the buffer, but for
1333 * consistency's sake discard it.
1335 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1336 nsc
->ns_outbuflen
= 0;
1340 * Allocate a buffer to do temporary reads into, before we
1341 * call sasl_decode()
1344 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
1347 * Okay, this is a bit weird. Make sure that the input buffer
1348 * is at least TWICE the size of the max buffer size. That's
1349 * because if we're consuming data but want to extend the current
1350 * buffer, we want to be sure there's room for another full buffer's
1354 if (nsc
->ns_inbufsize
< nsc
->sasl_maxbufsize
* 2) {
1355 size_t offset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
1356 nsc
->ns_inbufsize
= nsc
->sasl_maxbufsize
* 2;
1357 nsc
->ns_inbuffer
= mh_xrealloc(nsc
->ns_inbuffer
, nsc
->ns_inbufsize
);
1358 nsc
->ns_inptr
= nsc
->ns_inbuffer
+ offset
;
1361 nsc
->sasl_seclayer
= 1;
1368 netsec_err(errstr
, "SASL not supported");
1371 #endif /* CYRUS_SASL */
1375 * Retrieve our chosen SASL mechanism
1379 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1382 return nsc
->sasl_chosen_mech
;
1383 #else /* CYRUS_SASL */
1387 #endif /* CYRUS_SASL */
1391 * Set an OAuth2 service name, if we support it.
1395 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1397 #ifdef OAUTH_SUPPORT
1398 nsc
->oauth_service
= getcpy(service
);
1400 #else /* OAUTH_SUPPORT */
1402 #endif /* OAUTH_SUPPORT */
1406 * Initialize (and enable) TLS for this connection
1410 netsec_set_tls(netsec_context
*nsc
, int tls
, char **errstr
)
1415 BIO
*rbio
, *wbio
, *ssl_bio
;;
1417 if (! tls_initialized
) {
1419 SSL_load_error_strings();
1422 * Create the SSL context; this has the properties for all
1423 * SSL connections (we are only doing one), though. Make sure
1424 * we only support secure (known as of now) TLS protocols.
1427 sslctx
= SSL_CTX_new(SSLv23_client_method());
1430 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1431 ERR_error_string(ERR_get_error(), NULL
));
1435 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1441 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1442 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1447 * Create the SSL structure which holds the data for a single
1451 ssl
= SSL_new(sslctx
);
1454 netsec_err(errstr
, "Unable to create SSL connection: %s",
1455 ERR_error_string(ERR_get_error(), NULL
));
1460 * Never bother us, since we are using blocking sockets.
1463 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1466 * This is a bit weird, so pay attention.
1468 * We create a socket BIO, and bind it to our SSL connection.
1469 * That means reads and writes to the SSL connection will use our
1472 * Then we create an SSL BIO, and assign our current SSL connection
1473 * to it. We then create a buffer BIO and push it in front of our
1474 * SSL BIO. So the chain looks like:
1476 * buffer BIO -> SSL BIO -> socket BIO.
1478 * So writes and reads are buffered (we mostly care about writes).
1481 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_NOCLOSE
);
1484 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1485 ERR_error_string(ERR_get_error(), NULL
));
1490 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_NOCLOSE
);
1493 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1494 ERR_error_string(ERR_get_error(), NULL
));
1500 SSL_set_bio(ssl
, rbio
, wbio
);
1501 SSL_set_connect_state(ssl
);
1503 ssl_bio
= BIO_new(BIO_f_ssl());
1506 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1507 ERR_error_string(ERR_get_error(), NULL
));
1512 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1513 nsc
->ssl_io
= ssl_bio
;
1517 BIO_free_all(nsc
->ssl_io
);
1522 #else /* TLS_SUPPORT */
1523 netsec_err(errstr
, "TLS is not supported");
1527 #endif /* TLS_SUPPORT */
1531 * Start TLS negotiation on this connection
1535 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1538 if (! nsc
->ssl_io
) {
1539 netsec_err(errstr
, "TLS has not been configured for this connection");
1543 if (BIO_do_handshake(nsc
->ssl_io
) < 1) {
1544 netsec_err(errstr
, "TLS negotiation failed: %s",
1545 ERR_error_string(ERR_get_error(), NULL
));
1549 if (nsc
->ns_snoop
) {
1552 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1553 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
1555 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
1556 fprintf(stderr
, "TLS negotiation successful: %s(%d) %s\n",
1557 SSL_CIPHER_get_name(cipher
),
1558 SSL_CIPHER_get_bits(cipher
, NULL
),
1559 SSL_CIPHER_get_version(cipher
));
1563 nsc
->tls_active
= 1;
1566 #else /* TLS_SUPPORT */
1567 netsec_err(errstr
, "TLS not supported");
1570 #endif /* TLS_SUPPORT */
1574 * Generate an (allocated) error string
1578 netsec_err(char **errstr
, const char *fmt
, ...)
1582 char *errbuf
= NULL
;
1589 errbufsize
= rc
+ 1;
1590 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1592 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1594 } while (rc
>= (int) errbufsize
);