source: mod_gnutls/src/gnutls_hooks.c @ de3fad3

debian/master
Last change on this file since de3fad3 was de3fad3, checked in by Fiona Klute <fiona.klute@…>, 11 months ago

Require handshake and request to use the same server

The new check prevents clients from establishing a TLS connection to
one virtual host and then requesting data from another. This is
particularly important for servers using TLS client authentication as
the only means of access control, because the server context for
certificate validation is selected based on the TLS connection.

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