source: mod_gnutls/src/gnutls_hooks.c @ 86cd5f6

mod_gnutls/0.11.0
Last change on this file since 86cd5f6 was e63515a, checked in by Fiona Klute <fiona.klute@…>, 4 months ago

Fix SNI/ServerAlias matching

ServerAliases? were never checked if ServerName? was set, which is
obviously wrong, and the test did not verify if the correct virtual
host had been selected. Fix this by checking ServerName?, explicit
ServerAliases?, and wildcard ServerAliases? in order, and returning the
first match. Also the test now verifies the selected virtual host and
tests a wildcard match in addition to an explicit ServerAlias?.

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