From 9a2ed619f22489dc9976851b774ec294c302a603 Mon Sep 17 00:00:00 2001 From: Nokis Mavrogiannopoulos Date: Wed, 20 Feb 2008 18:50:41 +0000 Subject: added tags --- Makefile.am | 4 +- NEWS | 13 +++ NOTICE | 3 + README | 44 +++++--- README.ENV | 8 +- configure.ac | 18 ++-- include/mod_gnutls.h.in | 22 +++- src/gnutls_cache.c | 84 ++++++++------- src/gnutls_config.c | 131 +++++++++++++++++++++-- src/gnutls_hooks.c | 273 +++++++++++++++++++++++++++++++++++++----------- src/mod_gnutls.c | 28 ++++- 11 files changed, 493 insertions(+), 135 deletions(-) diff --git a/Makefile.am b/Makefile.am index d61ebd3..a19e755 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,11 +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.ENV NEWS \ NOTICE LICENSE autogen.sh -SUBDIRS = src data +SUBDIRS = src ACLOCAL_AMFLAGS = -I m4 diff --git a/NEWS b/NEWS index e4b908d..a9fe9fd 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,16 @@ +** 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. 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 85418de..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 @@ -54,31 +54,26 @@ 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 500 -GnuTLSProtocols TLS1.1 TLS1.0 SSL3.0 +GnuTLSCacheTimeout 600 NameVirtualHost 1.2.3.4:443 Servername server.com:443 GnuTLSEnable on - GnuTLSCiphers AES-128-CBC 3DES-CBC ARCFOUR-128 - GnuTLSKeyExchangeAlgorithms RSA DHE-RSA DHE-DSS SRP SRP-RSA SRP-DSS - GnuTLSMACAlgorithms SHA1 MD5 - GnuTLSCompressionMethods NULL + GnuTLSPriority NORMAL # To export exactly the same environment variables as mod_ssl to CGI scripts. GNUTLSExportCertificates on - GnuTLSCertificateFile /etc/apache2/server-cert.pem - GnuTLSKeyFile /etc/apache2/server-key.pem + 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 @@ -88,6 +83,29 @@ NameVirtualHost 1.2.3.4:443 # GnuTLSClientVerify could be ignore or require. The GnuTLSClientCAFile # contains the CAs to verify client certificates. GnuTLSClientVerify request - GnuTLSClientCAFile ca.pem + 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 index c055dfe..34dbcf6 100644 --- a/README.ENV +++ b/README.ENV @@ -19,7 +19,7 @@ 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_SAN%: These will contain the alternative names of the client certificate +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. @@ -30,13 +30,13 @@ 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_S_TYPE: The certificate type can be X.509 or OPENPGP. +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_SAN%: These will contain the alternative names of the server certificate +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. @@ -45,5 +45,5 @@ 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_S_TYPE: The certificate type can be X.509 or OPENPGP. +SSL_SERVER_CERT_TYPE: The certificate type can be X.509 or OPENPGP. diff --git a/configure.ac b/configure.ac index c401940..0cdcdd9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ dnl -AC_INIT(mod_gnutls, 0.4.1) +AC_INIT(mod_gnutls, 0.5.0-alpha) OOO_CONFIG_NICE(config.nice) MOD_GNUTLS_VERSION=AC_PACKAGE_VERSION AC_PREREQ(2.53) @@ -28,8 +28,14 @@ CHECK_APACHE(,$AP_VERSION, dnl LIBTOOL="`${APR_CONFIG} --apr-libtool`" dnl AC_SUBST(LIBTOOL) -MIN_TLS_VERSION=2.1.7 -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/ +*** +]])) dnl CHECK_LUA() @@ -37,13 +43,13 @@ 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 6a311a3..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 @@ -80,7 +82,10 @@ typedef struct /* The maximum number of client CA certificates allowed. */ #define MAX_CA_CRTS 128 -#define MAX_CIPHERS 16 + +/* The maximum number of certificates to send in a chain + */ +#define MAX_CHAIN_SIZE 8 typedef struct { @@ -88,8 +93,11 @@ typedef struct 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; /* whether to send the PEM encoded certificates * to CGIs @@ -104,6 +112,7 @@ typedef struct 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; @@ -250,6 +259,12 @@ const char *mgs_set_cert_file(cmd_parms * parms, void *dummy, 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); @@ -262,6 +277,9 @@ 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, diff --git a/src/gnutls_cache.c b/src/gnutls_cache.c index 86b843e..83e7bb5 100644 --- a/src/gnutls_cache.c +++ b/src/gnutls_cache.c @@ -34,18 +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) -#if 0 -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,7 +51,27 @@ static char *gnutls_session_id2sz(unsigned char *id, int idlen, *cp = '\0'; return str; } -#endif + + +/* 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 buf[STR_SESSION_LEN]; +char *sz; + + 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) @@ -70,24 +88,23 @@ char *mgs_time2sz(time_t in_time, char *str, int strsize) return str; } -char *mgs_session_id2sz(unsigned char *id, int idlen, - char *str, int strsize) +#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 *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 NULL; + + return apr_psprintf(c->pool, MC_TAG"%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz); } - -#if HAVE_APR_MEMCACHE - /** * GnuTLS Session Cache using libmemcached * @@ -184,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; @@ -211,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; } @@ -252,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; @@ -366,8 +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 = (void*)key.data; - dbmkey.dsize = key.size; + 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); @@ -413,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); @@ -467,9 +481,9 @@ 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; + + 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); diff --git a/src/gnutls_config.c b/src/gnutls_config.c index 7b5a42b..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. @@ -151,15 +152,10 @@ const char *mgs_set_cert_file(cmd_parms * parms, void *dummy, "Certificate '%s'", file); } - ret = gnutls_x509_crt_init(&sc->cert_x509); - if (ret < 0) { - return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize" - ": (%d) %s", ret, gnutls_strerror(ret)); - } - + sc->certs_x509_num = MAX_CHAIN_SIZE; ret = - gnutls_x509_crt_import(sc->cert_x509, &data, GNUTLS_X509_FMT_PEM); - if (ret != 0) { + 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)); @@ -207,6 +203,84 @@ const char *mgs_set_key_file(cmd_parms * parms, void *dummy, 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) { @@ -355,6 +429,44 @@ const char *mgs_set_client_ca_file(cmd_parms * parms, void *dummy, return NULL; } +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 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); + return NULL; +} + const char *mgs_set_enabled(cmd_parms * parms, void *dummy, const char *arg) { @@ -440,7 +552,8 @@ void *mgs_config_server_create(apr_pool_t * p, server_rec * s) 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"); diff --git a/src/gnutls_hooks.c b/src/gnutls_hooks.c index 4364add..26917b8 100644 --- a/src/gnutls_hooks.c +++ b/src/gnutls_hooks.c @@ -36,7 +36,10 @@ 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 cert, +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); @@ -68,9 +71,23 @@ int ret; mpm_is_threaded = 0; #endif + 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) /* FIXME: can we print here? */ - exit(ret); + 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); @@ -82,19 +99,18 @@ int ret; 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; } -/* We don't support openpgp certificates, yet */ -const static int cert_type_prio[2] = { GNUTLS_CRT_X509, 0 }; - 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); @@ -126,17 +142,22 @@ static int mgs_select_virtual_server_cb(gnutls_session_t session) * negotiation. */ ret = gnutls_priority_set(session, ctxt->sc->priorities); - gnutls_certificate_type_set_priority(session, cert_type_prio); - - /* actually it shouldn't fail since we have checked at startup */ if (ret < 0) return ret; - /* allow separate caches per virtual host. Actually allowing the same is a - * bad idea, since they might have different security requirements. + /* If both certificate types are not present disallow them from + * being negotiated. */ - mgs_cache_session_init(ctxt); + 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; } @@ -147,15 +168,31 @@ static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret) ctxt = gnutls_transport_get_ptr(session); - ret->type = GNUTLS_CRT_X509; - ret->ncerts = 1; - ret->deinit_all = 0; + 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; + + 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; + + } - ret->cert.x509 = &ctxt->sc->cert_x509; - ret->key.x509 = ctxt->sc->privkey_x509; - return 0; + return GNUTLS_E_INTERNAL_ERROR; } +/* 2048-bit group parameters from SRP specification */ const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n" "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n" "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n" @@ -171,7 +208,7 @@ const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n" * Returns negative on error. */ static int read_crt_cn(server_rec * s, apr_pool_t * p, - gnutls_x509_crt cert, char **cert_cn) + gnutls_x509_crt_t cert, char **cert_cn) { int rv = 0, i; size_t data_len; @@ -179,6 +216,7 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p, *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); @@ -189,8 +227,8 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p, GNUTLS_OID_X520_COMMON_NAME, 0, 0, *cert_cn, &data_len); } else { /* No CN return subject alternative name */ - ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, - "No common name found in certificate for '%s:%d'. Looking for 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 */ @@ -218,9 +256,33 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p, } 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; + + + *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); + } + return rv; } + int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * base_server) @@ -334,7 +396,7 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, } } - if (sc->cert_x509 == NULL + 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 " @@ -353,7 +415,10 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, } if (sc->enabled == GNUTLS_ENABLED_TRUE) { - rv = read_crt_cn(s, p, sc->cert_x509, &sc->cert_cn); + 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'!", @@ -482,15 +547,6 @@ mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session) 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); @@ -591,6 +647,8 @@ static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c) gnutls_handshake_set_post_client_hello_function(ctxt->session, mgs_select_virtual_server_cb); + mgs_cache_session_init(ctxt); + return ctxt; } @@ -686,7 +744,11 @@ int mgs_hook_fixups(request_rec * r) tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf)); apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp)); - mgs_add_common_cert_vars(r, ctxt->sc->cert_x509, 0, + 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; @@ -748,7 +810,7 @@ int mgs_hook_authz(request_rec * r) */ #define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT") static void -mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side, +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 */ @@ -795,7 +857,7 @@ mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side, apr_psprintf(r->pool, "%u", ret)); apr_table_setn(env, - apr_pstrcat(r->pool, MGS_SIDE, "_S_TYPE", NULL), "X.509"); + apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL), "X.509"); tmp = mgs_time2sz(gnutls_x509_crt_get_expiration_time @@ -837,34 +899,99 @@ mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side, if (ret == GNUTLS_SAN_DNSNAME) { apr_table_setn(env, - apr_psprintf(r->pool, "%s_S_SAN%u", MGS_SIDE, i), + 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_SAN%u", MGS_SIDE, i), + 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_SAN%u", MGS_SIDE, i), + 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_SAN%u", MGS_SIDE, i), + apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i), "UNSUPPORTED"); } } } +} +static void +mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, + int export_certificates_enabled) +{ + 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)); + + } + + 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; - gnutls_x509_crt_t cert; + union { + gnutls_x509_crt_t x509; + gnutls_openpgp_crt_t pgp; + } cert; apr_time_t activation_time, expiration_time, cur_time; cert_list = @@ -890,32 +1017,56 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) return HTTP_FORBIDDEN; } - gnutls_x509_crt_init(&cert); - rv = gnutls_x509_crt_import(cert, &cert_list[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, + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "GnuTLS: Failed to Verify Peer: " "Failed to import peer certificates."); - ret = HTTP_FORBIDDEN; - goto exit; + ret = HTTP_FORBIDDEN; + goto exit; } - apr_time_ansi_put(&expiration_time, - gnutls_x509_crt_get_expiration_time(cert)); - apr_time_ansi_put(&activation_time, - gnutls_x509_crt_get_activation_time(cert)); + 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, ctxt->sc->ca_list, + 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 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; } + /* 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) { @@ -950,16 +1101,11 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) "GnuTLS: Peer Certificate is revoked."); } - /* TODO: Further Verification. */ - /* Revocation is X.509 non workable paradigm, I really doubt implementation - * is worth doing --nmav - */ -/// ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); - -// mgs_hook_fixups(r); -// rv = mgs_authz_lua(r); - - mgs_add_common_cert_vars(r, cert, 1, + 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); { @@ -983,7 +1129,10 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) } exit: - gnutls_x509_crt_deinit(cert); + 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/mod_gnutls.c b/src/mod_gnutls.c index a6e5528..014bfc8 100644 --- a/src/mod_gnutls.c +++ b/src/mod_gnutls.c @@ -64,6 +64,14 @@ static const command_rec mgs_config_cmds[] = { NULL, RSRC_CONF, "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, @@ -75,11 +83,27 @@ static const command_rec mgs_config_cmds[] = { 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 SRP Password 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, -- cgit