source: mod_gnutls/src/gnutls_cache.c @ 03a9a6b

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

Force SDBM.

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