Changeset 19e80a5 in mod_gnutls for src/gnutls_cache.c


Ignore:
Timestamp:
Jan 28, 2019, 2:50:38 PM (21 months ago)
Author:
Fiona Klute <fiona.klute@…>
Branches:
debian/master
Children:
102aa67
Parents:
0931b35 (diff), ea9c699 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Update upstream source from tag 'upstream/0.9.0'

Update to upstream version '0.9.0'
with Debian dir 619b546038886b240d2c8e61ee1a1b13ce0867d7

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/gnutls_cache.c

    r0931b35 r19e80a5  
    33 *  Copyright 2008 Nikos Mavrogiannopoulos
    44 *  Copyright 2011 Dash Shendy
    5  *  Copyright 2015-2016 Fiona Klute
     5 *  Copyright 2015-2018 Fiona Klute
    66 *
    77 *  Licensed under the Apache License, Version 2.0 (the "License");
     
    2121 * @file gnutls_cache.c
    2222 *
    23  * The signatures of the `(dbm|mc)_cache_...()` functions may be a bit
    24  * confusing: "store" and "expire" take a server_rec, "fetch" an
    25  * mgs_handle_t, and "delete" the `void*` required for a
    26  * `gnutls_db_remove_func`. The first two have matching `..._session`
    27  * functions to fit their respective GnuTLS session cache signatures.
    28  *
    29  * This is because "store", "expire" (dbm only), and "fetch" are also
    30  * needed for the OCSP cache. Their `..._session` variants have been
    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  *
    35  * Additionally, there are the `mc_cache_(store|fetch)_generic()`
    36  * functions. They exist because memcached requires string keys while
    37  * DBM accepts binary keys, and provide wrappers to turn binary keys
    38  * into hex strings with a `mod_gnutls:` prefix.
    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.
     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).
    4428 */
    4529
     
    4731#include "mod_gnutls.h"
    4832#include "gnutls_config.h"
    49 
    50 #if HAVE_APR_MEMCACHE
    51 #include "apr_memcache.h"
    52 #endif
    53 
    54 #include "apr_dbm.h"
     33#include "gnutls_ocsp.h"
     34
     35#include <ap_socache.h>
     36#include <apr_strings.h>
     37#include <mod_status.h>
    5538#include <apr_escape.h>
    56 
    57 #include "ap_mpm.h"
    5839#include <util_mutex.h>
    59 
    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
    6640
    6741/** Default session cache timeout */
    6842#define MGS_DEFAULT_CACHE_TIMEOUT 300
    6943
    70 /** Prefix for keys used with a memcached cache */
    71 #define MC_TAG "mod_gnutls:"
     44/** Session cache name */
     45#define MGS_SESSION_CACHE_NAME "gnutls_session"
     46
     47/** Default type for OCSP cache */
     48#define DEFAULT_OCSP_CACHE_TYPE "shmcb"
     49/** Default config string for OCSP cache */
     50#define DEFAULT_OCSP_CACHE_CONF "gnutls_ocsp_cache"
     51
    7252/** Maximum length of the hex string representation of a GnuTLS
    7353 * session ID: two characters per byte, plus one more for `\0` */
     
    7858#endif
    7959
    80 #if MODULE_MAGIC_NUMBER_MAJOR < 20081201
    81 #define ap_unixd_config unixd_config
    82 #endif
    83 
    8460#ifdef APLOG_USE_MODULE
    8561APLOG_USE_MODULE(gnutls);
     
    8763
    8864/**
    89  * Turn a GnuTLS session ID into the key format we use with DBM
     65 * Turn a GnuTLS session ID into the key format we use for
    9066 * caches. Name the Session ID as `server:port.SessionID` to disallow
    9167 * resuming sessions on different servers.
     
    129105}
    130106
    131 #if HAVE_APR_MEMCACHE
     107
     108
     109int mgs_cache_store(mgs_cache_t cache, server_rec *server,
     110                    gnutls_datum_t key, gnutls_datum_t data,
     111                    apr_time_t expiry)
     112{
     113    apr_pool_t *spool;
     114    apr_pool_create(&spool, NULL);
     115
     116    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     117        apr_global_mutex_lock(cache->mutex);
     118    apr_status_t rv = cache->prov->store(cache->socache, server,
     119                                         key.data, key.size,
     120                                         expiry,
     121                                         data.data, data.size,
     122                                         spool);
     123    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     124        apr_global_mutex_unlock(cache->mutex);
     125
     126    if (rv != APR_SUCCESS)
     127    {
     128        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, server,
     129                     "error storing in cache '%s:%s'",
     130                     cache->prov->name, cache->config);
     131        apr_pool_destroy(spool);
     132        return -1;
     133    }
     134
     135    ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
     136                 "stored %u bytes of data (%u byte key) in cache '%s:%s'",
     137                 data.size, key.size,
     138                 cache->prov->name, cache->config);
     139    apr_pool_destroy(spool);
     140    return 0;
     141}
     142
     143
    132144
    133145/**
    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
    139  */
    140 static 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)
    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);
    150 }
    151 
    152 /**
    153  * GnuTLS Session Cache using libmemcached
    154  *
    155  */
    156 
    157 /* The underlying apr_memcache system is thread safe... woohoo */
    158 static apr_memcache_t *mc;
    159 
    160 static int mc_cache_child_init(apr_pool_t * p, server_rec * s,
    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,
    182                      "Failed to create Memcache object of size '%d'.",
    183                      nservers);
    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,
    200                          "Failed to parse server: '%s'", split);
    201             return rv;
    202         }
    203 
    204         if (host_str == NULL) {
    205             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
    206                          "Failed to parse server, "
    207                          "no hostname specified: '%s'", split);
    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,
    222                          "Failed to create server: %s:%d",
    223                          host_str, port);
    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,
    230                          "Failed to add server: %s:%d",
    231                          host_str, port);
    232             return rv;
    233         }
    234 
    235         split = apr_strtok(NULL, " ", &tok);
    236     }
    237     return rv;
    238 }
    239 
    240 static 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);
    245 
    246     if (rv != APR_SUCCESS)
    247     {
    248         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
    249                      "error storing key '%s' with %d bytes of data",
    250                      key, data.size);
     146 * Store function for the GnuTLS session cache, see
     147 * gnutls_db_set_store_function().
     148 *
     149 * @param baton mgs_handle_t for the connection, as set via
     150 * gnutls_db_set_ptr()
     151 *
     152 * @param key object key to store
     153 *
     154 * @param data the object to store
     155 *
     156 * @return `0` in case of success, `-1` in case of failure
     157 */
     158static int socache_store_session(void *baton, gnutls_datum_t key,
     159                                 gnutls_datum_t data)
     160{
     161    mgs_handle_t *ctxt = baton;
     162    gnutls_datum_t dbmkey;
     163
     164    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
    251165        return -1;
    252     }
    253 
    254     return 0;
    255 }
    256 
    257 static int mc_cache_store_generic(server_rec *s, gnutls_datum_t key,
    258                                   gnutls_datum_t data, apr_time_t expiry)
    259 {
    260     apr_uint32_t timeout = apr_time_sec(expiry - apr_time_now());
    261 
    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);
    269         return -1;
    270     }
    271 
    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;
    278 }
    279 
    280 static int mc_cache_store_session(void *baton, gnutls_datum_t key,
    281                                   gnutls_datum_t data)
    282 {
    283     mgs_handle_t *ctxt = baton;
    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 
    294 static gnutls_datum_t mc_cache_fetch(conn_rec *c, const char *key)
    295 {
    296     apr_status_t rv = APR_SUCCESS;
    297     char *value;
    298     apr_size_t value_len;
     166
     167    apr_time_t expiry = apr_time_now() + ctxt->sc->cache_timeout;
     168
     169    return mgs_cache_store(ctxt->sc->cache, ctxt->c->base_server,
     170                           dbmkey, data, expiry);
     171}
     172
     173
     174
     175/** 8K is the maximum size accepted when receiving OCSP responses,
     176 * sessions cache entries should be much smaller. The buffer is
     177 * reallocated to actual size after fetching, so memory waste is
     178 * minimal and temporary. */
     179#define SOCACHE_FETCH_BUF_SIZE (8 * 1024)
     180
     181gnutls_datum_t mgs_cache_fetch(mgs_cache_t cache, server_rec *server,
     182                               gnutls_datum_t key, apr_pool_t *pool)
     183{
    299184    gnutls_datum_t data = {NULL, 0};
    300 
    301     rv = apr_memcache_getp(mc, c->pool, key, &value, &value_len, NULL);
    302 
    303     if (rv != APR_SUCCESS)
    304     {
    305 #if MOD_GNUTLS_DEBUG
    306         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
    307                       "error fetching key '%s'",
    308                       key);
    309 #endif
    310         return data;
    311     }
    312 
    313     /* TODO: Eliminate this memcpy. gnutls-- */
    314     data.data = gnutls_malloc(value_len);
     185    data.data = gnutls_malloc(SOCACHE_FETCH_BUF_SIZE);
    315186    if (data.data == NULL)
    316187        return data;
    317 
    318     data.size = value_len;
    319     memcpy(data.data, value, value_len);
    320 
    321     return data;
    322 }
    323 
    324 static gnutls_datum_t mc_cache_fetch_generic(mgs_handle_t *ctxt,
    325                                              gnutls_datum_t key)
    326 {
    327     gnutls_datum_t data = {NULL, 0};
    328     const char *hex = apr_pescape_hex(ctxt->c->pool, key.data, key.size, 1);
    329     if (hex == NULL)
    330         return data;
    331 
    332     const char *strkey = apr_psprintf(ctxt->c->pool, MC_TAG "%s", hex);
    333     return mc_cache_fetch(ctxt->c, strkey);
    334 }
    335 
    336 static gnutls_datum_t mc_cache_fetch_session(void *baton, gnutls_datum_t key)
    337 {
    338     mgs_handle_t *ctxt = baton;
    339     gnutls_datum_t data = {NULL, 0};
    340 
    341     const char *strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
    342     if (!strkey)
    343         return data;
    344 
    345     return mc_cache_fetch(ctxt->c, strkey);
    346 }
    347 
    348 static int mc_cache_delete(void *baton, gnutls_datum_t key) {
    349     apr_status_t rv = APR_SUCCESS;
    350     mgs_handle_t *ctxt = baton;
    351     char *strkey = NULL;
    352 
    353     strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
    354     if (!strkey)
    355         return -1;
    356 
    357     rv = apr_memcache_delete(mc, strkey, 0);
    358 
    359     if (rv != APR_SUCCESS) {
    360         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
    361                      ctxt->c->base_server,
    362                      "error deleting key '%s'",
    363                      strkey);
    364         return -1;
    365     }
    366 
    367     return 0;
    368 }
    369 
    370 #endif  /* have_apr_memcache */
    371 
    372 static const char *db_type(mgs_srvconf_rec * sc) {
    373     if (sc->cache_type == mgs_cache_gdbm)
    374         return "gdbm";
    375     else
    376         return "db";
    377 }
    378 
    379 #define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
    380 
    381 static void dbm_cache_expire(server_rec *s)
    382 {
    383     mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
    384         ap_get_module_config(s->module_config, &gnutls_module);
    385 
    386     apr_status_t rv;
    387     apr_dbm_t *dbm;
    388     apr_datum_t dbmkey;
    389     apr_datum_t dbmval;
    390     apr_time_t dtime;
     188    data.size = SOCACHE_FETCH_BUF_SIZE;
     189
    391190    apr_pool_t *spool;
    392     int total, deleted;
    393 
    394     apr_time_t now = apr_time_now();
    395 
    396     if (now - sc->last_cache_check < (sc->cache_timeout) / 2)
    397         return;
    398 
    399     sc->last_cache_check = now;
    400 
    401     apr_pool_create(&spool, NULL);
    402 
    403     total = 0;
    404     deleted = 0;
    405 
    406     apr_global_mutex_lock(sc->cache->mutex);
    407 
    408     rv = apr_dbm_open_ex(&dbm, db_type(sc),
    409             sc->cache_config, APR_DBM_RWCREATE,
    410             SSL_DBM_FILE_MODE, spool);
    411     if (rv != APR_SUCCESS) {
    412         ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s,
    413                      "error opening cache '%s'",
    414                      sc->cache_config);
    415         apr_global_mutex_unlock(sc->cache->mutex);
    416         apr_pool_destroy(spool);
    417         return;
    418     }
    419 
    420     apr_dbm_firstkey(dbm, &dbmkey);
    421     while (dbmkey.dptr != NULL) {
    422         apr_dbm_fetch(dbm, dbmkey, &dbmval);
    423         if (dbmval.dptr != NULL
    424                 && dbmval.dsize >= sizeof (apr_time_t)) {
    425             memcpy(&dtime, dbmval.dptr, sizeof (apr_time_t));
    426 
    427             if (now >= dtime) {
    428                 apr_dbm_delete(dbm, dbmkey);
    429                 deleted++;
    430             }
    431             apr_dbm_freedatum(dbm, dbmval);
    432         } else {
    433             apr_dbm_delete(dbm, dbmkey);
    434             deleted++;
    435         }
    436         total++;
    437         apr_dbm_nextkey(dbm, &dbmkey);
    438     }
    439     apr_dbm_close(dbm);
    440 
    441     rv = apr_global_mutex_unlock(sc->cache->mutex);
    442 
    443     ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
    444                  "Cleaned up cache '%s'. Deleted %d and left %d",
    445                  sc->cache_config, deleted, total - deleted);
    446 
    447     apr_pool_destroy(spool);
    448 
    449     return;
    450 }
    451 
    452 static gnutls_datum_t dbm_cache_fetch(mgs_handle_t *ctxt, gnutls_datum_t key)
    453 {
    454     gnutls_datum_t data = {NULL, 0};
    455     apr_dbm_t *dbm;
    456     apr_datum_t dbmkey = {(char*) key.data, key.size};
    457     apr_datum_t dbmval;
    458     apr_time_t expiry = 0;
    459     apr_status_t rv;
    460 
    461     /* check if it is time for cache expiration */
    462     dbm_cache_expire(ctxt->c->base_server);
    463 
    464     apr_global_mutex_lock(ctxt->sc->cache->mutex);
    465 
    466     rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
    467             ctxt->sc->cache_config, APR_DBM_READONLY,
    468             SSL_DBM_FILE_MODE, ctxt->c->pool);
    469     if (rv != APR_SUCCESS) {
    470         ap_log_cerror(APLOG_MARK, APLOG_NOTICE, rv, ctxt->c,
    471                       "error opening cache '%s'",
    472                       ctxt->sc->cache_config);
    473         apr_global_mutex_unlock(ctxt->sc->cache->mutex);
    474         return data;
    475     }
    476 
    477     rv = apr_dbm_fetch(dbm, dbmkey, &dbmval);
     191    apr_pool_create(&spool, pool);
     192
     193    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     194        apr_global_mutex_lock(cache->mutex);
     195    apr_status_t rv = cache->prov->retrieve(cache->socache, server,
     196                                            key.data, key.size,
     197                                            data.data, &data.size,
     198                                            spool);
     199    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     200        apr_global_mutex_unlock(cache->mutex);
    478201
    479202    if (rv != APR_SUCCESS)
    480         goto close_db;
    481 
    482     if (dbmval.dptr == NULL || dbmval.dsize <= sizeof (apr_time_t))
    483         goto cleanup;
    484 
    485     data.size = dbmval.dsize - sizeof (apr_time_t);
    486     /* get data expiration tag */
    487     expiry = *((apr_time_t *) dbmval.dptr);
    488 
    489     data.data = gnutls_malloc(data.size);
    490     if (data.data == NULL)
    491     {
    492         data.size = 0;
    493         goto cleanup;
    494     }
    495 
    496     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, ctxt->c,
    497                   "fetched %" APR_SIZE_T_FMT " bytes from cache",
    498                   dbmval.dsize);
    499 
    500     memcpy(data.data, dbmval.dptr + sizeof (apr_time_t), data.size);
    501 
    502  cleanup:
    503     apr_dbm_freedatum(dbm, dbmval);
    504  close_db:
    505     apr_dbm_close(dbm);
    506     apr_global_mutex_unlock(ctxt->sc->cache->mutex);
    507 
    508     /* cache entry might have expired since last cache cleanup */
    509     if (expiry != 0 && expiry < apr_time_now())
    510     {
     203    {
     204        /* APR_NOTFOUND means there's no such object. */
     205        if (rv == APR_NOTFOUND)
     206            ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
     207                         "requested entry not found in cache '%s:%s'.",
     208                         cache->prov->name, cache->config);
     209        else
     210            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, server,
     211                         "error fetching from cache '%s:%s'",
     212                         cache->prov->name, cache->config);
     213        /* free unused buffer */
    511214        gnutls_free(data.data);
    512215        data.data = NULL;
    513216        data.size = 0;
    514         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
    515                       "dropped expired cache data");
    516     }
     217    }
     218    else
     219    {
     220        ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
     221                     "fetched %u bytes from cache '%s:%s'",
     222                     data.size, cache->prov->name, cache->config);
     223
     224        /* Realloc buffer to data.size. Data size must be less than or
     225         * equal to the initial buffer size, so this REALLY should not
     226         * fail. */
     227        data.data = gnutls_realloc(data.data, data.size);
     228        if (__builtin_expect(data.data == NULL, 0))
     229        {
     230            ap_log_error(APLOG_MARK, APLOG_CRIT, APR_ENOMEM, server,
     231                         "%s: Could not realloc fetch buffer to data size!",
     232                         __func__);
     233            data.size = 0;
     234        }
     235    }
     236    apr_pool_destroy(spool);
    517237
    518238    return data;
    519239}
    520240
    521 static gnutls_datum_t dbm_cache_fetch_session(void *baton, gnutls_datum_t key)
     241
     242
     243/**
     244 * Fetch function for the GnuTLS session cache, see
     245 * gnutls_db_set_retrieve_function().
     246 *
     247 * *Warning*: The `data` element of the returned `gnutls_datum_t` is
     248 * allocated using `gnutls_malloc()` for compatibility with the GnuTLS
     249 * session caching API, and must be released using `gnutls_free()`.
     250 *
     251 * @param baton mgs_handle_t for the connection, as set via
     252 * gnutls_db_set_ptr()
     253 *
     254 * @param key object key to fetch
     255 *
     256 * @return the requested cache entry, or `{NULL, 0}`
     257 */
     258static gnutls_datum_t socache_fetch_session(void *baton, gnutls_datum_t key)
    522259{
    523260    gnutls_datum_t data = {NULL, 0};
     
    528265        return data;
    529266
    530     return dbm_cache_fetch(ctxt, dbmkey);
    531 }
    532 
    533 static int dbm_cache_store(server_rec *s, gnutls_datum_t key,
    534                            gnutls_datum_t data, apr_time_t expiry)
    535 {
    536     mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
    537         ap_get_module_config(s->module_config, &gnutls_module);
    538 
    539     apr_dbm_t *dbm;
    540     apr_datum_t dbmkey = {(char*) key.data, key.size};
    541     apr_datum_t dbmval;
    542     apr_status_t rv;
    543     apr_pool_t *spool;
    544 
    545     /* check if it is time for cache expiration */
    546     dbm_cache_expire(s);
    547 
    548     apr_pool_create(&spool, NULL);
    549 
    550     /* create DBM value */
    551     dbmval.dsize = data.size + sizeof (apr_time_t);
    552     dbmval.dptr = (char *) apr_palloc(spool, dbmval.dsize);
    553 
    554     /* prepend expiration time */
    555     memcpy((char *) dbmval.dptr, &expiry, sizeof (apr_time_t));
    556     memcpy((char *) dbmval.dptr + sizeof (apr_time_t),
    557             data.data, data.size);
    558 
    559     apr_global_mutex_lock(sc->cache->mutex);
    560 
    561     rv = apr_dbm_open_ex(&dbm, db_type(sc),
    562                          sc->cache_config, APR_DBM_RWCREATE,
    563                          SSL_DBM_FILE_MODE, spool);
    564     if (rv != APR_SUCCESS)
    565     {
    566         ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s,
    567                      "error opening cache '%s'",
    568                      sc->cache_config);
    569         apr_global_mutex_unlock(sc->cache->mutex);
    570         apr_pool_destroy(spool);
    571         return -1;
    572     }
    573 
    574     rv = apr_dbm_store(dbm, dbmkey, dbmval);
    575     if (rv != APR_SUCCESS)
    576     {
    577         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
    578                      "error storing in cache '%s'",
    579                      sc->cache_config);
    580         apr_dbm_close(dbm);
    581         apr_global_mutex_unlock(sc->cache->mutex);
    582         apr_pool_destroy(spool);
    583         return -1;
    584     }
    585 
    586     apr_dbm_close(dbm);
    587     apr_global_mutex_unlock(sc->cache->mutex);
    588 
    589     ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, s,
    590                  "stored %" APR_SIZE_T_FMT " bytes of data (%"
    591                  APR_SIZE_T_FMT " byte key) in cache '%s'",
    592                  dbmval.dsize, dbmkey.dsize, sc->cache_config);
    593 
    594     apr_pool_destroy(spool);
    595 
    596     return 0;
    597 }
    598 
    599 static int dbm_cache_store_session(void *baton, gnutls_datum_t key,
    600                                    gnutls_datum_t data)
    601 {
    602     mgs_handle_t *ctxt = baton;
    603     gnutls_datum_t dbmkey;
    604 
    605     if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
    606         return -1;
    607 
    608     apr_time_t expiry = apr_time_now() + ctxt->sc->cache_timeout;
    609 
    610     return dbm_cache_store(ctxt->c->base_server, dbmkey, data, expiry);
    611 }
    612 
    613 static int dbm_cache_delete(void *baton, gnutls_datum_t key)
    614 {
    615     apr_dbm_t *dbm;
     267    return mgs_cache_fetch(ctxt->sc->cache, ctxt->c->base_server,
     268                           dbmkey, ctxt->c->pool);
     269}
     270
     271
     272
     273/**
     274 * Remove function for the GnuTLS session cache, see
     275 * gnutls_db_set_remove_function().
     276 *
     277 * @param baton mgs_handle_t for the connection, as set via
     278 * gnutls_db_set_ptr()
     279 *
     280 * @param key object key to remove
     281 *
     282 * @return `0` in case of success, `-1` in case of failure
     283 */
     284static int socache_delete_session(void *baton, gnutls_datum_t key)
     285{
    616286    gnutls_datum_t tmpkey;
    617287    mgs_handle_t *ctxt = baton;
    618     apr_status_t rv;
    619288
    620289    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &tmpkey) < 0)
    621290        return -1;
    622     apr_datum_t dbmkey = {(char*) tmpkey.data, tmpkey.size};
    623 
    624     apr_global_mutex_lock(ctxt->sc->cache->mutex);
    625 
    626     rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
    627             ctxt->sc->cache_config, APR_DBM_RWCREATE,
    628             SSL_DBM_FILE_MODE, ctxt->c->pool);
     291
     292    if (ctxt->sc->cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     293        apr_global_mutex_lock(ctxt->sc->cache->mutex);
     294    apr_status_t rv = ctxt->sc->cache->prov->remove(ctxt->sc->cache->socache,
     295                                                    ctxt->c->base_server,
     296                                                    key.data, key.size,
     297                                                    ctxt->c->pool);
     298    if (ctxt->sc->cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     299        apr_global_mutex_unlock(ctxt->sc->cache->mutex);
     300
    629301    if (rv != APR_SUCCESS) {
    630302        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
    631303                     ctxt->c->base_server,
    632                      "error opening cache '%s'",
    633                      ctxt->sc->cache_config);
    634         apr_global_mutex_unlock(ctxt->sc->cache->mutex);
     304                     "error deleting from cache '%s:%s'",
     305                     ctxt->sc->cache->prov->name, ctxt->sc->cache->config);
    635306        return -1;
    636307    }
    637 
    638     rv = apr_dbm_delete(dbm, dbmkey);
    639 
    640     if (rv != APR_SUCCESS) {
    641         ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
    642                      ctxt->c->base_server,
    643                      "error deleting from cache '%s'",
    644                      ctxt->sc->cache_config);
    645         apr_dbm_close(dbm);
    646         apr_global_mutex_unlock(ctxt->sc->cache->mutex);
    647         return -1;
    648     }
    649 
    650     apr_dbm_close(dbm);
    651     apr_global_mutex_unlock(ctxt->sc->cache->mutex);
    652 
    653308    return 0;
    654309}
    655310
    656 static int dbm_cache_post_config(apr_pool_t * p, server_rec * s,
    657         mgs_srvconf_rec * sc) {
    658     apr_status_t rv;
    659     apr_dbm_t *dbm;
    660     const char *path1;
    661     const char *path2;
    662 
    663     rv = apr_dbm_open_ex(&dbm, db_type(sc), sc->cache_config,
    664             APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, p);
    665 
    666     if (rv != APR_SUCCESS) {
    667         ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
    668                 "GnuTLS: Cannot create DBM Cache at `%s'",
    669                 sc->cache_config);
    670         return rv;
    671     }
    672 
    673     apr_dbm_close(dbm);
    674 
    675     apr_dbm_get_usednames_ex(p, db_type(sc), sc->cache_config, &path1,
    676             &path2);
    677 
    678     /* The Following Code takes logic directly from mod_ssl's DBM Cache */
    679 #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
    680     /* Running as Root */
    681     if (path1 && geteuid() == 0) {
    682         if (0 != chown(path1, ap_unixd_config.user_id, -1))
    683             ap_log_error(APLOG_MARK, APLOG_NOTICE, -1, s,
    684                          "GnuTLS: could not chown cache path1 `%s' to uid %d (errno: %d)",
    685                          path1, ap_unixd_config.user_id, errno);
    686         if (path2 != NULL) {
    687             if (0 != chown(path2, ap_unixd_config.user_id, -1))
    688                 ap_log_error(APLOG_MARK, APLOG_NOTICE, -1, s,
    689                              "GnuTLS: could not chown cache path2 `%s' to uid %d (errno: %d)",
    690                              path2, ap_unixd_config.user_id, errno);
    691         }
    692     }
    693 #endif
    694 
     311
     312
     313const char *mgs_cache_inst_config(mgs_cache_t *cache, server_rec *server,
     314                                  const char* type, const char* config,
     315                                  apr_pool_t *pconf, apr_pool_t *ptemp)
     316{
     317    /* Allocate cache structure, will be assigned to *cache after
     318     * successful configuration. */
     319    mgs_cache_t c = apr_pcalloc(pconf, sizeof(struct mgs_cache));
     320    if (c == NULL)
     321        return "Could not allocate memory for cache configuration!";
     322
     323    /* Find the right socache provider */
     324    c->prov = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
     325                                 type,
     326                                 AP_SOCACHE_PROVIDER_VERSION);
     327    if (c->prov == NULL)
     328    {
     329        return apr_psprintf(ptemp,
     330                            "Could not find socache provider '%s', please "
     331                            "make sure that the provider name is valid and "
     332                            "the appropriate module is loaded (maybe "
     333                            "mod_socache_%s.so?).",
     334                            type, type);
     335    }
     336
     337    /* shmcb works fine with NULL, but make sure there's a valid (if
     338     * empty) string for logging */
     339    if (config != NULL)
     340        c->config = apr_pstrdup(pconf, config);
     341    else
     342        c->config = "";
     343
     344    /* Create and configure the cache instance. */
     345    const char *err = c->prov->create(&c->socache, c->config, ptemp, pconf);
     346    if (err != NULL)
     347    {
     348        return apr_psprintf(ptemp,
     349                            "Creating cache '%s:%s' failed: %s",
     350                            c->prov->name, c->config, err);
     351    }
     352    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, server,
     353                 "%s: Socache '%s:%s' created.",
     354                 __func__, c->prov->name, c->config);
     355
     356    /* assign configured cache structure to server */
     357    *cache = c;
     358
     359    return NULL;
     360}
     361
     362
     363
     364/**
     365 * This function is supposed to be called during post_config to
     366 * initialize mutex and socache instance associated with an
     367 * mgs_cache_t.
     368 *
     369 * @param cache the mod_gnutls cache structure
     370 *
     371 * @param cache_name name for socache initialization
     372 *
     373 * @param mutex_name name to pass to ap_global_mutex_create(), must
     374 * have been registered during pre_config.
     375 *
     376 * @param server server for logging purposes
     377 *
     378 * @param pconf memory pool for server configuration
     379 */
     380static apr_status_t mgs_cache_inst_init(mgs_cache_t cache,
     381                                        const char *cache_name,
     382                                        const char *mutex_name,
     383                                        server_rec *server,
     384                                        apr_pool_t *pconf)
     385{
     386    apr_status_t rv = APR_SUCCESS;
     387
     388    if (cache->mutex == NULL)
     389    {
     390        rv = ap_global_mutex_create(&cache->mutex, NULL,
     391                                    mutex_name,
     392                                    NULL, server, pconf, 0);
     393        ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, server,
     394                     "%s: create mutex", __func__);
     395        if (rv != APR_SUCCESS)
     396            return rv;
     397    }
     398
     399    rv = cache->prov->init(cache->socache, cache_name, NULL, server, pconf);
     400    if (rv != APR_SUCCESS)
     401        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, server,
     402                     "Initializing cache '%s:%s' failed!",
     403                     cache->prov->name, cache->config);
     404    else
     405        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, server,
     406                     "%s: socache '%s:%s' initialized.", __func__,
     407                     cache->prov->name, cache->config);
    695408    return rv;
    696409}
    697410
    698 int mgs_cache_post_config(apr_pool_t * p, server_rec * s,
    699         mgs_srvconf_rec * sc) {
    700 
    701     /* if GnuTLSCache was never explicitly set: */
    702     if (sc->cache_type == mgs_cache_unset)
    703         sc->cache_type = mgs_cache_none;
     411
     412
     413static apr_status_t cleanup_socache(void *data)
     414{
     415    server_rec *s = data;
     416    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
     417        ap_get_module_config(s->module_config, &gnutls_module);
     418    if (sc->cache)
     419    {
     420        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
     421                     "Cleaning up session cache '%s:%s'",
     422                     sc->cache->prov->name, sc->cache->config);
     423        sc->cache->prov->destroy(sc->cache->socache, s);
     424    }
     425    if (sc->ocsp_cache)
     426    {
     427        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
     428                     "Cleaning up OCSP cache '%s:%s'",
     429                     sc->ocsp_cache->prov->name, sc->ocsp_cache->config);
     430        sc->ocsp_cache->prov->destroy(sc->ocsp_cache->socache, s);
     431    }
     432    return APR_SUCCESS;
     433}
     434
     435
     436
     437int mgs_cache_post_config(apr_pool_t *pconf, apr_pool_t *ptemp,
     438                          server_rec *s, mgs_srvconf_rec *sc)
     439{
     440    apr_status_t rv = APR_SUCCESS;
     441
     442    /* If the OCSP cache is unconfigured initialize it with
     443     * defaults. */
     444    if (sc->ocsp_cache == NULL)
     445    {
     446        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
     447                     "%s: OCSP cache unconfigured, using '%s:%s'.", __func__,
     448                     DEFAULT_OCSP_CACHE_TYPE, DEFAULT_OCSP_CACHE_CONF);
     449        const char *err = mgs_cache_inst_config(&sc->ocsp_cache, s,
     450                                                DEFAULT_OCSP_CACHE_TYPE,
     451                                                DEFAULT_OCSP_CACHE_CONF,
     452                                                pconf, ptemp);
     453        if (err != NULL)
     454            ap_log_error(APLOG_MARK, APLOG_WARNING, rv, s,
     455                         "%s: Configuring default OCSP cache '%s:%s' failed, "
     456                         "make sure that mod_socache_%s is loaded.", __func__,
     457                         DEFAULT_OCSP_CACHE_TYPE, DEFAULT_OCSP_CACHE_CONF,
     458                         DEFAULT_OCSP_CACHE_TYPE);
     459    }
     460
     461    /* Initialize the OCSP cache first so it's not skipped if the
     462     * session cache is disabled. */
     463    if (sc->ocsp_cache != NULL)
     464    {
     465        /* TODO: Maybe initialize only if explicitly enabled OR at
     466         * least one (virtual) host has OCSP enabled? */
     467        rv = mgs_cache_inst_init(sc->ocsp_cache, MGS_OCSP_CACHE_NAME,
     468                                 MGS_OCSP_CACHE_MUTEX_NAME, s, pconf);
     469        if (rv != APR_SUCCESS)
     470            return HTTP_INSUFFICIENT_STORAGE;
     471    }
     472
     473    /* GnuTLSCache was never explicitly set or is disabled: */
     474    if (sc->cache_enable == GNUTLS_ENABLED_UNSET
     475        || sc->cache_enable == GNUTLS_ENABLED_FALSE)
     476    {
     477        sc->cache_enable = GNUTLS_ENABLED_FALSE;
     478        /* Cache disabled, done. */
     479        return APR_SUCCESS;
     480    }
    704481    /* if GnuTLSCacheTimeout was never explicitly set: */
    705482    if (sc->cache_timeout == MGS_TIMEOUT_UNSET)
    706483        sc->cache_timeout = apr_time_from_sec(MGS_DEFAULT_CACHE_TIMEOUT);
    707484
    708     /* initialize mutex only once */
    709     if (sc->cache == NULL)
    710     {
    711         sc->cache = apr_palloc(p, sizeof(struct mgs_cache));
    712         apr_status_t rv = ap_global_mutex_create(&sc->cache->mutex, NULL,
    713                                                  MGS_CACHE_MUTEX_NAME,
    714                                                  NULL, s, p, 0);
    715         if (rv != APR_SUCCESS)
    716             return rv;
    717     }
    718 
    719     if (sc->cache_type == mgs_cache_dbm || sc->cache_type == mgs_cache_gdbm)
    720     {
    721         sc->cache->store = dbm_cache_store;
    722         sc->cache->fetch = dbm_cache_fetch;
    723         return dbm_cache_post_config(p, s, sc);
    724     }
    725 #if HAVE_APR_MEMCACHE
    726     else if (sc->cache_type == mgs_cache_memcache)
    727     {
    728         sc->cache->store = mc_cache_store_generic;
    729         sc->cache->fetch = mc_cache_fetch_generic;
    730     }
    731 #endif
     485    rv = mgs_cache_inst_init(sc->cache, MGS_SESSION_CACHE_NAME,
     486                             MGS_CACHE_MUTEX_NAME, s, pconf);
     487    if (rv != APR_SUCCESS)
     488        return HTTP_INSUFFICIENT_STORAGE;
     489
     490    apr_pool_pre_cleanup_register(pconf, s, cleanup_socache);
    732491
    733492    return APR_SUCCESS;
    734493}
    735494
    736 int mgs_cache_child_init(apr_pool_t * p,
    737                          server_rec * s,
    738                          mgs_srvconf_rec * sc)
     495int mgs_cache_child_init(apr_pool_t *p, server_rec *server,
     496                         mgs_cache_t cache, const char *mutex_name)
    739497{
    740498    /* reinit cache mutex */
    741     const char *lockfile = apr_global_mutex_lockfile(sc->cache->mutex);
    742     apr_status_t rv = apr_global_mutex_child_init(&sc->cache->mutex,
     499    const char *lockfile = apr_global_mutex_lockfile(cache->mutex);
     500    apr_status_t rv = apr_global_mutex_child_init(&cache->mutex,
    743501                                                  lockfile, p);
    744502    if (rv != APR_SUCCESS)
    745         ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
    746                      "Failed to reinit mutex '%s'", MGS_CACHE_MUTEX_NAME);
    747 
    748     if (sc->cache_type == mgs_cache_dbm
    749             || sc->cache_type == mgs_cache_gdbm) {
    750         return 0;
    751     }
    752 #if HAVE_APR_MEMCACHE
    753     else if (sc->cache_type == mgs_cache_memcache) {
    754         return mc_cache_child_init(p, s, sc);
    755     }
    756 #endif
     503        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, server,
     504                     "Failed to reinit mutex '%s'", mutex_name);
     505
     506    return rv;
     507}
     508
     509int mgs_cache_session_init(mgs_handle_t * ctxt)
     510{
     511    if (ctxt->sc->cache_enable)
     512    {
     513        gnutls_db_set_retrieve_function(ctxt->session,
     514                                        socache_fetch_session);
     515        gnutls_db_set_remove_function(ctxt->session,
     516                                      socache_delete_session);
     517        gnutls_db_set_store_function(ctxt->session,
     518                                     socache_store_session);
     519        gnutls_db_set_ptr(ctxt->session, ctxt);
     520    }
    757521    return 0;
    758522}
    759523
    760 #include <assert.h>
    761 
    762 int mgs_cache_session_init(mgs_handle_t * ctxt) {
    763     if (ctxt->sc->cache_type == mgs_cache_dbm
    764             || ctxt->sc->cache_type == mgs_cache_gdbm) {
    765         gnutls_db_set_retrieve_function(ctxt->session,
    766                 dbm_cache_fetch_session);
    767         gnutls_db_set_remove_function(ctxt->session,
    768                 dbm_cache_delete);
    769         gnutls_db_set_store_function(ctxt->session,
    770                 dbm_cache_store_session);
    771         gnutls_db_set_ptr(ctxt->session, ctxt);
    772     }
    773 #if HAVE_APR_MEMCACHE
    774     else if (ctxt->sc->cache_type == mgs_cache_memcache) {
    775         gnutls_db_set_retrieve_function(ctxt->session,
    776                 mc_cache_fetch_session);
    777         gnutls_db_set_remove_function(ctxt->session,
    778                 mc_cache_delete);
    779         gnutls_db_set_store_function(ctxt->session,
    780                 mc_cache_store_session);
    781         gnutls_db_set_ptr(ctxt->session, ctxt);
    782     }
    783 #endif
    784 
    785     return 0;
    786 }
     524
     525
     526int mgs_cache_status(mgs_cache_t cache, const char *header_title,
     527                     request_rec *r, int flags)
     528{
     529    if (!(flags & AP_STATUS_SHORT))
     530        ap_rprintf(r, "<h3>%s:</h3>\n", header_title);
     531    else
     532        ap_rprintf(r, "%s:\n", header_title);
     533
     534    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     535        apr_global_mutex_lock(cache->mutex);
     536    cache->prov->status(cache->socache, r, flags);
     537    if (cache->prov->flags & AP_SOCACHE_FLAG_NOTMPSAFE)
     538        apr_global_mutex_unlock(cache->mutex);
     539
     540    return OK;
     541}
Note: See TracChangeset for help on using the changeset viewer.