~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/ssl/ssl_scache_dbm.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
 
2
 * contributor license agreements.  See the NOTICE file distributed with
 
3
 * this work for additional information regarding copyright ownership.
 
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
5
 * (the "License"); you may not use this file except in compliance with
 
6
 * the License.  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
 *  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
 
19
 * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
 
20
 * | | | | | | (_) | (_| |   \__ \__ \ |
 
21
 * |_| |_| |_|\___/ \__,_|___|___/___/_|
 
22
 *                      |_____|
 
23
 *  ssl_scache_dbm.c
 
24
 *  Session Cache via DBM
 
25
 */
 
26
 
 
27
#include "ssl_private.h"
 
28
 
 
29
static void ssl_scache_dbm_expire(server_rec *s);
 
30
 
 
31
void ssl_scache_dbm_init(server_rec *s, apr_pool_t *p)
 
32
{
 
33
    SSLModConfigRec *mc = myModConfig(s);
 
34
    apr_dbm_t *dbm;
 
35
    apr_status_t rv;
 
36
 
 
37
    /* for the DBM we need the data file */
 
38
    if (mc->szSessionCacheDataFile == NULL) {
 
39
        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
 
40
                     "SSLSessionCache required");
 
41
        ssl_die();
 
42
    }
 
43
 
 
44
    /* open it once to create it and to make sure it _can_ be created */
 
45
    ssl_mutex_on(s);
 
46
    if ((rv = apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
47
            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool)) != APR_SUCCESS) {
 
48
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
49
                     "Cannot create SSLSessionCache DBM file `%s'",
 
50
                     mc->szSessionCacheDataFile);
 
51
        ssl_mutex_off(s);
 
52
        return;
 
53
    }
 
54
    apr_dbm_close(dbm);
 
55
 
 
56
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
 
57
    /*
 
58
     * We have to make sure the Apache child processes have access to
 
59
     * the DBM file. But because there are brain-dead platforms where we
 
60
     * cannot exactly determine the suffixes we try all possibilities.
 
61
     */
 
62
    if (geteuid() == 0 /* is superuser */) {
 
63
        chown(mc->szSessionCacheDataFile, unixd_config.user_id, -1 /* no gid change */);
 
64
        if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL),
 
65
                  unixd_config.user_id, -1) == -1) {
 
66
            if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
 
67
                      unixd_config.user_id, -1) == -1)
 
68
                chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL),
 
69
                      unixd_config.user_id, -1);
 
70
        }
 
71
        if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL),
 
72
                  unixd_config.user_id, -1) == -1) {
 
73
            if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
 
74
                      unixd_config.user_id, -1) == -1)
 
75
                chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL),
 
76
                      unixd_config.user_id, -1);
 
77
        }
 
78
    }
 
79
#endif
 
80
    ssl_mutex_off(s);
 
81
    ssl_scache_dbm_expire(s);
 
82
    return;
 
83
}
 
84
 
 
85
void ssl_scache_dbm_kill(server_rec *s)
 
86
{
 
87
    SSLModConfigRec *mc = myModConfig(s);
 
88
    apr_pool_t *p;
 
89
 
 
90
    apr_pool_create_ex(&p, mc->pPool, NULL, NULL);
 
91
    if (p != NULL) {
 
92
        /* the correct way */
 
93
        unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL));
 
94
        unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL));
 
95
        /* the additional ways to be sure */
 
96
        unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL));
 
97
        unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL));
 
98
        unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL));
 
99
        unlink(mc->szSessionCacheDataFile);
 
100
        apr_pool_destroy(p);
 
101
    }
 
102
    return;
 
103
}
 
104
 
 
105
BOOL ssl_scache_dbm_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
 
106
{
 
107
    SSLModConfigRec *mc = myModConfig(s);
 
108
    apr_dbm_t *dbm;
 
109
    apr_datum_t dbmkey;
 
110
    apr_datum_t dbmval;
 
111
    UCHAR ucaData[SSL_SESSION_MAX_DER];
 
112
    int nData;
 
113
    UCHAR *ucp;
 
114
    apr_status_t rv;
 
115
 
 
116
    /* streamline session data */
 
117
    if ((nData = i2d_SSL_SESSION(sess, NULL)) > sizeof(ucaData)) {
 
118
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
 
119
                     "streamline session data size too large: %d > "
 
120
                     "%" APR_SIZE_T_FMT,
 
121
                     nData, sizeof(ucaData));
 
122
        return FALSE;
 
123
    }
 
124
    ucp = ucaData;
 
125
    i2d_SSL_SESSION(sess, &ucp);
 
126
 
 
127
    /* be careful: do not try to store too much bytes in a DBM file! */
 
128
#ifdef PAIRMAX
 
