source: mod_gnutls/src/gnutls_hooks.c @ d827d0c

asyncio
Last change on this file since d827d0c was d827d0c, checked in by Fiona Klute <fiona.klute@…>, 14 months ago

Create cache keys for proxy session tickets

The key is based on the vhost name, backend server hostname (from SNI)
or IP, and its port. The vhost name is included because different
vhosts may have different settings for the same backend server. Post
handshake auth is not supported for proxy connections, so we do not
need to consider auth IDs.

  • Property mode set to 100644
File size: 74.3 KB
Line 
1/*
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008, 2014 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *  Copyright 2013-2014 Daniel Kahn Gillmor
6 *  Copyright 2015-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        {
662            /* GnuTLS 3.6.4 introduced automatic master key rotation */
663            if (gnutls_check_version_numeric(3, 6, 4))
664                sc->tickets = GNUTLS_ENABLED_TRUE;
665            else
666                sc->tickets = GNUTLS_ENABLED_FALSE;
667        }
668        if (sc->export_certificates_size < 0)
669            sc->export_certificates_size = 0;
670        if (sc->client_verify_mode == -1)
671            sc->client_verify_mode = GNUTLS_CERT_IGNORE;
672        if (sc->client_verify_method == mgs_cvm_unset)
673            sc->client_verify_method = mgs_cvm_cartel;
674
675        // TODO: None of the stuff below needs to be done if
676        // sc->enabled == GNUTLS_ENABLED_FALSE, we could just continue
677        // to the next host.
678
679        /* Load certificates and stuff (includes parsing priority) */
680        rv = mgs_load_files(pconf, ptemp, s);
681        if (rv != 0) {
682            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
683                         "%s: Loading credentials failed!", __func__);
684            return HTTP_NOT_FOUND;
685        }
686
687        sc->ocsp_mutex = sc_base->ocsp_mutex;
688        /* init OCSP configuration unless explicitly disabled */
689        if (sc->enabled && sc->ocsp_staple != GNUTLS_ENABLED_FALSE)
690        {
691            const char *err = mgs_ocsp_configure_stapling(pconf, ptemp, s);
692            if (err != NULL)
693            {
694                /* If OCSP stapling is enabled only by default ignore
695                 * error and disable stapling */
696                if (sc->ocsp_staple == GNUTLS_ENABLED_UNSET)
697                {
698                    ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
699                                 "Cannnot enable OCSP stapling for "
700                                 "host '%s:%d': %s",
701                                 s->server_hostname, s->addrs->host_port, err);
702                    sc->ocsp_staple = GNUTLS_ENABLED_FALSE;
703                }
704                /* If OCSP stapling is explicitly enabled this is a
705                 * critical error. */
706                else
707                {
708                    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s,
709                                 "OCSP stapling configuration failed for "
710                                 "host '%s:%d': %s",
711                                 s->server_hostname, s->addrs->host_port, err);
712                    return HTTP_INTERNAL_SERVER_ERROR;
713                }
714            }
715            else
716            {
717                /* Might already be set */
718                sc->ocsp_staple = GNUTLS_ENABLED_TRUE;
719                /* Set up stapling */
720                rv = mgs_ocsp_enable_stapling(pconf, ptemp, s);
721                if (rv != OK && rv != DECLINED)
722                    return rv;
723            }
724        }
725
726        /* Check if the priorities have been set */
727        if (sc->priorities == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
728            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
729                         "No GnuTLSPriorities directive for host '%s:%d', "
730                         "using default '%s'.",
731                         s->server_hostname, s->addrs->host_port,
732                         MGS_DEFAULT_PRIORITY);
733            sc->priorities = mgs_get_default_prio();
734        }
735
736        /* Set host DH params from user configuration or defaults */
737        if (sc->dh_params != NULL) {
738            gnutls_certificate_set_dh_params(sc->certs, sc->dh_params);
739            gnutls_anon_set_server_dh_params(sc->anon_creds, sc->dh_params);
740        } else {
741            rv = set_default_dh_param(s);
742            if (rv != OK)
743                return rv;
744        }
745
746        gnutls_certificate_set_retrieve_function3(sc->certs, cert_retrieve_fn);
747
748        if ((sc->certs_x509_chain == NULL || sc->certs_x509_chain_num < 1) &&
749            sc->enabled == GNUTLS_ENABLED_TRUE) {
750                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
751                                                "GnuTLS: Host '%s:%d' is missing a Certificate File!",
752                                                s->server_hostname, s->addrs->host_port);
753            return HTTP_UNAUTHORIZED;
754        }
755        if (sc->enabled == GNUTLS_ENABLED_TRUE &&
756            (sc->certs_x509_chain_num > 0 && sc->privkey_x509 == NULL))
757        {
758                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
759                                                "GnuTLS: Host '%s:%d' is missing a Private Key File!",
760                                                s->server_hostname, s->addrs->host_port);
761            return HTTP_UNAUTHORIZED;
762        }
763
764        if (sc->certs_x509_chain_num > 0
765            && gnutls_x509_tlsfeatures_check_crt(*must_staple,
766                                                 sc->certs_x509_crt_chain[0])
767            && sc->ocsp_staple == GNUTLS_ENABLED_FALSE)
768        {
769            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
770                         "Must-Staple is set in the host certificate "
771                         "of '%s:%d', but stapling is disabled!",
772                         s->server_hostname, s->addrs->host_port);
773            return HTTP_UNAUTHORIZED;
774        }
775
776        if (sc->enabled == GNUTLS_ENABLED_TRUE
777            && sc->proxy_enabled == GNUTLS_ENABLED_TRUE
778            && load_proxy_x509_credentials(pconf, ptemp, s) != APR_SUCCESS)
779        {
780            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
781                         "%s: loading proxy credentials for host "
782                         "'%s:%d' failed, exiting!",
783                         __func__, s->server_hostname, s->addrs->host_port);
784            return HTTP_PROXY_AUTHENTICATION_REQUIRED;
785        }
786    }
787
788
789    ap_add_version_component(pconf, "mod_gnutls/" MOD_GNUTLS_VERSION);
790
791    {
792        const char* libvers = gnutls_check_version(NULL);
793        char* gnutls_version = NULL;
794        if(libvers && (gnutls_version = apr_psprintf(pconf, "GnuTLS/%s", libvers))) {
795            ap_add_version_component(pconf, gnutls_version);
796        } else {
797            // In case we could not create the above string go for the static version instead
798            ap_add_version_component(pconf, "GnuTLS/" GNUTLS_VERSION "-static");
799        }
800    }
801
802    return OK;
803}
804
805void mgs_hook_child_init(apr_pool_t *p, server_rec *s)
806{
807    apr_status_t rv = APR_SUCCESS;
808    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
809        ap_get_module_config(s->module_config, &gnutls_module);
810
811    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
812
813    /* if we use PKCS #11 reinitialize it */
814    if (mgs_pkcs11_reinit(s) < 0) {
815            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
816                    "GnuTLS: Failed to reinitialize PKCS #11");
817            exit(-1);
818    }
819
820    if (sc->cache_enable == GNUTLS_ENABLED_TRUE)
821    {
822        rv = mgs_cache_child_init(p, s, sc->cache, MGS_CACHE_MUTEX_NAME);
823        if (rv != APR_SUCCESS)
824            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
825                    "Child init for session cache failed!");
826    }
827
828    if (sc->ocsp_cache != NULL)
829    {
830        rv = mgs_cache_child_init(p, s, sc->ocsp_cache,
831                                  MGS_OCSP_CACHE_MUTEX_NAME);
832        if (rv != APR_SUCCESS)
833            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
834                    "Child init for OCSP cache failed!");
835    }
836
837    /* reinit OCSP request mutex */
838    const char *lockfile = apr_global_mutex_lockfile(sc->ocsp_mutex);
839    rv = apr_global_mutex_child_init(&sc->ocsp_mutex, lockfile, p);
840    if (rv != APR_SUCCESS)
841        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
842                     "Failed to reinit mutex '" MGS_OCSP_MUTEX_NAME "'.");
843}
844
845const char *mgs_hook_http_scheme(const request_rec * r) {
846    mgs_srvconf_rec *sc;
847
848    if (r == NULL)
849        return NULL;
850
851    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
852            server->module_config,
853            &gnutls_module);
854
855    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
856    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
857        return NULL;
858    }
859
860    return "https";
861}
862
863apr_port_t mgs_hook_default_port(const request_rec * r) {
864    mgs_srvconf_rec *sc;
865
866    if (r == NULL)
867        return 0;
868
869    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
870            server->module_config,
871            &gnutls_module);
872
873    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
874    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
875        return 0;
876    }
877
878    return 443;
879}
880
881
882
883typedef struct {
884    mgs_handle_t *ctxt;
885    mgs_srvconf_rec *sc;
886    const char *sni_name;
887} vhost_cb_rec;
888
889/**
890 * Matches the current vhost's ServerAlias directives
891 *
892 * @param x vhost callback record
893 * @param s server record
894 * @param tsc mod_gnutls server data for `s`
895 *
896 * @return true if a match, false otherwise
897 *
898 */
899int check_server_aliases(vhost_cb_rec *x, server_rec * s, mgs_srvconf_rec *tsc)
900{
901    apr_array_header_t *names;
902    int rv = 0;
903    char ** name;
904
905    /* Check ServerName First! */
906    if (strcasecmp(x->sni_name, s->server_hostname) == 0) {
907        // We have a match, save this server configuration
908        x->sc = tsc;
909        rv = 1;
910        /* Check any ServerAlias directives */
911    } else if(s->names->nelts) {
912        names = s->names;
913        name = (char **) names->elts;
914        for (int i = 0; i < names->nelts; ++i)
915        {
916            if (!name[i])
917                continue;
918            if (strcasecmp(x->sni_name, name[i]) == 0)
919            {
920                // We have a match, save this server configuration
921                x->sc = tsc;
922                rv = 1;
923            }
924        }
925        /* ServerAlias directives may contain wildcards, check those last. */
926    } else if(s->wild_names->nelts) {
927        names = s->wild_names;
928        name = (char **) names->elts;
929        for (int i = 0; i < names->nelts; ++i)
930        {
931            if (!name[i])
932                continue;
933            if (ap_strcasecmp_match(x->sni_name, name[i]) == 0)
934            {
935                x->sc = tsc;
936                rv = 1;
937            }
938        }
939    }
940    return rv;
941}
942
943static int vhost_cb(void *baton, conn_rec *conn, server_rec * s)
944{
945    mgs_srvconf_rec *tsc;
946    vhost_cb_rec *x = baton;
947    int ret;
948
949    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
950    tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
951            &gnutls_module);
952
953    if (tsc->enabled != GNUTLS_ENABLED_TRUE) {
954        return 0;
955    }
956
957    if (tsc->certs_x509_chain_num > 0) {
958        /* this check is there to warn administrator of any missing hostname
959         * in the certificate. */
960        ret = gnutls_x509_crt_check_hostname(tsc->certs_x509_crt_chain[0], s->server_hostname);
961        if (0 == ret)
962            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn,
963                          "GnuTLS: the certificate doesn't match requested "
964                          "hostname '%s'", s->server_hostname);
965    } else {
966        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, conn,
967                      "GnuTLS: SNI request for '%s' but no X.509 certs "
968                      "available at all",
969                      s->server_hostname);
970    }
971        return check_server_aliases(x, s, tsc);
972}
973
974/**
975 * Get SNI data from GnuTLS (if any) and search for a matching virtual
976 * host configuration. This method is called from the post client
977 * hello function.
978 *
979 * @param ctxt the mod_gnutls connection handle
980 *
981 * @return either the matching mod_gnutls server config, or `NULL`
982 */
983mgs_srvconf_rec *mgs_find_sni_server(mgs_handle_t *ctxt)
984{
985    if (ctxt->sni_name == NULL)
986    {
987        const char *sni_name = mgs_server_name_get(ctxt);
988        if (sni_name != NULL)
989            ctxt->sni_name = sni_name;
990        else
991            return NULL;
992    }
993
994    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
995                  "%s: client requested server '%s'.",
996                  __func__, ctxt->sni_name);
997
998    /* Search for vhosts matching connection parameters and the
999     * SNI. If a match is found, cbx.sc will contain the mod_gnutls
1000     * server config for the vhost. */
1001    vhost_cb_rec cbx = {
1002        .ctxt = ctxt,
1003        .sc = NULL,
1004        .sni_name = ctxt->sni_name
1005    };
1006    int rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
1007    if (rv == 1) {
1008        return cbx.sc;
1009    }
1010    return NULL;
1011}
1012
1013
1014
1015/**
1016 * Pre client hello hook function for GnuTLS that implements early SNI
1017 * processing using `gnutls_ext_raw_parse()` (available since GnuTLS
1018 * 3.6.3). Reading the SNI (if any) before GnuTLS processes the client
1019 * hello allows loading virtual host settings that cannot be changed
1020 * in the post client hello hook, including ALPN proposals (required
1021 * for HTTP/2 support using the `Protocols` directive). In addition to
1022 * ALPN this function configures the server credentials.
1023 *
1024 * The function signature is required by the GnuTLS API.
1025 *
1026 * @param session the current session
1027 * @param htype handshake message type
1028 * @param when hook position relative to GnuTLS processing
1029 * @param incoming true if the message is incoming, for client hello
1030 * that means the hook is running on the server
1031 * @param msg raw message data
1032 *
1033 * @return `GNUTLS_E_SUCCESS` or a GnuTLS error code
1034 */
1035static int early_sni_hook(gnutls_session_t session,
1036                          unsigned int htype,
1037                          unsigned when,
1038                          unsigned int incoming,
1039                          const gnutls_datum_t *msg)
1040{
1041    if (!incoming)
1042        return 0;
1043
1044    mgs_handle_t *ctxt = (mgs_handle_t *) gnutls_session_get_ptr(session);
1045
1046    /* This is a hook for pre client hello ONLY! */
1047    if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || when != GNUTLS_HOOK_PRE)
1048    {
1049        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, ctxt->c,
1050                      "%s called outside pre client hello hook, this "
1051                      "indicates a programming error!",
1052                      __func__);
1053        return GNUTLS_E_SELF_TEST_ERROR;
1054    }
1055
1056    int ret = gnutls_ext_raw_parse(session, mgs_sni_ext_hook, msg,
1057                                   GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO);
1058    if (ret == 0 && ctxt->sni_name != NULL)
1059    {
1060        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
1061                      "%s found SNI name: '%s'",
1062                      __func__, ctxt->sni_name);
1063
1064        /* try to find a virtual host for that name */
1065        mgs_srvconf_rec *tsc = mgs_find_sni_server(ctxt);
1066        if (tsc != NULL)
1067        {
1068            /* Found a TLS vhost based on the SNI, configure the
1069             * connection context. */
1070            ctxt->sc = tsc;
1071            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
1072                          "%s: Selected virtual host %s from early SNI, "
1073                          "connection server is %s.",
1074                          __func__, ctxt->sc->s->server_hostname,
1075                          ctxt->c->base_server->server_hostname);
1076        }
1077    }
1078
1079    reload_session_credentials(ctxt);
1080
1081    prepare_alpn_proposals(ctxt);
1082
1083    return ret;
1084}
1085
1086
1087
1088/**
1089 * This function is intended as a cleanup handler for connections
1090 * using GnuTLS. If attached to the connection pool, it ensures that
1091 * session resources are released with the connection pool even if the
1092 * session wasn't terminated properly.
1093 *
1094 * @param data must point to the mgs_handle_t associated with the
1095 * connection
1096 */
1097static apr_status_t cleanup_gnutls_session(void *data)
1098{
1099    /* nothing to do */
1100    if (data == NULL)
1101        return APR_SUCCESS;
1102
1103    /* check if session needs closing */
1104    mgs_handle_t *ctxt = (mgs_handle_t *) data;
1105    if (ctxt->session != NULL)
1106    {
1107        ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_ECONNABORTED, ctxt->c,
1108                      "%s: connection pool cleanup in progress but %sTLS "
1109                      "session hasn't been terminated, trying to close",
1110                      __func__, IS_PROXY_STR(ctxt));
1111        int ret;
1112        /* Try A Clean Shutdown */
1113        do
1114            ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
1115        while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
1116        if (ret != GNUTLS_E_SUCCESS)
1117            ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_EGENERAL, ctxt->c,
1118                          "%s: error while closing TLS %sconnection: %s (%d)",
1119                          __func__, IS_PROXY_STR(ctxt),
1120                          gnutls_strerror(ret), ret);
1121        else
1122            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
1123                          "%s: TLS %sconnection closed.",
1124                          __func__, IS_PROXY_STR(ctxt));
1125        /* De-Initialize Session */
1126        gnutls_deinit(ctxt->session);
1127        ctxt->session = NULL;
1128    }
1129    return APR_SUCCESS;
1130}
1131
1132static int got_ticket_func(gnutls_session_t session,
1133                           unsigned int htype,
1134                           unsigned when,
1135                           unsigned int incoming __attribute__((unused)),
1136                           const gnutls_datum_t *msg __attribute__((unused)))
1137{
1138    /* Ignore all unexpected messages */
1139    if (htype != GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
1140        || when != GNUTLS_HOOK_POST)
1141        return GNUTLS_E_SUCCESS;
1142
1143    mgs_handle_t *ctxt = gnutls_session_get_ptr(session);
1144    if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SESSION_TICKET))
1145    {
1146        ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, ctxt->c,
1147                      "%s called but session has no ticket!",
1148                      __func__);
1149        /* Tickets are optional, so don't break the session on
1150         * errors. */
1151        return GNUTLS_E_SUCCESS;
1152    }
1153
1154    gnutls_datum_t dump;
1155    int ret = gnutls_session_get_data2(session, &dump);
1156    if (ret != GNUTLS_E_SUCCESS)
1157    {
1158        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
1159                      "%s: error reading session ticket: %s (%d)",
1160                      __func__, gnutls_strerror(ret), ret);
1161        if (dump.data)
1162            gnutls_free(dump.data);
1163        return GNUTLS_E_SUCCESS;
1164    }
1165
1166    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
1167                  "%s: session ticket read (%u bytes)",
1168                  __func__, dump.size);
1169    gnutls_free(dump.data);
1170    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
1171                  "%s: cache key for the session ticket is %s",
1172                  __func__, mgs_proxy_ticket_id(ctxt, NULL));
1173    return GNUTLS_E_SUCCESS;
1174}
1175
1176static void create_gnutls_handle(conn_rec * c)
1177{
1178    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1179
1180    /* Get connection specific configuration */
1181    mgs_handle_t *ctxt = init_gnutls_ctxt(c);
1182    ctxt->enabled = GNUTLS_ENABLED_TRUE;
1183    ctxt->status = 0;
1184    ctxt->input_rc = APR_SUCCESS;
1185    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
1186    ctxt->input_cbuf.length = 0;
1187    ctxt->output_rc = APR_SUCCESS;
1188    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
1189    ctxt->output_blen = 0;
1190    ctxt->output_length = 0;
1191
1192    /* Initialize GnuTLS Library */
1193    int err = 0;
1194    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
1195    {
1196        /* this is an outgoing proxy connection, client mode */
1197        err = gnutls_init(&ctxt->session, GNUTLS_CLIENT);
1198        if (err != GNUTLS_E_SUCCESS)
1199            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
1200                          "gnutls_init for proxy connection failed: %s (%d)",
1201                          gnutls_strerror(err), err);
1202        gnutls_handshake_set_hook_function(ctxt->session,
1203                                           GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
1204                                           GNUTLS_HOOK_POST, got_ticket_func);
1205    }
1206    else
1207    {
1208        /* incoming connection, server mode */
1209        err = gnutls_init(&ctxt->session,
1210                          GNUTLS_SERVER | GNUTLS_POST_HANDSHAKE_AUTH);
1211        if (err != GNUTLS_E_SUCCESS)
1212            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
1213                          "gnutls_init for server side failed: %s (%d)",
1214                          gnutls_strerror(err), err);
1215
1216        /* Pre-handshake hook for early SNI parsing */
1217        gnutls_handshake_set_hook_function(ctxt->session,
1218                                           GNUTLS_HANDSHAKE_CLIENT_HELLO,
1219                                           GNUTLS_HOOK_PRE, early_sni_hook);
1220    }
1221
1222    /* Ensure TLS session resources are released when the connection
1223     * pool is cleared, if the filters haven't done that already. */
1224    apr_pool_pre_cleanup_register(c->pool, ctxt, cleanup_gnutls_session);
1225
1226    /* Set Default Priority */
1227        err = gnutls_priority_set(ctxt->session, mgs_get_default_prio());
1228    if (err != GNUTLS_E_SUCCESS)
1229        ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
1230                      "gnutls_priority_set failed!");
1231
1232    /* Post client hello hook (called after GnuTLS has parsed it) */
1233    gnutls_handshake_set_post_client_hello_function(ctxt->session,
1234            post_client_hello_hook);
1235
1236    /* Set GnuTLS user pointer, so we can access the module session
1237     * context in GnuTLS callbacks */
1238    gnutls_session_set_ptr(ctxt->session, ctxt);
1239
1240    /* If mod_gnutls is the TLS server, early_sni_hook (or
1241     * post_client_hello_hook, if early SNI is not available) will
1242     * load appropriate credentials during the handshake. However,
1243     * when handling a proxy backend connection, mod_gnutls acts as
1244     * TLS client and credentials must be loaded here. */
1245    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
1246    {
1247        /* Set anonymous client credentials for proxy connections */
1248        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_ANON,
1249                               ctxt->sc->anon_client_creds);
1250        /* Set x509 credentials */
1251        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE,
1252                               ctxt->sc->proxy_x509_creds);
1253        /* Load priorities from the server configuration */
1254        err = gnutls_priority_set(ctxt->session, ctxt->sc->proxy_priorities);
1255        if (err != GNUTLS_E_SUCCESS)
1256            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
1257                          "%s: setting priorities for proxy connection "
1258                          "failed: %s (%d)",
1259                          __func__, gnutls_strerror(err), err);
1260    }
1261
1262    /* Initialize Session Cache */
1263    mgs_cache_session_init(ctxt);
1264
1265    /* Set pull, push & ptr functions */
1266    gnutls_transport_set_pull_function(ctxt->session,
1267                                       mgs_transport_read);
1268    gnutls_transport_set_pull_timeout_function(ctxt->session,
1269                                               mgs_transport_read_ready);
1270    gnutls_transport_set_push_function(ctxt->session,
1271                                       mgs_transport_write);
1272    gnutls_transport_set_ptr(ctxt->session, ctxt);
1273    /* Add IO filters */
1274    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME,
1275            ctxt, NULL, c);
1276    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
1277            ctxt, NULL, c);
1278}
1279
1280int mgs_hook_pre_connection(conn_rec * c, void *csd __attribute__((unused)))
1281{
1282    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1283
1284    if (c->master)
1285    {
1286        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
1287                      "%s declined secondary connection", __func__);
1288        return DECLINED;
1289    }
1290
1291    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1292        ap_get_module_config(c->base_server->module_config, &gnutls_module);
1293    mgs_handle_t *ctxt = (mgs_handle_t *)
1294        ap_get_module_config(c->conn_config, &gnutls_module);
1295
1296    if ((sc && (!sc->enabled))
1297        || (ctxt && ctxt->enabled == GNUTLS_ENABLED_FALSE))
1298    {
1299        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s declined connection",
1300                      __func__);
1301        return DECLINED;
1302    }
1303
1304    create_gnutls_handle(c);
1305    return OK;
1306}
1307
1308
1309
1310/**
1311 * process_connection hook: Do a zero byte read to trigger the
1312 * handshake. Doesn't change anything for traditional protocols that
1313 * just do reads, but HTTP/2 needs the TLS handshake and ALPN to
1314 * happen before its process_connection hook runs.
1315 */
1316int mgs_hook_process_connection(conn_rec* c)
1317{
1318    mgs_handle_t *ctxt = (mgs_handle_t *)
1319        ap_get_module_config(c->conn_config, &gnutls_module);
1320
1321    if ((ctxt != NULL) && (ctxt->enabled == GNUTLS_ENABLED_TRUE))
1322    {
1323        /* This connection is supposed to use TLS. Give the filters a
1324         * kick with a zero byte read to trigger the handshake. */
1325        apr_bucket_brigade* temp =
1326            apr_brigade_create(c->pool, c->bucket_alloc);
1327        ap_get_brigade(c->input_filters, temp,
1328                       AP_MODE_INIT, APR_BLOCK_READ, 0);
1329        apr_brigade_destroy(temp);
1330    }
1331    return DECLINED;
1332}
1333
1334
1335
1336/* Post request hook, checks if TLS connection and vhost match */
1337int mgs_req_vhost_check(request_rec *r)
1338{
1339    /* mod_gnutls server record for the request vhost */
1340    mgs_srvconf_rec *r_sc = (mgs_srvconf_rec *)
1341        ap_get_module_config(r->server->module_config, &gnutls_module);
1342    mgs_handle_t *ctxt = get_effective_gnutls_ctxt(r->connection);
1343
1344    /* Nothing to check for non-TLS and outgoing proxy connections */
1345    if (ctxt == NULL || !ctxt->enabled || ctxt->is_proxy)
1346        return DECLINED;
1347
1348    if (ctxt->sc != r_sc)
1349    {
1350        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, ctxt->c,
1351                      "%s: Mismatch between handshake and request servers!",
1352                      __func__);
1353        return HTTP_MISDIRECTED_REQUEST;
1354    }
1355
1356    if (!ctxt->sni_name)
1357        return DECLINED;
1358
1359    /* Got an SNI name, so verify it matches. */
1360    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
1361                  "%s: Checking request hostname against SNI name '%s'.",
1362                  __func__, ctxt->sni_name);
1363
1364    if (!r->hostname)
1365    {
1366        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, r->connection,
1367                      "Client requested '%s' via SNI, but provided "
1368                      "no hostname in HTTP request!", ctxt->sni_name);
1369        return HTTP_MISDIRECTED_REQUEST;
1370    }
1371
1372    if (strcasecmp(r->hostname, ctxt->sni_name) != 0)
1373    {
1374        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, r->connection,
1375                      "Client requested '%s' via SNI, but '%s' in "
1376                      "the HTTP request!", ctxt->sni_name, r->hostname);
1377        return HTTP_MISDIRECTED_REQUEST;
1378    }
1379
1380    return DECLINED;
1381}
1382
1383
1384
1385int mgs_hook_fixups(request_rec * r) {
1386    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
1387    const char *tmp;
1388    size_t len;
1389    mgs_handle_t *ctxt;
1390    int rv = OK;
1391
1392    if (r == NULL)
1393        return DECLINED;
1394
1395    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1396    apr_table_t *env = r->subprocess_env;
1397
1398    ctxt = get_effective_gnutls_ctxt(r->connection);
1399
1400    if (!ctxt || ctxt->enabled != GNUTLS_ENABLED_TRUE || ctxt->session == NULL)
1401    {
1402        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "request declined in %s", __func__);
1403        return DECLINED;
1404    }
1405
1406    apr_table_setn(env, "HTTPS", "on");
1407
1408    apr_table_setn(env, "SSL_VERSION_LIBRARY",
1409            "GnuTLS/" LIBGNUTLS_VERSION);
1410    apr_table_setn(env, "SSL_VERSION_INTERFACE",
1411            "mod_gnutls/" MOD_GNUTLS_VERSION);
1412
1413    apr_table_setn(env, "SSL_PROTOCOL",
1414            gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
1415
1416    /* should have been called SSL_CIPHERSUITE instead */
1417    apr_table_setn(env, "SSL_CIPHER",
1418            gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
1419                                         gnutls_cipher_get(ctxt->session),
1420                                         gnutls_mac_get(ctxt->session)));
1421
1422    /* Compression support has been removed since GnuTLS 3.6.0 */
1423    apr_table_setn(env, "SSL_COMPRESS_METHOD", "NULL");
1424
1425#ifdef ENABLE_SRP
1426    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
1427        tmp = gnutls_srp_server_get_username(ctxt->session);
1428        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
1429    } else {
1430        apr_table_unset(env, "SSL_SRP_USER");
1431    }
1432#endif
1433
1434    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
1435        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
1436
1437    unsigned int key_size = 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
1438    tmp = apr_psprintf(r->pool, "%u", key_size);
1439
1440    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
1441
1442    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
1443
1444    apr_table_setn(env, "SSL_CIPHER_EXPORT",
1445            (key_size <= 40) ? "true" : "false");
1446
1447    int dhsize = gnutls_dh_get_prime_bits(ctxt->session);
1448    if (dhsize > 0) {
1449        tmp = apr_psprintf(r->pool, "%d", dhsize);
1450        apr_table_setn(env, "SSL_DH_PRIME_BITS", tmp);
1451    }
1452
1453    len = sizeof (sbuf);
1454    gnutls_session_get_id(ctxt->session, sbuf, &len);
1455    apr_table_setn(env, "SSL_SESSION_ID",
1456                   apr_pescape_hex(r->pool, sbuf, len, 0));
1457
1458    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1459        mgs_add_common_cert_vars(r, ctxt->sc->certs_x509_crt_chain[0], 0,
1460                                 ctxt->sc->export_certificates_size);
1461    }
1462
1463    return rv;
1464}
1465
1466int mgs_hook_authz(request_rec *r)
1467{
1468    if (r == NULL)
1469        return DECLINED;
1470
1471    mgs_dirconf_rec *dc = ap_get_module_config(
1472        r->per_dir_config, &gnutls_module);
1473
1474    mgs_handle_t *ctxt = get_effective_gnutls_ctxt(r->connection);
1475    if (!ctxt || ctxt->session == NULL) {
1476        return DECLINED;
1477    }
1478
1479    /* The effective verify mode. Directory configuration takes
1480     * precedence if present (-1 means it is unset). */
1481    int client_verify_mode = ctxt->sc->client_verify_mode;
1482    if (dc->client_verify_mode != -1)
1483        client_verify_mode = dc->client_verify_mode;
1484
1485    char *verify_mode;
1486    if (client_verify_mode == GNUTLS_CERT_IGNORE)
1487        verify_mode = "ignore";
1488    else if (client_verify_mode == GNUTLS_CERT_REQUEST)
1489        verify_mode = "request";
1490    else if (client_verify_mode == GNUTLS_CERT_REQUIRE)
1491        verify_mode = "require";
1492    else
1493        verify_mode = "(undefined)";
1494    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1495                  "%s: verify mode is \"%s\"", __func__, verify_mode);
1496
1497    if (client_verify_mode == GNUTLS_CERT_IGNORE)
1498    {
1499        return DECLINED;
1500    }
1501
1502    /* At this point the verify mode is either request or require */
1503    unsigned int cert_list_size;
1504    const gnutls_datum_t *cert_list =
1505        gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1506
1507    /* We can reauthenticate the client if using TLS 1.3 and the
1508     * client annouced support. Note that there may still not be any
1509     * client certificate after. */
1510    if ((cert_list == NULL || cert_list_size == 0)
1511        && gnutls_protocol_get_version(ctxt->session) == GNUTLS_TLS1_3
1512        && (gnutls_session_get_flags(ctxt->session)
1513            & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH))
1514    {
1515        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1516                      "%s: No certificate, attempting post-handshake "
1517                      "authentication (%d)",
1518                      __func__, client_verify_mode);
1519
1520        if (r->proto_num == HTTP_VERSION(2, 0))
1521        {
1522            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1523                          "Rehandshake is prohibited for HTTP/2 "
1524                          "(RFC 7540, section 9.2.1).");
1525
1526            /* This also applies to request mode, otherwise
1527             * per-directory request would never work with HTTP/2. The
1528             * note makes mod_http2 send an HTTP_1_1_REQUIRED
1529             * error to tell the client to switch. */
1530            apr_table_setn(r->notes, RENEGOTIATE_FORBIDDEN_NOTE,
1531                           "verify-client");
1532            return HTTP_FORBIDDEN;
1533        }
1534
1535        /* The request mode sent to the client is always "request"
1536         * because if reauth with "require" fails GnuTLS invalidates
1537         * the session, so we couldn't send 403 to the client. */
1538        gnutls_certificate_server_set_request(ctxt->session,
1539                                              GNUTLS_CERT_REQUEST);
1540        int rv = mgs_reauth(ctxt, r);
1541        if (rv != GNUTLS_E_SUCCESS) {
1542            if (rv == GNUTLS_E_GOT_APPLICATION_DATA)
1543                return HTTP_REQUEST_ENTITY_TOO_LARGE;
1544            else
1545                return HTTP_FORBIDDEN;
1546        }
1547    }
1548
1549    int ret = mgs_cert_verify(r, ctxt);
1550    /* In "request" mode we always allow the request, otherwise the
1551     * verify result decides. */
1552    if (client_verify_mode == GNUTLS_CERT_REQUEST)
1553        return DECLINED;
1554    return ret;
1555}
1556
1557/* variables that are not sent by default:
1558 *
1559 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
1560 * SSL_SERVER_CERT      string  PEM-encoded client certificate
1561 */
1562
1563/* @param side is either 0 for SERVER or 1 for CLIENT
1564 *
1565 * @param export_cert_size (int) maximum size for environment variable
1566 * to use for the PEM-encoded certificate (0 means do not export)
1567 */
1568#define MGS_SIDE(suffix) ((side==0) ? "SSL_SERVER" suffix : "SSL_CLIENT" suffix)
1569
1570static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size) {
1571    unsigned char sbuf[64]; /* buffer to hold serials */
1572    char buf[AP_IOBUFSIZE];
1573    const char *tmp;
1574    char *tmp2;
1575    size_t len;
1576    int ret;
1577
1578    if (r == NULL)
1579        return;
1580
1581    apr_table_t *env = r->subprocess_env;
1582
1583    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1584    if (export_cert_size > 0) {
1585        len = 0;
1586        ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &len);
1587        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1588            if (len >= export_cert_size) {
1589                apr_table_setn(env, MGS_SIDE("_CERT"), "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1590                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1591                              "GnuTLS: Failed to export too-large X.509 certificate to environment");
1592            } else {
1593                char* cert_buf = apr_palloc(r->pool, len + 1);
1594                if (cert_buf != NULL && gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) {
1595                    cert_buf[len] = 0;
1596                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
1597                } else {
1598                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1599                                  "GnuTLS: failed to export X.509 certificate");
1600                }
1601            }
1602        } else {
1603            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1604                          "GnuTLS: dazed and confused about X.509 certificate size");
1605        }
1606    }
1607
1608    len = sizeof (buf);
1609    gnutls_x509_crt_get_dn(cert, buf, &len);
1610    apr_table_setn(env, MGS_SIDE("_S_DN"), apr_pstrmemdup(r->pool, buf, len));
1611
1612    len = sizeof (buf);
1613    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
1614    apr_table_setn(env, MGS_SIDE("_I_DN"), apr_pstrmemdup(r->pool, buf, len));
1615
1616    len = sizeof (sbuf);
1617    gnutls_x509_crt_get_serial(cert, sbuf, &len);
1618    apr_table_setn(env, MGS_SIDE("_M_SERIAL"),
1619                   apr_pescape_hex(r->pool, sbuf, len, 0));
1620
1621    ret = gnutls_x509_crt_get_version(cert);
1622    if (ret > 0)
1623        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1624                       apr_psprintf(r->pool, "%u", ret));
1625
1626    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "X.509");
1627
1628    tmp =
1629            mgs_time2sz(gnutls_x509_crt_get_expiration_time
1630            (cert), buf, sizeof (buf));
1631    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
1632
1633    tmp =
1634            mgs_time2sz(gnutls_x509_crt_get_activation_time
1635            (cert), buf, sizeof (buf));
1636    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
1637
1638    ret = gnutls_x509_crt_get_signature_algorithm(cert);
1639    if (ret >= 0) {
1640        apr_table_setn(env, MGS_SIDE("_A_SIG"),
1641                gnutls_sign_algorithm_get_name(ret));
1642    }
1643
1644    ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
1645    if (ret >= 0) {
1646        apr_table_setn(env, MGS_SIDE("_A_KEY"),
1647                gnutls_pk_algorithm_get_name(ret));
1648    }
1649
1650    /* export all the alternative names (DNS, RFC822 and URI) */
1651    for (int i = 0; !(ret < 0); i++)
1652    {
1653        const char *san, *sanlabel;
1654        len = 0;
1655        ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1656                NULL, &len,
1657                NULL);
1658
1659        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1660            tmp2 = apr_palloc(r->pool, len + 1);
1661
1662            ret =
1663                    gnutls_x509_crt_get_subject_alt_name(cert, i,
1664                    tmp2,
1665                    &len,
1666                    NULL);
1667            tmp2[len] = 0;
1668
1669            sanlabel = apr_psprintf(r->pool, "%s%u", MGS_SIDE("_S_AN"), i);
1670            if (ret == GNUTLS_SAN_DNSNAME) {
1671                san = apr_psprintf(r->pool, "DNSNAME:%s", tmp2);
1672            } else if (ret == GNUTLS_SAN_RFC822NAME) {
1673                san = apr_psprintf(r->pool, "RFC822NAME:%s", tmp2);
1674            } else if (ret == GNUTLS_SAN_URI) {
1675                san = apr_psprintf(r->pool, "URI:%s", tmp2);
1676            } else {
1677                san = "UNSUPPORTED";
1678            }
1679            apr_table_setn(env, sanlabel, san);
1680        }
1681    }
1682}
1683
1684
1685
1686/* TODO: Allow client sending a X.509 certificate chain */
1687static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) {
1688    const gnutls_datum_t *cert_list;
1689    unsigned int cert_list_size;
1690    /* assume the certificate is invalid unless explicitly set
1691     * otherwise */
1692    unsigned int status = GNUTLS_CERT_INVALID;
1693    int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1694    unsigned int ch_size = 0;
1695
1696    // TODO: union no longer needed here after removing its "pgp" component.
1697    union {
1698        gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1699    } cert;
1700    apr_time_t expiration_time, cur_time;
1701
1702    if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1703        return HTTP_FORBIDDEN;
1704
1705    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1706    cert_list =
1707            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1708
1709    if (cert_list == NULL || cert_list_size == 0) {
1710        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1711         */
1712        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1713            return DECLINED;
1714
1715        /* no certificate provided by the client, but one was required. */
1716        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1717                "GnuTLS: Failed to Verify Peer: "
1718                "Client did not submit a certificate");
1719        return HTTP_FORBIDDEN;
1720    }
1721
1722    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1723        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1724                "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1725                cert_list_size);
1726
1727        for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1728            gnutls_x509_crt_init(&cert.x509[ch_size]);
1729            rv = gnutls_x509_crt_import(cert.x509[ch_size],
1730                    &cert_list[ch_size],
1731                    GNUTLS_X509_FMT_DER);
1732            // When failure to import, leave the loop
1733            if (rv != GNUTLS_E_SUCCESS) {
1734                if (ch_size < 1) {
1735                    ap_log_rerror(APLOG_MARK,
1736                            APLOG_INFO, 0, r,
1737                            "GnuTLS: Failed to Verify Peer: "
1738                            "Failed to import peer certificates.");
1739                    ret = HTTP_FORBIDDEN;
1740                    goto exit;
1741                }
1742                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1743                        "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1744                        ch_size);
1745                rv = GNUTLS_E_SUCCESS;
1746                break;
1747            }
1748        }
1749    } else
1750        return HTTP_FORBIDDEN;
1751
1752    if (rv < 0) {
1753        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1754                "GnuTLS: Failed to Verify Peer: "
1755                "Failed to import peer certificates.");
1756        ret = HTTP_FORBIDDEN;
1757        goto exit;
1758    }
1759
1760    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1761        apr_time_ansi_put(&expiration_time,
1762                gnutls_x509_crt_get_expiration_time
1763                (cert.x509[0]));
1764
1765        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1766                      "GnuTLS: Verifying list of %d certificate(s) via method '%s'",
1767                      ch_size, mgs_readable_cvm(ctxt->sc->client_verify_method));
1768        switch(ctxt->sc->client_verify_method) {
1769        case mgs_cvm_cartel:
1770            rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1771                                             ctxt->sc->ca_list,
1772                                             ctxt->sc->ca_list_size,
1773                                             NULL, 0, 0, &status);
1774            break;
1775#ifdef ENABLE_MSVA
1776        case mgs_cvm_msva:
1777        {
1778            struct msv_response* resp = NULL;
1779            struct msv_query q = { .context="https", .peertype="client", .pkctype="x509pem" };
1780            msv_ctxt_t ctx = msv_ctxt_init(NULL);
1781            char cert_pem_buf[10 * 1024];
1782            size_t len = sizeof (cert_pem_buf);
1783
1784            rv = 0;
1785            if (gnutls_x509_crt_export(cert.x509[0], GNUTLS_X509_FMT_PEM, cert_pem_buf, &len) >= 0) {
1786                /* FIXME : put together a name from the cert we received, instead of hard-coding this value: */
1787                q.peername = mgs_x509_construct_uid(r, cert.x509[0]);
1788                q.pkcdata = cert_pem_buf;
1789                rv = msv_query_agent(ctx, q, &resp);
1790                if (rv == LIBMSV_ERROR_SUCCESS) {
1791                    status = 0;
1792                } else if (rv == LIBMSV_ERROR_INVALID) {
1793                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1794                                  "GnuTLS: Monkeysphere validation failed: (message: %s)", resp->message);
1795                    status = GNUTLS_CERT_INVALID;
1796                } else {
1797                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1798                                  "GnuTLS: Error communicating with the Monkeysphere Validation Agent: (%d) %s", rv, msv_strerror(ctx, rv));
1799                    status = GNUTLS_CERT_INVALID;
1800                    rv = -1;
1801                }
1802            } else {
1803                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1804                              "GnuTLS: Could not convert the client certificate to PEM format");
1805                status = GNUTLS_CERT_INVALID;
1806                rv = GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
1807            }
1808            msv_response_destroy(resp);
1809            msv_ctxt_destroy(ctx);
1810        }
1811            break;
1812#endif
1813        default:
1814            /* If this block is reached, that indicates a
1815             * configuration error or bug in mod_gnutls (invalid value
1816             * of ctxt->sc->client_verify_method). */
1817            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1818                          "GnuTLS: Failed to Verify X.509 Peer: method '%s' is not supported",
1819                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1820            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1821        }
1822
1823    } else {
1824        /* Unknown certificate type */
1825        rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1826    }
1827
1828    /* "goto exit" at the end of this block skips evaluation of the
1829     * "status" variable */
1830    if (rv < 0) {
1831        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1832                "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1833                rv, gnutls_strerror(rv));
1834        if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1835            ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1836                "GnuTLS: No certificate was found for verification. Did you set the GnuTLSClientCAFile directive?");
1837        ret = HTTP_FORBIDDEN;
1838        goto exit;
1839    }
1840
1841    /* TODO: X509 CRL Verification. */
1842    /* May add later if anyone needs it.
1843     */
1844    /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1845
1846    cur_time = apr_time_now();
1847
1848    if (status != 0) {
1849        gnutls_datum_t errmsg;
1850        gnutls_certificate_verification_status_print(
1851            status, gnutls_certificate_type_get(ctxt->session),
1852            &errmsg, 0);
1853        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1854                      "Client authentication failed: %s", errmsg.data);
1855        gnutls_free(errmsg.data);
1856    }
1857
1858    mgs_add_common_cert_vars(r, cert.x509[0], 1, ctxt->sc->export_certificates_size);
1859
1860    {
1861        /* days remaining */
1862        unsigned long remain =
1863                (apr_time_sec(expiration_time) -
1864                apr_time_sec(cur_time)) / 86400;
1865        apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1866                apr_psprintf(r->pool, "%lu", remain));
1867    }
1868
1869    if (status == 0) {
1870        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1871                "SUCCESS");
1872        ret = DECLINED;
1873    } else {
1874        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1875                "FAILED");
1876        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1877            ret = DECLINED;
1878        else
1879            ret = HTTP_FORBIDDEN;
1880    }
1881
1882exit:
1883    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1884        for (unsigned int i = 0; i < ch_size; i++)
1885            gnutls_x509_crt_deinit(cert.x509[i]);
1886
1887    return ret;
1888}
1889
1890
1891
1892#ifdef ENABLE_MSVA
1893/* this section of code is used only when trying to talk to the MSVA */
1894static const char* mgs_x509_leaf_oid_from_dn(apr_pool_t *pool, const char* oid, gnutls_x509_crt_t cert) {
1895    int rv=GNUTLS_E_SUCCESS, i;
1896    size_t sz=0, lastsz=0;
1897    char* data=NULL;
1898
1899    i = -1;
1900    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1901        i++;
1902        lastsz=sz;
1903        sz=0;
1904        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i, 0, NULL, &sz);
1905    }
1906    if (i > 0) {
1907        data = apr_palloc(pool, lastsz);
1908        sz=lastsz;
1909        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i-1, 0, data, &sz);
1910        if (rv == GNUTLS_E_SUCCESS)
1911            return data;
1912    }
1913    return NULL;
1914}
1915
1916static const char* mgs_x509_first_type_from_san(apr_pool_t *pool, gnutls_x509_subject_alt_name_t target, gnutls_x509_crt_t cert) {
1917    int rv=GNUTLS_E_SUCCESS;
1918    size_t sz;
1919    char* data=NULL;
1920    unsigned int i;
1921    gnutls_x509_subject_alt_name_t thistype;
1922
1923    i = 0;
1924    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1925        sz = 0;
1926        rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, NULL, &sz, &thistype, NULL);
1927        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && thistype == target) {
1928            data = apr_palloc(pool, sz);
1929            rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, data, &sz, &thistype, NULL);
1930            if (rv >=0 && (thistype == target))
1931                return data;
1932        }
1933        i++;
1934    }
1935    return NULL;
1936}
1937
1938
1939/* Create a string representing a candidate User ID from an X.509
1940 * certificate
1941
1942 * We need this for client certification because a client gives us a
1943 * certificate, but doesn't tell us (in any other way) who they are
1944 * trying to authenticate as.
1945
1946 * one complaint might be "but the user wanted to be another identity,
1947 * which is also in the certificate (e.g. in a SubjectAltName)"
1948 * However, given that any user can regenerate their own X.509
1949 * certificate with their own public key content, they should just do
1950 * so, and not expect us to guess at their identity :)
1951
1952 * This function allocates it's response from the pool given it.  When
1953 * that pool is reclaimed, the response will also be deallocated.
1954
1955 * FIXME: what about extracting a server-style cert
1956 *        (e.g. https://imposter.example) from the DN or any sAN?
1957
1958 * FIXME: what if we want to call this outside the context of a
1959 *        request?  That complicates the logging.
1960 */
1961static const char* mgs_x509_construct_uid(request_rec *r, gnutls_x509_crt_t cert) {
1962    /* basic strategy, assuming humans are the users: we are going to
1963     * try to reconstruct a "conventional" User ID by pulling in a
1964     * name, comment, and e-mail address.
1965     */
1966    apr_pool_t *pool = r->pool;
1967    const char *name=NULL, *comment=NULL, *email=NULL;
1968    const char *ret=NULL;
1969    /* subpool for temporary allocation: */
1970    apr_pool_t *sp=NULL;
1971
1972    if (APR_SUCCESS != apr_pool_create(&sp, pool))
1973        return NULL; /* i'm assuming that libapr would log this kind
1974                      * of error on its own */
1975
1976     /* Name
1977
1978     the name comes from the leaf commonName of the cert's Subject.
1979
1980     (MAYBE: should we look at trying to assemble a candidate from
1981             givenName, surName, suffix, etc?  the "name" field
1982             appears to be case-insensitive, which seems problematic
1983             from what we expect; see:
1984             http://www.itu.int/rec/T-REC-X.520-200102-s/e )
1985
1986     (MAYBE: should we try pulling a commonName or otherName or
1987             something from subjectAltName? see:
1988             https://tools.ietf.org/html/rfc5280#section-4.2.1.6
1989             GnuTLS does not support looking for Common Names in the
1990             SAN yet)
1991     */
1992    name = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_COMMON_NAME, cert);
1993
1994    /* Comment
1995
1996       I am inclined to punt on this for now, as Comment has been so
1997       atrociously misused in OpenPGP.  Perhaps if there is a
1998       pseudonym (OID 2.5.4.65, aka GNUTLS_OID_X520_PSEUDONYM) field
1999       in the subject or sAN?
2000    */
2001    comment = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_PSEUDONYM, cert);
2002
2003    /* E-mail
2004
2005       This should be the the first rfc822Name from the sAN.
2006
2007       failing that, we'll take the leaf email in the certificate's
2008       subject; this is a deprecated use though.
2009     */
2010    email = mgs_x509_first_type_from_san(sp, GNUTLS_SAN_RFC822NAME, cert);
2011    if (email == NULL)
2012        email = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_PKCS9_EMAIL, cert);
2013
2014    /* assemble all the parts: */
2015
2016    /* must have at least a name or an e-mail. */
2017    if (name == NULL && email == NULL) {
2018        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
2019                "GnuTLS: Need either a name or an e-mail address to get a User ID from an X.509 certificate.");
2020        goto end;
2021    }
2022    if (name) {
2023        if (comment) {
2024            if (email) {
2025                ret = apr_psprintf(pool, "%s (%s) <%s>", name, comment, email);
2026            } else {
2027                ret = apr_psprintf(pool, "%s (%s)", name, comment);
2028            }
2029        } else {
2030            if (email) {
2031                ret = apr_psprintf(pool, "%s <%s>", name, email);
2032            } else {
2033                ret = apr_pstrdup(pool, name);
2034            }
2035        }
2036    } else {
2037        if (comment) {
2038            ret = apr_psprintf(pool, "(%s) <%s>", comment, email);
2039        } else {
2040            ret = apr_psprintf(pool, "<%s>", email);
2041        }
2042    }
2043
2044end:
2045    apr_pool_destroy(sp);
2046    return ret;
2047}
2048#endif /* ENABLE_MSVA */
2049
2050
2051
2052/*
2053 * This hook writes the mod_gnutls status message for a mod_status
2054 * report. According to the comments in mod_status.h, the "flags"
2055 * parameter is a bitwise OR of the AP_STATUS_ flags.
2056 *
2057 * Note that this implementation gives flags explicitly requesting a
2058 * simple response priority, e.g. if AP_STATUS_SHORT is set, flags
2059 * requesting an HTML report will be ignored. As of Apache 2.4.10, the
2060 * following flags were defined in mod_status.h:
2061 *
2062 * AP_STATUS_SHORT (short, non-HTML report requested)
2063 * AP_STATUS_NOTABLE (HTML report without tables)
2064 * AP_STATUS_EXTENDED (detailed report)
2065 */
2066static int mgs_status_hook(request_rec *r, int flags)
2067{
2068    if (r == NULL)
2069        return OK;
2070
2071    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
2072        ap_get_module_config(r->server->module_config, &gnutls_module);
2073
2074    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
2075
2076    if (flags & AP_STATUS_SHORT)
2077    {
2078        ap_rprintf(r, "Using GnuTLS version: %s\n", gnutls_check_version(NULL));
2079        ap_rputs("Built against GnuTLS version: " GNUTLS_VERSION "\n", r);
2080    }
2081    else
2082    {
2083        ap_rputs("<hr>\n", r);
2084        ap_rputs("<h2>GnuTLS Information:</h2>\n<dl>\n", r);
2085
2086        ap_rprintf(r, "<dt>Using GnuTLS version:</dt><dd>%s</dd>\n",
2087                   gnutls_check_version(NULL));
2088        ap_rputs("<dt>Built against GnuTLS version:</dt><dd>"
2089                 GNUTLS_VERSION "</dd>\n", r);
2090        ap_rprintf(r, "<dt>Using TLS:</dt><dd>%s</dd>\n",
2091                   (sc->enabled == GNUTLS_ENABLED_FALSE ? "no" : "yes"));
2092    }
2093
2094    if (sc->enabled != GNUTLS_ENABLED_FALSE)
2095    {
2096        mgs_handle_t* ctxt = get_effective_gnutls_ctxt(r->connection);
2097        if (ctxt && ctxt->session != NULL)
2098        {
2099            char* s_info = gnutls_session_get_desc(ctxt->session);
2100            if (s_info)
2101            {
2102                if (flags & AP_STATUS_SHORT)
2103                    ap_rprintf(r, "Current TLS session: %s\n", s_info);
2104                else
2105                    ap_rprintf(r, "<dt>Current TLS session:</dt><dd>%s</dd>\n",
2106                               s_info);
2107                gnutls_free(s_info);
2108            }
2109        }
2110    }
2111
2112    if (!(flags & AP_STATUS_SHORT))
2113        ap_rputs("</dl>\n", r);
2114
2115    if (sc->ocsp_cache)
2116        mgs_cache_status(sc->ocsp_cache, "GnuTLS OCSP Cache", r, flags);
2117    if (sc->cache_enable)
2118        mgs_cache_status(sc->cache, "GnuTLS Session Cache", r, flags);
2119
2120    return OK;
2121}
Note: See TracBrowser for help on using the repository browser.