diff options
Diffstat (limited to 'src/lib-ssl-iostream/iostream-ssl-context-cache.c')
-rw-r--r-- | src/lib-ssl-iostream/iostream-ssl-context-cache.c | 129 |
1 files changed, 129 insertions, 0 deletions
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 index 0000000..a7245ce --- /dev/null +++ b/src/lib-ssl-iostream/iostream-ssl-context-cache.c @@ -0,0 +1,129 @@ +/* Copyright (c) 2017-2018 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); +} |