source: mod_gnutls/src/gnutls_cache.c @ a2cb12f

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