diff options
Diffstat (limited to '')
-rw-r--r-- | modules/tls/tls_cache.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/modules/tls/tls_cache.c b/modules/tls/tls_cache.c new file mode 100644 index 0000000..de4be18 --- /dev/null +++ b/modules/tls/tls_cache.c @@ -0,0 +1,310 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <assert.h> +#include <apr_lib.h> +#include <apr_strings.h> +#include <apr_hash.h> + +#include <httpd.h> +#include <http_connection.h> +#include <http_log.h> +#include <ap_socache.h> +#include <util_mutex.h> + +#include <rustls.h> + +#include "tls_conf.h" +#include "tls_core.h" +#include "tls_cache.h" + +extern module AP_MODULE_DECLARE_DATA tls_module; +APLOG_USE_MODULE(tls); + +#define TLS_CACHE_DEF_PROVIDER "shmcb" +#define TLS_CACHE_DEF_DIR "tls" +#define TLS_CACHE_DEF_FILE "session_cache" +#define TLS_CACHE_DEF_SIZE 512000 + +static const char *cache_provider_unknown(const char *name, apr_pool_t *p) +{ + apr_array_header_t *known; + const char *known_names; + + known = ap_list_provider_names(p, AP_SOCACHE_PROVIDER_GROUP, + AP_SOCACHE_PROVIDER_VERSION); + known_names = apr_array_pstrcat(p, known, ','); + return apr_psprintf(p, "cache type '%s' not supported " + "(known names: %s). Maybe you need to load the " + "appropriate socache module (mod_socache_%s?).", + name, known_names, name); +} + +void tls_cache_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) +{ + (void)plog; + (void)ptemp; + /* we make this visible, in case someone wants to configure it. + * this does not mean that we will really use it, which is determined + * by configuration and cache provider capabilities. */ + ap_mutex_register(pconf, TLS_SESSION_CACHE_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0); +} + +static const char *cache_init(tls_conf_global_t *gconf, apr_pool_t *p, apr_pool_t *ptemp) +{ + const char *err = NULL; + const char *name, *args = NULL; + apr_status_t rv; + + if (gconf->session_cache) { + goto cleanup; + } + else if (!apr_strnatcasecmp("none", gconf->session_cache_spec)) { + gconf->session_cache_provider = NULL; + gconf->session_cache = NULL; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, gconf->ap_server, APLOGNO(10346) + "session cache explicitly disabled"); + goto cleanup; + } + else if (!apr_strnatcasecmp("default", gconf->session_cache_spec)) { + const char *path = TLS_CACHE_DEF_DIR; + +#if AP_MODULE_MAGIC_AT_LEAST(20180906, 2) + path = ap_state_dir_relative(p, path); +#endif + gconf->session_cache_spec = apr_psprintf(p, "%s:%s/%s(%ld)", + TLS_CACHE_DEF_PROVIDER, path, TLS_CACHE_DEF_FILE, (long)TLS_CACHE_DEF_SIZE); + gconf->session_cache_spec = "shmcb:mod_tls-sesss(64000)"; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, gconf->ap_server, APLOGNO(10347) + "Using session cache: %s", gconf->session_cache_spec); + name = gconf->session_cache_spec; + args = ap_strchr((char*)name, ':'); + if (args) { + name = apr_pstrmemdup(p, name, (apr_size_t)(args - name)); + ++args; + } + gconf->session_cache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP, + name, AP_SOCACHE_PROVIDER_VERSION); + if (!gconf->session_cache_provider) { + err = cache_provider_unknown(name, p); + goto cleanup; + } + err = gconf->session_cache_provider->create(&gconf->session_cache, args, ptemp, p); + if (err != NULL) goto cleanup; + + if (gconf->session_cache_provider->flags & AP_SOCACHE_FLAG_NOTMPSAFE + && !gconf->session_cache_mutex) { + /* we need a global lock to access the cache */ + rv = ap_global_mutex_create(&gconf->session_cache_mutex, NULL, + TLS_SESSION_CACHE_MUTEX_TYPE, NULL, gconf->ap_server, p, 0); + if (APR_SUCCESS != rv) { + err = apr_psprintf(p, "error setting up global %s mutex: %d", + TLS_SESSION_CACHE_MUTEX_TYPE, rv); + gconf->session_cache_mutex = NULL; + goto cleanup; + } + } + +cleanup: + if (NULL != err) { + gconf->session_cache_provider = NULL; + gconf->session_cache = NULL; + } + return err; +} + +const char *tls_cache_set_specification( + const char *spec, tls_conf_global_t *gconf, apr_pool_t *p, apr_pool_t *ptemp) +{ + gconf->session_cache_spec = spec; + return cache_init(gconf, p, ptemp); +} + +apr_status_t tls_cache_post_config(apr_pool_t *p, apr_pool_t *ptemp, server_rec *s) +{ + tls_conf_server_t *sc = tls_conf_server_get(s); + const char *err; + apr_status_t rv = APR_SUCCESS; + + err = cache_init(sc->global, p, ptemp); + if (err) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10348) + "session cache [%s] could not be initialized, will continue " + "without session one. Since this will impact performance, " + "consider making use of the 'TLSSessionCache' directive. The " + "error was: %s", sc->global->session_cache_spec, err); + } + + if (sc->global->session_cache) { + struct ap_socache_hints hints; + + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "provider init session cache [%s]", + sc->global->session_cache_spec); + memset(&hints, 0, sizeof(hints)); + hints.avg_obj_size = 100; + hints.avg_id_len = 33; + hints.expiry_interval = 30; + + rv = sc->global->session_cache_provider->init( + sc->global->session_cache, "mod_tls-sess", &hints, s, p); + if (APR_SUCCESS != rv) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10349) + "error initializing session cache."); + } + } + return rv; +} + +void tls_cache_init_child(apr_pool_t *p, server_rec *s) +{ + tls_conf_server_t *sc = tls_conf_server_get(s); + const char *lockfile; + apr_status_t rv; + + if (sc->global->session_cache_mutex) { + lockfile = apr_global_mutex_lockfile(sc->global->session_cache_mutex); + rv = apr_global_mutex_child_init(&sc->global->session_cache_mutex, lockfile, p); + if (APR_SUCCESS != rv) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(10350) + "Cannot reinit %s mutex (file `%s`)", + TLS_SESSION_CACHE_MUTEX_TYPE, lockfile? lockfile : "-"); + } + } +} + +void tls_cache_free(server_rec *s) +{ + tls_conf_server_t *sc = tls_conf_server_get(s); + if (sc->global->session_cache_provider) { + sc->global->session_cache_provider->destroy(sc->global->session_cache, s); + } +} + +static void tls_cache_lock(tls_conf_global_t *gconf) +{ + if (gconf->session_cache_mutex) { + apr_status_t rv = apr_global_mutex_lock(gconf->session_cache_mutex); + if (APR_SUCCESS != rv) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, gconf->ap_server, APLOGNO(10351) + "Failed to acquire TLS session cache lock"); + } + } +} + +static void tls_cache_unlock(tls_conf_global_t *gconf) +{ + if (gconf->session_cache_mutex) { + apr_status_t rv = apr_global_mutex_unlock(gconf->session_cache_mutex); + if (APR_SUCCESS != rv) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, gconf->ap_server, APLOGNO(10352) + "Failed to release TLS session cache lock"); + } + } +} + +static rustls_result tls_cache_get( + void *userdata, + const rustls_slice_bytes *key, + int remove_after, + unsigned char *buf, + size_t count, + size_t *out_n) +{ + conn_rec *c = userdata; + tls_conf_conn_t *cc = tls_conf_conn_get(c); + tls_conf_server_t *sc = tls_conf_server_get(cc->server); + apr_status_t rv = APR_ENOENT; + unsigned int vlen, klen; + const unsigned char *kdata; + + if (!sc->global->session_cache) goto not_found; + tls_cache_lock(sc->global); + + kdata = key->data; + klen = (unsigned int)key->len; + vlen = (unsigned int)count; + rv = sc->global->session_cache_provider->retrieve( + sc->global->session_cache, cc->server, kdata, klen, buf, &vlen, c->pool); + + if (APLOGctrace4(c)) { + apr_ssize_t n = klen; + ap_log_cerror(APLOG_MARK, APLOG_TRACE4, rv, c, "retrieve key %d[%8x], found %d val", + klen, apr_hashfunc_default((const char*)kdata, &n), vlen); + } + if (remove_after || (APR_SUCCESS != rv && !APR_STATUS_IS_NOTFOUND(rv))) { + sc->global->session_cache_provider->remove( + sc->global->session_cache, cc->server, key->data, klen, c->pool); + } + + tls_cache_unlock(sc->global); + if (APR_SUCCESS != rv) goto not_found; + cc->session_id_cache_hit = 1; + *out_n = count; + return RUSTLS_RESULT_OK; + +not_found: + *out_n = 0; + return RUSTLS_RESULT_NOT_FOUND; +} + +static rustls_result tls_cache_put( + void *userdata, + const rustls_slice_bytes *key, + const rustls_slice_bytes *val) +{ + conn_rec *c = userdata; + tls_conf_conn_t *cc = tls_conf_conn_get(c); + tls_conf_server_t *sc = tls_conf_server_get(cc->server); + apr_status_t rv = APR_ENOENT; + apr_time_t expires_at; + unsigned int klen, vlen; + const unsigned char *kdata; + + if (!sc->global->session_cache) goto not_stored; + tls_cache_lock(sc->global); + + expires_at = apr_time_now() + apr_time_from_sec(300); + kdata = key->data; + klen = (unsigned int)key->len; + vlen = (unsigned int)val->len; + rv = sc->global->session_cache_provider->store(sc->global->session_cache, cc->server, + kdata, klen, expires_at, + (unsigned char*)val->data, vlen, c->pool); + if (APLOGctrace4(c)) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE4, rv, c, + "stored %d key bytes, with %d val bytes", klen, vlen); + } + tls_cache_unlock(sc->global); + if (APR_SUCCESS != rv) goto not_stored; + return RUSTLS_RESULT_OK; + +not_stored: + return RUSTLS_RESULT_NOT_FOUND; +} + +apr_status_t tls_cache_init_server( + rustls_server_config_builder *builder, server_rec *s) +{ + tls_conf_server_t *sc = tls_conf_server_get(s); + + if (sc && sc->global->session_cache) { + ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s, "adding session persistence to rustls"); + rustls_server_config_builder_set_persistence( + builder, tls_cache_get, tls_cache_put); + } + return APR_SUCCESS; +} |