source: mod_gnutls/src/gnutls_cache.c @ a85de63

asynciodebian/masterproxy-ticket
Last change on this file since a85de63 was a85de63, checked in by Fiona Klute <fiona.klute@…>, 3 years ago

Make cache_fetch_func work without a connection context

This is necessary to do cache fetches without a client connection,
e.g. while doing asynchronous OCSP updates.

  • Property mode set to 100644
File size: 23.4 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 *
23 * The signatures of the `(dbm|mc)_cache_...()` functions may be a bit
[3e22b82]24 * confusing: "store" and "expire" take a server_rec, "fetch" an
[14548b9]25 * mgs_handle_t, and "delete" the `void*` required for a
26 * `gnutls_db_remove_func`. The first two have matching `..._session`
[3e22b82]27 * functions to fit their respective GnuTLS session cache signatures.
28 *
29 * This is because "store", "expire" (dbm only), and "fetch" are also
[14548b9]30 * needed for the OCSP cache. Their `..._session` variants have been
[3e22b82]31 * created to take care of the session cache specific parts, mainly
32 * calculating the DB key from the session ID. They have to match the
33 * appropriate GnuTLS DB function signatures.
34 *
[14548b9]35 * Additionally, there are the `mc_cache_(store|fetch)_generic()`
[3e22b82]36 * functions. They exist because memcached requires string keys while
37 * DBM accepts binary keys, and provide wrappers to turn binary keys
[14548b9]38 * into hex strings with a `mod_gnutls:` prefix.
[3e22b82]39 *
40 * To update cached OCSP responses independent of client connections,
41 * "store" and "expire" have to work without a connection context. On
42 * the other hand "fetch" does not need to do that, because cached
43 * OCSP responses will be retrieved for use in client connections.
[14548b9]44 */
[3e22b82]45
[04e6e65]46#include "gnutls_cache.h"
[0b3bc05]47#include "mod_gnutls.h"
[c39ae1a]48#include "gnutls_config.h"
[6e0bfd6]49
50#if HAVE_APR_MEMCACHE
51#include "apr_memcache.h"
52#endif
53
[fcb122d]54#include "apr_dbm.h"
[f450ac9]55#include <apr_escape.h>
[fcb122d]56
[a66e147]57#include "ap_mpm.h"
[c005645]58#include <util_mutex.h>
[0b3bc05]59
[fcb122d]60#include <unistd.h>
61#include <sys/types.h>
62
63#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
64#include "unixd.h"
65#endif
66
[14548b9]67/** Default session cache timeout */
[c005645]68#define MGS_DEFAULT_CACHE_TIMEOUT 300
69
[14548b9]70/** Prefix for keys used with a memcached cache */
[6e0bfd6]71#define MC_TAG "mod_gnutls:"
[14548b9]72/** Maximum length of the hex string representation of a GnuTLS
73 * session ID: two characters per byte, plus one more for `\0` */
[ac3f500]74#if GNUTLS_VERSION_NUMBER >= 0x030400
[f450ac9]75#define GNUTLS_SESSION_ID_STRING_LEN ((GNUTLS_MAX_SESSION_ID_SIZE * 2) + 1)
[ac3f500]76#else
77#define GNUTLS_SESSION_ID_STRING_LEN ((GNUTLS_MAX_SESSION_ID * 2) + 1)
78#endif
[6e0bfd6]79
[7e67487]80#if MODULE_MAGIC_NUMBER_MAJOR < 20081201
81#define ap_unixd_config unixd_config
82#endif
83
[55dc3f0]84#ifdef APLOG_USE_MODULE
85APLOG_USE_MODULE(gnutls);
86#endif
87
[14548b9]88/**
89 * Turn a GnuTLS session ID into the key format we use with DBM
90 * caches. Name the Session ID as `server:port.SessionID` to disallow
91 * resuming sessions on different servers.
92 *
93 * @return `0` on success, `-1` on failure
[c055502]94 */
[2f932fa]95static int mgs_session_id2dbm(conn_rec *c, unsigned char *id, int idlen,
96                              gnutls_datum_t *dbmkey)
97{
[f450ac9]98    char sz[GNUTLS_SESSION_ID_STRING_LEN];
99    apr_status_t rv = apr_escape_hex(sz, id, idlen, 0, NULL);
100    if (rv != APR_SUCCESS)
[e183628]101        return -1;
102
[2f932fa]103    char *newkey = apr_psprintf(c->pool, "%s:%d.%s",
104                                c->base_server->server_hostname,
105                                c->base_server->port, sz);
106    dbmkey->size = strlen(newkey);
107    /* signedness does not matter for arbitrary bits */
108    dbmkey->data = (unsigned char*) newkey;
[e183628]109    return 0;
[c055502]110}
[7bebb42]111
[14548b9]112/** The OPENSSL_TIME_FORMAT macro and mgs_time2sz() serve to print
113 * time in a format compatible with OpenSSL's `ASN1_TIME_print()`
[0831437]114 * function. */
115#define OPENSSL_TIME_FORMAT "%b %d %k:%M:%S %Y %Z"
116
117char *mgs_time2sz(time_t in_time, char *str, int strsize)
118{
[e183628]119    apr_time_exp_t vtm;
120    apr_size_t ret_size;
121    apr_time_t t;
[e02dd8c]122
123
[e183628]124    apr_time_ansi_put(&t, in_time);
125    apr_time_exp_gmt(&vtm, t);
[0831437]126    apr_strftime(str, &ret_size, strsize - 1, OPENSSL_TIME_FORMAT, &vtm);
[e183628]127
128    return str;
[7bebb42]129}
[6e0bfd6]130
[c055502]131#if HAVE_APR_MEMCACHE
[e183628]132
[14548b9]133/**
134 * Turn a GnuTLS session ID into the key format we use with memcached
135 * caches. Name the Session ID as `server:port.SessionID` to disallow
136 * resuming sessions on different servers.
137 *
138 * @return `0` on success, `-1` on failure
[c055502]139 */
[f450ac9]140static char *mgs_session_id2mc(conn_rec * c, unsigned char *id, int idlen)
141{
142    char sz[GNUTLS_SESSION_ID_STRING_LEN];
143    apr_status_t rv = apr_escape_hex(sz, id, idlen, 0, NULL);
144    if (rv != APR_SUCCESS)
[e183628]145        return NULL;
146
147    return apr_psprintf(c->pool, MC_TAG "%s:%d.%s",
148            c->base_server->server_hostname,
149            c->base_server->port, sz);
[42307a9]150}
151
[0b3bc05]152/**
153 * GnuTLS Session Cache using libmemcached
154 *
155 */
156
[a66e147]157/* The underlying apr_memcache system is thread safe... woohoo */
[e02dd8c]158static apr_memcache_t *mc;
[a66e147]159
[e02dd8c]160static int mc_cache_child_init(apr_pool_t * p, server_rec * s,
[e183628]161        mgs_srvconf_rec * sc) {
162    apr_status_t rv = APR_SUCCESS;
163    int thread_limit = 0;
164    int nservers = 0;
165    char *cache_config;
166    char *split;
167    char *tok;
168
169    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
170
171    /* Find all the servers in the first run to get a total count */
172    cache_config = apr_pstrdup(p, sc->cache_config);
173    split = apr_strtok(cache_config, " ", &tok);
174    while (split) {
175        nservers++;
176        split = apr_strtok(NULL, " ", &tok);
177    }
178
179    rv = apr_memcache_create(p, nservers, 0, &mc);
180    if (rv != APR_SUCCESS) {
181        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
[9c456a9]182                     "Failed to create Memcache object of size '%d'.",
183                     nservers);
[e183628]184        return rv;
185    }
186
187    /* Now add each server to the memcache */
188    cache_config = apr_pstrdup(p, sc->cache_config);
189    split = apr_strtok(cache_config, " ", &tok);
190    while (split) {
191        apr_memcache_server_t *st;
192        char *host_str;
193        char *scope_id;
194        apr_port_t port;
195
196        rv = apr_parse_addr_port(&host_str, &scope_id, &port,
197                split, p);
198        if (rv != APR_SUCCESS) {
199            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
[9c456a9]200                         "Failed to parse server: '%s'", split);
[e183628]201            return rv;
202        }
203
204        if (host_str == NULL) {
205            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
[9c456a9]206                         "Failed to parse server, "
207                         "no hostname specified: '%s'", split);
[e183628]208            return rv;
209        }
210
211        if (port == 0) {
212            port = 11211; /* default port */
213        }
214
215        /* Should Max Conns be (thread_limit / nservers) ? */
216        rv = apr_memcache_server_create(p,
217                host_str, port,
218                0,
219                1, thread_limit, 600, &st);
220        if (rv != APR_SUCCESS) {
221            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
[9c456a9]222                         "Failed to create server: %s:%d",
223                         host_str, port);
[e183628]224            return rv;
225        }
226
227        rv = apr_memcache_add_server(mc, st);
228        if (rv != APR_SUCCESS) {
229            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
[9c456a9]230                         "Failed to add server: %s:%d",
231                         host_str, port);
[e183628]232            return rv;
233        }
234
235        split = apr_strtok(NULL, " ", &tok);
236    }
237    return rv;
[32f2e60]238}
[a66e147]239
[3e22b82]240static int mc_cache_store(server_rec *s, const char *key,
241                          gnutls_datum_t data, apr_uint32_t timeout)
242{
243    apr_status_t rv = apr_memcache_set(mc, key, (char *) data.data,
244                                       data.size, timeout, 0);
[e183628]245
[3e22b82]246    if (rv != APR_SUCCESS)
247    {
248        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
[9c456a9]249                     "error storing key '%s' with %d bytes of data",
250                     key, data.size);
[e183628]251        return -1;
[3e22b82]252    }
[e183628]253
[3e22b82]254    return 0;
255}
[e183628]256
[e809fb3]257static int mc_cache_store_generic(server_rec *s, gnutls_datum_t key,
258                                  gnutls_datum_t data, apr_time_t expiry)
[3e22b82]259{
260    apr_uint32_t timeout = apr_time_sec(expiry - apr_time_now());
[e183628]261
[3e22b82]262    apr_pool_t *p;
263    apr_pool_create(&p, NULL);
264
265    const char *hex = apr_pescape_hex(p, key.data, key.size, 1);
266    if (hex == NULL)
267    {
268        apr_pool_destroy(p);
[e183628]269        return -1;
270    }
271
[3e22b82]272    const char *strkey = apr_psprintf(p, MC_TAG "%s", hex);
273
274    int ret = mc_cache_store(s, strkey, data, timeout);
275
276    apr_pool_destroy(p);
277    return ret;
[a66e147]278}
279
[3e22b82]280static int mc_cache_store_session(void *baton, gnutls_datum_t key,
281                                  gnutls_datum_t data)
282{
[e183628]283    mgs_handle_t *ctxt = baton;
[3e22b82]284
285    const char *strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
286    if (!strkey)
287        return -1;
288
289    apr_uint32_t timeout = apr_time_sec(ctxt->sc->cache_timeout);
290
291    return mc_cache_store(ctxt->c->base_server, strkey, data, timeout);
292}
293
[6945efb]294/**
295 * @param s server reference for logging
296 * @param key the key to fetch
297 * @param pool pool from which to allocate memory for the result
298 */
299static gnutls_datum_t mc_cache_fetch(server_rec *s, const char *key,
300                                     apr_pool_t *pool)
[3e22b82]301{
302    apr_status_t rv = APR_SUCCESS;
[e183628]303    char *value;
304    apr_size_t value_len;
305    gnutls_datum_t data = {NULL, 0};
[e02dd8c]306
[6945efb]307    rv = apr_memcache_getp(mc, pool, key, &value, &value_len, NULL);
[e02dd8c]308
[3e22b82]309    if (rv != APR_SUCCESS)
310    {
[6945efb]311        ap_log_error(APLOG_MARK, APLOG_TRACE2, rv, s,
312                     "error fetching key '%s'",
313                     key);
[e183628]314        return data;
315    }
[a66e147]316
[e183628]317    /* TODO: Eliminate this memcpy. gnutls-- */
318    data.data = gnutls_malloc(value_len);
319    if (data.data == NULL)
320        return data;
[a66e147]321
[e183628]322    data.size = value_len;
323    memcpy(data.data, value, value_len);
[a66e147]324
[e183628]325    return data;
[32f2e60]326}
327
[a85de63]328static gnutls_datum_t mc_cache_fetch_generic(server_rec *server,
329                                             gnutls_datum_t key,
330                                             apr_pool_t *pool)
[3e22b82]331{
332    gnutls_datum_t data = {NULL, 0};
[a85de63]333    const char *hex = apr_pescape_hex(pool, key.data, key.size, 1);
[3e22b82]334    if (hex == NULL)
335        return data;
336
[a85de63]337    const char *strkey = apr_psprintf(pool, MC_TAG "%s", hex);
338    return mc_cache_fetch(server, strkey, pool);
[3e22b82]339}
340
341static gnutls_datum_t mc_cache_fetch_session(void *baton, gnutls_datum_t key)
342{
343    mgs_handle_t *ctxt = baton;
344    gnutls_datum_t data = {NULL, 0};
345
346    const char *strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
347    if (!strkey)
348        return data;
349
[6945efb]350    return mc_cache_fetch(ctxt->c->base_server, strkey, ctxt->c->pool);
[3e22b82]351}
352
[e183628]353static int mc_cache_delete(void *baton, gnutls_datum_t key) {
354    apr_status_t rv = APR_SUCCESS;
355    mgs_handle_t *ctxt = baton;
356    char *strkey = NULL;
[a66e147]357
[e183628]358    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
359    if (!strkey)
360        return -1;
[a66e147]361
[e183628]362    rv = apr_memcache_delete(mc, strkey, 0);
[a66e147]363
[e183628]364    if (rv != APR_SUCCESS) {
365        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
[9c456a9]366                     ctxt->c->base_server,
367                     "error deleting key '%s'",
368                     strkey);
[e183628]369        return -1;
370    }
[a66e147]371
[e183628]372    return 0;
[32f2e60]373}
374
[410d216]375#endif  /* have_apr_memcache */
[6e0bfd6]376
[410d216]377static const char *db_type(mgs_srvconf_rec * sc) {
[e183628]378    if (sc->cache_type == mgs_cache_gdbm)
379        return "gdbm";
380    else
381        return "db";
[771ca63]382}
383
[fcb122d]384#define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
385
[f785704]386static void dbm_cache_expire(server_rec *s)
387{
388    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
389        ap_get_module_config(s->module_config, &gnutls_module);
390
[e183628]391    apr_status_t rv;
392    apr_dbm_t *dbm;
393    apr_datum_t dbmkey;
394    apr_datum_t dbmval;
395    apr_time_t dtime;
396    apr_pool_t *spool;
397    int total, deleted;
398
[f785704]399    apr_time_t now = apr_time_now();
[e183628]400
[f785704]401    if (now - sc->last_cache_check < (sc->cache_timeout) / 2)
[e183628]402        return;
403
[f785704]404    sc->last_cache_check = now;
[e183628]405
[f785704]406    apr_pool_create(&spool, NULL);
[e183628]407
408    total = 0;
409    deleted = 0;
410
[aa68232]411    apr_global_mutex_lock(sc->cache->mutex);
[c005645]412
[f785704]413    rv = apr_dbm_open_ex(&dbm, db_type(sc),
414            sc->cache_config, APR_DBM_RWCREATE,
[e183628]415            SSL_DBM_FILE_MODE, spool);
416    if (rv != APR_SUCCESS) {
[f785704]417        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s,
[9c456a9]418                     "error opening cache '%s'",
419                     sc->cache_config);
[aa68232]420        apr_global_mutex_unlock(sc->cache->mutex);
[e183628]421        apr_pool_destroy(spool);
422        return;
423    }
424
425    apr_dbm_firstkey(dbm, &dbmkey);
426    while (dbmkey.dptr != NULL) {
427        apr_dbm_fetch(dbm, dbmkey, &dbmval);
428        if (dbmval.dptr != NULL
429                && dbmval.dsize >= sizeof (apr_time_t)) {
430            memcpy(&dtime, dbmval.dptr, sizeof (apr_time_t));
431
432            if (now >= dtime) {
433                apr_dbm_delete(dbm, dbmkey);
434                deleted++;
435            }
436            apr_dbm_freedatum(dbm, dbmval);
437        } else {
438            apr_dbm_delete(dbm, dbmkey);
439            deleted++;
440        }
441        total++;
442        apr_dbm_nextkey(dbm, &dbmkey);
443    }
444    apr_dbm_close(dbm);
445
[aa68232]446    rv = apr_global_mutex_unlock(sc->cache->mutex);
[c005645]447
[f785704]448    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
[9c456a9]449                 "Cleaned up cache '%s'. Deleted %d and left %d",
450                 sc->cache_config, deleted, total - deleted);
[e183628]451
452    apr_pool_destroy(spool);
453
454    return;
[fcb122d]455}
456
[a85de63]457
458
459static gnutls_datum_t dbm_cache_fetch(server_rec *server, gnutls_datum_t key,
460                                      apr_pool_t *pool)
[15245bf]461{
[c34a68b]462    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
463        ap_get_module_config(server->module_config, &gnutls_module);
464
[e183628]465    gnutls_datum_t data = {NULL, 0};
466    apr_dbm_t *dbm;
[2f932fa]467    apr_datum_t dbmkey = {(char*) key.data, key.size};
[e183628]468    apr_datum_t dbmval;
[d18afb8]469    apr_time_t expiry = 0;
[e183628]470    apr_status_t rv;
471
[c55902b]472    /* check if it is time for cache expiration */
[c34a68b]473    dbm_cache_expire(server);
[c55902b]474
[c34a68b]475    apr_global_mutex_lock(sc->cache->mutex);
[c005645]476
[c34a68b]477    rv = apr_dbm_open_ex(&dbm, db_type(sc),
478                         sc->cache_config, APR_DBM_READONLY,
479                         SSL_DBM_FILE_MODE, pool);
[e183628]480    if (rv != APR_SUCCESS) {
[c34a68b]481        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, server,
482                     "error opening cache '%s'",
483                     sc->cache_config);
484        apr_global_mutex_unlock(sc->cache->mutex);
[e183628]485        return data;
486    }
487
[2f932fa]488    rv = apr_dbm_fetch(dbm, dbmkey, &dbmval);
[e183628]489
[c55902b]490    if (rv != APR_SUCCESS)
491        goto close_db;
[e183628]492
[c55902b]493    if (dbmval.dptr == NULL || dbmval.dsize <= sizeof (apr_time_t))
494        goto cleanup;
[e183628]495
496    data.size = dbmval.dsize - sizeof (apr_time_t);
[d18afb8]497    /* get data expiration tag */
498    expiry = *((apr_time_t *) dbmval.dptr);
[e183628]499
500    data.data = gnutls_malloc(data.size);
[c55902b]501    if (data.data == NULL)
[11e6205]502    {
503        data.size = 0;
[c55902b]504        goto cleanup;
[11e6205]505    }
[e183628]506
[c34a68b]507    ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
508                 "fetched %" APR_SIZE_T_FMT " bytes from cache",
509                 dbmval.dsize);
[15245bf]510
[e183628]511    memcpy(data.data, dbmval.dptr + sizeof (apr_time_t), data.size);
512
[c55902b]513 cleanup:
[e183628]514    apr_dbm_freedatum(dbm, dbmval);
[c55902b]515 close_db:
[e183628]516    apr_dbm_close(dbm);
[c34a68b]517    apr_global_mutex_unlock(sc->cache->mutex);
[e183628]518
[d18afb8]519    /* cache entry might have expired since last cache cleanup */
520    if (expiry != 0 && expiry < apr_time_now())
521    {
522        gnutls_free(data.data);
523        data.data = NULL;
524        data.size = 0;
[c34a68b]525        ap_log_error(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, server,
526                     "dropped expired cache data");
[d18afb8]527    }
528
[e183628]529    return data;
[fcb122d]530}
531
[2f932fa]532static gnutls_datum_t dbm_cache_fetch_session(void *baton, gnutls_datum_t key)
[15245bf]533{
534    gnutls_datum_t data = {NULL, 0};
[2f932fa]535    gnutls_datum_t dbmkey;
[15245bf]536    mgs_handle_t *ctxt = baton;
537
538    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
539        return data;
540
[a85de63]541    return dbm_cache_fetch(ctxt->c->base_server, dbmkey, ctxt->c->pool);
[15245bf]542}
543
[e809fb3]544static int dbm_cache_store(server_rec *s, gnutls_datum_t key,
545                           gnutls_datum_t data, apr_time_t expiry)
[ae08186]546{
[1d1361f]547    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
548        ap_get_module_config(s->module_config, &gnutls_module);
549
[e183628]550    apr_dbm_t *dbm;
[2f932fa]551    apr_datum_t dbmkey = {(char*) key.data, key.size};
[e183628]552    apr_datum_t dbmval;
553    apr_status_t rv;
554    apr_pool_t *spool;
555
[c55902b]556    /* check if it is time for cache expiration */
[1d1361f]557    dbm_cache_expire(s);
[e183628]558
[1d1361f]559    apr_pool_create(&spool, NULL);
[e183628]560
561    /* create DBM value */
562    dbmval.dsize = data.size + sizeof (apr_time_t);
563    dbmval.dptr = (char *) apr_palloc(spool, dbmval.dsize);
564
[ae08186]565    /* prepend expiration time */
[e183628]566    memcpy((char *) dbmval.dptr, &expiry, sizeof (apr_time_t));
567    memcpy((char *) dbmval.dptr + sizeof (apr_time_t),
568            data.data, data.size);
569
[aa68232]570    apr_global_mutex_lock(sc->cache->mutex);
[c005645]571
[1d1361f]572    rv = apr_dbm_open_ex(&dbm, db_type(sc),
573                         sc->cache_config, APR_DBM_RWCREATE,
574                         SSL_DBM_FILE_MODE, spool);
575    if (rv != APR_SUCCESS)
576    {
577        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s,
578                     "error opening cache '%s'",
579                     sc->cache_config);
[aa68232]580        apr_global_mutex_unlock(sc->cache->mutex);
[e183628]581        apr_pool_destroy(spool);
582        return -1;
583    }
584
[2f932fa]585    rv = apr_dbm_store(dbm, dbmkey, dbmval);
[1d1361f]586    if (rv != APR_SUCCESS)
587    {
588        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
589                     "error storing in cache '%s'",
590                     sc->cache_config);
[e183628]591        apr_dbm_close(dbm);
[aa68232]592        apr_global_mutex_unlock(sc->cache->mutex);
[e183628]593        apr_pool_destroy(spool);
594        return -1;
595    }
596
[c005645]597    apr_dbm_close(dbm);
[aa68232]598    apr_global_mutex_unlock(sc->cache->mutex);
[c005645]599
[9c456a9]600    ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, s,
[5ac4bbe]601                 "stored %" APR_SIZE_T_FMT " bytes of data (%"
602                 APR_SIZE_T_FMT " byte key) in cache '%s'",
[2f932fa]603                 dbmval.dsize, dbmkey.dsize, sc->cache_config);
[1d1361f]604
[e183628]605    apr_pool_destroy(spool);
606
607    return 0;
[fcb122d]608}
609
[ae08186]610static int dbm_cache_store_session(void *baton, gnutls_datum_t key,
611                                   gnutls_datum_t data)
612{
613    mgs_handle_t *ctxt = baton;
[2f932fa]614    gnutls_datum_t dbmkey;
[ae08186]615
616    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
617        return -1;
618
619    apr_time_t expiry = apr_time_now() + ctxt->sc->cache_timeout;
620
[1d1361f]621    return dbm_cache_store(ctxt->c->base_server, dbmkey, data, expiry);
[ae08186]622}
623
[c55902b]624static int dbm_cache_delete(void *baton, gnutls_datum_t key)
625{
[e183628]626    apr_dbm_t *dbm;
[2f932fa]627    gnutls_datum_t tmpkey;
[e183628]628    mgs_handle_t *ctxt = baton;
629    apr_status_t rv;
630
[2f932fa]631    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &tmpkey) < 0)
[e183628]632        return -1;
[2f932fa]633    apr_datum_t dbmkey = {(char*) tmpkey.data, tmpkey.size};
[e183628]634
[aa68232]635    apr_global_mutex_lock(ctxt->sc->cache->mutex);
[c005645]636
[e183628]637    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
638            ctxt->sc->cache_config, APR_DBM_RWCREATE,
639            SSL_DBM_FILE_MODE, ctxt->c->pool);
640    if (rv != APR_SUCCESS) {
641        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
[9c456a9]642                     ctxt->c->base_server,
643                     "error opening cache '%s'",
644                     ctxt->sc->cache_config);
[aa68232]645        apr_global_mutex_unlock(ctxt->sc->cache->mutex);
[e183628]646        return -1;
647    }
648
649    rv = apr_dbm_delete(dbm, dbmkey);
650
651    if (rv != APR_SUCCESS) {
652        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
[9c456a9]653                     ctxt->c->base_server,
654                     "error deleting from cache '%s'",
655                     ctxt->sc->cache_config);
[e183628]656        apr_dbm_close(dbm);
[aa68232]657        apr_global_mutex_unlock(ctxt->sc->cache->mutex);
[e183628]658        return -1;
659    }
660
661    apr_dbm_close(dbm);
[aa68232]662    apr_global_mutex_unlock(ctxt->sc->cache->mutex);
[e183628]663
664    return 0;
[fcb122d]665}
666
[e02dd8c]667static int dbm_cache_post_config(apr_pool_t * p, server_rec * s,
[e183628]668        mgs_srvconf_rec * sc) {
669    apr_status_t rv;
670    apr_dbm_t *dbm;
671    const char *path1;
672    const char *path2;
[fcb122d]673
[e183628]674    rv = apr_dbm_open_ex(&dbm, db_type(sc), sc->cache_config,
675            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, p);
[fcb122d]676
[e183628]677    if (rv != APR_SUCCESS) {
678        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
679                "GnuTLS: Cannot create DBM Cache at `%s'",
680                sc->cache_config);
681        return rv;
682    }
[fcb122d]683
[e183628]684    apr_dbm_close(dbm);
[fcb122d]685
[e183628]686    apr_dbm_get_usednames_ex(p, db_type(sc), sc->cache_config, &path1,
687            &path2);
[fcb122d]688
[e183628]689    /* The Following Code takes logic directly from mod_ssl's DBM Cache */
[fcb122d]690#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
[e183628]691    /* Running as Root */
692    if (path1 && geteuid() == 0) {
[422f5b7]693        if (0 != chown(path1, ap_unixd_config.user_id, -1))
694            ap_log_error(APLOG_MARK, APLOG_NOTICE, -1, s,
695                         "GnuTLS: could not chown cache path1 `%s' to uid %d (errno: %d)",
696                         path1, ap_unixd_config.user_id, errno);
[e183628]697        if (path2 != NULL) {
[422f5b7]698            if (0 != chown(path2, ap_unixd_config.user_id, -1))
699                ap_log_error(APLOG_MARK, APLOG_NOTICE, -1, s,
700                             "GnuTLS: could not chown cache path2 `%s' to uid %d (errno: %d)",
701                             path2, ap_unixd_config.user_id, errno);
[e183628]702        }
703    }
[fcb122d]704#endif
705
[e183628]706    return rv;
[fcb122d]707}
708
[e02dd8c]709int mgs_cache_post_config(apr_pool_t * p, server_rec * s,
[e183628]710        mgs_srvconf_rec * sc) {
[040387c]711
712    /* if GnuTLSCache was never explicitly set: */
713    if (sc->cache_type == mgs_cache_unset)
714        sc->cache_type = mgs_cache_none;
715    /* if GnuTLSCacheTimeout was never explicitly set: */
[c39ae1a]716    if (sc->cache_timeout == MGS_TIMEOUT_UNSET)
[c005645]717        sc->cache_timeout = apr_time_from_sec(MGS_DEFAULT_CACHE_TIMEOUT);
718
719    /* initialize mutex only once */
[aa68232]720    if (sc->cache == NULL)
[c005645]721    {
[aa68232]722        sc->cache = apr_palloc(p, sizeof(struct mgs_cache));
723        apr_status_t rv = ap_global_mutex_create(&sc->cache->mutex, NULL,
[c005645]724                                                 MGS_CACHE_MUTEX_NAME,
725                                                 NULL, s, p, 0);
726        if (rv != APR_SUCCESS)
727            return rv;
728    }
[040387c]729
[e809fb3]730    if (sc->cache_type == mgs_cache_dbm || sc->cache_type == mgs_cache_gdbm)
731    {
732        sc->cache->store = dbm_cache_store;
733        sc->cache->fetch = dbm_cache_fetch;
[e183628]734        return dbm_cache_post_config(p, s, sc);
735    }
[e809fb3]736#if HAVE_APR_MEMCACHE
737    else if (sc->cache_type == mgs_cache_memcache)
738    {
739        sc->cache->store = mc_cache_store_generic;
740        sc->cache->fetch = mc_cache_fetch_generic;
741    }
742#endif
[c005645]743
744    return APR_SUCCESS;
[fcb122d]745}
746
[e765670]747int mgs_cache_child_init(apr_pool_t * p,
748                         server_rec * s,
749                         mgs_srvconf_rec * sc)
750{
[aa68232]751    /* reinit cache mutex */
752    const char *lockfile = apr_global_mutex_lockfile(sc->cache->mutex);
753    apr_status_t rv = apr_global_mutex_child_init(&sc->cache->mutex,
754                                                  lockfile, p);
755    if (rv != APR_SUCCESS)
756        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
757                     "Failed to reinit mutex '%s'", MGS_CACHE_MUTEX_NAME);
758
[e183628]759    if (sc->cache_type == mgs_cache_dbm
760            || sc->cache_type == mgs_cache_gdbm) {
761        return 0;
762    }
[6e0bfd6]763#if HAVE_APR_MEMCACHE
[e183628]764    else if (sc->cache_type == mgs_cache_memcache) {
765        return mc_cache_child_init(p, s, sc);
766    }
[6e0bfd6]767#endif
[e183628]768    return 0;
[6e0bfd6]769}
770
[e02dd8c]771#include <assert.h>
[fcb122d]772
[e183628]773int mgs_cache_session_init(mgs_handle_t * ctxt) {
774    if (ctxt->sc->cache_type == mgs_cache_dbm
775            || ctxt->sc->cache_type == mgs_cache_gdbm) {
776        gnutls_db_set_retrieve_function(ctxt->session,
[15245bf]777                dbm_cache_fetch_session);
[e183628]778        gnutls_db_set_remove_function(ctxt->session,
779                dbm_cache_delete);
780        gnutls_db_set_store_function(ctxt->session,
[ae08186]781                dbm_cache_store_session);
[e183628]782        gnutls_db_set_ptr(ctxt->session, ctxt);
783    }
[6e0bfd6]784#if HAVE_APR_MEMCACHE
[e183628]785    else if (ctxt->sc->cache_type == mgs_cache_memcache) {
786        gnutls_db_set_retrieve_function(ctxt->session,
[3e22b82]787                mc_cache_fetch_session);
[e183628]788        gnutls_db_set_remove_function(ctxt->session,
789                mc_cache_delete);
790        gnutls_db_set_store_function(ctxt->session,
[3e22b82]791                mc_cache_store_session);
[e183628]792        gnutls_db_set_ptr(ctxt->session, ctxt);
793    }
[6e0bfd6]794#endif
[42307a9]795
[e183628]796    return 0;
[32f2e60]797}
Note: See TracBrowser for help on using the repository browser.