/*
* How this code works, in general.
*
- * _If_ we are using no encryption or SASL encryption, then we buffer the
- * network data through ns_inbuffer and ns_outbuffer. That should be
- * relatively self-explanatory.
+ * _If_ we are using no encryption then we buffer the network data
+ * through ns_inbuffer and ns_outbuffer. That should be relatively
+ * self-explanatory.
*
- * If we are using SSL for encryption, then use a buffering BIO for output
- * (that just easier). Still do buffering for reads; when we need more
- * data we call the BIO_read() function to fill our local buffer.
+ * If we use encryption, then ns_inbuffer and ns_outbuffer contain the
+ * cleartext data. When it comes time to send the encrypted data on the
+ * (either from a flush or the buffer is full) we either use BIO_write()
+ * for TLS or sasl_encode() (followed by a write() for Cyrus-SASL. For
+ * reads we either use BIO_read() (TLS) or do a network read into a
+ * temporary buffer and use sasl_decode() (Cyrus-SASL). Note that if
+ * negotiate TLS then we disable SASL encryption.
*
- * For SASL, we make use of (for now) the Cyrus-SASL library. For some
- * mechanisms, we implement those mechanisms directly since the Cyrus SASL
- * library doesn't support them (like OAuth).
+ * We used to use a buffering BIO for the reads/writes for TLS, but it
+ * ended up being complicated to special-case the buffering for everything
+ * except TLS, so the buffering is now unified, no matter which encryption
+ * method is being used (even none).
+ *
+ * For SASL authentication, we make use of (for now) the Cyrus-SASL
+ * library. For some mechanisms, we implement those mechanisms directly
+ * since the Cyrus SASL library doesn't support them (like OAuth).
*/
/*
netsec_context *
netsec_init(void)
{
- netsec_context *nsc = mh_xmalloc(sizeof(*nsc));
+ netsec_context *nsc;
+ NEW(nsc);
nsc->ns_readfd = -1;
nsc->ns_writefd = -1;
nsc->ns_snoop = 0;
nsc->ns_inptr = nsc->ns_inbuffer;
}
-#ifdef CYRUS_SASL
+#if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
retry:
-#endif /* CYRUS_SASL */
+#endif /* CYRUS_SASL || TLS_SUPPORT */
/*
* If we are using TLS and there's anything pending, then skip the
* select call
*/
}
+ /*
+ * Some explanation:
+ *
+ * startoffset is the offset from the beginning of the input
+ * buffer to data that is in our input buffer, but has not yet
+ * been consumed. This can be non-zero if functions like
+ * netsec_readline() leave leftover data.
+ *
+ * remaining is the remaining amount of unconsumed data in the input
+ * buffer.
+ *
+ * end is a pointer to the end of the valid data + 1; it's where
+ * the next read should go.
+ */
+
startoffset = nsc->ns_inptr - nsc->ns_inbuffer;
remaining = nsc->ns_inbufsize - (startoffset + nsc->ns_inbuflen);
end = nsc->ns_inptr + nsc->ns_inbuflen;
+ /*
+ * If we're past the halfway point in our read buffers, shuffle everything
+ * back to the beginning.
+ */
+
+ if (startoffset > nsc->ns_inbufsize / 2) {
+ memmove(nsc->ns_inbuffer, nsc->ns_inptr, nsc->ns_inbuflen);
+ nsc->ns_inptr = nsc->ns_inbuffer;
+ startoffset = 0;
+ remaining = nsc->ns_inbufsize - nsc->ns_inbuflen;
+ end = nsc->ns_inptr + nsc->ns_inbuflen;
+ }
+
/*
* If we are using TLS, then just read via the BIO. But we still
* use our local buffer.
if (nsc->tls_active) {
rc = BIO_read(nsc->ssl_io, end, remaining);
if (rc == 0) {
+ SSL *ssl;
+ int errcode;
+
+ /*
+ * Check to see if we're supposed to retry; if so,
+ * then go back and read again.
+ */
+
+ if (BIO_should_retry(nsc->ssl_io))
+ goto retry;
+
/*
- * Either EOF, or possibly an error. Either way, it was probably
- * unexpected, so treat as error.
+ * Okay, fine. Get the real error out of the SSL context.
*/
- netsec_err(errstr, "TLS peer aborted connection");
+
+ if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
+ netsec_err(errstr, "SSL_read() returned 0, but cannot "
+ "retrieve SSL context");
+ return NOTOK;
+ }
+
+ errcode = SSL_get_error(ssl, rc);
+ if (errcode == SSL_ERROR_ZERO_RETURN) {
+ netsec_err(errstr, "TLS peer closed remote connection");
+ } else {
+ netsec_err(errstr, "TLS network read failed: %s",
+ ERR_error_string(ERR_peek_last_error(), NULL));
+ }
+ if (nsc->ns_snoop)
+ ERR_print_errors_fp(stderr);
return NOTOK;
} else if (rc < 0) {
/* Definitely an error */
#endif /* CYRUS_SASL */
nsc->ns_inbuflen += rc;
- /*
- * If we're past the halfway point in our read buffers, shuffle everything
- * back to the beginning.
- */
-
- if (startoffset > nsc->ns_inbufsize / 2) {
- memmove(nsc->ns_inbuffer, nsc->ns_inptr, nsc->ns_inbuflen);
- nsc->ns_inptr = nsc->ns_inbuffer;
- }
-
return OK;
}
return NOTOK;
}
- nsc->sasl_hostname = getcpy(hostname);
+ nsc->sasl_hostname = mh_xstrdup(hostname);
#else /* CYRUS_SASL */
NMH_UNUSED(hostname);
NMH_UNUSED(service);
if (mechanism) {
char *p;
- nsc->sasl_mech = getcpy(mechanism);
+ nsc->sasl_mech = mh_xstrdup(mechanism);
for (p = nsc->sasl_mech; *p; p++)
if (isascii((unsigned char) *p)) /* Just in case */
*/
if (nsc->sasl_creds == NULL) {
- nsc->sasl_creds = mh_xmalloc(sizeof(*nsc->sasl_creds));
+ NEW(nsc->sasl_creds);
nsc->sasl_creds->user = NULL;
nsc->sasl_creds->password = NULL;
}
return SASL_BADPARAM;
if (nsc->sasl_creds == NULL) {
- nsc->sasl_creds = mh_xmalloc(sizeof(*nsc->sasl_creds));
+ NEW(nsc->sasl_creds);
nsc->sasl_creds->user = NULL;
nsc->sasl_creds->password = NULL;
}
return NOTOK;
}
- nsc->sasl_chosen_mech = getcpy(nsc->sasl_mech);
+ nsc->sasl_chosen_mech = mh_xstrdup(nsc->sasl_mech);
if (mh_oauth_do_xoauth(nsc->ns_userid, nsc->oauth_service,
&xoauth_client_res, &xoauth_client_res_len,