source: mod_gnutls/src/gnutls_hooks.c @ 845c112

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

Async OCSP updates for multi-stapling

There's now one mod_watchdog callback per certificate with stapling
enabled, so they all get updated independently. Adding "server" to the
OCSP data structure is necessary because the async update function
needs access to the virtual host configuration.

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