source: mod_gnutls/src/gnutls_cache.c

mod_gnutls/0.10.0
Last change on this file was 556783e, checked in by Fiona Klute <fiona.klute@…>, 7 months ago

Provide OCSP response via gnutls_certificate_retrieve_function3 callback

This replaces the old OCSP callback function, which is still used
internally.

  • Property mode set to 100644
File size: 17.9 KB
RevLine 
[14548b9]1/*
[fcb122d]2 *  Copyright 2004-2005 Paul Querna
[e183628]3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
[a85de63]5 *  Copyright 2015-2018 Fiona Klute
[0b3bc05]6 *
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 */
19
[14548b9]20/**
21 * @file gnutls_cache.c
22 *
[adf36c3]23 * This file contains the cache implementation used for session
24 * caching and OCSP stapling. The `socache_*_session` functions
25 * implement the GnuTLS session cache API using the configured cache,
26 * using mgs_cache_store() and mgs_cache_fetch() as appropriate (see
27 * gnutls_cache.h).
[14548b9]28 */
[3e22b82]29
[04e6e65]30#include "gnutls_cache.h"
[0b3bc05]31#include "mod_gnutls.h"
[c39ae1a]32#include "gnutls_config.h"
[babdb29]33#include "gnutls_ocsp.h"
[6e0bfd6]34
[de1ceab]35#include <ap_socache.h>
[6d8c00c]36#include <apr_strings.h>
[3aff94d]37#include <mod_status.h>
[f450ac9]38#include <apr_escape.h>
[c005645]39#include <util_mutex.h>
[0b3bc05]40
[14548b9]41/** Default session cache timeout */
[c005645]42#define MGS_DEFAULT_CACHE_TIMEOUT 300
43
[2d454a2]44/** Session cache name */
45#define MGS_SESSION_CACHE_NAME "gnutls_session"
46
[994200a]47/** Default type for OCSP cache */
48#define DEFAULT_OCSP_CACHE_TYPE "shmcb"
49/** Default config string for OCSP cache */
[0d7660d]50#define DEFAULT_OCSP_CACHE_CONF "gnutls_ocsp_cache"
[994200a]51
[14548b9]52/** Maximum length of the hex string representation of a GnuTLS
53 * session ID: two characters per byte, plus one more for `\0` */
[f450ac9]54#define GNUTLS_SESSION_ID_STRING_LEN ((GNUTLS_MAX_SESSION_ID_SIZE * 2) + 1)
[6e0bfd6]55
[55dc3f0]56#ifdef APLOG_USE_MODULE
57APLOG_USE_MODULE(gnutls);
58#endif
59
[14548b9]60/**
[adf36c3]61 * Turn a GnuTLS session ID into the key format we use for
[14548b9]62 * caches. Name the Session ID as `server:port.SessionID` to disallow
63 * resuming sessions on different servers.
64 *
65 * @return `0` on success, `-1` on failure
[c055502]66 */
[2f932fa]67static int mgs_session_id2dbm(conn_rec *c, unsigned char *id, int idlen,
68                              gnutls_datum_t *dbmkey)
69{
[f450ac9]70    char sz[GNUTLS_SESSION_ID_STRING_LEN];
71    apr_status_t rv = apr_escape_hex(sz, id, idlen, 0, NULL);
72    if (rv != APR_SUCCESS)
[e183628]73        return -1;
74
[2f932fa]75    char *newkey = apr_psprintf(c->pool, "%s:%d.%s",
76                                c->base_server->server_hostname,
77                                c->base_server->port, sz);
78    dbmkey->size = strlen(newkey);
79    /* signedness does not matter for arbitrary bits */
80    dbmkey->data = (unsigned char*) newkey;
[e183628]81    return 0;
[c055502]82}
[7bebb42]83
[14548b9]84/** The OPENSSL_TIME_FORMAT macro and mgs_time2sz() serve to print
85 * time in a format compatible with OpenSSL's `ASN1_TIME_print()`
[0831437]86 * function. */
87#define OPENSSL_TIME_FORMAT "%b %d %k:%M:%S %Y %Z"
88
89char *mgs_time2sz(time_t in_time, char *str, int strsize)
90{
[e183628]91    apr_time_exp_t vtm;
92    apr_size_t ret_size;
93    apr_time_t t;
[e02dd8c]94
95
[e183628]96    apr_time_ansi_put(&t, in_time);
97    apr_time_exp_gmt(&vtm, t);
[0831437]98    apr_strftime(str, &ret_size, strsize - 1, OPENSSL_TIME_FORMAT, &vtm);
[e183628]99
100    return str;
[7bebb42]101}
[6e0bfd6]102
[a66e147]103
[e183628]104
[41f9bcb]105int mgs_cache_store(mgs_cache_t cache, server_rec *server,
106                    gnutls_datum_t key, gnutls_datum_t data,
107                    apr_time_t expiry)
[de1ceab]108{
109    apr_pool_t *spool;
110    apr_pool_create(&spool, NULL);
[a66e147]111
[41f9bcb]112    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
113        apr_global_mutex_lock(cache->mutex);
114    apr_status_t rv = cache->prov->store(cache->socache, server,
115                                         key.data, key.size,
116                                         expiry,
117                                         data.data, data.size,
118                                         spool);
119    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
120        apr_global_mutex_unlock(cache->mutex);
[e183628]121
[3e22b82]122    if (rv != APR_SUCCESS)
123    {
[de1ceab]124        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, server,
125                     "error storing in cache '%s:%s'",
[78d75ac]126                     cache->prov->name, cache->config);
[de1ceab]127        apr_pool_destroy(spool);
[e183628]128        return -1;
[3e22b82]129    }
[e183628]130
[de1ceab]131    ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
132                 "stored %u bytes of data (%u byte key) in cache '%s:%s'",
133                 data.size, key.size,
[78d75ac]134                 cache->prov->name, cache->config);
[de1ceab]135    apr_pool_destroy(spool);
[3e22b82]136    return 0;
137}
[e183628]138
[3e22b82]139
140
[adf36c3]141/**
142 * Store function for the GnuTLS session cache, see
143 * gnutls_db_set_store_function().
144 *
145 * @param baton mgs_handle_t for the connection, as set via
146 * gnutls_db_set_ptr()
147 *
148 * @param key object key to store
149 *
150 * @param data the object to store
151 *
152 * @return `0` in case of success, `-1` in case of failure
153 */
[de1ceab]154static int socache_store_session(void *baton, gnutls_datum_t key,
155                                 gnutls_datum_t data)
[3e22b82]156{
157    mgs_handle_t *ctxt = baton;
[de1ceab]158    gnutls_datum_t dbmkey;
[a66e147]159
[de1ceab]160    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
[e183628]161        return -1;
[c005645]162
[de1ceab]163    apr_time_t expiry = apr_time_now() + ctxt->sc->cache_timeout;
[e183628]164
[ded2291]165    return mgs_cache_store(ctxt->sc->cache, ctxt->c->base_server,
166                           dbmkey, data, expiry);
[fcb122d]167}
168
[a85de63]169
170
[d8d6b1e]171/** 8K is the maximum size accepted when receiving OCSP responses,
172 * sessions cache entries should be much smaller. The buffer is
173 * reallocated to actual size after fetching, so memory waste is
174 * minimal and temporary. */
175#define SOCACHE_FETCH_BUF_SIZE (8 * 1024)
176
[556783e]177apr_status_t mgs_cache_fetch(mgs_cache_t cache, server_rec *server,
178                             gnutls_datum_t key, gnutls_datum_t *output,
179                             apr_pool_t *pool)
[15245bf]180{
[de1ceab]181    apr_pool_t *spool;
182    apr_pool_create(&spool, pool);
[e183628]183
[41f9bcb]184    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
185        apr_global_mutex_lock(cache->mutex);
186    apr_status_t rv = cache->prov->retrieve(cache->socache, server,
187                                            key.data, key.size,
[556783e]188                                            output->data, &output->size,
[41f9bcb]189                                            spool);
190    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
191        apr_global_mutex_unlock(cache->mutex);
[e183628]192
[de1ceab]193    if (rv != APR_SUCCESS)
[d18afb8]194    {
[de1ceab]195        /* APR_NOTFOUND means there's no such object. */
196        if (rv == APR_NOTFOUND)
197            ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
198                         "requested entry not found in cache '%s:%s'.",
[78d75ac]199                         cache->prov->name, cache->config);
[de1ceab]200        else
201            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, server,
202                         "error fetching from cache '%s:%s'",
[78d75ac]203                         cache->prov->name, cache->config);
[d18afb8]204    }
[de1ceab]205    else
206    {
207        ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
208                     "fetched %u bytes from cache '%s:%s'",
[556783e]209                     output->size, cache->prov->name, cache->config);
[de1ceab]210    }
211    apr_pool_destroy(spool);
[d18afb8]212
[556783e]213    return rv;
[fcb122d]214}
215
[adf36c3]216
217
218/**
219 * Fetch function for the GnuTLS session cache, see
220 * gnutls_db_set_retrieve_function().
221 *
222 * *Warning*: The `data` element of the returned `gnutls_datum_t` is
223 * allocated using `gnutls_malloc()` for compatibility with the GnuTLS
224 * session caching API, and must be released using `gnutls_free()`.
225 *
226 * @param baton mgs_handle_t for the connection, as set via
227 * gnutls_db_set_ptr()
228 *
229 * @param key object key to fetch
230 *
231 * @return the requested cache entry, or `{NULL, 0}`
232 */
[de1ceab]233static gnutls_datum_t socache_fetch_session(void *baton, gnutls_datum_t key)
[15245bf]234{
235    gnutls_datum_t data = {NULL, 0};
[2f932fa]236    gnutls_datum_t dbmkey;
[15245bf]237    mgs_handle_t *ctxt = baton;
238
239    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
240        return data;
241
[556783e]242    data.data = gnutls_malloc(SOCACHE_FETCH_BUF_SIZE);
243    if (data.data == NULL)
244        return data;
245    data.size = SOCACHE_FETCH_BUF_SIZE;
246
247    apr_status_t rv = mgs_cache_fetch(ctxt->sc->cache, ctxt->c->base_server,
248                                      dbmkey, &data, ctxt->c->pool);
249
250    if (rv != APR_SUCCESS)
251    {
252        /* free unused buffer */
253        gnutls_free(data.data);
254        data.data = NULL;
255        data.size = 0;
256    }
257    else
258    {
259        /* Realloc buffer to data.size. Data size must be less than or
260         * equal to the initial buffer size, so this REALLY should not
261         * fail. */
262        data.data = gnutls_realloc(data.data, data.size);
263        if (__builtin_expect(data.data == NULL, 0))
264        {
265            ap_log_cerror(APLOG_MARK, APLOG_CRIT, APR_ENOMEM, ctxt->c,
266                         "%s: Could not realloc fetch buffer to data size!",
267                         __func__);
268            data.size = 0;
269        }
270    }
271
272    return data;
[fcb122d]273}
274
[ae08186]275
276
[adf36c3]277/**
278 * Remove function for the GnuTLS session cache, see
279 * gnutls_db_set_remove_function().
280 *
281 * @param baton mgs_handle_t for the connection, as set via
282 * gnutls_db_set_ptr()
283 *
284 * @param key object key to remove
285 *
286 * @return `0` in case of success, `-1` in case of failure
287 */
288static int socache_delete_session(void *baton, gnutls_datum_t key)
[c55902b]289{
[2f932fa]290    gnutls_datum_t tmpkey;
[e183628]291    mgs_handle_t *ctxt = baton;
292
[2f932fa]293    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &tmpkey) < 0)
[e183628]294        return -1;
295
[0363315]296    if (ctxt->sc->cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
297        apr_global_mutex_lock(ctxt->sc->cache->mutex);
[de1ceab]298    apr_status_t rv = ctxt->sc->cache->prov->remove(ctxt->sc->cache->socache,
299                                                    ctxt->c->base_server,
300                                                    key.data, key.size,
301                                                    ctxt->c->pool);
[0363315]302    if (ctxt->sc->cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
303        apr_global_mutex_unlock(ctxt->sc->cache->mutex);
[e183628]304
305    if (rv != APR_SUCCESS) {
306        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
[9c456a9]307                     ctxt->c->base_server,
[de1ceab]308                     "error deleting from cache '%s:%s'",
[a314ec9]309                     ctxt->sc->cache->prov->name, ctxt->sc->cache->config);
[e183628]310        return -1;
311    }
312    return 0;
[fcb122d]313}
314
315
316
[ce5f776]317const char *mgs_cache_inst_config(mgs_cache_t *cache, server_rec *server,
318                                  const char* type, const char* config,
319                                  apr_pool_t *pconf, apr_pool_t *ptemp)
320{
[3358887]321    /* Allocate cache structure, will be assigned to *cache after
322     * successful configuration. */
323    mgs_cache_t c = apr_pcalloc(pconf, sizeof(struct mgs_cache));
324    if (c == NULL)
325        return "Could not allocate memory for cache configuration!";
[ce5f776]326
327    /* Find the right socache provider */
328    c->prov = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
329                                 type,
330                                 AP_SOCACHE_PROVIDER_VERSION);
331    if (c->prov == NULL)
332    {
333        return apr_psprintf(ptemp,
334                            "Could not find socache provider '%s', please "
335                            "make sure that the provider name is valid and "
336                            "the appropriate module is loaded (maybe "
337                            "mod_socache_%s.so?).",
338                            type, type);
339    }
340
341    /* shmcb works fine with NULL, but make sure there's a valid (if
342     * empty) string for logging */
343    if (config != NULL)
344        c->config = apr_pstrdup(pconf, config);
345    else
346        c->config = "";
347
348    /* Create and configure the cache instance. */
349    const char *err = c->prov->create(&c->socache, c->config, ptemp, pconf);
350    if (err != NULL)
351    {
352        return apr_psprintf(ptemp,
353                            "Creating cache '%s:%s' failed: %s",
354                            c->prov->name, c->config, err);
355    }
356    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, server,
357                 "%s: Socache '%s:%s' created.",
358                 __func__, c->prov->name, c->config);
359
[3358887]360    /* assign configured cache structure to server */
361    *cache = c;
362
[ce5f776]363    return NULL;
364}
365
366
367
368/**
369 * This function is supposed to be called during post_config to
370 * initialize mutex and socache instance associated with an
371 * mgs_cache_t.
372 *
373 * @param cache the mod_gnutls cache structure
374 *
375 * @param cache_name name for socache initialization
376 *
377 * @param mutex_name name to pass to ap_global_mutex_create(), must
378 * have been registered during pre_config.
379 *
380 * @param server server for logging purposes
381 *
382 * @param pconf memory pool for server configuration
383 */
384static apr_status_t mgs_cache_inst_init(mgs_cache_t cache,
385                                        const char *cache_name,
386                                        const char *mutex_name,
387                                        server_rec *server,
388                                        apr_pool_t *pconf)
389{
390    apr_status_t rv = APR_SUCCESS;
391
392    if (cache->mutex == NULL)
393    {
394        rv = ap_global_mutex_create(&cache->mutex, NULL,
395                                    mutex_name,
396                                    NULL, server, pconf, 0);
397        ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
398                     "%s: create mutex", __func__);
399        if (rv != APR_SUCCESS)
400            return rv;
401    }
402
403    rv = cache->prov->init(cache->socache, cache_name, NULL, server, pconf);
404    if (rv != APR_SUCCESS)
405        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, server,
406                     "Initializing cache '%s:%s' failed!",
407                     cache->prov->name, cache->config);
408    else
409        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, server,
410                     "%s: socache '%s:%s' initialized.", __func__,
411                     cache->prov->name, cache->config);
412    return rv;
413}
414
415
416
[de1ceab]417static apr_status_t cleanup_socache(void *data)
418{
419    server_rec *s = data;
420    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
421        ap_get_module_config(s->module_config, &gnutls_module);
[babdb29]422    if (sc->cache)
423    {
424        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
425                     "Cleaning up session cache '%s:%s'",
426                     sc->cache->prov->name, sc->cache->config);
427        sc->cache->prov->destroy(sc->cache->socache, s);
428    }
429    if (sc->ocsp_cache)
430    {
431        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
432                     "Cleaning up OCSP cache '%s:%s'",
433                     sc->ocsp_cache->prov->name, sc->ocsp_cache->config);
434        sc->ocsp_cache->prov->destroy(sc->ocsp_cache->socache, s);
435    }
[de1ceab]436    return APR_SUCCESS;
[fcb122d]437}
438
[040387c]439
[de1ceab]440
[994200a]441int mgs_cache_post_config(apr_pool_t *pconf, apr_pool_t *ptemp,
[de1ceab]442                          server_rec *s, mgs_srvconf_rec *sc)
443{
444    apr_status_t rv = APR_SUCCESS;
[babdb29]445
[994200a]446    /* If the OCSP cache is unconfigured initialize it with
447     * defaults. */
448    if (sc->ocsp_cache == NULL)
449    {
450        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
451                     "%s: OCSP cache unconfigured, using '%s:%s'.", __func__,
452                     DEFAULT_OCSP_CACHE_TYPE, DEFAULT_OCSP_CACHE_CONF);
453        const char *err = mgs_cache_inst_config(&sc->ocsp_cache, s,
454                                                DEFAULT_OCSP_CACHE_TYPE,
455                                                DEFAULT_OCSP_CACHE_CONF,
456                                                pconf, ptemp);
457        if (err != NULL)
458            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s,
459                         "%s: Configuring default OCSP cache '%s:%s' failed, "
460                         "make sure that mod_socache_%s is loaded.", __func__,
461                         DEFAULT_OCSP_CACHE_TYPE, DEFAULT_OCSP_CACHE_CONF,
462                         DEFAULT_OCSP_CACHE_TYPE);
463    }
464
[babdb29]465    /* Initialize the OCSP cache first so it's not skipped if the
466     * session cache is disabled. */
467    if (sc->ocsp_cache != NULL)
468    {
469        /* TODO: Maybe initialize only if explicitly enabled OR at
470         * least one (virtual) host has OCSP enabled? */
471        rv = mgs_cache_inst_init(sc->ocsp_cache, MGS_OCSP_CACHE_NAME,
472                                 MGS_OCSP_CACHE_MUTEX_NAME, s, pconf);
473        if (rv != APR_SUCCESS)
474            return HTTP_INSUFFICIENT_STORAGE;
475    }
476
[b94aee2]477    /* GnuTLSCache was never explicitly set or is disabled: */
478    if (sc->cache_enable == GNUTLS_ENABLED_UNSET
479        || sc->cache_enable == GNUTLS_ENABLED_FALSE)
[de1ceab]480    {
[b94aee2]481        sc->cache_enable = GNUTLS_ENABLED_FALSE;
[de1ceab]482        /* Cache disabled, done. */
483        return APR_SUCCESS;
484    }
[040387c]485    /* if GnuTLSCacheTimeout was never explicitly set: */
[c39ae1a]486    if (sc->cache_timeout == MGS_TIMEOUT_UNSET)
[c005645]487        sc->cache_timeout = apr_time_from_sec(MGS_DEFAULT_CACHE_TIMEOUT);
488
[2d454a2]489    rv = mgs_cache_inst_init(sc->cache, MGS_SESSION_CACHE_NAME,
[ce5f776]490                             MGS_CACHE_MUTEX_NAME, s, pconf);
491    if (rv != APR_SUCCESS)
492        return HTTP_INSUFFICIENT_STORAGE;
[de1ceab]493
494    apr_pool_pre_cleanup_register(pconf, s, cleanup_socache);
[c005645]495
496    return APR_SUCCESS;
[fcb122d]497}
498
[babdb29]499int mgs_cache_child_init(apr_pool_t *p, server_rec *server,
500                         mgs_cache_t cache, const char *mutex_name)
[e765670]501{
[aa68232]502    /* reinit cache mutex */
[babdb29]503    const char *lockfile = apr_global_mutex_lockfile(cache->mutex);
504    apr_status_t rv = apr_global_mutex_child_init(&cache->mutex,
[aa68232]505                                                  lockfile, p);
506    if (rv != APR_SUCCESS)
[babdb29]507        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, server,
508                     "Failed to reinit mutex '%s'", mutex_name);
[aa68232]509
[babdb29]510    return rv;
[6e0bfd6]511}
512
[de1ceab]513int mgs_cache_session_init(mgs_handle_t * ctxt)
514{
[b94aee2]515    if (ctxt->sc->cache_enable)
[de1ceab]516    {
[e183628]517        gnutls_db_set_retrieve_function(ctxt->session,
[de1ceab]518                                        socache_fetch_session);
[e183628]519        gnutls_db_set_remove_function(ctxt->session,
[adf36c3]520                                      socache_delete_session);
[e183628]521        gnutls_db_set_store_function(ctxt->session,
[de1ceab]522                                     socache_store_session);
[e183628]523        gnutls_db_set_ptr(ctxt->session, ctxt);
524    }
525    return 0;
[32f2e60]526}
[3aff94d]527
528
529
530int mgs_cache_status(mgs_cache_t cache, const char *header_title,
531                     request_rec *r, int flags)
532{
533    if (!(flags & AP_STATUS_SHORT))
534        ap_rprintf(r, "<h3>%s:</h3>\n", header_title);
535    else
536        ap_rprintf(r, "%s:\n", header_title);
537
538    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
539        apr_global_mutex_lock(cache->mutex);
540    cache->prov->status(cache->socache, r, flags);
541    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
542        apr_global_mutex_unlock(cache->mutex);
543
544    return OK;
545}
Note: See TracBrowser for help on using the repository browser.