source: mod_gnutls/src/gnutls_hooks.c @ f030883

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

Set GnuTLS priorities for proxy connections separately

Until now, proxy connections were configured with the same priorities as
the server side. This commit introduces the new configuration option
"GnuTLSProxyPriorities" to set the priorities for proxy connections
separately. Note that GnuTLSProxyPriorities MUST be set when
SSLProxyEngine is enabled.

Since the parameters to GnuTLSPriorities and GnuTLSProxyPriorities need
the same processing, mgs_set_priorities has been rewritten to select the
priority cache to write to based on the option name, rather than adding
a new function to handle GnuTLSProxyPriorities.

  • Property mode set to 100644
File size: 65.4 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            /* Check if the proxy priorities have been set */
466            if (sc->proxy_priorities == NULL)
467            {
468                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
469                             "Host '%s:%d' is missing the "
470                             "GnuTLSProxyPriorities directive!",
471                             s->server_hostname, s->port);
472                exit(-1);
473            }
474            /* Set up proxy credentials */
475            load_proxy_x509_credentials(s);
476        }
477    }
478
479
480    ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
481
482    {
483        const char* libvers = gnutls_check_version(NULL);
484        char* gnutls_version = NULL;
485        if(libvers && (gnutls_version = apr_psprintf(p, "GnuTLS/%s", libvers))) {
486            ap_add_version_component(p, gnutls_version);
487        } else {
488            // In case we could not create the above string go for the static version instead
489            ap_add_version_component(p, "GnuTLS/" GNUTLS_VERSION "-static");
490        }
491    }
492
493    return OK;
494}
495
496void mgs_hook_child_init(apr_pool_t * p, server_rec * s) {
497    apr_status_t rv = APR_SUCCESS;
498    mgs_srvconf_rec *sc = ap_get_module_config(s->module_config,
499            &gnutls_module);
500
501    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
502    if (sc->cache_type != mgs_cache_none) {
503        rv = mgs_cache_child_init(p, s, sc);
504        if (rv != APR_SUCCESS) {
505            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
506                    "[GnuTLS] - Failed to run Cache Init");
507        }
508    }
509    /* Block SIGPIPE Signals */
510    rv = apr_signal_block(SIGPIPE);
511    if(rv != APR_SUCCESS) {
512        /* error sending output */
513        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
514                "GnuTLS: Error Blocking SIGPIPE Signal!");
515    }
516}
517
518const char *mgs_hook_http_scheme(const request_rec * r) {
519    mgs_srvconf_rec *sc;
520
521    if (r == NULL)
522        return NULL;
523
524    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
525            server->module_config,
526            &gnutls_module);
527
528    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
529    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
530        return NULL;
531    }
532
533    return "https";
534}
535
536apr_port_t mgs_hook_default_port(const request_rec * r) {
537    mgs_srvconf_rec *sc;
538
539    if (r == NULL)
540        return 0;
541
542    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
543            server->module_config,
544            &gnutls_module);
545
546    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
547    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
548        return 0;
549    }
550
551    return 443;
552}
553
554#define MAX_HOST_LEN 255
555
556#if USING_2_1_RECENT
557
558typedef struct {
559    mgs_handle_t *ctxt;
560    mgs_srvconf_rec *sc;
561    const char *sni_name;
562} vhost_cb_rec;
563
564/**
565 * Matches the current vhost's ServerAlias directives
566 *
567 * @param x vhost callback record
568 * @param s server record
569 * @return true if a match, false otherwise
570 *
571 */
572int check_server_aliases(vhost_cb_rec *x, server_rec * s, mgs_srvconf_rec *tsc) {
573        apr_array_header_t *names;
574        int i,rv = 0;
575        char ** name;
576
577        /* Check ServerName First! */
578        if(apr_strnatcasecmp(x->sni_name, s->server_hostname) == 0) {
579                // We have a match, save this server configuration
580                x->sc = tsc;
581                rv = 1;
582        /* Check any ServerAlias directives */
583        } else if(s->names->nelts) {
584                names = s->names;
585                name = (char **)names->elts;
586                for (i = 0; i < names->nelts; ++i) {
587                        if (!name[i]) { continue; }
588                                if (apr_strnatcasecmp(x->sni_name, name[i]) == 0) {
589                                        // We have a match, save this server configuration
590                                        x->sc = tsc;
591                                        rv = 1;
592                        }
593                }
594        /* Wild any ServerAlias Directives */
595        } else if(s->wild_names->nelts) {
596                names = s->wild_names;
597        name = (char **)names->elts;
598                for (i = 0; i < names->nelts; ++i) {
599                        if (!name[i]) { continue; }
600                                if(apr_fnmatch(name[i], x->sni_name ,
601                                                                APR_FNM_CASE_BLIND|
602                                                                APR_FNM_PERIOD|
603                                                                APR_FNM_PATHNAME|
604                                                                APR_FNM_NOESCAPE) == APR_SUCCESS) {
605                                x->sc = tsc;
606                                rv = 1;
607                        }
608                }
609        }
610        return rv;
611}
612
613static int vhost_cb(void *baton, conn_rec * conn __attribute__((unused)), server_rec * s) {
614    mgs_srvconf_rec *tsc;
615    vhost_cb_rec *x = baton;
616    int ret;
617
618    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
619    tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
620            &gnutls_module);
621
622    if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
623        return 0;
624    }
625
626    if (tsc->certs_x509_chain_num > 0) {
627        /* why are we doing this check? */
628        ret = gnutls_x509_crt_check_hostname(tsc->certs_x509_chain[0], s->server_hostname);
629        if (0 == ret)
630            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
631                         "GnuTLS: Error checking certificate for hostname "
632                         "'%s'", s->server_hostname);
633    } else {
634        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
635                     "GnuTLS: SNI request for '%s' but no X.509 certs available at all",
636                     s->server_hostname);
637    }
638        return check_server_aliases(x, s, tsc);
639}
640#endif
641
642mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
643{
644    int rv;
645    unsigned int sni_type;
646    size_t data_len = MAX_HOST_LEN;
647    char sni_name[MAX_HOST_LEN];
648    mgs_handle_t *ctxt;
649#if USING_2_1_RECENT
650    vhost_cb_rec cbx;
651#else
652    server_rec *s;
653    mgs_srvconf_rec *tsc;
654#endif
655
656    if (session == NULL)
657        return NULL;
658
659    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
660    ctxt = gnutls_transport_get_ptr(session);
661
662    rv = gnutls_server_name_get(ctxt->session, sni_name,
663            &data_len, &sni_type, 0);
664
665    if (rv != 0) {
666        return NULL;
667    }
668
669    if (sni_type != GNUTLS_NAME_DNS) {
670        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
671                ctxt->c->base_server,
672                "GnuTLS: Unknown type '%d' for SNI: "
673                "'%s'", sni_type, sni_name);
674        return NULL;
675    }
676
677    /**
678     * Code in the Core already sets up the c->base_server as the base
679     * for this IP/Port combo.  Trust that the core did the 'right' thing.
680     */
681#if USING_2_1_RECENT
682    cbx.ctxt = ctxt;
683    cbx.sc = NULL;
684    cbx.sni_name = sni_name;
685
686    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
687    if (rv == 1) {
688        return cbx.sc;
689    }
690#else
691    for (s = ap_server_conf; s; s = s->next) {
692
693        tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
694                                                       &gnutls_module);
695
696        if (tsc->enabled != GNUTLS_ENABLED_TRUE) { continue; }
697
698        if(check_server_aliases(x, s, tsc)) {
699            return tsc;
700        }
701    }
702#endif
703    return NULL;
704}
705
706/*
707 * This function is intended as a cleanup handler for connections
708 * using GnuTLS.
709 *
710 * @param data must point to the mgs_handle_t associated with the
711 * connection
712 */
713static apr_status_t cleanup_gnutls_session(void *data)
714{
715    /* nothing to do */
716    if (data == NULL)
717        return APR_SUCCESS;
718
719    /* check if session needs closing */
720    mgs_handle_t *ctxt = (mgs_handle_t *) data;
721    if (ctxt->session != NULL)
722    {
723        int ret;
724        /* Try A Clean Shutdown */
725        do
726            ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
727        while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
728        if (ret != GNUTLS_E_SUCCESS)
729            ap_log_cerror(APLOG_MARK, APLOG_INFO, ret, ctxt->c,
730                          "%s: error while closing TLS %sconnection: %s (%d)",
731                          __func__, IS_PROXY_STR(ctxt),
732                          gnutls_strerror(ret), ret);
733        else
734            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, ret, ctxt->c,
735                          "%s: TLS %sconnection closed.",
736                          __func__, IS_PROXY_STR(ctxt));
737        /* De-Initialize Session */
738        gnutls_deinit(ctxt->session);
739        ctxt->session = NULL;
740    }
741    return APR_SUCCESS;
742}
743
744static void create_gnutls_handle(conn_rec * c)
745{
746    /* Get mod_gnutls server configuration */
747    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
748            ap_get_module_config(c->base_server->module_config, &gnutls_module);
749
750    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
751
752    /* Get connection specific configuration */
753    mgs_handle_t *ctxt = (mgs_handle_t *) ap_get_module_config(c->conn_config, &gnutls_module);
754    if (ctxt == NULL)
755    {
756        ctxt = apr_pcalloc(c->pool, sizeof (*ctxt));
757        ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
758        ctxt->is_proxy = GNUTLS_ENABLED_FALSE;
759    }
760    ctxt->enabled = GNUTLS_ENABLED_TRUE;
761    ctxt->c = c;
762    ctxt->sc = sc;
763    ctxt->status = 0;
764    ctxt->input_rc = APR_SUCCESS;
765    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
766    ctxt->input_cbuf.length = 0;
767    ctxt->output_rc = APR_SUCCESS;
768    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
769    ctxt->output_blen = 0;
770    ctxt->output_length = 0;
771
772    /* Initialize GnuTLS Library */
773    int err = 0;
774    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
775    {
776        /* this is an outgoing proxy connection, client mode */
777        err = gnutls_init(&ctxt->session, GNUTLS_CLIENT);
778        if (err != GNUTLS_E_SUCCESS)
779            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
780                          "gnutls_init for proxy connection failed: %s (%d)",
781                          gnutls_strerror(err), err);
782        err = gnutls_session_ticket_enable_client(ctxt->session);
783        if (err != GNUTLS_E_SUCCESS)
784            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
785                          "gnutls_session_ticket_enable_client failed: %s (%d)",
786                          gnutls_strerror(err), err);
787        /* Try to close and deinit the session when the connection
788         * pool is cleared. Note that mod_proxy might not close
789         * connections immediately, if you need that, look at the
790         * "proxy-nokeepalive" environment variable for
791         * mod_proxy_http. */
792        apr_pool_pre_cleanup_register(c->pool, ctxt, cleanup_gnutls_session);
793    }
794    else
795    {
796        /* incoming connection, server mode */
797        err = gnutls_init(&ctxt->session, GNUTLS_SERVER);
798        if (err != GNUTLS_E_SUCCESS)
799            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
800                          "gnutls_init for server side failed: %s (%d)",
801                          gnutls_strerror(err), err);
802        /* Initialize Session Tickets */
803        if (session_ticket_key.data != NULL && ctxt->sc->tickets != 0)
804        {
805            err = gnutls_session_ticket_enable_server(ctxt->session, &session_ticket_key);
806            if (err != GNUTLS_E_SUCCESS)
807                ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
808                              "gnutls_session_ticket_enable_server failed: %s (%d)",
809                              gnutls_strerror(err), err);
810        }
811    }
812
813    /* Set Default Priority */
814        err = gnutls_priority_set_direct(ctxt->session, "NORMAL", NULL);
815    if (err != GNUTLS_E_SUCCESS)
816        ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c, "gnutls_priority_set_direct failed!");
817    /* Set Handshake function */
818    gnutls_handshake_set_post_client_hello_function(ctxt->session,
819            mgs_select_virtual_server_cb);
820
821    /* Set GnuTLS user pointer, so we can access the module session
822     * context in GnuTLS callbacks */
823    gnutls_session_set_ptr(ctxt->session, ctxt);
824
825    /* If mod_gnutls is the TLS server, mgs_select_virtual_server_cb
826     * will load appropriate credentials during handshake. However,
827     * when handling a proxy backend connection, mod_gnutls acts as
828     * TLS client and credentials must be loaded here. */
829    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
830    {
831        /* Set anonymous client credentials for proxy connections */
832        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_ANON,
833                               ctxt->sc->anon_client_creds);
834        /* Set x509 credentials */
835        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE,
836                               ctxt->sc->proxy_x509_creds);
837        /* Load priorities from the server configuration */
838        err = gnutls_priority_set(ctxt->session, ctxt->sc->proxy_priorities);
839        if (err != GNUTLS_E_SUCCESS)
840            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
841                          "%s: setting priorities for proxy connection "
842                          "failed: %s (%d)",
843                          __func__, gnutls_strerror(err), err);
844    }
845
846    /* Initialize Session Cache */
847    mgs_cache_session_init(ctxt);
848
849    /* Set pull, push & ptr functions */
850    gnutls_transport_set_pull_function(ctxt->session,
851            mgs_transport_read);
852    gnutls_transport_set_push_function(ctxt->session,
853            mgs_transport_write);
854    gnutls_transport_set_ptr(ctxt->session, ctxt);
855    /* Add IO filters */
856    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME,
857            ctxt, NULL, c);
858    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
859            ctxt, NULL, c);
860}
861
862int mgs_hook_pre_connection(conn_rec * c, void *csd __attribute__((unused)))
863{
864    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
865
866    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
867        ap_get_module_config(c->base_server->module_config, &gnutls_module);
868    mgs_handle_t *ctxt = (mgs_handle_t *)
869        ap_get_module_config(c->conn_config, &gnutls_module);
870
871    if ((sc && (!sc->enabled)) || (ctxt && ctxt->enabled == GNUTLS_ENABLED_FALSE))
872    {
873        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s declined connection",
874                      __func__);
875        return DECLINED;
876    }
877
878    create_gnutls_handle(c);
879    return OK;
880}
881
882int mgs_hook_fixups(request_rec * r) {
883    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
884    char buf[AP_IOBUFSIZE];
885    const char *tmp;
886    size_t len;
887    mgs_handle_t *ctxt;
888    int rv = OK;
889
890    if (r == NULL)
891        return DECLINED;
892
893    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
894    apr_table_t *env = r->subprocess_env;
895
896    ctxt = ap_get_module_config(r->connection->conn_config,
897                                &gnutls_module);
898
899    if (!ctxt || ctxt->enabled != GNUTLS_ENABLED_TRUE || ctxt->session == NULL)
900    {
901        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "request declined in %s", __func__);
902        return DECLINED;
903    }
904
905    apr_table_setn(env, "HTTPS", "on");
906
907    apr_table_setn(env, "SSL_VERSION_LIBRARY",
908            "GnuTLS/" LIBGNUTLS_VERSION);
909    apr_table_setn(env, "SSL_VERSION_INTERFACE",
910            "mod_gnutls/" MOD_GNUTLS_VERSION);
911
912    apr_table_setn(env, "SSL_PROTOCOL",
913            gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
914
915    /* should have been called SSL_CIPHERSUITE instead */
916    apr_table_setn(env, "SSL_CIPHER",
917            gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
918                                         gnutls_cipher_get(ctxt->session),
919                                         gnutls_mac_get(ctxt->session)));
920
921    apr_table_setn(env, "SSL_COMPRESS_METHOD",
922            gnutls_compression_get_name(gnutls_compression_get(ctxt->session)));
923
924#ifdef ENABLE_SRP
925    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
926        tmp = gnutls_srp_server_get_username(ctxt->session);
927        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
928    } else {
929        apr_table_unset(env, "SSL_SRP_USER");
930    }
931#endif
932
933    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
934        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
935
936    unsigned int key_size = 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
937    tmp = apr_psprintf(r->pool, "%u", key_size);
938
939    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
940
941    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
942
943    apr_table_setn(env, "SSL_CIPHER_EXPORT",
944            (key_size <= 40) ? "true" : "false");
945
946    int dhsize = gnutls_dh_get_prime_bits(ctxt->session);
947    if (dhsize > 0) {
948        tmp = apr_psprintf(r->pool, "%d", dhsize);
949        apr_table_setn(env, "SSL_DH_PRIME_BITS", tmp);
950    }
951
952    len = sizeof (sbuf);
953    gnutls_session_get_id(ctxt->session, sbuf, &len);
954    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
955    apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
956
957    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
958                mgs_add_common_cert_vars(r, ctxt->sc->certs_x509_chain[0], 0, ctxt->sc->export_certificates_size);
959        } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
960        mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_pgp, 0, ctxt->sc->export_certificates_size);
961        }
962
963    return rv;
964}
965
966int mgs_hook_authz(request_rec * r) {
967    int rv;
968    mgs_handle_t *ctxt;
969    mgs_dirconf_rec *dc;
970
971    if (r == NULL)
972        return DECLINED;
973
974    dc = ap_get_module_config(r->per_dir_config, &gnutls_module);
975
976    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
977    ctxt =
978            ap_get_module_config(r->connection->conn_config,
979            &gnutls_module);
980
981    if (!ctxt || ctxt->session == NULL) {
982        return DECLINED;
983    }
984
985    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
986        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
987                "GnuTLS: Directory set to Ignore Client Certificate!");
988    } else {
989        if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
990            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
991                    "GnuTLS: Attempting to rehandshake with peer. %d %d",
992                    ctxt->sc->client_verify_mode,
993                    dc->client_verify_mode);
994
995            /* If we already have a client certificate, there's no point in
996             * re-handshaking... */
997            rv = mgs_cert_verify(r, ctxt);
998            if (rv != DECLINED && rv != HTTP_FORBIDDEN)
999                return rv;
1000
1001            gnutls_certificate_server_set_request
1002                    (ctxt->session, dc->client_verify_mode);
1003
1004            if (mgs_rehandshake(ctxt) != 0) {
1005                return HTTP_FORBIDDEN;
1006            }
1007        } else if (ctxt->sc->client_verify_mode ==
1008                GNUTLS_CERT_IGNORE) {
1009#if MOD_GNUTLS_DEBUG
1010            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1011                    "GnuTLS: Peer is set to IGNORE");
1012#endif
1013            return DECLINED;
1014        }
1015        rv = mgs_cert_verify(r, ctxt);
1016        if (rv != DECLINED
1017            && (rv != HTTP_FORBIDDEN
1018                || dc->client_verify_mode == GNUTLS_CERT_REQUIRE
1019                || (dc->client_verify_mode == -1
1020                    && ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUIRE)))
1021        {
1022            return rv;
1023        }
1024    }
1025
1026    return DECLINED;
1027}
1028
1029/* variables that are not sent by default:
1030 *
1031 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
1032 * SSL_SERVER_CERT      string  PEM-encoded client certificate
1033 */
1034
1035/* @param side is either 0 for SERVER or 1 for CLIENT
1036 *
1037 * @param export_cert_size (int) maximum size for environment variable
1038 * to use for the PEM-encoded certificate (0 means do not export)
1039 */
1040#define MGS_SIDE(suffix) ((side==0) ? "SSL_SERVER" suffix : "SSL_CLIENT" suffix)
1041
1042static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size) {
1043    unsigned char sbuf[64]; /* buffer to hold serials */
1044    char buf[AP_IOBUFSIZE];
1045    const char *tmp;
1046    char *tmp2;
1047    size_t len;
1048    int ret, i;
1049
1050    if (r == NULL)
1051        return;
1052
1053    apr_table_t *env = r->subprocess_env;
1054
1055    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1056    if (export_cert_size > 0) {
1057        len = 0;
1058        ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &len);
1059        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1060            if (len >= export_cert_size) {
1061                apr_table_setn(env, MGS_SIDE("_CERT"), "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1062                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1063                              "GnuTLS: Failed to export too-large X.509 certificate to environment");
1064            } else {
1065                char* cert_buf = apr_palloc(r->pool, len + 1);
1066                if (cert_buf != NULL && gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) {
1067                    cert_buf[len] = 0;
1068                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
1069                } else {
1070                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1071                                  "GnuTLS: failed to export X.509 certificate");
1072                }
1073            }
1074        } else {
1075            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1076                          "GnuTLS: dazed and confused about X.509 certificate size");
1077        }
1078    }
1079
1080    len = sizeof (buf);
1081    gnutls_x509_crt_get_dn(cert, buf, &len);
1082    apr_table_setn(env, MGS_SIDE("_S_DN"), apr_pstrmemdup(r->pool, buf, len));
1083
1084    len = sizeof (buf);
1085    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
1086    apr_table_setn(env, MGS_SIDE("_I_DN"), apr_pstrmemdup(r->pool, buf, len));
1087
1088    len = sizeof (sbuf);
1089    gnutls_x509_crt_get_serial(cert, sbuf, &len);
1090    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
1091    apr_table_setn(env, MGS_SIDE("_M_SERIAL"), apr_pstrdup(r->pool, tmp));
1092
1093    ret = gnutls_x509_crt_get_version(cert);
1094    if (ret > 0)
1095        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1096                       apr_psprintf(r->pool, "%u", ret));
1097
1098    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "X.509");
1099
1100    tmp =
1101            mgs_time2sz(gnutls_x509_crt_get_expiration_time
1102            (cert), buf, sizeof (buf));
1103    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
1104
1105    tmp =
1106            mgs_time2sz(gnutls_x509_crt_get_activation_time
1107            (cert), buf, sizeof (buf));
1108    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
1109
1110    ret = gnutls_x509_crt_get_signature_algorithm(cert);
1111    if (ret >= 0) {
1112        apr_table_setn(env, MGS_SIDE("_A_SIG"),
1113                gnutls_sign_algorithm_get_name(ret));
1114    }
1115
1116    ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
1117    if (ret >= 0) {
1118        apr_table_setn(env, MGS_SIDE("_A_KEY"),
1119                gnutls_pk_algorithm_get_name(ret));
1120    }
1121
1122    /* export all the alternative names (DNS, RFC822 and URI) */
1123    for (i = 0; !(ret < 0); i++) {
1124        const char *san, *sanlabel;
1125        len = 0;
1126        ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1127                NULL, &len,
1128                NULL);
1129
1130        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1131            tmp2 = apr_palloc(r->pool, len + 1);
1132
1133            ret =
1134                    gnutls_x509_crt_get_subject_alt_name(cert, i,
1135                    tmp2,
1136                    &len,
1137                    NULL);
1138            tmp2[len] = 0;
1139
1140            sanlabel = apr_psprintf(r->pool, "%s%u", MGS_SIDE("_S_AN"), i);
1141            if (ret == GNUTLS_SAN_DNSNAME) {
1142                san = apr_psprintf(r->pool, "DNSNAME:%s", tmp2);
1143            } else if (ret == GNUTLS_SAN_RFC822NAME) {
1144                san = apr_psprintf(r->pool, "RFC822NAME:%s", tmp2);
1145            } else if (ret == GNUTLS_SAN_URI) {
1146                san = apr_psprintf(r->pool, "URI:%s", tmp2);
1147            } else {
1148                san = "UNSUPPORTED";
1149            }
1150            apr_table_setn(env, sanlabel, san);
1151        }
1152    }
1153}
1154
1155
1156/* @param side 0: server, 1: client
1157 *
1158 * @param export_cert_size (int) maximum size for environment variable
1159 * to use for the PEM-encoded certificate (0 means do not export)
1160 */
1161static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, size_t export_cert_size) {
1162
1163        unsigned char sbuf[64]; /* buffer to hold serials */
1164    char buf[AP_IOBUFSIZE];
1165    const char *tmp;
1166    size_t len;
1167    int ret;
1168
1169    if (r == NULL)
1170        return;
1171
1172    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1173    apr_table_t *env = r->subprocess_env;
1174
1175    if (export_cert_size > 0) {
1176        len = 0;
1177        ret = gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, NULL, &len);
1178        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1179            if (len >= export_cert_size) {
1180                apr_table_setn(env, MGS_SIDE("_CERT"),
1181                               "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1182                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1183                              "GnuTLS: Failed to export too-large OpenPGP certificate to environment");
1184            } else {
1185                char* cert_buf = apr_palloc(r->pool, len + 1);
1186                if (cert_buf != NULL && gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0) {
1187                    cert_buf[len] = 0;
1188                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
1189                } else {
1190                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1191                                  "GnuTLS: failed to export OpenPGP certificate");
1192                }
1193            }
1194        } else {
1195            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1196                          "GnuTLS: dazed and confused about OpenPGP certificate size");
1197        }
1198    }
1199
1200    len = sizeof (buf);
1201    gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
1202    apr_table_setn(env, MGS_SIDE("_NAME"), apr_pstrmemdup(r->pool, buf, len));
1203
1204    len = sizeof (sbuf);
1205    gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
1206    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
1207    apr_table_setn(env, MGS_SIDE("_FINGERPRINT"), apr_pstrdup(r->pool, tmp));
1208
1209    ret = gnutls_openpgp_crt_get_version(cert);
1210    if (ret > 0)
1211        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1212                       apr_psprintf(r->pool, "%u", ret));
1213
1214    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "OPENPGP");
1215
1216    tmp =
1217            mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
1218            (cert), buf, sizeof (buf));
1219    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
1220
1221    tmp =
1222            mgs_time2sz(gnutls_openpgp_crt_get_creation_time
1223            (cert), buf, sizeof (buf));
1224    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
1225
1226    ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
1227    if (ret >= 0) {
1228        apr_table_setn(env, MGS_SIDE("_A_KEY"), gnutls_pk_algorithm_get_name(ret));
1229    }
1230
1231}
1232
1233/* TODO: Allow client sending a X.509 certificate chain */
1234static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) {
1235    const gnutls_datum_t *cert_list;
1236    unsigned int cert_list_size, status;
1237    int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1238    unsigned int ch_size = 0;
1239
1240    union {
1241        gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1242        gnutls_openpgp_crt_t pgp;
1243    } cert;
1244    apr_time_t expiration_time, cur_time;
1245
1246    if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1247        return HTTP_FORBIDDEN;
1248
1249    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1250    cert_list =
1251            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1252
1253    if (cert_list == NULL || cert_list_size == 0) {
1254        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1255         */
1256        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1257            return OK;
1258
1259        /* no certificate provided by the client, but one was required. */
1260        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1261                "GnuTLS: Failed to Verify Peer: "
1262                "Client did not submit a certificate");
1263        return HTTP_FORBIDDEN;
1264    }
1265
1266    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1267        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1268                "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1269                cert_list_size);
1270
1271        for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1272            gnutls_x509_crt_init(&cert.x509[ch_size]);
1273            rv = gnutls_x509_crt_import(cert.x509[ch_size],
1274                    &cert_list[ch_size],
1275                    GNUTLS_X509_FMT_DER);
1276            // When failure to import, leave the loop
1277            if (rv != GNUTLS_E_SUCCESS) {
1278                if (ch_size < 1) {
1279                    ap_log_rerror(APLOG_MARK,
1280                            APLOG_INFO, 0, r,
1281                            "GnuTLS: Failed to Verify Peer: "
1282                            "Failed to import peer certificates.");
1283                    ret = HTTP_FORBIDDEN;
1284                    goto exit;
1285                }
1286                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1287                        "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1288                        ch_size);
1289                rv = GNUTLS_E_SUCCESS;
1290                break;
1291            }
1292        }
1293    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
1294        if (cert_list_size > 1) {
1295            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1296                    "GnuTLS: Failed to Verify Peer: "
1297                    "Chained Client Certificates are not supported.");
1298            return HTTP_FORBIDDEN;
1299        }
1300
1301        gnutls_openpgp_crt_init(&cert.pgp);
1302        rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0],
1303                GNUTLS_OPENPGP_FMT_RAW);
1304
1305    } else
1306        return HTTP_FORBIDDEN;
1307
1308    if (rv < 0) {
1309        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1310                "GnuTLS: Failed to Verify Peer: "
1311                "Failed to import peer certificates.");
1312        ret = HTTP_FORBIDDEN;
1313        goto exit;
1314    }
1315
1316    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1317        apr_time_ansi_put(&expiration_time,
1318                gnutls_x509_crt_get_expiration_time
1319                (cert.x509[0]));
1320
1321        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1322                      "GnuTLS: Verifying list of %d certificate(s) via method '%s'",
1323                      ch_size, mgs_readable_cvm(ctxt->sc->client_verify_method));
1324        switch(ctxt->sc->client_verify_method) {
1325        case mgs_cvm_cartel:
1326            rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1327                                             ctxt->sc->ca_list,
1328                                             ctxt->sc->ca_list_size,
1329                                             NULL, 0, 0, &status);
1330            break;
1331#ifdef ENABLE_MSVA
1332        case mgs_cvm_msva:
1333        {
1334            struct msv_response* resp = NULL;
1335            struct msv_query q = { .context="https", .peertype="client", .pkctype="x509pem" };
1336            msv_ctxt_t ctx = msv_ctxt_init(NULL);
1337            char cert_pem_buf[10 * 1024];
1338            size_t len = sizeof (cert_pem_buf);
1339
1340            rv = 0;
1341            if (gnutls_x509_crt_export(cert.x509[0], GNUTLS_X509_FMT_PEM, cert_pem_buf, &len) >= 0) {
1342                /* FIXME : put together a name from the cert we received, instead of hard-coding this value: */
1343                q.peername = mgs_x509_construct_uid(r, cert.x509[0]);
1344                q.pkcdata = cert_pem_buf;
1345                rv = msv_query_agent(ctx, q, &resp);
1346                if (rv == LIBMSV_ERROR_SUCCESS) {
1347                    status = 0;
1348                } else if (rv == LIBMSV_ERROR_INVALID) {
1349                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1350                                  "GnuTLS: Monkeysphere validation failed: (message: %s)", resp->message);
1351                    status = GNUTLS_CERT_INVALID;
1352                } else {
1353                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1354                                  "GnuTLS: Error communicating with the Monkeysphere Validation Agent: (%d) %s", rv, msv_strerror(ctx, rv));
1355                    status = GNUTLS_CERT_INVALID;
1356                    rv = -1;
1357                }
1358            } else {
1359                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1360                              "GnuTLS: Could not convert the client certificate to PEM format");
1361                status = GNUTLS_CERT_INVALID;
1362                rv = GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
1363            }
1364            msv_response_destroy(resp);
1365            msv_ctxt_destroy(ctx);
1366        }
1367            break;
1368#endif
1369        default:
1370            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1371                          "GnuTLS: Failed to Verify X.509 Peer: method '%s' is not supported",
1372                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1373        }
1374
1375    } else {
1376        apr_time_ansi_put(&expiration_time,
1377                gnutls_openpgp_crt_get_expiration_time
1378                (cert.pgp));
1379
1380        switch(ctxt->sc->client_verify_method) {
1381        case mgs_cvm_cartel:
1382            rv = gnutls_openpgp_crt_verify_ring(cert.pgp,
1383                                                ctxt->sc->pgp_list, 0,
1384                                                &status);
1385            break;
1386#ifdef ENABLE_MSVA
1387        case mgs_cvm_msva:
1388            /* need to set status and rv */
1389            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1390                          "GnuTLS:  OpenPGP verification via MSVA is not yet implemented");
1391            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1392            break;
1393#endif
1394        default:
1395            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1396                          "GnuTLS: Failed to Verify OpenPGP Peer: method '%s' is not supported",
1397                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1398        }
1399    }
1400
1401    if (rv < 0) {
1402        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1403                "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1404                rv, gnutls_strerror(rv));
1405        if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1406            ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1407                "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
1408        ret = HTTP_FORBIDDEN;
1409        goto exit;
1410    }
1411
1412    /* TODO: X509 CRL Verification. */
1413    /* May add later if anyone needs it.
1414     */
1415    /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1416
1417    cur_time = apr_time_now();
1418
1419    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1420        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1421                "GnuTLS: Could not find Signer for Peer Certificate");
1422    }
1423
1424    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
1425        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1426                "GnuTLS: Peer's Certificate signer is not a CA");
1427    }
1428
1429    if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
1430        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1431                "GnuTLS: Peer's Certificate is using insecure algorithms");
1432    }
1433
1434    if (status & GNUTLS_CERT_EXPIRED
1435            || status & GNUTLS_CERT_NOT_ACTIVATED) {
1436        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1437                "GnuTLS: Peer's Certificate signer is expired or not yet activated");
1438    }
1439
1440    if (status & GNUTLS_CERT_INVALID) {
1441        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1442                "GnuTLS: Peer Certificate is invalid.");
1443    } else if (status & GNUTLS_CERT_REVOKED) {
1444        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1445                "GnuTLS: Peer Certificate is revoked.");
1446    }
1447
1448    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1449        mgs_add_common_cert_vars(r, cert.x509[0], 1, ctxt->sc->export_certificates_size);
1450    else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP)
1451        mgs_add_common_pgpcert_vars(r, cert.pgp, 1, ctxt->sc->export_certificates_size);
1452
1453    {
1454        /* days remaining */
1455        unsigned long remain =
1456                (apr_time_sec(expiration_time) -
1457                apr_time_sec(cur_time)) / 86400;
1458        apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1459                apr_psprintf(r->pool, "%lu", remain));
1460    }
1461
1462    if (status == 0) {
1463        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1464                "SUCCESS");
1465        ret = OK;
1466    } else {
1467        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1468                "FAILED");
1469        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1470            ret = OK;
1471        else
1472            ret = HTTP_FORBIDDEN;
1473    }
1474
1475exit:
1476    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1477        unsigned int i;
1478        for (i = 0; i < ch_size; i++) {
1479            gnutls_x509_crt_deinit(cert.x509[i]);
1480        }
1481    } else if (gnutls_certificate_type_get(ctxt->session) ==
1482            GNUTLS_CRT_OPENPGP)
1483        gnutls_openpgp_crt_deinit(cert.pgp);
1484    return ret;
1485
1486
1487}
1488
1489#ifdef ENABLE_MSVA
1490/* this section of code is used only when trying to talk to the MSVA */
1491static const char* mgs_x509_leaf_oid_from_dn(apr_pool_t *pool, const char* oid, gnutls_x509_crt_t cert) {
1492    int rv=GNUTLS_E_SUCCESS, i;
1493    size_t sz=0, lastsz=0;
1494    char* data=NULL;
1495
1496    i = -1;
1497    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1498        i++;
1499        lastsz=sz;
1500        sz=0;
1501        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i, 0, NULL, &sz);
1502    }
1503    if (i > 0) {
1504        data = apr_palloc(pool, lastsz);
1505        sz=lastsz;
1506        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i-1, 0, data, &sz);
1507        if (rv == GNUTLS_E_SUCCESS)
1508            return data;
1509    }
1510    return NULL;
1511}
1512
1513static const char* mgs_x509_first_type_from_san(apr_pool_t *pool, gnutls_x509_subject_alt_name_t target, gnutls_x509_crt_t cert) {
1514    int rv=GNUTLS_E_SUCCESS;
1515    size_t sz;
1516    char* data=NULL;
1517    unsigned int i;
1518    gnutls_x509_subject_alt_name_t thistype;
1519
1520    i = 0;
1521    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1522        sz = 0;
1523        rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, NULL, &sz, &thistype, NULL);
1524        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && thistype == target) {
1525            data = apr_palloc(pool, sz);
1526            rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, data, &sz, &thistype, NULL);
1527            if (rv >=0 && (thistype == target))
1528                return data;
1529        }
1530        i++;
1531    }
1532    return NULL;
1533}
1534
1535
1536/* Create a string representing a candidate User ID from an X.509
1537 * certificate
1538
1539 * We need this for client certification because a client gives us a
1540 * certificate, but doesn't tell us (in any other way) who they are
1541 * trying to authenticate as.
1542
1543 * TODO: we might need another parallel for OpenPGP, but for that it's
1544 * much simpler: we can just assume that the first User ID marked as
1545 * "primary" (or the first User ID, period) is the identity the user
1546 * is trying to present as.
1547
1548 * one complaint might be "but the user wanted to be another identity,
1549 * which is also in the certificate (e.g. in a SubjectAltName)"
1550 * However, given that any user can regenerate their own X.509
1551 * certificate with their own public key content, they should just do
1552 * so, and not expect us to guess at their identity :)
1553
1554 * This function allocates it's response from the pool given it.  When
1555 * that pool is reclaimed, the response will also be deallocated.
1556
1557 * FIXME: what about extracting a server-style cert
1558 *        (e.g. https://imposter.example) from the DN or any sAN?
1559
1560 * FIXME: what if we want to call this outside the context of a
1561 *        request?  That complicates the logging.
1562 */
1563static const char* mgs_x509_construct_uid(request_rec *r, gnutls_x509_crt_t cert) {
1564    /* basic strategy, assuming humans are the users: we are going to
1565     * try to reconstruct a "conventional" User ID by pulling in a
1566     * name, comment, and e-mail address.
1567     */
1568    apr_pool_t *pool = r->pool;
1569    const char *name=NULL, *comment=NULL, *email=NULL;
1570    const char *ret=NULL;
1571    /* subpool for temporary allocation: */
1572    apr_pool_t *sp=NULL;
1573
1574    if (APR_SUCCESS != apr_pool_create(&sp, pool))
1575        return NULL; /* i'm assuming that libapr would log this kind
1576                      * of error on its own */
1577
1578     /* Name
1579
1580     the name comes from the leaf commonName of the cert's Subject.
1581
1582     (MAYBE: should we look at trying to assemble a candidate from
1583             givenName, surName, suffix, etc?  the "name" field
1584             appears to be case-insensitive, which seems problematic
1585             from what we expect; see:
1586             http://www.itu.int/rec/T-REC-X.520-200102-s/e )
1587
1588     (MAYBE: should we try pulling a commonName or otherName or
1589             something from subjectAltName? see:
1590             https://tools.ietf.org/html/rfc5280#section-4.2.1.6
1591             GnuTLS does not support looking for Common Names in the
1592             SAN yet)
1593     */
1594    name = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_COMMON_NAME, cert);
1595
1596    /* Comment
1597
1598       I am inclined to punt on this for now, as Comment has been so
1599       atrociously misused in OpenPGP.  Perhaps if there is a
1600       pseudonym (OID 2.5.4.65, aka GNUTLS_OID_X520_PSEUDONYM) field
1601       in the subject or sAN?
1602    */
1603    comment = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_PSEUDONYM, cert);
1604
1605    /* E-mail
1606
1607       This should be the the first rfc822Name from the sAN.
1608
1609       failing that, we'll take the leaf email in the certificate's
1610       subject; this is a deprecated use though.
1611     */
1612    email = mgs_x509_first_type_from_san(sp, GNUTLS_SAN_RFC822NAME, cert);
1613    if (email == NULL)
1614        email = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_PKCS9_EMAIL, cert);
1615
1616    /* assemble all the parts: */
1617
1618    /* must have at least a name or an e-mail. */
1619    if (name == NULL && email == NULL) {
1620        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1621                "GnuTLS: Need either a name or an e-mail address to get a User ID from an X.509 certificate.");
1622        goto end;
1623    }
1624    if (name) {
1625        if (comment) {
1626            if (email) {
1627                ret = apr_psprintf(pool, "%s (%s) <%s>", name, comment, email);
1628            } else {
1629                ret = apr_psprintf(pool, "%s (%s)", name, comment);
1630            }
1631        } else {
1632            if (email) {
1633                ret = apr_psprintf(pool, "%s <%s>", name, email);
1634            } else {
1635                ret = apr_pstrdup(pool, name);
1636            }
1637        }
1638    } else {
1639        if (comment) {
1640            ret = apr_psprintf(pool, "(%s) <%s>", comment, email);
1641        } else {
1642            ret = apr_psprintf(pool, "<%s>", email);
1643        }
1644    }
1645
1646end:
1647    apr_pool_destroy(sp);
1648    return ret;
1649}
1650#endif /* ENABLE_MSVA */
1651
1652static int mgs_status_hook(request_rec *r, int flags __attribute__((unused)))
1653{
1654    mgs_srvconf_rec *sc;
1655
1656    if (r == NULL)
1657        return OK;
1658
1659    sc = (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, &gnutls_module);
1660
1661    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1662
1663    ap_rputs("<hr>\n", r);
1664    ap_rputs("<h2>GnuTLS Information:</h2>\n<dl>\n", r);
1665
1666    ap_rprintf(r, "<dt>GnuTLS version:</dt><dd>%s</dd>\n", gnutls_check_version(NULL));
1667    ap_rputs("<dt>Built against:</dt><dd>" GNUTLS_VERSION "</dd>\n", r);
1668    ap_rprintf(r, "<dt>using TLS:</dt><dd>%s</dd>\n", (sc->enabled == GNUTLS_ENABLED_FALSE ? "no" : "yes"));
1669    if (sc->enabled != GNUTLS_ENABLED_FALSE) {
1670        mgs_handle_t* ctxt;
1671        ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module);
1672        if (ctxt && ctxt->session != NULL) {
1673#if GNUTLS_VERSION_MAJOR < 3
1674            ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n",
1675                gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
1676                gnutls_cipher_get(ctxt->session),
1677                gnutls_mac_get(ctxt->session)));
1678#else
1679            char* z = NULL;
1680            z = gnutls_session_get_desc(ctxt->session);
1681            if (z) {
1682                ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n", z);
1683                gnutls_free(z);
1684            }
1685#endif
1686        }
1687    }
1688
1689    ap_rputs("</dl>\n", r);
1690    return OK;
1691}
1692
1693
1694
1695/*
1696 * Callback to check the server certificate for proxy HTTPS
1697 * connections, to be used with
1698 * gnutls_certificate_set_verify_function.
1699
1700 * Returns: 0 if certificate check was successful (certificate
1701 * trusted), non-zero otherwise (error during check or untrusted
1702 * certificate).
1703 */
1704static int gtls_check_server_cert(gnutls_session_t session)
1705{
1706    mgs_handle_t *ctxt = (mgs_handle_t *) gnutls_session_get_ptr(session);
1707    unsigned int status;
1708
1709    /* Get peer hostname from a note left by mod_proxy */
1710    const char *peer_hostname =
1711        apr_table_get(ctxt->c->notes, "proxy-request-hostname");
1712    if (peer_hostname == NULL)
1713        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
1714                      "%s: proxy-request-hostname is NULL, cannot check "
1715                      "peer's hostname", __func__);
1716
1717    /* Verify certificate, including hostname match. Should
1718     * peer_hostname be NULL for some reason, the name is not
1719     * checked. */
1720    int err = gnutls_certificate_verify_peers3(session, peer_hostname,
1721                                               &status);
1722    if (err != GNUTLS_E_SUCCESS)
1723    {
1724        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
1725                      "%s: server certificate check failed: %s (%d)",
1726                      __func__, gnutls_strerror(err), err);
1727        return err;
1728    }
1729
1730    gnutls_datum_t * out = gnutls_malloc(sizeof(gnutls_datum_t));
1731    /* GNUTLS_CRT_X509: ATM, only X509 is supported for proxy certs
1732     * 0: according to function API, the last argument should be 0 */
1733    err = gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509,
1734                                                       out, 0);
1735    if (err != GNUTLS_E_SUCCESS)
1736        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
1737                      "%s: server verify print failed: %s (%d)",
1738                      __func__, gnutls_strerror(err), err);
1739    else
1740    {
1741        /* If the certificate is trusted, logging the result is just
1742         * nice for debugging. But if the back end server provided an
1743         * untrusted certificate, warn! */
1744        int level = (status == 0 ? APLOG_DEBUG : APLOG_WARNING);
1745        ap_log_cerror(APLOG_MARK, level, 0, ctxt->c,
1746                      "%s: server certificate verify result: %s",
1747                      __func__, out->data);
1748    }
1749
1750    gnutls_free(out);
1751    return status;
1752}
1753
1754
1755
1756static apr_status_t load_proxy_x509_credentials(server_rec *s)
1757{
1758    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1759        ap_get_module_config(s->module_config, &gnutls_module);
1760
1761    if (sc == NULL)
1762        return APR_EGENERAL;
1763
1764    apr_status_t ret = APR_SUCCESS;
1765    int err = GNUTLS_E_SUCCESS;
1766
1767    /* Function pool, gets destroyed before exit. */
1768    apr_pool_t *pool;
1769    ret = apr_pool_create(&pool, s->process->pool);
1770    if (ret != APR_SUCCESS)
1771    {
1772        ap_log_error(APLOG_MARK, APLOG_ERR, ret, s,
1773                     "%s: failed to allocate function memory pool.", __func__);
1774        return ret;
1775    }
1776
1777    /* load certificate and key for client auth, if configured */
1778    if (sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
1779    {
1780        char* cert_file = ap_server_root_relative(pool,
1781                                                  sc->proxy_x509_cert_file);
1782        char* key_file = ap_server_root_relative(pool,
1783                                                 sc->proxy_x509_key_file);
1784        err = gnutls_certificate_set_x509_key_file(sc->proxy_x509_creds,
1785                                                   cert_file,
1786                                                   key_file,
1787                                                   GNUTLS_X509_FMT_PEM);
1788        if (err != GNUTLS_E_SUCCESS)
1789        {
1790            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1791                         "%s: loading proxy client credentials failed: %s (%d)",
1792                         __func__, gnutls_strerror(err), err);
1793            ret = APR_EGENERAL;
1794        }
1795    }
1796    else if (!sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
1797    {
1798        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1799                     "%s: proxy key file not set!", __func__);
1800        ret = APR_EGENERAL;
1801    }
1802    else if (!sc->proxy_x509_cert_file && sc->proxy_x509_key_file)
1803    {
1804        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1805                     "%s: proxy certificate file not set!", __func__);
1806        ret = APR_EGENERAL;
1807    }
1808    else
1809        /* if both key and cert are NULL, client auth is not used */
1810        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1811                     "%s: no client credentials for proxy", __func__);
1812
1813    /* must be set if the server certificate is to be checked */
1814    if (sc->proxy_x509_ca_file)
1815    {
1816        /* initialize the trust list */
1817        err = gnutls_x509_trust_list_init(&sc->proxy_x509_tl, 0);
1818        if (err != GNUTLS_E_SUCCESS)
1819        {
1820            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1821                         "%s: gnutls_x509_trust_list_init failed: %s (%d)",
1822                         __func__, gnutls_strerror(err), err);
1823            ret = APR_EGENERAL;
1824        }
1825
1826        char* ca_file = ap_server_root_relative(pool,
1827                                                sc->proxy_x509_ca_file);
1828        /* if no CRL is used, sc->proxy_x509_crl_file is NULL */
1829        char* crl_file = NULL;
1830        if (sc->proxy_x509_crl_file)
1831            crl_file = ap_server_root_relative(pool,
1832                                               sc->proxy_x509_crl_file);
1833
1834        /* returns number of loaded elements */
1835        err = gnutls_x509_trust_list_add_trust_file(sc->proxy_x509_tl,
1836                                                    ca_file,
1837                                                    crl_file,
1838                                                    GNUTLS_X509_FMT_PEM,
1839                                                    0 /* tl_flags */,
1840                                                    0 /* tl_vflags */);
1841        if (err > 0)
1842            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
1843                         "%s: proxy CA trust list: %d structures loaded",
1844                         __func__, err);
1845        else if (err == 0)
1846            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1847                         "%s: proxy CA trust list is empty (%d)",
1848                         __func__, err);
1849        else /* err < 0 */
1850        {
1851            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1852                         "%s: error loading proxy CA trust list: %s (%d)",
1853                         __func__, gnutls_strerror(err), err);
1854            ret = APR_EGENERAL;
1855        }
1856
1857        /* attach trust list to credentials */
1858        gnutls_certificate_set_trust_list(sc->proxy_x509_creds,
1859                                          sc->proxy_x509_tl, 0);
1860    }
1861    else
1862        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1863                     "%s: no CA trust list for proxy connections, "
1864                     "TLS connections will fail!", __func__);
1865
1866    gnutls_certificate_set_verify_function(sc->proxy_x509_creds,
1867                                           gtls_check_server_cert);
1868    apr_pool_destroy(pool);
1869    return ret;
1870}
Note: See TracBrowser for help on using the repository browser.