bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen#include "lib.h"
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen#include "hash.h"
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen#include "iostream-ssl-private.h"
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenstruct ssl_iostream_context_cache {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen bool server;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_settings set;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen};
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenstatic pool_t ssl_iostream_contexts_pool;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenstatic HASH_TABLE(struct ssl_iostream_context_cache *,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context *) ssl_iostream_contexts;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenstatic unsigned int
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenssl_iostream_context_cache_hash(const struct ssl_iostream_context_cache *cache)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen{
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen unsigned int n, i, g, h = 0;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen const char *const cert[] = { cache->set.cert.cert, cache->set.alt_cert.cert };
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen /* checking for different certs is typically good enough,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen and it should be enough to check only the first few bytes (after the
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen "BEGIN CERTIFICATE" line). */
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen for (n = 0; n < N_ELEMENTS(cert); n++) {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (cert[n] == NULL)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen continue;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen for (i = 0; i < 64 && cert[n][i] != '\0'; i++) {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen h = (h << 4) + cert[n][i];
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if ((g = h & 0xf0000000UL) != 0) {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen h = h ^ (g >> 24);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen h = h ^ g;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen }
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen }
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen }
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return h ^ (cache->server ? 1 : 0);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen}
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenstatic int
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenssl_iostream_context_cache_cmp(const struct ssl_iostream_context_cache *c1,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen const struct ssl_iostream_context_cache *c2)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen{
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (c1->server != c2->server)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return -1;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return ssl_iostream_settings_equals(&c1->set, &c2->set) ? 0 : -1;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen}
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenstatic int
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenssl_iostream_context_cache_get(const struct ssl_iostream_settings *set,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen bool server,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context **ctx_r,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen const char **error_r)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen{
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context *ctx;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context_cache *cache;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context_cache lookup = {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen .server = server,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen .set = *set,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen };
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (ssl_iostream_contexts_pool == NULL) {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_contexts_pool =
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen pool_alloconly_create(MEMPOOL_GROWING"ssl iostream context cache", 1024);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen hash_table_create(&ssl_iostream_contexts,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_contexts_pool, 0,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_context_cache_hash,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_context_cache_cmp);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen }
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_settings_drop_stream_only(&lookup.set);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ctx = hash_table_lookup(ssl_iostream_contexts, &lookup);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (ctx != NULL) {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_context_ref(ctx);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen *ctx_r = ctx;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return 0;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen }
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen /* add to cache */
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (server) {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (ssl_iostream_context_init_server(&lookup.set, &ctx, error_r) < 0)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return -1;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen } else {
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (ssl_iostream_context_init_client(&lookup.set, &ctx, error_r) < 0)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return -1;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen }
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen cache = p_new(ssl_iostream_contexts_pool,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context_cache, 1);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen cache->server = server;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_settings_init_from(ssl_iostream_contexts_pool,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen &cache->set, &lookup.set);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen hash_table_insert(ssl_iostream_contexts, cache, ctx);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_context_ref(ctx);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen *ctx_r = ctx;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return 0;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen}
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenint ssl_iostream_client_context_cache_get(const struct ssl_iostream_settings *set,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context **ctx_r,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen const char **error_r)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen{
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return ssl_iostream_context_cache_get(set, FALSE, ctx_r, error_r);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen}
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenint ssl_iostream_server_context_cache_get(const struct ssl_iostream_settings *set,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context **ctx_r,
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen const char **error_r)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen{
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return ssl_iostream_context_cache_get(set, TRUE, ctx_r, error_r);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen}
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainenvoid ssl_iostream_context_cache_free(void)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen{
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct hash_iterate_context *iter;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context_cache *lookup;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen struct ssl_iostream_context *ctx;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen if (ssl_iostream_contexts_pool == NULL)
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen return;
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen iter = hash_table_iterate_init(ssl_iostream_contexts);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen while (hash_table_iterate(iter, ssl_iostream_contexts, &lookup, &ctx))
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen ssl_iostream_context_unref(&ctx);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen hash_table_iterate_deinit(&iter);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen hash_table_destroy(&ssl_iostream_contexts);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen pool_unref(&ssl_iostream_contexts_pool);
86cc86047bee861a6f7fc3a9cfdb8600b984732eTimo Sirainen}