source: mod_gnutls/src/gnutls_proxy.c @ 411d286

asyncio
Last change on this file since 411d286 was 411d286, checked in by Fiona Klute <fiona.klute@…>, 19 months ago

Store session tickets for proxy connections in the session cache

The cache key is stored in the session context because it will be
needed for every proxy connection: For checking if there is a cached
ticket (not implemented yet), and to store new tickets if any.

  • Property mode set to 100644
File size: 15.5 KB
Line 
1/*
2 *  Copyright 2015-2020 Fiona Klute
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16
17#include "mod_gnutls.h"
18#include "gnutls_proxy.h"
19#include "gnutls_util.h"
20
21#include <apr_strings.h>
22#include <gnutls/gnutls.h>
23
24/*
25 * Callback to check the server certificate for proxy HTTPS
26 * connections, to be used with
27 * gnutls_certificate_set_verify_function.
28
29 * Returns: 0 if certificate check was successful (certificate
30 * trusted), non-zero otherwise (error during check or untrusted
31 * certificate).
32 */
33static int gtls_check_server_cert(gnutls_session_t session)
34{
35    mgs_handle_t *ctxt = (mgs_handle_t *) gnutls_session_get_ptr(session);
36    unsigned int status;
37
38    /* Get peer hostname from a note left by mod_proxy */
39    const char *peer_hostname =
40        apr_table_get(ctxt->c->notes, PROXY_SNI_NOTE);
41    if (peer_hostname == NULL)
42        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
43                      "%s: " PROXY_SNI_NOTE " NULL, cannot check "
44                      "peer's hostname", __func__);
45
46    /* Verify certificate, including hostname match. Should
47     * peer_hostname be NULL for some reason, the name is not
48     * checked. */
49    int err = gnutls_certificate_verify_peers3(session, peer_hostname,
50                                               &status);
51    if (err != GNUTLS_E_SUCCESS)
52    {
53        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
54                      "%s: server certificate check failed: %s (%d)",
55                      __func__, gnutls_strerror(err), err);
56        return err;
57    }
58
59    if (status == 0)
60        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
61                      "%s: server certificate is trusted.",
62                      __func__);
63    else
64    {
65        gnutls_datum_t out;
66        /* GNUTLS_CRT_X509: ATM, only X509 is supported for proxy
67         * certs 0: according to function API, the last argument
68         * should be 0 */
69        err = gnutls_certificate_verification_status_print(status,
70                                                           GNUTLS_CRT_X509,
71                                                           &out, 0);
72        if (err != GNUTLS_E_SUCCESS)
73            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
74                          "%s: server verify print failed: %s (%d)",
75                          __func__, gnutls_strerror(err), err);
76        else
77            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
78                          "%s: %s",
79                          __func__, out.data);
80        gnutls_free(out.data);
81    }
82
83    return status;
84}
85
86
87
88static apr_status_t cleanup_proxy_x509_credentials(void *arg)
89{
90    mgs_srvconf_rec *sc = (mgs_srvconf_rec *) arg;
91
92    if (sc->proxy_x509_creds)
93    {
94        /* This implicitly releases the associated trust list
95         * sc->proxy_x509_tl, too. */
96        gnutls_certificate_free_credentials(sc->proxy_x509_creds);
97        sc->proxy_x509_creds = NULL;
98        sc->proxy_x509_tl = NULL;
99    }
100
101    if (sc->anon_client_creds)
102    {
103        gnutls_anon_free_client_credentials(sc->anon_client_creds);
104        sc->anon_client_creds = NULL;
105    }
106
107    /* Deinit proxy priorities only if set from
108     * sc->proxy_priorities_str. Otherwise the server is using the
109     * default global priority cache, which must not be deinitialized
110     * here. */
111    if (sc->proxy_priorities_str && sc->proxy_priorities)
112    {
113        gnutls_priority_deinit(sc->proxy_priorities);
114        sc->proxy_priorities = NULL;
115    }
116
117    return APR_SUCCESS;
118}
119
120
121
122apr_status_t load_proxy_x509_credentials(apr_pool_t *pconf,
123                                         apr_pool_t *ptemp,
124                                         server_rec *s)
125{
126    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
127        ap_get_module_config(s->module_config, &gnutls_module);
128
129    if (sc == NULL)
130        return APR_EGENERAL;
131
132    apr_status_t ret = APR_EINIT;
133    int err = GNUTLS_E_SUCCESS;
134
135    /* Cleanup function for the GnuTLS structures allocated below */
136    apr_pool_cleanup_register(pconf, sc, cleanup_proxy_x509_credentials,
137                              apr_pool_cleanup_null);
138
139    /* Function pool, gets destroyed before exit. */
140    apr_pool_t *pool;
141    ret = apr_pool_create(&pool, ptemp);
142    if (ret != APR_SUCCESS)
143    {
144        ap_log_error(APLOG_MARK, APLOG_ERR, ret, s,
145                     "%s: failed to allocate function memory pool.", __func__);
146        return ret;
147    }
148
149    /* allocate credentials structures */
150    err = gnutls_certificate_allocate_credentials(&sc->proxy_x509_creds);
151    if (err != GNUTLS_E_SUCCESS)
152    {
153        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
154                     "%s: Failed to initialize proxy credentials: (%d) %s",
155                     __func__, err, gnutls_strerror(err));
156        return APR_EGENERAL;
157    }
158    err = gnutls_anon_allocate_client_credentials(&sc->anon_client_creds);
159    if (err != GNUTLS_E_SUCCESS)
160    {
161        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
162                     "%s: Failed to initialize anon credentials for proxy: "
163                     "(%d) %s", __func__, err, gnutls_strerror(err));
164        return APR_EGENERAL;
165    }
166
167    /* Check if the proxy priorities have been set, fail immediately
168     * if not */
169    if (sc->proxy_priorities_str == NULL)
170    {
171        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
172                     "No GnuTLSProxyPriorities directive for host '%s:%d', "
173                     "using default '%s'.",
174                     s->server_hostname, s->addrs->host_port,
175                     MGS_DEFAULT_PRIORITY);
176        sc->proxy_priorities = mgs_get_default_prio();
177    }
178    else
179    {
180        /* parse proxy priorities */
181        const char *err_pos = NULL;
182        err = gnutls_priority_init(&sc->proxy_priorities,
183                                   sc->proxy_priorities_str, &err_pos);
184        if (err != GNUTLS_E_SUCCESS)
185        {
186            if (ret == GNUTLS_E_INVALID_REQUEST)
187                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
188                             "%s: Syntax error parsing proxy priorities "
189                             "string at: %s",
190                             __func__, err_pos);
191            else
192                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
193                             "Error setting proxy priorities: %s (%d)",
194                             gnutls_strerror(err), err);
195            ret = APR_EGENERAL;
196        }
197    }
198
199    /* load certificate and key for client auth, if configured */
200    if (sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
201    {
202        char* cert_file = ap_server_root_relative(pool,
203                                                  sc->proxy_x509_cert_file);
204        char* key_file = ap_server_root_relative(pool,
205                                                 sc->proxy_x509_key_file);
206        err = gnutls_certificate_set_x509_key_file(sc->proxy_x509_creds,
207                                                   cert_file,
208                                                   key_file,
209                                                   GNUTLS_X509_FMT_PEM);
210        if (err != GNUTLS_E_SUCCESS)
211        {
212            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
213                         "%s: loading proxy client credentials failed: %s (%d)",
214                         __func__, gnutls_strerror(err), err);
215            ret = APR_EGENERAL;
216        }
217    }
218    else if (!sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
219    {
220        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
221                     "%s: proxy key file not set!", __func__);
222        ret = APR_EGENERAL;
223    }
224    else if (!sc->proxy_x509_cert_file && sc->proxy_x509_key_file)
225    {
226        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
227                     "%s: proxy certificate file not set!", __func__);
228        ret = APR_EGENERAL;
229    }
230    else
231        /* if both key and cert are NULL, client auth is not used */
232        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
233                     "%s: no client credentials for proxy", __func__);
234
235    /* must be set if the server certificate is to be checked */
236    if (sc->proxy_x509_ca_file)
237    {
238        /* initialize the trust list */
239        err = gnutls_x509_trust_list_init(&sc->proxy_x509_tl, 0);
240        if (err != GNUTLS_E_SUCCESS)
241        {
242            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
243                         "%s: gnutls_x509_trust_list_init failed: %s (%d)",
244                         __func__, gnutls_strerror(err), err);
245            ret = APR_EGENERAL;
246        }
247
248        char* ca_file = ap_server_root_relative(pool,
249                                                sc->proxy_x509_ca_file);
250        /* if no CRL is used, sc->proxy_x509_crl_file is NULL */
251        char* crl_file = NULL;
252        if (sc->proxy_x509_crl_file)
253            crl_file = ap_server_root_relative(pool,
254                                               sc->proxy_x509_crl_file);
255
256        /* returns number of loaded elements */
257        err = gnutls_x509_trust_list_add_trust_file(sc->proxy_x509_tl,
258                                                    ca_file,
259                                                    crl_file,
260                                                    GNUTLS_X509_FMT_PEM,
261                                                    0 /* tl_flags */,
262                                                    0 /* tl_vflags */);
263        if (err > 0)
264            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
265                         "%s: proxy CA trust list: %d structures loaded",
266                         __func__, err);
267        else if (err == 0)
268            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
269                         "%s: proxy CA trust list is empty (%d)",
270                         __func__, err);
271        else /* err < 0 */
272        {
273            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
274                         "%s: error loading proxy CA trust list: %s (%d)",
275                         __func__, gnutls_strerror(err), err);
276            ret = APR_EGENERAL;
277        }
278
279        /* attach trust list to credentials */
280        gnutls_certificate_set_trust_list(sc->proxy_x509_creds,
281                                          sc->proxy_x509_tl, 0);
282    }
283    else
284        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
285                     "%s: no CA trust list for proxy connections, "
286                     "TLS connections will fail!", __func__);
287
288    gnutls_certificate_set_verify_function(sc->proxy_x509_creds,
289                                           gtls_check_server_cert);
290    apr_pool_destroy(pool);
291    return ret;
292}
293
294
295
296/**
297 * Returns either a valid hostname for use with SNI, or NULL.
298 */
299static const char *get_proxy_sni_name(mgs_handle_t *ctxt)
300{
301    /* Get peer hostname from note left by mod_proxy */
302    const char *peer_hostname =
303        apr_table_get(ctxt->c->notes, PROXY_SNI_NOTE);
304
305    /* Used only as target for apr_ipsubnet_create() */
306    apr_ipsubnet_t *probe;
307    /* If the note is present (!= NULL) check that the value is NOT an
308     * IP address, which wouldn't be valid for SNI. */
309    if ((peer_hostname != NULL)
310        && (apr_ipsubnet_create(&probe, peer_hostname, NULL, ctxt->c->pool)
311            == APR_SUCCESS))
312        return NULL;
313
314    return peer_hostname;
315}
316
317
318
319static void proxy_conn_set_sni(mgs_handle_t *ctxt)
320{
321    const char *peer_hostname = get_proxy_sni_name(ctxt);
322    if (peer_hostname != NULL)
323    {
324        int ret = gnutls_server_name_set(ctxt->session, GNUTLS_NAME_DNS,
325                                         peer_hostname, strlen(peer_hostname));
326        if (ret != GNUTLS_E_SUCCESS)
327            ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, ctxt->c,
328                          "Could not set SNI '%s' for proxy connection: "
329                          "%s (%d)",
330                          peer_hostname, gnutls_strerror(ret), ret);
331    }
332}
333
334
335
336/** Initial size for the APR array storing ALPN protocol
337 * names. Currently only mod_proxy_http2 uses ALPN for proxy
338 * connections and proposes "h2" exclusively. This provides enough
339 * room without additional allocation even if an HTTP/1.1 fallback
340 * should be added while still being small. */
341#define INIT_ALPN_ARR_SIZE 2
342
343/**
344 * Set ALPN proposals for a proxy handshake based on the note from the
345 * proxy module (see `PROXY_SNI_NOTE`). The note is expected to
346 * contain a string, multiple protocol names can be separated by ","
347 * or " ", or a combination of them.
348 *
349 * @param ctxt the mod_gnutls connection handle
350 */
351static void proxy_conn_set_alpn(mgs_handle_t *ctxt)
352{
353    const char *proxy_alpn =
354        apr_table_get(ctxt->c->notes, PROXY_ALPN_NOTE);
355    if (proxy_alpn == NULL)
356        return;
357    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
358                  "%s: proxy module ALPN note is '%s', "
359                  "length %" APR_SIZE_T_FMT ".",
360                  __func__, proxy_alpn, strlen(proxy_alpn));
361
362    apr_array_header_t* protocols =
363        apr_array_make(ctxt->c->pool, INIT_ALPN_ARR_SIZE,
364                       sizeof(const char *));
365
366    /* mod_ssl tokenizes the note by "," or " " to allow multiple
367     * protocols. We need to copy the note because apr_strtok()
368     * modifies the string to make each token NULL terminated. On the
369     * plus side that means we do not need to copy individual
370     * tokens. */
371    char *tok = apr_pstrdup(ctxt->c->pool, proxy_alpn);
372    /* state for apr_strtok, pointer to character following current
373     * token */
374    char *last = NULL;
375    while ((tok = apr_strtok(tok, ", ", &last)))
376    {
377        APR_ARRAY_PUSH(protocols, const char *) = tok;
378        tok = NULL;
379    }
380
381    gnutls_datum_t* alpn_protos =
382        mgs_str_array_to_datum_array(protocols,
383                                     ctxt->c->pool,
384                                     protocols->nelts);
385    int ret = gnutls_alpn_set_protocols(ctxt->session,
386                                        alpn_protos,
387                                        protocols->nelts,
388                                        0 /* flags */);
389    if (ret != GNUTLS_E_SUCCESS)
390        ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, ctxt->c,
391                      "Could not set ALPN proposals for proxy "
392                      "connection: %s (%d)",
393                      gnutls_strerror(ret), ret);
394}
395
396
397
398gnutls_datum_t mgs_proxy_ticket_id(mgs_handle_t *ctxt, apr_pool_t *pool)
399{
400    apr_pool_t *tmp;
401    if (pool)
402        tmp = pool;
403    else
404        tmp = ctxt->c->pool;
405
406    /* c->client_addr->port and c->client_ip actually contain
407     * information on the remote server for outgoing proxy
408     * connections, prefer SNI hostname over IP.
409     *
410     * The server_hostname is used to tie the cache entry to a
411     * specific vhost, because different vhosts may have different
412     * settings for the same backend server.
413     */
414    const char *peer_hostname = get_proxy_sni_name(ctxt);
415    gnutls_datum_t key;
416    key.data = (unsigned char *)
417        apr_psprintf(tmp, "proxy:%s:%s:%d",
418                     ctxt->c->base_server->server_hostname,
419                     peer_hostname ? peer_hostname : ctxt->c->client_ip,
420                     ctxt->c->client_addr->port);
421    key.size = strlen((const char*) key.data);
422    return key;
423}
424
425
426
427void mgs_set_proxy_handshake_ext(mgs_handle_t *ctxt)
428{
429    proxy_conn_set_sni(ctxt);
430    proxy_conn_set_alpn(ctxt);
431}
Note: See TracBrowser for help on using the repository browser.