mirror of
https://github.com/apache/httpd.git
synced 2025-08-13 14:40:20 +00:00

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@94264 13f79535-47bb-0310-9956-ffa450edef68
457 lines
15 KiB
C
457 lines
15 KiB
C
/* _ _
|
|
** _ __ ___ ___ __| | ___ ___| | mod_ssl
|
|
** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
|
|
** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
|
|
** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
|
|
** |_____|
|
|
** ssl_scache_dbm.c
|
|
** Session Cache via DBM
|
|
*/
|
|
|
|
/* ====================================================================
|
|
* The Apache Software License, Version 1.1
|
|
*
|
|
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
|
|
* reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. The end-user documentation included with the redistribution,
|
|
* if any, must include the following acknowledgment:
|
|
* "This product includes software developed by the
|
|
* Apache Software Foundation (http://www.apache.org/)."
|
|
* Alternately, this acknowledgment may appear in the software itself,
|
|
* if and wherever such third-party acknowledgments normally appear.
|
|
*
|
|
* 4. The names "Apache" and "Apache Software Foundation" must
|
|
* not be used to endorse or promote products derived from this
|
|
* software without prior written permission. For written
|
|
* permission, please contact apache@apache.org.
|
|
*
|
|
* 5. Products derived from this software may not be called "Apache",
|
|
* nor may "Apache" appear in their name, without prior written
|
|
* permission of the Apache Software Foundation.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
* ====================================================================
|
|
*/
|
|
|
|
#include "mod_ssl.h"
|
|
|
|
void ssl_scache_dbm_init(server_rec *s, apr_pool_t *p)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
apr_dbm_t *dbm;
|
|
|
|
/* for the DBM we need the data file */
|
|
if (mc->szSessionCacheDataFile == NULL) {
|
|
ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
|
|
ssl_die();
|
|
}
|
|
|
|
/* open it once to create it and to make sure it _can_ be created */
|
|
ssl_mutex_on(s);
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot create SSLSessionCache DBM file `%s'",
|
|
mc->szSessionCacheDataFile);
|
|
ssl_mutex_off(s);
|
|
return;
|
|
}
|
|
apr_dbm_close(dbm);
|
|
|
|
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
|
|
/*
|
|
* We have to make sure the Apache child processes have access to
|
|
* the DBM file. But because there are brain-dead platforms where we
|
|
* cannot exactly determine the suffixes we try all possibilities.
|
|
*/
|
|
if (geteuid() == 0 /* is superuser */) {
|
|
chown(mc->szSessionCacheDataFile, unixd_config.user_id, -1 /* no gid change */);
|
|
if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL),
|
|
unixd_config.user_id, -1) == -1) {
|
|
if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
|
|
unixd_config.user_id, -1) == -1)
|
|
chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL),
|
|
unixd_config.user_id, -1);
|
|
}
|
|
if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL),
|
|
unixd_config.user_id, -1) == -1) {
|
|
if (chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL),
|
|
unixd_config.user_id, -1) == -1)
|
|
chown(apr_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL),
|
|
unixd_config.user_id, -1);
|
|
}
|
|
}
|
|
#endif
|
|
ssl_mutex_off(s);
|
|
ssl_scache_dbm_expire(s);
|
|
return;
|
|
}
|
|
|
|
void ssl_scache_dbm_kill(server_rec *s)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
apr_pool_t *p;
|
|
|
|
apr_pool_sub_make(&p, mc->pPool, NULL);
|
|
if (p != NULL) {
|
|
/* the correct way */
|
|
unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL));
|
|
unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL));
|
|
/* the additional ways to be sure */
|
|
unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL));
|
|
unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL));
|
|
unlink(apr_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL));
|
|
unlink(mc->szSessionCacheDataFile);
|
|
apr_pool_destroy(p);
|
|
}
|
|
return;
|
|
}
|
|
|
|
BOOL ssl_scache_dbm_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
apr_dbm_t *dbm;
|
|
apr_datum_t dbmkey;
|
|
apr_datum_t dbmval;
|
|
UCHAR ucaData[SSL_SESSION_MAX_DER];
|
|
int nData;
|
|
UCHAR *ucp;
|
|
|
|
/* streamline session data */
|
|
if ((nData = i2d_SSL_SESSION(sess, NULL)) > sizeof(ucaData))
|
|
return FALSE;
|
|
ucp = ucaData;
|
|
i2d_SSL_SESSION(sess, &ucp);
|
|
|
|
/* be careful: do not try to store too much bytes in a DBM file! */
|
|
#ifdef PAIRMAX
|
|
if ((idlen + nData) >= PAIRMAX)
|
|
return FALSE;
|
|
#else
|
|
if ((idlen + nData) >= 950 /* at least less than approx. 1KB */)
|
|
return FALSE;
|
|
#endif
|
|
|
|
/* create DBM key */
|
|
dbmkey.dptr = (char *)id;
|
|
dbmkey.dsize = idlen;
|
|
|
|
/* create DBM value */
|
|
dbmval.dsize = sizeof(time_t) + nData;
|
|
dbmval.dptr = (char *)malloc(dbmval.dsize);
|
|
if (dbmval.dptr == NULL)
|
|
return FALSE;
|
|
memcpy((char *)dbmval.dptr, &expiry, sizeof(time_t));
|
|
memcpy((char *)dbmval.dptr+sizeof(time_t), ucaData, nData);
|
|
|
|
/* and store it to the DBM file */
|
|
ssl_mutex_on(s);
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot open SSLSessionCache DBM file `%s' for writing (store)",
|
|
mc->szSessionCacheDataFile);
|
|
ssl_mutex_off(s);
|
|
free(dbmval.dptr);
|
|
return FALSE;
|
|
}
|
|
if (apr_dbm_store(dbm, dbmkey, dbmval) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot store SSL session to DBM file `%s'",
|
|
mc->szSessionCacheDataFile);
|
|
apr_dbm_close(dbm);
|
|
ssl_mutex_off(s);
|
|
free(dbmval.dptr);
|
|
return FALSE;
|
|
}
|
|
apr_dbm_close(dbm);
|
|
ssl_mutex_off(s);
|
|
|
|
/* free temporary buffers */
|
|
free(dbmval.dptr);
|
|
|
|
/* allow the regular expiring to occur */
|
|
ssl_scache_dbm_expire(s);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *s, UCHAR *id, int idlen)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
apr_dbm_t *dbm;
|
|
apr_datum_t dbmkey;
|
|
apr_datum_t dbmval;
|
|
SSL_SESSION *sess = NULL;
|
|
UCHAR *ucpData;
|
|
int nData;
|
|
time_t expiry;
|
|
time_t now;
|
|
apr_status_t rc;
|
|
|
|
/* allow the regular expiring to occur */
|
|
ssl_scache_dbm_expire(s);
|
|
|
|
/* create DBM key and values */
|
|
dbmkey.dptr = (char *)id;
|
|
dbmkey.dsize = idlen;
|
|
|
|
/* and fetch it from the DBM file
|
|
* XXX: Should we open the dbm against r->pool so the cleanup will
|
|
* do the apr_dbm_close? This would make the code a bit cleaner.
|
|
*/
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot open SSLSessionCache DBM file `%s' for reading (fetch)",
|
|
mc->szSessionCacheDataFile);
|
|
return NULL;
|
|
}
|
|
rc = apr_dbm_fetch(dbm, dbmkey, &dbmval);
|
|
if (rc != APR_SUCCESS) {
|
|
apr_dbm_close(dbm);
|
|
return NULL;
|
|
}
|
|
if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t)) {
|
|
apr_dbm_close(dbm);
|
|
return NULL;
|
|
}
|
|
|
|
/* parse resulting data */
|
|
nData = dbmval.dsize-sizeof(time_t);
|
|
ucpData = (UCHAR *)malloc(nData);
|
|
if (ucpData == NULL) {
|
|
apr_dbm_close(dbm);
|
|
return NULL;
|
|
}
|
|
memcpy(ucpData, (char *)dbmval.dptr+sizeof(time_t), nData);
|
|
memcpy(&expiry, dbmval.dptr, sizeof(time_t));
|
|
|
|
apr_dbm_close(dbm);
|
|
|
|
/* make sure the stuff is still not expired */
|
|
now = time(NULL);
|
|
if (expiry <= now) {
|
|
ssl_scache_dbm_remove(s, id, idlen);
|
|
return NULL;
|
|
}
|
|
|
|
/* unstreamed SSL_SESSION */
|
|
sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
|
|
|
|
return sess;
|
|
}
|
|
|
|
void ssl_scache_dbm_remove(server_rec *s, UCHAR *id, int idlen)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
apr_dbm_t *dbm;
|
|
apr_datum_t dbmkey;
|
|
|
|
/* create DBM key and values */
|
|
dbmkey.dptr = (char *)id;
|
|
dbmkey.dsize = idlen;
|
|
|
|
/* and delete it from the DBM file */
|
|
ssl_mutex_on(s);
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot open SSLSessionCache DBM file `%s' for writing (delete)",
|
|
mc->szSessionCacheDataFile);
|
|
ssl_mutex_off(s);
|
|
return;
|
|
}
|
|
apr_dbm_delete(dbm, dbmkey);
|
|
apr_dbm_close(dbm);
|
|
ssl_mutex_off(s);
|
|
|
|
return;
|
|
}
|
|
|
|
void ssl_scache_dbm_expire(server_rec *s)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
SSLSrvConfigRec *sc = mySrvConfig(s);
|
|
static time_t tLast = 0;
|
|
apr_dbm_t *dbm;
|
|
apr_datum_t dbmkey;
|
|
apr_datum_t dbmval;
|
|
apr_pool_t *p;
|
|
time_t tExpiresAt;
|
|
int nElements = 0;
|
|
int nDeleted = 0;
|
|
int bDelete;
|
|
apr_datum_t *keylist;
|
|
int keyidx;
|
|
int i;
|
|
time_t tNow;
|
|
|
|
/*
|
|
* make sure the expiration for still not-accessed session
|
|
* cache entries is done only from time to time
|
|
*/
|
|
tNow = time(NULL);
|
|
if (tNow < tLast+sc->session_cache_timeout)
|
|
return;
|
|
tLast = tNow;
|
|
|
|
/*
|
|
* Here we have to be very carefully: Not all DBM libraries are
|
|
* smart enough to allow one to iterate over the elements and at the
|
|
* same time delete expired ones. Some of them get totally crazy
|
|
* while others have no problems. So we have to do it the slower but
|
|
* more safe way: we first iterate over all elements and remember
|
|
* those which have to be expired. Then in a second pass we delete
|
|
* all those expired elements. Additionally we reopen the DBM file
|
|
* to be really safe in state.
|
|
*/
|
|
|
|
#define KEYMAX 1024
|
|
|
|
ssl_mutex_on(s);
|
|
for (;;) {
|
|
/* allocate the key array in a memory sub pool */
|
|
apr_pool_sub_make(&p, mc->pPool, NULL);
|
|
if (p == NULL)
|
|
break;
|
|
if ((keylist = apr_palloc(p, sizeof(dbmkey)*KEYMAX)) == NULL) {
|
|
apr_pool_destroy(p);
|
|
break;
|
|
}
|
|
|
|
/* pass 1: scan DBM database */
|
|
keyidx = 0;
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE,SSL_DBM_FILE_MODE, p) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot open SSLSessionCache DBM file `%s' for scanning",
|
|
mc->szSessionCacheDataFile);
|
|
apr_pool_destroy(p);
|
|
break;
|
|
}
|
|
apr_dbm_firstkey(dbm, &dbmkey);
|
|
while (dbmkey.dptr != NULL) {
|
|
nElements++;
|
|
bDelete = FALSE;
|
|
apr_dbm_fetch(dbm, dbmkey, &dbmval);
|
|
if (dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL)
|
|
bDelete = TRUE;
|
|
else {
|
|
memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t));
|
|
if (tExpiresAt <= tNow)
|
|
bDelete = TRUE;
|
|
}
|
|
if (bDelete) {
|
|
if ((keylist[keyidx].dptr = apr_palloc(p, dbmkey.dsize)) != NULL) {
|
|
memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize);
|
|
keylist[keyidx].dsize = dbmkey.dsize;
|
|
keyidx++;
|
|
if (keyidx == KEYMAX)
|
|
break;
|
|
}
|
|
}
|
|
apr_dbm_nextkey(dbm, &dbmkey);
|
|
}
|
|
apr_dbm_close(dbm);
|
|
|
|
/* pass 2: delete expired elements */
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE,SSL_DBM_FILE_MODE, p) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot re-open SSLSessionCache DBM file `%s' for expiring",
|
|
mc->szSessionCacheDataFile);
|
|
apr_pool_destroy(p);
|
|
break;
|
|
}
|
|
for (i = 0; i < keyidx; i++) {
|
|
apr_dbm_delete(dbm, keylist[i]);
|
|
nDeleted++;
|
|
}
|
|
apr_dbm_close(dbm);
|
|
|
|
/* destroy temporary pool */
|
|
apr_pool_destroy(p);
|
|
|
|
if (keyidx < KEYMAX)
|
|
break;
|
|
}
|
|
ssl_mutex_off(s);
|
|
|
|
ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (DBM) Expiry: "
|
|
"old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted);
|
|
return;
|
|
}
|
|
|
|
void ssl_scache_dbm_status(server_rec *s, apr_pool_t *p, void (*func)(char *, void *), void *arg)
|
|
{
|
|
SSLModConfigRec *mc = myModConfig(s);
|
|
apr_dbm_t *dbm;
|
|
apr_datum_t dbmkey;
|
|
apr_datum_t dbmval;
|
|
int nElem;
|
|
int nSize;
|
|
int nAverage;
|
|
|
|
nElem = 0;
|
|
nSize = 0;
|
|
ssl_mutex_on(s);
|
|
/*
|
|
* XXX - Check what pool is to be used - TBD
|
|
*/
|
|
if (apr_dbm_open(&dbm, mc->szSessionCacheDataFile,
|
|
APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, mc->pPool) != APR_SUCCESS) {
|
|
ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO,
|
|
"Cannot open SSLSessionCache DBM file `%s' for status retrival",
|
|
mc->szSessionCacheDataFile);
|
|
ssl_mutex_off(s);
|
|
return;
|
|
}
|
|
/*
|
|
* XXX - Check the return value of apr_dbm_firstkey, apr_dbm_fetch - TBD
|
|
*/
|
|
apr_dbm_firstkey(dbm, &dbmkey);
|
|
for ( ; dbmkey.dptr != NULL; apr_dbm_nextkey(dbm, &dbmkey)) {
|
|
apr_dbm_fetch(dbm, dbmkey, &dbmval);
|
|
if (dbmval.dptr == NULL)
|
|
continue;
|
|
nElem += 1;
|
|
nSize += dbmval.dsize;
|
|
}
|
|
apr_dbm_close(dbm);
|
|
ssl_mutex_off(s);
|
|
if (nSize > 0 && nElem > 0)
|
|
nAverage = nSize / nElem;
|
|
else
|
|
nAverage = 0;
|
|
func(apr_psprintf(p, "cache type: <b>DBM</b>, maximum size: <b>unlimited</b><br>"), arg);
|
|
func(apr_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
|
|
func(apr_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
|
|
return;
|
|
}
|
|
|