source: mod_gnutls/src/gnutls_proxy.c @ b6c7866

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

Update copyright headers of files changed this year

  • Property mode set to 100644
File size: 14.3 KB
RevLine 
[68b5156]1/*
[b6c7866]2 *  Copyright 2015-2019 Fiona Klute
[68b5156]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
[564f33f]21#include <apr_strings.h>
[68b5156]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}
[564f33f]293
294
295
296static void proxy_conn_set_sni(mgs_handle_t *ctxt)
297{
298    /* Get peer hostname from note left by mod_proxy */
299    const char *peer_hostname =
300        apr_table_get(ctxt->c->notes, PROXY_SNI_NOTE);
301    /* Used only as target for apr_ipsubnet_create() */
302    apr_ipsubnet_t *probe;
303    /* Check if the note is present (!= NULL) and NOT an IP
304     * address */
305    if ((peer_hostname) != NULL
306        && (apr_ipsubnet_create(&probe, peer_hostname, NULL, ctxt->c->pool)
307            != APR_SUCCESS))
308    {
309        int ret = gnutls_server_name_set(ctxt->session, GNUTLS_NAME_DNS,
310                                         peer_hostname, strlen(peer_hostname));
311        if (ret != GNUTLS_E_SUCCESS)
312            ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, ctxt->c,
313                          "Could not set SNI '%s' for proxy connection: "
314                          "%s (%d)",
315                          peer_hostname, gnutls_strerror(ret), ret);
316    }
317}
318
319
320
[a900948]321/** Initial size for the APR array storing ALPN protocol
322 * names. Currently only mod_proxy_http2 uses ALPN for proxy
323 * connections and proposes "h2" exclusively. This provides enough
324 * room without additional allocation even if an HTTP/1.1 fallback
325 * should be added while still being small. */
326#define INIT_ALPN_ARR_SIZE 2
327
328/**
329 * Set ALPN proposals for a proxy handshake based on the note from the
330 * proxy module (see `PROXY_SNI_NOTE`). The note is expected to
331 * contain a string, multiple protocol names can be separated by ","
332 * or " ", or a combination of them.
333 *
334 * @param ctxt the mod_gnutls connection handle
335 */
[564f33f]336static void proxy_conn_set_alpn(mgs_handle_t *ctxt)
337{
338    const char *proxy_alpn =
339        apr_table_get(ctxt->c->notes, PROXY_ALPN_NOTE);
[c7710cf]340    if (proxy_alpn == NULL)
341        return;
[a900948]342    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_SUCCESS, ctxt->c,
343                  "%s: proxy module ALPN note is '%s', "
[c7710cf]344                  "length %" APR_SIZE_T_FMT ".",
345                  __func__, proxy_alpn, strlen(proxy_alpn));
[a900948]346
347    apr_array_header_t* protocols =
348        apr_array_make(ctxt->c->pool, INIT_ALPN_ARR_SIZE,
349                       sizeof(const char *));
350
351    /* mod_ssl tokenizes the note by "," or " " to allow multiple
352     * protocols. We need to copy the note because apr_strtok()
353     * modifies the string to make each token NULL terminated. On the
354     * plus side that means we do not need to copy individual
355     * tokens. */
356    char *tok = apr_pstrdup(ctxt->c->pool, proxy_alpn);
357    /* state for apr_strtok, pointer to character following current
358     * token */
359    char *last = NULL;
360    while ((tok = apr_strtok(tok, ", ", &last)))
361    {
362        APR_ARRAY_PUSH(protocols, const char *) = tok;
363        tok = NULL;
364    }
365
366    gnutls_datum_t* alpn_protos =
367        mgs_str_array_to_datum_array(protocols,
368                                     ctxt->c->pool,
369                                     protocols->nelts);
[c7710cf]370    int ret = gnutls_alpn_set_protocols(ctxt->session,
[a900948]371                                        alpn_protos,
372                                        protocols->nelts,
[c7710cf]373                                        0 /* flags */);
374    if (ret != GNUTLS_E_SUCCESS)
375        ap_log_cerror(APLOG_MARK, APLOG_ERR, ret, ctxt->c,
[a900948]376                      "Could not set ALPN proposals for proxy "
[c7710cf]377                      "connection: %s (%d)",
[a900948]378                      gnutls_strerror(ret), ret);
[564f33f]379}
380
381
382
383void mgs_set_proxy_handshake_ext(mgs_handle_t *ctxt)
384{
385    proxy_conn_set_sni(ctxt);
386    proxy_conn_set_alpn(ctxt);
387}
Note: See TracBrowser for help on using the repository browser.