source: mod_gnutls/src/gnutls_cache.c @ d8c7cf4

asynciodebian/masterdebian/stretch-backportsjessie-backportsmainmsvaproxy-ticketupstream
Last change on this file since d8c7cf4 was d8c7cf4, checked in by Nikos Mavrogiannopoulos <nmav@…>, 13 years ago

Only allow two options for DB. Berkeley DB and gdbm. The other options
such as SDBM had serious limitations. Thanks to Hardy Griech for pointing out.

  • Property mode set to 100644
File size: 16.4 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 MC_TAG "mod_gnutls:"
39#define MC_TAG_LEN sizeof(MC_TAG)
40#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN)
41
42#if MODULE_MAGIC_NUMBER_MAJOR < 20081201
43#define ap_unixd_config unixd_config
44#endif
45
46char *mgs_session_id2sz(unsigned char *id, int idlen,
47                               char *str, int strsize)
48{
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
62/* Name the Session ID as:
63 * server:port.SessionID
64 * to disallow resuming sessions on different servers
65 */
66static int mgs_session_id2dbm(conn_rec* c, unsigned char *id, int idlen,
67                               apr_datum_t* dbmkey)
68{
69char buf[STR_SESSION_LEN];
70char *sz;
71   
72    sz = mgs_session_id2sz(id, idlen, buf, sizeof(buf));
73    if (sz == NULL)
74      return -1;
75     
76    dbmkey->dptr = apr_psprintf(c->pool, "%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz);
77    dbmkey->dsize = strlen( dbmkey->dptr);
78   
79    return 0;
80}
81
82#define CTIME "%b %d %k:%M:%S %Y %Z"
83char *mgs_time2sz(time_t in_time, char *str, int strsize)
84{
85    apr_time_exp_t vtm;
86    apr_size_t ret_size;
87    apr_time_t t;
88   
89 
90    apr_time_ansi_put (&t, in_time);
91    apr_time_exp_gmt (&vtm, t);
92    apr_strftime(str, &ret_size, strsize-1, CTIME, &vtm);
93
94    return str;
95}
96
97#if HAVE_APR_MEMCACHE
98/* Name the Session ID as:
99 * server:port.SessionID
100 * to disallow resuming sessions on different servers
101 */
102static char* mgs_session_id2mc(conn_rec* c, unsigned char *id, int idlen)
103{
104char buf[STR_SESSION_LEN];
105char *sz;
106   
107    sz = mgs_session_id2sz(id, idlen, buf, sizeof(buf));
108    if (sz == NULL)
109      return NULL;
110     
111    return apr_psprintf(c->pool, MC_TAG"%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz);
112}
113
114/**
115 * GnuTLS Session Cache using libmemcached
116 *
117 */
118
119/* The underlying apr_memcache system is thread safe... woohoo */
120static apr_memcache_t* mc;
121
122static int mc_cache_child_init(apr_pool_t *p, server_rec *s, 
123                                mgs_srvconf_rec *sc)
124{
125    apr_status_t rv = APR_SUCCESS;
126    int thread_limit = 0;
127    int nservers = 0;
128    char* cache_config;
129    char* split;
130    char* tok;
131
132    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
133
134    /* Find all the servers in the first run to get a total count */
135    cache_config = apr_pstrdup(p, sc->cache_config);
136    split = apr_strtok(cache_config, " ", &tok);
137    while (split) {
138        nservers++;
139        split = apr_strtok(NULL," ", &tok);
140    }
141
142    rv = apr_memcache_create(p, nservers, 0, &mc);
143    if (rv != APR_SUCCESS) {
144        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
145                        "[gnutls_cache] Failed to create Memcache Object of '%d' size.", 
146                         nservers);
147        return rv;
148    }
149
150    /* Now add each server to the memcache */
151    cache_config = apr_pstrdup(p, sc->cache_config);
152    split = apr_strtok(cache_config, " ", &tok);
153    while (split) {
154        apr_memcache_server_t* st;
155        char* host_str;
156        char* scope_id;
157        apr_port_t port;
158
159        rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
160        if (rv != APR_SUCCESS) {
161            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
162                         "[gnutls_cache] Failed to Parse Server: '%s'", split);
163            return rv;
164        }
165
166        if (host_str == NULL) {
167            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
168                         "[gnutls_cache] Failed to Parse Server, "
169                         "no hostname specified: '%s'", split);
170            return rv;
171        }
172
173        if (port == 0) {
174            port = 11211; /* default port */
175        }
176
177        /* Should Max Conns be (thread_limit / nservers) ? */
178        rv = apr_memcache_server_create(p,
179                                        host_str, port,
180                                        0,
181                                        1,
182                                        thread_limit, 
183                                        600,
184                                        &st);
185        if (rv != APR_SUCCESS) {
186            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
187                         "[gnutls_cache] Failed to Create Server: %s:%d", 
188                         host_str, port);
189            return rv;
190        }
191
192        rv = apr_memcache_add_server(mc, st);
193        if (rv != APR_SUCCESS) {
194            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
195                         "[gnutls_cache] Failed to Add Server: %s:%d", 
196                         host_str, port);
197            return rv;
198        }
199
200        split = apr_strtok(NULL," ", &tok);
201    }
202    return rv;
203}
204
205static int mc_cache_store(void* baton, gnutls_datum_t key, 
206                          gnutls_datum_t data)
207{
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, data.data, data.size, timeout, 0);
220
221    if (rv != APR_SUCCESS) {
222        ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
223                     ctxt->c->base_server,
224                     "[gnutls_cache] error setting key '%s' "
225                     "with %d bytes of data", strkey, data.size);
226        return -1;
227    }
228
229    return 0;
230}
231
232static gnutls_datum_t mc_cache_fetch(void* baton, gnutls_datum_t key)
233{
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{
274    apr_status_t rv = APR_SUCCESS;
275    mgs_handle_t *ctxt = baton;
276    char* strkey = NULL;
277
278    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
279    if(!strkey)
280        return -1;
281
282    rv = apr_memcache_delete(mc, strkey, 0);
283
284    if (rv != APR_SUCCESS) {
285        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
286                     ctxt->c->base_server,
287                     "[gnutls_cache] error deleting key '%s' ",
288                      strkey);
289        return -1;
290    }
291
292    return 0;
293}
294
295#endif /* have_apr_memcache */
296
297const char* db_type(mgs_srvconf_rec * sc)
298{
299        if (sc->cache_type == mgs_cache_gdbm)
300                return "gdbm";
301        else
302                return "db";
303}
304
305#define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
306
307static void dbm_cache_expire(mgs_handle_t *ctxt)
308{
309    apr_status_t rv;
310    apr_dbm_t *dbm;
311    apr_datum_t dbmkey;
312    apr_datum_t dbmval;
313    apr_time_t now;
314    apr_time_t dtime;
315    apr_pool_t* spool;
316    int total, deleted;
317
318    now = apr_time_now();
319   
320    if (now - ctxt->sc->last_cache_check < (ctxt->sc->cache_timeout)/2)
321        return;
322
323    ctxt->sc->last_cache_check = now;
324
325    apr_pool_create(&spool, ctxt->c->pool);
326
327    total = 0;
328    deleted = 0;
329
330    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc), ctxt->sc->cache_config, APR_DBM_RWCREATE,
331                      SSL_DBM_FILE_MODE, spool);
332    if (rv != APR_SUCCESS) {
333        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
334                     ctxt->c->base_server,
335                     "[gnutls_cache] error opening cache searcher '%s'",
336                     ctxt->sc->cache_config);
337        apr_pool_destroy(spool);
338        return;
339    }
340
341    apr_dbm_firstkey(dbm, &dbmkey);
342    while (dbmkey.dptr != NULL) {
343        apr_dbm_fetch(dbm, dbmkey, &dbmval);
344        if (dbmval.dptr != NULL && 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{
373    gnutls_datum_t data = { NULL, 0 };
374    apr_dbm_t *dbm;
375    apr_datum_t dbmkey;
376    apr_datum_t dbmval;
377    mgs_handle_t *ctxt = baton;
378    apr_status_t rv;
379
380    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
381        return data;
382
383    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc), ctxt->sc->cache_config,
384                      APR_DBM_READONLY, 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{
426    apr_dbm_t *dbm;
427    apr_datum_t dbmkey;
428    apr_datum_t dbmval;
429    mgs_handle_t *ctxt = baton;
430    apr_status_t rv;
431    apr_time_t expiry;
432    apr_pool_t* spool;
433
434    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
435        return -1;
436
437    /* we expire dbm only on every store
438     */
439    dbm_cache_expire(ctxt);
440
441    apr_pool_create(&spool, ctxt->c->pool);
442
443    /* create DBM value */
444    dbmval.dsize = data.size + sizeof(apr_time_t);
445    dbmval.dptr  = (char *)apr_palloc(spool, dbmval.dsize);
446
447    expiry = apr_time_now() + ctxt->sc->cache_timeout;
448
449    memcpy((char *)dbmval.dptr, &expiry, sizeof(apr_time_t));
450    memcpy((char *)dbmval.dptr+sizeof(apr_time_t),
451           data.data, data.size);
452
453    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc), ctxt->sc->cache_config,
454                      APR_DBM_RWCREATE, 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{
485    apr_dbm_t *dbm;
486    apr_datum_t dbmkey;
487    mgs_handle_t *ctxt = baton;
488    apr_status_t rv;
489
490    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
491        return -1;
492
493    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc), ctxt->sc->cache_config,
494                      APR_DBM_RWCREATE, 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{
522    apr_status_t rv;
523    apr_dbm_t *dbm;
524    const char* path1;
525    const char* path2;
526
527    rv = apr_dbm_open_ex(&dbm, db_type(sc), sc->cache_config, APR_DBM_RWCREATE, 
528                      SSL_DBM_FILE_MODE, p);
529
530    if (rv != APR_SUCCESS) {
531        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
532                     "GnuTLS: Cannot create DBM Cache at `%s'", 
533                     sc->cache_config);
534        return rv; 
535    }
536
537    apr_dbm_close(dbm);
538
539    apr_dbm_get_usednames_ex(p, db_type(sc), sc->cache_config, &path1, &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{
558    if (sc->cache_type == mgs_cache_dbm || 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{
567    if (sc->cache_type == mgs_cache_dbm || 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{
582    if (ctxt->sc->cache_type == mgs_cache_dbm || ctxt->sc->cache_type == mgs_cache_gdbm) {
583        gnutls_db_set_retrieve_function(ctxt->session, dbm_cache_fetch);
584        gnutls_db_set_remove_function(ctxt->session, dbm_cache_delete);
585        gnutls_db_set_store_function(ctxt->session, dbm_cache_store);
586        gnutls_db_set_ptr(ctxt->session, ctxt);
587    }
588#if HAVE_APR_MEMCACHE
589    else if (ctxt->sc->cache_type == mgs_cache_memcache) { 
590        gnutls_db_set_retrieve_function(ctxt->session, mc_cache_fetch);
591        gnutls_db_set_remove_function(ctxt->session, mc_cache_delete);
592        gnutls_db_set_store_function(ctxt->session, mc_cache_store);
593        gnutls_db_set_ptr(ctxt->session, ctxt);
594    }
595#endif
596
597    return 0;
598}
Note: See TracBrowser for help on using the repository browser.