From: Ken Hornstein Date: Wed, 14 Sep 2016 04:00:48 +0000 (-0400) Subject: A bit more code. X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/cfa78ce60b0f0cc87a441357d8e56e6c0ad80c65?ds=inline;hp=-c A bit more code. --- cfa78ce60b0f0cc87a441357d8e56e6c0ad80c65 diff --git a/h/netsec.h b/h/netsec.h index 3daa1940..f5abbcb5 100644 --- a/h/netsec.h +++ b/h/netsec.h @@ -16,14 +16,15 @@ typedef struct _netsec_context netsec_context; netsec_context *netsec_init(void); /* - * Free()s an allocated netsec context and all associated resources. + * Shuts down the security context for a connection and frees all + * associated resources. * * Arguments: * * ns_context - Network security context */ -void netsec_free(netsec_context *ns_context); +void netsec_shutdown(netsec_context *ns_context); /* * Sets the file descriptor for this connection. This will be used by @@ -49,6 +50,55 @@ void netset_set_fd(netsec_context *ns_context, int fd); void netsec_set_snoop(netsec_context *ns_context, int snoop); +/* + * Read a "line" from the network. This reads one CR/LF terminated line. + * Returns a pointer to a NUL-terminated string. This memory is valid + * until the next call to any read function. Will return an error if + * the line does not terminated with CR/LF. Note that this will not work + * if the data might have embedded NULs. + * + * Arguments: + * + * ns_context - Network security context + * errstr - Error string + * + * Returns pointer to string, or NULL on error. + */ + +char *netsec_readline(netsec_context *ns_context, char **errstr); + +/* + * Read bytes from the network. + * + * Arguments: + * + * ns_context - Network security context + * buffer - Read buffer + * size - Buffer size + * errstr - Error size + * + * Returns number of bytes read, or -1 on error. + */ + +ssize_t netsec_read(netsec_context *ns_context, void *buffer, size_t size, + char **errstr); + +/* + * Write bytes using printf formatting + * + * Arguments: + * + * ns_context - Network security context + * errstr - Error string + * format - Format string + * ... - Arguments for format string + * + * Returns OK on success, NOTOK on error. + */ + +int netsec_printf(netsec_context *ns_context, char **errstr, + const char *format, ...); + /* * Enumerated types for the type of message we are sending/receiving. */ @@ -134,7 +184,7 @@ typedef int (*netsec_sasl_callback)(enum sasl_message_type mtype, int netsec_set_sasl_params(netsec_context *ns_context, const char *service, const char *mechanism, - netsec_sasl_callback *callback); + netsec_sasl_callback callback); /* * Set the OAuth service name used to retrieve the OAuth parameters from @@ -163,10 +213,10 @@ int netsec_set_oauth_service(netsec_context *ns_context, const char *service); * tls - If nonzero, enable TLS. Otherwise disable TLS * negotiation. * - * Returns NOTOK if TLS is not supported. + * Returns NOTOK if TLS is not supported or was unable to initialize. */ -int netsec_set_tls(netsec_context *context, int tls); +int netsec_set_tls(netsec_context *context, int tls, char **errstr); /* * Start TLS negotiation on this protocol. This connection should have diff --git a/sbr/netsec.c b/sbr/netsec.c index 26e90c9c..cd33df9d 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef CYRUS_SASL #include @@ -29,6 +30,7 @@ #include static int tls_initialized = 0; +static SSL_CTX *sslctx = NULL; /* SSL Context */ #endif /* TLS_SUPPORT */ @@ -49,16 +51,24 @@ struct _netsec_context { unsigned int ns_outbuflen; /* Output buffer data length */ unsigned int ns_outbufsize; /* Output buffer size */ #ifdef CYRUS_SASL + char *sasl_service; /* SASL service name */ + char *sasl_mech; /* User-requested mechanism */ sasl_conn_t *sasl_conn; /* SASL connection context */ sasl_ssf_t sasl_ssf; /* SASL Security Strength Factor */ + netsec_sasl_callback sasl_cb; /* SASL callback */ #endif /* CYRUS_SASL */ #ifdef TLS_SUPPORT - SSL_CTX *sslctx; /* SSL Context */ - SSL *ssl; /* SSL connection information */ BIO *ssl_io; /* BIO used for connection I/O */ + int tls_active; /* If true, TLS is running */ #endif /* TLS_SUPPORT */ }; +/* + * Function to allocate error message strings + */ + +static char *netsec_errstr(const char *format, ...); + /* * How this code works, in general. * @@ -69,6 +79,14 @@ struct _netsec_context { * 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. + * + * 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). + */ + +/* + * Allocate and initialize our security context */ netsec_context * @@ -78,6 +96,180 @@ netsec_init(void) nsc->ns_fd = -1; nsc->ns_snoop = 0; - + nsc->ns_inbuffer = nsc->ns_inptr = NULL; + nsc->ns_inbuflen = nsc->ns_inbufsize = 0; + nsc->ns_outbuffer = nsc->ns_outptr = NULL; + nsc->ns_outbuflen = nsc->ns_outbufsize = 0; +#ifdef CYRUS_SASL + nsc->sasl_conn = NULL; + nsc->sasl_service = nsc->sasl_mech = NULL; +#endif /* CYRUS_SASL */ +#ifdef TLS_SUPPORT + nsc->ssl_io = NULL; + nsc->tls_active = 0; +#endif /* TLS_SUPPORT */ return nsc; } + +/* + * Set the file descriptor for our context + */ + +void +netsec_set_fd(netsec_context *nsc, int fd) +{ + nsc->ns_fd = fd; +} + +/* + * Set the snoop flag for this connection + */ + +void +netsec_set_snoop(netsec_context *nsc, int snoop) +{ + nsc->ns_snoop = snoop; +} + +/* + * Initialize (and enable) TLS for this connection + */ + +int +netsec_set_tls(netsec_context *nsc, int tls, char **errstr) +{ + if (tls) { +#ifdef TLS_SUPPORT + SSL *ssl; + BIO *sbio, *ssl_bio;; + + if (! tls_initialized) { + SSL_library_init(); + SSL_load_error_strings(); + + /* + * Create the SSL context; this has the properties for all + * SSL connections (we are only doing one), though. Make sure + * we only support secure (known as of now) TLS protocols. + */ + + sslctx = SSL_CTX_new(SSLv23_client_method()); + + if (! sslctx) { + *errstr = netsec_errstr("Unable to initialize OpenSSL " + "context: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NOTOK; + } + + SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_TLSv1); + + tls_initialized++; + } + + if (nsc->ns_fd == -1) { + *errstr = netsec_errstr("Invalid file descriptor in netsec " + "context"); + return NOTOK; + } + + /* + * Create the SSL structure which holds the data for a single + * TLS connection. + */ + + ssl = SSL_new(sslctx); + + if (! ssl) { + *errstr = netsec_errstr("Unable to create SSL connection: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NOTOK; + } + + /* + * This is a bit weird, so pay attention. + * + * We create a socket BIO, and bind it to our SSL connection. + * That means reads and writes to the SSL connection will use our + * supplied socket. + * + * Then we create an SSL BIO, and assign our current SSL connection + * to it. We then create a buffer BIO and push it in front of our + * SSL BIO. So the chain looks like: + * + * buffer BIO -> SSL BIO -> socket BIO. + * + * So writes and reads are buffered (we mostly care about writes). + */ + + sbio = BIO_new_socket(nsc->ns_fd, BIO_NOCLOSE); + + if (! sbio) { + *errstr = netsec_errstr("Unable to create a 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()); + + if (! nsc->ssl_io) { + *errstr = netsec_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) { + *errstr = netsec_errstr("Unable to create a SSL BIO: %s", + ERR_error_string(ERR_get_error(), NULL)); + SSL_free(ssl); + return NOTOK; + } + + BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE); + BIO_push(nsc->ssl_io, ssl_bio); + + return OK; + } else { + BIO_free_all(nsc->ssl_io); + nsc->ssl_io = NULL; + + return OK; + } +#else /* TLS_SUPPORT */ + *errstr = netsec_errstr("TLS is not supported"); + + return NOTOK; + } +#endif /* TLS_SUPPORT */ +} + +/* + * Generate an (allocated) error string + */ + +static char * +netsec_errstr(const char *fmt, ...) +{ + va_list ap; + size_t errbufsize; + char *errbuf = NULL; + int rc = 127; + + do { + errbufsize = rc + 1; + errbuf = mh_xrealloc(errbuf, errbufsize); + va_start(ap, fmt); + rc = vsnprintf(errbuf, errbufsize, fmt, ap); + va_end(ap); + } while (rc >= (int) errbufsize); + + return errbuf; +}