source: mod_gnutls/src/gnutls_cache.c @ e02dd8c

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

indented code

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