source: mod_gnutls/src/gnutls_cache.c @ 0499540

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 0499540 was 2b29da0, checked in by Nikos Mavrogiannopoulos <nmav@…>, 9 years ago

Check the cache DB every 15 minutes instead of checking on every write.

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