129
    if ((idlen + nData) >= PAIRMAX) {
 
130
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
 
131
                 "data size too large for DBM session cache: %d >= %d",
 
132
                 (idlen + nData), PAIRMAX);
 
133
        return FALSE;
 
134
    }
 
135
#else
 
136
    if ((idlen + nData) >= 950 /* at least less than approx. 1KB */) {
 
137
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
 
138
                 "data size too large for DBM session cache: %d >= %d",
 
139
                 (idlen + nData), 950);
 
140
        return FALSE;
 
141
    }
 
142
#endif
 
143
 
 
144
    /* create DBM key */
 
145
    dbmkey.dptr  = (char *)id;
 
146
    dbmkey.dsize = idlen;
 
147
 
 
148
    /* create DBM value */
 
149
    dbmval.dsize = sizeof(time_t) + nData;
 
150
    dbmval.dptr  = (char *)malloc(dbmval.dsize);
 
151
    if (dbmval.dptr == NULL) {
 
152
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
 
153
                 "malloc error creating DBM value");
 
154
        return FALSE;
 
155
    }
 
156
    memcpy((char *)dbmval.dptr, &expiry, sizeof(time_t));
 
157
    memcpy((char *)dbmval.dptr+sizeof(time_t), ucaData, nData);
 
158
 
 
159
    /* and store it to the DBM file */
 
160
    ssl_mutex_on(s);
 
161
    if ((rv = apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
162
            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool)) != APR_SUCCESS) {
 
163
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
164
                     "Cannot open SSLSessionCache DBM file `%s' for writing "
 
165
                     "(store)",
 
166
                     mc->szSessionCacheDataFile);
 
167
        ssl_mutex_off(s);
 
168
        free(dbmval.dptr);
 
169
        return FALSE;
 
170
    }
 
171
    if ((rv = apr_dbm_store(dbm, dbmkey, dbmval)) != APR_SUCCESS) {
 
172
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
173
                     "Cannot store SSL session to DBM file `%s'",
 
174
                     mc->szSessionCacheDataFile);
 
175
        apr_dbm_close(dbm);
 
176
        ssl_mutex_off(s);
 
177
        free(dbmval.dptr);
 
178
        return FALSE;
 
179
    }
 
180
    apr_dbm_close(dbm);
 
181
    ssl_mutex_off(s);
 
182
 
 
183
    /* free temporary buffers */
 
184
    free(dbmval.dptr);
 
185
 
 
186
    /* allow the regular expiring to occur */
 
187
    ssl_scache_dbm_expire(s);
 
188
 
 
189
    return TRUE;
 
190
}
 
191
 
 
192
SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *s, UCHAR *id, int idlen)
 
193
{
 
194
    SSLModConfigRec *mc = myModConfig(s);
 
195
    apr_dbm_t *dbm;
 
196
    apr_datum_t dbmkey;
 
197
    apr_datum_t dbmval;
 
198
    SSL_SESSION *sess = NULL;
 
199
    MODSSL_D2I_SSL_SESSION_CONST unsigned char *ucpData;
 
200
    int nData;
 
201
    time_t expiry;
 
202
    time_t now;
 
203
    apr_status_t rc;
 
204
 
 
205
    /* allow the regular expiring to occur */
 
206
    ssl_scache_dbm_expire(s);
 
207
 
 
208
    /* create DBM key and values */
 
209
    dbmkey.dptr  = (char *)id;
 
210
    dbmkey.dsize = idlen;
 
211
 
 
212
    /* and fetch it from the DBM file
 
213
     * XXX: Should we open the dbm against r->pool so the cleanup will
 
214
     * do the apr_dbm_close? This would make the code a bit cleaner.
 
215
     */
 
216
    ssl_mutex_on(s);
 
217
    if ((rc = apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
218
            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool)) != APR_SUCCESS) {
 
219
        ap_log_error(APLOG_MARK, APLOG_ERR, rc, s,
 
220
                     "Cannot open SSLSessionCache DBM file `%s' for reading "
 
221
                     "(fetch)",
 
222
                     mc->szSessionCacheDataFile);
 
223
        ssl_mutex_off(s);
 
224
        return NULL;
 
225
    }
 
226
    rc = apr_dbm_fetch(dbm, dbmkey, &dbmval);
 
227
    if (rc != APR_SUCCESS) {
 
228
        apr_dbm_close(dbm);
 
229
        ssl_mutex_off(s);
 
230
        return NULL;
 
231
    }
 
232
    if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t)) {
 
233
        apr_dbm_close(dbm);
 
234
        ssl_mutex_off(s);
 
235
        return NULL;
 
236
    }
 
237
 
 
238
    /* parse resulting data */
 
239
    nData = dbmval.dsize-sizeof(time_t);
 
240
    ucpData = malloc(nData);
 
