source: mod_gnutls/src/gnutls_hooks.c @ 64470ce

debian/masterproxy-ticket
Last change on this file since 64470ce was 64470ce, checked in by Fiona Klute <fiona.klute@…>, 21 months ago

Load credentials and prepare ALPN in pre client hello hook

This fully enables early SNI, falling back to default virtual host
ALPN and post client hello SNI if early SNI is not available.

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