1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

Add support for distributed caching of SSL Sessions inside memcached, using apr_memcache, which is present in APR-Util 1.3/trunk.

This was originally written at ApacheCon US 2005 (San Diego), and was sent to the list:
http://mail-archives.apache.org/mod_mbox/httpd-dev/200512.mbox/%3C439C6C07.9030904@force-elite.com%3E

This version is slightly cleaned up, and of course, uses the now bundled apr_memcache, rather than an external dependency.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@545379 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Paul Querna
2007-06-08 02:48:04 +00:00
parent 94743e8f7b
commit 1b7a5c2566
6 changed files with 391 additions and 0 deletions

View File

@@ -2,6 +2,8 @@
Changes with Apache 2.3.0
[Remove entries to the current 2.0 and 2.2 section below, when backported]
*) mod_ssl: Add support for caching SSL Sessions in memcached. [Paul Querna]
*) SECURITY: CVE-2007-1862 (cve.mitre.org)
mod_mem_cache: Copy headers into longer lived storage; header names and
values could previously point to cleaned up storage

View File

@@ -86,6 +86,56 @@ ap_ssltk_dc="no"])
fi
])
AC_DEFUN([CHECK_SSL_MEMCACHE], [
AC_MSG_CHECKING(for ssl session caching in memcache)
ap_ssltk_mc="no"
tmp_nomessage=""
tmp_forced="no"
AC_ARG_ENABLE(ssl-memcache,
APACHE_HELP_STRING(--enable-ssl-memcache,Select memcache support in mod_ssl),
ap_ssltk_mc="$enableval"
tmp_nomessage=""
tmp_forced="yes"
if test "x$ap_ssltk_mc" = "x"; then
ap_ssltk_mc="yes"
dnl our "error"s become "tests revealed that..."
tmp_forced="no"
fi
if test "$ap_ssltk_mc" != "yes" -a "$ap_ssltk_mc" != "no"; then
tmp_nomessage="--enable-ssl-cache-memcache had illegal syntax - disabling"
ap_ssltk_mc="no"
fi)
if test "$tmp_forced" = "no"; then
AC_MSG_RESULT($ap_ssltk_mc (default))
else
AC_MSG_RESULT($ap_ssltk_mc (specified))
fi
if test "$tmp_forced" = "yes" -a "x$ap_ssltk_mc" = "xno" -a "x$tmp_nomessage" != "x"; then
AC_MSG_ERROR(ssl memcache support failed: $tmp_nomessage)
fi
if test "$ap_ssltk_mc" = "yes"; then
save_cpp=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$APR_INCLUDEDIR -I$APU_INCLUDEDIR"
AC_CHECK_HEADER(
[apr_memcache.h],
[],
[tmp_nomessage="can't include apr_memcache headers"
ap_ssltk_mc="no"])
CPPFLAGS=$save_cpp
if test "$tmp_forced" = "yes" -a "x$ap_ssltk_mc" = "xno"; then
AC_MSG_ERROR(ssl memcache support failed: $tmp_nomessage)
fi
fi
if test "$ap_ssltk_mc" = "yes"; then
AC_DEFINE(HAVE_SSL_CACHE_MEMCACHE, 1, [Define if ssl-memcache support is enabled])
fi
])
dnl # start of module specific part
APACHE_MODPATH_INIT(ssl)
@@ -110,6 +160,7 @@ ssl_scache.lo dnl
ssl_scache_dbm.lo dnl
ssl_scache_shmcb.lo dnl
ssl_scache_dc.lo dnl
ssl_scache_memcache.lo dnl
ssl_util.lo dnl
ssl_util_ssl.lo dnl
"
@@ -118,6 +169,7 @@ APACHE_MODULE(ssl, [SSL/TLS support (mod_ssl)], $ssl_objs, , no, [
APACHE_CHECK_SSL_TOOLKIT
APR_SETVAR(MOD_SSL_LDADD, [\$(SSL_LIBS)])
CHECK_DISTCACHE
CHECK_SSL_MEMCACHE
if test "x$enable_ssl" = "xshared"; then
# The only symbol which needs to be exported is the module
# structure, so ask libtool to hide everything else:

View File

@@ -1032,6 +1032,19 @@ const char *ssl_cmd_SSLSessionCache(cmd_parms *cmd,
}
#else
return "SSLSessionCache: distcache support disabled";
#endif
}
else if ((arglen > 3) && strcEQn(arg, "memcache:", 9)) {
#ifdef HAVE_SSL_CACHE_MEMCACHE
mc->nSessionCacheMode = SSL_SCMODE_MC;
mc->szSessionCacheDataFile = apr_pstrdup(mc->pPool, arg+9);
if (!mc->szSessionCacheDataFile) {
return apr_pstrcat(cmd->pool,
"SSLSessionCache: Invalid memcache config: ",
arg+9, NULL);
}
#else
return "SSLSessionCache: distcache support disabled";
#endif
}
else {

View File

@@ -175,6 +175,7 @@ typedef enum {
#endif
#endif
/**
* Define the certificate algorithm types
*/
@@ -277,6 +278,7 @@ typedef enum {
SSL_SCMODE_DBM = 1,
SSL_SCMODE_SHMCB = 3,
SSL_SCMODE_DC = 4,
SSL_SCMODE_MC = 5,
SSL_SCMODE_NONE_NOT_NULL = 5
} ssl_scmode_t;
@@ -599,6 +601,15 @@ SSL_SESSION *ssl_scache_dc_retrieve(server_rec *, UCHAR *, int);
void ssl_scache_dc_remove(server_rec *, UCHAR *, int);
void ssl_scache_dc_status(request_rec *r, int flags, apr_pool_t *pool);
#ifdef HAVE_SSL_CACHE_MEMCACHE
void ssl_scache_mc_init(server_rec *, apr_pool_t *);
void ssl_scache_mc_kill(server_rec *);
BOOL ssl_scache_mc_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *);
SSL_SESSION *ssl_scache_mc_retrieve(server_rec *, UCHAR *, int);
void ssl_scache_mc_remove(server_rec *, UCHAR *, int);
void ssl_scache_mc_status(request_rec *r, int flags, apr_pool_t *pool);
#endif
/** Proxy Support */
int ssl_proxy_enable(conn_rec *c);
int ssl_engine_disable(conn_rec *c);

View File

@@ -58,6 +58,10 @@ void ssl_scache_init(server_rec *s, apr_pool_t *p)
#ifdef HAVE_DISTCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_DC)
ssl_scache_dc_init(s, p);
#endif
#ifdef HAVE_SSL_CACHE_MEMCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_MC)
ssl_scache_mc_init(s, p);
#endif
else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) {
void *data;
@@ -84,6 +88,10 @@ void ssl_scache_kill(server_rec *s)
#ifdef HAVE_DISTCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_DC)
ssl_scache_dc_kill(s);
#endif
#ifdef HAVE_SSL_CACHE_MEMCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_MC)
ssl_scache_mc_kill(s);
#endif
return;
}
@@ -100,6 +108,10 @@ BOOL ssl_scache_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SE
#ifdef HAVE_DISTCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_DC)
rv = ssl_scache_dc_store(s, id, idlen, expiry, sess);
#endif
#ifdef HAVE_SSL_CACHE_MEMCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_MC)
rv = ssl_scache_mc_store(s, id, idlen, expiry, sess);
#endif
return rv;
}
@@ -116,6 +128,10 @@ SSL_SESSION *ssl_scache_retrieve(server_rec *s, UCHAR *id, int idlen)
#ifdef HAVE_DISTCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_DC)
sess = ssl_scache_dc_retrieve(s, id, idlen);
#endif
#ifdef HAVE_SSL_CACHE_MEMCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_MC)
sess = ssl_scache_mc_retrieve(s, id, idlen);
#endif
return sess;
}
@@ -131,6 +147,10 @@ void ssl_scache_remove(server_rec *s, UCHAR *id, int idlen)
#ifdef HAVE_DISTCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_DC)
ssl_scache_dc_remove(s, id, idlen);
#endif
#ifdef HAVE_SSL_CACHE_MEMCACHE
else if (mc->nSessionCacheMode == SSL_SCMODE_MC)
ssl_scache_mc_remove(s, id, idlen);
#endif
return;
}
@@ -162,6 +182,10 @@ static int ssl_ext_status_hook(request_rec *r, int flags)
else if (sc->mc->nSessionCacheMode == SSL_SCMODE_DC)
ssl_scache_dc_status(r, flags, r->pool);
#endif
#ifdef HAVE_SSL_CACHE_MEMCACHE
else if (sc->mc->nSessionCacheMode == SSL_SCMODE_MC)
ssl_scache_mc_status(r, flags, r->pool);
#endif
ap_rputs("</td></tr>\n", r);
ap_rputs("</table>\n", r);

