source: mod_gnutls/src/gnutls_hooks.c @ 0da10eb

asynciodebian/mastermainproxy-ticket
Last change on this file since 0da10eb was 0da10eb, checked in by Fiona Klute <fiona.klute@…>, 4 years ago

Document Early SNI related functions and rename the post client hello hook

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