source: mod_gnutls/src/gnutls_hooks.c @ 0de1839

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 0de1839 was 0de1839, checked in by Thomas Klute <thomas2.klute@…>, 5 years ago

Support X.509 auth for TLS proxy connections

This commit adds support for X.509 certificate based authentication for
TLS proxy back end connections, including both server certificate
checking and (optionally) TLS client authentication. Some functions used
for this require GnuTLS 3.1.4 or later, so requirements change
accordingly.

Three new configuration parameters are added:

GnuTLSProxyCAFile FILEPATH

The given file must contain trusted CA certificates for server
verification. Required.

GnuTLSProxyKeyFile FILEPATH
GnuTLSProxyCertificateFile FILEPATH

Key and certificate for TLS client auth towards TLS back end servers. If
not set, TLS client auth is disabled.

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