source: mod_gnutls/src/gnutls_hooks.c @ a2b4ab6

debian/masterdebian/stretch-backportsproxy-ticketupstream
Last change on this file since a2b4ab6 was a2b4ab6, checked in by Thomas Klute <thomas2.klute@…>, 3 years ago

Use GnuTLS known DH parameters

If the user does not configure the DH parameters to use, mod_gnutls
now estimates the GnuTLS security parameter based on the private key
and uses the matching DH group built into GnuTLS. Using one of the
known DH groups is recommended by the GnuTLS developers.

Using built-in DH groups requires compiling against GnuTLS version
3.5.6 or newer. Otherwise the ffdhe2048 DH group as defined in RFC
7919, Appendix A.1 is the new default, which is the built-in for
security parameter "medium" in current GnuTLS versions.

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