source: mod_gnutls/src/gnutls_hooks.c @ 8873a06

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 8873a06 was 87f1ed2, checked in by Thomas Klute <thomas2.klute@…>, 4 years ago

Allow loading of an additional PKCS #11 provider library

When using PKCS #11, it may not be desirable to add the PKCS #11 module
to be used by mod_gnutls to the system wide config, and we definitely
cannot demand it for tests.

To work around such problems, add the new configuration parameter
"GnuTLSP11Module", which may contain the path of a library to load. Note
that the value is only used if present in the base server configuration
(not a virtual host), and that the library is used in addition to
system defaults (if any).

  • Property mode set to 100644
File size: 66.1 KB
RevLine 
[c301152]1/**
2 *  Copyright 2004-2005 Paul Querna
[e183628]3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
[765cac2]5 *  Copyright 2013-2014 Daniel Kahn Gillmor
[c301152]6 *
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 */
20
21#include "mod_gnutls.h"
22#include "http_vhost.h"
[84cb5b2]23#include "ap_mpm.h"
[b4739cd]24#include "mod_status.h"
[c301152]25
[07889ab]26#ifdef ENABLE_MSVA
27#include <msv/msv.h>
28#endif
[0499540]29
[55dc3f0]30#ifdef APLOG_USE_MODULE
31APLOG_USE_MODULE(gnutls);
32#endif
33
[c301152]34#if !USING_2_1_RECENT
35extern server_rec *ap_server_conf;
36#endif
37
38#if MOD_GNUTLS_DEBUG
[7bebb42]39static apr_file_t *debug_log_fp;
[c301152]40#endif
41
[b429e4c]42#define IS_PROXY_STR(c) \
43    ((c->is_proxy == GNUTLS_ENABLED_TRUE) ? "proxy " : "")
44
[b8df283]45static gnutls_datum_t session_ticket_key = {NULL, 0};
[84cb5b2]46
[7bebb42]47static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt);
48/* use side==0 for server and side==1 for client */
[fd82e59]49static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size);
50static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, size_t export_cert_size);
[b4739cd]51static int mgs_status_hook(request_rec *r, int flags);
[fd82e59]52#ifdef ENABLE_MSVA
53static const char* mgs_x509_construct_uid(request_rec * pool, gnutls_x509_crt_t cert);
54#endif
[0de1839]55static int load_proxy_x509_credentials(server_rec *s);
[e183628]56
[3b4c0d0]57/* Pool Cleanup Function */
[fd82e59]58apr_status_t mgs_cleanup_pre_config(void *data __attribute__((unused))) {
[3b4c0d0]59        /* Free all session data */
[e183628]60    gnutls_free(session_ticket_key.data);
61    session_ticket_key.data = NULL;
62    session_ticket_key.size = 0;
[3b4c0d0]63        /* Deinitialize GnuTLS Library */
[e183628]64    gnutls_global_deinit();
65    return APR_SUCCESS;
[c301152]66}
67
[3b4c0d0]68/* Logging Function for Maintainers */
[c301152]69#if MOD_GNUTLS_DEBUG
[e183628]70static void gnutls_debug_log_all(int level, const char *str) {
[9ecd212]71    apr_file_printf(debug_log_fp, "<%d> %s", level, str);
[c301152]72}
[a208cd3]73#define _gnutls_log apr_file_printf
74#else
[e183628]75#define _gnutls_log(...)
[c301152]76#endif
77
[07889ab]78static const char* mgs_readable_cvm(mgs_client_verification_method_e m) {
79    switch(m) {
80    case mgs_cvm_unset:
81        return "unset";
82    case mgs_cvm_cartel:
83        return "cartel";
84    case mgs_cvm_msva:
85        return "msva";
86    }
87    return "unknown";
88}
89
[3b4c0d0]90/* Pre-Configuration HOOK: Runs First */
[fd82e59]91int mgs_hook_pre_config(apr_pool_t * pconf, apr_pool_t * plog, apr_pool_t * ptemp __attribute__((unused))) {
[26b08fd]92
[3b4c0d0]93/* Maintainer Logging */
94#if MOD_GNUTLS_DEBUG
95    apr_file_open(&debug_log_fp, "/tmp/gnutls_debug", APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pconf);
[e183628]96    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
97    gnutls_global_set_log_level(9);
98    gnutls_global_set_log_function(gnutls_debug_log_all);
[37f8282]99    _gnutls_log(debug_log_fp, "gnutls: %s\n", gnutls_check_version(NULL));
[671b64f]100#endif
[3b4c0d0]101
[33826c5]102    int ret;
[26b08fd]103
[3b4c0d0]104        /* Check for required GnuTLS Library Version */
[932b68e]105    if (gnutls_check_version(LIBGNUTLS_VERSION) == NULL) {
[cb5188f]106                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_check_version() failed. Required: "
[9ecd212]107                                        "gnutls-%s Found: gnutls-%s", LIBGNUTLS_VERSION, gnutls_check_version(NULL));
[421ef1c]108        return DONE;
[e183628]109    }
[e02dd8c]110
[3b4c0d0]111        /* Initialize GnuTLS Library */
[e183628]112    ret = gnutls_global_init();
113    if (ret < 0) {
[9ecd212]114                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_global_init: %s", gnutls_strerror(ret));
[421ef1c]115                return DONE;
[e183628]116    }
[2b3a248]117
[3b4c0d0]118        /* Generate a Session Key */
[e183628]119    ret = gnutls_session_ticket_key_generate(&session_ticket_key);
120    if (ret < 0) {
[9ecd212]121                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_session_ticket_key_generate: %s", gnutls_strerror(ret));
[421ef1c]122                return DONE;
[e183628]123    }
[e5bbda4]124
[b4739cd]125    AP_OPTIONAL_HOOK(status_hook, mgs_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
126
[3b4c0d0]127        /* Register a pool clean-up function */
[421ef1c]128    apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, apr_pool_cleanup_null);
[c301152]129
[e183628]130    return OK;
[c301152]131}
132
[e183628]133static int mgs_select_virtual_server_cb(gnutls_session_t session) {
[3b4c0d0]134
135    mgs_handle_t *ctxt = NULL;
136    mgs_srvconf_rec *tsc = NULL;
[9180a60]137    int ret = 0;
[7bebb42]138
[e183628]139    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[26b08fd]140
[e183628]141    ctxt = gnutls_transport_get_ptr(session);
[7bebb42]142
[e183628]143    /* find the virtual server */
144    tsc = mgs_find_sni_server(session);
[7bebb42]145
[8985a6b]146    if (tsc != NULL) {
147        // Found a TLS vhost based on the SNI from the client; use it instead.
[e183628]148        ctxt->sc = tsc;
[3b4c0d0]149        }
[7bebb42]150
[3b4c0d0]151    gnutls_certificate_server_set_request(session, ctxt->sc->client_verify_mode);
[7bebb42]152
[beb14d9]153    /* Set x509 credentials */
[3b4c0d0]154    gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctxt->sc->certs);
[beb14d9]155    /* Set Anon credentials */
[3b4c0d0]156    gnutls_credentials_set(session, GNUTLS_CRD_ANON, ctxt->sc->anon_creds);
[7bebb42]157
[787dab7]158#ifdef ENABLE_SRP
[3b4c0d0]159        /* Set SRP credentials */
160    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
161        gnutls_credentials_set(session, GNUTLS_CRD_SRP, ctxt->sc->srp_creds);
[e183628]162    }
[787dab7]163#endif
[7bebb42]164
[e183628]165    /* update the priorities - to avoid negotiating a ciphersuite that is not
166     * enabled on this virtual server. Note that here we ignore the version
167     * negotiation.
168     */
[3b4c0d0]169
[b668622]170    ret = gnutls_priority_set(session, ctxt->sc->priorities);
[e183628]171    /* actually it shouldn't fail since we have checked at startup */
[9180a60]172    return ret;
[3b4c0d0]173
[7bebb42]174}
175
[671b64f]176static int cert_retrieve_fn(gnutls_session_t session,
[259e835]177                            const gnutls_datum_t * req_ca_rdn __attribute__((unused)),
178                            int nreqs __attribute__((unused)),
179                            const gnutls_pk_algorithm_t * pk_algos __attribute__((unused)),
180                            int pk_algos_length __attribute__((unused)),
181                            gnutls_pcert_st **pcerts,
182                            unsigned int *pcert_length,
[031acac]183                            gnutls_privkey_t *privkey)
184{
185    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[3b4c0d0]186
[031acac]187    mgs_handle_t *ctxt;
[3b4c0d0]188
189    if (session == NULL) {
190                // ERROR INVALID SESSION
191        return -1;
[031acac]192    }
193
[c0dd3ab]194    ctxt = gnutls_transport_get_ptr(session);
195
196    if (gnutls_certificate_type_get(session) == GNUTLS_CRT_X509) {
[3b4c0d0]197                // X509 CERTIFICATE
[031acac]198        *pcerts = ctxt->sc->certs_x509_chain;
199        *pcert_length = ctxt->sc->certs_x509_chain_num;
200        *privkey = ctxt->sc->privkey_x509;
[e183628]201        return 0;
[9180a60]202    } else if (gnutls_certificate_type_get(session) == GNUTLS_CRT_OPENPGP) {
[3b4c0d0]203                // OPENPGP CERTIFICATE
[031acac]204        *pcerts = ctxt->sc->cert_pgp;
205        *pcert_length = 1;
206        *privkey = ctxt->sc->privkey_pgp;
[e183628]207        return 0;
[3b4c0d0]208    } else {
209                // UNKNOWN CERTIFICATE
210            return -1;
211        }
[7bebb42]212}
213
[fd73a08]214/* Read the common name or the alternative name of the certificate.
215 * We only support a single name per certificate.
216 *
217 * Returns negative on error.
218 */
[3b4c0d0]219static int read_crt_cn(server_rec * s, apr_pool_t * p, gnutls_x509_crt_t cert, char **cert_cn) {
220
[e183628]221    int rv = 0, i;
222    size_t data_len;
223
224
225    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
226    *cert_cn = NULL;
227
228    data_len = 0;
[3b4c0d0]229    rv = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, NULL, &data_len);
[e183628]230
231    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
232        *cert_cn = apr_palloc(p, data_len);
233        rv = gnutls_x509_crt_get_dn_by_oid(cert,
234                GNUTLS_OID_X520_COMMON_NAME,
235                0, 0, *cert_cn,
236                &data_len);
237    } else { /* No CN return subject alternative name */
238        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
239                "No common name found in certificate for '%s:%d'. Looking for subject alternative name...",
240                s->server_hostname, s->port);
241        rv = 0;
242        /* read subject alternative name */
243        for (i = 0; !(rv < 0); i++) {
244            data_len = 0;
245            rv = gnutls_x509_crt_get_subject_alt_name(cert, i,
246                    NULL,
247                    &data_len,
248                    NULL);
249
250            if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER
251                    && data_len > 1) {
252                /* FIXME: not very efficient. What if we have several alt names
253                 * before DNSName?
254                 */
255                *cert_cn = apr_palloc(p, data_len + 1);
256
257                rv = gnutls_x509_crt_get_subject_alt_name
258                        (cert, i, *cert_cn, &data_len, NULL);
259                (*cert_cn)[data_len] = 0;
260
261                if (rv == GNUTLS_SAN_DNSNAME)
262                    break;
263            }
264        }
265    }
266
267    return rv;
[e5bbda4]268}
269
270static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p,
[e183628]271        gnutls_openpgp_crt_t cert, char **cert_cn) {
272    int rv = 0;
273    size_t data_len;
[e5bbda4]274
275
[e183628]276    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
277    *cert_cn = NULL;
[e5bbda4]278
[e183628]279    data_len = 0;
280    rv = gnutls_openpgp_crt_get_name(cert, 0, NULL, &data_len);
[e5bbda4]281
[e183628]282    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
283        *cert_cn = apr_palloc(p, data_len);
284        rv = gnutls_openpgp_crt_get_name(cert, 0, *cert_cn,
285                &data_len);
286    } else { /* No CN return subject alternative name */
287        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
288                "No name found in PGP certificate for '%s:%d'.",
289                s->server_hostname, s->port);
290    }
[fd73a08]291
[e183628]292    return rv;
[fd73a08]293}
[7bebb42]294
[fd82e59]295int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog __attribute__((unused)), apr_pool_t * ptemp __attribute__((unused)), server_rec * base_server) {
[3b4c0d0]296
[e183628]297    int rv;
298    server_rec *s;
299    gnutls_dh_params_t dh_params = NULL;
300    mgs_srvconf_rec *sc;
301    mgs_srvconf_rec *sc_base;
302    void *data = NULL;
303    const char *userdata_key = "mgs_init";
304
305    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[3b4c0d0]306
307    apr_pool_userdata_get(&data, userdata_key, base_server->process->pool);
[e183628]308    if (data == NULL) {
[3b4c0d0]309        apr_pool_userdata_set((const void *) 1, userdata_key, apr_pool_cleanup_null, base_server->process->pool);
[e183628]310    }
311
312    s = base_server;
[3b4c0d0]313    sc_base = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
[e183628]314
315
316    rv = mgs_cache_post_config(p, s, sc_base);
317    if (rv != 0) {
318        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
319                "GnuTLS: Post Config for GnuTLSCache Failed."
320                " Shutting Down.");
321        exit(-1);
322    }
323
[87f1ed2]324    /* Load additional PKCS #11 module, if requested */
325    if (sc_base->p11_module != NULL)
326    {
327        rv = gnutls_pkcs11_add_provider(sc_base->p11_module, NULL);
328        if (rv != GNUTLS_E_SUCCESS)
329            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
330                         "GnuTLS: Loading PKCS #11 provider module %s "
331                         "failed: %s (%d).",
332                         sc_base->p11_module, gnutls_strerror(rv), rv);
333    }
334
[e183628]335    for (s = base_server; s; s = s->next) {
[3b4c0d0]336        sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
[e183628]337        sc->cache_type = sc_base->cache_type;
338        sc->cache_config = sc_base->cache_config;
[040387c]339        sc->cache_timeout = sc_base->cache_timeout;
340
[031acac]341        rv = mgs_load_files(p, s);
342        if (rv != 0) {
343            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
344                "GnuTLS: Loading required files failed."
345                " Shutting Down.");
346            exit(-1);
347        }
348
[040387c]349        /* defaults for unset values: */
350        if (sc->enabled == GNUTLS_ENABLED_UNSET)
351            sc->enabled = GNUTLS_ENABLED_FALSE;
[7d1ab49]352        if (sc->tickets == GNUTLS_ENABLED_UNSET)
[040387c]353            sc->tickets = GNUTLS_ENABLED_TRUE;
[2aaf4f5]354        if (sc->export_certificates_size < 0)
355            sc->export_certificates_size = 0;
[040387c]356        if (sc->client_verify_mode ==  -1)
357            sc->client_verify_mode = GNUTLS_CERT_IGNORE;
[cf2b905]358        if (sc->client_verify_method ==  mgs_cvm_unset)
359            sc->client_verify_method = mgs_cvm_cartel;
[040387c]360
[e183628]361        /* Check if the priorities have been set */
[33826c5]362        if (sc->priorities == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
[e183628]363            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
364                    "GnuTLS: Host '%s:%d' is missing the GnuTLSPriorities directive!",
365                    s->server_hostname, s->port);
366            exit(-1);
367        }
368
[3b4c0d0]369        /* Check if DH params have been set per host */
[410d216]370        if (sc->dh_params != NULL) {
371            gnutls_certificate_set_dh_params(sc->certs, sc->dh_params);
[671b64f]372            gnutls_anon_set_server_dh_params(sc->anon_creds, sc->dh_params);
[410d216]373        } else if (dh_params) {
374            gnutls_certificate_set_dh_params(sc->certs, dh_params);
[671b64f]375            gnutls_anon_set_server_dh_params(sc->anon_creds, dh_params);
[e183628]376        }
377
[2cde8111]378        /* The call after this comment is a workaround for bug in
379         * gnutls_certificate_set_retrieve_function2 that ignores
380         * supported certificate types. Should be fixed in GnuTLS
381         * 3.3.12.
382         *
383         * Details:
384         * https://lists.gnupg.org/pipermail/gnutls-devel/2015-January/007377.html
385         * Workaround from:
[d04f7da]386         * https://github.com/vanrein/tlspool/commit/4938102d3d1b086491d147e6c8e4e2a02825fc12 */
[2cde8111]387#if GNUTLS_VERSION_NUMBER < 0x030312
388        gnutls_certificate_set_retrieve_function(sc->certs, (void *) exit);
[787dab7]389#endif
[7bebb42]390
[031acac]391        gnutls_certificate_set_retrieve_function2(sc->certs, cert_retrieve_fn);
[7bebb42]392
[2b76a9c]393        if ((sc->certs_x509_chain == NULL || sc->certs_x509_chain_num < 1) &&
394            sc->cert_pgp == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
[671b64f]395                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
[031acac]396                                                "GnuTLS: Host '%s:%d' is missing a Certificate File!",
[3b4c0d0]397                                                s->server_hostname, s->port);
[e183628]398            exit(-1);
399        }
[2b76a9c]400        if (sc->enabled == GNUTLS_ENABLED_TRUE &&
[031acac]401            ((sc->certs_x509_chain_num > 0 && sc->privkey_x509 == NULL) ||
402             (sc->cert_crt_pgp[0] != NULL && sc->privkey_pgp == NULL))) {
[671b64f]403                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
[031acac]404                                                "GnuTLS: Host '%s:%d' is missing a Private Key File!",
[3b4c0d0]405                                                s->server_hostname, s->port);
[e183628]406            exit(-1);
407        }
408
409        if (sc->enabled == GNUTLS_ENABLED_TRUE) {
[2b76a9c]410            rv = -1;
411            if (sc->certs_x509_chain_num > 0) {
[031acac]412                rv = read_crt_cn(s, p, sc->certs_x509_crt_chain[0], &sc->cert_cn);
[2b76a9c]413            }
[671b64f]414            if (rv < 0 && sc->cert_pgp != NULL) {
[031acac]415                rv = read_pgpcrt_cn(s, p, sc->cert_crt_pgp[0], &sc->cert_cn);
[3b4c0d0]416                        }
[e183628]417
418            if (rv < 0) {
[671b64f]419                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
[031acac]420                                                        "GnuTLS: Cannot find a certificate for host '%s:%d'!",
[3b4c0d0]421                                                        s->server_hostname, s->port);
[e183628]422                sc->cert_cn = NULL;
423                continue;
424            }
425        }
[0de1839]426
427        if (sc->enabled == GNUTLS_ENABLED_TRUE
[4133f2d]428            && sc->proxy_enabled == GNUTLS_ENABLED_TRUE
429            && load_proxy_x509_credentials(s) != APR_SUCCESS)
[0de1839]430        {
[4133f2d]431            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
432                         "%s: loading proxy credentials for host "
433                         "'%s:%d' failed, exiting!",
434                         __func__, s->server_hostname, s->port);
435            exit(-1);
[0de1839]436        }
[e183628]437    }
438
439
440    ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
441
[4ec9183]442    {
[83eafed]443        const char* libvers = gnutls_check_version(NULL);
444        char* gnutls_version = NULL;
445        if(libvers && (gnutls_version = apr_psprintf(p, "GnuTLS/%s", libvers))) {
446            ap_add_version_component(p, gnutls_version);
447        } else {
[4ec9183]448            // In case we could not create the above string go for the static version instead
449            ap_add_version_component(p, "GnuTLS/" GNUTLS_VERSION "-static");
450        }
451    }
452
[e183628]453    return OK;
[c301152]454}
455
[031acac]456void mgs_hook_child_init(apr_pool_t * p, server_rec *s) {
[e183628]457    apr_status_t rv = APR_SUCCESS;
[031acac]458    mgs_srvconf_rec *sc =
459        (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
[e183628]460
461    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[031acac]462    /* if we use PKCS #11 reinitialize it */
463
464    if (mgs_pkcs11_reinit(s) < 0) {
465            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
466                    "GnuTLS: Failed to reinitialize PKCS #11");
467            exit(-1);
468    }
469
[e183628]470    if (sc->cache_type != mgs_cache_none) {
471        rv = mgs_cache_child_init(p, s, sc);
472        if (rv != APR_SUCCESS) {
473            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
[031acac]474                    "GnuTLS: Failed to run Cache Init");
[e183628]475        }
476    }
[33826c5]477    /* Block SIGPIPE Signals */
[671b64f]478    rv = apr_signal_block(SIGPIPE);
[37f8282]479    if(rv != APR_SUCCESS) {
[33826c5]480        /* error sending output */
[37f8282]481        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
[671b64f]482                "GnuTLS: Error Blocking SIGPIPE Signal!");
483    }
[c301152]484}
485
[e183628]486const char *mgs_hook_http_scheme(const request_rec * r) {
487    mgs_srvconf_rec *sc;
[c301152]488
[e183628]489    if (r == NULL)
490        return NULL;
[e02dd8c]491
[e183628]492    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
493            server->module_config,
494            &gnutls_module);
[e02dd8c]495
[e183628]496    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
497    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
498        return NULL;
499    }
[e02dd8c]500
[e183628]501    return "https";
[c301152]502}
503
[e183628]504apr_port_t mgs_hook_default_port(const request_rec * r) {
505    mgs_srvconf_rec *sc;
[e02dd8c]506
[e183628]507    if (r == NULL)
508        return 0;
[e02dd8c]509
[e183628]510    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
511            server->module_config,
512            &gnutls_module);
[e02dd8c]513
[e183628]514    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
515    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
516        return 0;
517    }
[c301152]518
[e183628]519    return 443;
[c301152]520}
521
522#define MAX_HOST_LEN 255
523
524#if USING_2_1_RECENT
[e183628]525
[7bebb42]526typedef struct {
[e183628]527    mgs_handle_t *ctxt;
528    mgs_srvconf_rec *sc;
529    const char *sni_name;
[c301152]530} vhost_cb_rec;
531
[14d718f]532/**
533 * Matches the current vhost's ServerAlias directives
534 *
535 * @param x vhost callback record
536 * @param s server record
537 * @return true if a match, false otherwise
538 *
539 */
540int check_server_aliases(vhost_cb_rec *x, server_rec * s, mgs_srvconf_rec *tsc) {
541        apr_array_header_t *names;
542        int i,rv = 0;
[cb60afc]543        char ** name;
[14d718f]544
545        /* Check ServerName First! */
546        if(apr_strnatcasecmp(x->sni_name, s->server_hostname) == 0) {
547                // We have a match, save this server configuration
548                x->sc = tsc;
549                rv = 1;
550        /* Check any ServerAlias directives */
[e3d36c7]551        } else if(s->names->nelts) {
[14d718f]552                names = s->names;
[cb60afc]553                name = (char **)names->elts;
[14d718f]554                for (i = 0; i < names->nelts; ++i) {
[671b64f]555                        if (!name[i]) { continue; }
556                                if (apr_strnatcasecmp(x->sni_name, name[i]) == 0) {
[e3d36c7]557                                        // We have a match, save this server configuration
558                                        x->sc = tsc;
559                                        rv = 1;
[671b64f]560                        }
[14d718f]561                }
562        /* Wild any ServerAlias Directives */
[e3d36c7]563        } else if(s->wild_names->nelts) {
[14d718f]564                names = s->wild_names;
[cb60afc]565        name = (char **)names->elts;
[14d718f]566                for (i = 0; i < names->nelts; ++i) {
567                        if (!name[i]) { continue; }
[671b64f]568                                if(apr_fnmatch(name[i], x->sni_name ,
[e3d36c7]569                                                                APR_FNM_CASE_BLIND|
570                                                                APR_FNM_PERIOD|
571                                                                APR_FNM_PATHNAME|
[671b64f]572                                                                APR_FNM_NOESCAPE) == APR_SUCCESS) {
[14d718f]573                                x->sc = tsc;
[671b64f]574                                rv = 1;
[14d718f]575                        }
576                }
577        }
578        return rv;
579}
580
[fd82e59]581static int vhost_cb(void *baton, conn_rec * conn __attribute__((unused)), server_rec * s) {
[e183628]582    mgs_srvconf_rec *tsc;
583    vhost_cb_rec *x = baton;
[b1c2b01]584    int ret;
[671b64f]585
[e183628]586    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
587    tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
588            &gnutls_module);
[7bebb42]589
[e183628]590    if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
591        return 0;
592    }
[671b64f]593
[b1c2b01]594    if (tsc->certs_x509_chain_num > 0) {
[031acac]595        /* this check is there to warn administrator of any missing hostname
596         * in the certificate. */
597        ret = gnutls_x509_crt_check_hostname(tsc->certs_x509_crt_chain[0], s->server_hostname);
[b1c2b01]598        if (0 == ret)
[031acac]599            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
600                         "GnuTLS: the certificate doesn't match requested hostname "
[b1c2b01]601                         "'%s'", s->server_hostname);
602    } else {
[6055aff]603        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
[b1c2b01]604                     "GnuTLS: SNI request for '%s' but no X.509 certs available at all",
605                     s->server_hostname);
606    }
[3b4c0d0]607        return check_server_aliases(x, s, tsc);
[c301152]608}
609#endif
610
[5342265]611mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
612{
[e183628]613    int rv;
614    unsigned int sni_type;
615    size_t data_len = MAX_HOST_LEN;
616    char sni_name[MAX_HOST_LEN];
617    mgs_handle_t *ctxt;
[c301152]618#if USING_2_1_RECENT
[e183628]619    vhost_cb_rec cbx;
[c301152]620#else
[e183628]621    server_rec *s;
622    mgs_srvconf_rec *tsc;
[c301152]623#endif
[7bebb42]624
[e183628]625    if (session == NULL)
626        return NULL;
[368b574]627
[e183628]628    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
629    ctxt = gnutls_transport_get_ptr(session);
[7bebb42]630
[e183628]631    rv = gnutls_server_name_get(ctxt->session, sni_name,
632            &data_len, &sni_type, 0);
[7bebb42]633
[e183628]634    if (rv != 0) {
635        return NULL;
636    }
[7bebb42]637
[e183628]638    if (sni_type != GNUTLS_NAME_DNS) {
639        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
640                ctxt->c->base_server,
641                "GnuTLS: Unknown type '%d' for SNI: "
642                "'%s'", sni_type, sni_name);
643        return NULL;
644    }
[7bebb42]645
[c301152]646    /**
647     * Code in the Core already sets up the c->base_server as the base
648     * for this IP/Port combo.  Trust that the core did the 'right' thing.
649     */
650#if USING_2_1_RECENT
[e183628]651    cbx.ctxt = ctxt;
652    cbx.sc = NULL;
653    cbx.sni_name = sni_name;
654
655    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
656    if (rv == 1) {
657        return cbx.sc;
658    }
[e02dd8c]659#else
[e183628]660    for (s = ap_server_conf; s; s = s->next) {
661
[671b64f]662        tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
[5342265]663                                                       &gnutls_module);
[671b64f]664
[b7098b2]665        if (tsc->enabled != GNUTLS_ENABLED_TRUE) { continue; }
[671b64f]666
[5342265]667        if(check_server_aliases(x, s, tsc)) {
668            return tsc;
669        }
670    }
[c301152]671#endif
[e183628]672    return NULL;
[836417f]673}
674
[b429e4c]675/*
676 * This function is intended as a cleanup handler for connections
677 * using GnuTLS.
678 *
679 * @param data must point to the mgs_handle_t associated with the
680 * connection
681 */
682static apr_status_t cleanup_gnutls_session(void *data)
683{
684    /* nothing to do */
685    if (data == NULL)
686        return APR_SUCCESS;
687
688    /* check if session needs closing */
689    mgs_handle_t *ctxt = (mgs_handle_t *) data;
690    if (ctxt->session != NULL)
691    {
692        int ret;
693        /* Try A Clean Shutdown */
694        do
695            ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
696        while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
697        if (ret != GNUTLS_E_SUCCESS)
698            ap_log_cerror(APLOG_MARK, APLOG_INFO, ret, ctxt->c,
699                          "%s: error while closing TLS %sconnection: %s (%d)",
700                          __func__, IS_PROXY_STR(ctxt),
701                          gnutls_strerror(ret), ret);
702        else
703            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, ret, ctxt->c,
704                          "%s: TLS %sconnection closed.",
705                          __func__, IS_PROXY_STR(ctxt));
706        /* De-Initialize Session */
707        gnutls_deinit(ctxt->session);
708        ctxt->session = NULL;
709    }
710    return APR_SUCCESS;
711}
712
[e8acf05]713static void create_gnutls_handle(conn_rec * c)
714{
715    /* Get mod_gnutls server configuration */
716    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
717            ap_get_module_config(c->base_server->module_config, &gnutls_module);
[e183628]718
719    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[e8acf05]720
721    /* Get connection specific configuration */
722    mgs_handle_t *ctxt = (mgs_handle_t *) ap_get_module_config(c->conn_config, &gnutls_module);
723    if (ctxt == NULL)
724    {
725        ctxt = apr_pcalloc(c->pool, sizeof (*ctxt));
726        ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
[c1ef069]727        ctxt->is_proxy = GNUTLS_ENABLED_FALSE;
[e8acf05]728    }
729    ctxt->enabled = GNUTLS_ENABLED_TRUE;
[e183628]730    ctxt->c = c;
731    ctxt->sc = sc;
732    ctxt->status = 0;
733    ctxt->input_rc = APR_SUCCESS;
734    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
735    ctxt->input_cbuf.length = 0;
736    ctxt->output_rc = APR_SUCCESS;
737    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
738    ctxt->output_blen = 0;
739    ctxt->output_length = 0;
[e8acf05]740
[d0be765]741    /* Initialize GnuTLS Library */
[beb14d9]742    int err = 0;
743    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
744    {
745        /* this is an outgoing proxy connection, client mode */
746        err = gnutls_init(&ctxt->session, GNUTLS_CLIENT);
747        if (err != GNUTLS_E_SUCCESS)
748            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
749                          "gnutls_init for proxy connection failed: %s (%d)",
750                          gnutls_strerror(err), err);
751        err = gnutls_session_ticket_enable_client(ctxt->session);
752        if (err != GNUTLS_E_SUCCESS)
753            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
754                          "gnutls_session_ticket_enable_client failed: %s (%d)",
755                          gnutls_strerror(err), err);
[b429e4c]756        /* Try to close and deinit the session when the connection
757         * pool is cleared. Note that mod_proxy might not close
758         * connections immediately, if you need that, look at the
759         * "proxy-nokeepalive" environment variable for
760         * mod_proxy_http. */
761        apr_pool_pre_cleanup_register(c->pool, ctxt, cleanup_gnutls_session);
[beb14d9]762    }
763    else
764    {
765        /* incoming connection, server mode */
766        err = gnutls_init(&ctxt->session, GNUTLS_SERVER);
[e4b58b6]767        if (err != GNUTLS_E_SUCCESS)
[beb14d9]768            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
769                          "gnutls_init for server side failed: %s (%d)",
770                          gnutls_strerror(err), err);
771        /* Initialize Session Tickets */
772        if (session_ticket_key.data != NULL && ctxt->sc->tickets != 0)
773        {
774            err = gnutls_session_ticket_enable_server(ctxt->session, &session_ticket_key);
775            if (err != GNUTLS_E_SUCCESS)
776                ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
777                              "gnutls_session_ticket_enable_server failed: %s (%d)",
778                              gnutls_strerror(err), err);
779        }
[d0be765]780    }
[e183628]781
[d0be765]782    /* Set Default Priority */
[e4b58b6]783        err = gnutls_priority_set_direct(ctxt->session, "NORMAL", NULL);
784    if (err != GNUTLS_E_SUCCESS)
785        ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c, "gnutls_priority_set_direct failed!");
[d0be765]786    /* Set Handshake function */
[e183628]787    gnutls_handshake_set_post_client_hello_function(ctxt->session,
788            mgs_select_virtual_server_cb);
[beb14d9]789
[0de1839]790    /* Set GnuTLS user pointer, so we can access the module session
791     * context in GnuTLS callbacks */
792    gnutls_session_set_ptr(ctxt->session, ctxt);
793
[beb14d9]794    /* If mod_gnutls is the TLS server, mgs_select_virtual_server_cb
795     * will load appropriate credentials during handshake. However,
796     * when handling a proxy backend connection, mod_gnutls acts as
797     * TLS client and credentials must be loaded here. */
798    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
799    {
800        /* Set anonymous client credentials for proxy connections */
801        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_ANON,
802                               ctxt->sc->anon_client_creds);
803        /* Set x509 credentials */
804        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE,
[0de1839]805                               ctxt->sc->proxy_x509_creds);
[beb14d9]806        /* Load priorities from the server configuration */
[f030883]807        err = gnutls_priority_set(ctxt->session, ctxt->sc->proxy_priorities);
[beb14d9]808        if (err != GNUTLS_E_SUCCESS)
809            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
[f030883]810                          "%s: setting priorities for proxy connection "
811                          "failed: %s (%d)",
[beb14d9]812                          __func__, gnutls_strerror(err), err);
813    }
814
[d0be765]815    /* Initialize Session Cache */
[e183628]816    mgs_cache_session_init(ctxt);
[671b64f]817
[33826c5]818    /* Set pull, push & ptr functions */
819    gnutls_transport_set_pull_function(ctxt->session,
820            mgs_transport_read);
821    gnutls_transport_set_push_function(ctxt->session,
822            mgs_transport_write);
[9ee0464]823    gnutls_transport_set_ptr(ctxt->session, ctxt);
[33826c5]824    /* Add IO filters */
[671b64f]825    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME,
826            ctxt, NULL, c);
827    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
[33826c5]828            ctxt, NULL, c);
[e183628]829}
[e02dd8c]830
[e8acf05]831int mgs_hook_pre_connection(conn_rec * c, void *csd __attribute__((unused)))
832{
[e183628]833    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[e02dd8c]834
[e8acf05]835    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
836        ap_get_module_config(c->base_server->module_config, &gnutls_module);
837    mgs_handle_t *ctxt = (mgs_handle_t *)
838        ap_get_module_config(c->conn_config, &gnutls_module);
[c301152]839
[07d548d]840    if ((sc && (!sc->enabled)) || (ctxt && ctxt->enabled == GNUTLS_ENABLED_FALSE))
[e8acf05]841    {
842        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s declined connection",
843                      __func__);
[e183628]844        return DECLINED;
845    }
[ae4a2b0]846
[33826c5]847    create_gnutls_handle(c);
[e183628]848    return OK;
849}
[e02dd8c]850
[e183628]851int mgs_hook_fixups(request_rec * r) {
852    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
853    char buf[AP_IOBUFSIZE];
854    const char *tmp;
855    size_t len;
856    mgs_handle_t *ctxt;
857    int rv = OK;
[c301152]858
[e183628]859    if (r == NULL)
860        return DECLINED;
[c301152]861
[e183628]862    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
863    apr_table_t *env = r->subprocess_env;
[7bebb42]864
[e8acf05]865    ctxt = ap_get_module_config(r->connection->conn_config,
866                                &gnutls_module);
[c301152]867
[e8acf05]868    if (!ctxt || ctxt->enabled != GNUTLS_ENABLED_TRUE || ctxt->session == NULL)
869    {
870        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "request declined in %s", __func__);
[e183628]871        return DECLINED;
872    }
[c301152]873
[e183628]874    apr_table_setn(env, "HTTPS", "on");
875
876    apr_table_setn(env, "SSL_VERSION_LIBRARY",
877            "GnuTLS/" LIBGNUTLS_VERSION);
878    apr_table_setn(env, "SSL_VERSION_INTERFACE",
879            "mod_gnutls/" MOD_GNUTLS_VERSION);
880
881    apr_table_setn(env, "SSL_PROTOCOL",
[9720026]882            gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
[e183628]883
884    /* should have been called SSL_CIPHERSUITE instead */
885    apr_table_setn(env, "SSL_CIPHER",
[9720026]886            gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
887                                         gnutls_cipher_get(ctxt->session),
888                                         gnutls_mac_get(ctxt->session)));
[e183628]889
890    apr_table_setn(env, "SSL_COMPRESS_METHOD",
[9720026]891            gnutls_compression_get_name(gnutls_compression_get(ctxt->session)));
[7bebb42]892
[787dab7]893#ifdef ENABLE_SRP
[369f47a]894    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
895        tmp = gnutls_srp_server_get_username(ctxt->session);
896        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
897    } else {
898        apr_table_unset(env, "SSL_SRP_USER");
899    }
[787dab7]900#endif
[c301152]901
[e183628]902    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
903        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
[c301152]904
[9720026]905    unsigned int key_size = 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
[e183628]906    tmp = apr_psprintf(r->pool, "%u", key_size);
[c301152]907
[e183628]908    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
[c301152]909
[e183628]910    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
[c301152]911
[e183628]912    apr_table_setn(env, "SSL_CIPHER_EXPORT",
913            (key_size <= 40) ? "true" : "false");
[7bebb42]914
[5674676]915    int dhsize = gnutls_dh_get_prime_bits(ctxt->session);
916    if (dhsize > 0) {
917        tmp = apr_psprintf(r->pool, "%d", dhsize);
918        apr_table_setn(env, "SSL_DH_PRIME_BITS", tmp);
919    }
920
[e183628]921    len = sizeof (sbuf);
922    gnutls_session_get_id(ctxt->session, sbuf, &len);
923    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
924    apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
[7ba803b]925
[3b4c0d0]926    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
[031acac]927        mgs_add_common_cert_vars(r, ctxt->sc->certs_x509_crt_chain[0], 0, ctxt->sc->export_certificates_size);
928    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
929        mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_crt_pgp[0], 0, ctxt->sc->export_certificates_size);
930    }
[7bebb42]931
[e183628]932    return rv;
[c301152]933}
934
[e183628]935int mgs_hook_authz(request_rec * r) {
936    int rv;
937    mgs_handle_t *ctxt;
938    mgs_dirconf_rec *dc;
939
940    if (r == NULL)
941        return DECLINED;
942
943    dc = ap_get_module_config(r->per_dir_config, &gnutls_module);
944
945    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
946    ctxt =
947            ap_get_module_config(r->connection->conn_config,
948            &gnutls_module);
949
950    if (!ctxt || ctxt->session == NULL) {
951        return DECLINED;
952    }
953
954    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
955        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
956                "GnuTLS: Directory set to Ignore Client Certificate!");
957    } else {
958        if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
959            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
960                    "GnuTLS: Attempting to rehandshake with peer. %d %d",
961                    ctxt->sc->client_verify_mode,
962                    dc->client_verify_mode);
963
964            /* If we already have a client certificate, there's no point in
965             * re-handshaking... */
966            rv = mgs_cert_verify(r, ctxt);
967            if (rv != DECLINED && rv != HTTP_FORBIDDEN)
968                return rv;
969
970            gnutls_certificate_server_set_request
971                    (ctxt->session, dc->client_verify_mode);
972
973            if (mgs_rehandshake(ctxt) != 0) {
974                return HTTP_FORBIDDEN;
975            }
976        } else if (ctxt->sc->client_verify_mode ==
977                GNUTLS_CERT_IGNORE) {
[c301152]978#if MOD_GNUTLS_DEBUG
[e183628]979            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
980                    "GnuTLS: Peer is set to IGNORE");
[c301152]981#endif
[e183628]982            return DECLINED;
983        }
984        rv = mgs_cert_verify(r, ctxt);
[5a8a32b]985        if (rv != DECLINED
986            && (rv != HTTP_FORBIDDEN
987                || dc->client_verify_mode == GNUTLS_CERT_REQUIRE
988                || (dc->client_verify_mode == -1
989                    && ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUIRE)))
990        {
[e183628]991            return rv;
992        }
993    }
994
995    return DECLINED;
[7bebb42]996}
997
998/* variables that are not sent by default:
999 *
1000 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
1001 * SSL_SERVER_CERT      string  PEM-encoded client certificate
1002 */
[836c2f9]1003
[7d1ab49]1004/* @param side is either 0 for SERVER or 1 for CLIENT
[671b64f]1005 *
[2aaf4f5]1006 * @param export_cert_size (int) maximum size for environment variable
1007 * to use for the PEM-encoded certificate (0 means do not export)
[7bebb42]1008 */
[765cac2]1009#define MGS_SIDE(suffix) ((side==0) ? "SSL_SERVER" suffix : "SSL_CLIENT" suffix)
[e183628]1010
[fd82e59]1011static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size) {
[e183628]1012    unsigned char sbuf[64]; /* buffer to hold serials */
1013    char buf[AP_IOBUFSIZE];
1014    const char *tmp;
1015    char *tmp2;
1016    size_t len;
1017    int ret, i;
1018
1019    if (r == NULL)
1020        return;
1021
1022    apr_table_t *env = r->subprocess_env;
1023
1024    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
[2aaf4f5]1025    if (export_cert_size > 0) {
1026        len = 0;
1027        ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &len);
1028        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1029            if (len >= export_cert_size) {
[765cac2]1030                apr_table_setn(env, MGS_SIDE("_CERT"), "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
[2aaf4f5]1031                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1032                              "GnuTLS: Failed to export too-large X.509 certificate to environment");
1033            } else {
1034                char* cert_buf = apr_palloc(r->pool, len + 1);
1035                if (cert_buf != NULL && gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) {
1036                    cert_buf[len] = 0;
[765cac2]1037                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
[2aaf4f5]1038                } else {
1039                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1040                                  "GnuTLS: failed to export X.509 certificate");
1041                }
1042            }
1043        } else {
1044            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1045                          "GnuTLS: dazed and confused about X.509 certificate size");
1046        }
[7d1ab49]1047    }
[671b64f]1048
[e183628]1049    len = sizeof (buf);
1050    gnutls_x509_crt_get_dn(cert, buf, &len);
[765cac2]1051    apr_table_setn(env, MGS_SIDE("_S_DN"), apr_pstrmemdup(r->pool, buf, len));
[e183628]1052
1053    len = sizeof (buf);
1054    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
[765cac2]1055    apr_table_setn(env, MGS_SIDE("_I_DN"), apr_pstrmemdup(r->pool, buf, len));
[e183628]1056
1057    len = sizeof (sbuf);
1058    gnutls_x509_crt_get_serial(cert, sbuf, &len);
1059    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
[765cac2]1060    apr_table_setn(env, MGS_SIDE("_M_SERIAL"), apr_pstrdup(r->pool, tmp));
[e183628]1061
1062    ret = gnutls_x509_crt_get_version(cert);
1063    if (ret > 0)
[765cac2]1064        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1065                       apr_psprintf(r->pool, "%u", ret));
[e183628]1066
[765cac2]1067    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "X.509");
[e183628]1068
1069    tmp =
1070            mgs_time2sz(gnutls_x509_crt_get_expiration_time
1071            (cert), buf, sizeof (buf));
[765cac2]1072    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
[e183628]1073
1074    tmp =
1075            mgs_time2sz(gnutls_x509_crt_get_activation_time
1076            (cert), buf, sizeof (buf));
[765cac2]1077    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
[e183628]1078
1079    ret = gnutls_x509_crt_get_signature_algorithm(cert);
1080    if (ret >= 0) {
[765cac2]1081        apr_table_setn(env, MGS_SIDE("_A_SIG"),
[e183628]1082                gnutls_sign_algorithm_get_name(ret));
1083    }
1084
1085    ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
1086    if (ret >= 0) {
[765cac2]1087        apr_table_setn(env, MGS_SIDE("_A_KEY"),
[e183628]1088                gnutls_pk_algorithm_get_name(ret));
1089    }
1090
1091    /* export all the alternative names (DNS, RFC822 and URI) */
1092    for (i = 0; !(ret < 0); i++) {
[765cac2]1093        const char *san, *sanlabel;
[e183628]1094        len = 0;
1095        ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1096                NULL, &len,
1097                NULL);
1098
1099        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1100            tmp2 = apr_palloc(r->pool, len + 1);
1101
1102            ret =
1103                    gnutls_x509_crt_get_subject_alt_name(cert, i,
1104                    tmp2,
1105                    &len,
1106                    NULL);
1107            tmp2[len] = 0;
1108
[765cac2]1109            sanlabel = apr_psprintf(r->pool, "%s%u", MGS_SIDE("_S_AN"), i);
[e183628]1110            if (ret == GNUTLS_SAN_DNSNAME) {
[765cac2]1111                san = apr_psprintf(r->pool, "DNSNAME:%s", tmp2);
[e183628]1112            } else if (ret == GNUTLS_SAN_RFC822NAME) {
[765cac2]1113                san = apr_psprintf(r->pool, "RFC822NAME:%s", tmp2);
[e183628]1114            } else if (ret == GNUTLS_SAN_URI) {
[765cac2]1115                san = apr_psprintf(r->pool, "URI:%s", tmp2);
[e183628]1116            } else {
[765cac2]1117                san = "UNSUPPORTED";
[e183628]1118            }
[765cac2]1119            apr_table_setn(env, sanlabel, san);
[e183628]1120        }
1121    }
[e5bbda4]1122}
[7bebb42]1123
[7d1ab49]1124
1125/* @param side 0: server, 1: client
[671b64f]1126 *
[2aaf4f5]1127 * @param export_cert_size (int) maximum size for environment variable
1128 * to use for the PEM-encoded certificate (0 means do not export)
[7d1ab49]1129 */
[fd82e59]1130static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, size_t export_cert_size) {
[3b4c0d0]1131
1132        unsigned char sbuf[64]; /* buffer to hold serials */
[e183628]1133    char buf[AP_IOBUFSIZE];
1134    const char *tmp;
1135    size_t len;
1136    int ret;
1137
1138    if (r == NULL)
1139        return;
1140
1141    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1142    apr_table_t *env = r->subprocess_env;
1143
[2aaf4f5]1144    if (export_cert_size > 0) {
1145        len = 0;
1146        ret = gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, NULL, &len);
1147        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1148            if (len >= export_cert_size) {
[765cac2]1149                apr_table_setn(env, MGS_SIDE("_CERT"),
[2aaf4f5]1150                               "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1151                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1152                              "GnuTLS: Failed to export too-large OpenPGP certificate to environment");
1153            } else {
1154                char* cert_buf = apr_palloc(r->pool, len + 1);
1155                if (cert_buf != NULL && gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0) {
1156                    cert_buf[len] = 0;
[765cac2]1157                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
[2aaf4f5]1158                } else {
1159                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1160                                  "GnuTLS: failed to export OpenPGP certificate");
1161                }
1162            }
1163        } else {
1164            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1165                          "GnuTLS: dazed and confused about OpenPGP certificate size");
1166        }
[7d1ab49]1167    }
1168
[e183628]1169    len = sizeof (buf);
1170    gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
[765cac2]1171    apr_table_setn(env, MGS_SIDE("_NAME"), apr_pstrmemdup(r->pool, buf, len));
[e183628]1172
1173    len = sizeof (sbuf);
1174    gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
1175    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
[765cac2]1176    apr_table_setn(env, MGS_SIDE("_FINGERPRINT"), apr_pstrdup(r->pool, tmp));
[e183628]1177
1178    ret = gnutls_openpgp_crt_get_version(cert);
1179    if (ret > 0)
[765cac2]1180        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1181                       apr_psprintf(r->pool, "%u", ret));
[e183628]1182
[765cac2]1183    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "OPENPGP");
[e183628]1184
1185    tmp =
1186            mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
1187            (cert), buf, sizeof (buf));
[765cac2]1188    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
[e183628]1189
1190    tmp =
1191            mgs_time2sz(gnutls_openpgp_crt_get_creation_time
1192            (cert), buf, sizeof (buf));
[765cac2]1193    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
[e183628]1194
1195    ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
1196    if (ret >= 0) {
[765cac2]1197        apr_table_setn(env, MGS_SIDE("_A_KEY"), gnutls_pk_algorithm_get_name(ret));
[e183628]1198    }
[e5bbda4]1199
1200}
1201
[2b3a248]1202/* TODO: Allow client sending a X.509 certificate chain */
[e183628]1203static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) {
1204    const gnutls_datum_t *cert_list;
1205    unsigned int cert_list_size, status;
1206    int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1207    unsigned int ch_size = 0;
1208
1209    union {
1210        gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1211        gnutls_openpgp_crt_t pgp;
1212    } cert;
1213    apr_time_t expiration_time, cur_time;
1214
1215    if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1216        return HTTP_FORBIDDEN;
1217
1218    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1219    cert_list =
1220            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1221
1222    if (cert_list == NULL || cert_list_size == 0) {
1223        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1224         */
1225        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1226            return OK;
1227
1228        /* no certificate provided by the client, but one was required. */
1229        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1230                "GnuTLS: Failed to Verify Peer: "
1231                "Client did not submit a certificate");
1232        return HTTP_FORBIDDEN;
1233    }
1234
1235    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1236        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1237                "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1238                cert_list_size);
1239
1240        for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1241            gnutls_x509_crt_init(&cert.x509[ch_size]);
1242            rv = gnutls_x509_crt_import(cert.x509[ch_size],
1243                    &cert_list[ch_size],
1244                    GNUTLS_X509_FMT_DER);
1245            // When failure to import, leave the loop
1246            if (rv != GNUTLS_E_SUCCESS) {
1247                if (ch_size < 1) {
1248                    ap_log_rerror(APLOG_MARK,
1249                            APLOG_INFO, 0, r,
1250                            "GnuTLS: Failed to Verify Peer: "
1251                            "Failed to import peer certificates.");
1252                    ret = HTTP_FORBIDDEN;
1253                    goto exit;
1254                }
1255                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1256                        "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1257                        ch_size);
1258                rv = GNUTLS_E_SUCCESS;
1259                break;
1260            }
1261        }
[07889ab]1262    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
[e183628]1263        if (cert_list_size > 1) {
1264            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1265                    "GnuTLS: Failed to Verify Peer: "
1266                    "Chained Client Certificates are not supported.");
1267            return HTTP_FORBIDDEN;
1268        }
1269
1270        gnutls_openpgp_crt_init(&cert.pgp);
1271        rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0],
1272                GNUTLS_OPENPGP_FMT_RAW);
1273
1274    } else
1275        return HTTP_FORBIDDEN;
1276
1277    if (rv < 0) {
1278        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1279                "GnuTLS: Failed to Verify Peer: "
1280                "Failed to import peer certificates.");
1281        ret = HTTP_FORBIDDEN;
1282        goto exit;
1283    }
1284
1285    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1286        apr_time_ansi_put(&expiration_time,
1287                gnutls_x509_crt_get_expiration_time
1288                (cert.x509[0]));
1289
1290        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
[5c0d491]1291                      "GnuTLS: Verifying list of %d certificate(s) via method '%s'",
1292                      ch_size, mgs_readable_cvm(ctxt->sc->client_verify_method));
1293        switch(ctxt->sc->client_verify_method) {
1294        case mgs_cvm_cartel:
1295            rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1296                                             ctxt->sc->ca_list,
1297                                             ctxt->sc->ca_list_size,
1298                                             NULL, 0, 0, &status);
1299            break;
1300#ifdef ENABLE_MSVA
1301        case mgs_cvm_msva:
1302        {
[a01f8ab]1303            struct msv_response* resp = NULL;
1304            struct msv_query q = { .context="https", .peertype="client", .pkctype="x509pem" };
1305            msv_ctxt_t ctx = msv_ctxt_init(NULL);
[5c0d491]1306            char cert_pem_buf[10 * 1024];
1307            size_t len = sizeof (cert_pem_buf);
1308
1309            rv = 0;
1310            if (gnutls_x509_crt_export(cert.x509[0], GNUTLS_X509_FMT_PEM, cert_pem_buf, &len) >= 0) {
1311                /* FIXME : put together a name from the cert we received, instead of hard-coding this value: */
[a01f8ab]1312                q.peername = mgs_x509_construct_uid(r, cert.x509[0]);
1313                q.pkcdata = cert_pem_buf;
1314                rv = msv_query_agent(ctx, q, &resp);
[5c0d491]1315                if (rv == LIBMSV_ERROR_SUCCESS) {
1316                    status = 0;
1317                } else if (rv == LIBMSV_ERROR_INVALID) {
1318                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
[a01f8ab]1319                                  "GnuTLS: Monkeysphere validation failed: (message: %s)", resp->message);
[5c0d491]1320                    status = GNUTLS_CERT_INVALID;
1321                } else {
1322                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
[a01f8ab]1323                                  "GnuTLS: Error communicating with the Monkeysphere Validation Agent: (%d) %s", rv, msv_strerror(ctx, rv));
[5c0d491]1324                    status = GNUTLS_CERT_INVALID;
1325                    rv = -1;
[671b64f]1326                }
[5c0d491]1327            } else {
1328                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1329                              "GnuTLS: Could not convert the client certificate to PEM format");
1330                status = GNUTLS_CERT_INVALID;
1331                rv = GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
1332            }
[a01f8ab]1333            msv_response_destroy(resp);
1334            msv_ctxt_destroy(ctx);
[5c0d491]1335        }
1336            break;
1337#endif
1338        default:
1339            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1340                          "GnuTLS: Failed to Verify X.509 Peer: method '%s' is not supported",
1341                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1342        }
[671b64f]1343
[e183628]1344    } else {
1345        apr_time_ansi_put(&expiration_time,
1346                gnutls_openpgp_crt_get_expiration_time
1347                (cert.pgp));
1348
[5c0d491]1349        switch(ctxt->sc->client_verify_method) {
1350        case mgs_cvm_cartel:
1351            rv = gnutls_openpgp_crt_verify_ring(cert.pgp,
1352                                                ctxt->sc->pgp_list, 0,
1353                                                &status);
1354            break;
1355#ifdef ENABLE_MSVA
1356        case mgs_cvm_msva:
1357            /* need to set status and rv */
1358            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1359                          "GnuTLS:  OpenPGP verification via MSVA is not yet implemented");
1360            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1361            break;
1362#endif
1363        default:
1364            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1365                          "GnuTLS: Failed to Verify OpenPGP Peer: method '%s' is not supported",
1366                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1367        }
[e183628]1368    }
1369
1370    if (rv < 0) {
1371        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1372                "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1373                rv, gnutls_strerror(rv));
1374        if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1375            ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1376                "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
1377        ret = HTTP_FORBIDDEN;
1378        goto exit;
1379    }
1380
1381    /* TODO: X509 CRL Verification. */
1382    /* May add later if anyone needs it.
1383     */
1384    /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1385
1386    cur_time = apr_time_now();
1387
1388    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1389        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1390                "GnuTLS: Could not find Signer for Peer Certificate");
1391    }
1392
1393    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
1394        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1395                "GnuTLS: Peer's Certificate signer is not a CA");
1396    }
1397
1398    if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
1399        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1400                "GnuTLS: Peer's Certificate is using insecure algorithms");
1401    }
1402
1403    if (status & GNUTLS_CERT_EXPIRED
1404            || status & GNUTLS_CERT_NOT_ACTIVATED) {
1405        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1406                "GnuTLS: Peer's Certificate signer is expired or not yet activated");
1407    }
1408
1409    if (status & GNUTLS_CERT_INVALID) {
1410        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1411                "GnuTLS: Peer Certificate is invalid.");
1412    } else if (status & GNUTLS_CERT_REVOKED) {
1413        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1414                "GnuTLS: Peer Certificate is revoked.");
1415    }
1416
1417    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
[2aaf4f5]1418        mgs_add_common_cert_vars(r, cert.x509[0], 1, ctxt->sc->export_certificates_size);
[7d1ab49]1419    else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP)
[2aaf4f5]1420        mgs_add_common_pgpcert_vars(r, cert.pgp, 1, ctxt->sc->export_certificates_size);
[e183628]1421
1422    {
1423        /* days remaining */
1424        unsigned long remain =
1425                (apr_time_sec(expiration_time) -
1426                apr_time_sec(cur_time)) / 86400;
1427        apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1428                apr_psprintf(r->pool, "%lu", remain));
1429    }
1430
1431    if (status == 0) {
1432        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1433                "SUCCESS");
1434        ret = OK;
1435    } else {
1436        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1437                "FAILED");
1438        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1439            ret = OK;
1440        else
1441            ret = HTTP_FORBIDDEN;
1442    }
1443
1444exit:
1445    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
[fd82e59]1446        unsigned int i;
[e183628]1447        for (i = 0; i < ch_size; i++) {
1448            gnutls_x509_crt_deinit(cert.x509[i]);
1449        }
1450    } else if (gnutls_certificate_type_get(ctxt->session) ==
1451            GNUTLS_CRT_OPENPGP)
1452        gnutls_openpgp_crt_deinit(cert.pgp);
1453    return ret;
[7bebb42]1454
1455
[9ee0464]1456}
[832182b]1457
[fd82e59]1458#ifdef ENABLE_MSVA
1459/* this section of code is used only when trying to talk to the MSVA */
[832182b]1460static const char* mgs_x509_leaf_oid_from_dn(apr_pool_t *pool, const char* oid, gnutls_x509_crt_t cert) {
1461    int rv=GNUTLS_E_SUCCESS, i;
1462    size_t sz=0, lastsz=0;
1463    char* data=NULL;
1464
1465    i = -1;
1466    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1467        i++;
1468        lastsz=sz;
1469        sz=0;
1470        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i, 0, NULL, &sz);
1471    }
1472    if (i > 0) {
1473        data = apr_palloc(pool, lastsz);
1474        sz=lastsz;
1475        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i-1, 0, data, &sz);
1476        if (rv == GNUTLS_E_SUCCESS)
1477            return data;
1478    }
1479    return NULL;
1480}
1481
1482static const char* mgs_x509_first_type_from_san(apr_pool_t *pool, gnutls_x509_subject_alt_name_t target, gnutls_x509_crt_t cert) {
1483    int rv=GNUTLS_E_SUCCESS;
1484    size_t sz;
1485    char* data=NULL;
1486    unsigned int i;
1487    gnutls_x509_subject_alt_name_t thistype;
1488
1489    i = 0;
1490    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1491        sz = 0;
1492        rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, NULL, &sz, &thistype, NULL);
1493        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && thistype == target) {
1494            data = apr_palloc(pool, sz);
1495            rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, data, &sz, &thistype, NULL);
[fd82e59]1496            if (rv >=0 && (thistype == target))
[832182b]1497                return data;
1498        }
1499        i++;
1500    }
1501    return NULL;
1502}
1503
[fd82e59]1504
[832182b]1505/* Create a string representing a candidate User ID from an X.509
1506 * certificate
1507
1508 * We need this for client certification because a client gives us a
1509 * certificate, but doesn't tell us (in any other way) who they are
1510 * trying to authenticate as.
1511
1512 * TODO: we might need another parallel for OpenPGP, but for that it's
1513 * much simpler: we can just assume that the first User ID marked as
1514 * "primary" (or the first User ID, period) is the identity the user
1515 * is trying to present as.
1516
1517 * one complaint might be "but the user wanted to be another identity,
1518 * which is also in the certificate (e.g. in a SubjectAltName)"
1519 * However, given that any user can regenerate their own X.509
1520 * certificate with their own public key content, they should just do
1521 * so, and not expect us to guess at their identity :)
1522
1523 * This function allocates it's response from the pool given it.  When
1524 * that pool is reclaimed, the response will also be deallocated.
1525
1526 * FIXME: what about extracting a server-style cert
1527 *        (e.g. https://imposter.example) from the DN or any sAN?
1528
1529 * FIXME: what if we want to call this outside the context of a
1530 *        request?  That complicates the logging.
1531 */
1532static const char* mgs_x509_construct_uid(request_rec *r, gnutls_x509_crt_t cert) {
1533    /* basic strategy, assuming humans are the users: we are going to
1534     * try to reconstruct a "conventional" User ID by pulling in a
1535     * name, comment, and e-mail address.
1536     */
1537    apr_pool_t *pool = r->pool;
1538    const char *name=NULL, *comment=NULL, *email=NULL;
1539    const char *ret=NULL;
1540    /* subpool for temporary allocation: */
1541    apr_pool_t *sp=NULL;
1542
[671b64f]1543    if (APR_SUCCESS != apr_pool_create(&sp, pool))
[832182b]1544        return NULL; /* i'm assuming that libapr would log this kind
1545                      * of error on its own */
1546
1547     /* Name
[671b64f]1548
[832182b]1549     the name comes from the leaf commonName of the cert's Subject.
[671b64f]1550
[832182b]1551     (MAYBE: should we look at trying to assemble a candidate from
1552             givenName, surName, suffix, etc?  the "name" field
1553             appears to be case-insensitive, which seems problematic
1554             from what we expect; see:
1555             http://www.itu.int/rec/T-REC-X.520-200102-s/e )
1556
1557     (MAYBE: should we try pulling a commonName or otherName or
1558             something from subjectAltName? see:
1559             https://tools.ietf.org/html/rfc5280#section-4.2.1.6
1560             GnuTLS does not support looking for Common Names in the
1561             SAN yet)
1562     */
1563    name = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_COMMON_NAME, cert);
1564
1565    /* Comment
1566
1567       I am inclined to punt on this for now, as Comment has been so
1568       atrociously misused in OpenPGP.  Perhaps if there is a
1569       pseudonym (OID 2.5.4.65, aka GNUTLS_OID_X520_PSEUDONYM) field
1570       in the subject or sAN?
1571    */
1572    comment = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_PSEUDONYM, cert);
1573
1574    /* E-mail
1575
1576       This should be the the first rfc822Name from the sAN.
1577
[b55bf71]1578       failing that, we'll take the leaf email in the certificate's
1579       subject; this is a deprecated use though.
[832182b]1580     */
1581    email = mgs_x509_first_type_from_san(sp, GNUTLS_SAN_RFC822NAME, cert);
[b55bf71]1582    if (email == NULL)
1583        email = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_PKCS9_EMAIL, cert);
[832182b]1584
1585    /* assemble all the parts: */
1586
1587    /* must have at least a name or an e-mail. */
1588    if (name == NULL && email == NULL) {
1589        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1590                "GnuTLS: Need either a name or an e-mail address to get a User ID from an X.509 certificate.");
1591        goto end;
1592    }
1593    if (name) {
1594        if (comment) {
1595            if (email) {
1596                ret = apr_psprintf(pool, "%s (%s) <%s>", name, comment, email);
1597            } else {
1598                ret = apr_psprintf(pool, "%s (%s)", name, comment);
1599            }
1600        } else {
1601            if (email) {
1602                ret = apr_psprintf(pool, "%s <%s>", name, email);
1603            } else {
1604                ret = apr_pstrdup(pool, name);
1605            }
1606        }
1607    } else {
1608        if (comment) {
1609            ret = apr_psprintf(pool, "(%s) <%s>", comment, email);
1610        } else {
1611            ret = apr_psprintf(pool, "<%s>", email);
1612        }
1613    }
1614
1615end:
1616    apr_pool_destroy(sp);
1617    return ret;
1618}
[fd82e59]1619#endif /* ENABLE_MSVA */
[b4739cd]1620
[fd82e59]1621static int mgs_status_hook(request_rec *r, int flags __attribute__((unused)))
[b4739cd]1622{
1623    mgs_srvconf_rec *sc;
1624
1625    if (r == NULL)
1626        return OK;
1627
1628    sc = (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, &gnutls_module);
1629
1630    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1631
1632    ap_rputs("<hr>\n", r);
1633    ap_rputs("<h2>GnuTLS Information:</h2>\n<dl>\n", r);
1634
1635    ap_rprintf(r, "<dt>GnuTLS version:</dt><dd>%s</dd>\n", gnutls_check_version(NULL));
1636    ap_rputs("<dt>Built against:</dt><dd>" GNUTLS_VERSION "</dd>\n", r);
1637    ap_rprintf(r, "<dt>using TLS:</dt><dd>%s</dd>\n", (sc->enabled == GNUTLS_ENABLED_FALSE ? "no" : "yes"));
1638    if (sc->enabled != GNUTLS_ENABLED_FALSE) {
1639        mgs_handle_t* ctxt;
1640        ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module);
1641        if (ctxt && ctxt->session != NULL) {
[46de753]1642#if GNUTLS_VERSION_MAJOR < 3
1643            ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n",
1644                gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
1645                gnutls_cipher_get(ctxt->session),
1646                gnutls_mac_get(ctxt->session)));
1647#else
1648            char* z = NULL;
[b4739cd]1649            z = gnutls_session_get_desc(ctxt->session);
1650            if (z) {
1651                ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n", z);
1652                gnutls_free(z);
1653            }
[46de753]1654#endif
[b4739cd]1655        }
1656    }
1657
1658    ap_rputs("</dl>\n", r);
1659    return OK;
1660}
1661
[0de1839]1662
1663
1664/*
1665 * Callback to check the server certificate for proxy HTTPS
1666 * connections, to be used with
1667 * gnutls_certificate_set_verify_function.
1668
1669 * Returns: 0 if certificate check was successful (certificate
1670 * trusted), non-zero otherwise (error during check or untrusted
1671 * certificate).
1672 */
1673static int gtls_check_server_cert(gnutls_session_t session)
1674{
1675    mgs_handle_t *ctxt = (mgs_handle_t *) gnutls_session_get_ptr(session);
1676    unsigned int status;
1677
[6bbc00a]1678    /* Get peer hostname from a note left by mod_proxy */
1679    const char *peer_hostname =
1680        apr_table_get(ctxt->c->notes, "proxy-request-hostname");
1681    if (peer_hostname == NULL)
1682        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
1683                      "%s: proxy-request-hostname is NULL, cannot check "
1684                      "peer's hostname", __func__);
1685
1686    /* Verify certificate, including hostname match. Should
1687     * peer_hostname be NULL for some reason, the name is not
1688     * checked. */
1689    int err = gnutls_certificate_verify_peers3(session, peer_hostname,
1690                                               &status);
[0de1839]1691    if (err != GNUTLS_E_SUCCESS)
1692    {
1693        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
1694                      "%s: server certificate check failed: %s (%d)",
1695                      __func__, gnutls_strerror(err), err);
1696        return err;
1697    }
1698
1699    gnutls_datum_t * out = gnutls_malloc(sizeof(gnutls_datum_t));
1700    /* GNUTLS_CRT_X509: ATM, only X509 is supported for proxy certs
1701     * 0: according to function API, the last argument should be 0 */
1702    err = gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509,
1703                                                       out, 0);
1704    if (err != GNUTLS_E_SUCCESS)
1705        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
1706                      "%s: server verify print failed: %s (%d)",
1707                      __func__, gnutls_strerror(err), err);
1708    else
1709    {
1710        /* If the certificate is trusted, logging the result is just
1711         * nice for debugging. But if the back end server provided an
1712         * untrusted certificate, warn! */
1713        int level = (status == 0 ? APLOG_DEBUG : APLOG_WARNING);
1714        ap_log_cerror(APLOG_MARK, level, 0, ctxt->c,
1715                      "%s: server certificate verify result: %s",
1716                      __func__, out->data);
1717    }
1718
1719    gnutls_free(out);
1720    return status;
1721}
1722
1723
1724
[8b472af]1725static apr_status_t load_proxy_x509_credentials(server_rec *s)
[0de1839]1726{
1727    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1728        ap_get_module_config(s->module_config, &gnutls_module);
1729
1730    if (sc == NULL)
1731        return APR_EGENERAL;
1732
[8b472af]1733    apr_status_t ret = APR_SUCCESS;
[0de1839]1734    int err = GNUTLS_E_SUCCESS;
[bd24203]1735
[8b472af]1736    /* Function pool, gets destroyed before exit. */
1737    apr_pool_t *pool;
1738    ret = apr_pool_create(&pool, s->process->pool);
1739    if (ret != APR_SUCCESS)
1740    {
1741        ap_log_error(APLOG_MARK, APLOG_ERR, ret, s,
1742                     "%s: failed to allocate function memory pool.", __func__);
1743        return ret;
1744    }
1745
[2cde026d]1746    /* allocate credentials structures */
1747    err = gnutls_certificate_allocate_credentials(&sc->proxy_x509_creds);
1748    if (err != GNUTLS_E_SUCCESS)
1749    {
1750        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1751                     "%s: Failed to initialize proxy credentials: (%d) %s",
1752                     __func__, err, gnutls_strerror(err));
1753        return APR_EGENERAL;
1754    }
1755    err = gnutls_anon_allocate_client_credentials(&sc->anon_client_creds);
1756    if (err != GNUTLS_E_SUCCESS)
1757    {
1758        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1759                     "%s: Failed to initialize anon credentials for proxy: "
1760                     "(%d) %s", __func__, err, gnutls_strerror(err));
1761        return APR_EGENERAL;
1762    }
1763
[4133f2d]1764    /* Check if the proxy priorities have been set, fail immediately
1765     * if not */
1766    if (sc->proxy_priorities_str == NULL)
1767    {
1768        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
1769                     "Host '%s:%d' is missing the GnuTLSProxyPriorities "
1770                     "directive!",
1771                     s->server_hostname, s->port);
1772        return APR_EGENERAL;
1773    }
1774    /* parse proxy priorities */
1775    const char *err_pos = NULL;
1776    err = gnutls_priority_init(&sc->proxy_priorities,
1777                               sc->proxy_priorities_str, &err_pos);
1778    if (err != GNUTLS_E_SUCCESS)
1779    {
1780        if (ret == GNUTLS_E_INVALID_REQUEST)
1781            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1782                         "%s: Syntax error parsing proxy priorities "
1783                         "string at: %s",
1784                         __func__, err_pos);
1785        else
1786            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1787                         "Error setting proxy priorities: %s (%d)",
1788                         gnutls_strerror(err), err);
1789        ret = APR_EGENERAL;
1790    }
1791
[bd24203]1792    /* load certificate and key for client auth, if configured */
[0de1839]1793    if (sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
1794    {
[8b472af]1795        char* cert_file = ap_server_root_relative(pool,
1796                                                  sc->proxy_x509_cert_file);
1797        char* key_file = ap_server_root_relative(pool,
1798                                                 sc->proxy_x509_key_file);
[0de1839]1799        err = gnutls_certificate_set_x509_key_file(sc->proxy_x509_creds,
[8b472af]1800                                                   cert_file,
1801                                                   key_file,
[0de1839]1802                                                   GNUTLS_X509_FMT_PEM);
1803        if (err != GNUTLS_E_SUCCESS)
1804        {
1805            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1806                         "%s: loading proxy client credentials failed: %s (%d)",
1807                         __func__, gnutls_strerror(err), err);
1808            ret = APR_EGENERAL;
1809        }
1810    }
1811    else if (!sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
1812    {
1813        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1814                     "%s: proxy key file not set!", __func__);
1815        ret = APR_EGENERAL;
1816    }
1817    else if (!sc->proxy_x509_cert_file && sc->proxy_x509_key_file)
1818    {
1819        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1820                     "%s: proxy certificate file not set!", __func__);
1821        ret = APR_EGENERAL;
1822    }
1823    else
1824        /* if both key and cert are NULL, client auth is not used */
[8b472af]1825        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
[0de1839]1826                     "%s: no client credentials for proxy", __func__);
1827
1828    /* must be set if the server certificate is to be checked */
1829    if (sc->proxy_x509_ca_file)
1830    {
[bd24203]1831        /* initialize the trust list */
1832        err = gnutls_x509_trust_list_init(&sc->proxy_x509_tl, 0);
1833        if (err != GNUTLS_E_SUCCESS)
1834        {
1835            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1836                         "%s: gnutls_x509_trust_list_init failed: %s (%d)",
1837                         __func__, gnutls_strerror(err), err);
1838            ret = APR_EGENERAL;
1839        }
1840
[8b472af]1841        char* ca_file = ap_server_root_relative(pool,
1842                                                sc->proxy_x509_ca_file);
1843        /* if no CRL is used, sc->proxy_x509_crl_file is NULL */
1844        char* crl_file = NULL;
1845        if (sc->proxy_x509_crl_file)
1846            crl_file = ap_server_root_relative(pool,
1847                                               sc->proxy_x509_crl_file);
1848
[bd24203]1849        /* returns number of loaded elements */
1850        err = gnutls_x509_trust_list_add_trust_file(sc->proxy_x509_tl,
[8b472af]1851                                                    ca_file,
1852                                                    crl_file,
[bd24203]1853                                                    GNUTLS_X509_FMT_PEM,
1854                                                    0 /* tl_flags */,
1855                                                    0 /* tl_vflags */);
[7d2123d]1856        if (err > 0)
[0de1839]1857            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
[bd24203]1858                         "%s: proxy CA trust list: %d structures loaded",
[0de1839]1859                         __func__, err);
[7d2123d]1860        else if (err == 0)
1861            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1862                         "%s: proxy CA trust list is empty (%d)",
1863                         __func__, err);
1864        else /* err < 0 */
[8b472af]1865        {
[7d2123d]1866            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1867                         "%s: error loading proxy CA trust list: %s (%d)",
1868                         __func__, gnutls_strerror(err), err);
[8b472af]1869            ret = APR_EGENERAL;
1870        }
[bd24203]1871
1872        /* attach trust list to credentials */
1873        gnutls_certificate_set_trust_list(sc->proxy_x509_creds,
1874                                          sc->proxy_x509_tl, 0);
[0de1839]1875    }
1876    else
1877        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
[bd24203]1878                     "%s: no CA trust list for proxy connections, "
[0de1839]1879                     "TLS connections will fail!", __func__);
1880
1881    gnutls_certificate_set_verify_function(sc->proxy_x509_creds,
1882                                           gtls_check_server_cert);
[8b472af]1883    apr_pool_destroy(pool);
[0de1839]1884    return ret;
1885}
Note: See TracBrowser for help on using the repository browser.