source: mod_gnutls/src/gnutls_cache.c @ 46b85d8

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 46b85d8 was 46b85d8, checked in by Paul Querna <chip@…>, 14 years ago

move config functions to their own file.

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