[14548b9] | 1 | /* |
---|
[fcb122d] | 2 | * Copyright 2004-2005 Paul Querna |
---|
[e183628] | 3 | * Copyright 2008 Nikos Mavrogiannopoulos |
---|
| 4 | * Copyright 2011 Dash Shendy |
---|
[8913410] | 5 | * Copyright 2015-2016 Thomas 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 |
---|
| 85 | APLOG_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] | 95 | static 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 | |
---|
| 117 | char *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] | 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) |
---|
[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] | 158 | static apr_memcache_t *mc; |
---|
[a66e147] | 159 | |
---|
[e02dd8c] | 160 | static 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] | 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); |
---|
[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] | 257 | static 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] | 280 | static 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 | |
---|
| 294 | static gnutls_datum_t mc_cache_fetch(conn_rec *c, const char *key) |
---|
| 295 | { |
---|
| 296 | apr_status_t rv = APR_SUCCESS; |
---|
[e183628] | 297 | char *value; |
---|
| 298 | apr_size_t value_len; |
---|
| 299 | gnutls_datum_t data = {NULL, 0}; |
---|
[e02dd8c] | 300 | |
---|
[3e22b82] | 301 | rv = apr_memcache_getp(mc, c->pool, key, &value, &value_len, NULL); |
---|
[e02dd8c] | 302 | |
---|
[3e22b82] | 303 | if (rv != APR_SUCCESS) |
---|
| 304 | { |
---|
[316bd8c] | 305 | #if MOD_GNUTLS_DEBUG |
---|
[3e22b82] | 306 | ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, |
---|
[9c456a9] | 307 | "error fetching key '%s'", |
---|
[3e22b82] | 308 | key); |
---|
[316bd8c] | 309 | #endif |
---|
[e183628] | 310 | return data; |
---|
| 311 | } |
---|
[a66e147] | 312 | |
---|
[e183628] | 313 | /* TODO: Eliminate this memcpy. gnutls-- */ |
---|
| 314 | data.data = gnutls_malloc(value_len); |
---|
| 315 | if (data.data == NULL) |
---|
| 316 | return data; |
---|
[a66e147] | 317 | |
---|
[e183628] | 318 | data.size = value_len; |
---|
| 319 | memcpy(data.data, value, value_len); |
---|
[a66e147] | 320 | |
---|
[e183628] | 321 | return data; |
---|
[32f2e60] | 322 | } |
---|
| 323 | |
---|
[e809fb3] | 324 | static gnutls_datum_t mc_cache_fetch_generic(mgs_handle_t *ctxt, |
---|
| 325 | gnutls_datum_t key) |
---|
[3e22b82] | 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 | |
---|
[e183628] | 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; |
---|
[a66e147] | 352 | |
---|
[e183628] | 353 | strkey = mgs_session_id2mc(ctxt->c, key.data, key.size); |
---|
| 354 | if (!strkey) |
---|
| 355 | return -1; |
---|
[a66e147] | 356 | |
---|
[e183628] | 357 | rv = apr_memcache_delete(mc, strkey, 0); |
---|
[a66e147] | 358 | |
---|
[e183628] | 359 | if (rv != APR_SUCCESS) { |
---|
| 360 | ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, |
---|
[9c456a9] | 361 | ctxt->c->base_server, |
---|
| 362 | "error deleting key '%s'", |
---|
| 363 | strkey); |
---|
[e183628] | 364 | return -1; |
---|
| 365 | } |
---|
[a66e147] | 366 | |
---|
[e183628] | 367 | return 0; |
---|
[32f2e60] | 368 | } |
---|
| 369 | |
---|
[410d216] | 370 | #endif /* have_apr_memcache */ |
---|
[6e0bfd6] | 371 | |
---|
[410d216] | 372 | static const char *db_type(mgs_srvconf_rec * sc) { |
---|
[e183628] | 373 | if (sc->cache_type == mgs_cache_gdbm) |
---|
| 374 | return "gdbm"; |
---|
| 375 | else |
---|
| 376 | return "db"; |
---|
[771ca63] | 377 | } |
---|
| 378 | |
---|
[fcb122d] | 379 | #define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD ) |
---|
| 380 | |
---|
[f785704] | 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 | |
---|
[e183628] | 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; |
---|
| 391 | apr_pool_t *spool; |
---|
| 392 | int total, deleted; |
---|
| 393 | |
---|
[f785704] | 394 | apr_time_t now = apr_time_now(); |
---|
[e183628] | 395 | |
---|
[f785704] | 396 | if (now - sc->last_cache_check < (sc->cache_timeout) / 2) |
---|
[e183628] | 397 | return; |
---|
| 398 | |
---|
[f785704] | 399 | sc->last_cache_check = now; |
---|
[e183628] | 400 | |
---|
[f785704] | 401 | apr_pool_create(&spool, NULL); |
---|
[e183628] | 402 | |
---|
| 403 | total = 0; |
---|
| 404 | deleted = 0; |
---|
| 405 | |
---|
[aa68232] | 406 | apr_global_mutex_lock(sc->cache->mutex); |
---|
[c005645] | 407 | |
---|
[f785704] | 408 | rv = apr_dbm_open_ex(&dbm, db_type(sc), |
---|
| 409 | sc->cache_config, APR_DBM_RWCREATE, |
---|
[e183628] | 410 | SSL_DBM_FILE_MODE, spool); |
---|
| 411 | if (rv != APR_SUCCESS) { |
---|
[f785704] | 412 | ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s, |
---|
[9c456a9] | 413 | "error opening cache '%s'", |
---|
| 414 | sc->cache_config); |
---|
[aa68232] | 415 | apr_global_mutex_unlock(sc->cache->mutex); |
---|
[e183628] | 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 | |
---|
[aa68232] | 441 | rv = apr_global_mutex_unlock(sc->cache->mutex); |
---|
[c005645] | 442 | |
---|
[f785704] | 443 | ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, |
---|
[9c456a9] | 444 | "Cleaned up cache '%s'. Deleted %d and left %d", |
---|
| 445 | sc->cache_config, deleted, total - deleted); |
---|
[e183628] | 446 | |
---|
| 447 | apr_pool_destroy(spool); |
---|
| 448 | |
---|
| 449 | return; |
---|
[fcb122d] | 450 | } |
---|
| 451 | |
---|
[e809fb3] | 452 | static gnutls_datum_t dbm_cache_fetch(mgs_handle_t *ctxt, gnutls_datum_t key) |
---|
[15245bf] | 453 | { |
---|
[e183628] | 454 | gnutls_datum_t data = {NULL, 0}; |
---|
| 455 | apr_dbm_t *dbm; |
---|
[2f932fa] | 456 | apr_datum_t dbmkey = {(char*) key.data, key.size}; |
---|
[e183628] | 457 | apr_datum_t dbmval; |
---|
[d18afb8] | 458 | apr_time_t expiry = 0; |
---|
[e183628] | 459 | apr_status_t rv; |
---|
| 460 | |
---|
[c55902b] | 461 | /* check if it is time for cache expiration */ |
---|
| 462 | dbm_cache_expire(ctxt->c->base_server); |
---|
| 463 | |
---|
[aa68232] | 464 | apr_global_mutex_lock(ctxt->sc->cache->mutex); |
---|
[c005645] | 465 | |
---|
[e183628] | 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) { |
---|
[15245bf] | 470 | ap_log_cerror(APLOG_MARK, APLOG_NOTICE, rv, ctxt->c, |
---|
| 471 | "error opening cache '%s'", |
---|
| 472 | ctxt->sc->cache_config); |
---|
[aa68232] | 473 | apr_global_mutex_unlock(ctxt->sc->cache->mutex); |
---|
[e183628] | 474 | return data; |
---|
| 475 | } |
---|
| 476 | |
---|
[2f932fa] | 477 | rv = apr_dbm_fetch(dbm, dbmkey, &dbmval); |
---|
[e183628] | 478 | |
---|
[c55902b] | 479 | if (rv != APR_SUCCESS) |
---|
| 480 | goto close_db; |
---|
[e183628] | 481 | |
---|
[c55902b] | 482 | if (dbmval.dptr == NULL || dbmval.dsize <= sizeof (apr_time_t)) |
---|
| 483 | goto cleanup; |
---|
[e183628] | 484 | |
---|
| 485 | data.size = dbmval.dsize - sizeof (apr_time_t); |
---|
[d18afb8] | 486 | /* get data expiration tag */ |
---|
| 487 | expiry = *((apr_time_t *) dbmval.dptr); |
---|
[e183628] | 488 | |
---|
| 489 | data.data = gnutls_malloc(data.size); |
---|
[c55902b] | 490 | if (data.data == NULL) |
---|
[11e6205] | 491 | { |
---|
| 492 | data.size = 0; |
---|
[c55902b] | 493 | goto cleanup; |
---|
[11e6205] | 494 | } |
---|
[e183628] | 495 | |
---|
[9c456a9] | 496 | ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, ctxt->c, |
---|
[5ac4bbe] | 497 | "fetched %" APR_SIZE_T_FMT " bytes from cache", |
---|
[15245bf] | 498 | dbmval.dsize); |
---|
| 499 | |
---|
[e183628] | 500 | memcpy(data.data, dbmval.dptr + sizeof (apr_time_t), data.size); |
---|
| 501 | |
---|
[c55902b] | 502 | cleanup: |
---|
[e183628] | 503 | apr_dbm_freedatum(dbm, dbmval); |
---|
[c55902b] | 504 | close_db: |
---|
[e183628] | 505 | apr_dbm_close(dbm); |
---|
[aa68232] | 506 | apr_global_mutex_unlock(ctxt->sc->cache->mutex); |
---|
[e183628] | 507 | |
---|
[d18afb8] | 508 | /* cache entry might have expired since last cache cleanup */ |
---|
| 509 | if (expiry != 0 && expiry < apr_time_now()) |
---|
| 510 | { |
---|
| 511 | gnutls_free(data.data); |
---|
| 512 | data.data = NULL; |
---|
| 513 | data.size = 0; |
---|
[9c456a9] | 514 | ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c, |
---|
[d18afb8] | 515 | "dropped expired cache data"); |
---|
| 516 | } |
---|
| 517 | |
---|
[e183628] | 518 | return data; |
---|
[fcb122d] | 519 | } |
---|
| 520 | |
---|
[2f932fa] | 521 | static gnutls_datum_t dbm_cache_fetch_session(void *baton, gnutls_datum_t key) |
---|
[15245bf] | 522 | { |
---|
| 523 | gnutls_datum_t data = {NULL, 0}; |
---|
[2f932fa] | 524 | gnutls_datum_t dbmkey; |
---|
[15245bf] | 525 | mgs_handle_t *ctxt = baton; |
---|
| 526 | |
---|
| 527 | if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0) |
---|
| 528 | return data; |
---|
| 529 | |
---|
| 530 | return dbm_cache_fetch(ctxt, dbmkey); |
---|
| 531 | } |
---|
| 532 | |
---|
[e809fb3] | 533 | static int dbm_cache_store(server_rec *s, gnutls_datum_t key, |
---|
| 534 | gnutls_datum_t data, apr_time_t expiry) |
---|
[ae08186] | 535 | { |
---|
[1d1361f] | 536 | mgs_srvconf_rec *sc = (mgs_srvconf_rec *) |
---|
| 537 | ap_get_module_config(s->module_config, &gnutls_module); |
---|
| 538 | |
---|
[e183628] | 539 | apr_dbm_t *dbm; |
---|
[2f932fa] | 540 | apr_datum_t dbmkey = {(char*) key.data, key.size}; |
---|
[e183628] | 541 | apr_datum_t dbmval; |
---|
| 542 | apr_status_t rv; |
---|
| 543 | apr_pool_t *spool; |
---|
| 544 | |
---|
[c55902b] | 545 | /* check if it is time for cache expiration */ |
---|
[1d1361f] | 546 | dbm_cache_expire(s); |
---|
[e183628] | 547 | |
---|
[1d1361f] | 548 | apr_pool_create(&spool, NULL); |
---|
[e183628] | 549 | |
---|
| 550 | /* create DBM value */ |
---|
| 551 | dbmval.dsize = data.size + sizeof (apr_time_t); |
---|
| 552 | dbmval.dptr = (char *) apr_palloc(spool, dbmval.dsize); |
---|
| 553 | |
---|
[ae08186] | 554 | /* prepend expiration time */ |
---|
[e183628] | 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 | |
---|
[aa68232] | 559 | apr_global_mutex_lock(sc->cache->mutex); |
---|
[c005645] | 560 | |
---|
[1d1361f] | 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); |
---|
[aa68232] | 569 | apr_global_mutex_unlock(sc->cache->mutex); |
---|
[e183628] | 570 | apr_pool_destroy(spool); |
---|
| 571 | return -1; |
---|
| 572 | } |
---|
| 573 | |
---|
[2f932fa] | 574 | rv = apr_dbm_store(dbm, dbmkey, dbmval); |
---|
[1d1361f] | 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); |
---|
[e183628] | 580 | apr_dbm_close(dbm); |
---|
[aa68232] | 581 | apr_global_mutex_unlock(sc->cache->mutex); |
---|
[e183628] | 582 | apr_pool_destroy(spool); |
---|
| 583 | return -1; |
---|
| 584 | } |
---|
| 585 | |
---|
[c005645] | 586 | apr_dbm_close(dbm); |
---|
[aa68232] | 587 | apr_global_mutex_unlock(sc->cache->mutex); |
---|
[c005645] | 588 | |
---|
[9c456a9] | 589 | ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, s, |
---|
[5ac4bbe] | 590 | "stored %" APR_SIZE_T_FMT " bytes of data (%" |
---|
| 591 | APR_SIZE_T_FMT " byte key) in cache '%s'", |
---|
[2f932fa] | 592 | dbmval.dsize, dbmkey.dsize, sc->cache_config); |
---|
[1d1361f] | 593 | |
---|
[e183628] | 594 | apr_pool_destroy(spool); |
---|
| 595 | |
---|
| 596 | return 0; |
---|
[fcb122d] | 597 | } |
---|
| 598 | |
---|
[ae08186] | 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; |
---|
[2f932fa] | 603 | gnutls_datum_t dbmkey; |
---|
[ae08186] | 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 | |
---|
[1d1361f] | 610 | return dbm_cache_store(ctxt->c->base_server, dbmkey, data, expiry); |
---|
[ae08186] | 611 | } |
---|
| 612 | |
---|
[c55902b] | 613 | static int dbm_cache_delete(void *baton, gnutls_datum_t key) |
---|
| 614 | { |
---|
[e183628] | 615 | apr_dbm_t *dbm; |
---|
[2f932fa] | 616 | gnutls_datum_t tmpkey; |
---|
[e183628] | 617 | mgs_handle_t *ctxt = baton; |
---|
| 618 | apr_status_t rv; |
---|
| 619 | |
---|
[2f932fa] | 620 | if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &tmpkey) < 0) |
---|
[e183628] | 621 | return -1; |
---|
[2f932fa] | 622 | apr_datum_t dbmkey = {(char*) tmpkey.data, tmpkey.size}; |
---|
[e183628] | 623 | |
---|
[aa68232] | 624 | apr_global_mutex_lock(ctxt->sc->cache->mutex); |
---|
[c005645] | 625 | |
---|
[e183628] | 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); |
---|
| 629 | if (rv != APR_SUCCESS) { |
---|
| 630 | ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, |
---|
[9c456a9] | 631 | ctxt->c->base_server, |
---|
| 632 | "error opening cache '%s'", |
---|
| 633 | ctxt->sc->cache_config); |
---|
[aa68232] | 634 | apr_global_mutex_unlock(ctxt->sc->cache->mutex); |
---|
[e183628] | 635 | return -1; |
---|
| 636 | } |
---|
| 637 | |
---|
| 638 | rv = apr_dbm_delete(dbm, dbmkey); |
---|
| 639 | |
---|
| 640 | if (rv != APR_SUCCESS) { |
---|
| 641 | ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, |
---|
[9c456a9] | 642 | ctxt->c->base_server, |
---|
| 643 | "error deleting from cache '%s'", |
---|
| 644 | ctxt->sc->cache_config); |
---|
[e183628] | 645 | apr_dbm_close(dbm); |
---|
[aa68232] | 646 | apr_global_mutex_unlock(ctxt->sc->cache->mutex); |
---|
[e183628] | 647 | return -1; |
---|
| 648 | } |
---|
| 649 | |
---|
| 650 | apr_dbm_close(dbm); |
---|
[aa68232] | 651 | apr_global_mutex_unlock(ctxt->sc->cache->mutex); |
---|
[e183628] | 652 | |
---|
| 653 | return 0; |
---|
[fcb122d] | 654 | } |
---|
| 655 | |
---|
[e02dd8c] | 656 | static int dbm_cache_post_config(apr_pool_t * p, server_rec * s, |
---|
[e183628] | 657 | mgs_srvconf_rec * sc) { |
---|
| 658 | apr_status_t rv; |
---|
| 659 | apr_dbm_t *dbm; |
---|
| 660 | const char *path1; |
---|
| 661 | const char *path2; |
---|
[fcb122d] | 662 | |
---|
[e183628] | 663 | rv = apr_dbm_open_ex(&dbm, db_type(sc), sc->cache_config, |
---|
| 664 | APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, p); |
---|
[fcb122d] | 665 | |
---|
[e183628] | 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 | } |
---|
[fcb122d] | 672 | |
---|
[e183628] | 673 | apr_dbm_close(dbm); |
---|
[fcb122d] | 674 | |
---|
[e183628] | 675 | apr_dbm_get_usednames_ex(p, db_type(sc), sc->cache_config, &path1, |
---|
| 676 | &path2); |
---|
[fcb122d] | 677 | |
---|
[e183628] | 678 | /* The Following Code takes logic directly from mod_ssl's DBM Cache */ |
---|
[fcb122d] | 679 | #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) |
---|
[e183628] | 680 | /* Running as Root */ |
---|
| 681 | if (path1 && geteuid() == 0) { |
---|
[422f5b7] | 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); |
---|
[e183628] | 686 | if (path2 != NULL) { |
---|
[422f5b7] | 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); |
---|
[e183628] | 691 | } |
---|
| 692 | } |
---|
[fcb122d] | 693 | #endif |
---|
| 694 | |
---|
[e183628] | 695 | return rv; |
---|
[fcb122d] | 696 | } |
---|
| 697 | |
---|
[e02dd8c] | 698 | int mgs_cache_post_config(apr_pool_t * p, server_rec * s, |
---|
[e183628] | 699 | mgs_srvconf_rec * sc) { |
---|
[040387c] | 700 | |
---|
| 701 | /* if GnuTLSCache was never explicitly set: */ |
---|
| 702 | if (sc->cache_type == mgs_cache_unset) |
---|
| 703 | sc->cache_type = mgs_cache_none; |
---|
| 704 | /* if GnuTLSCacheTimeout was never explicitly set: */ |
---|
[c39ae1a] | 705 | if (sc->cache_timeout == MGS_TIMEOUT_UNSET) |
---|
[c005645] | 706 | sc->cache_timeout = apr_time_from_sec(MGS_DEFAULT_CACHE_TIMEOUT); |
---|
| 707 | |
---|
| 708 | /* initialize mutex only once */ |
---|
[aa68232] | 709 | if (sc->cache == NULL) |
---|
[c005645] | 710 | { |
---|
[aa68232] | 711 | sc->cache = apr_palloc(p, sizeof(struct mgs_cache)); |
---|
| 712 | apr_status_t rv = ap_global_mutex_create(&sc->cache->mutex, NULL, |
---|
[c005645] | 713 | MGS_CACHE_MUTEX_NAME, |
---|
| 714 | NULL, s, p, 0); |
---|
| 715 | if (rv != APR_SUCCESS) |
---|
| 716 | return rv; |
---|
| 717 | } |
---|
[040387c] | 718 | |
---|
[e809fb3] | 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; |
---|
[e183628] | 723 | return dbm_cache_post_config(p, s, sc); |
---|
| 724 | } |
---|
[e809fb3] | 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 |
---|
[c005645] | 732 | |
---|
| 733 | return APR_SUCCESS; |
---|
[fcb122d] | 734 | } |
---|
| 735 | |
---|
[e765670] | 736 | int mgs_cache_child_init(apr_pool_t * p, |
---|
| 737 | server_rec * s, |
---|
| 738 | mgs_srvconf_rec * sc) |
---|
| 739 | { |
---|
[aa68232] | 740 | /* 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, |
---|
| 743 | lockfile, p); |
---|
| 744 | 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 | |
---|
[e183628] | 748 | if (sc->cache_type == mgs_cache_dbm |
---|
| 749 | || sc->cache_type == mgs_cache_gdbm) { |
---|
| 750 | return 0; |
---|
| 751 | } |
---|
[6e0bfd6] | 752 | #if HAVE_APR_MEMCACHE |
---|
[e183628] | 753 | else if (sc->cache_type == mgs_cache_memcache) { |
---|
| 754 | return mc_cache_child_init(p, s, sc); |
---|
| 755 | } |
---|
[6e0bfd6] | 756 | #endif |
---|
[e183628] | 757 | return 0; |
---|
[6e0bfd6] | 758 | } |
---|
| 759 | |
---|
[e02dd8c] | 760 | #include <assert.h> |
---|
[fcb122d] | 761 | |
---|
[e183628] | 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, |
---|
[15245bf] | 766 | dbm_cache_fetch_session); |
---|
[e183628] | 767 | gnutls_db_set_remove_function(ctxt->session, |
---|
| 768 | dbm_cache_delete); |
---|
| 769 | gnutls_db_set_store_function(ctxt->session, |
---|
[ae08186] | 770 | dbm_cache_store_session); |
---|
[e183628] | 771 | gnutls_db_set_ptr(ctxt->session, ctxt); |
---|
| 772 | } |
---|
[6e0bfd6] | 773 | #if HAVE_APR_MEMCACHE |
---|
[e183628] | 774 | else if (ctxt->sc->cache_type == mgs_cache_memcache) { |
---|
| 775 | gnutls_db_set_retrieve_function(ctxt->session, |
---|
[3e22b82] | 776 | mc_cache_fetch_session); |
---|
[e183628] | 777 | gnutls_db_set_remove_function(ctxt->session, |
---|
| 778 | mc_cache_delete); |
---|
| 779 | gnutls_db_set_store_function(ctxt->session, |
---|
[3e22b82] | 780 | mc_cache_store_session); |
---|
[e183628] | 781 | gnutls_db_set_ptr(ctxt->session, ctxt); |
---|
| 782 | } |
---|
[6e0bfd6] | 783 | #endif |
---|
[42307a9] | 784 | |
---|
[e183628] | 785 | return 0; |
---|
[32f2e60] | 786 | } |
---|