source: mod_gnutls/src/gnutls_cache.c @ 771ca63

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

The GnuTLSCache variable now can be given the specific
option "sdbm" instead of "dbm". "dbm" will use the default
dbm type of libapr while sdbm will force sdbm to be used.

  • 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_sdbm)
300                return "sdbm";
301        else
302                return "default";
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_sdbm) {
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_sdbm) {
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_sdbm) {
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.