source: mod_gnutls/src/gnutls_cache.c @ b072204

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

corrected SRP enable flag, and corrected the DBM hook support. It now free data needed by some DBM providers.

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