blob: 1901efe9fba67d8dc181db4426e33d3cc506d5b6 [file] [log] [blame]
/* _ _
** _ __ ___ ___ __| | ___ ___| | mod_ssl
** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
** |_____|
** ssl_scache_shmht.c
** Session Cache via Shared Memory (Hash Table Variant)
*/
/* ====================================================================
* 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"
/*
* Wrapper functions for table library which resemble malloc(3) & Co
* but use the variants from the MM shared memory library.
*/
static void *ssl_scache_shmht_malloc(void *opt_param, size_t size)
{
SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
apr_rmm_off_t off = apr_rmm_calloc(mc->pSessionCacheDataRMM, size);
return apr_rmm_addr_get(mc->pSessionCacheDataRMM, off);
}
static void *ssl_scache_shmht_calloc(void *opt_param,
size_t number, size_t size)
{
SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
apr_rmm_off_t off = apr_rmm_calloc(mc->pSessionCacheDataRMM, (number*size));
return apr_rmm_addr_get(mc->pSessionCacheDataRMM, off);
}
static void *ssl_scache_shmht_realloc(void *opt_param, void *ptr, size_t size)
{
SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
apr_rmm_off_t off = apr_rmm_realloc(mc->pSessionCacheDataRMM, ptr, size);
return apr_rmm_addr_get(mc->pSessionCacheDataRMM, off);
}
static void ssl_scache_shmht_free(void *opt_param, void *ptr)
{
SSLModConfigRec *mc = myModConfig((server_rec *)opt_param);
apr_rmm_off_t off = apr_rmm_offset_get(mc->pSessionCacheDataRMM, ptr);
apr_rmm_free(mc->pSessionCacheDataRMM, off);
return;
}
/*
* Now the actual session cache implementation
* based on a hash table inside a shared memory segment.
*/
void ssl_scache_shmht_init(server_rec *s, apr_pool_t *p)
{
SSLModConfigRec *mc = myModConfig(s);
table_t *ta;
int ta_errno;
apr_size_t avail;
int n;
apr_status_t rv;
/*
* Create shared memory segment
*/
if (mc->szSessionCacheDataFile == NULL) {
ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
ssl_die();
}
if ((rv = apr_shm_create(&(mc->pSessionCacheDataMM),
mc->nSessionCacheDataSize,
mc->szSessionCacheDataFile, mc->pPool)) != APR_SUCCESS) {
ssl_log(s, SSL_LOG_ERROR,
"Cannot allocate shared memory: %d", rv);
ssl_die();
}
if ((rv = apr_rmm_init(&(mc->pSessionCacheDataRMM), NULL,
apr_shm_baseaddr_get(mc->pSessionCacheDataMM),
mc->nSessionCacheDataSize, mc->pPool)) != APR_SUCCESS) {
ssl_log(s, SSL_LOG_ERROR,
"Cannot initialize rmm: %d", rv);
ssl_die();
}
ssl_log(s, SSL_LOG_ERROR, "initialize MM %ld RMM %ld",
mc->pSessionCacheDataMM, mc->pSessionCacheDataRMM);
/*
* Create hash table in shared memory segment
*/
avail = mc->nSessionCacheDataSize;
n = (avail/2) / 1024;
n = n < 10 ? 10 : n;
/*
* Passing server_rec as opt_param to table_alloc so that we can do
* logging if required ssl_util_table. Otherwise, mc is sufficient.
*/
if ((ta = table_alloc(n, &ta_errno,
ssl_scache_shmht_malloc,
ssl_scache_shmht_calloc,
ssl_scache_shmht_realloc,
ssl_scache_shmht_free, s )) == NULL) {
ssl_log(s, SSL_LOG_ERROR,
"Cannot allocate hash table in shared memory: %s",
table_strerror(ta_errno));
ssl_die();
}
table_attr(ta, TABLE_FLAG_AUTO_ADJUST|TABLE_FLAG_ADJUST_DOWN);
table_set_data_alignment(ta, sizeof(char *));
table_clear(ta);
mc->tSessionCacheDataTable = ta;
/*
* Log the done work
*/
ssl_log(s, SSL_LOG_INFO,
"Init: Created hash-table (%d buckets) "
"in shared memory (%d bytes) for SSL session cache", n, avail);
return;
}
void ssl_scache_shmht_kill(server_rec *s)
{
SSLModConfigRec *mc = myModConfig(s);
if (mc->pSessionCacheDataRMM != NULL) {
apr_rmm_destroy(mc->pSessionCacheDataRMM);
mc->pSessionCacheDataRMM = NULL;
}
if (mc->pSessionCacheDataMM != NULL) {
apr_shm_destroy(mc->pSessionCacheDataMM);
mc->pSessionCacheDataMM = NULL;
}
return;
}
BOOL ssl_scache_shmht_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
{
SSLModConfigRec *mc = myModConfig(s);
void *vp;
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);
ssl_mutex_on(s);
if (table_insert_kd(mc->tSessionCacheDataTable,
id, idlen, NULL, sizeof(time_t)+nData,
NULL, &vp, 1) != TABLE_ERROR_NONE) {
ssl_mutex_off(s);
return FALSE;
}
memcpy(vp, &expiry, sizeof(time_t));
memcpy((char *)vp+sizeof(time_t), ucaData, nData);
ssl_mutex_off(s);
/* allow the regular expiring to occur */
ssl_scache_shmht_expire(s);
return TRUE;
}
SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *s, UCHAR *id, int idlen)
{
SSLModConfigRec *mc = myModConfig(s);
void *vp;
SSL_SESSION *sess = NULL;
UCHAR *ucpData;
int nData;
time_t expiry;
time_t now;
int n;
/* allow the regular expiring to occur */
ssl_scache_shmht_expire(s);
/* lookup key in table */
ssl_mutex_on(s);
if (table_retrieve(mc->tSessionCacheDataTable,
id, idlen, &vp, &n) != TABLE_ERROR_NONE) {
ssl_mutex_off(s);
return NULL;
}
/* copy over the information to the SCI */
nData = n-sizeof(time_t);
ucpData = (UCHAR *)malloc(nData);
if (ucpData == NULL) {
ssl_mutex_off(s);
return NULL;
}
memcpy(&expiry, vp, sizeof(time_t));
memcpy(ucpData, (char *)vp+sizeof(time_t), nData);
ssl_mutex_off(s);
/* make sure the stuff is still not expired */
now = time(NULL);
if (expiry <= now) {
ssl_scache_shmht_remove(s, id, idlen);
return NULL;
}
/* unstreamed SSL_SESSION */
sess = d2i_SSL_SESSION(NULL, &ucpData, nData);
return sess;
}
void ssl_scache_shmht_remove(server_rec *s, UCHAR *id, int idlen)
{
SSLModConfigRec *mc = myModConfig(s);
/* remove value under key in table */
ssl_mutex_on(s);
table_delete(mc->tSessionCacheDataTable, id, idlen, NULL, NULL);
ssl_mutex_off(s);
return;
}
void ssl_scache_shmht_expire(server_rec *s)
{
SSLModConfigRec *mc = myModConfig(s);
SSLSrvConfigRec *sc = mySrvConfig(s);
static time_t tLast = 0;
table_linear_t iterator;
time_t tExpiresAt;
void *vpKey;
void *vpKeyThis;
void *vpData;
int nKey;
int nKeyThis;
int nData;
int nElements = 0;
int nDeleted = 0;
int bDelete;
int rc;
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->nSessionCacheTimeout)
return;
tLast = tNow;
ssl_mutex_on(s);
if (table_first_r(mc->tSessionCacheDataTable, &iterator,
&vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
do {
bDelete = FALSE;
nElements++;
if (nData < sizeof(time_t) || vpData == NULL)
bDelete = TRUE;
else {
memcpy(&tExpiresAt, vpData, sizeof(time_t));
/*
* XXX : Force the record to be cleaned up. TBD (Madhu)
* tExpiresAt = tNow;
*/
if (tExpiresAt <= tNow)
bDelete = TRUE;
}
vpKeyThis = vpKey;
nKeyThis = nKey;
rc = table_next_r(mc->tSessionCacheDataTable, &iterator,
&vpKey, &nKey, &vpData, &nData);
if (bDelete) {
table_delete(mc->tSessionCacheDataTable,
vpKeyThis, nKeyThis, NULL, NULL);
nDeleted++;
}
} while (rc == TABLE_ERROR_NONE);
/* (vpKeyThis != vpKey) && (nKeyThis != nKey) */
}
ssl_mutex_off(s);
ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (SHMHT) Expiry: "
"old: %d, new: %d, removed: %d",
nElements, nElements-nDeleted, nDeleted);
return;
}
void ssl_scache_shmht_status(server_rec *s, apr_pool_t *p, void (*func)(char *, void *), void *arg)
{
SSLModConfigRec *mc = myModConfig(s);
void *vpKey;
void *vpData;
int nKey;
int nData;
int nElem;
int nSize;
int nAverage;
nElem = 0;
nSize = 0;
ssl_mutex_on(s);
if (table_first(mc->tSessionCacheDataTable,
&vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
do {
if (vpKey == NULL || vpData == NULL)
continue;
nElem += 1;
nSize += nData;
} while (table_next(mc->tSessionCacheDataTable,
&vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE);
}
ssl_mutex_off(s);
if (nSize > 0 && nElem > 0)
nAverage = nSize / nElem;
else
nAverage = 0;
func(apr_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), 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;
}