source: mod_gnutls/src/gnutls_hooks.c @ b22def6

proxy-ticket
Last change on this file since b22def6 was b22def6, checked in by Fiona Klute <fiona.klute@…>, 7 months ago

Remove SIGPIPE signal block

There's no discernible reason to keep this, the main HTTPD code
doesn't use it at all, and the proxy code with which it was originally
added in 33826c53d7991024eeed255f860e9818188e2bcb works fine without
it.

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