View File

@@ -0,0 +1,289 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* _ _
* _ __ ___ ___ __| | ___ ___| | mod_ssl
* | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
* | | | | | | (_) | (_| | \__ \__ \ |
* |_| |_| |_|\___/ \__,_|___|___/___/_|
* |_____|
* ssl_scache_memcache.c
* Distributed Session Cache on top of memcached
*/
#include "ssl_private.h"
#ifdef HAVE_SSL_CACHE_MEMCACHE
#include "apr_memcache.h"
#include "ap_mpm.h"
/*
* SSL Session Caching using memcached as a backend.
*/
/*
**
** High-Level "handlers" as per ssl_scache.c
**
*/
/* The underlying apr_memcache system is thread safe.. */
static apr_memcache_t *memctxt;
#define MC_TAG "mod_ssl:"
#define MC_TAG_LEN \
(sizeof(MC_TAG))
#define MC_KEY_LEN 254
void ssl_scache_mc_init(server_rec *s, apr_pool_t *p)
{
apr_status_t rv;
int thread_limit = 0;
int nservers = 0;
char *cache_config;
char *split;
char *tok;
SSLModConfigRec *mc = myModConfig(s);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
if (mc->szSessionCacheDataFile == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "SSLSessionCache required");
ssl_die();
}
/* Find all the servers in the first run to get a total count */
cache_config = apr_pstrdup(p, mc->szSessionCacheDataFile);
split = apr_strtok(cache_config, ",", &tok);
while (split) {
nservers++;
split = apr_strtok(NULL,",", &tok);
}
rv = apr_memcache_create(p, nservers, 0, &memctxt);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to create Memcache Object of '%d' size.",
nservers);
ssl_die();
}
/* Now add each server to the memcache */
cache_config = apr_pstrdup(p, mc->szSessionCacheDataFile);
split = apr_strtok(cache_config, ",", &tok);
while (split) {
apr_memcache_server_t *st;
char *host_str;
char *scope_id;
apr_port_t port;
rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Parse Server: '%s'", split);
ssl_die();
}
if (host_str == NULL) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Parse Server, "
"no hostname specified: '%s'", split);
ssl_die();
}
if (port == 0) {
port = 11211; /* default port */
}
/* Should Max Conns be (thread_limit / nservers) ? */
rv = apr_memcache_server_create(p,
host_str, port,
0,
1,
thread_limit,
600,
&st);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Create Server: %s:%d",
host_str, port);
ssl_die();
}
rv = apr_memcache_add_server(memctxt, st);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"SSLSessionCache: Failed to Add Server: %s:%d",
host_str, port);
ssl_die();
}
split = apr_strtok(NULL,",", &tok);
}
return;
}
void ssl_scache_mc_kill(server_rec *s)
{
}
static char *mc_session_id2sz(unsigned char *id, int idlen,
char *str, int strsize)
{
char *cp;
int n;
cp = apr_cpystrn(str, MC_TAG, MC_TAG_LEN);
for (n = 0; n < idlen && n < (MC_KEY_LEN - MC_TAG_LEN); n++) {
apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]);
cp += 2;
}
*cp = '\0';
return str;
}
BOOL ssl_scache_mc_store(server_rec *s, UCHAR *id, int idlen,
time_t timeout, SSL_SESSION *pSession)
{
char buf[MC_KEY_LEN];
char *strkey = NULL;
UCHAR ucaData[SSL_SESSION_MAX_DER];
UCHAR *ucp;
int nData;
apr_status_t rv;
/* streamline session data */
if ((nData = i2d_SSL_SESSION(pSession, NULL)) > sizeof(ucaData)) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"scache_mc: streamline session data size too large: %d > "
"%" APR_SIZE_T_FMT,
nData, sizeof(ucaData));
return FALSE;
}
ucp = ucaData;
i2d_SSL_SESSION(pSession, &ucp);
strkey = mc_session_id2sz(id, idlen, buf, sizeof(buf));
if(!strkey) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "scache_mc: Key generation borked.");
return FALSE;
}
timeout -= time(NULL);
timeout = apr_time_sec(timeout);
rv = apr_memcache_set(memctxt, strkey, (char*)ucp, nData, timeout, 0);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
"scache_mc: error setting key '%s' "
"with %d bytes of data", strkey, nData);
return FALSE;
}
return TRUE;
}
SSL_SESSION *ssl_scache_mc_retrieve(server_rec *s, UCHAR *id, int idlen)
{
SSL_SESSION *pSession;
MODSSL_D2I_SSL_SESSION_CONST unsigned char *pder;
apr_size_t der_len;
SSLModConfigRec *mc = myModConfig(s);
char buf[MC_KEY_LEN];
char* strkey = NULL;
apr_status_t rv;
strkey = mc_session_id2sz(id, idlen, buf, sizeof(buf));
if(!strkey) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"scache_mc: Key generation borked.");
return NULL;
}
rv = apr_memcache_getp(memctxt, mc->pPool, strkey,
(char**)&pder, &der_len, NULL);
if (rv == APR_NOTFOUND) {
/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"scache_mc: 'get_session' MISS"); */
return NULL;
}
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"scache_mc: 'get_session' FAIL");
return NULL;
}
if (der_len > SSL_SESSION_MAX_DER) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"scache_mc: 'get_session' OVERFLOW");
return NULL;
}
pSession = d2i_SSL_SESSION(NULL, &pder, der_len);
if (!pSession) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"scache_mc: 'get_session' CORRUPT");
return NULL;
}
/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"scache_mc: 'get_session' HIT"); */
return pSession;
}
void ssl_scache_mc_remove(server_rec *s, UCHAR *id, int idlen)
{
char buf[MC_KEY_LEN];
char* strkey = NULL;
apr_status_t rv;
strkey = mc_session_id2sz(id, idlen, buf, sizeof(buf));
if(!strkey) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "scache_mc: Key generation borked.");
return;
}
rv = apr_memcache_delete(memctxt, strkey, 0);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s,
"scache_mc: error deleting key '%s' ",
strkey);
return;
}
}
void ssl_scache_mc_status(request_rec *r, int flags, apr_pool_t *pool)
{
/* SSLModConfigRec *mc = myModConfig(r->server); */
/* TODO: Make a mod_status handler. meh. */
}
#endif