From 75636faa679422cb7e360167d03163ca1985f772 Mon Sep 17 00:00:00 2001 From: Nokis Mavrogiannopoulos Date: Wed, 20 Feb 2008 18:50:41 +0000 Subject: added tags --- src/gnutls_cache.c | 84 +++++++++------- src/gnutls_config.c | 131 +++++++++++++++++++++++-- src/gnutls_hooks.c | 273 ++++++++++++++++++++++++++++++++++++++++------------ src/mod_gnutls.c | 28 +++++- 4 files changed, 408 insertions(+), 108 deletions(-) (limited to 'src') 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