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 * 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
;
151 nsc
->oauth_service
= NULL
;
152 #endif /* OAUTH_SUPPORT */
154 nsc
->sasl_conn
= NULL
;
155 nsc
->sasl_hostname
= NULL
;
156 nsc
->sasl_cbs
= NULL
;
157 nsc
->sasl_creds
= NULL
;
158 nsc
->sasl_secret
= NULL
;
159 nsc
->sasl_chosen_mech
= NULL
;
161 nsc
->sasl_seclayer
= 0;
162 nsc
->sasl_tmpbuf
= NULL
;
163 nsc
->sasl_maxbufsize
= 0;
164 #endif /* CYRUS_SASL */
168 #endif /* TLS_SUPPORT */
173 * Shutdown the connection completely and free all resources.
174 * The connection is only closed if the flag is given.
178 netsec_shutdown(netsec_context
*nsc
, int closeflag
)
181 free(nsc
->ns_userid
);
182 if (nsc
->ns_inbuffer
)
183 free(nsc
->ns_inbuffer
);
184 if (nsc
->ns_outbuffer
)
185 free(nsc
->ns_outbuffer
);
187 free(nsc
->sasl_mech
);
189 if (nsc
->oauth_service
)
190 free(nsc
->oauth_service
);
191 #endif /* OAUTH_SERVICE */
194 sasl_dispose(&nsc
->sasl_conn
);
195 if (nsc
->sasl_hostname
)
196 free(nsc
->sasl_hostname
);
199 if (nsc
->sasl_creds
) {
200 if (nsc
->sasl_creds
->password
)
201 memset(nsc
->sasl_creds
->password
, 0,
202 strlen(nsc
->sasl_creds
->password
));
203 free(nsc
->sasl_creds
);
205 if (nsc
->sasl_secret
) {
206 if (nsc
->sasl_secret
->len
> 0) {
207 memset(nsc
->sasl_secret
->data
, 0, nsc
->sasl_secret
->len
);
209 free(nsc
->sasl_secret
);
211 if (nsc
->sasl_chosen_mech
)
212 free(nsc
->sasl_chosen_mech
);
213 if (nsc
->sasl_tmpbuf
)
214 free(nsc
->sasl_tmpbuf
);
215 #endif /* CYRUS_SASL */
219 * I checked; BIO_free_all() will cause SSL_shutdown to be called
220 * on the SSL object in the chain.
222 BIO_free_all(nsc
->ssl_io
);
223 #endif /* TLS_SUPPORT */
226 if (nsc
->ns_readfd
!= -1)
227 close(nsc
->ns_readfd
);
228 if (nsc
->ns_writefd
!= -1 && nsc
->ns_writefd
!= nsc
->ns_readfd
)
229 close(nsc
->ns_writefd
);
236 * Set the file descriptor for our context
240 netsec_set_fd(netsec_context
*nsc
, int readfd
, int writefd
)
242 nsc
->ns_readfd
= readfd
;
243 nsc
->ns_writefd
= writefd
;
247 * Set the userid used for authentication for this context
251 netsec_set_userid(netsec_context
*nsc
, const char *userid
)
253 nsc
->ns_userid
= getcpy(userid
);
257 * Get the snoop flag for this connection
261 netsec_get_snoop(netsec_context
*nsc
)
263 return nsc
->ns_snoop
;
267 * Set the snoop flag for this connection
271 netsec_set_snoop(netsec_context
*nsc
, int snoop
)
273 nsc
->ns_snoop
= snoop
;
277 * Set the snoop callback for this connection.
280 void netsec_set_snoop_callback(netsec_context
*nsc
,
281 netsec_snoop_callback callback
, void *context
)
283 nsc
->ns_snoop_cb
= callback
;
284 nsc
->ns_snoop_context
= context
;
288 * A base64-decoding snoop callback
292 netsec_b64_snoop_decoder(netsec_context
*nsc
, const char *string
, size_t len
,
295 unsigned char *decoded
;
300 offset
= context
? *((int *) context
) : 0;
304 * Output non-base64 data first.
306 fprintf(stderr
, "%.*s", offset
, string
);
311 if (decodeBase64(string
, &decoded
, &decodedlen
, 1, NULL
) == OK
) {
313 * Some mechanisms preoduce large binary tokens, which aren't really
314 * readable. So let's do a simple heuristic. If the token is greater
315 * than 100 characters _and_ the first 100 bytes are more than 50%
316 * non-ASCII, then don't print the decoded buffer, just the
319 if (decodedlen
> 100 && !checkascii(decoded
, 100)) {
320 fprintf(stderr
, "%.*s\n", (int) len
, string
);
323 hexify(decoded
, decodedlen
, &hexified
);
324 fprintf(stderr
, "b64<%s>\n", hexified
);
329 fprintf(stderr
, "%.*s\n", (int) len
, string
);
334 * If the ASCII content is > 50%, return 1
338 checkascii(const unsigned char *bytes
, size_t len
)
340 size_t count
= 0, half
= len
/ 2;
343 if (isascii(*bytes
) && isprint(*bytes
) && ++count
> half
)
346 /* No chance by this point */
347 if (count
+ len
< half
)
355 * Set the read timeout for this connection
359 netsec_set_timeout(netsec_context
*nsc
, int timeout
)
361 nsc
->ns_timeout
= timeout
;
365 * Read data from the network. Basically, return anything in our buffer,
366 * otherwise fill from the network.
370 netsec_read(netsec_context
*nsc
, void *buffer
, size_t size
, char **errstr
)
375 * If our buffer is empty, then we should fill it now
378 if (nsc
->ns_inbuflen
== 0) {
379 if (netsec_fillread(nsc
, errstr
) != OK
)
384 * netsec_fillread only returns if the buffer is full, so we can
385 * assume here that this has something in it.
388 retlen
= size
> nsc
->ns_inbuflen
? nsc
->ns_inbuflen
: size
;
390 memcpy(buffer
, nsc
->ns_inptr
, retlen
);
392 if (retlen
== (int) nsc
->ns_inbuflen
) {
394 * We've emptied our buffer, so reset everything.
396 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
397 nsc
->ns_inbuflen
= 0;
399 nsc
->ns_inptr
+= size
;
400 nsc
->ns_inbuflen
-= size
;
407 * Get a "line" (CR/LF) terminated from the network.
409 * Okay, we play some games here, so pay attention:
411 * - Unlike every other function, we return a pointer to the
412 * existing buffer. This pointer is valid until you call another
413 * read functiona again.
414 * - We NUL-terminated the buffer right at the end, before the terminator.
415 * - Technically we look for a LF; if we find a CR right before it, then
417 * - If your data may contain embedded NULs, this won't work.
421 netsec_readline(netsec_context
*nsc
, size_t *len
, char **errstr
)
423 unsigned char *ptr
= nsc
->ns_inptr
;
424 size_t count
= 0, offset
;
428 * Search through our existing buffer for a LF
431 while (count
< nsc
->ns_inbuflen
) {
433 if (*ptr
++ == '\n') {
434 char *sptr
= (char *) nsc
->ns_inptr
;
435 if (count
> 1 && *(ptr
- 2) == '\r')
439 *len
= ptr
- nsc
->ns_inptr
;
440 nsc
->ns_inptr
+= count
;
441 nsc
->ns_inbuflen
-= count
;
444 if (nsc
->sasl_seclayer
)
445 fprintf(stderr
, "(sasl-decrypted) ");
446 #endif /* CYRUS_SASL */
449 fprintf(stderr
, "(tls-decrypted) ");
450 #endif /* TLS_SUPPORT */
451 fprintf(stderr
, "<= ");
452 if (nsc
->ns_snoop_cb
)
453 nsc
->ns_snoop_cb(nsc
, sptr
, strlen(sptr
),
454 nsc
->ns_snoop_context
);
456 fprintf(stderr
, "%s\n", sptr
);
463 * Hm, we didn't find a \n. If we've already searched half of the input
464 * buffer, return an error.
467 if (count
>= nsc
->ns_inbufsize
/ 2) {
468 netsec_err(errstr
, "Unable to find a line terminator after %d bytes",
474 * Okay, get some more network data. This may move inptr, so regenerate
478 offset
= ptr
- nsc
->ns_inptr
;
480 if (netsec_fillread(nsc
, errstr
) != OK
)
483 ptr
= nsc
->ns_inptr
+ offset
;
487 return NULL
; /* Should never reach this */
491 * Fill our read buffer with some data from the network.
495 netsec_fillread(netsec_context
*nsc
, char **errstr
)
499 size_t readbufsize
, remaining
, startoffset
;
503 * If inbuflen is zero, that means the buffer has been emptied
504 * completely. In that case move inptr back to the start.
507 if (nsc
->ns_inbuflen
== 0) {
508 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
513 * If we are using TLS and there's anything pending, then skip the
517 if (!nsc
->tls_active
|| BIO_pending(nsc
->ssl_io
) == 0)
518 #endif /* TLS_SUPPORT */
524 FD_SET(nsc
->ns_readfd
, &rfds
);
526 tv
.tv_sec
= nsc
->ns_timeout
;
529 rc
= select(nsc
->ns_readfd
+ 1, &rfds
, NULL
, NULL
, &tv
);
532 netsec_err(errstr
, "select() while reading failed: %s",
538 netsec_err(errstr
, "read() timed out after %d seconds",
544 * At this point, we know that rc is 1, so there's not even any
545 * point to check to see if our descriptor is set in rfds.
549 startoffset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
550 remaining
= nsc
->ns_inbufsize
- (startoffset
+ nsc
->ns_inbuflen
);
551 end
= nsc
->ns_inptr
+ nsc
->ns_inbuflen
;
554 * If we are using TLS, then just read via the BIO. But we still
555 * use our local buffer.
558 if (nsc
->tls_active
) {
559 rc
= BIO_read(nsc
->ssl_io
, end
, remaining
);
562 * Either EOF, or possibly an error. Either way, it was probably
563 * unexpected, so treat as error.
565 netsec_err(errstr
, "TLS peer aborted connection");
568 /* Definitely an error */
569 netsec_err(errstr
, "Read on TLS connection failed: %s",
570 ERR_error_string(ERR_get_error(), NULL
));
574 nsc
->ns_inbuflen
+= rc
;
578 #endif /* TLS_SUPPORT */
581 * Okay, time to read some data. Either we're just doing it straight
582 * or we're passing it through sasl_decode() first.
586 if (nsc
->sasl_seclayer
) {
587 readbuf
= nsc
->sasl_tmpbuf
;
588 readbufsize
= nsc
->sasl_maxbufsize
;
590 #endif /* CYRUS_SASL */
592 readbuf
= (char *) end
;
593 readbufsize
= remaining
;
597 * At this point, we should have active data on the connection (see
598 * select() above) so this read SHOULDN'T block. Hopefully.
601 rc
= read(nsc
->ns_readfd
, readbuf
, readbufsize
);
604 netsec_err(errstr
, "Received EOF on network read");
609 netsec_err(errstr
, "Network read failed: %s", strerror(errno
));
614 * Okay, so we've had a successful read. If we are doing SASL security
615 * layers, pass this through sasl_decode(). sasl_decode() can return
616 * 0 bytes decoded; if that happens, jump back to the beginning. Otherwise
617 * we can just update our length pointer.
621 if (nsc
->sasl_seclayer
) {
623 unsigned int tmpoutlen
;
625 rc
= sasl_decode(nsc
->sasl_conn
, nsc
->sasl_tmpbuf
, rc
,
626 &tmpout
, &tmpoutlen
);
629 netsec_err(errstr
, "Unable to decode SASL network data: %s",
630 sasl_errdetail(nsc
->sasl_conn
));
641 if (tmpoutlen
> remaining
) {
642 netsec_err(errstr
, "Internal error: SASL decode buffer overflow!");
646 memcpy(end
, tmpout
, tmpoutlen
);
648 nsc
->ns_inbuflen
+= tmpoutlen
;
650 #endif /* CYRUS_SASL */
651 nsc
->ns_inbuflen
+= rc
;
654 * If we're past the halfway point in our read buffers, shuffle everything
655 * back to the beginning.
658 if (startoffset
> nsc
->ns_inbufsize
/ 2) {
659 memmove(nsc
->ns_inbuffer
, nsc
->ns_inptr
, nsc
->ns_inbuflen
);
660 nsc
->ns_inptr
= nsc
->ns_inbuffer
;
667 * Write data to our network connection. Really, fill up the buffer as
668 * much as we can, and flush it out if necessary. netsec_flush() does
673 netsec_write(netsec_context
*nsc
, const void *buffer
, size_t size
,
676 const unsigned char *bufptr
= buffer
;
685 * Run a loop copying in data to our local buffer; when we're done with
686 * any buffer overflows then just copy any remaining data in.
689 while ((int) size
>= (remaining
= nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
690 memcpy(nsc
->ns_outptr
, bufptr
, remaining
);
693 * In theory I should increment outptr, but netsec_flush just resets
696 nsc
->ns_outbuflen
= nsc
->ns_outbufsize
;
698 rc
= netsec_flush(nsc
, errstr
);
708 * Copy any leftover data into the buffer.
712 memcpy(nsc
->ns_outptr
, bufptr
, size
);
713 nsc
->ns_outptr
+= size
;
714 nsc
->ns_outbuflen
+= size
;
721 * Our network printf() routine, which really just calls netsec_vprintf().
725 netsec_printf(netsec_context
*nsc
, char **errstr
, const char *format
, ...)
730 va_start(ap
, format
);
731 rc
= netsec_vprintf(nsc
, errstr
, format
, ap
);
738 * Write bytes to the network using printf()-style formatting.
740 * Again, for the most part copy stuff into our buffer to be flushed
745 netsec_vprintf(netsec_context
*nsc
, char **errstr
, const char *format
,
751 * Cheat a little. If we can fit the data into our outgoing buffer,
752 * great! If not, generate a flush and retry once.
756 rc
= vsnprintf((char *) nsc
->ns_outptr
,
757 nsc
->ns_outbufsize
- nsc
->ns_outbuflen
, format
, ap
);
759 if (rc
>= (int) (nsc
->ns_outbufsize
- nsc
->ns_outbuflen
)) {
761 * This means we have an overflow. Note that we don't actually
762 * make use of the terminating NUL, but according to the spec
763 * vsnprintf() won't write to the last byte in the string; that's
764 * why we have to use >= in the comparison above.
766 if (nsc
->ns_outbuffer
== nsc
->ns_outptr
) {
768 * Whoops, if the buffer pointer was the same as the start of the
769 * buffer, that means we overflowed the internal buffer.
770 * At that point, just give up.
772 netsec_err(errstr
, "Internal error: wanted to printf() a total of "
773 "%d bytes, but our buffer size was only %d bytes",
774 rc
, nsc
->ns_outbufsize
);
778 * Generate a flush (which may be inefficient, but hopefully
779 * it isn't) and then try again.
781 if (netsec_flush(nsc
, errstr
) != OK
)
784 * After this, outbuffer should == outptr, so we shouldn't
785 * hit this next time around.
793 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\n') {
795 if (outlen
> 0 && nsc
->ns_outptr
[outlen
- 1] == '\r')
798 nsc
->ns_snoop_noend
= 1;
800 if (outlen
> 0 || nsc
->ns_snoop_noend
== 0) {
802 if (nsc
->sasl_seclayer
)
803 fprintf(stderr
, "(sasl-encrypted) ");
804 #endif /* CYRUS_SASL */
807 fprintf(stderr
, "(tls-encrypted) ");
808 #endif /* TLS_SUPPORT */
809 fprintf(stderr
, "=> ");
810 if (nsc
->ns_snoop_cb
)
811 nsc
->ns_snoop_cb(nsc
, (char *) nsc
->ns_outptr
, outlen
,
812 nsc
->ns_snoop_context
);
814 fprintf(stderr
, "%.*s\n", outlen
, nsc
->ns_outptr
);
816 nsc
->ns_snoop_noend
= 0;
820 nsc
->ns_outptr
+= rc
;
821 nsc
->ns_outbuflen
+= rc
;
827 * Flush out any buffered data in our output buffers. This routine is
828 * actually where the real network writes take place.
832 netsec_flush(netsec_context
*nsc
, char **errstr
)
834 const char *netoutbuf
= (const char *) nsc
->ns_outbuffer
;
835 unsigned int netoutlen
= nsc
->ns_outbuflen
;
846 * If SASL security layers are in effect, run the data through
847 * sasl_encode() first.
850 if (nsc
->sasl_seclayer
) {
851 rc
= sasl_encode(nsc
->sasl_conn
, (const char *) nsc
->ns_outbuffer
,
852 nsc
->ns_outbuflen
, &netoutbuf
, &netoutlen
);
855 netsec_err(errstr
, "SASL data encoding failed: %s",
856 sasl_errdetail(nsc
->sasl_conn
));
861 #endif /* CYRUS_SASL */
864 * If TLS is active, then use those functions to write out the
868 if (nsc
->tls_active
) {
869 if (BIO_write(nsc
->ssl_io
, netoutbuf
, netoutlen
) <= 0) {
870 netsec_err(errstr
, "Error writing to TLS connection: %s",
871 ERR_error_string(ERR_get_error(), NULL
));
875 #endif /* TLS_SUPPORT */
877 rc
= write(nsc
->ns_writefd
, netoutbuf
, netoutlen
);
880 netsec_err(errstr
, "write() failed: %s", strerror(errno
));
885 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
886 nsc
->ns_outbuflen
= 0;
892 * Set various SASL protocol parameters
896 netsec_set_sasl_params(netsec_context
*nsc
, const char *hostname
,
897 const char *service
, const char *mechanism
,
898 netsec_sasl_callback callback
, char **errstr
)
901 sasl_callback_t
*sasl_cbs
;
904 if (! sasl_initialized
) {
905 retval
= sasl_client_init(NULL
);
906 if (retval
!= SASL_OK
) {
907 netsec_err(errstr
, "SASL client initialization failed: %s",
908 sasl_errstring(retval
, NULL
, NULL
));
915 * Allocate an array of SASL callbacks for this connection.
916 * Right now we just allocate an array of four callbacks.
919 sasl_cbs
= mh_xmalloc(sizeof(*sasl_cbs
) * 4);
921 sasl_cbs
[0].id
= SASL_CB_USER
;
922 sasl_cbs
[0].proc
= (sasl_callback_ft
) netsec_get_user
;
923 sasl_cbs
[0].context
= nsc
;
925 sasl_cbs
[1].id
= SASL_CB_AUTHNAME
;
926 sasl_cbs
[1].proc
= (sasl_callback_ft
) netsec_get_user
;
927 sasl_cbs
[1].context
= nsc
;
929 sasl_cbs
[2].id
= SASL_CB_PASS
;
930 sasl_cbs
[2].proc
= (sasl_callback_ft
) netsec_get_password
;
931 sasl_cbs
[2].context
= nsc
;
933 sasl_cbs
[3].id
= SASL_CB_LIST_END
;
934 sasl_cbs
[3].proc
= NULL
;
935 sasl_cbs
[3].context
= NULL
;
937 nsc
->sasl_cbs
= sasl_cbs
;
939 retval
= sasl_client_new(service
, hostname
, NULL
, NULL
, nsc
->sasl_cbs
, 0,
943 netsec_err(errstr
, "SASL new client allocation failed: %s",
944 sasl_errstring(retval
, NULL
, NULL
));
949 * According to the RFC, mechanisms can only be uppercase letter, numbers,
950 * and a hypen or underscore. So make sure we uppercase any letters
951 * in case the user passed in lowercase.
956 nsc
->sasl_mech
= getcpy(mechanism
);
958 for (p
= nsc
->sasl_mech
; *p
; p
++)
959 if (isascii((unsigned char) *p
)) /* Just in case */
960 *p
= toupper((unsigned char) *p
);
963 nsc
->sasl_proto_cb
= callback
;
964 nsc
->sasl_hostname
= getcpy(hostname
);
967 #else /* CYRUS_SASL */
968 netsec_err(errstr
, "SASL is not supported");
971 #endif /* CYRUS_SASL */
976 * Our userid callback; return the specified username to the SASL
977 * library when asked.
980 int netsec_get_user(void *context
, int id
, const char **result
,
983 netsec_context
*nsc
= (netsec_context
*) context
;
985 if (! result
|| (id
!= SASL_CB_USER
&& id
!= SASL_CB_AUTHNAME
))
986 return SASL_BADPARAM
;
988 if (nsc
->ns_userid
== NULL
) {
990 * Pass the 1 third argument to nmh_get_credentials() so that
991 * a defauly user if the -user switch wasn't supplied, and so
992 * that a default password will be supplied. That's used when
993 * those values really don't matter, and only with legacy/.netrc,
994 * i.e., with a credentials profile entry.
997 if (nsc
->sasl_creds
== NULL
) {
998 nsc
->sasl_creds
= mh_xmalloc(sizeof(*nsc
->sasl_creds
));
999 nsc
->sasl_creds
->user
= NULL
;
1000 nsc
->sasl_creds
->password
= NULL
;
1003 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 1,
1004 nsc
->sasl_creds
) != OK
)
1005 return SASL_BADPARAM
;
1007 if (nsc
->ns_userid
!= nsc
->sasl_creds
->user
) {
1009 free(nsc
->ns_userid
);
1010 nsc
->ns_userid
= getcpy(nsc
->sasl_creds
->user
);
1014 *result
= nsc
->ns_userid
;
1016 *len
= strlen(nsc
->ns_userid
);
1022 * Retrieve a password and return it to SASL
1026 netsec_get_password(sasl_conn_t
*conn
, void *context
, int id
,
1027 sasl_secret_t
**psecret
)
1029 netsec_context
*nsc
= (netsec_context
*) context
;
1034 if (! psecret
|| id
!= SASL_CB_PASS
)
1035 return SASL_BADPARAM
;
1037 if (nsc
->sasl_creds
== NULL
) {
1038 nsc
->sasl_creds
= mh_xmalloc(sizeof(*nsc
->sasl_creds
));
1039 nsc
->sasl_creds
->user
= NULL
;
1040 nsc
->sasl_creds
->password
= NULL
;
1043 if (nsc
->sasl_creds
->password
== NULL
) {
1045 * Pass the 0 third argument to nmh_get_credentials() so
1046 * that the default password isn't used. With legacy/.netrc
1047 * credentials support, we'll only get here if the -user
1048 * switch to send(1)/post(8) wasn't used.
1051 if (nmh_get_credentials(nsc
->sasl_hostname
, nsc
->ns_userid
, 0,
1052 nsc
->sasl_creds
) != OK
) {
1053 return SASL_BADPARAM
;
1057 len
= strlen(nsc
->sasl_creds
->password
);
1060 * sasl_secret_t includes 1 bytes for "data" already, so that leaves
1061 * us room for a terminating NUL
1064 *psecret
= (sasl_secret_t
*) malloc(sizeof(sasl_secret_t
) + len
);
1069 (*psecret
)->len
= len
;
1070 strcpy((char *) (*psecret
)->data
, nsc
->sasl_creds
->password
);
1072 nsc
->sasl_secret
= *psecret
;
1076 #endif /* CYRUS_SASL */
1079 * Negotiate SASL on this connection
1083 netsec_negotiate_sasl(netsec_context
*nsc
, const char *mechlist
, char **errstr
)
1086 sasl_security_properties_t secprops
;
1087 const char *chosen_mech
;
1088 const unsigned char *saslbuf
;
1089 unsigned char *outbuf
;
1090 unsigned int saslbuflen
, outbuflen
;
1094 #ifdef OAUTH_SUPPORT
1095 unsigned char *xoauth_client_res
;
1096 size_t xoauth_client_res_len
;
1097 #endif /* OAUTH_SUPPORT */
1101 * If we've been passed a requested mechanism, check our mechanism
1102 * list from the protocol. If it's not supported, return an error.
1105 if (nsc
->sasl_mech
) {
1106 char **str
, *mlist
= getcpy(mechlist
);
1109 str
= brkstring(mlist
, " ", NULL
);
1111 for (i
= 0; str
[i
] != NULL
; i
++) {
1112 if (strcasecmp(nsc
->sasl_mech
, str
[i
]) == 0) {
1117 i
= (str
[i
] == NULL
);
1122 netsec_err(errstr
, "Chosen mechanism %s not supported by server",
1128 #ifdef OAUTH_SUPPORT
1129 if (nsc
->sasl_mech
&& strcasecmp(nsc
->sasl_mech
, "XOAUTH2") == 0) {
1131 * This should be relatively straightforward, but requires some
1132 * help from the plugin. Basically, if XOAUTH2 is a success,
1133 * the callback has to return success, but no output data. If
1134 * there is output data, it will be assumed that it is the JSON
1138 if (! nsc
->oauth_service
) {
1139 netsec_err(errstr
, "Internal error: OAuth2 service name not given");
1143 nsc
->sasl_chosen_mech
= getcpy(nsc
->sasl_mech
);
1145 if (mh_oauth_do_xoauth(nsc
->ns_userid
, nsc
->oauth_service
,
1146 &xoauth_client_res
, &xoauth_client_res_len
,
1147 nsc
->ns_snoop
? stderr
: NULL
) != OK
) {
1148 netsec_err(errstr
, "Internal error: Unable to get OAuth2 "
1153 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_START
, xoauth_client_res
,
1154 xoauth_client_res_len
, NULL
, 0, errstr
);
1155 free(xoauth_client_res
);
1161 * Okay, we need to do a NETSEC_SASL_FINISH now. If we return
1162 * success, we indicate that with no output data. But if we
1163 * fail, then send a blank message and get the resulting
1167 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0, errstr
);
1171 * We're going to assume the error here is a JSON response;
1172 * we ignore it and send a blank message in response. We should
1173 * then get either an +OK or -ERR
1176 nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, NULL
, 0, NULL
, 0, NULL
);
1177 rc
= nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1180 netsec_err(errstr
, "Unexpected success after OAuth failure!");
1186 #endif /* OAUTH_SUPPORT */
1190 * In netsec_set_sasl_params, we've already done all of our setup with
1191 * sasl_client_init() and sasl_client_new(). So time to set security
1192 * properties, call sasl_client_start(), and generate the protocol
1196 memset(&secprops
, 0, sizeof(secprops
));
1197 secprops
.maxbufsize
= SASL_MAXRECVBUF
;
1200 * If we're using TLS, do not negotiate a security layer
1205 nsc
->tls_active
? 0 :
1206 #endif /* TLS_SUPPORT */
1209 rc
= sasl_setprop(nsc
->sasl_conn
, SASL_SEC_PROPS
, &secprops
);
1211 if (rc
!= SASL_OK
) {
1212 netsec_err(errstr
, "SASL security property initialization failed: %s",
1213 sasl_errstring(rc
, NULL
, NULL
));
1218 * Start the actual protocol negotiation, and go through the
1219 * sasl_client_step() loop (after sasl_client_start, of course).
1222 rc
= sasl_client_start(nsc
->sasl_conn
,
1223 nsc
->sasl_mech
? nsc
->sasl_mech
: mechlist
, NULL
,
1224 (const char **) &saslbuf
, &saslbuflen
,
1227 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1228 netsec_err(errstr
, "SASL client start failed: %s",
1229 sasl_errdetail(nsc
->sasl_conn
));
1233 nsc
->sasl_chosen_mech
= getcpy(chosen_mech
);
1235 if (nsc
->sasl_proto_cb(NETSEC_SASL_START
, saslbuf
, saslbuflen
, NULL
, 0,
1240 * We've written out our first message; enter in the step loop
1243 while (rc
== SASL_CONTINUE
) {
1245 * Call our SASL callback, which will handle the details of
1246 * reading data from the network.
1249 if (nsc
->sasl_proto_cb(NETSEC_SASL_READ
, NULL
, 0, &outbuf
, &outbuflen
,
1251 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1255 rc
= sasl_client_step(nsc
->sasl_conn
, (char *) outbuf
, outbuflen
, NULL
,
1256 (const char **) &saslbuf
, &saslbuflen
);
1261 if (rc
!= SASL_OK
&& rc
!= SASL_CONTINUE
) {
1262 netsec_err(errstr
, "SASL client negotiation failed: %s",
1263 sasl_errdetail(nsc
->sasl_conn
));
1264 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1268 if (nsc
->sasl_proto_cb(NETSEC_SASL_WRITE
, saslbuf
, saslbuflen
,
1269 NULL
, 0, errstr
) != OK
) {
1270 nsc
->sasl_proto_cb(NETSEC_SASL_CANCEL
, NULL
, 0, NULL
, 0, NULL
);
1276 * SASL exchanges should be complete, process the final response message
1280 if (nsc
->sasl_proto_cb(NETSEC_SASL_FINISH
, NULL
, 0, NULL
, 0,
1283 * At this point we can't really send an abort since the SASL dialog
1284 * has completed, so just bubble back up the error message.
1291 * At this point, SASL should be complete. Get a few properties
1292 * from the authentication exchange.
1295 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_SSF
, (const void **) &ssf
);
1297 if (rc
!= SASL_OK
) {
1298 netsec_err(errstr
, "Cannot retrieve SASL negotiated security "
1299 "strength factor: %s", sasl_errstring(rc
, NULL
, NULL
));
1303 nsc
->sasl_ssf
= *ssf
;
1305 if (nsc
->sasl_ssf
> 0) {
1306 rc
= sasl_getprop(nsc
->sasl_conn
, SASL_MAXOUTBUF
,
1307 (const void **) &outbufmax
);
1309 if (rc
!= SASL_OK
) {
1310 netsec_err(errstr
, "Cannot retrieve SASL negotiated output "
1311 "buffer size: %s", sasl_errstring(rc
, NULL
, NULL
));
1316 * If our output buffer isn't the same size as the input buffer,
1317 * reallocate it and set the new size (since we won't encode any
1318 * data larger than that).
1321 nsc
->sasl_maxbufsize
= *outbufmax
;
1323 if (nsc
->ns_outbufsize
!= nsc
->sasl_maxbufsize
) {
1324 nsc
->ns_outbufsize
= nsc
->sasl_maxbufsize
;
1325 nsc
->ns_outbuffer
= mh_xrealloc(nsc
->ns_outbuffer
,
1326 nsc
->ns_outbufsize
);
1328 * There shouldn't be any data in the buffer, but for
1329 * consistency's sake discard it.
1331 nsc
->ns_outptr
= nsc
->ns_outbuffer
;
1332 nsc
->ns_outbuflen
= 0;
1336 * Allocate a buffer to do temporary reads into, before we
1337 * call sasl_decode()
1340 nsc
->sasl_tmpbuf
= mh_xmalloc(nsc
->sasl_maxbufsize
);
1343 * Okay, this is a bit weird. Make sure that the input buffer
1344 * is at least TWICE the size of the max buffer size. That's
1345 * because if we're consuming data but want to extend the current
1346 * buffer, we want to be sure there's room for another full buffer's
1350 if (nsc
->ns_inbufsize
< nsc
->sasl_maxbufsize
* 2) {
1351 size_t offset
= nsc
->ns_inptr
- nsc
->ns_inbuffer
;
1352 nsc
->ns_inbufsize
= nsc
->sasl_maxbufsize
* 2;
1353 nsc
->ns_inbuffer
= mh_xrealloc(nsc
->ns_inbuffer
, nsc
->ns_inbufsize
);
1354 nsc
->ns_inptr
= nsc
->ns_inbuffer
+ offset
;
1357 nsc
->sasl_seclayer
= 1;
1362 netsec_err(errstr
, "SASL not supported");
1365 #endif /* CYRUS_SASL */
1369 * Retrieve our chosen SASL mechanism
1373 netsec_get_sasl_mechanism(netsec_context
*nsc
)
1376 return nsc
->sasl_chosen_mech
;
1377 #else /* CYRUS_SASL */
1379 #endif /* CYRUS_SASL */
1383 * Set an OAuth2 service name, if we support it.
1387 netsec_set_oauth_service(netsec_context
*nsc
, const char *service
)
1389 #ifdef OAUTH_SUPPORT
1390 nsc
->oauth_service
= getcpy(service
);
1392 #else /* OAUTH_SUPPORT */
1394 #endif /* OAUTH_SUPPORT */
1398 * Initialize (and enable) TLS for this connection
1402 netsec_set_tls(netsec_context
*nsc
, int tls
, char **errstr
)
1407 BIO
*rbio
, *wbio
, *ssl_bio
;;
1409 if (! tls_initialized
) {
1411 SSL_load_error_strings();
1414 * Create the SSL context; this has the properties for all
1415 * SSL connections (we are only doing one), though. Make sure
1416 * we only support secure (known as of now) TLS protocols.
1419 sslctx
= SSL_CTX_new(SSLv23_client_method());
1422 netsec_err(errstr
, "Unable to initialize OpenSSL context: %s",
1423 ERR_error_string(ERR_get_error(), NULL
));
1427 SSL_CTX_set_options(sslctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
|
1433 if (nsc
->ns_readfd
== -1 || nsc
->ns_writefd
== -1) {
1434 netsec_err(errstr
, "Invalid file descriptor in netsec context");
1439 * Create the SSL structure which holds the data for a single
1443 ssl
= SSL_new(sslctx
);
1446 netsec_err(errstr
, "Unable to create SSL connection: %s",
1447 ERR_error_string(ERR_get_error(), NULL
));
1452 * Never bother us, since we are using blocking sockets.
1455 SSL_set_mode(ssl
, SSL_MODE_AUTO_RETRY
);
1458 * This is a bit weird, so pay attention.
1460 * We create a socket BIO, and bind it to our SSL connection.
1461 * That means reads and writes to the SSL connection will use our
1464 * Then we create an SSL BIO, and assign our current SSL connection
1465 * to it. We then create a buffer BIO and push it in front of our
1466 * SSL BIO. So the chain looks like:
1468 * buffer BIO -> SSL BIO -> socket BIO.
1470 * So writes and reads are buffered (we mostly care about writes).
1473 rbio
= BIO_new_socket(nsc
->ns_readfd
, BIO_NOCLOSE
);
1476 netsec_err(errstr
, "Unable to create a read socket BIO: %s",
1477 ERR_error_string(ERR_get_error(), NULL
));
1482 wbio
= BIO_new_socket(nsc
->ns_writefd
, BIO_NOCLOSE
);
1485 netsec_err(errstr
, "Unable to create a write socket BIO: %s",
1486 ERR_error_string(ERR_get_error(), NULL
));
1492 SSL_set_bio(ssl
, rbio
, wbio
);
1493 SSL_set_connect_state(ssl
);
1495 ssl_bio
= BIO_new(BIO_f_ssl());
1498 netsec_err(errstr
, "Unable to create a SSL BIO: %s",
1499 ERR_error_string(ERR_get_error(), NULL
));
1504 BIO_set_ssl(ssl_bio
, ssl
, BIO_CLOSE
);
1505 nsc
->ssl_io
= ssl_bio
;
1509 BIO_free_all(nsc
->ssl_io
);
1514 #else /* TLS_SUPPORT */
1515 netsec_err(errstr
, "TLS is not supported");
1519 #endif /* TLS_SUPPORT */
1523 * Start TLS negotiation on this connection
1527 netsec_negotiate_tls(netsec_context
*nsc
, char **errstr
)
1530 if (! nsc
->ssl_io
) {
1531 netsec_err(errstr
, "TLS has not been configured for this connection");
1535 if (BIO_do_handshake(nsc
->ssl_io
) < 1) {
1536 netsec_err(errstr
, "TLS negotiation failed: %s",
1537 ERR_error_string(ERR_get_error(), NULL
));
1541 if (nsc
->ns_snoop
) {
1544 if (BIO_get_ssl(nsc
->ssl_io
, &ssl
) < 1) {
1545 fprintf(stderr
, "WARNING: cannot determine SSL ciphers\n");
1547 const SSL_CIPHER
*cipher
= SSL_get_current_cipher(ssl
);
1548 fprintf(stderr
, "TLS negotiation successful: %s(%d) %s\n",
1549 SSL_CIPHER_get_name(cipher
),
1550 SSL_CIPHER_get_bits(cipher
, NULL
),
1551 SSL_CIPHER_get_version(cipher
));
1555 nsc
->tls_active
= 1;
1558 #else /* TLS_SUPPORT */
1559 netsec_err(errstr
, "TLS not supported");
1562 #endif /* TLS_SUPPORT */
1566 * Generate an (allocated) error string
1570 netsec_err(char **errstr
, const char *fmt
, ...)
1574 char *errbuf
= NULL
;
1581 errbufsize
= rc
+ 1;
1582 errbuf
= mh_xrealloc(errbuf
, errbufsize
);
1584 rc
= vsnprintf(errbuf
, errbufsize
, fmt
, ap
);
1586 } while (rc
>= (int) errbufsize
);