]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-ssl-iostream: Add ssl_iostream_context cache
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 1 Nov 2017 23:04:00 +0000 (01:04 +0200)
committerTimo Sirainen <tss@dovecot.fi>
Mon, 6 Nov 2017 23:09:00 +0000 (01:09 +0200)
This can be used to easily get a shared ssl_iostream_context for either
server or client. There's no upper size limit for the cache.

src/lib-ssl-iostream/Makefile.am
src/lib-ssl-iostream/iostream-ssl-context-cache.c [new file with mode: 0644]
src/lib-ssl-iostream/iostream-ssl.h

index f6cb95ad88a9c2266d0f92bae9e353e955b39dbf..81707d1a27a7bcb76d456330f0396528789f2ade 100644 (file)
@@ -22,7 +22,8 @@ libssl_iostream_openssl_la_SOURCES = \
 endif
 
 libssl_iostream_la_SOURCES = \
-       iostream-ssl.c
+       iostream-ssl.c \
+       iostream-ssl-context-cache.c
 
 noinst_HEADERS = \
        dovecot-openssl-common.h
diff --git a/src/lib-ssl-iostream/iostream-ssl-context-cache.c b/src/lib-ssl-iostream/iostream-ssl-context-cache.c
new file mode 100644 (file)
index 0000000..78e352d
--- /dev/null
@@ -0,0 +1,129 @@
+/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "hash.h"
+#include "iostream-ssl-private.h"
+
+struct ssl_iostream_context_cache {
+       bool server;
+       struct ssl_iostream_settings set;
+};
+
+static pool_t ssl_iostream_contexts_pool;
+static HASH_TABLE(struct ssl_iostream_context_cache *,
+                 struct ssl_iostream_context *) ssl_iostream_contexts;
+
+static unsigned int
+ssl_iostream_context_cache_hash(const struct ssl_iostream_context_cache *cache)
+{
+       unsigned int n, i, g, h = 0;
+       const char *const cert[] = { cache->set.cert.cert, cache->set.alt_cert.cert };
+
+       /* checking for different certs is typically good enough,
+          and it should be enough to check only the first few bytes (after the
+          "BEGIN CERTIFICATE" line). */
+       for (n = 0; n < N_ELEMENTS(cert); n++) {
+               if (cert[n] == NULL)
+                       continue;
+
+               for (i = 0; i < 64 && cert[n][i] != '\0'; i++) {
+                       h = (h << 4) + cert[n][i];
+                       if ((g = h & 0xf0000000UL) != 0) {
+                               h = h ^ (g >> 24);
+                               h = h ^ g;
+                       }
+               }
+       }
+       return h ^ (cache->server ? 1 : 0);
+}
+
+static int
+ssl_iostream_context_cache_cmp(const struct ssl_iostream_context_cache *c1,
+                              const struct ssl_iostream_context_cache *c2)
+{
+       if (c1->server != c2->server)
+               return -1;
+       return ssl_iostream_settings_equals(&c1->set, &c2->set) ? 0 : -1;
+}
+
+static int
+ssl_iostream_context_cache_get(const struct ssl_iostream_settings *set,
+                              bool server,
+                              struct ssl_iostream_context **ctx_r,
+                              const char **error_r)
+{
+       struct ssl_iostream_context *ctx;
+       struct ssl_iostream_context_cache *cache;
+       struct ssl_iostream_context_cache lookup = {
+               .server = server,
+               .set = *set,
+       };
+
+       if (ssl_iostream_contexts_pool == NULL) {
+               ssl_iostream_contexts_pool =
+                       pool_alloconly_create(MEMPOOL_GROWING"ssl iostream context cache", 1024);
+               hash_table_create(&ssl_iostream_contexts,
+                                 ssl_iostream_contexts_pool, 0,
+                                 ssl_iostream_context_cache_hash,
+                                 ssl_iostream_context_cache_cmp);
+       }
+       ssl_iostream_settings_drop_stream_only(&lookup.set);
+
+       ctx = hash_table_lookup(ssl_iostream_contexts, &lookup);
+       if (ctx != NULL) {
+               ssl_iostream_context_ref(ctx);
+               *ctx_r = ctx;
+               return 0;
+       }
+
+       /* add to cache */
+       if (server) {
+               if (ssl_iostream_context_init_server(&lookup.set, &ctx, error_r) < 0)
+                       return -1;
+       } else {
+               if (ssl_iostream_context_init_client(&lookup.set, &ctx, error_r) < 0)
+                       return -1;
+       }
+
+       cache = p_new(ssl_iostream_contexts_pool,
+                     struct ssl_iostream_context_cache, 1);
+       cache->server = server;
+       ssl_iostream_settings_init_from(ssl_iostream_contexts_pool,
+                                       &cache->set, &lookup.set);
+       hash_table_insert(ssl_iostream_contexts, cache, ctx);
+
+       ssl_iostream_context_ref(ctx);
+       *ctx_r = ctx;
+       return 0;
+}
+
+int ssl_iostream_client_context_cache_get(const struct ssl_iostream_settings *set,
+                                         struct ssl_iostream_context **ctx_r,
+                                         const char **error_r)
+{
+       return ssl_iostream_context_cache_get(set, FALSE, ctx_r, error_r);
+}
+
+int ssl_iostream_server_context_cache_get(const struct ssl_iostream_settings *set,
+                                         struct ssl_iostream_context **ctx_r,
+                                         const char **error_r)
+{
+       return ssl_iostream_context_cache_get(set, TRUE, ctx_r, error_r);
+}
+
+void ssl_iostream_context_cache_free(void)
+{
+       struct hash_iterate_context *iter;
+       struct ssl_iostream_context_cache *lookup;
+       struct ssl_iostream_context *ctx;
+
+       if (ssl_iostream_contexts_pool == NULL)
+               return;
+
+       iter = hash_table_iterate_init(ssl_iostream_contexts);
+       while (hash_table_iterate(iter, ssl_iostream_contexts, &lookup, &ctx))
+               ssl_iostream_context_unref(&ctx);
+       hash_table_iterate_deinit(&iter);
+       hash_table_destroy(&ssl_iostream_contexts);
+       pool_unref(&ssl_iostream_contexts_pool);
+}
index 255495f1dc1432bdd80445a06cd0cb210367f9a3..a385f95043ecf41ab3f776ff92d06710341dbd46 100644 (file)
@@ -12,7 +12,8 @@ struct ssl_iostream_cert {
 
 struct ssl_iostream_settings {
        /* NOTE: when updating, remember to update:
-          ssl_iostream_settings_string_offsets[] */
+          ssl_iostream_settings_string_offsets[],
+          ssl_iostream_settings_drop_stream_only() */
        const char *protocols; /* both */
        const char *cipher_list; /* both */
        const char *curve_list; /* both */
@@ -103,4 +104,15 @@ void ssl_iostream_settings_init_from(pool_t pool,
                                     struct ssl_iostream_settings *dest,
                                     const struct ssl_iostream_settings *src);
 
+/* Persistent cache of ssl_iostream_contexts. The context is permanently stored
+   until ssl_iostream_context_cache_free() is called. The returned context
+   must be unreferenced by the caller. */
+int ssl_iostream_client_context_cache_get(const struct ssl_iostream_settings *set,
+                                         struct ssl_iostream_context **ctx_r,
+                                         const char **error_r);
+int ssl_iostream_server_context_cache_get(const struct ssl_iostream_settings *set,
+                                         struct ssl_iostream_context **ctx_r,
+                                         const char **error_r);
+void ssl_iostream_context_cache_free(void);
+
 #endif