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