241
    if (ucpData == NULL) {
 
242
        apr_dbm_close(dbm);
 
243
        ssl_mutex_off(s);
 
244
        return NULL;
 
245
    }
 
246
    /* Cast needed, ucpData may be const */
 
247
    memcpy((unsigned char *)ucpData,
 
248
           (char *)dbmval.dptr + sizeof(time_t), nData);
 
249
    memcpy(&expiry, dbmval.dptr, sizeof(time_t));
 
250
 
 
251
    apr_dbm_close(dbm);
 
252
    ssl_mutex_off(s);
 
253
 
 
254
    /* make sure the stuff is still not expired */
 
255
    now = time(NULL);
 
256
    if (expiry <= now) {
 
257
        ssl_scache_dbm_remove(s, id, idlen);
 
258
        return NULL;
 
259
    }
 
260
 
 
261
    /* unstreamed SSL_SESSION */
 
262
    sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
 
263
 
 
264
    return sess;
 
265
}
 
266
 
 
267
void ssl_scache_dbm_remove(server_rec *s, UCHAR *id, int idlen)
 
268
{
 
269
    SSLModConfigRec *mc = myModConfig(s);
 
270
    apr_dbm_t *dbm;
 
271
    apr_datum_t dbmkey;
 
272
    apr_status_t rv;
 
273
 
 
274
    /* create DBM key and values */
 
275
    dbmkey.dptr  = (char *)id;
 
276
    dbmkey.dsize = idlen;
 
277
 
 
278
    /* and delete it from the DBM file */
 
279
    ssl_mutex_on(s);
 
280
    if ((rv = apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
281
            APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool)) != APR_SUCCESS) {
 
282
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
283
                     "Cannot open SSLSessionCache DBM file `%s' for writing "
 
284
                     "(delete)",
 
285
                     mc->szSessionCacheDataFile);
 
286
        ssl_mutex_off(s);
 
287
        return;
 
288
    }
 
289
    apr_dbm_delete(dbm, dbmkey);
 
290
    apr_dbm_close(dbm);
 
291
    ssl_mutex_off(s);
 
292
 
 
293
    return;
 
294
}
 
295
 
 
296
static void ssl_scache_dbm_expire(server_rec *s)
 
297
{
 
298
    SSLModConfigRec *mc = myModConfig(s);
 
299
    SSLSrvConfigRec *sc = mySrvConfig(s);
 
300
    static time_t tLast = 0;
 
301
    apr_dbm_t *dbm;
 
302
    apr_datum_t dbmkey;
 
303
    apr_datum_t dbmval;
 
304
    apr_pool_t *p;
 
305
    time_t tExpiresAt;
 
306
    int nElements = 0;
 
307
    int nDeleted = 0;
 
308
    int bDelete;
 
309
    apr_datum_t *keylist;
 
310
    int keyidx;
 
311
    int i;
 
312
    time_t tNow;
 
313
    apr_status_t rv;
 
314
 
 
315
    /*
 
316
     * make sure the expiration for still not-accessed session
 
317
     * cache entries is done only from time to time
 
318
     */
 
319
    tNow = time(NULL);
 
320
    if (tNow < tLast+sc->session_cache_timeout)
 
321
        return;
 
322
    tLast = tNow;
 
323
 
 
324
    /*
 
325
     * Here we have to be very carefully: Not all DBM libraries are
 
326
     * smart enough to allow one to iterate over the elements and at the
 
327
     * same time delete expired ones. Some of them get totally crazy
 
328
     * while others have no problems. So we have to do it the slower but
 
329
     * more safe way: we first iterate over all elements and remember
 
330
     * those which have to be expired. Then in a second pass we delete
 
331
     * all those expired elements. Additionally we reopen the DBM file
 
332
     * to be really safe in state.
 
333
     */
 
334
 
 
335
#define KEYMAX 1024
 
336
 
 
337
    ssl_mutex_on(s);
 
338
    for (;;) {
 
339
        /* allocate the key array in a memory sub pool */
 
340
        apr_pool_create_ex(&p, mc->pPool, NULL, NULL);
 
341
        if (p == NULL)
 
342
            break;
 
343
        if ((keylist = apr_palloc(p, sizeof(dbmkey)*KEYMAX)) == NULL) {
 
344
            apr_pool_destroy(p);
 
345
            break;
 
346
        }
 
347
 
 
348
        /* pass 1: scan DBM database */
 
349
        keyidx = 0;
 
350
        if ((rv = apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
351
                               APR_DBM_RWCREATE,SSL_DBM_FILE_MODE,
 
352
                               p)) != APR_SUCCESS) {
 
353
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
354
                         "Cannot open SSLSessionCache DBM file `%s' for "
 
355
                         "scanning",
 
356
                         mc->szSessionCacheDataFile);
 
357
            apr_pool_destroy(p);
 
358
            break;
 
359
        }
 
