X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/735132076ce596a46936878532cbfe409b1be87d..44c4b1b168d0ac7c860f91495c423f425e8a87d9:/sbr/netsec.c diff --git a/sbr/netsec.c b/sbr/netsec.c index 3fd31854..895892a2 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -54,10 +54,12 @@ static SSL_CTX *sslctx = NULL; /* SSL Context */ */ 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_snoop_callback *ns_snoop_cb; /* Snoop output callback */ + void *ns_snoop_context; /* Context data for snoop function */ int ns_timeout; /* Network read timeout, in seconds */ char *ns_userid; /* Userid for authentication */ unsigned char *ns_inbuffer; /* Our read input buffer */ @@ -122,10 +124,12 @@ netsec_init(void) { 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; + nsc->ns_snoop_context = NULL; nsc->ns_userid = NULL; nsc->ns_timeout = 60; /* Our default */ nsc->ns_inbufsize = NETSEC_BUFSIZE; @@ -212,8 +216,12 @@ netsec_shutdown(netsec_context *nsc, int closeflag) 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); } @@ -223,9 +231,10 @@ netsec_shutdown(netsec_context *nsc, int closeflag) */ 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; } /* @@ -258,6 +267,52 @@ netsec_set_snoop(netsec_context *nsc, int snoop) nsc->ns_snoop = snoop; } +/* + * Set the snoop callback for this connection. + */ + +void netsec_set_snoop_callback(netsec_context *nsc, + netsec_snoop_callback callback, void *context) +{ + nsc->ns_snoop_cb = callback; + nsc->ns_snoop_context = context; +} + +/* + * A base64-decoding snoop callback + */ + +void +netsec_b64_snoop_decoder(netsec_context *nsc, const char *string, size_t len, + void *context) +{ + const char *decoded; + size_t decodedlen; + NMH_UNUSED(nsc); + int offset; + + offset = context ? *((int *) context) : 0; + + if (offset > 0) { + /* + * Output non-base64 data first. + */ + fprintf(stderr, "%.*s", offset, string); + string += offset; + len -= offset; + } + + if (decodeBase64(string, &decoded, &decodedlen, 1, NULL) == OK) { + char *hexified; + hexify((const unsigned char *) decoded, decodedlen, &hexified); + fprintf(stderr, "b64<%s>\n", hexified); + free(hexified); + free((char *) decoded); + } else { + fprintf(stderr, "%.*s\n", (int) len, string); + } +} + /* * Set the read timeout for this connection */ @@ -349,15 +404,16 @@ retry: 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) - nsc->ns_snoop_cb(nsc, sptr, strlen(sptr)); + nsc->ns_snoop_cb(nsc, sptr, strlen(sptr), + nsc->ns_snoop_context); else fprintf(stderr, "%s\n", sptr); } @@ -427,12 +483,12 @@ retry: 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", @@ -504,7 +560,7 @@ retry: * 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"); @@ -587,25 +643,6 @@ netsec_write(netsec_context *nsc, const void *buffer, size_t size, 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. @@ -672,23 +709,6 @@ netsec_vprintf(netsec_context *nsc, char **errstr, const char *format, { 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. @@ -750,7 +770,8 @@ retry: #endif /* TLS_SUPPORT */ fprintf(stderr, "=> "); if (nsc->ns_snoop_cb) - nsc->ns_snoop_cb(nsc, nsc->ns_outptr, outlen); + nsc->ns_snoop_cb(nsc, (char *) nsc->ns_outptr, outlen, + nsc->ns_snoop_context); else fprintf(stderr, "%.*s\n", outlen, nsc->ns_outptr); } else { @@ -776,24 +797,6 @@ netsec_flush(netsec_context *nsc, char **errstr) 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 */ @@ -803,7 +806,7 @@ netsec_flush(netsec_context *nsc, char **errstr) /* * 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) { @@ -818,11 +821,27 @@ netsec_flush(netsec_context *nsc, char **errstr) } #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; @@ -888,7 +907,21 @@ netsec_set_sasl_params(netsec_context *nsc, const char *hostname, 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); @@ -1045,7 +1078,6 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) i = (str[i] == NULL); - free(str); free(mlist); if (i) { @@ -1102,7 +1134,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) * we ignore it and send a blank message in response. We should * then get either an +OK or -ERR */ - free(errstr); + free(*errstr); nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0, NULL); rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, errstr); @@ -1334,7 +1366,7 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr) if (tls) { #ifdef TLS_SUPPORT SSL *ssl; - BIO *sbio, *ssl_bio;; + BIO *rbio, *wbio, *ssl_bio;; if (! tls_initialized) { SSL_library_init(); @@ -1360,7 +1392,7 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr) 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; } @@ -1400,27 +1432,28 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr) * 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) { @@ -1431,7 +1464,7 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr) } BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE); - BIO_push(nsc->ssl_io, ssl_bio); + nsc->ssl_io = ssl_bio; return OK; } else { @@ -1474,7 +1507,7 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr) 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)); @@ -1483,16 +1516,6 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr) 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");