From 586e3bef5d34d090d9480e146783c33313f9dc34 Mon Sep 17 00:00:00 2001 From: Nokis Mavrogiannopoulos Date: Wed, 20 Feb 2008 18:50:41 +0000 Subject: added tags --- Makefile.am | 7 +- NEWS | 30 ++ NOTICE | 3 + README | 67 ++- README.ENV | 49 ++ autogen.sh | 30 +- configure.ac | 28 +- include/mod_gnutls.h.in | 69 ++- src/Makefile.am | 3 +- src/gnutls_cache.c | 97 ++-- src/gnutls_config.c | 603 ++++++++++++++++------- src/gnutls_hooks.c | 1248 +++++++++++++++++++++++++++++++---------------- src/gnutls_io.c | 7 +- src/mod_gnutls.c | 56 ++- 14 files changed, 1623 insertions(+), 674 deletions(-) create mode 100644 NEWS create mode 100644 README.ENV diff --git a/Makefile.am b/Makefile.am index 77f3b84..a19e755 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,11 @@ AUTOMAKE_OPTIONS = foreign dist-bzip2 EXTRA_DIST = m4/outoforder.m4 m4/apache.m4 \ - m4/libgnutls.m4 m4/apr_memcache.m4 \ + libgnutls.m4 m4/apr_memcache.m4 \ m4/apache_test.m4 m4/lua.m4 \ include/mod_gnutls.h.in \ - README \ + README README.ENV NEWS \ NOTICE LICENSE autogen.sh -SUBDIRS = src data +SUBDIRS = src +ACLOCAL_AMFLAGS = -I m4 diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..a9fe9fd --- /dev/null +++ b/NEWS @@ -0,0 +1,30 @@ +** Version 0.5.0-alpha (2008-01-24) + +- Added support for OpenPGP keys. The new directives are: + GnuTLSPGPKeyringFile, GnuTLSPGPCertificateFile, GnuTLSPGPKeyFile + +** Version 0.4.2 (2007-12-10) + +- Added support for sending a certificate chain. + +- Corrected bug which did not allow the TLS session cache to be used. + +- Do not allow resuming sessions on different servers. + +** Version 0.4.1 (2007-12-03) + +- Added support for subject alternative names in certificates. +Only one per certificate is supported. + +- New enviroment variables: SSL_CLIENT_M_VERSION, SSL_CLIENT_S_SAN%, +SSL_CLIENT_S_TYPE, SSL_SERVER_M_VERSION, SSL_SERVER_S_SAN%, SSL_SERVER_S_TYPE + +- The compatibility mode can now be enabled explicitely with the +%COMPAT keyword at the GnuTLSPriorities string. It is no longer the default. + +- Check for GnuTLSPriorities directive. This corrects a segfault. Thanks +to David Hrbáč. + +- Better handling of GnuTLSDHFile and GnuTLSRSAFile. + +- No longer default paths for RSA and DH parameter files. diff --git a/NOTICE b/NOTICE index 7b09606..25efd6f 100644 --- a/NOTICE +++ b/NOTICE @@ -1,3 +1,6 @@ +This product includes software developed by +Nikos Mavrogiannopoulos (http://www.gnutls.org/). + This product includes software developed by Paul Querna (http://www.outoforder.cc/). diff --git a/README b/README index 557ba77..5198ed7 100644 --- a/README +++ b/README @@ -11,7 +11,7 @@ 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. Lines of Code in mod_ssl: 15,324 -Lines of Code in mod_gnutls: 1,886 +Lines of Code in mod_gnutls: 3,594 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 @@ -19,19 +19,20 @@ lift code and ideas directly from mod_ssl. Kudos to the original authors of mod_ ---------------------------- -Author: Paul Querna + +Heavily modified by Nikos Mavrogiannopoulos License: Apache Software License v2.0. (see the LICENSE file for details) Current Status: - SSL and TLS connections with all popular browsers work! -- Sets some enviromental vars for scripts +- 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 - -Future Development: -- Support for Server Name Indication (partial support is in, but disabled) +- Support for Server Name Indication - Support for Client Certificates +- Support for TLS-SRP Basic Configuration: @@ -53,8 +54,58 @@ GnuTLSCache dbm conf/gnutls_cache GnuTLSEnable On # This is the Private key for your server. - GnuTLSKeyFile conf/server.key + GnuTLSX509KeyFile conf/server.key # This is the Server Certificate. - GnuTLSCertificateFile conf/server.cert + 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 + GnuTLSEnable on + 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 diff --git a/README.ENV b/README.ENV new file mode 100644 index 0000000..34dbcf6 --- /dev/null +++ b/README.ENV @@ -0,0 +1,49 @@ +All the environment variables set by this module are: + +HTTPS: can be "on" or "off" +SSL_VERSION_LIBRARY: The version of the gnutls library +SSL_VERSION_INTERFACE: The version of this module +SSL_PROTOCOL: The SSL or TLS protocol name (such as "TLS 1.0" etc.) +SSL_CIPHER: The SSL or TLS cipher suite name. +SSL_COMPRESS_METHOD: The negotiated compression method (NULL or DEFLATE) +SSL_SRP_USER: The SRP username used for authentication. +SSL_CIPHER_USEKEYSIZE and SSL_CIPHER_ALGKEYSIZE: The number if bits used in the used cipher + algorithm. This does not fully reflect the security level since the size of + RSA or DHE key exchange parameters affect the security level too. +SSL_CIPHER_EXPORT: true or false. Whether the cipher suite negotiated is an export one. +SSL_SESSION_ID: The session ID negotiated in this session. Can be the same during + client reloads. + +SSL_CLIENT_V_REMAIN: The number of days until the client's certificate is expired. +SSL_CLIENT_V_START: The activation time of client's certificate. +SSL_CLIENT_V_END: The expiration time of client's certificate. +SSL_CLIENT_S_DN: The distinguished name of client's certificate in RFC2253 format. +SSL_CLIENT_I_DN: The distinguished name of client's issuer certificate in RFC2253 format. +SSL_CLIENT_S_AN%: These will contain the alternative names of the client certificate + (% is a number starting from zero). The values will be prepended by "DNSNAME:", + "RFC822NAME:" or "URI:" depending on the type. If it is not supported the value + "UNSUPPORTED" will be set. +SSL_CLIENT_M_SERIAL: The serial number of the client's certificate. +SSL_CLIENT_M_VERSION: The version of the client's certificate. +SSL_CLIENT_A_SIG: The algorithm used for the signature in client's certificate. +SSL_CLIENT_A_KEY: The public key algorithm in client's certificate. +SSL_CLIENT_CERT: The PEM-encoded client certificate +SSL_CLIENT_VERIFY: + whether the client's certificate was verified. (NONE if none was sent, or SUCCESS or FAILED) +SSL_CLIENT_CERT_TYPE: The certificate type can be X.509 or OPENPGP. + +SSL_SERVER_V_START: The activation time of server's certificate. +SSL_SERVER_V_END: The expiration time of server's certificate. +SSL_SERVER_S_DN: The distinguished name of the server's certificate in RFC2253 format. +SSL_SERVER_I_DN: The distinguished name of the server's issuer certificate in RFC2253 format. +SSL_SERVER_S_AN%: These will contain the alternative names of the server certificate + (% is a number starting from zero). The values will be prepended by "DNSNAME:", + "RFC822NAME:" or "URI:" depending on the type. If it is not supported the value + "UNSUPPORTED" will be set. +SSL_SERVER_M_SERIAL: The serial number of the server's certificate. +SSL_SERVER_M_VERSION: The version of the server's certificate. +SSL_SERVER_A_SIG: The algorithm used for the signature in server's certificate. +SSL_SERVER_A_KEY: The public key algorithm in server's certificate. +SSL_SERVER_CERT: The PEM-encoded server certificate +SSL_SERVER_CERT_TYPE: The certificate type can be X.509 or OPENPGP. + diff --git a/autogen.sh b/autogen.sh index dc16b29..742b2cc 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,9 +1,25 @@ #!/bin/sh -m4/buildconf.py \ - --libtoolize \ - --aclocal \ - --automake \ - --autoconf \ - --autoheader -rm -rf autom4te.cache +if [ -z $ACLOCAL ]; then + ACLOCAL=aclocal +fi +if [ -z $AUTOCONF ]; then + AUTOCONF=autoconf +fi +if [ -z $AUTOHEADER ]; then + AUTOHEADER=autoheader +fi +if [ -z $AUTORECONF ]; then + AUTORECONF=autoreconf +fi + +#rm -rf autom4te.cache +$AUTORECONF -f -i +#touch stamp-h.in + +for x in providers/*; do + if [ -e $x/autogen.sh ]; then + echo Generating Config files in $x + (cd $x; ./autogen.sh $*) + fi +done diff --git a/configure.ac b/configure.ac index fb84c67..0cdcdd9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,11 @@ -AC_INIT(mod_gnutls, 0.2.0) +dnl +AC_INIT(mod_gnutls, 0.5.0-alpha) OOO_CONFIG_NICE(config.nice) MOD_GNUTLS_VERSION=AC_PACKAGE_VERSION AC_PREREQ(2.53) AC_CONFIG_SRCDIR([src/mod_gnutls.c]) AC_CONFIG_AUX_DIR(config) + OOO_MAINTAIN_MODE AM_MAINTAINER_MODE AC_CANONICAL_TARGET @@ -15,7 +17,7 @@ AC_SUBST(MOD_GNUTLS_VERSION) AC_PROG_CC AC_PROG_LD AC_PROG_INSTALL - +AC_PROG_LIBTOOL AP_VERSION=2.0.40 CHECK_APACHE(,$AP_VERSION, @@ -23,25 +25,31 @@ CHECK_APACHE(,$AP_VERSION, AC_MSG_ERROR([*** Apache version $AP_VERSION not found!]) ) -LIBTOOL="`${APR_CONFIG} --apr-libtool`" -AC_SUBST(LIBTOOL) +dnl LIBTOOL="`${APR_CONFIG} --apr-libtool`" +dnl AC_SUBST(LIBTOOL) -MIN_TLS_VERSION=1.2.0 -CHECK_LIBGNUTLS($MIN_TLS_VERSION) +MIN_TLS_VERSION=2.2.1 +AM_PATH_LIBGNUTLS_EXTRA($MIN_TLS_VERSION,, + AC_MSG_ERROR([[ +*** +*** libgnutls and libgnutls-extra were not found. You may want to get it from +*** http://www.gnutls.org/ +*** +]])) -CHECK_LUA() +dnl CHECK_LUA() have_apr_memcache=0 CHECK_APR_MEMCACHE([have_apr_memcache=1], [have_apr_memcache=0]) AC_SUBST(have_apr_memcache) -MODULE_CFLAGS="${LIBGNUTLS_CFLAGS} ${APR_MEMCACHE_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" -MODULE_LIBS="${APR_MEMCACHE_LIBS} ${LIBGNUTLS_LIBS}" +MODULE_CFLAGS="${LIBGNUTLS_EXTRA_CFLAGS} ${APR_MEMCACHE_CFLAGS} ${APXS_CFLAGS} ${AP_INCLUDES} ${APR_INCLUDES} ${APU_INCLUDES}" +MODULE_LIBS="${APR_MEMCACHE_LIBS} ${LIBGNUTLS_EXTRA_LIBS}" AC_SUBST(MODULE_CFLAGS) AC_SUBST(MODULE_LIBS) -AC_CONFIG_FILES([Makefile src/Makefile include/mod_gnutls.h data/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile include/mod_gnutls.h]) AC_OUTPUT echo "---" diff --git a/include/mod_gnutls.h.in b/include/mod_gnutls.h.in index 3473bf1..db7e7dd 100644 --- a/include/mod_gnutls.h.in +++ b/include/mod_gnutls.h.in @@ -29,6 +29,8 @@ #include #include +#include +#include #include #ifndef __mod_gnutls_h_inc @@ -76,24 +78,42 @@ typedef struct apr_size_t lua_bytecode_len; } mgs_dirconf_rec; + +/* The maximum number of client CA certificates allowed. + */ +#define MAX_CA_CRTS 128 + +/* The maximum number of certificates to send in a chain + */ +#define MAX_CHAIN_SIZE 8 + typedef struct { gnutls_certificate_credentials_t certs; + gnutls_srp_server_credentials_t srp_creds; + gnutls_anon_server_credentials_t anon_creds; char* cert_cn; - gnutls_x509_crt_t cert_x509; + gnutls_x509_crt_t certs_x509[MAX_CHAIN_SIZE]; /* A certificate chain */ + unsigned int certs_x509_num; gnutls_x509_privkey_t privkey_x509; + gnutls_openpgp_crt_t cert_pgp; /* A certificate chain */ + gnutls_openpgp_privkey_t privkey_pgp; int enabled; - int ciphers[16]; - int key_exchange[16]; - int macs[16]; - int protocol[16]; - int compression[16]; - int cert_types[16]; - apr_time_t cache_timeout; + /* whether to send the PEM encoded certificates + * to CGIs + */ + int export_certificates_enabled; + gnutls_priority_t priorities; + gnutls_rsa_params_t rsa_params; + gnutls_dh_params_t dh_params; + int cache_timeout; mgs_cache_e cache_type; const char* cache_config; - const char* rsa_params_file; - const char* dh_params_file; + const char* srp_tpasswd_file; + const char* srp_tpasswd_conf_file; + gnutls_x509_crt_t ca_list[MAX_CA_CRTS]; + gnutls_openpgp_keyring_t pgp_list; + unsigned int ca_list_size; int client_verify_mode; } mgs_srvconf_rec; @@ -214,15 +234,37 @@ int mgs_cache_session_init(mgs_handle_t *ctxt); char *mgs_session_id2sz(unsigned char *id, int idlen, char *str, int strsize); +/** + * Convert a time_t into a Null Terminated String + * @param t time_t time + * @param str Location to store the Hex Encoded String + * @param strsize The Maximum Length that can be stored in str + */ +char *mgs_time2sz(time_t t, char *str, int strsize); + /* Configuration Functions */ +const char *mgs_set_srp_tpasswd_conf_file(cmd_parms * parms, void *dummy, + const char *arg); +const char *mgs_set_srp_tpasswd_file(cmd_parms * parms, void *dummy, + const char *arg); +const char *mgs_set_dh_file(cmd_parms * parms, void *dummy, + const char *arg); +const char *mgs_set_rsa_export_file(cmd_parms * parms, void *dummy, + const char *arg); const char *mgs_set_cert_file(cmd_parms * parms, void *dummy, const char *arg); const char *mgs_set_key_file(cmd_parms * parms, void *dummy, const char *arg); +const char *mgs_set_pgpcert_file(cmd_parms * parms, void *dummy, + const char *arg); + +const char *mgs_set_pgpkey_file(cmd_parms * parms, void *dummy, + const char *arg); + const char *mgs_set_cache(cmd_parms * parms, void *dummy, const char *type, const char* arg); @@ -235,8 +277,15 @@ const char *mgs_set_client_verify(cmd_parms * parms, void *dummy, const char *mgs_set_client_ca_file(cmd_parms * parms, void *dummy, const char *arg); +const char *mgs_set_keyring_file(cmd_parms * parms, void *dummy, + const char *arg); + const char *mgs_set_enabled(cmd_parms * parms, void *dummy, const char *arg); +const char *mgs_set_export_certificates_enabled(cmd_parms * parms, void *dummy, + const char *arg); +const char *mgs_set_priorities(cmd_parms * parms, void *dummy, + const char *arg); const char *mgs_set_require_section(cmd_parms *cmd, void *mconfig, const char *arg); diff --git a/src/Makefile.am b/src/Makefile.am index 59c8909..93b217d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,7 @@ CLEANFILES = .libs/libmod_gnutls *~ -libmod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c gnutls_config.c gnutls_hooks.c gnutls_lua.c +libmod_gnutls_la_SOURCES = mod_gnutls.c gnutls_io.c gnutls_cache.c gnutls_config.c gnutls_hooks.c +#gnutls_lua.c libmod_gnutls_la_CFLAGS = -Wall ${MODULE_CFLAGS} ${LUA_CFLAGS} libmod_gnutls_la_LDFLAGS = -rpath ${AP_LIBEXECDIR} -module -avoid-version ${MODULE_LIBS} ${LUA_LIBS} diff --git a/src/gnutls_cache.c b/src/gnutls_cache.c index 4c094fa..83e7bb5 100644 --- a/src/gnutls_cache.c +++ b/src/gnutls_cache.c @@ -34,17 +34,16 @@ #define MC_TAG "mod_gnutls:" -#define MC_TAG_LEN \ - (sizeof(MC_TAG)) +#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 *mgs_session_id2sz(unsigned char *id, int idlen, char *str, int strsize) { char *cp; int n; - - cp = apr_cpystrn(str, MC_TAG, MC_TAG_LEN); + + cp = str; for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) { apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]); cp += 2; @@ -53,23 +52,58 @@ static char *gnutls_session_id2sz(unsigned char *id, int idlen, return str; } -char *mgs_session_id2sz(unsigned char *id, int idlen, - char *str, int strsize) + +/* Name the Session ID as: + * server:port.SessionID + * to disallow resuming sessions on different servers + */ +static int mgs_session_id2dbm(conn_rec* c, unsigned char *id, int idlen, + apr_datum_t* dbmkey) { - char *cp; - int n; +char buf[STR_SESSION_LEN]; +char *sz; - cp = str; - 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; + sz = mgs_session_id2sz(id, idlen, buf, sizeof(buf)); + if (sz == NULL) + return -1; + + dbmkey->dptr = apr_psprintf(c->pool, "%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz); + dbmkey->dsize = strlen( dbmkey->dptr); + + return 0; } +#define CTIME "%b %d %k:%M:%S %Y %Z" +char *mgs_time2sz(time_t in_time, char *str, int strsize) +{ + apr_time_exp_t vtm; + apr_size_t ret_size; + apr_time_t t; + + + apr_time_ansi_put (&t, in_time); + apr_time_exp_gmt (&vtm, t); + apr_strftime(str, &ret_size, strsize-1, CTIME, &vtm); + + return str; +} #if HAVE_APR_MEMCACHE +/* Name the Session ID as: + * server:port.SessionID + * to disallow resuming sessions on different servers + */ +static char* mgs_session_id2mc(conn_rec* c, unsigned char *id, int idlen) +{ +char buf[STR_SESSION_LEN]; +char *sz; + + sz = mgs_session_id2sz(id, idlen, buf, sizeof(buf)); + if (sz == NULL) + return NULL; + + return apr_psprintf(c->pool, MC_TAG"%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz); +} /** * GnuTLS Session Cache using libmemcached @@ -167,11 +201,10 @@ static int mc_cache_store(void* baton, gnutls_datum_t key, { apr_status_t rv = APR_SUCCESS; mgs_handle_t *ctxt = baton; - char buf[STR_SESSION_LEN]; char* strkey = NULL; apr_uint32_t timeout; - strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + strkey = mgs_session_id2mc(ctxt->c, key.data, key.size); if(!strkey) return -1; @@ -194,13 +227,12 @@ static gnutls_datum_t mc_cache_fetch(void* baton, gnutls_datum_t key) { apr_status_t rv = APR_SUCCESS; mgs_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)); + strkey = mgs_session_id2mc(ctxt->c, key.data, key.size); if (!strkey) { return data; } @@ -235,10 +267,9 @@ static int mc_cache_delete(void* baton, gnutls_datum_t key) { apr_status_t rv = APR_SUCCESS; mgs_handle_t *ctxt = baton; - char buf[STR_SESSION_LEN]; char* strkey = NULL; - strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); + strkey = mgs_session_id2mc(ctxt->c, key.data, key.size); if(!strkey) return -1; @@ -349,10 +380,8 @@ static gnutls_datum_t dbm_cache_fetch(void* baton, gnutls_datum_t key) mgs_handle_t *ctxt = baton; apr_status_t rv; - dbmkey.dptr = key.data; - dbmkey.dsize = key.size; - - dbm_cache_expire(ctxt); + if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0) + return data; rv = apr_dbm_open(&dbm, ctxt->sc->cache_config, APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool); @@ -398,9 +427,9 @@ static int dbm_cache_store(void* baton, gnutls_datum_t key, mgs_handle_t *ctxt = baton; apr_status_t rv; apr_time_t expiry; - - dbmkey.dptr = (char *)key.data; - dbmkey.dsize = key.size; + + if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0) + return -1; /* create DBM value */ dbmval.dsize = data.size + sizeof(apr_time_t); @@ -412,6 +441,8 @@ static int dbm_cache_store(void* baton, gnutls_datum_t key, memcpy((char *)dbmval.dptr+sizeof(apr_time_t), data.data, data.size); + /* we expire dbm only on every store + */ dbm_cache_expire(ctxt); rv = apr_dbm_open(&dbm, ctxt->sc->cache_config, @@ -450,12 +481,10 @@ static int dbm_cache_delete(void* baton, gnutls_datum_t key) apr_datum_t dbmkey; mgs_handle_t *ctxt = baton; apr_status_t rv; - - dbmkey.dptr = (char *)key.data; - dbmkey.dsize = key.size; - dbm_cache_expire(ctxt); - + if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0) + return -1; + rv = apr_dbm_open(&dbm, ctxt->sc->cache_config, APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool); if (rv != APR_SUCCESS) { diff --git a/src/gnutls_config.c b/src/gnutls_config.c index d3879f1..f08512e 100644 --- a/src/gnutls_config.c +++ b/src/gnutls_config.c @@ -1,5 +1,6 @@ /** * Copyright 2004-2005 Paul Querna + * Copyright 2007 Nikos Mavrogiannopoulos * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,225 +18,449 @@ #include "mod_gnutls.h" -static int load_datum_from_file(apr_pool_t* pool, - const char* file, - gnutls_datum_t* data) +static int load_datum_from_file(apr_pool_t * pool, + const char *file, gnutls_datum_t * data) { - apr_file_t* fp; + apr_file_t *fp; apr_finfo_t finfo; apr_status_t rv; apr_size_t br = 0; - - rv = apr_file_open(&fp, file, APR_READ|APR_BINARY, APR_OS_DEFAULT, - pool); + + rv = apr_file_open(&fp, file, APR_READ | APR_BINARY, APR_OS_DEFAULT, + pool); if (rv != APR_SUCCESS) { - return rv; + return rv; } - + rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp); - + if (rv != APR_SUCCESS) { - return rv; + return rv; } - - data->data = apr_palloc(pool, finfo.size+1); + + data->data = apr_palloc(pool, finfo.size + 1); rv = apr_file_read_full(fp, data->data, finfo.size, &br); - + if (rv != APR_SUCCESS) { - return rv; + return rv; } apr_file_close(fp); - + data->data[br] = '\0'; data->size = br; - + return 0; } -const char *mgs_set_cert_file(cmd_parms * parms, void *dummy, - const char *arg) +const char *mgs_set_dh_file(cmd_parms * parms, void *dummy, + const char *arg) { int ret; gnutls_datum_t data; - const char* file; - apr_pool_t* spool; + const char *file; + apr_pool_t *spool; mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(parms->server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + apr_pool_create(&spool, parms->pool); - + file = ap_server_root_relative(spool, arg); if (load_datum_from_file(spool, file, &data) != 0) { - return apr_psprintf(parms->pool, "GnuTLS: Error Reading " - "Certificate '%s'", file); + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "DH params '%s'", file); } - - gnutls_x509_crt_init(&sc->cert_x509); - ret = gnutls_x509_crt_import(sc->cert_x509, &data, GNUTLS_X509_FMT_PEM); + + ret = gnutls_dh_params_init(&sc->dh_params); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + ret = + gnutls_dh_params_import_pkcs3(sc->dh_params, &data, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " + "DH params '%s': (%d) %s", file, ret, + gnutls_strerror(ret)); + } + + apr_pool_destroy(spool); + + return NULL; +} + +const char *mgs_set_rsa_export_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + int ret; + gnutls_datum_t data; + const char *file; + apr_pool_t *spool; + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + + apr_pool_create(&spool, parms->pool); + + file = ap_server_root_relative(spool, arg); + + if (load_datum_from_file(spool, file, &data) != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "RSA params '%s'", file); + } + + ret = gnutls_rsa_params_init(&sc->rsa_params); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + ret = + gnutls_rsa_params_import_pkcs1(sc->rsa_params, &data, GNUTLS_X509_FMT_PEM); if (ret != 0) { - return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " - "Certificate'%s': (%d) %s", file, ret, - gnutls_strerror(ret)); + return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " + "RSA params '%s': (%d) %s", file, ret, + gnutls_strerror(ret)); } - + + apr_pool_destroy(spool); + return NULL; +} + + +const char *mgs_set_cert_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + int ret; + gnutls_datum_t data; + const char *file; + apr_pool_t *spool; + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + apr_pool_create(&spool, parms->pool); + + file = ap_server_root_relative(spool, arg); + + if (load_datum_from_file(spool, file, &data) != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "Certificate '%s'", file); + } + + sc->certs_x509_num = MAX_CHAIN_SIZE; + ret = + gnutls_x509_crt_list_import(sc->certs_x509, &sc->certs_x509_num, &data, GNUTLS_X509_FMT_PEM, 0); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " + "Certificate '%s': (%d) %s", file, ret, + gnutls_strerror(ret)); + } + apr_pool_destroy(spool); return NULL; } const char *mgs_set_key_file(cmd_parms * parms, void *dummy, - const char *arg) + const char *arg) { int ret; gnutls_datum_t data; - const char* file; - apr_pool_t* spool; + const char *file; + apr_pool_t *spool; mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(parms->server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); apr_pool_create(&spool, parms->pool); - + file = ap_server_root_relative(spool, arg); - + if (load_datum_from_file(spool, file, &data) != 0) { - return apr_psprintf(parms->pool, "GnuTLS: Error Reading " - "Private Key '%s'", file); + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "Private Key '%s'", file); } - - gnutls_x509_privkey_init(&sc->privkey_x509); - ret = gnutls_x509_privkey_import(sc->privkey_x509, &data, GNUTLS_X509_FMT_PEM); + + ret = gnutls_x509_privkey_init(&sc->privkey_x509); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + ret = + gnutls_x509_privkey_import(sc->privkey_x509, &data, + GNUTLS_X509_FMT_PEM); if (ret != 0) { - return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " - "Private Key '%s': (%d) %s", file, ret, - gnutls_strerror(ret)); + return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " + "Private Key '%s': (%d) %s", file, ret, + gnutls_strerror(ret)); } apr_pool_destroy(spool); return NULL; } +const char *mgs_set_pgpcert_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + int ret; + gnutls_datum_t data; + const char *file; + apr_pool_t *spool; + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + apr_pool_create(&spool, parms->pool); + + file = ap_server_root_relative(spool, arg); + + if (load_datum_from_file(spool, file, &data) != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "Certificate '%s'", file); + } + + ret = gnutls_openpgp_crt_init( &sc->cert_pgp); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to Init " + "PGP Certificate: (%d) %s", ret, + gnutls_strerror(ret)); + } + + ret = + gnutls_openpgp_crt_import(sc->cert_pgp, &data, GNUTLS_OPENPGP_FMT_BASE64); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " + "PGP Certificate '%s': (%d) %s", file, ret, + gnutls_strerror(ret)); + } + + apr_pool_destroy(spool); + return NULL; +} + +const char *mgs_set_pgpkey_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + int ret; + gnutls_datum_t data; + const char *file; + apr_pool_t *spool; + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + apr_pool_create(&spool, parms->pool); + + file = ap_server_root_relative(spool, arg); + + if (load_datum_from_file(spool, file, &data) != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "Private Key '%s'", file); + } + + ret = gnutls_openpgp_privkey_init(&sc->privkey_pgp); + if (ret < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + ret = + gnutls_openpgp_privkey_import(sc->privkey_pgp, &data, + GNUTLS_OPENPGP_FMT_BASE64, NULL, 0); + if (ret != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " + "PGP Private Key '%s': (%d) %s", file, ret, + gnutls_strerror(ret)); + } + apr_pool_destroy(spool); + return NULL; +} + + +const char *mgs_set_srp_tpasswd_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + + sc->srp_tpasswd_file = ap_server_root_relative(parms->pool, arg); + + return NULL; +} + +const char *mgs_set_srp_tpasswd_conf_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + + sc->srp_tpasswd_conf_file = ap_server_root_relative(parms->pool, arg); + + return NULL; +} + const char *mgs_set_cache(cmd_parms * parms, void *dummy, - const char *type, const char* arg) + const char *type, const char *arg) { - const char* err; + const char *err; mgs_srvconf_rec *sc = ap_get_module_config(parms->server-> - module_config, - &gnutls_module); + module_config, + &gnutls_module); if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY))) { - return err; + return err; } if (strcasecmp("none", type) == 0) { - sc->cache_type = mgs_cache_none; - } - else if (strcasecmp("dbm", type) == 0) { - sc->cache_type = mgs_cache_dbm; + sc->cache_type = mgs_cache_none; + } else if (strcasecmp("dbm", type) == 0) { + sc->cache_type = mgs_cache_dbm; } #if HAVE_APR_MEMCACHE else if (strcasecmp("memcache", type) == 0) { - sc->cache_type = mgs_cache_memcache; + sc->cache_type = mgs_cache_memcache; } #endif else { - return "Invalid Type for GnuTLSCache!"; + return "Invalid Type for GnuTLSCache!"; } if (sc->cache_type == mgs_cache_dbm) { - sc->cache_config = ap_server_root_relative(parms->pool, arg); - } - else { - sc->cache_config = apr_pstrdup(parms->pool, arg); + sc->cache_config = ap_server_root_relative(parms->pool, arg); + } else { + sc->cache_config = apr_pstrdup(parms->pool, arg); } return NULL; } const char *mgs_set_cache_timeout(cmd_parms * parms, void *dummy, - const char *arg) + const char *arg) { int argint; mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(parms->server-> - module_config, - &gnutls_module); - + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + argint = atoi(arg); - + if (argint < 0) { - return "GnuTLSCacheTimeout: Invalid argument"; - } - else if (argint == 0) { - sc->cache_timeout = 0; + return "GnuTLSCacheTimeout: Invalid argument"; + } else if (argint == 0) { + sc->cache_timeout = 0; + } else { + sc->cache_timeout = apr_time_from_sec(argint); } - else { - sc->cache_timeout = apr_time_from_sec(argint); - } - + return NULL; } const char *mgs_set_client_verify(cmd_parms * parms, void *dummy, - const char *arg) + const char *arg) { int mode; if (strcasecmp("none", arg) == 0 || strcasecmp("ignore", arg) == 0) { - mode = GNUTLS_CERT_IGNORE; - } - else if (strcasecmp("optional", arg) == 0 || strcasecmp("request", arg) == 0) { - mode = GNUTLS_CERT_REQUEST; - } - else if (strcasecmp("require", arg) == 0) { - mode = GNUTLS_CERT_REQUIRE; - } - else { - return "GnuTLSClientVerify: Invalid argument"; + mode = GNUTLS_CERT_IGNORE; + } else if (strcasecmp("optional", arg) == 0 + || strcasecmp("request", arg) == 0) { + mode = GNUTLS_CERT_REQUEST; + } else if (strcasecmp("require", arg) == 0) { + mode = GNUTLS_CERT_REQUIRE; + } else { + return "GnuTLSClientVerify: Invalid argument"; } - + /* This was set from a directory context */ if (parms->path) { - mgs_dirconf_rec *dc = (mgs_dirconf_rec *)dummy; - dc->client_verify_mode = mode; - } - else { - mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(parms->server-> - module_config, - &gnutls_module); - sc->client_verify_mode = mode; + mgs_dirconf_rec *dc = (mgs_dirconf_rec *) dummy; + dc->client_verify_mode = mode; + } else { + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + sc->client_verify_mode = mode; } return NULL; } const char *mgs_set_client_ca_file(cmd_parms * parms, void *dummy, - const char *arg) + const char *arg) { int rv; - const char* file; - apr_pool_t* spool; + const char *file; + apr_pool_t *spool; gnutls_datum_t data; - mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(parms->server-> - module_config, - &gnutls_module); + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); apr_pool_create(&spool, parms->pool); file = ap_server_root_relative(spool, arg); - sc->ca_list_size = 16; + if (load_datum_from_file(spool, file, &data) != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "Client CA File '%s'", file); + } + + sc->ca_list_size = MAX_CA_CRTS; + rv = gnutls_x509_crt_list_import(sc->ca_list, &sc->ca_list_size, + &data, GNUTLS_X509_FMT_PEM, + GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); + if (rv < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to load " + "Client CA File '%s': (%d) %s", file, rv, + gnutls_strerror(rv)); + } - load_datum_from_file(spool, file, &data); + apr_pool_destroy(spool); + return NULL; +} - rv = gnutls_x509_crt_list_import(sc->ca_list, &sc->ca_list_size, - &data, GNUTLS_X509_FMT_PEM, - GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); +const char *mgs_set_keyring_file(cmd_parms * parms, void *dummy, + const char *arg) +{ + int rv; + const char *file; + apr_pool_t *spool; + gnutls_datum_t data; + + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + apr_pool_create(&spool, parms->pool); + + file = ap_server_root_relative(spool, arg); + + if (load_datum_from_file(spool, file, &data) != 0) { + return apr_psprintf(parms->pool, "GnuTLS: Error Reading " + "Keyring File '%s'", file); + } + + rv = gnutls_openpgp_keyring_init(&sc->pgp_list); if (rv < 0) { - return apr_psprintf(parms->pool, "GnuTLS: Failed to load " - "Client CA File '%s': (%d) %s", file, rv, - gnutls_strerror(rv)); + return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize" + "keyring: (%d) %s", rv, gnutls_strerror(rv)); + } + + rv = gnutls_openpgp_keyring_import(sc->pgp_list, &data, GNUTLS_OPENPGP_FMT_BASE64); + if (rv < 0) { + return apr_psprintf(parms->pool, "GnuTLS: Failed to load " + "Keyring File '%s': (%d) %s", file, rv, + gnutls_strerror(rv)); } apr_pool_destroy(spool); @@ -243,20 +468,57 @@ const char *mgs_set_client_ca_file(cmd_parms * parms, void *dummy, } const char *mgs_set_enabled(cmd_parms * parms, void *dummy, - const char *arg) + const char *arg) { mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(parms->server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); if (!strcasecmp(arg, "On")) { - sc->enabled = GNUTLS_ENABLED_TRUE; + sc->enabled = GNUTLS_ENABLED_TRUE; + } else if (!strcasecmp(arg, "Off")) { + sc->enabled = GNUTLS_ENABLED_FALSE; + } else { + return "GnuTLSEnable must be set to 'On' or 'Off'"; } - else if (!strcasecmp(arg, "Off")) { - sc->enabled = GNUTLS_ENABLED_FALSE; + + return NULL; +} + +const char *mgs_set_export_certificates_enabled(cmd_parms * parms, void *dummy, + const char *arg) +{ + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + if (!strcasecmp(arg, "On")) { + sc->export_certificates_enabled = GNUTLS_ENABLED_TRUE; + } else if (!strcasecmp(arg, "Off")) { + sc->export_certificates_enabled = GNUTLS_ENABLED_FALSE; + } else { + return "GnuTLSExportCertificates must be set to 'On' or 'Off'"; } - else { - return "GnuTLSEnable must be set to 'On' or 'Off'"; + + return NULL; +} + + +const char *mgs_set_priorities(cmd_parms * parms, void *dummy, const char *arg) +{ + int ret; + const char *err; + mgs_srvconf_rec *sc = + (mgs_srvconf_rec *) ap_get_module_config(parms->server-> + module_config, + &gnutls_module); + + + ret = gnutls_priority_init( &sc->priorities, arg, &err); + if (ret < 0) { + if (ret == GNUTLS_E_INVALID_REQUEST) + return apr_psprintf(parms->pool, "GnuTLS: Syntax error parsing priorities string at: %s", err); + return "Error setting priorities"; } return NULL; @@ -264,92 +526,63 @@ const char *mgs_set_enabled(cmd_parms * parms, void *dummy, void *mgs_config_server_create(apr_pool_t * p, server_rec * s) { - int i; mgs_srvconf_rec *sc = apr_pcalloc(p, sizeof(*sc)); + int ret; sc->enabled = GNUTLS_ENABLED_FALSE; - - gnutls_certificate_allocate_credentials(&sc->certs); + + ret = gnutls_certificate_allocate_credentials(&sc->certs); + if (ret < 0) { + return apr_psprintf(p, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + ret = gnutls_anon_allocate_server_credentials(&sc->anon_creds); + if (ret < 0) { + return apr_psprintf(p, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + ret = gnutls_srp_allocate_server_credentials(&sc->srp_creds); + if (ret < 0) { + return apr_psprintf(p, "GnuTLS: Failed to initialize" + ": (%d) %s", ret, gnutls_strerror(ret)); + } + + sc->srp_tpasswd_conf_file = NULL; + sc->srp_tpasswd_file = NULL; sc->privkey_x509 = NULL; - sc->cert_x509 = NULL; + memset( sc->certs_x509, 0, sizeof(sc->certs_x509)); + sc->certs_x509_num = 0; sc->cache_timeout = apr_time_from_sec(300); sc->cache_type = mgs_cache_dbm; sc->cache_config = ap_server_root_relative(p, "conf/gnutls_cache"); - - /* TODO: Make this Configurable. But it isn't configurable in mod_ssl? */ - sc->dh_params_file = ap_server_root_relative(p, "conf/dhfile"); - sc->rsa_params_file = ap_server_root_relative(p, "conf/rsafile"); - - /* Finish SSL Client Certificate Support */ + sc->client_verify_mode = GNUTLS_CERT_IGNORE; - - /* TODO: Make this Configurable ! */ - /* mod_ssl uses a flex based parser for this part.. sigh */ - i = 0; - sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC; - sc->ciphers[i++] = GNUTLS_CIPHER_AES_128_CBC; - sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_128; - sc->ciphers[i++] = GNUTLS_CIPHER_3DES_CBC; - sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_40; - sc->ciphers[i] = 0; - - i = 0; - sc->key_exchange[i++] = GNUTLS_KX_RSA; - sc->key_exchange[i++] = GNUTLS_KX_RSA_EXPORT; - sc->key_exchange[i++] = GNUTLS_KX_DHE_DSS; - sc->key_exchange[i++] = GNUTLS_KX_DHE_RSA; - sc->key_exchange[i++] = GNUTLS_KX_ANON_DH; - sc->key_exchange[i++] = GNUTLS_KX_SRP; - sc->key_exchange[i++] = GNUTLS_KX_SRP_RSA; - sc->key_exchange[i++] = GNUTLS_KX_SRP_DSS; - sc->key_exchange[i] = 0; - - i = 0; - sc->macs[i++] = GNUTLS_MAC_SHA; - sc->macs[i++] = GNUTLS_MAC_MD5; - sc->macs[i++] = GNUTLS_MAC_RMD160; - sc->macs[i] = 0; - - i = 0; - sc->protocol[i++] = GNUTLS_TLS1_1; - sc->protocol[i++] = GNUTLS_TLS1; - sc->protocol[i++] = GNUTLS_SSL3; - sc->protocol[i] = 0; - - i = 0; - sc->compression[i++] = GNUTLS_COMP_NULL; - sc->compression[i++] = GNUTLS_COMP_ZLIB; - sc->compression[i++] = GNUTLS_COMP_LZO; - sc->compression[i] = 0; - - i = 0; - sc->cert_types[i++] = GNUTLS_CRT_X509; - sc->cert_types[i] = 0; - + return sc; } -void *mgs_config_dir_merge(apr_pool_t *p, void *basev, void *addv) +void *mgs_config_dir_merge(apr_pool_t * p, void *basev, void *addv) { mgs_dirconf_rec *new; - mgs_dirconf_rec *base = (mgs_dirconf_rec *) basev; +/* mgs_dirconf_rec *base = (mgs_dirconf_rec *) basev; */ mgs_dirconf_rec *add = (mgs_dirconf_rec *) addv; - + new = (mgs_dirconf_rec *) apr_pcalloc(p, sizeof(mgs_dirconf_rec)); new->lua_bytecode = apr_pstrmemdup(p, add->lua_bytecode, - add->lua_bytecode_len); + add->lua_bytecode_len); new->lua_bytecode_len = add->lua_bytecode_len; new->client_verify_mode = add->client_verify_mode; return new; } -void *mgs_config_dir_create(apr_pool_t *p, char *dir) +void *mgs_config_dir_create(apr_pool_t * p, char *dir) { mgs_dirconf_rec *dc = apr_palloc(p, sizeof(*dc)); - + dc->client_verify_mode = -1; dc->lua_bytecode = NULL; dc->lua_bytecode_len = 0; return dc; } - diff --git a/src/gnutls_hooks.c b/src/gnutls_hooks.c index 8452d36..26917b8 100644 --- a/src/gnutls_hooks.c +++ b/src/gnutls_hooks.c @@ -1,5 +1,6 @@ /** * Copyright 2004-2005 Paul Querna + * Copyright 2007 Nikos Mavrogiannopoulos * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,11 +29,20 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL; #endif #if MOD_GNUTLS_DEBUG -static apr_file_t* debug_log_fp; +static apr_file_t *debug_log_fp; #endif static int mpm_is_threaded; +static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt); +/* use side==0 for server and side==1 for client */ +static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, + int side, + int export_certificates_enabled); +static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, + int side, + int export_certificates_enabled); + static apr_status_t mgs_cleanup_pre_config(void *data) { gnutls_global_deinit(); @@ -40,208 +50,384 @@ static apr_status_t mgs_cleanup_pre_config(void *data) } #if MOD_GNUTLS_DEBUG -static void gnutls_debug_log_all( int level, const char* str) +static void gnutls_debug_log_all(int level, const char *str) { apr_file_printf(debug_log_fp, "<%d> %s\n", level, str); } #endif -int mgs_hook_pre_config(apr_pool_t * pconf, - apr_pool_t * plog, apr_pool_t * ptemp) +int +mgs_hook_pre_config(apr_pool_t * pconf, + apr_pool_t * plog, apr_pool_t * ptemp) { +int ret; #if APR_HAS_THREADS ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_is_threaded); if (mpm_is_threaded) { - gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); } #else mpm_is_threaded = 0; #endif - gnutls_global_init(); + if (gnutls_check_version(LIBGNUTLS_VERSION)==NULL) { + fprintf(stderr, "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)); + return -3; + } + ret = gnutls_global_init_extra(); + if (ret < 0) { + fprintf(stderr, "gnutls_global_init_extra: %s\n", gnutls_strerror(ret)); + return -3; + } + apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, - apr_pool_cleanup_null); + 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); + 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; } +static int mgs_select_virtual_server_cb(gnutls_session_t session) +{ + mgs_handle_t *ctxt; + mgs_srvconf_rec *tsc; + int ret; + int cprio[2]; + + ctxt = gnutls_transport_get_ptr(session); + + /* find the virtual server */ + tsc = mgs_find_sni_server(session); + + if (tsc != NULL) + ctxt->sc = tsc; + + gnutls_certificate_server_set_request(session, + ctxt->sc->client_verify_mode); + + /* set the new server credentials + */ + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + ctxt->sc->certs); + + gnutls_credentials_set(session, GNUTLS_CRD_ANON, ctxt->sc->anon_creds); + + if (ctxt->sc->srp_tpasswd_conf_file != NULL + && ctxt->sc->srp_tpasswd_file != NULL) { + gnutls_credentials_set(session, GNUTLS_CRD_SRP, + ctxt->sc->srp_creds); + } + + /* update the priorities - to avoid negotiating a ciphersuite that is not + * enabled on this virtual server. Note that here we ignore the version + * negotiation. + */ + ret = gnutls_priority_set(session, ctxt->sc->priorities); + /* actually it shouldn't fail since we have checked at startup */ + if (ret < 0) + return ret; + + /* If both certificate types are not present disallow them from + * being negotiated. + */ + if (ctxt->sc->certs_x509[0] != NULL && ctxt->sc->cert_pgp == NULL) { + cprio[0] = GNUTLS_CRT_X509; + cprio[1] = 0; + gnutls_certificate_type_set_priority( session, cprio); + } else if (ctxt->sc->cert_pgp != NULL && ctxt->sc->certs_x509[0]==NULL) { + cprio[0] = GNUTLS_CRT_OPENPGP; + cprio[1] = 0; + gnutls_certificate_type_set_priority( session, cprio); + } + + return 0; +} -static gnutls_datum load_params(const char* file, server_rec* s, - apr_pool_t* pool) +static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret) { - gnutls_datum ret = { NULL, 0 }; - apr_file_t* fp; - apr_finfo_t finfo; - apr_status_t rv; - apr_size_t br = 0; + mgs_handle_t *ctxt; + + ctxt = gnutls_transport_get_ptr(session); + + if (gnutls_certificate_type_get( session) == GNUTLS_CRT_X509) { + ret->type = GNUTLS_CRT_X509; + ret->ncerts = ctxt->sc->certs_x509_num; + ret->deinit_all = 0; - rv = apr_file_open(&fp, file, APR_READ|APR_BINARY, APR_OS_DEFAULT, - pool); - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, - "GnuTLS failed to load params file at: %s", file); - return ret; + ret->cert.x509 = ctxt->sc->certs_x509; + ret->key.x509 = ctxt->sc->privkey_x509; + + return 0; + } else if (gnutls_certificate_type_get( session) == GNUTLS_CRT_OPENPGP) { + ret->type = GNUTLS_CRT_OPENPGP; + ret->ncerts = 1; + ret->deinit_all = 0; + + ret->cert.pgp = ctxt->sc->cert_pgp; + ret->key.pgp = ctxt->sc->privkey_pgp; + + return 0; + } - rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp); + return GNUTLS_E_INTERNAL_ERROR; +} - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, - "GnuTLS failed to stat params file at: %s", file); - return ret; +/* 2048-bit group parameters from SRP specification */ +const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n" + "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n" + "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n" + "KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n" + "dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n" + "YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n" + "Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n" + "-----END DH PARAMETERS-----\n"; + +/* Read the common name or the alternative name of the certificate. + * We only support a single name per certificate. + * + * Returns negative on error. + */ +static int read_crt_cn(server_rec * s, apr_pool_t * p, + gnutls_x509_crt_t cert, char **cert_cn) +{ + int rv = 0, i; + size_t data_len; + + + *cert_cn = NULL; + + data_len = 0; + rv = gnutls_x509_crt_get_dn_by_oid(cert, + GNUTLS_OID_X520_COMMON_NAME, + 0, 0, NULL, &data_len); + + if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) { + *cert_cn = apr_palloc(p, data_len); + rv = gnutls_x509_crt_get_dn_by_oid(cert, + GNUTLS_OID_X520_COMMON_NAME, 0, + 0, *cert_cn, &data_len); + } else { /* No CN return subject alternative name */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "No common name found in certificate for '%s:%d'. Looking for subject alternative name...", + s->server_hostname, s->port); + rv = 0; + /* read subject alternative name */ + for (i = 0; !(rv < 0); i++) { + data_len = 0; + rv = gnutls_x509_crt_get_subject_alt_name(cert, i, + NULL, &data_len, + NULL); + + if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) { + /* FIXME: not very efficient. What if we have several alt names + * before DNSName? + */ + *cert_cn = apr_palloc(p, data_len + 1); + + rv = gnutls_x509_crt_get_subject_alt_name(cert, i, + *cert_cn, + &data_len, NULL); + (*cert_cn)[data_len] = 0; + + if (rv == GNUTLS_SAN_DNSNAME) + break; + } + } } - ret.data = apr_palloc(pool, finfo.size+1); - rv = apr_file_read_full(fp, ret.data, finfo.size, &br); + return rv; +} + +static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p, + gnutls_openpgp_crt_t cert, char **cert_cn) +{ + int rv = 0; + size_t data_len; + - if (rv != APR_SUCCESS) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, - "GnuTLS failed to read params file at: %s", file); - return ret; + *cert_cn = NULL; + + data_len = 0; + rv = gnutls_openpgp_crt_get_name(cert, 0, NULL, &data_len); + + if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) { + *cert_cn = apr_palloc(p, data_len); + rv = gnutls_openpgp_crt_get_name(cert, 0, *cert_cn, &data_len); + } else { /* No CN return subject alternative name */ + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "No name found in PGP certificate for '%s:%d'.", + s->server_hostname, s->port); } - apr_file_close(fp); - ret.data[br] = '\0'; - ret.size = br; - return ret; + return rv; } -int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, - apr_pool_t * ptemp, - server_rec * base_server) + +int +mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, + apr_pool_t * ptemp, server_rec * base_server) { int rv; - int data_len; server_rec *s; - gnutls_dh_params_t dh_params; - gnutls_rsa_params_t rsa_params; + gnutls_dh_params_t dh_params = NULL; + gnutls_rsa_params_t rsa_params = NULL; mgs_srvconf_rec *sc; mgs_srvconf_rec *sc_base; void *data = NULL; int first_run = 0; const char *userdata_key = "mgs_init"; - + apr_pool_userdata_get(&data, userdata_key, base_server->process->pool); if (data == NULL) { - first_run = 1; - apr_pool_userdata_set((const void *)1, userdata_key, - apr_pool_cleanup_null, - base_server->process->pool); + first_run = 1; + apr_pool_userdata_set((const void *) 1, userdata_key, + apr_pool_cleanup_null, + base_server->process->pool); } { - gnutls_datum pdata; - apr_pool_t* tpool; - s = base_server; - sc_base = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, - &gnutls_module); - - apr_pool_create(&tpool, p); - - gnutls_dh_params_init(&dh_params); - - pdata = load_params(sc_base->dh_params_file, s, tpool); - - if (pdata.size != 0) { - rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata, - GNUTLS_X509_FMT_PEM); - if (rv != 0) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, - "GnuTLS: Unable to load DH Params: (%d) %s", - rv, gnutls_strerror(rv)); + s = base_server; + sc_base = + (mgs_srvconf_rec *) ap_get_module_config(s->module_config, + &gnutls_module); + + gnutls_dh_params_init(&dh_params); + + if (sc_base->dh_params == NULL) { + gnutls_datum pdata = { (void *) static_dh_params, sizeof(static_dh_params) }; + /* loading defaults */ + rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata, + GNUTLS_X509_FMT_PEM); + + if (rv < 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, + "GnuTLS: Unable to load DH Params: (%d) %s", + rv, gnutls_strerror(rv)); exit(rv); } - } - else { - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, - "GnuTLS: Unable to load DH Params." - " Shutting Down."); - exit(-1); - } - apr_pool_clear(tpool); - - gnutls_rsa_params_init(&rsa_params); - - pdata = load_params(sc_base->rsa_params_file, s, tpool); - - if (pdata.size != 0) { - rv = gnutls_rsa_params_import_pkcs1(rsa_params, &pdata, - GNUTLS_X509_FMT_PEM); - if (rv != 0) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, - "GnuTLS: Unable to load RSA Params: (%d) %s", - rv, gnutls_strerror(rv)); - exit(rv); - } - } - else { - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, - "GnuTLS: Unable to load RSA Params." - " Shutting Down."); - exit(-1); - } - - apr_pool_destroy(tpool); - rv = mgs_cache_post_config(p, s, sc_base); - if (rv != 0) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, - "GnuTLS: Post Config for GnuTLSCache Failed." - " Shutting Down."); - exit(-1); - } - - for (s = base_server; s; s = s->next) { - sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, - &gnutls_module); - sc->cache_type = sc_base->cache_type; - sc->cache_config = sc_base->cache_config; - - gnutls_certificate_set_rsa_export_params(sc->certs, - rsa_params); - gnutls_certificate_set_dh_params(sc->certs, dh_params); - - if (sc->cert_x509 == 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, s->port); - exit(-1); + } else dh_params = sc_base->dh_params; + + if (sc_base->rsa_params != NULL) + rsa_params = sc_base->rsa_params; + + /* else not an error but RSA-EXPORT ciphersuites are not available + */ + + rv = mgs_cache_post_config(p, s, sc_base); + if (rv != 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, + "GnuTLS: Post Config for GnuTLSCache Failed." + " Shutting Down."); + exit(-1); + } + + for (s = base_server; s; s = s->next) { + void *load = NULL; + sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, + &gnutls_module); + sc->cache_type = sc_base->cache_type; + sc->cache_config = sc_base->cache_config; + + /* Check if the priorities have been set */ + if (sc->priorities == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, + "GnuTLS: Host '%s:%d' is missing the GnuTLSPriorities directive!", + s->server_hostname, s->port); + exit(-1); } - if (sc->privkey_x509 == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) { - ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, - "[GnuTLS] - Host '%s:%d' is missing a " - "Private Key File!", - s->server_hostname, s->port); - exit(-1); - } + /* Check if DH or RSA params have been set per host */ + if (sc->rsa_params != NULL) + load = sc->rsa_params; + else if (rsa_params) load = rsa_params; - rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509, - GNUTLS_OID_X520_COMMON_NAME, 0, 0, - NULL, &data_len); + if (load != NULL) + gnutls_certificate_set_rsa_export_params(sc->certs, load); + + + load = NULL; + if (sc->dh_params != NULL) + load = sc->dh_params; + else if (dh_params) load = dh_params; - if (data_len < 1) { - sc->enabled = GNUTLS_ENABLED_FALSE; - sc->cert_cn = NULL; - continue; + if (load != NULL) { /* not needed but anyway */ + gnutls_certificate_set_dh_params(sc->certs, load); + gnutls_anon_set_server_dh_params(sc->anon_creds, load); } - - sc->cert_cn = apr_palloc(p, data_len); - rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509, - GNUTLS_OID_X520_COMMON_NAME, 0, 0, - sc->cert_cn, &data_len); - } + + gnutls_certificate_server_set_retrieve_function(sc->certs, + cert_retrieve_fn); + + if (sc->srp_tpasswd_conf_file != NULL + && sc->srp_tpasswd_file != NULL) { + rv = gnutls_srp_set_server_credentials_file(sc->srp_creds, + sc-> + srp_tpasswd_file, + sc-> + srp_tpasswd_conf_file); + + if (rv < 0 && sc->enabled == GNUTLS_ENABLED_TRUE) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, + "[GnuTLS] - Host '%s:%d' is missing a " + "SRP password or conf File!", + s->server_hostname, s->port); + exit(-1); + } + } + + if (sc->certs_x509[0] == 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, + s->port); + exit(-1); + } + + if (sc->privkey_x509 == NULL + && sc->enabled == GNUTLS_ENABLED_TRUE) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, + "[GnuTLS] - Host '%s:%d' is missing a " + "Private Key File!", + s->server_hostname, s->port); + exit(-1); + } + + if (sc->enabled == GNUTLS_ENABLED_TRUE) { + rv = read_crt_cn(s, p, sc->certs_x509[0], &sc->cert_cn); + if (rv < 0 && sc->cert_pgp != NULL) /* try openpgp certificate */ + rv = read_pgpcrt_cn(s, p, sc->cert_pgp, &sc->cert_cn); + + if (rv < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, + "[GnuTLS] - Cannot find a certificate for host '%s:%d'!", + s->server_hostname, s->port); + sc->cert_cn = NULL; + continue; + } + } + } } ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION); @@ -249,34 +435,32 @@ int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, return OK; } -void mgs_hook_child_init(apr_pool_t *p, server_rec *s) +void mgs_hook_child_init(apr_pool_t * p, server_rec * s) { apr_status_t rv = APR_SUCCESS; mgs_srvconf_rec *sc = ap_get_module_config(s->module_config, - &gnutls_module); + &gnutls_module); if (sc->cache_type != mgs_cache_none) { - rv = mgs_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"); + rv = mgs_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"); } } const char *mgs_hook_http_scheme(const request_rec * r) { mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(r->server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, + &gnutls_module); if (sc->enabled == GNUTLS_ENABLED_FALSE) { - return NULL; + return NULL; } return "https"; @@ -285,12 +469,11 @@ const char *mgs_hook_http_scheme(const request_rec * r) apr_port_t mgs_hook_default_port(const request_rec * r) { mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(r->server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, + &gnutls_module); if (sc->enabled == GNUTLS_ENABLED_FALSE) { - return 0; + return 0; } return 443; @@ -299,88 +482,86 @@ apr_port_t mgs_hook_default_port(const request_rec * r) #define MAX_HOST_LEN 255 #if USING_2_1_RECENT -typedef struct -{ +typedef struct { mgs_handle_t *ctxt; mgs_srvconf_rec *sc; - const char* sni_name; + const char *sni_name; } vhost_cb_rec; -static int vhost_cb (void* baton, conn_rec* conn, server_rec* s) +static int vhost_cb(void *baton, conn_rec * conn, server_rec * s) { mgs_srvconf_rec *tsc; - vhost_cb_rec* x = baton; + vhost_cb_rec *x = baton; tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, - &gnutls_module); - + &gnutls_module); + if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) { - return 0; + return 0; } - + /* The CN can contain a * -- this will match those too. */ if (ap_strcasecmp_match(x->sni_name, tsc->cert_cn) == 0) { - /* found a match */ + /* found a match */ #if MOD_GNUTLS_DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, - x->ctxt->c->base_server, - "GnuTLS: Virtual Host CB: " - "'%s' == '%s'", tsc->cert_cn, x->sni_name); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + x->ctxt->c->base_server, + "GnuTLS: Virtual Host CB: " + "'%s' == '%s'", tsc->cert_cn, x->sni_name); #endif - /* Because we actually change the server used here, we need to reset - * things like ClientVerify. - */ - x->sc = tsc; - /* Shit. Crap. Dammit. We *really* should rehandshake here, as our - * certificate structure *should* change when the server changes. - * acccckkkkkk. - */ - return 1; + /* Because we actually change the server used here, we need to reset + * things like ClientVerify. + */ + x->sc = tsc; + /* Shit. Crap. Dammit. We *really* should rehandshake here, as our + * certificate structure *should* change when the server changes. + * acccckkkkkk. + */ + return 1; + } else { +#if MOD_GNUTLS_DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + x->ctxt->c->base_server, + "GnuTLS: Virtual Host CB: " + "'%s' != '%s'", tsc->cert_cn, x->sni_name); +#endif + } return 0; } #endif -mgs_srvconf_rec* mgs_find_sni_server(gnutls_session_t session) +mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session) { int rv; - int sni_type; - int data_len = MAX_HOST_LEN; + unsigned int sni_type; + size_t data_len = MAX_HOST_LEN; char sni_name[MAX_HOST_LEN]; mgs_handle_t *ctxt; #if USING_2_1_RECENT vhost_cb_rec cbx; #else - server_rec* s; - mgs_srvconf_rec *tsc; + server_rec *s; + mgs_srvconf_rec *tsc; #endif - + ctxt = gnutls_transport_get_ptr(session); - - sni_type = gnutls_certificate_type_get(session); - if (sni_type != GNUTLS_CRT_X509) { - /* In theory, we could support OpenPGP Certificates. Theory != code. */ - ap_log_error(APLOG_MARK, APLOG_CRIT, 0, - ctxt->c->base_server, - "GnuTLS: Only x509 Certificates are currently supported."); - return NULL; - } - - rv = gnutls_server_name_get(ctxt->session, sni_name, - &data_len, &sni_type, 0); - + + rv = gnutls_server_name_get(ctxt->session, sni_name, + &data_len, &sni_type, 0); + if (rv != 0) { - return NULL; + return NULL; } - + if (sni_type != GNUTLS_NAME_DNS) { - ap_log_error(APLOG_MARK, APLOG_CRIT, 0, - ctxt->c->base_server, - "GnuTLS: Unknown type '%d' for SNI: " - "'%s'", sni_type, sni_name); - return NULL; + ap_log_error(APLOG_MARK, APLOG_CRIT, 0, + ctxt->c->base_server, + "GnuTLS: Unknown type '%d' for SNI: " + "'%s'", sni_type, sni_name); + return NULL; } - + /** * Code in the Core already sets up the c->base_server as the base * for this IP/Port combo. Trust that the core did the 'right' thing. @@ -389,72 +570,57 @@ mgs_srvconf_rec* mgs_find_sni_server(gnutls_session_t session) cbx.ctxt = ctxt; cbx.sc = NULL; cbx.sni_name = sni_name; - + rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx); if (rv == 1) { - return cbx.sc; + return cbx.sc; } #else for (s = ap_server_conf; s; s = s->next) { - - tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, - &gnutls_module); - if (tsc->enabled != GNUTLS_ENABLED_TRUE) { - continue; - } + + tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, + &gnutls_module); + if (tsc->enabled != GNUTLS_ENABLED_TRUE) { + continue; + } #if MOD_GNUTLS_DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, - ctxt->c->base_server, - "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X s->n: 0x%08X sc: 0x%08X", tsc->cert_cn, rv, - gnutls_pk_algorithm_get_name(gnutls_x509_privkey_get_pk_algorithm(ctxt->sc->privkey_x509)), - (unsigned int)s, (unsigned int)s->next, (unsigned int)tsc); -#endif - /* The CN can contain a * -- this will match those too. */ - if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + ctxt->c->base_server, + "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X s->n: 0x%08X sc: 0x%08X", + tsc->cert_cn, rv, + gnutls_pk_algorithm_get_name + (gnutls_x509_privkey_get_pk_algorithm + (ctxt->sc->privkey_x509)), (unsigned int) s, + (unsigned int) s->next, (unsigned int) tsc); +#endif + /* The CN can contain a * -- this will match those too. */ + if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) { #if MOD_GNUTLS_DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, - ctxt->c->base_server, - "GnuTLS: Virtual Host: " - "'%s' == '%s'", tsc->cert_cn, sni_name); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + ctxt->c->base_server, + "GnuTLS: Virtual Host: " + "'%s' == '%s'", tsc->cert_cn, sni_name); #endif - return tsc; - } + return tsc; + } } #endif return NULL; } -static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st* ret) -{ - mgs_handle_t *ctxt; - mgs_srvconf_rec *tsc; - - ctxt = gnutls_transport_get_ptr(session); +static const int protocol_priority[] = { + GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 +}; - ret->type = GNUTLS_CRT_X509; - ret->ncerts = 1; - ret->deinit_all = 0; - tsc = mgs_find_sni_server(session); - - if (tsc != NULL) { - ctxt->sc = tsc; - gnutls_certificate_server_set_request(ctxt->session, ctxt->sc->client_verify_mode); - } - - ret->cert.x509 = &ctxt->sc->cert_x509; - ret->key.x509 = ctxt->sc->privkey_x509; - return 0; -} - -static mgs_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c) +static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c) { mgs_handle_t *ctxt; mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(c->base_server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(c->base_server-> + module_config, + &gnutls_module); ctxt = apr_pcalloc(pool, sizeof(*ctxt)); ctxt->c = c; @@ -472,19 +638,17 @@ static mgs_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c) gnutls_init(&ctxt->session, GNUTLS_SERVER); - gnutls_protocol_set_priority(ctxt->session, sc->protocol); - gnutls_cipher_set_priority(ctxt->session, sc->ciphers); - gnutls_compression_set_priority(ctxt->session, sc->compression); - gnutls_kx_set_priority(ctxt->session, sc->key_exchange); - gnutls_mac_set_priority(ctxt->session, sc->macs); - gnutls_certificate_type_set_priority(ctxt->session, sc->cert_types); + /* because we don't set any default priorities here (we set later at + * the user hello callback) we need to at least set this in order for + * gnutls to be able to read packets. + */ + gnutls_protocol_set_priority(ctxt->session, protocol_priority); + + gnutls_handshake_set_post_client_hello_function(ctxt->session, + mgs_select_virtual_server_cb); mgs_cache_session_init(ctxt); - - gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE, ctxt->sc->certs); - gnutls_certificate_server_set_retrieve_function(sc->certs, cert_retrieve_fn); - gnutls_certificate_server_set_request(ctxt->session, ctxt->sc->client_verify_mode); return ctxt; } @@ -492,218 +656,484 @@ int mgs_hook_pre_connection(conn_rec * c, void *csd) { mgs_handle_t *ctxt; mgs_srvconf_rec *sc = - (mgs_srvconf_rec *) ap_get_module_config(c->base_server-> - module_config, - &gnutls_module); + (mgs_srvconf_rec *) ap_get_module_config(c->base_server-> + module_config, + &gnutls_module); if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) { - return DECLINED; + return DECLINED; } ctxt = create_gnutls_handle(c->pool, c); ap_set_module_config(c->conn_config, &gnutls_module, ctxt); - gnutls_transport_set_pull_function(ctxt->session, - mgs_transport_read); - gnutls_transport_set_push_function(ctxt->session, - mgs_transport_write); + gnutls_transport_set_pull_function(ctxt->session, mgs_transport_read); + gnutls_transport_set_push_function(ctxt->session, mgs_transport_write); gnutls_transport_set_ptr(ctxt->session, ctxt); - - ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, - NULL, c); - ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, - NULL, c); + + ctxt->input_filter = + ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c); + ctxt->output_filter = + ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c); return OK; } -int mgs_hook_fixups(request_rec *r) +int mgs_hook_fixups(request_rec * r) { unsigned char sbuf[GNUTLS_MAX_SESSION_ID]; char buf[AP_IOBUFSIZE]; - const char* tmp; - int len; + const char *tmp; + size_t len; mgs_handle_t *ctxt; int rv = OK; - + apr_table_t *env = r->subprocess_env; - ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module); + ctxt = + ap_get_module_config(r->connection->conn_config, &gnutls_module); - if(!ctxt) { - return DECLINED; + if (!ctxt) { + return DECLINED; } apr_table_setn(env, "HTTPS", "on"); - apr_table_setn(env, "GNUTLS_VERSION_INTERFACE", MOD_GNUTLS_VERSION); - apr_table_setn(env, "GNUTLS_VERSION_LIBRARY", LIBGNUTLS_VERSION); + apr_table_setn(env, "SSL_VERSION_LIBRARY", + "GnuTLS/" LIBGNUTLS_VERSION); + apr_table_setn(env, "SSL_VERSION_INTERFACE", + "mod_gnutls/" MOD_GNUTLS_VERSION); apr_table_setn(env, "SSL_PROTOCOL", - gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session))); + gnutls_protocol_get_name(gnutls_protocol_get_version + (ctxt->session))); + /* should have been called SSL_CIPHERSUITE instead */ apr_table_setn(env, "SSL_CIPHER", - gnutls_cipher_get_name(gnutls_cipher_get(ctxt->session))); + gnutls_cipher_suite_get_name(gnutls_kx_get + (ctxt->session), + gnutls_cipher_get(ctxt-> + session), + gnutls_mac_get(ctxt-> + session))); + + apr_table_setn(env, "SSL_COMPRESS_METHOD", + gnutls_compression_get_name(gnutls_compression_get + (ctxt->session))); - apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE"); + apr_table_setn(env, "SSL_SRP_USER", + gnutls_srp_server_get_username(ctxt->session)); - tmp = apr_psprintf(r->pool, "%d", - 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session))); + if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL) + apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE"); + + unsigned int key_size = + 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session)); + tmp = apr_psprintf(r->pool, "%u", key_size); apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp); apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp); + apr_table_setn(env, "SSL_CIPHER_EXPORT", + (key_size <= 40) ? "true" : "false"); + len = sizeof(sbuf); gnutls_session_get_id(ctxt->session, sbuf, &len); tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf)); apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp)); - /* TODO: There are many other env vars that we need to add */ - { - len = sizeof(buf); - gnutls_x509_crt_get_dn(ctxt->sc->cert_x509, buf, &len); - apr_table_setn(env, "SSL_SERVER_S_DN", apr_pstrmemdup(r->pool, buf, len)); - - len = sizeof(buf); - gnutls_x509_crt_get_issuer_dn(ctxt->sc->cert_x509, buf, &len); - apr_table_setn(env, "SSL_SERVER_I_DN", apr_pstrmemdup(r->pool, buf, len)); - } + if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) + mgs_add_common_cert_vars(r, ctxt->sc->certs_x509[0], 0, + ctxt->sc->export_certificates_enabled); + else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP) + mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_pgp, 0, + ctxt->sc->export_certificates_enabled); + return rv; } -int mgs_hook_authz(request_rec *r) +int mgs_hook_authz(request_rec * r) { int rv; - int status; mgs_handle_t *ctxt; mgs_dirconf_rec *dc = ap_get_module_config(r->per_dir_config, - &gnutls_module); - - ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module); - + &gnutls_module); + + ctxt = + ap_get_module_config(r->connection->conn_config, &gnutls_module); + if (!ctxt) { - return DECLINED; + return DECLINED; } if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "GnuTLS: Directory set to Ignore Client Certificate!"); - } - else { - if (ctxt->sc->client_verify_mode < dc->client_verify_mode) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "GnuTLS: Attempting to rehandshake with peer. %d %d", - ctxt->sc->client_verify_mode, dc->client_verify_mode); - - gnutls_certificate_server_set_request(ctxt->session, - dc->client_verify_mode); - - if (mgs_rehandshake(ctxt) != 0) { - return HTTP_FORBIDDEN; - } - } - else if (ctxt->sc->client_verify_mode == GNUTLS_CERT_IGNORE) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "GnuTLS: Directory set to Ignore Client Certificate!"); + } else { + if (ctxt->sc->client_verify_mode < dc->client_verify_mode) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "GnuTLS: Attempting to rehandshake with peer. %d %d", + ctxt->sc->client_verify_mode, + dc->client_verify_mode); + + gnutls_certificate_server_set_request(ctxt->session, + dc->client_verify_mode); + + if (mgs_rehandshake(ctxt) != 0) { + return HTTP_FORBIDDEN; + } + } else if (ctxt->sc->client_verify_mode == GNUTLS_CERT_IGNORE) { #if MOD_GNUTLS_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Peer is set to IGNORE"); + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Peer is set to IGNORE"); #endif - } - else { - rv = mgs_cert_verify(r, ctxt); - if (rv != DECLINED) { - return rv; - } - } + } else { + rv = mgs_cert_verify(r, ctxt); + if (rv != DECLINED) { + return rv; + } + } + } + + return DECLINED; +} + +/* variables that are not sent by default: + * + * SSL_CLIENT_CERT string PEM-encoded client certificate + * SSL_SERVER_CERT string PEM-encoded client certificate + */ + +/* side is either 0 for SERVER or 1 for CLIENT + */ +#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT") +static void +mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, + int export_certificates_enabled) +{ + unsigned char sbuf[64]; /* buffer to hold serials */ + char buf[AP_IOBUFSIZE]; + const char *tmp; + char *tmp2; + size_t len; + int ret, i; + + apr_table_t *env = r->subprocess_env; + + if (export_certificates_enabled != 0) { + char cert_buf[10 * 1024]; + len = sizeof(cert_buf); + + if (gnutls_x509_crt_export + (cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) + apr_table_setn(env, + apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL), + apr_pstrmemdup(r->pool, cert_buf, len)); + + } + + len = sizeof(buf); + gnutls_x509_crt_get_dn(cert, buf, &len); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_S_DN", NULL), + apr_pstrmemdup(r->pool, buf, len)); + + len = sizeof(buf); + gnutls_x509_crt_get_issuer_dn(cert, buf, &len); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_I_DN", NULL), + apr_pstrmemdup(r->pool, buf, len)); + + len = sizeof(sbuf); + gnutls_x509_crt_get_serial(cert, sbuf, &len); + tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf)); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_M_SERIAL", NULL), + apr_pstrdup(r->pool, tmp)); + + ret = gnutls_x509_crt_get_version(cert); + if (ret > 0) + apr_table_setn(env, + apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION", NULL), + apr_psprintf(r->pool, "%u", ret)); + + apr_table_setn(env, + apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL), "X.509"); + + tmp = + mgs_time2sz(gnutls_x509_crt_get_expiration_time + (cert), buf, sizeof(buf)); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL), + apr_pstrdup(r->pool, tmp)); + + tmp = + mgs_time2sz(gnutls_x509_crt_get_activation_time + (cert), buf, sizeof(buf)); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL), + apr_pstrdup(r->pool, tmp)); + + ret = gnutls_x509_crt_get_signature_algorithm(cert); + if (ret >= 0) { + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_SIG", NULL), + gnutls_sign_algorithm_get_name(ret)); + } + + ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL); + if (ret >= 0) { + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY", NULL), + gnutls_pk_algorithm_get_name(ret)); + } + /* export all the alternative names (DNS, RFC822 and URI) */ + for (i = 0; !(ret < 0); i++) { + len = 0; + ret = gnutls_x509_crt_get_subject_alt_name(cert, i, + NULL, &len, NULL); + + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) { + tmp2 = apr_palloc(r->pool, len + 1); + + ret = + gnutls_x509_crt_get_subject_alt_name(cert, i, tmp2, &len, + NULL); + tmp2[len] = 0; + + if (ret == GNUTLS_SAN_DNSNAME) { + apr_table_setn(env, + apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i), + apr_psprintf(r->pool, "DNSNAME:%s", tmp2)); + } else if (ret == GNUTLS_SAN_RFC822NAME) { + apr_table_setn(env, + apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i), + apr_psprintf(r->pool, "RFC822NAME:%s", tmp2)); + } else if (ret == GNUTLS_SAN_URI) { + apr_table_setn(env, + apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i), + apr_psprintf(r->pool, "URI:%s", tmp2)); + } else { + apr_table_setn(env, + apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i), + "UNSUPPORTED"); + } + } + } +} -static int mgs_cert_verify(request_rec *r, mgs_handle_t *ctxt) +static void +mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, + int export_certificates_enabled) { - const gnutls_datum_t* cert_list; - int cert_list_size; - gnutls_x509_crt_t cert; + unsigned char sbuf[64]; /* buffer to hold serials */ + char buf[AP_IOBUFSIZE]; + const char *tmp; + size_t len; + int ret; + + apr_table_t *env = r->subprocess_env; + if (export_certificates_enabled != 0) { + char cert_buf[10 * 1024]; + len = sizeof(cert_buf); + + if (gnutls_openpgp_crt_export + (cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0) + apr_table_setn(env, + apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL), + apr_pstrmemdup(r->pool, cert_buf, len)); + + } - cert_list = gnutls_certificate_get_peers(ctxt->session, &cert_list_size); + len = sizeof(buf); + gnutls_openpgp_crt_get_name(cert, 0, buf, &len); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_NAME", NULL), + apr_pstrmemdup(r->pool, buf, len)); + + len = sizeof(sbuf); + gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len); + tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf)); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_FINGERPRINT", NULL), + apr_pstrdup(r->pool, tmp)); + + ret = gnutls_openpgp_crt_get_version(cert); + if (ret > 0) + apr_table_setn(env, + apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION", NULL), + apr_psprintf(r->pool, "%u", ret)); + + apr_table_setn(env, + apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL), "OPENPGP"); + + tmp = + mgs_time2sz(gnutls_openpgp_crt_get_expiration_time + (cert), buf, sizeof(buf)); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL), + apr_pstrdup(r->pool, tmp)); + + tmp = + mgs_time2sz(gnutls_openpgp_crt_get_creation_time + (cert), buf, sizeof(buf)); + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL), + apr_pstrdup(r->pool, tmp)); + + ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL); + if (ret >= 0) { + apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY", NULL), + gnutls_pk_algorithm_get_name(ret)); + } + +} + +/* TODO: Allow client sending a X.509 certificate chain */ +static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) +{ + const gnutls_datum_t *cert_list; + unsigned int cert_list_size, status, expired; + int rv, ret; + union { + gnutls_x509_crt_t x509; + gnutls_openpgp_crt_t pgp; + } cert; + apr_time_t activation_time, expiration_time, cur_time; + + cert_list = + gnutls_certificate_get_peers(ctxt->session, &cert_list_size); if (cert_list == NULL || cert_list_size == 0) { - /* no certificate provided by the client, but one was required. */ - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Failed to Verify Peer: " - "Client did not submit a certificate"); - return HTTP_FORBIDDEN; + /* It is perfectly OK for a client not to send a certificate if on REQUEST mode + */ + if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST) + return OK; + + /* no certificate provided by the client, but one was required. */ + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Failed to Verify Peer: " + "Client did not submit a certificate"); + return HTTP_FORBIDDEN; } if (cert_list_size > 1) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Failed to Verify Peer: " - "Chained Client Certificates are not supported."); - return HTTP_FORBIDDEN; + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Failed to Verify Peer: " + "Chained Client Certificates are not supported."); + return HTTP_FORBIDDEN; } - gnutls_x509_crt_init(&cert); - gnutls_x509_crt_import(cert, &cert_chain[0], GNUTLS_X509_FMT_DER); + if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) { + gnutls_x509_crt_init(&cert.x509); + rv = gnutls_x509_crt_import(cert.x509, &cert_list[0], GNUTLS_X509_FMT_DER); + } else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP) { + gnutls_openpgp_crt_init(&cert.pgp); + rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0], GNUTLS_OPENPGP_FMT_RAW); + } else return HTTP_FORBIDDEN; + + if (rv < 0) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Failed to Verify Peer: " + "Failed to import peer certificates."); + ret = HTTP_FORBIDDEN; + goto exit; + } - rv = gnutls_x509_crt_verify(cert, ctxt->sc->ca_list, ctxt->sc->ca_list_size, 0, &status); + if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) { + apr_time_ansi_put(&expiration_time, + gnutls_x509_crt_get_expiration_time(cert.x509)); + apr_time_ansi_put(&activation_time, + gnutls_x509_crt_get_activation_time(cert.x509)); + + rv = gnutls_x509_crt_verify(cert.x509, ctxt->sc->ca_list, + ctxt->sc->ca_list_size, 0, &status); + } else { + apr_time_ansi_put(&expiration_time, + gnutls_openpgp_crt_get_expiration_time(cert.pgp)); + apr_time_ansi_put(&activation_time, + gnutls_openpgp_crt_get_creation_time(cert.pgp)); + + rv = gnutls_openpgp_crt_verify_ring(cert.pgp, ctxt->sc->pgp_list, + 0, &status); + } if (rv < 0) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Failed to Verify Peer: (%d) %s", - rv, gnutls_strerror(rv)); - return HTTP_FORBIDDEN; + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Failed to Verify Peer certificate: (%d) %s", + rv, gnutls_strerror(rv)); + if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND) + ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r, + "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?"); + ret = HTTP_FORBIDDEN; + goto exit; } - - if (status < 0) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Peer Status is invalid."); - return HTTP_FORBIDDEN; - } - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Could not find Signer for Peer Certificate"); - } - - if (status & GNUTLS_CERT_SIGNER_NOT_CA) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Could not find CA for Peer Certificate"); - } - - if (status & GNUTLS_CERT_INVALID) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Peer Certificate is invalid."); - return HTTP_FORBIDDEN; - } - else if (status & GNUTLS_CERT_REVOKED) { - ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, - "GnuTLS: Peer Certificate is revoked."); - return HTTP_FORBIDDEN; - } - - /* TODO: OpenPGP Certificates */ - if (gnutls_certificate_type_get(ctxt->session) != GNUTLS_CRT_X509) { - ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, - "GnuTLS: Only x509 is supported for client certificates"); - return HTTP_FORBIDDEN; - } - /* TODO: Further Verification. */ -// gnutls_x509_crt_get_expiration_time() < time -// gnutls_x509_crt_get_activation_time() > time -/// ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); - ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, - "GnuTLS: Verified Peer."); - } - - ap_add_common_vars(r); - mgs_hook_fixups(r); - status = mgs_authz_lua(r); - - if (status != 0) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "GnuTLS: FAILED Authorization Test"); - return HTTP_FORBIDDEN; + + /* TODO: X509 CRL Verification. */ + /* May add later if anyone needs it. + */ + /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */ + + expired = 0; + cur_time = apr_time_now(); + if (activation_time > cur_time) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Failed to Verify Peer: " + "Peer Certificate is not yet activated."); + expired = 1; } -} + if (expiration_time < cur_time) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Failed to Verify Peer: " + "Peer Certificate is expired."); + expired = 1; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Could not find Signer for Peer Certificate"); + } + + if (status & GNUTLS_CERT_SIGNER_NOT_CA) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Peer's Certificate signer is not a CA"); + } + if (status & GNUTLS_CERT_INVALID) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Peer Certificate is invalid."); + } else if (status & GNUTLS_CERT_REVOKED) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "GnuTLS: Peer Certificate is revoked."); + } + + if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) + mgs_add_common_cert_vars(r, cert.x509, 1, + ctxt->sc->export_certificates_enabled); + else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP) + mgs_add_common_pgpcert_vars(r, cert.pgp, 1, + ctxt->sc->export_certificates_enabled); + + { + /* days remaining */ + unsigned long remain = + (apr_time_sec(expiration_time) - + apr_time_sec(cur_time)) / 86400; + apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN", + apr_psprintf(r->pool, "%lu", remain)); + } + + if (status == 0 && expired == 0) { + apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY", "SUCCESS"); + ret = OK; + } else { + apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY", "FAILED"); + if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST) + ret = OK; + else + ret = HTTP_FORBIDDEN; + } + + exit: + if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) + gnutls_x509_crt_deinit(cert.x509); + else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP) + gnutls_openpgp_crt_deinit(cert.pgp); + return ret; + + +} diff --git a/src/gnutls_io.c b/src/gnutls_io.c index 14e7cf6..753c87b 100644 --- a/src/gnutls_io.c +++ b/src/gnutls_io.c @@ -543,8 +543,7 @@ apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket *bucket = APR_BRIGADE_FIRST(bb); if (AP_BUCKET_IS_EOC(bucket)) { do { - ret = gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, - GNUTLS_A_CLOSE_NOTIFY); + ret = gnutls_bye( ctxt->session, GNUTLS_SHUT_WR); } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); apr_bucket_copy(bucket, &e); @@ -556,7 +555,6 @@ apr_status_t mgs_filter_output(ap_filter_t * f, } apr_brigade_cleanup(ctxt->output_bb); - gnutls_bye(ctxt->session, GNUTLS_SHUT_WR); gnutls_deinit(ctxt->session); continue; @@ -568,6 +566,7 @@ apr_status_t mgs_filter_output(ap_filter_t * f, apr_brigade_cleanup(ctxt->output_bb); return status; } + apr_brigade_cleanup(ctxt->output_bb); continue; } @@ -599,7 +598,7 @@ apr_status_t mgs_filter_output(ap_filter_t * f, ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->output_rc, ctxt->c->base_server, "GnuTLS: Error writing data." - " (%d) '%s'", ret, gnutls_strerror(ret)); + " (%d) '%s'", (int)ret, gnutls_strerror(ret)); if (ctxt->output_rc == APR_SUCCESS) { ctxt->output_rc = APR_EGENERAL; } diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c index 5ca198d..014bfc8 100644 --- a/src/mod_gnutls.c +++ b/src/mod_gnutls.c @@ -63,15 +63,55 @@ static const command_rec mgs_config_cmds[] = { AP_INIT_TAKE1("GnuTLSClientCAFile", mgs_set_client_ca_file, NULL, RSRC_CONF, - "Set the CA File for Client Certificates"), + "Set the CA File to verify Client Certificates"), + AP_INIT_TAKE1("GnuTLSX509CAFile", mgs_set_client_ca_file, + NULL, + RSRC_CONF, + "Set the CA File to verify Client Certificates"), + AP_INIT_TAKE1("GnuTLSPGPKeyringFile", mgs_set_keyring_file, + NULL, + RSRC_CONF, + "Set the Keyring File to verify Client Certificates"), + AP_INIT_TAKE1("GnuTLSDHFile", mgs_set_dh_file, + NULL, + RSRC_CONF, + "Set the file to read Diffie Hellman parameters from"), + AP_INIT_TAKE1("GnuTLSRSAFile", mgs_set_rsa_export_file, + NULL, + RSRC_CONF, + "Set the file to read RSA-EXPORT parameters from"), AP_INIT_TAKE1("GnuTLSCertificateFile", mgs_set_cert_file, NULL, RSRC_CONF, - "SSL Server Key file"), + "SSL Server X509 Certificate file"), AP_INIT_TAKE1("GnuTLSKeyFile", mgs_set_key_file, NULL, RSRC_CONF, - "SSL Server Certificate file"), + "SSL Server X509 Private Key file"), + AP_INIT_TAKE1("GnuTLSX509CertificateFile", mgs_set_cert_file, + NULL, + RSRC_CONF, + "SSL Server X509 Certificate file"), + AP_INIT_TAKE1("GnuTLSX509KeyFile", mgs_set_key_file, + NULL, + RSRC_CONF, + "SSL Server X509 Private Key file"), + AP_INIT_TAKE1("GnuTLSPGPCertificateFile", mgs_set_pgpcert_file, + NULL, + RSRC_CONF, + "SSL Server PGP Certificate file"), + AP_INIT_TAKE1("GnuTLSPGPKeyFile", mgs_set_pgpkey_file, + NULL, + RSRC_CONF, + "SSL Server PGP Private key file"), + AP_INIT_TAKE1("GnuTLSSRPPasswdFile", mgs_set_srp_tpasswd_file, + NULL, + RSRC_CONF, + "SSL Server SRP Password Conf file"), + AP_INIT_TAKE1("GnuTLSSRPPasswdConfFile", mgs_set_srp_tpasswd_conf_file, + NULL, + RSRC_CONF, + "SSL Server SRP Parameters file"), AP_INIT_TAKE1("GnuTLSCacheTimeout", mgs_set_cache_timeout, NULL, RSRC_CONF, @@ -80,10 +120,19 @@ static const command_rec mgs_config_cmds[] = { NULL, RSRC_CONF, "Cache Configuration"), + AP_INIT_RAW_ARGS("GnuTLSPriorities", mgs_set_priorities, + NULL, + RSRC_CONF, + "The priorities to enable (ciphers, Key exchange, macs, compression)."), AP_INIT_TAKE1("GnuTLSEnable", mgs_set_enabled, NULL, RSRC_CONF, "Whether this server has GnuTLS Enabled. Default: Off"), + AP_INIT_TAKE1("GnuTLSExportCertificates", mgs_set_export_certificates_enabled, + NULL, + RSRC_CONF, + "Whether to export PEM encoded certificates to CGIs. Default: Off"), +#if 0 AP_INIT_RAW_ARGS("