X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/d98775ebddf66dabdc0e46153f6d4308ff015054..2ebd126892a99580042a4c52ad90054114d49a2f:/sbr/netsec.c diff --git a/sbr/netsec.c b/sbr/netsec.c index 9fc6f465..18bb97ac 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -99,6 +99,12 @@ struct _netsec_context { static int netsec_fillread(netsec_context *ns_context, char **errstr); +/* + * Code to check the ASCII content of a byte array. + */ + +static int checkascii(const unsigned char *byte, size_t len); + /* * How this code works, in general. * @@ -231,7 +237,7 @@ netsec_shutdown(netsec_context *nsc, int closeflag) */ void -netsec_set_fd(netsec_context *nsc, int readfd, writefd) +netsec_set_fd(netsec_context *nsc, int readfd, int writefd) { nsc->ns_readfd = readfd; nsc->ns_writefd = writefd; @@ -286,11 +292,12 @@ void netsec_b64_snoop_decoder(netsec_context *nsc, const char *string, size_t len, void *context) { - const char *decoded; + unsigned char *decoded; size_t decodedlen; + int offset; NMH_UNUSED(nsc); - int offset = context ? *((int *) context) : 0; + offset = context ? *((int *) context) : 0; if (offset > 0) { /* @@ -302,16 +309,48 @@ netsec_b64_snoop_decoder(netsec_context *nsc, const char *string, size_t len, } 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); + /* + * Some mechanisms preoduce large binary tokens, which aren't really + * readable. So let's do a simple heuristic. If the token is greater + * than 100 characters _and_ the first 100 bytes are more than 50% + * non-ASCII, then don't print the decoded buffer, just the + * base64 text. + */ + if (decodedlen > 100 && !checkascii(decoded, 100)) { + fprintf(stderr, "%.*s\n", (int) len, string); + } else { + char *hexified; + hexify(decoded, decodedlen, &hexified); + fprintf(stderr, "b64<%s>\n", hexified); + free(hexified); + } + free(decoded); } else { fprintf(stderr, "%.*s\n", (int) len, string); } } +/* + * If the ASCII content is > 50%, return 1 + */ + +static int +checkascii(const unsigned char *bytes, size_t len) +{ + size_t count = 0, half = len / 2; + + while (len-- > 0) { + if (isascii(*bytes) && isprint(*bytes) && ++count > half) + return 1; + bytes++; + /* No chance by this point */ + if (count + len < half) + return 0; + } + + return 0; +} + /* * Set the read timeout for this connection */ @@ -403,11 +442,11 @@ 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) @@ -642,25 +681,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. @@ -727,23 +747,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. @@ -832,24 +835,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 */ @@ -859,7 +844,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) { @@ -874,11 +859,27 @@ netsec_flush(netsec_context *nsc, char **errstr) } #endif /* CYRUS_SASL */ - rc = write(nsc->ns_writefd, 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; @@ -944,7 +945,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); @@ -1477,15 +1492,6 @@ netsec_set_tls(netsec_context *nsc, int tls, char **errstr) SSL_set_bio(ssl, rbio, wbio); SSL_set_connect_state(ssl); - nsc->ssl_io = BIO_new(BIO_f_buffer()); - - if (! nsc->ssl_io) { - netsec_err(errstr, "Unable to create a buffer BIO: %s", - ERR_error_string(ERR_get_error(), NULL)); - SSL_free(ssl); - return NOTOK; - } - ssl_bio = BIO_new(BIO_f_ssl()); if (! ssl_bio) { @@ -1496,7 +1502,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 { @@ -1539,7 +1545,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)); @@ -1548,16 +1554,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");