source: mod_gnutls/src/gnutls_hooks.c @ 7ff6c6c

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

Add proof-of-concept SNI parser in a pre client hello hook

The SNI parser is complete, but right now the hook only retrieves the
SNI data and logs it. The goal is to select the right virtual host and
load ALPN parameters (and possibly others) before GnuTLS processes the
ClientHello? message. That should make different "Protocols" directives
between virtual hosts work as expected.

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