[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 |
---|
| 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 | |
---|
[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 | */ |
---|
| 299 | static 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] | 328 | static 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 | |
---|
| 341 | static 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] | 353 | static 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] | 377 | static 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] | 386 | static 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 | |
---|
| 459 | static 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] | 532 | static 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] | 544 | static 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] | 610 | static 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] | 624 | static 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] | 667 | static 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] | 709 | int 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] | 747 | int 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] | 773 | int 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 | } |
---|