360
        apr_dbm_firstkey(dbm, &dbmkey);
 
361
        while (dbmkey.dptr != NULL) {
 
362
            nElements++;
 
363
            bDelete = FALSE;
 
364
            apr_dbm_fetch(dbm, dbmkey, &dbmval);
 
365
            if (dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL)
 
366
                bDelete = TRUE;
 
367
            else {
 
368
                memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t));
 
369
                if (tExpiresAt <= tNow)
 
370
                    bDelete = TRUE;
 
371
            }
 
372
            if (bDelete) {
 
373
                if ((keylist[keyidx].dptr = apr_palloc(p, dbmkey.dsize)) != NULL) {
 
374
                    memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize);
 
375
                    keylist[keyidx].dsize = dbmkey.dsize;
 
376
                    keyidx++;
 
377
                    if (keyidx == KEYMAX)
 
378
                        break;
 
379
                }
 
380
            }
 
381
            apr_dbm_nextkey(dbm, &dbmkey);
 
382
        }
 
383
        apr_dbm_close(dbm);
 
384
 
 
385
        /* pass 2: delete expired elements */
 
386
        if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
387
                APR_DBM_RWCREATE,SSL_DBM_FILE_MODE, p) != APR_SUCCESS) {
 
388
            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
 
389
                         "Cannot re-open SSLSessionCache DBM file `%s' for "
 
390
                         "expiring",
 
391
                         mc->szSessionCacheDataFile);
 
392
            apr_pool_destroy(p);
 
393
            break;
 
394
        }
 
395
        for (i = 0; i < keyidx; i++) {
 
396
            apr_dbm_delete(dbm, keylist[i]);
 
397
            nDeleted++;
 
398
        }
 
399
        apr_dbm_close(dbm);
 
400
 
 
401
        /* destroy temporary pool */
 
402
        apr_pool_destroy(p);
 
403
 
 
404
        if (keyidx < KEYMAX)
 
405
            break;
 
406
    }
 
407
    ssl_mutex_off(s);
 
408
 
 
409
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
 
410
                 "Inter-Process Session Cache (DBM) Expiry: "
 
411
                 "old: %d, new: %d, removed: %d",
 
412
                 nElements, nElements-nDeleted, nDeleted);
 
413
    return;
 
414
}
 
415
 
 
416
void ssl_scache_dbm_status(request_rec *r, int flags, apr_pool_t *p)
 
417
{
 
418
    SSLModConfigRec *mc = myModConfig(r->server);
 
419
    apr_dbm_t *dbm;
 
420
    apr_datum_t dbmkey;
 
421
    apr_datum_t dbmval;
 
422
    int nElem;
 
423
    int nSize;
 
424
    int nAverage;
 
425
    apr_status_t rv;
 
426
 
 
427
    nElem = 0;
 
428
    nSize = 0;
 
429
    ssl_mutex_on(r->server);
 
430
    /*
 
431
     * XXX - Check what pool is to be used - TBD
 
432
     */
 
433
    if ((rv = apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
 
434
                               APR_DBM_RWCREATE, SSL_DBM_FILE_MODE,
 
435
                           mc->pPool)) != APR_SUCCESS) {
 
436
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
 
437
                     "Cannot open SSLSessionCache DBM file `%s' for status "
 
438
                     "retrival",
 
439
                     mc->szSessionCacheDataFile);
 
440
        ssl_mutex_off(r->server);
 
441
        return;
 
442
    }
 
443
    /*
 
444
     * XXX - Check the return value of apr_dbm_firstkey, apr_dbm_fetch - TBD
 
445
     */
 
446
    apr_dbm_firstkey(dbm, &dbmkey);
 
447
    for ( ; dbmkey.dptr != NULL; apr_dbm_nextkey(dbm, &dbmkey)) {
 
448
        apr_dbm_fetch(dbm, dbmkey, &dbmval);
 
449
        if (dbmval.dptr == NULL)
 
450
            continue;
 
451
        nElem += 1;
 
452
        nSize += dbmval.dsize;
 
453
    }
 
454
    apr_dbm_close(dbm);
 
455
    ssl_mutex_off(r->server);
 
456
    if (nSize > 0 && nElem > 0)
 
457
        nAverage = nSize / nElem;
 
458
    else
 
459
        nAverage = 0;
 
460
    ap_rprintf(r, "cache type: <b>DBM</b>, maximum size: <b>unlimited</b><br>");
 
461
    ap_rprintf(r, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize);
 
462
    ap_rprintf(r, "average session size: <b>%d</b> bytes<br>", nAverage);
 
463
    return;
 
464
}
 
465