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
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.
*/
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
* 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
#include <h/mh.h>
#include <h/utils.h>
#include <h/netsec.h>
+#include <stdarg.h>
#ifdef CYRUS_SASL
#include <sasl/sasl.h>
#include <openssl/err.h>
static int tls_initialized = 0;
+static SSL_CTX *sslctx = NULL; /* SSL Context */
#endif /* TLS_SUPPORT */
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.
*
* 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 *
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;
+}