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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * _ __ ___ ___ __| | ___ ___| | mod_ssl
19 * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
20 * | | | | | | (_) | (_| | \__ \__ \ |
21 * |_| |_| |_|\___/ \__,_|___|___/___/_|
24 * Session Cache via DBM
29 void ssl_scache_dbm_init(server_rec *s, apr_pool_t *p)
31 SSLModConfigRec *mc = myModConfig(s);
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");
42 /* open it once to create it and to make sure it _can_ be created */
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);
54 #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
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.
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);
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);
79 ssl_scache_dbm_expire(s);
83 void ssl_scache_dbm_kill(server_rec *s)
85 SSLModConfigRec *mc = myModConfig(s);
88 apr_pool_sub_make(&p, mc->pPool, NULL);
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);
103 BOOL ssl_scache_dbm_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
105 SSLModConfigRec *mc = myModConfig(s);
109 UCHAR ucaData[SSL_SESSION_MAX_DER];
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));
122 i2d_SSL_SESSION(sess, &ucp);
124 /* be careful: do not try to store too much bytes in a DBM file! */
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);
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);
142 dbmkey.dptr = (char *)id;
143 dbmkey.dsize = idlen;
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");
153 memcpy((char *)dbmval.dptr, &expiry, sizeof(time_t));
154 memcpy((char *)dbmval.dptr+sizeof(time_t), ucaData, nData);
156 /* and store it to the DBM file */
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 "
163 mc->szSessionCacheDataFile);
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);
180 /* free temporary buffers */
183 /* allow the regular expiring to occur */
184 ssl_scache_dbm_expire(s);
189 SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *s, UCHAR *id, int idlen)
191 SSLModConfigRec *mc = myModConfig(s);
195 SSL_SESSION *sess = NULL;
196 MODSSL_D2I_SSL_SESSION_CONST unsigned char *ucpData;
202 /* allow the regular expiring to occur */
203 ssl_scache_dbm_expire(s);
205 /* create DBM key and values */
206 dbmkey.dptr = (char *)id;
207 dbmkey.dsize = idlen;
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.
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 "
219 mc->szSessionCacheDataFile);
223 rc = apr_dbm_fetch(dbm, dbmkey, &dbmval);
224 if (rc != APR_SUCCESS) {
229 if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t)) {
235 /* parse resulting data */
236 nData = dbmval.dsize-sizeof(time_t);
237 ucpData = malloc(nData);
238 if (ucpData == NULL) {
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));
251 /* make sure the stuff is still not expired */
254 ssl_scache_dbm_remove(s, id, idlen);
258 /* unstreamed SSL_SESSION */
259 sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
264 void ssl_scache_dbm_remove(server_rec *s, UCHAR *id, int idlen)
266 SSLModConfigRec *mc = myModConfig(s);
271 /* create DBM key and values */
272 dbmkey.dptr = (char *)id;
273 dbmkey.dsize = idlen;
275 /* and delete it from the DBM file */
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 "
282 mc->szSessionCacheDataFile);
286 apr_dbm_delete(dbm, dbmkey);
293 void ssl_scache_dbm_expire(server_rec *s)
295 SSLModConfigRec *mc = myModConfig(s);
296 SSLSrvConfigRec *sc = mySrvConfig(s);
297 static time_t tLast = 0;
306 apr_datum_t *keylist;
313 * make sure the expiration for still not-accessed session
314 * cache entries is done only from time to time
317 if (tNow < tLast+sc->session_cache_timeout)
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.
336 /* allocate the key array in a memory sub pool */
337 apr_pool_sub_make(&p, mc->pPool, NULL);
340 if ((keylist = apr_palloc(p, sizeof(dbmkey)*KEYMAX)) == NULL) {
345 /* pass 1: scan DBM database */
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 "
353 mc->szSessionCacheDataFile);
357 apr_dbm_firstkey(dbm, &dbmkey);
358 while (dbmkey.dptr != NULL) {
361 apr_dbm_fetch(dbm, dbmkey, &dbmval);
362 if (dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL)
365 memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t));
366 if (tExpiresAt <= tNow)
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;
374 if (keyidx == KEYMAX)
378 apr_dbm_nextkey(dbm, &dbmkey);
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 "
388 mc->szSessionCacheDataFile);
392 for (i = 0; i < keyidx; i++) {
393 apr_dbm_delete(dbm, keylist[i]);
398 /* destroy temporary pool */
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);
413 void ssl_scache_dbm_status(server_rec *s, apr_pool_t *p, void (*func)(char *, void *), void *arg)
415 SSLModConfigRec *mc = myModConfig(s);
428 * XXX - Check what pool is to be used - TBD
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 "
436 mc->szSessionCacheDataFile);
441 * XXX - Check the return value of apr_dbm_firstkey, apr_dbm_fetch - TBD
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)
449 nSize += dbmval.dsize;
453 if (nSize > 0 && nElem > 0)
454 nAverage = nSize / nElem;
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);