source: mod_gnutls/src/gnutls_cache.c @ 600cf16

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 600cf16 was e391197, checked in by Thomas Klute <thomas2.klute@…>, 4 years ago

Update copyright headers for C source

  • Property mode set to 100644
File size: 17.1 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *  Copyright 2015 Thomas Klute
6 *
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 */
20
21#include "mod_gnutls.h"
22
23#if HAVE_APR_MEMCACHE
24#include "apr_memcache.h"
25#endif
26
27#include "apr_dbm.h"
28
29#include "ap_mpm.h"
30
31#include <unistd.h>
32#include <sys/types.h>
33
34#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
35#include "unixd.h"
36#endif
37
38/* it seems the default has some strange errors. Use SDBM
39 */
40#define MC_TAG "mod_gnutls:"
41#define MC_TAG_LEN sizeof(MC_TAG)
42#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN)
43
44#if MODULE_MAGIC_NUMBER_MAJOR < 20081201
45#define ap_unixd_config unixd_config
46#endif
47
48#ifdef APLOG_USE_MODULE
49APLOG_USE_MODULE(gnutls);
50#endif
51
52char *mgs_session_id2sz(unsigned char *id, int idlen,
53        char *str, int strsize) {
54    char *cp;
55    int n;
56
57    cp = str;
58    for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) {
59        apr_snprintf(cp, strsize - (cp - str), "%02X", id[n]);
60        cp += 2;
61    }
62    *cp = '\0';
63    return str;
64}
65
66/* Name the Session ID as:
67 * server:port.SessionID
68 * to disallow resuming sessions on different servers
69 */
70static int mgs_session_id2dbm(conn_rec * c, unsigned char *id, int idlen,
71        apr_datum_t * dbmkey) {
72    char buf[STR_SESSION_LEN];
73    char *sz;
74
75    sz = mgs_session_id2sz(id, idlen, buf, sizeof (buf));
76    if (sz == NULL)
77        return -1;
78
79    dbmkey->dptr =
80            apr_psprintf(c->pool, "%s:%d.%s",
81            c->base_server->server_hostname,
82            c->base_server->port, sz);
83    dbmkey->dsize = strlen(dbmkey->dptr);
84
85    return 0;
86}
87
88#define CTIME "%b %d %k:%M:%S %Y %Z"
89
90char *mgs_time2sz(time_t in_time, char *str, int strsize) {
91    apr_time_exp_t vtm;
92    apr_size_t ret_size;
93    apr_time_t t;
94
95
96    apr_time_ansi_put(&t, in_time);
97    apr_time_exp_gmt(&vtm, t);
98    apr_strftime(str, &ret_size, strsize - 1, CTIME, &vtm);
99
100    return str;
101}
102
103#if HAVE_APR_MEMCACHE
104
105/* Name the Session ID as:
106 * server:port.SessionID
107 * to disallow resuming sessions on different servers
108 */
109static char *mgs_session_id2mc(conn_rec * c, unsigned char *id, int idlen) {
110    char buf[STR_SESSION_LEN];
111    char *sz;
112
113    sz = mgs_session_id2sz(id, idlen, buf, sizeof (buf));
114    if (sz == NULL)
115        return NULL;
116
117    return apr_psprintf(c->pool, MC_TAG "%s:%d.%s",
118            c->base_server->server_hostname,
119            c->base_server->port, sz);
120}
121
122/**
123 * GnuTLS Session Cache using libmemcached
124 *
125 */
126
127/* The underlying apr_memcache system is thread safe... woohoo */
128static apr_memcache_t *mc;
129
130static int mc_cache_child_init(apr_pool_t * p, server_rec * s,
131        mgs_srvconf_rec * sc) {
132    apr_status_t rv = APR_SUCCESS;
133    int thread_limit = 0;
134    int nservers = 0;
135    char *cache_config;
136    char *split;
137    char *tok;
138
139    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
140
141    /* Find all the servers in the first run to get a total count */
142    cache_config = apr_pstrdup(p, sc->cache_config);
143    split = apr_strtok(cache_config, " ", &tok);
144    while (split) {
145        nservers++;
146        split = apr_strtok(NULL, " ", &tok);
147    }
148
149    rv = apr_memcache_create(p, nservers, 0, &mc);
150    if (rv != APR_SUCCESS) {
151        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
152                "[gnutls_cache] Failed to create Memcache Object of '%d' size.",
153                nservers);
154        return rv;
155    }
156
157    /* Now add each server to the memcache */
158    cache_config = apr_pstrdup(p, sc->cache_config);
159    split = apr_strtok(cache_config, " ", &tok);
160    while (split) {
161        apr_memcache_server_t *st;
162        char *host_str;
163        char *scope_id;
164        apr_port_t port;
165
166        rv = apr_parse_addr_port(&host_str, &scope_id, &port,
167                split, p);
168        if (rv != APR_SUCCESS) {
169            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
170                    "[gnutls_cache] Failed to Parse Server: '%s'",
171                    split);
172            return rv;
173        }
174
175        if (host_str == NULL) {
176            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
177                    "[gnutls_cache] Failed to Parse Server, "
178                    "no hostname specified: '%s'", split);
179            return rv;
180        }
181
182        if (port == 0) {
183            port = 11211; /* default port */
184        }
185
186        /* Should Max Conns be (thread_limit / nservers) ? */
187        rv = apr_memcache_server_create(p,
188                host_str, port,
189                0,
190                1, thread_limit, 600, &st);
191        if (rv != APR_SUCCESS) {
192            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
193                    "[gnutls_cache] Failed to Create Server: %s:%d",
194                    host_str, port);
195            return rv;
196        }
197
198        rv = apr_memcache_add_server(mc, st);
199        if (rv != APR_SUCCESS) {
200            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
201                    "[gnutls_cache] Failed to Add Server: %s:%d",
202                    host_str, port);
203            return rv;
204        }
205
206        split = apr_strtok(NULL, " ", &tok);
207    }
208    return rv;
209}
210
211static int mc_cache_store(void *baton, gnutls_datum_t key,
212        gnutls_datum_t data) {
213    apr_status_t rv = APR_SUCCESS;
214    mgs_handle_t *ctxt = baton;
215    char *strkey = NULL;
216    apr_uint32_t timeout;
217
218    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
219    if (!strkey)
220        return -1;
221
222    timeout = apr_time_sec(ctxt->sc->cache_timeout);
223
224    rv = apr_memcache_set(mc, strkey, (char *) data.data, data.size, timeout,
225            0);
226
227    if (rv != APR_SUCCESS) {
228        ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
229                ctxt->c->base_server,
230                "[gnutls_cache] error setting key '%s' "
231                "with %d bytes of data", strkey, data.size);
232        return -1;
233    }
234
235    return 0;
236}
237
238static gnutls_datum_t mc_cache_fetch(void *baton, gnutls_datum_t key) {
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    apr_status_t rv = APR_SUCCESS;
279    mgs_handle_t *ctxt = baton;
280    char *strkey = NULL;
281
282    strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
283    if (!strkey)
284        return -1;
285
286    rv = apr_memcache_delete(mc, strkey, 0);
287
288    if (rv != APR_SUCCESS) {
289        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
290                ctxt->c->base_server,
291                "[gnutls_cache] error deleting key '%s' ",
292                strkey);
293        return -1;
294    }
295
296    return 0;
297}
298
299#endif  /* have_apr_memcache */
300
301static const char *db_type(mgs_srvconf_rec * sc) {
302    if (sc->cache_type == mgs_cache_gdbm)
303        return "gdbm";
304    else
305        return "db";
306}
307
308#define SSL_DBM_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
309
310static void dbm_cache_expire(mgs_handle_t * ctxt) {
311    apr_status_t rv;
312    apr_dbm_t *dbm;
313    apr_datum_t dbmkey;
314    apr_datum_t dbmval;
315    apr_time_t now;
316    apr_time_t dtime;
317    apr_pool_t *spool;
318    int total, deleted;
319
320    now = apr_time_now();
321
322    if (now - ctxt->sc->last_cache_check <
323            (ctxt->sc->cache_timeout) / 2)
324        return;
325
326    ctxt->sc->last_cache_check = now;
327
328    apr_pool_create(&spool, ctxt->c->pool);
329
330    total = 0;
331    deleted = 0;
332
333    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
334            ctxt->sc->cache_config, APR_DBM_RWCREATE,
335            SSL_DBM_FILE_MODE, spool);
336    if (rv != APR_SUCCESS) {
337        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
338                ctxt->c->base_server,
339                "[gnutls_cache] error opening cache searcher '%s'",
340                ctxt->sc->cache_config);
341        apr_pool_destroy(spool);
342        return;
343    }
344
345    apr_dbm_firstkey(dbm, &dbmkey);
346    while (dbmkey.dptr != NULL) {
347        apr_dbm_fetch(dbm, dbmkey, &dbmval);
348        if (dbmval.dptr != NULL
349                && dbmval.dsize >= sizeof (apr_time_t)) {
350            memcpy(&dtime, dbmval.dptr, sizeof (apr_time_t));
351
352            if (now >= dtime) {
353                apr_dbm_delete(dbm, dbmkey);
354                deleted++;
355            }
356            apr_dbm_freedatum(dbm, dbmval);
357        } else {
358            apr_dbm_delete(dbm, dbmkey);
359            deleted++;
360        }
361        total++;
362        apr_dbm_nextkey(dbm, &dbmkey);
363    }
364    apr_dbm_close(dbm);
365
366    ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
367            ctxt->c->base_server,
368            "[gnutls_cache] Cleaned up cache '%s'. Deleted %d and left %d",
369            ctxt->sc->cache_config, deleted, total - deleted);
370
371    apr_pool_destroy(spool);
372
373    return;
374}
375
376static gnutls_datum_t dbm_cache_fetch(void *baton, gnutls_datum_t key) {
377    gnutls_datum_t data = {NULL, 0};
378    apr_dbm_t *dbm;
379    apr_datum_t dbmkey;
380    apr_datum_t dbmval;
381    mgs_handle_t *ctxt = baton;
382    apr_status_t rv;
383
384    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
385        return data;
386
387    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
388            ctxt->sc->cache_config, APR_DBM_READONLY,
389            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    apr_dbm_t *dbm;
431    apr_datum_t dbmkey;
432    apr_datum_t dbmval;
433    mgs_handle_t *ctxt = baton;
434    apr_status_t rv;
435    apr_time_t expiry;
436    apr_pool_t *spool;
437
438    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
439        return -1;
440
441    /* we expire dbm only on every store
442     */
443    dbm_cache_expire(ctxt);
444
445    apr_pool_create(&spool, ctxt->c->pool);
446
447    /* create DBM value */
448    dbmval.dsize = data.size + sizeof (apr_time_t);
449    dbmval.dptr = (char *) apr_palloc(spool, dbmval.dsize);
450
451    expiry = apr_time_now() + ctxt->sc->cache_timeout;
452
453    memcpy((char *) dbmval.dptr, &expiry, sizeof (apr_time_t));
454    memcpy((char *) dbmval.dptr + sizeof (apr_time_t),
455            data.data, data.size);
456
457    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
458            ctxt->sc->cache_config, APR_DBM_RWCREATE,
459            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        apr_pool_destroy(spool);
466        return -1;
467    }
468
469    rv = apr_dbm_store(dbm, dbmkey, dbmval);
470
471    if (rv != APR_SUCCESS) {
472        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv,
473                ctxt->c->base_server,
474                "[gnutls_cache] error storing in cache '%s'",
475                ctxt->sc->cache_config);
476        apr_dbm_close(dbm);
477        apr_pool_destroy(spool);
478        return -1;
479    }
480
481    apr_dbm_close(dbm);
482
483    apr_pool_destroy(spool);
484
485    return 0;
486}
487
488static int dbm_cache_delete(void *baton, gnutls_datum_t key) {
489    apr_dbm_t *dbm;
490    apr_datum_t dbmkey;
491    mgs_handle_t *ctxt = baton;
492    apr_status_t rv;
493
494    if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
495        return -1;
496
497    rv = apr_dbm_open_ex(&dbm, db_type(ctxt->sc),
498            ctxt->sc->cache_config, APR_DBM_RWCREATE,
499            SSL_DBM_FILE_MODE, ctxt->c->pool);
500    if (rv != APR_SUCCESS) {
501        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
502                ctxt->c->base_server,
503                "[gnutls_cache] error opening cache '%s'",
504                ctxt->sc->cache_config);
505        return -1;
506    }
507
508    rv = apr_dbm_delete(dbm, dbmkey);
509
510    if (rv != APR_SUCCESS) {
511        ap_log_error(APLOG_MARK, APLOG_NOTICE, rv,
512                ctxt->c->base_server,
513                "[gnutls_cache] error deleting from cache '%s'",
514                ctxt->sc->cache_config);
515        apr_dbm_close(dbm);
516        return -1;
517    }
518
519    apr_dbm_close(dbm);
520
521    return 0;
522}
523
524static int dbm_cache_post_config(apr_pool_t * p, server_rec * s,
525        mgs_srvconf_rec * sc) {
526    apr_status_t rv;
527    apr_dbm_t *dbm;
528    const char *path1;
529    const char *path2;
530
531    rv = apr_dbm_open_ex(&dbm, db_type(sc), sc->cache_config,
532            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, p);
533
534    if (rv != APR_SUCCESS) {
535        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
536                "GnuTLS: Cannot create DBM Cache at `%s'",
537                sc->cache_config);
538        return rv;
539    }
540
541    apr_dbm_close(dbm);
542
543    apr_dbm_get_usednames_ex(p, db_type(sc), sc->cache_config, &path1,
544            &path2);
545
546    /* The Following Code takes logic directly from mod_ssl's DBM Cache */
547#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
548    /* Running as Root */
549    if (path1 && geteuid() == 0) {
550        if (0 != chown(path1, ap_unixd_config.user_id, -1))
551            ap_log_error(APLOG_MARK, APLOG_NOTICE, -1, s,
552                         "GnuTLS: could not chown cache path1 `%s' to uid %d (errno: %d)",
553                         path1, ap_unixd_config.user_id, errno);
554        if (path2 != NULL) {
555            if (0 != chown(path2, ap_unixd_config.user_id, -1))
556                ap_log_error(APLOG_MARK, APLOG_NOTICE, -1, s,
557                             "GnuTLS: could not chown cache path2 `%s' to uid %d (errno: %d)",
558                             path2, ap_unixd_config.user_id, errno);
559        }
560    }
561#endif
562
563    return rv;
564}
565
566int mgs_cache_post_config(apr_pool_t * p, server_rec * s,
567        mgs_srvconf_rec * sc) {
568
569    /* if GnuTLSCache was never explicitly set: */
570    if (sc->cache_type == mgs_cache_unset)
571        sc->cache_type = mgs_cache_none;
572    /* if GnuTLSCacheTimeout was never explicitly set: */
573    if (sc->cache_timeout == -1)
574        sc->cache_timeout = apr_time_from_sec(300);
575
576    if (sc->cache_type == mgs_cache_dbm
577            || sc->cache_type == mgs_cache_gdbm) {
578        return dbm_cache_post_config(p, s, sc);
579    }
580    return 0;
581}
582
583#if HAVE_APR_MEMCACHE
584int mgs_cache_child_init(apr_pool_t * p,
585                         server_rec * s,
586                         mgs_srvconf_rec * sc)
587#else
588int mgs_cache_child_init(apr_pool_t * p __attribute__((unused)),
589                         server_rec * s __attribute__((unused)),
590                         mgs_srvconf_rec * sc)
591#endif
592{
593    if (sc->cache_type == mgs_cache_dbm
594            || sc->cache_type == mgs_cache_gdbm) {
595        return 0;
596    }
597#if HAVE_APR_MEMCACHE
598    else if (sc->cache_type == mgs_cache_memcache) {
599        return mc_cache_child_init(p, s, sc);
600    }
601#endif
602    return 0;
603}
604
605#include <assert.h>
606
607int mgs_cache_session_init(mgs_handle_t * ctxt) {
608    if (ctxt->sc->cache_type == mgs_cache_dbm
609            || ctxt->sc->cache_type == mgs_cache_gdbm) {
610        gnutls_db_set_retrieve_function(ctxt->session,
611                dbm_cache_fetch);
612        gnutls_db_set_remove_function(ctxt->session,
613                dbm_cache_delete);
614        gnutls_db_set_store_function(ctxt->session,
615                dbm_cache_store);
616        gnutls_db_set_ptr(ctxt->session, ctxt);
617    }
618#if HAVE_APR_MEMCACHE
619    else if (ctxt->sc->cache_type == mgs_cache_memcache) {
620        gnutls_db_set_retrieve_function(ctxt->session,
621                mc_cache_fetch);
622        gnutls_db_set_remove_function(ctxt->session,
623                mc_cache_delete);
624        gnutls_db_set_store_function(ctxt->session,
625                mc_cache_store);
626        gnutls_db_set_ptr(ctxt->session, ctxt);
627    }
628#endif
629
630    return 0;
631}
Note: See TracBrowser for help on using the repository browser.