From 75636faa679422cb7e360167d03163ca1985f772 Mon Sep 17 00:00:00 2001 From: Nokis Mavrogiannopoulos Date: Wed, 20 Feb 2008 18:50:41 +0000 Subject: added tags --- (limited to 'src/gnutls_hooks.c') 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; -- cgit v0.9.2