source: mod_gnutls/src/gnutls_cache.c @ 42307a9

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 42307a9 was 42307a9, checked in by Paul Querna <chip@…>, 15 years ago
  • remove anno creds
  • initial attempt at Server Name Extension
  • change to adding 'mod_gnutls' to the server sig instead of GnuTLS/
  • fix for EOF/EOC/EOS buckets
  • 'general' code cleanups
  • 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
82int 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        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
213                     ctxt->c->base_server,
214                     "[gnutls_cache] error fetching key '%s' ",
215                     strkey);
216
217        data.size = 0;
218        data.data = NULL;
219        return data;
220    }
221
222    /* TODO: Eliminate this memcpy. gnutls-- */
223    data.data = gnutls_malloc(value_len);
224    if (data.data == NULL)
225        return data;
226
227    data.size = value_len;
228    memcpy(data.data, value, value_len);
229
230    return data;
231}
232
233static int mc_cache_delete(void* baton, gnutls_datum_t key)
234{
235    apr_status_t rv = APR_SUCCESS;
236    mod_gnutls_handle_t *ctxt = baton;
237    char buf[STR_SESSION_LEN];
238    char* strkey = NULL;
239
240    strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf));
241    if(!strkey)
242        return -1;
243
244    rv = apr_memcache_delete(mc, strkey, 0);
245
246    if (rv != APR_SUCCESS) {
247        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
248                     ctxt->c->base_server,
249                     "[gnutls_cache] error deleting key '%s' ",
250                      strkey);
251        return -1;
252    }
253
254    return 0;
255}
256
257#endif /* have_apr_memcache */
258
259#define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
260
261static int dbm_cache_expire(mod_gnutls_handle_t *ctxt)
262{
263    apr_status_t rv;
264    apr_dbm_t *dbm;
265    apr_datum_t *keylist;
266    apr_datum_t dbmkey;
267    apr_datum_t dbmval;
268    apr_time_t ex;
269    apr_time_t dtime;
270    apr_pool_t* spool;
271    int i = 0;
272    int keyidx = 0;
273    int should_delete = 0;
274
275    apr_pool_create(&spool, ctxt->c->pool);
276    ex = apr_time_now();
277   
278    rv = apr_dbm_open(&dbm, ctxt->sc->cache_config, APR_DBM_READONLY,
279                      SSL_DBM_FILE_MODE, spool);
280    if (rv != APR_SUCCESS) {
281        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
282                     ctxt->c->base_server,
283                     "[gnutls_cache] error opening cache searcher '%s'",
284                     ctxt->sc->cache_config);
285        return -1;
286    }
287
288#define KEYMAX 128
289
290    keylist = apr_palloc(spool, sizeof(dbmkey)*KEYMAX);
291
292    apr_dbm_firstkey(dbm, &dbmkey);
293    while (dbmkey.dptr != NULL) {
294        apr_dbm_fetch(dbm, dbmkey, &dbmval);
295        if (dbmval.dptr != NULL) {
296            if (dbmval.dsize >= sizeof(apr_time_t)) {
297                memcpy(&dtime, dbmval.dptr, sizeof(apr_time_t));
298                if (dtime < ex) {
299                    should_delete = 1;
300                }
301            }
302            else {
303                should_delete = 1;
304            }
305           
306            if (should_delete == 1) {
307                should_delete = 0;
308                keylist[keyidx].dptr = apr_palloc(spool, dbmkey.dsize) ;
309                memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize);
310                keylist[keyidx].dsize = dbmkey.dsize;
311                keyidx++;
312                if (keyidx == KEYMAX) {
313                    break;
314                }
315            }
316           
317        }
318        apr_dbm_nextkey(dbm, &dbmkey);
319    }
320    apr_dbm_close(dbm);
321
322    rv = apr_dbm_open(&dbm, ctxt->sc->cache_config,
323                  APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, spool);
324    if (rv != APR_SUCCESS) {
325        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
326                 ctxt->c->base_server,
327                 "[gnutls_cache] error opening cache writer '%s'",
328                 ctxt->sc->cache_config);
329        return -1;
330    }
331
332    for (i = 0; i < keyidx; i++) {
333        apr_dbm_delete(dbm, keylist[i]);
334    }
335
336    apr_dbm_close(dbm);
337    apr_pool_destroy(spool);
338   
339    return 0;
340}
341
342static gnutls_datum_t dbm_cache_fetch(void* baton, gnutls_datum_t key)
343{
344    gnutls_datum_t data = { NULL, 0 };
345    apr_dbm_t *dbm;
346    apr_datum_t dbmkey;
347    apr_datum_t dbmval;
348    mod_gnutls_handle_t *ctxt = baton;
349    apr_status_t rv;
350
351    dbmkey.dptr  = key.data;
352    dbmkey.dsize = key.size;
353
354    dbm_cache_expire(ctxt);
355
356    rv = apr_dbm_open(&dbm, ctxt->sc->cache_config,
357                      APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool);
358    if (rv != APR_SUCCESS) {
359        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
360                     ctxt->c->base_server,
361                     "[gnutls_cache] error opening cache '%s'",
362                     ctxt->sc->cache_config);
363        return data;
364    }
365
366    rv = apr_dbm_fetch(dbm, dbmkey, &dbmval);
367   
368    if (rv != APR_SUCCESS) {
369        apr_dbm_close(dbm);
370        return data;
371    }
372
373    if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(apr_time_t)) {
374        apr_dbm_close(dbm);
375        return data;
376    }
377    apr_dbm_close(dbm);
378
379    data.size = dbmval.dsize - sizeof(apr_time_t);
380
381    data.data = gnutls_malloc(data.size);
382    if (data.data == NULL) {
383        return data;
384    }
385   
386    memcpy(data.data, dbmval.dptr+sizeof(apr_time_t), data.size);
387
388    return data;
389}
390
391static int dbm_cache_store(void* baton, gnutls_datum_t key, 
392                          gnutls_datum_t data)
393{
394    apr_dbm_t *dbm;
395    apr_datum_t dbmkey;
396    apr_datum_t dbmval;
397    mod_gnutls_handle_t *ctxt = baton;
398    apr_status_t rv;
399    apr_time_t expiry;
400   
401    dbmkey.dptr  = (char *)key.data;
402    dbmkey.dsize = key.size;
403
404    /* create DBM value */
405    dbmval.dsize = data.size + sizeof(apr_time_t);
406    dbmval.dptr  = (char *)malloc(dbmval.dsize);
407
408    expiry = apr_time_now() + ctxt->sc->cache_timeout;
409
410    memcpy((char *)dbmval.dptr, &expiry, sizeof(apr_time_t));
411    memcpy((char *)dbmval.dptr+sizeof(apr_time_t),
412           data.data, data.size);
413
414    dbm_cache_expire(ctxt);
415
416    rv = apr_dbm_open(&dbm, ctxt->sc->cache_config,
417                      APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool);
418    if (rv != APR_SUCCESS) {
419        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
420                     ctxt->c->base_server,
421                     "[gnutls_cache] error opening cache '%s'",
422                     ctxt->sc->cache_config);
423        free(dbmval.dptr);       
424        return -1;
425    }
426
427    rv = apr_dbm_store(dbm, dbmkey, dbmval);
428   
429    if (rv != APR_SUCCESS) {
430        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
431                     ctxt->c->base_server,
432                     "[gnutls_cache] error storing in cache '%s'",
433                     ctxt->sc->cache_config);
434        apr_dbm_close(dbm);
435        free(dbmval.dptr);
436        return -1;
437    }
438
439    apr_dbm_close(dbm);
440
441    free(dbmval.dptr);
442   
443    return 0;
444}
445
446static int dbm_cache_delete(void* baton, gnutls_datum_t key)
447{
448    apr_dbm_t *dbm;
449    apr_datum_t dbmkey;
450    mod_gnutls_handle_t *ctxt = baton;
451    apr_status_t rv;
452   
453    dbmkey.dptr  = (char *)key.data;
454    dbmkey.dsize = key.size;
455
456    dbm_cache_expire(ctxt);
457   
458    rv = apr_dbm_open(&dbm, ctxt->sc->cache_config,
459                      APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool);
460    if (rv != APR_SUCCESS) {
461        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
462                     ctxt->c->base_server,
463                     "[gnutls_cache] error opening cache '%s'",
464                     ctxt->sc->cache_config);
465        return -1;
466    }
467
468    rv = apr_dbm_delete(dbm, dbmkey);
469
470    if (rv != APR_SUCCESS) {
471        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
472                     ctxt->c->base_server,
473                     "[gnutls_cache] error deleting from cache '%s'",
474                     ctxt->sc->cache_config);
475        apr_dbm_close(dbm);
476        return -1;
477    }
478
479    apr_dbm_close(dbm);
480
481    return 0;
482}
483
484static int dbm_cache_post_config(apr_pool_t *p, server_rec *s, 
485                                mod_gnutls_srvconf_rec *sc)
486{
487    apr_status_t rv;
488    apr_dbm_t *dbm;
489    const char* path1;
490    const char* path2;
491
492    rv = apr_dbm_open(&dbm, sc->cache_config, APR_DBM_RWCREATE, 
493                      SSL_DBM_FILE_MODE, p);
494
495    if (rv != APR_SUCCESS) {
496        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
497                     "GnuTLS: Cannot create DBM Cache at `%s'", 
498                     sc->cache_config);
499        return rv; 
500    }
501
502    apr_dbm_close(dbm);
503
504    apr_dbm_get_usednames(p, sc->cache_config, &path1, &path2);
505
506    /* The Following Code takes logic directly from mod_ssl's DBM Cache */ 
507#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
508    /* Running as Root */
509    if (geteuid() == 0)  {
510        chown(path1, unixd_config.user_id, -1);
511        if (path2 != NULL) { 
512            chown(path2, unixd_config.user_id, -1);
513        }
514    }
515#endif
516
517    return rv;
518}
519
520int mod_gnutls_cache_post_config(apr_pool_t *p, server_rec *s, 
521                                 mod_gnutls_srvconf_rec *sc)
522{
523    if (sc->cache_type == mod_gnutls_cache_dbm) {
524        return dbm_cache_post_config(p, s, sc);
525    }
526    return 0;
527}
528
529int mod_gnutls_cache_child_init(apr_pool_t *p, server_rec *s, 
530                                mod_gnutls_srvconf_rec *sc)
531{
532    if (sc->cache_type == mod_gnutls_cache_dbm) {
533        return 0;
534    }
535#if HAVE_APR_MEMCACHE
536    else if (sc->cache_type == mod_gnutls_cache_memcache) { 
537        return mc_cache_child_init(p, s, sc);
538    }
539#endif
540    return 0;
541}
542
543 #include <assert.h>
544
545int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt)
546{
547    if (ctxt->sc->cache_type == mod_gnutls_cache_dbm) {
548        gnutls_db_set_retrieve_function(ctxt->session, dbm_cache_fetch);
549        gnutls_db_set_remove_function(ctxt->session, dbm_cache_delete);
550        gnutls_db_set_store_function(ctxt->session, dbm_cache_store);
551        gnutls_db_set_ptr(ctxt->session, ctxt);
552    }
553#if HAVE_APR_MEMCACHE
554    else if (ctxt->sc->cache_type == mod_gnutls_cache_memcache) { 
555        gnutls_db_set_retrieve_function(ctxt->session, mc_cache_fetch);
556        gnutls_db_set_remove_function(ctxt->session, mc_cache_delete);
557        gnutls_db_set_store_function(ctxt->session, mc_cache_store);
558        gnutls_db_set_ptr(ctxt->session, ctxt);
559    }
560#endif
561
562    return 0;
563}
Note: See TracBrowser for help on using the repository browser.