]> diplodocus.org Git - nmh/commitdiff
A bit more code.
authorKen Hornstein <kenh@pobox.com>
Wed, 14 Sep 2016 04:00:48 +0000 (00:00 -0400)
committerKen Hornstein <kenh@pobox.com>
Wed, 14 Sep 2016 04:00:48 +0000 (00:00 -0400)
h/netsec.h
sbr/netsec.c

index 3daa19403e117d24441e3cd08fb2d98400364d94..f5abbcb5c80af27c22125a9360304c30e8faa531 100644 (file)
@@ -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
index 26e90c9cce30252867e6a697d87cb8322a2c8bd9..cd33df9d902f2cae52528b9bbc2197d688c3b4f1 100644 (file)
@@ -11,6 +11,7 @@
 #include <h/mh.h>
 #include <h/utils.h>
 #include <h/netsec.h>
+#include <stdarg.h>
 
 #ifdef CYRUS_SASL
 #include <sasl/sasl.h>
@@ -29,6 +30,7 @@
 #include <openssl/err.h>
 
 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;
+}