source: mod_gnutls/src/gnutls_hooks.c @ 54d07a1

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

Do not announce (unused) session ticket support on proxy connections

Session resumption for proxy connections is not used at the moment, so
announcing support for session tickets is pointless.

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