]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-ssl-iostream: Add TLS SNI callback and a way to change SSL context
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 1 Nov 2017 23:05:01 +0000 (01:05 +0200)
committerTimo Sirainen <tss@dovecot.fi>
Mon, 6 Nov 2017 23:09:00 +0000 (01:09 +0200)
src/lib-ssl-iostream/iostream-openssl-context.c
src/lib-ssl-iostream/iostream-openssl.c
src/lib-ssl-iostream/iostream-openssl.h
src/lib-ssl-iostream/iostream-ssl-private.h
src/lib-ssl-iostream/iostream-ssl.c
src/lib-ssl-iostream/iostream-ssl.h

index 940aebb44e5d95be50566b49defdebcb6fb30a50..d05e0c208a10ceef3e9e8c6dd9e446a89dd9dc50 100644 (file)
@@ -296,7 +296,7 @@ static int ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED,
                                   void *context ATTR_UNUSED)
 {
        struct ssl_iostream *ssl_io;
-       const char *host;
+       const char *host, *error;
 
        ssl_io = SSL_get_ex_data(ssl, dovecot_ssl_extdata_index);
        host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
@@ -306,6 +306,14 @@ static int ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED,
        } else if (ssl_io->verbose) {
                i_debug("SSL_get_servername() failed");
        }
+
+       if (ssl_io->sni_callback != NULL) {
+               if (ssl_io->sni_callback(ssl_io->sni_host, &error,
+                                        ssl_io->sni_context) < 0) {
+                       openssl_iostream_set_error(ssl_io, error);
+                       return SSL_TLSEXT_ERR_ALERT_FATAL;
+               }
+       }
        return SSL_TLSEXT_ERR_OK;
 }
 #endif
index 221ae0acd66d14e49ccfc8dcc08817018c367de9..e960f485ef546ce10488ad6f3aaed8c8be5141de 100644 (file)
@@ -10,8 +10,7 @@
 
 static void openssl_iostream_free(struct ssl_iostream *ssl_io);
 
-static void
-openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
+void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str)
 {
        if (ssl_io->verbose) {
                /* This error should normally be logged by lib-ssl-iostream's
@@ -661,6 +660,27 @@ openssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
        ssl_io->handshake_context = context;
 }
 
+static void
+openssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
+                                 ssl_iostream_sni_callback_t *callback,
+                                 void *context)
+{
+       ssl_io->sni_callback = callback;
+       ssl_io->sni_context = context;
+}
+
+static void
+openssl_iostream_change_context(struct ssl_iostream *ssl_io,
+                               struct ssl_iostream_context *ctx)
+{
+       if (ctx != ssl_io->ctx) {
+               SSL_set_SSL_CTX(ssl_io->ssl, ctx->ssl_ctx);
+               ssl_iostream_context_ref(ctx);
+               ssl_iostream_context_unref(&ssl_io->ctx);
+               ssl_io->ctx = ctx;
+       }
+}
+
 static void openssl_iostream_set_log_prefix(struct ssl_iostream *ssl_io,
                                            const char *prefix)
 {
@@ -789,6 +809,8 @@ static const struct iostream_ssl_vfuncs ssl_vfuncs = {
 
        .handshake = openssl_iostream_handshake,
        .set_handshake_callback = openssl_iostream_set_handshake_callback,
+       .set_sni_callback = openssl_iostream_set_sni_callback,
+       .change_context = openssl_iostream_change_context,
 
        .set_log_prefix = openssl_iostream_set_log_prefix,
        .is_handshaked = openssl_iostream_is_handshaked,
index d67de5207b0947b689999734c9dcc0b1619589c4..caa34ed1f27f0e8f025089e0e88333969be3373a 100644 (file)
@@ -56,6 +56,9 @@ struct ssl_iostream {
        ssl_iostream_handshake_callback_t *handshake_callback;
        void *handshake_context;
 
+       ssl_iostream_sni_callback_t *sni_callback;
+       void *sni_context;
+
        bool handshaked:1;
        bool handshake_failed:1;
        bool cert_received:1;
@@ -105,6 +108,7 @@ int openssl_iostream_handle_error(struct ssl_iostream *ssl_io, int ret,
                                  enum openssl_iostream_sync_type type,
                                  const char *func_name);
 
+void openssl_iostream_set_error(struct ssl_iostream *ssl_io, const char *str);
 const char *openssl_iostream_error(void);
 const char *openssl_iostream_key_load_error(void);
 const char *
index b5f16112e7d7688848f336856fa2b2cbbafbbaf1..19b581e28a02306a44205e40d465c6de7474a5b1 100644 (file)
@@ -24,6 +24,11 @@ struct iostream_ssl_vfuncs {
        void (*set_handshake_callback)(struct ssl_iostream *ssl_io,
                                       ssl_iostream_handshake_callback_t *callback,
                                       void *context);
+       void (*set_sni_callback)(struct ssl_iostream *ssl_io,
+                                ssl_iostream_sni_callback_t *callback,
+                                void *context);
+       void (*change_context)(struct ssl_iostream *ssl_io,
+                              struct ssl_iostream_context *ctx);
 
        void (*set_log_prefix)(struct ssl_iostream *ssl_io, const char *prefix);
        bool (*is_handshaked)(const struct ssl_iostream *ssl_io);
index 6656698c8012e73e1670c666070874a7f22c1dea..228c982eaf3aa6583302c34d6d6c09746b83c1fa 100644 (file)
@@ -167,6 +167,19 @@ void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
        ssl_vfuncs->set_handshake_callback(ssl_io, callback, context);
 }
 
+void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
+                                  ssl_iostream_sni_callback_t *callback,
+                                  void *context)
+{
+       ssl_vfuncs->set_sni_callback(ssl_io, callback, context);
+}
+
+void ssl_iostream_change_context(struct ssl_iostream *ssl_io,
+                                struct ssl_iostream_context *ctx)
+{
+       ssl_vfuncs->change_context(ssl_io, ctx);
+}
+
 bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io)
 {
        return ssl_vfuncs->is_handshaked(ssl_io);
index 86c569a5a7d6e7c18f9613987d461730c5341078..cee3810a100cfdcb65e8a6ebb6e423929192d1a1 100644 (file)
@@ -43,6 +43,9 @@ int ssl_module_load(const char **error_r);
    likely should be calling ssl_iostream_check_cert_validity(). */
 typedef int
 ssl_iostream_handshake_callback_t(const char **error_r, void *context);
+/* Called when TLS SNI becomes available. */
+typedef int ssl_iostream_sni_callback_t(const char *name, const char **error_r,
+                                       void *context);
 
 int io_stream_create_ssl_client(struct ssl_iostream_context *ctx, const char *host,
                                const struct ssl_iostream_settings *set,
@@ -71,6 +74,14 @@ int ssl_iostream_handshake(struct ssl_iostream *ssl_io);
 void ssl_iostream_set_handshake_callback(struct ssl_iostream *ssl_io,
                                         ssl_iostream_handshake_callback_t *callback,
                                         void *context);
+/* Call the given callback when client sends SNI. The callback can change the
+   ssl_iostream's context (with different certificates) by using
+   ssl_iostream_change_context(). */
+void ssl_iostream_set_sni_callback(struct ssl_iostream *ssl_io,
+                                  ssl_iostream_sni_callback_t *callback,
+                                  void *context);
+void ssl_iostream_change_context(struct ssl_iostream *ssl_io,
+                                struct ssl_iostream_context *ctx);
 
 bool ssl_iostream_is_handshaked(const struct ssl_iostream *ssl_io);
 /* Returns TRUE if the remote cert is invalid, or handshake callback returned