source: mod_gnutls/src/gnutls_cache.c @ e183628

asynciodebian/masterdebian/stretch-backportsjessie-backportsmainmsvaproxy-ticketupstream
Last change on this file since e183628 was e183628, checked in by Dash Shendy <neuromancer@…>, 12 years ago

Updated Copyright Headers & Formatting

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *
6 *  Licensed under the Apache License, Version 2.0 (the "License");
7 *  you may not use this file except in compliance with the License.
8 *  You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *  Unless required by applicable law or agreed to in writing, software
13 *  distributed under the License is distributed on an "AS IS" BASIS,
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 *
18 */
19
20#include "mod_gnutls.h"
21
22#if HAVE_APR_MEMCACHE
23#include "apr_memcache.h"
24#endif
25
26#include "apr_dbm.h"
27
28#include "ap_mpm.h"
29
30#include <unistd.h>
31#include <sys/types.h>
32
33#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
34#include "unixd.h"
35#endif
36
37/* it seems the default has some strange errors. Use SDBM
38 */
39#define MC_TAG "mod_gnutls:"
40#define MC_TAG_LEN sizeof(MC_TAG)
41#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN)
42
43#if MODULE_MAGIC_NUMBER_MAJOR < 20081201
44#define ap_unixd_config unixd_config
45#endif
46
47char *mgs_session_id2sz(unsigned char *id, int idlen,
48        char *str, int strsize) {
49    char *cp;
50    int n;
51
52    cp = str;
53    for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) {
54        apr_snprintf(cp, strsize - (cp - str), "%02X", id[n]);
55        cp += 2;
56    }
57    *cp = '\0';
58    return str;
59}
60
61/* Name the Session ID as:
62 * server:port.SessionID
63 * to disallow resuming sessions on different servers
64 */
65static int mgs_session_id2dbm(conn_rec * c, unsigned char *id, int idlen,
66        apr_datum_t * dbmkey) {
67    char buf[STR_SESSION_LEN];
68    char *sz;
69
70    sz = mgs_session_id2sz(id, idlen, buf, sizeof (buf));
71    if (sz == NULL)
72        return -1;
73
74    dbmkey->dptr =
75            apr_psprintf(c->pool, "%s:%d.%s",
76            c->base_server->server_hostname,
77            c->base_server->port, sz);
78    dbmkey->dsize = strlen(dbmkey->dptr);
79
80    return 0;
81}
82
83#define CTIME "%b %d %k:%M:%S %Y %Z"
84
85char *mgs_time2sz(time_t in_time, char *str, int strsize) {
86    apr_time_exp_t vtm;
87    apr_size_t ret_size;
88    apr_time_t t;
89
90
91    apr_time_ansi_put(&t, in_time);
92    apr_time_exp_gmt(&vtm, t);
93    apr_strftime(str, &ret_size, strsize - 1, CTIME, &vtm);
94
95    return str;
96}
97
98#if HAVE_APR_MEMCACHE
99
100/* Name the Session ID as:
101 * server:port.SessionID
102 * to disallow resuming sessions on different servers
103 */
104static char *mgs_session_id2mc(conn_rec * c, unsigned char *id, int idlen) {
105    char buf[STR_SESSION_LEN];
106    char *sz;
107
108    sz = mgs_session_id2sz(id, idlen, buf, sizeof (buf));
109    if (sz == NULL)
110        return NULL;
111
112    return apr_psprintf(c->pool, MC_TAG "%s:%d.%s",
113            c->base_server->server_hostname,
114            c->base_server->port, sz);
115}
116
117/**
118 * GnuTLS Session Cache using libmemcached
119 *
120 */
121
122/* The underlying apr_memcache system is thread safe... woohoo */
123static apr_memcache_t *mc;
124
125static int mc_cache_child_init(apr_pool_t * p, server_rec * s,
126        mgs_srvconf_rec * sc) {
127    apr_status_t rv = APR_SUCCESS;
128    int thread_limit = 0;
129    int nservers = 0;
130    char *cache_config;
131    char *split;
132    char *tok;
133
134    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
135
136    /* Find all the servers in the first run to get a total count */
137    cache_config = apr_pstrdup(p, sc->cache_config);
138    split = apr_strtok(cache_config, " ", &tok);
139    while (split) {
140        nservers++;
141        split = apr_strtok(NULL, " ", &tok);
142    }
143
144    rv = apr_memcache_create(p, nservers, 0, &mc);
145    if (rv != APR_SUCCESS) {
146        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
147                "[gnutls_cache] Failed to create Memcache Object of '%d' size.",
148                nservers);
149        return rv;
150    }
151
152    /* Now add each server to the memcache */
153    cache_config = apr_pstrdup(p, sc->cache_config);
154    split = apr_strtok(cache_config, " ", &tok);
155    while (split) {
156        apr_memcache_server_t *st;
157        char *host_str;
158        char *scope_id;
159        apr_port_t port;
160
161        rv = apr_parse_addr_port(&host_str, &scope_id, &port,
162                split, p);
163        if (rv != APR_SUCCESS) {
164            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
165                    "[gnutls_cache] Failed to Parse Server: '%s'",
166                    split);
167            return rv;
168        }
169
170        if (host_str == NULL) {
171            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
172                    "[gnutls_cache] Failed to Parse Server, "
173                    "no hostname specified: '%s'", split);
174            return rv;
175        }
176
177        if (port == 0) {
178            port = 11211; /* default port */
179        }
180
181        /* Should Max Conns be (thread_limit / nservers) ? */
182        rv = apr_memcache_server_create(p,
183                host_str, port,
184                0,
185                1, thread_limit, 600, &st);
186        if (rv != APR_SUCCESS) {
187            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
188                    "[gnutls_cache] Failed to Create Server: %s:%d",
189                    host_str, port);
190            return rv;
191        }
192
193        rv = apr_memcache_add_server(mc, st);
194        if (rv != APR_SUCCESS) {
195            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
196                    "[gnutls_cache] Failed to Add Server: %s:%d",
197                    host_str, port);
198            return rv;
199        }
200
201        split = apr_strtok(NULL, " ", &tok);
202    }
203    return rv;
204}
205
206static int mc_cache_store(void *baton, gnutls_datum_t key,
207        gnutls_datum_t data) {
208    apr_status_t rv = APR_SUCCESS;
209    mgs_handle_t *ctxt = baton;
210    char *strkey = NULL;
211    apr_uint32_t timeout;
212
213    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
214    if (!strkey)
215        return -1;
216
217    timeout = apr_time_sec(ctxt->sc->cache_timeout);
218
219    rv = apr_memcache_set(mc, strkey, (char *) data.data, data.size, timeout,
220            0);
221
222    if (rv != APR_SUCCESS) {
223        ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
224                ctxt->c->base_server,
225                "[gnutls_cache] error setting key '%s' "
226                "with %d bytes of data", strkey, data.size);
227        return -1;
228    }
229
230    return 0;
231}
232
233static gnutls_datum_t mc_cache_fetch(void *baton, gnutls_datum_t key) {
234    apr_status_t rv = APR_SUCCESS;
235    mgs_handle_t *ctxt = baton;
236    char *strkey = NULL;
237    char *value;
238    apr_size_t value_len;
239    gnutls_datum_t data = {NULL, 0};
240
241    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
242    if (!strkey) {
243        return data;
244    }
245
246    rv = apr_memcache_getp(mc, ctxt->c->pool, strkey,
247            &value, &value_len, NULL);
248
249    if (rv != APR_SUCCESS) {
250#if MOD_GNUTLS_DEBUG
251        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
252                ctxt->c->base_server,
253                "[gnutls_cache] error fetching key '%s' ",
254                strkey);
255#endif
256        data.size = 0;
257        data.data = NULL;
258        return data;
259    }
260
261    /* TODO: Eliminate this memcpy. gnutls-- */
262    data.data = gnutls_malloc(value_len);
263    if (data.data == NULL)
264        return data;
265
266    data.size = value_len;
267    memcpy(data.data, value, value_len);
268
269    return data;
270}
271
272static int mc_cache_delete(void *baton, gnutls_datum_t key) {
273    apr_status_t rv = APR_SUCCESS;
274    mgs_handle_t *ctxt = baton;
275    char *strkey = NULL;
276
277    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
278    if (!strkey)
279        return -1;
280
281    rv = apr_memcache_delete(mc, strkey, 0);
282
283    if (rv != APR_SUCCESS) {
284        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
285                ctxt->c->base_server,
286                "[gnutls_cache] error deleting key '%s' ",
287                strkey);
288        return -1;
289    }
290
291    return 0;
292}
293
294#endif                          /* have_apr_memcache */
295
296const char *db_type(mgs_srvconf_rec * sc) {
297    if (sc->cache_type == mgs_cache_gdbm)
298        return "gdbm";
299    else
300        return "db";
301}
302
303#define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
304
305static void dbm_cache_expire(mgs_handle_t * ctxt) {
306    apr_status_t rv;
307    apr_dbm_t *dbm;
308    apr_datum_t dbmkey;
309    apr_datum_t dbmval;
310    apr_time_t now;
311    apr_time_t dtime;
312    apr_pool_t *spool;
313    int total, deleted;
314
315    now = apr_time_now();
316
317    if (now - ctxt->sc->last_cache_check <
318            (ctxt->sc->cache_timeout) / 2)
319        return;
320
321    ctxt->sc->last_cache_check = now;
322
323    apr_pool_create(&spool, ctxt->c->pool);
324
325    total = 0;
326    deleted = 0;
327
328    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
329            ctxt->sc->cache_config, APR_DBM_RWCREATE,
330            SSL_DBM_FILE_MODE, spool);
331    if (rv != APR_SUCCESS) {
332        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
333                ctxt->c->base_server,
334                "[gnutls_cache] error opening cache searcher '%s'",
335                ctxt->sc->cache_config);
336        apr_pool_destroy(spool);
337        return;
338    }
339
340    apr_dbm_firstkey(dbm, &dbmkey);
341    while (dbmkey.dptr != NULL) {
342        apr_dbm_fetch(dbm, dbmkey, &dbmval);
343        if (dbmval.dptr != NULL
344                && dbmval.dsize >= sizeof (apr_time_t)) {
345            memcpy(&dtime, dbmval.dptr, sizeof (apr_time_t));
346
347            if (now >= dtime) {
348                apr_dbm_delete(dbm, dbmkey);
349                deleted++;
350            }
351            apr_dbm_freedatum(dbm, dbmval);
352        } else {
353            apr_dbm_delete(dbm, dbmkey);
354            deleted++;
355        }
356        total++;
357        apr_dbm_nextkey(dbm, &dbmkey);
358    }
359    apr_dbm_close(dbm);
360
361    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
362            ctxt->c->base_server,
363            "[gnutls_cache] Cleaned up cache '%s'. Deleted %d and left %d",
364            ctxt->sc->cache_config, deleted, total - deleted);
365
366    apr_pool_destroy(spool);
367
368    return;
369}
370
371static gnutls_datum_t dbm_cache_fetch(void *baton, gnutls_datum_t key) {
372    gnutls_datum_t data = {NULL, 0};
373    apr_dbm_t *dbm;
374    apr_datum_t dbmkey;
375    apr_datum_t dbmval;
376    mgs_handle_t *ctxt = baton;
377    apr_status_t rv;
378
379    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
380        return data;
381
382    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
383            ctxt->sc->cache_config, APR_DBM_READONLY,
384            SSL_DBM_FILE_MODE, ctxt->c->pool);
385    if (rv != APR_SUCCESS) {
386        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
387                ctxt->c->base_server,
388                "[gnutls_cache] error opening cache '%s'",
389                ctxt->sc->cache_config);
390        return data;
391    }
392
393    rv = apr_dbm_fetch(dbm, dbmkey, &dbmval);
394
395    if (rv != APR_SUCCESS) {
396        apr_dbm_close(dbm);
397        return data;
398    }
399
400    if (dbmval.dptr == NULL || dbmval.dsize <= sizeof (apr_time_t)) {
401        apr_dbm_freedatum(dbm, dbmval);
402        apr_dbm_close(dbm);
403        return data;
404    }
405
406    data.size = dbmval.dsize - sizeof (apr_time_t);
407
408    data.data = gnutls_malloc(data.size);
409    if (data.data == NULL) {
410        apr_dbm_freedatum(dbm, dbmval);
411        apr_dbm_close(dbm);
412        return data;
413    }
414
415    memcpy(data.data, dbmval.dptr + sizeof (apr_time_t), data.size);
416
417    apr_dbm_freedatum(dbm, dbmval);
418    apr_dbm_close(dbm);
419
420    return data;
421}
422
423static int dbm_cache_store(void *baton, gnutls_datum_t key,
424        gnutls_datum_t data) {
425    apr_dbm_t *dbm;
426    apr_datum_t dbmkey;
427    apr_datum_t dbmval;
428    mgs_handle_t *ctxt = baton;
429    apr_status_t rv;
430    apr_time_t expiry;
431    apr_pool_t *spool;
432
433    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
434        return -1;
435
436    /* we expire dbm only on every store
437     */
438    dbm_cache_expire(ctxt);
439
440    apr_pool_create(&spool, ctxt->c->pool);
441
442    /* create DBM value */
443    dbmval.dsize = data.size + sizeof (apr_time_t);
444    dbmval.dptr = (char *) apr_palloc(spool, dbmval.dsize);
445
446    expiry = apr_time_now() + ctxt->sc->cache_timeout;
447
448    memcpy((char *) dbmval.dptr, &expiry, sizeof (apr_time_t));
449    memcpy((char *) dbmval.dptr + sizeof (apr_time_t),
450            data.data, data.size);
451
452    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
453            ctxt->sc->cache_config, APR_DBM_RWCREATE,
454            SSL_DBM_FILE_MODE, ctxt->c->pool);
455    if (rv != APR_SUCCESS) {
456        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
457                ctxt->c->base_server,
458                "[gnutls_cache] error opening cache '%s'",
459                ctxt->sc->cache_config);
460        apr_pool_destroy(spool);
461        return -1;
462    }
463
464    rv = apr_dbm_store(dbm, dbmkey, dbmval);
465
466    if (rv != APR_SUCCESS) {
467        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
468                ctxt->c->base_server,
469                "[gnutls_cache] error storing in cache '%s'",
470                ctxt->sc->cache_config);
471        apr_dbm_close(dbm);
472        apr_pool_destroy(spool);
473        return -1;
474    }
475
476    apr_dbm_close(dbm);
477
478    apr_pool_destroy(spool);
479
480    return 0;
481}
482
483static int dbm_cache_delete(void *baton, gnutls_datum_t key) {
484    apr_dbm_t *dbm;
485    apr_datum_t dbmkey;
486    mgs_handle_t *ctxt = baton;
487    apr_status_t rv;
488
489    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
490        return -1;
491
492    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
493            ctxt->sc->cache_config, APR_DBM_RWCREATE,
494            SSL_DBM_FILE_MODE, ctxt->c->pool);
495    if (rv != APR_SUCCESS) {
496        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
497                ctxt->c->base_server,
498                "[gnutls_cache] error opening cache '%s'",
499                ctxt->sc->cache_config);
500        return -1;
501    }
502
503    rv = apr_dbm_delete(dbm, dbmkey);
504
505    if (rv != APR_SUCCESS) {
506        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
507                ctxt->c->base_server,
508                "[gnutls_cache] error deleting from cache '%s'",
509                ctxt->sc->cache_config);
510        apr_dbm_close(dbm);
511        return -1;
512    }
513
514    apr_dbm_close(dbm);
515
516    return 0;
517}
518
519static int dbm_cache_post_config(apr_pool_t * p, server_rec * s,
520        mgs_srvconf_rec * sc) {
521    apr_status_t rv;
522    apr_dbm_t *dbm;
523    const char *path1;
524    const char *path2;
525
526    rv = apr_dbm_open_ex(&dbm, db_type(sc), sc->cache_config,
527            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, p);
528
529    if (rv != APR_SUCCESS) {
530        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
531                "GnuTLS: Cannot create DBM Cache at `%s'",
532                sc->cache_config);
533        return rv;
534    }
535
536    apr_dbm_close(dbm);
537
538    apr_dbm_get_usednames_ex(p, db_type(sc), sc->cache_config, &path1,
539            &path2);
540
541    /* The Following Code takes logic directly from mod_ssl's DBM Cache */
542#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
543    /* Running as Root */
544    if (path1 && geteuid() == 0) {
545        chown(path1, ap_unixd_config.user_id, -1);
546        if (path2 != NULL) {
547            chown(path2, ap_unixd_config.user_id, -1);
548        }
549    }
550#endif
551
552    return rv;
553}
554
555int mgs_cache_post_config(apr_pool_t * p, server_rec * s,
556        mgs_srvconf_rec * sc) {
557    if (sc->cache_type == mgs_cache_dbm
558            || sc->cache_type == mgs_cache_gdbm) {
559        return dbm_cache_post_config(p, s, sc);
560    }
561    return 0;
562}
563
564int mgs_cache_child_init(apr_pool_t * p, server_rec * s,
565        mgs_srvconf_rec * sc) {
566    if (sc->cache_type == mgs_cache_dbm
567            || sc->cache_type == mgs_cache_gdbm) {
568        return 0;
569    }
570#if HAVE_APR_MEMCACHE
571    else if (sc->cache_type == mgs_cache_memcache) {
572        return mc_cache_child_init(p, s, sc);
573    }
574#endif
575    return 0;
576}
577
578#include <assert.h>
579
580int mgs_cache_session_init(mgs_handle_t * ctxt) {
581    if (ctxt->sc->cache_type == mgs_cache_dbm
582            || ctxt->sc->cache_type == mgs_cache_gdbm) {
583        gnutls_db_set_retrieve_function(ctxt->session,
584                dbm_cache_fetch);
585        gnutls_db_set_remove_function(ctxt->session,
586                dbm_cache_delete);
587        gnutls_db_set_store_function(ctxt->session,
588                dbm_cache_store);
589        gnutls_db_set_ptr(ctxt->session, ctxt);
590    }
591#if HAVE_APR_MEMCACHE
592    else if (ctxt->sc->cache_type == mgs_cache_memcache) {
593        gnutls_db_set_retrieve_function(ctxt->session,
594                mc_cache_fetch);
595        gnutls_db_set_remove_function(ctxt->session,
596                mc_cache_delete);
597        gnutls_db_set_store_function(ctxt->session,
598                mc_cache_store);
599        gnutls_db_set_ptr(ctxt->session, ctxt);
600    }
601#endif
602
603    return 0;
604}
Note: See TracBrowser for help on using the repository browser.