*/
struct _netsec_context {
- int ns_fd; /* Descriptor for network connection */
+ int ns_readfd; /* Read descriptor for network connection */
+ int ns_writefd; /* Write descriptor for network connection */
int ns_snoop; /* If true, display network data */
int ns_snoop_noend; /* If true, didn't get a CR/LF on last line */
netsec_snoop_callback *ns_snoop_cb; /* Snoop output callback */
{
netsec_context *nsc = mh_xmalloc(sizeof(*nsc));
- nsc->ns_fd = -1;
+ nsc->ns_readfd = -1;
+ nsc->ns_writefd = -1;
nsc->ns_snoop = 0;
nsc->ns_snoop_noend = 0;
nsc->ns_snoop_cb = NULL;
BIO_free_all(nsc->ssl_io);
#endif /* TLS_SUPPORT */
- if (closeflag && nsc->ns_fd != -1)
- close(nsc->ns_fd);
+ if (closeflag) {
+ if (nsc->ns_readfd != -1)
+ close(nsc->ns_readfd);
+ if (nsc->ns_writefd != -1 && nsc->ns_writefd != nsc->ns_readfd)
+ close(nsc->ns_writefd);
+ }
free(nsc);
}
*/
void
-netsec_set_fd(netsec_context *nsc, int fd)
+netsec_set_fd(netsec_context *nsc, int readfd, int writefd)
{
- nsc->ns_fd = fd;
+ nsc->ns_readfd = readfd;
+ nsc->ns_writefd = writefd;
}
/*
const char *decoded;
size_t decodedlen;
NMH_UNUSED(nsc);
+ int offset;
- int offset = context ? *((int *) context) : 0;
+ offset = context ? *((int *) context) : 0;
if (offset > 0) {
/*
if (nsc->ns_snoop) {
#ifdef CYRUS_SASL
if (nsc->sasl_seclayer)
- fprintf(stderr, "(sasl-encrypted) ");
+ fprintf(stderr, "(sasl-decrypted) ");
#endif /* CYRUS_SASL */
#ifdef TLS_SUPPORT
if (nsc->tls_active)
- fprintf(stderr, "(tls-encrypted) ");
+ fprintf(stderr, "(tls-decrypted) ");
#endif /* TLS_SUPPORT */
fprintf(stderr, "<= ");
if (nsc->ns_snoop_cb)
fd_set rfds;
FD_ZERO(&rfds);
- FD_SET(nsc->ns_fd, &rfds);
+ FD_SET(nsc->ns_readfd, &rfds);
tv.tv_sec = nsc->ns_timeout;
tv.tv_usec = 0;
- rc = select(nsc->ns_fd + 1, &rfds, NULL, NULL, &tv);
+ rc = select(nsc->ns_readfd + 1, &rfds, NULL, NULL, &tv);
if (rc == -1) {
netsec_err(errstr, "select() while reading failed: %s",
* select() above) so this read SHOULDN'T block. Hopefully.
*/
- rc = read(nsc->ns_fd, readbuf, readbufsize);
+ rc = read(nsc->ns_readfd, readbuf, readbufsize);
if (rc == 0) {
netsec_err(errstr, "Received EOF on network read");
if (size == 0)
return OK;
- /*
- * If TLS is active, then bypass all of our buffering logic; just
- * write it directly to our BIO. We have a buffering BIO first in
- * our stack, so buffering will take place there.
- */
-#ifdef TLS_SUPPORT
- if (nsc->tls_active) {
- rc = BIO_write(nsc->ssl_io, buffer, size);
-
- if (rc <= 0) {
- netsec_err(errstr, "Error writing to TLS connection: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return NOTOK;
- }
-
- return OK;
- }
-#endif /* TLS_SUPPORT */
-
/*
* Run a loop copying in data to our local buffer; when we're done with
* any buffer overflows then just copy any remaining data in.
{
int rc;
- /*
- * Again, if we're using TLS, then bypass our local buffering
- */
-#ifdef TLS_SUPPORT
- if (nsc->tls_active) {
- rc = BIO_vprintf(nsc->ssl_io, format, ap);
-
- if (rc <= 0) {
- netsec_err(errstr, "Error writing to TLS connection: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return NOTOK;
- }
-
- return OK;
- }
-#endif /* TLS_SUPPORT */
-
/*
* Cheat a little. If we can fit the data into our outgoing buffer,
* great! If not, generate a flush and retry once.
unsigned int netoutlen = nsc->ns_outbuflen;
int rc;
- /*
- * For TLS connections, just call BIO_flush(); we'll let TLS handle
- * all of our output buffering.
- */
-#ifdef TLS_SUPPORT
- if (nsc->tls_active) {
- rc = BIO_flush(nsc->ssl_io);
-
- if (rc <= 0) {
- netsec_err(errstr, "Error flushing TLS connection: %s",
- ERR_error_string(ERR_get_error(), NULL));
- return NOTOK;
- }
-
- return OK;
- }
-#endif /* TLS_SUPPORT */
-
/*
* Small optimization
*/
/*
* If SASL security layers are in effect, run the data through
- * sasl_encode() first and then write it.
+ * sasl_encode() first.
*/
#ifdef CYRUS_SASL
if (nsc->sasl_seclayer) {
}
#endif /* CYRUS_SASL */
- rc = write(nsc->ns_fd, netoutbuf, netoutlen);
- if (rc < 0) {
- netsec_err(errstr, "write() failed: %s", strerror(errno));
- return NOTOK;
+ /*
+ * If TLS is active, then use those functions to write out the
+ * data.
+ */
+#ifdef TLS_SUPPORT
+ if (nsc->tls_active) {
+ if (BIO_write(nsc->ssl_io, netoutbuf, netoutlen) <= 0) {
+ netsec_err(errstr, "Error writing to TLS connection: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NOTOK;
+ }
+ } else
+#endif /* TLS_SUPPORT */
+ {
+ rc = write(nsc->ns_writefd, netoutbuf, netoutlen);
+
+ if (rc < 0) {
+ netsec_err(errstr, "write() failed: %s", strerror(errno));
+ return NOTOK;
+ }
}
nsc->ns_outptr = nsc->ns_outbuffer;
return NOTOK;
}
- nsc->sasl_mech = mechanism ? getcpy(mechanism) : NULL;
+ /*
+ * According to the RFC, mechanisms can only be uppercase letter, numbers,
+ * and a hypen or underscore. So make sure we uppercase any letters
+ * in case the user passed in lowercase.
+ */
+
+ if (mechanism) {
+ char *p;
+ nsc->sasl_mech = getcpy(mechanism);
+
+ for (p = nsc->sasl_mech; *p; p++)
+ if (isascii((unsigned char) *p)) /* Just in case */
+ *p = toupper((unsigned char) *p);
+ }
+
nsc->sasl_proto_cb = callback;
nsc->sasl_hostname = getcpy(hostname);
if (tls) {
#ifdef TLS_SUPPORT
SSL *ssl;
- BIO *sbio, *ssl_bio;;
+ BIO *rbio, *wbio, *ssl_bio;;
if (! tls_initialized) {
SSL_library_init();
tls_initialized++;
}
- if (nsc->ns_fd == -1) {
+ if (nsc->ns_readfd == -1 || nsc->ns_writefd == -1) {
netsec_err(errstr, "Invalid file descriptor in netsec context");
return NOTOK;
}
* So writes and reads are buffered (we mostly care about writes).
*/
- sbio = BIO_new_socket(nsc->ns_fd, BIO_NOCLOSE);
+ rbio = BIO_new_socket(nsc->ns_readfd, BIO_NOCLOSE);
- if (! sbio) {
- netsec_err(errstr, "Unable to create a socket BIO: %s",
+ if (! rbio) {
+ netsec_err(errstr, "Unable to create a read socket BIO: %s",
ERR_error_string(ERR_get_error(), NULL));
SSL_free(ssl);
return NOTOK;
}
- SSL_set_bio(ssl, sbio, sbio);
- SSL_set_connect_state(ssl);
-
- nsc->ssl_io = BIO_new(BIO_f_buffer());
+ wbio = BIO_new_socket(nsc->ns_writefd, BIO_NOCLOSE);
- if (! nsc->ssl_io) {
- netsec_err(errstr, "Unable to create a buffer BIO: %s",
+ if (! wbio) {
+ netsec_err(errstr, "Unable to create a write socket BIO: %s",
ERR_error_string(ERR_get_error(), NULL));
SSL_free(ssl);
+ BIO_free(rbio);
return NOTOK;
}
+ SSL_set_bio(ssl, rbio, wbio);
+ SSL_set_connect_state(ssl);
+
ssl_bio = BIO_new(BIO_f_ssl());
if (! ssl_bio) {
}
BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
- BIO_push(nsc->ssl_io, ssl_bio);
+ nsc->ssl_io = ssl_bio;
return OK;
} else {
fprintf(stderr, "WARNING: cannot determine SSL ciphers\n");
} else {
const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
- fprintf(stderr, "TLS negotation successful: %s(%d) %s\n",
+ fprintf(stderr, "TLS negotiation successful: %s(%d) %s\n",
SSL_CIPHER_get_name(cipher),
SSL_CIPHER_get_bits(cipher, NULL),
SSL_CIPHER_get_version(cipher));
nsc->tls_active = 1;
- /*
- * At this point, TLS has been activated; we're not going to use
- * the output buffer, so free it now to save a little bit of memory.
- */
-
- if (nsc->ns_outbuffer) {
- free(nsc->ns_outbuffer);
- nsc->ns_outbuffer = NULL;
- }
-
return OK;
#else /* TLS_SUPPORT */
netsec_err(errstr, "TLS not supported");