From a66e1478c77fc8c8a7ab831916ab3d1c3aacb539 Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Fri, 10 Dec 2004 06:08:52 +0000 Subject: working support for a ssl session cache via memcached. --- src/Makefile.am | 2 +- src/gnutls_cache.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++------ src/gnutls_io.c | 30 ++++---- src/mod_gnutls.c | 46 +++++++++++- 4 files changed, 244 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 05d2fab..1f1860b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ CLEANFILES = .libs/libmod_gnutls *~ libmod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c libmod_gnutls_la_CFLAGS = -Wall ${MODULE_CFLAGS} -libmod_gnutls_la_LDFLAGS = ${MODULE_LIBS} +libmod_gnutls_la_LDFLAGS = -rpath ${AP_LIBEXECDIR} -module -avoid-version ${MODULE_LIBS} lib_LTLIBRARIES = libmod_gnutls.la diff --git a/src/gnutls_cache.c b/src/gnutls_cache.c index 683cdf4..bc13440 100644 --- a/src/gnutls_cache.c +++ b/src/gnutls_cache.c @@ -16,46 +16,212 @@ */ #include "mod_gnutls.h" +#include "ap_mpm.h" /** * GnuTLS Session Cache using libmemcached * */ -/* -#include "memcache.h" -int mod_gnutls_cache_init() +/* The underlying apr_memcache system is thread safe... woohoo */ +static apr_memcache_t* mc; + +int mod_gnutls_cache_child_init(apr_pool_t *p, server_rec *s, + mod_gnutls_srvconf_rec *sc) { - return 0; + apr_status_t rv = APR_SUCCESS; + int thread_limit = 0; + int nservers = 0; + char* cache_config; + char* split; + char* tok; + + ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); + + /* Find all the servers in the first run to get a total count */ + cache_config = apr_pstrdup(p, sc->cache_config); + split = apr_strtok(cache_config, " ", &tok); + while (split) { + nservers++; + split = apr_strtok(NULL," ", &tok); + } + + rv = apr_memcache_create(p, nservers, 0, &mc); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "[gnutls_cache] Failed to create Memcache Object of '%d' size.", + nservers); + return rv; + } + + /* Now add each server to the memcache */ + cache_config = apr_pstrdup(p, sc->cache_config); + split = apr_strtok(cache_config, " ", &tok); + while (split) { + apr_memcache_server_t* st; + char* split2; + char* host_str; + char* port_str; + int port; + + host_str = apr_strtok(split,":", &split2); + port_str = apr_strtok(NULL,":", &split2); + if (!port_str) { + port = 11211; /* default port */ + } + else { + port = atoi(port_str); + } + + /* 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, + "[gnutls_cache] Failed to Create Server: %s:%d", + host_str, port); + return rv; + } + + rv = apr_memcache_add_server(mc, st); + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, + "[gnutls_cache] Failed to Add Server: %s:%d", + host_str, port); + return rv; + } + + split = apr_strtok(NULL," ", &tok); + } + return rv; } -static int cache_store((void* baton, gnutls_datum_t key, gnutls_datum_t data) + +/* thanks mod_ssl */ +#define GNUTLS_SESSION_ID_STRING_LEN \ + ((GNUTLS_MAX_SESSION_ID + 1) * 2) +#define MC_TAG "mod_gnutls:" +#define MC_TAG_LEN \ + (sizeof(MC_TAG)) +#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN) + + +static char *gnutls_session_id2sz(unsigned char *id, int idlen, + char *str, int strsize) { - mc_set(struct memcache *mc, - key->data, key->size, - data->data, data->size, - 3600, 0); - return 0; + char *cp; + int n; + + cp = apr_cpystrn(str, MC_TAG, MC_TAG_LEN); + for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) { + apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]); + cp += 2; + } + *cp = '\0'; + return str; } -static int cache_fetch(void* baton, gnutls_datum_t key) + +static int cache_store(void* baton, gnutls_datum_t key, gnutls_datum_t data) { + apr_status_t rv = APR_SUCCESS; mod_gnutls_handle_t *ctxt = baton; - return 0; + char buf[STR_SESSION_LEN]; + char* strkey = NULL; + apr_uint32_t timeout; + + strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + if(!strkey) + return -1; + + timeout = 3600; + + rv = apr_memcache_set(mc, strkey, data.data, data.size, timeout, 0); + + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rv, + ctxt->c->base_server, + "[gnutls_cache] error setting key '%s' " + "with %d bytes of data", strkey, data.size); + return -1; + } + + return 0; +} + +static gnutls_datum_t cache_fetch(void* baton, gnutls_datum_t key) +{ + apr_status_t rv = APR_SUCCESS; + mod_gnutls_handle_t *ctxt = baton; + char buf[STR_SESSION_LEN]; + char* strkey = NULL; + char* value; + apr_size_t value_len; + gnutls_datum_t data = { NULL, 0 }; + + strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + if(!strkey) { + return data; + } + + rv = apr_memcache_getp(mc, ctxt->c->pool, strkey, + &value, &value_len, NULL); + + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, + ctxt->c->base_server, + "[gnutls_cache] error fetching key '%s' ", + strkey); + + data.size = 0; + data.data = NULL; + return data; + } + + /* TODO: Eliminate this memcpy. ffs. gnutls-- */ + data.data = gnutls_malloc(value_len); + if (data.data == NULL) + return data; + + data.size = value_len; + memcpy(data.data, value, value_len); + + return data; } static int cache_delete(void* baton, gnutls_datum_t key) { + apr_status_t rv = APR_SUCCESS; mod_gnutls_handle_t *ctxt = baton; - return 0; + char buf[STR_SESSION_LEN]; + char* strkey = NULL; + + strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + if(!strkey) + return -1; + + rv = apr_memcache_delete(mc, strkey, 0); + + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, + ctxt->c->base_server, + "[gnutls_cache] error deleting key '%s' ", + strkey); + return -1; + } + + return 0; } int mod_gnutls_cache_session_init(mod_gnutls_handle_t *ctxt) { - gnutls_db_set_cache_expiration - gnutls_db_set_retrieve_function(session, cache_fetch); - gnutls_db_set_remove_function(session, cache_delete); - gnutls_db_set_store_function(session, cache_store); - gnutls_db_set_ptr(session, NULL); - return 0; + gnutls_db_set_retrieve_function(ctxt->session, cache_fetch); + gnutls_db_set_remove_function(ctxt->session, cache_delete); + gnutls_db_set_store_function(ctxt->session, cache_store); + gnutls_db_set_ptr(ctxt->session, ctxt); + return 0; } -*/ diff --git a/src/gnutls_io.c b/src/gnutls_io.c index e1c84be..e92646b 100644 --- a/src/gnutls_io.c +++ b/src/gnutls_io.c @@ -273,10 +273,16 @@ static apr_status_t gnutls_io_input_read(mod_gnutls_handle_t * ctxt, } else { /* Some Other Error. Report it. Die. */ - ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, - ctxt->c->base_server, - "GnuTLS: Error reading data. (%d) '%s'", rc, - gnutls_strerror(rc)); + if(gnutls_error_is_fatal(rc)) { + ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc, + ctxt->c->base_server, + "GnuTLS: Error reading data. (%d) '%s'", rc, + gnutls_strerror(rc)); + } + else if(*len > 0) { + ctxt->input_rc = APR_SUCCESS; + break; + } } if (ctxt->input_rc == APR_SUCCESS) { @@ -449,9 +455,10 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *bucket = APR_BRIGADE_FIRST(bb); - if (APR_BUCKET_IS_EOS(bucket)) { + if (APR_BUCKET_IS_EOS(bucket) || AP_BUCKET_IS_EOC(bucket)) { - /* gnutls_bye(ctxt->session, GNUTLS_SHUT_RDWR); */ + gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); + gnutls_deinit(ctxt->session); if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { return status; @@ -464,17 +471,6 @@ apr_status_t mod_gnutls_filter_output(ap_filter_t * f, return status; } break; - - } - else if (AP_BUCKET_IS_EOC(bucket)) { - - gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); - gnutls_deinit(ctxt->session); - if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) { - return status; - } - break; - } else { /* filter output */ diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c index 3dfbd9a..833edc2 100644 --- a/src/mod_gnutls.c +++ b/src/mod_gnutls.c @@ -70,7 +70,7 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, } - if(first_run) { +// if(first_run) { /* TODO: Should we regenerate these after X requests / X time ? */ gnutls_dh_params_init(&dh_params); gnutls_dh_params_generate2(dh_params, DH_BITS); @@ -78,7 +78,7 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, gnutls_rsa_params_init(&rsa_params); gnutls_rsa_params_generate2(rsa_params, RSA_BITS); #endif - } +// } for (s = base_server; s; s = s->next) { sc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config, @@ -105,6 +105,25 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, return OK; } +static void mod_gnutls_hook_child_init(apr_pool_t *p, server_rec *s) +{ + apr_status_t rv = APR_SUCCESS; + mod_gnutls_srvconf_rec *sc = ap_get_module_config(s->module_config, + &gnutls_module); + + if(sc->cache_config != NULL) { + rv = mod_gnutls_cache_child_init(p, s, sc); + if(rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, + "[GnuTLS] - Failed to run Cache Init"); + } + } + else { + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, + "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache"); + } +} + static const char *mod_gnutls_hook_http_method(const request_rec * r) { mod_gnutls_srvconf_rec *sc = @@ -172,6 +191,7 @@ static mod_gnutls_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c) gnutls_dh_set_prime_bits(ctxt->session, DH_BITS); + mod_gnutls_cache_session_init(ctxt); return ctxt; } @@ -250,6 +270,21 @@ static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy, return NULL; } +static const char *gnutls_set_cache(cmd_parms * parms, void *dummy, + const char *arg) +{ + const char* err; + mod_gnutls_srvconf_rec *sc = ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY))) { + return err; + } + + sc->cache_config = apr_pstrdup(parms->pool, arg); + return NULL; +} + static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy, const char *arg) { @@ -279,6 +314,10 @@ static const command_rec gnutls_cmds[] = { NULL, RSRC_CONF, "SSL Server Certificate file"), + AP_INIT_TAKE1("GnuTLSCache", gnutls_set_cache, + NULL, + RSRC_CONF, + "SSL Server Certificate file"), AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled, NULL, RSRC_CONF, "Whether this server has GnuTLS Enabled. Default: Off"), @@ -299,6 +338,8 @@ static void gnutls_hooks(apr_pool_t * p) APR_HOOK_MIDDLE); ap_hook_post_config(mod_gnutls_hook_post_config, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(mod_gnutls_hook_child_init, NULL, NULL, + APR_HOOK_MIDDLE); ap_hook_http_method(mod_gnutls_hook_http_method, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_default_port(mod_gnutls_hook_default_port, NULL, NULL, @@ -331,6 +372,7 @@ static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s) gnutls_anon_allocate_server_credentials(&sc->anoncred); sc->key_file = NULL; sc->cert_file = NULL; + sc->cache_config = NULL; i = 0; sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC; -- cgit