source: mod_gnutls/src/gnutls_cache.c @ 47a3f49

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 47a3f49 was 55dc3f0, checked in by Daniel Kahn Gillmor <dkg@…>, 6 years ago

Make Apache 2.4 display the correct module in error logs

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