source: mod_gnutls/src/gnutls_cache.c @ 7bebb42

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 7bebb42 was 7bebb42, checked in by Nokis Mavrogiannopoulos <nmav@…>, 12 years ago

upgraded to 0.4.0

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