From 5353b12db4cb345f358ae8fdf1257acfccdbd842 Mon Sep 17 00:00:00 2001 From: Nokis Mavrogiannopoulos Date: Thu, 16 Oct 2008 18:58:36 +0000 Subject: --- NEWS | 6 ++ README | 232 +++++++++++++++++++++++++++++++++-------------------- configure.ac | 6 +- src/gnutls_hooks.c | 59 ++++++++++---- src/gnutls_io.c | 6 +- 5 files changed, 202 insertions(+), 107 deletions(-) diff --git a/NEWS b/NEWS index 0c50249..c77084d 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +** Version 0.5.3 (2008-10-16) + +- Corrected bug to allow having an OpenPGP-only web site. + +- Increased Max handshake tries due to interrupted system calls. + ** Version 0.5.2 (2008-06-29) - Depend on gnutls 2.4 which has openpgp support in main library. diff --git a/README b/README index 5198ed7..ad7f9c6 100644 --- a/README +++ b/README @@ -1,111 +1,171 @@ -mod_gnutls -This module started back in September of 2004 because I was tired of trying to -fix bugs in mod_ssl. mod_ssl is a giant beast of a module -- no offense to it's -authors is intended -- but I believe it has fallen prey to massive feature bloat. + mod_gnutls, Apache GnuTLS module. + ================================= -When I started hacking on httpd, mod_ssl remained a great mystery to me, and -when I actually looked at it, I ran away. The shear ammount code is huge, and it -does not conform to the style guidelines. It was painful to read, and even harder -to debug. I wanted to understand how it worked, and I had recently heard about -GnuTLS, so long story short, I decided to implement a mod_gnutls. +$LastChangedDate: $ -Lines of Code in mod_ssl: 15,324 -Lines of Code in mod_gnutls: 3,594 +Contents: -Because of writing mod_gnutls, I now understand how input and output filters work, -better than I ever thought possible. It was a little painful at times, and some parts -lift code and ideas directly from mod_ssl. Kudos to the original authors of mod_ssl. + I. ABOUT + II. AUTHORS + III. LICENSE + IV. STATUS + V. BASIC CONFIGURATION + VI. CREATE OPENPGP CREDENTIALS FOR THE SERVER ----------------------------- -Author: Paul Querna -Heavily modified by Nikos Mavrogiannopoulos +I. ABOUT -License: Apache Software License v2.0. (see the LICENSE file for details) + This module started back in September of 2004 because I was tired of + trying to fix bugs in mod_ssl. mod_ssl is a giant beast of a module -- + no offense to it's authors is intended -- but I believe it has fallen + prey to massive feature bloat. -Current Status: -- SSL and TLS connections with all popular browsers work! -- Sets enviromental vars for scripts (compatible with mod_ssl vars) -- Supports Memcached as a distributed SSL Session Cache -- Supports DBM as a local SSL Session Cache -- Support for Server Name Indication -- Support for Client Certificates -- Support for TLS-SRP + When I started hacking on httpd, mod_ssl remained a great mystery to me, + and when I actually looked at it, I ran away. The shear amount code is + huge, and it does not conform to the style guidelines. It was painful to + read, and even harder to debug. I wanted to understand how it worked, + and I had recently heard about GnuTLS, so long story short, I decided to + implement a mod_gnutls. -Basic Configuration: + Lines of Code in mod_ssl: 15,324 + Lines of Code in mod_gnutls: 3,594 -LoadModule gnutls_module modules/mod_gnutls.so + Because of writing mod_gnutls, I now understand how input and output + filters work, better than I ever thought possible. It was a little + painful at times, and some parts lift code and ideas directly from + mod_ssl. Kudos to the original authors of mod_ssl. -# mod_gnutls can optionaly use a memcached server to store it's SSL Sessions. -# This is useful in a cluster enviroment, where you want all of your servers -# to share a single SSL Session Cache. -#GnuTLSCache memcache "127.0.0.1 server2.example.com server3.example.com" -# The Default method is to use a DBM backed Cache. It isn't super fast, but -# it is portable and does not require another server to be running like memcached. -GnuTLSCache dbm conf/gnutls_cache - - # insert other directives ... here ... +II. AUTHORS - # This enables the mod_gnutls Handlers for this Virtual Host - GnuTLSEnable On + Paul Querna + Nikos Mavrogiannopoulos - # This is the Private key for your server. - GnuTLSX509KeyFile conf/server.key - # This is the Server Certificate. - GnuTLSX509CertificateFile conf/server.cert - -# a more advanced configuration -GnuTLSCache dbm "/var/cache/www-tls-cache/cache" -GnuTLSCacheTimeout 600 -NameVirtualHost 1.2.3.4:443 +III. LICENSE - - Servername server.com:443 + Apache License, Version 2.0 (see the LICENSE file for details) + + + +IV. STATUS + + * SSL and TLS connections with all popular browsers work! + * Sets environmental vars for scripts (compatible with mod_ssl vars) + * Supports memcached as a distributed SSL session cache + * Supports DBM as a local SSL session cache + * Support for server name indication (SNI), RFC3546 + * Support for client certificates + * Support for secure remote password (SRP), RFC5054 + + + +V. BASIC CONFIGURATION + + LoadModule gnutls_module modules/mod_gnutls.so + + # mod_gnutls can optionally use a memcached server to store it's SSL + # Sessions. This is useful in a cluster environment, where you want all + # of your servers to share a single SSL session cache. + #GnuTLSCache memcache "127.0.0.1 server2.example.com server3.example.com" + + # The Default method is to use a DBM backed Cache. It isn't super fast, + # but it is portable and does not require another server to be running + # like memcached. + GnuTLSCache dbm conf/gnutls_cache + + + + # Enable mod_gnutls handlers for this virtual host + GnuTLSEnable On + + # This is the private key for your server + GnuTLSX509KeyFile conf/server.key + + # This is the server certificate + GnuTLSX509CertificateFile conf/server.cert + + + + # A more advanced configuration + GnuTLSCache dbm "/var/cache/www-tls-cache/cache" + GnuTLSCacheTimeout 600 + NameVirtualHost 1.2.3.4:443 + + + + Servername server.com:443 GnuTLSEnable on - GnuTLSPriority NORMAL -# To export exactly the same environment variables as mod_ssl to CGI scripts. - GNUTLSExportCertificates on - - GnuTLSX509CertificateFile /etc/apache2/server-cert.pem - GnuTLSX509KeyFile /etc/apache2/server-key.pem - -# To enable SRP you must have these files installed. Check the gnutls srptool. - GnuTLSSRPPasswdFile /etc/apache2/tpasswd - GnuTLSSRPPasswdConfFile /etc/apache2/tpasswd.conf - -# In order to verify client certificates. Other options to -# GnuTLSClientVerify could be ignore or require. The GnuTLSClientCAFile -# contains the CAs to verify client certificates. - GnuTLSClientVerify request - GnuTLSX509CAFile ca.pem - ... - - -# A setup for OpenPGP and X.509 authentication - - Servername crystal.lan:443 + GnuTLSPriority NORMAL + + # Export exactly the same environment variables as mod_ssl to CGI + # scripts. + GNUTLSExportCertificates on + + GnuTLSX509CertificateFile /etc/apache2/server-cert.pem + GnuTLSX509KeyFile /etc/apache2/server-key.pem + + # To enable SRP you must have these files installed. Check the gnutls + # srptool. + GnuTLSSRPPasswdFile /etc/apache2/tpasswd + GnuTLSSRPPasswdConfFile /etc/apache2/tpasswd.conf + + # In order to verify client certificates. Other options to + # GnuTLSClientVerify could be ignore or require. The + # GnuTLSClientCAFile contains the CAs to verify client certificates. + GnuTLSClientVerify request + GnuTLSX509CAFile ca.pem + + + + # A setup for OpenPGP and X.509 authentication + + + Servername crystal.lan:443 GnuTLSEnable on - GnuTLSPriorities NORMAL:+COMP-NULL + GnuTLSPriorities NORMAL:+COMP-NULL + + # Setup the openpgp keys + GnuTLSPGPCertificateFile /etc/apache2/test.pub.asc + GnuTLSPGPKeyFile /etc/apache2/test.sec.asc + + # - and the X.509 keys + GnuTLSCertificateFile /etc/apache2/server-cert.pem + GnuTLSKeyFile /etc/apache2/server-key.pem + + GnuTLSClientVerify ignore + + # To avoid using the default DH params + GnuTLSDHFile /etc/apache2/dh.pem + + # These are only needed if GnuTLSClientVerify != ignore + GnuTLSClientCAFile ca.pem + GnuTLSPGPKeyringFile /etc/apache2/ring.asc + + + + + +VI. CREATE OPENPGP CREDENTIALS FOR THE SERVER + + mod_gnutls currently cannot read encrypted OpenPGP credentials. That is, + when you generate a key with gpg and gpg prompts you for a passphrase, + just press enter. Then press enter again, to confirm an empty + passphrase. http://news.gmane.org/gmane.comp.apache.outoforder.modules -# setup the openpgp keys - GnuTLSPGPCertificateFile /etc/apache2/test.pub.asc - GnuTLSPGPKeyFile /etc/apache2/test.sec.asc + These instructions are from the GnuTLS manual: + http://www.gnu.org/software/gnutls/manual/html_node/Invoking-gnutls_002dserv.html#Invoking-gnutls_002dserv -# and the X.509 keys - GnuTLSCertificateFile /etc/apache2/server-cert.pem - GnuTLSKeyFile /etc/apache2/server-key.pem - GnuTLSClientVerify ignore + $ gpg --gen-key + ...enter whatever details you want, use 'test.gnutls.org' as name... -# To avoid using the default DH params - GnuTLSDHFile /etc/apache2/dh.pem + Make a note of the OpenPGP key identifier of the newly generated key, + here it was 5D1D14D8. You will need to export the key for GnuTLS to be + able to use it. -# these are only needed if GnuTLSClientVerify != ignore - GnuTLSClientCAFile ca.pem - GnuTLSPGPKeyringFile /etc/apache2/ring.asc - + $ gpg -a --export 5D1D14D8 > openpgp-server.txt + $ gpg -a --export-secret-keys 5D1D14D8 > openpgp-server-key.txt diff --git a/configure.ac b/configure.ac index 1e42464..0ffc69b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ dnl -AC_INIT(mod_gnutls, 0.5.2) +AC_INIT(mod_gnutls, 0.5.3) OOO_CONFIG_NICE(config.nice) MOD_GNUTLS_VERSION=AC_PACKAGE_VERSION AC_PREREQ(2.53) @@ -55,8 +55,8 @@ have_apr_memcache=0 CHECK_APR_MEMCACHE([have_apr_memcache=1], [have_apr_memcache=0]) AC_SUBST(have_apr_memcache) -MODULE_CFLAGS="${LIBGNUTLS_EXTRA_CFLAGS} ${SRP_CFLAGS} ${APR_MEMCACHE_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" -MODULE_LIBS="${APR_MEMCACHE_LIBS} ${LIBGNUTLS_EXTRA_LIBS}" +MODULE_CFLAGS="${LIBGNUTLS_CFLAGS} ${SRP_CFLAGS} ${APR_MEMCACHE_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" +MODULE_LIBS="${APR_MEMCACHE_LIBS} ${LIBGNUTLS_LIBS}" AC_SUBST(MODULE_CFLAGS) AC_SUBST(MODULE_LIBS) diff --git a/src/gnutls_hooks.c b/src/gnutls_hooks.c index 5ced25b..57bfeba 100644 --- a/src/gnutls_hooks.c +++ b/src/gnutls_hooks.c @@ -54,6 +54,9 @@ static void gnutls_debug_log_all(int level, const char *str) { apr_file_printf(debug_log_fp, "<%d> %s\n", level, str); } +#define _gnutls_log apr_file_printf +#else +# define _gnutls_log(...) #endif int @@ -62,6 +65,18 @@ mgs_hook_pre_config(apr_pool_t * pconf, { int ret; +#if MOD_GNUTLS_DEBUG + apr_file_open(&debug_log_fp, "/tmp/gnutls_debug", + APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, + pconf); + + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); + + gnutls_global_set_log_level(9); + gnutls_global_set_log_function(gnutls_debug_log_all); + _gnutls_log(debug_log_fp, "gnutls: %s\n", gnutls_check_version(NULL)); +#endif + #if APR_HAS_THREADS ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_is_threaded); if (mpm_is_threaded) { @@ -72,29 +87,20 @@ int ret; #endif if (gnutls_check_version(LIBGNUTLS_VERSION)==NULL) { - fprintf(stderr, "gnutls_check_version() failed. Required: gnutls-%s Found: gnutls-%s\n", + _gnutls_log(debug_log_fp, "gnutls_check_version() failed. Required: gnutls-%s Found: gnutls-%s\n", LIBGNUTLS_VERSION, gnutls_check_version(NULL)); return -3; } ret = gnutls_global_init(); if (ret < 0) { - fprintf(stderr, "gnutls_global_init: %s\n", gnutls_strerror(ret)); + _gnutls_log(debug_log_fp, "gnutls_global_init: %s\n", gnutls_strerror(ret)); return -3; } apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, apr_pool_cleanup_null); -#if MOD_GNUTLS_DEBUG - apr_file_open(&debug_log_fp, "/tmp/gnutls_debug", - APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, - pconf); - - gnutls_global_set_log_level(9); - gnutls_global_set_log_function(gnutls_debug_log_all); - apr_file_printf(debug_log_fp, "gnutls: %s\n", gnutls_check_version(NULL)); -#endif return OK; } @@ -106,6 +112,8 @@ static int mgs_select_virtual_server_cb(gnutls_session_t session) int ret; int cprio[2]; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); + ctxt = gnutls_transport_get_ptr(session); /* find the virtual server */ @@ -162,8 +170,12 @@ static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret) { mgs_handle_t *ctxt; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); ctxt = gnutls_transport_get_ptr(session); + if (ctxt == NULL) + return GNUTLS_E_INTERNAL_ERROR; + if (gnutls_certificate_type_get( session) == GNUTLS_CRT_X509) { ret->type = GNUTLS_CRT_X509; ret->ncerts = ctxt->sc->certs_x509_num; @@ -210,6 +222,7 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p, size_t data_len; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); *cert_cn = NULL; data_len = 0; @@ -261,6 +274,7 @@ static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p, size_t data_len; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); *cert_cn = NULL; data_len = 0; @@ -293,6 +307,7 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, int first_run = 0; const char *userdata_key = "mgs_init"; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); apr_pool_userdata_get(&data, userdata_key, base_server->process->pool); if (data == NULL) { first_run = 1; @@ -394,8 +409,9 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, } #endif - if (sc->certs_x509[0] == NULL - && sc->enabled == GNUTLS_ENABLED_TRUE) { + if (sc->certs_x509[0] == NULL && + sc->cert_pgp == NULL && + sc->enabled == GNUTLS_ENABLED_TRUE) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, "[GnuTLS] - Host '%s:%d' is missing a " "Certificate File!", s->server_hostname, @@ -403,8 +419,9 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, exit(-1); } - if (sc->privkey_x509 == NULL - && sc->enabled == GNUTLS_ENABLED_TRUE) { + if (sc->enabled == GNUTLS_ENABLED_TRUE && + ((sc->certs_x509[0] != NULL && sc->privkey_x509 == NULL) || + (sc->cert_pgp != NULL && sc->privkey_pgp == NULL))) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, "[GnuTLS] - Host '%s:%d' is missing a " "Private Key File!", @@ -439,6 +456,7 @@ void mgs_hook_child_init(apr_pool_t * p, server_rec * s) mgs_srvconf_rec *sc = ap_get_module_config(s->module_config, &gnutls_module); + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); if (sc->cache_type != mgs_cache_none) { rv = mgs_cache_child_init(p, s, sc); if (rv != APR_SUCCESS) { @@ -457,6 +475,7 @@ const char *mgs_hook_http_scheme(const request_rec * r) (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, &gnutls_module); + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); if (sc->enabled == GNUTLS_ENABLED_FALSE) { return NULL; } @@ -470,6 +489,7 @@ apr_port_t mgs_hook_default_port(const request_rec * r) (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, &gnutls_module); + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); if (sc->enabled == GNUTLS_ENABLED_FALSE) { return 0; } @@ -491,6 +511,7 @@ static int vhost_cb(void *baton, conn_rec * conn, server_rec * s) mgs_srvconf_rec *tsc; vhost_cb_rec *x = baton; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module); @@ -543,6 +564,7 @@ mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session) mgs_srvconf_rec *tsc; #endif + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); ctxt = gnutls_transport_get_ptr(session); rv = gnutls_server_name_get(ctxt->session, sni_name, @@ -620,6 +642,7 @@ static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c) module_config, &gnutls_module); + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); ctxt = apr_pcalloc(pool, sizeof(*ctxt)); ctxt->c = c; ctxt->sc = sc; @@ -658,6 +681,7 @@ int mgs_hook_pre_connection(conn_rec * c, void *csd) module_config, &gnutls_module); + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) { return DECLINED; } @@ -687,6 +711,7 @@ int mgs_hook_fixups(request_rec * r) mgs_handle_t *ctxt; int rv = OK; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); apr_table_t *env = r->subprocess_env; ctxt = @@ -761,6 +786,7 @@ int mgs_hook_authz(request_rec * r) mgs_dirconf_rec *dc = ap_get_module_config(r->per_dir_config, &gnutls_module); + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module); @@ -822,6 +848,7 @@ mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, apr_table_t *env = r->subprocess_env; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); if (export_certificates_enabled != 0) { char cert_buf[10 * 1024]; len = sizeof(cert_buf); @@ -928,6 +955,7 @@ mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side size_t len; int ret; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); apr_table_t *env = r->subprocess_env; if (export_certificates_enabled != 0) { @@ -994,6 +1022,7 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) } cert; apr_time_t activation_time, expiration_time, cur_time; + _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__); cert_list = gnutls_certificate_get_peers(ctxt->session, &cert_list_size); diff --git a/src/gnutls_io.c b/src/gnutls_io.c index 753c87b..4f8e486 100644 --- a/src/gnutls_io.c +++ b/src/gnutls_io.c @@ -72,13 +72,13 @@ static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, if (buffer->length > inl) { /* we have have enough to fill the caller's buffer */ - memcpy(in, buffer->value, inl); + memmove(in, buffer->value, inl); buffer->value += inl; buffer->length -= inl; } else { /* swallow remainder of the buffer */ - memcpy(in, buffer->value, buffer->length); + memmove(in, buffer->value, buffer->length); inl = buffer->length; buffer->value = NULL; buffer->length = 0; @@ -353,7 +353,7 @@ static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt, return APR_SUCCESS; } -#define HANDSHAKE_MAX_TRIES 100 +#define HANDSHAKE_MAX_TRIES 1024 static int gnutls_do_handshake(mgs_handle_t * ctxt) { int ret; -- cgit