+
+/*
+ * 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;
+}