source: mod_gnutls/src/gnutls_ocsp.c @ 08817d0

debian/masterdebian/stretch-backportsupstream
Last change on this file since 08817d0 was 08817d0, checked in by Thomas Klute <thomas2.klute@…>, 3 years ago

Check OCSP response before caching

Only verified responses should be cached. This improves both security
and performance: The cache does not need to touch untrusted data, and
if only verified responses are cached there is no need to do full
verification every time a response is fetched from the cache.

  • Property mode set to 100644
File size: 16.3 KB
Line 
1/**
2 *  Copyright 2016 Thomas 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 "gnutls_ocsp.h"
18#include "mod_gnutls.h"
19#include "gnutls_cache.h"
20
21#include <apr_lib.h>
22#include <apr_time.h>
23#include <gnutls/ocsp.h>
24#include <time.h>
25
26#ifdef APLOG_USE_MODULE
27APLOG_USE_MODULE(gnutls);
28#endif
29
30
31
32#define _log_one_ocsp_fail(str, srv)                                    \
33    ap_log_error(APLOG_MARK, APLOG_INFO, APR_EGENERAL, (srv),           \
34                 "Reason for failed OCSP response verification: %s", (str))
35/*
36 * Log all matching reasons for verification failure
37 */
38static void _log_verify_fail_reason(const unsigned int verify, server_rec *s)
39{
40    if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
41        _log_one_ocsp_fail("Signer cert not found", s);
42
43    if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
44        _log_one_ocsp_fail("Signer cert keyusage error", s);
45
46    if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
47        _log_one_ocsp_fail("Signer cert is not trusted", s);
48
49    if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
50        _log_one_ocsp_fail("Insecure algorithm", s);
51
52    if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
53        _log_one_ocsp_fail("Signature failure", s);
54
55    if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
56        _log_one_ocsp_fail("Signer cert not yet activated", s);
57
58    if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
59        _log_one_ocsp_fail("Signer cert expired", s);
60}
61
62
63
64const char *mgs_store_ocsp_response_path(cmd_parms *parms,
65                                         void *dummy __attribute__((unused)),
66                                         const char *arg)
67{
68    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
69        ap_get_module_config(parms->server->module_config, &gnutls_module);
70
71    sc->ocsp_response_file = ap_server_root_relative(parms->pool, arg);
72    return NULL;
73}
74
75
76
77/**
78 * Check if the provided OCSP response is usable for stapling in
79 * connections to this server. Returns GNUTLS_E_SUCCESS if yes.
80 *
81 * Supports only one certificate status per response.
82 */
83int check_ocsp_response(server_rec *s, const gnutls_datum_t *ocsp_response)
84{
85    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
86        ap_get_module_config(s->module_config, &gnutls_module);
87
88    if (sc->ocsp_trust == NULL)
89    {
90        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
91                     "No OCSP trust list available for server \"%s\"!",
92                     s->server_hostname);
93        return GNUTLS_E_NO_CERTIFICATE_FOUND;
94    }
95
96    gnutls_ocsp_resp_t resp;
97    int ret = gnutls_ocsp_resp_init(&resp);
98    if (ret != GNUTLS_E_SUCCESS)
99    {
100        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
101                     "Could not initialize OCSP response structure: %s (%d)",
102                     gnutls_strerror(ret), ret);
103        goto resp_cleanup;
104    }
105    ret = gnutls_ocsp_resp_import(resp, ocsp_response);
106    if (ret != GNUTLS_E_SUCCESS)
107    {
108        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
109                     "Importing OCSP response failed: %s (%d)",
110                     gnutls_strerror(ret), ret);
111        goto resp_cleanup;
112    }
113
114    ret = gnutls_ocsp_resp_check_crt(resp, 0, sc->certs_x509_crt_chain[0]);
115    if (ret != GNUTLS_E_SUCCESS)
116    {
117        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
118                     "OCSP response is not for server certificate: %s (%d)",
119                     gnutls_strerror(ret), ret);
120        goto resp_cleanup;
121    }
122
123    unsigned int verify;
124    ret = gnutls_ocsp_resp_verify(resp, *(sc->ocsp_trust), &verify, 0);
125    if (ret != GNUTLS_E_SUCCESS)
126    {
127        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
128                     "OCSP response verification failed: %s (%d)",
129                     gnutls_strerror(ret), ret);
130        goto resp_cleanup;
131    }
132    else
133    {
134        /* verification worked, check the result */
135        if (verify != 0)
136        {
137            _log_verify_fail_reason(verify, s);
138            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
139            goto resp_cleanup;
140        }
141        else
142            ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
143                         "OCSP response is valid.");
144    }
145
146    /* OK, response is for our certificate and valid, let's get the
147     * actual response data. */
148    unsigned int cert_status;
149    time_t this_update;
150    time_t next_update;
151    ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
152                                      &cert_status, &this_update,
153                                      &next_update, NULL, NULL);
154    if (ret != GNUTLS_E_SUCCESS)
155    {
156        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
157                     "Could not get OCSP response data: %s (%d)",
158                     gnutls_strerror(ret), ret);
159        goto resp_cleanup;
160    }
161
162    apr_time_t now = apr_time_now();
163    apr_time_t valid_at;
164    apr_time_ansi_put(&valid_at, this_update);
165    /* Buffer for human-readable times produced by apr_rfc822_date,
166     * see apr_time.h */
167    char date_str[APR_RFC822_DATE_LEN];
168    apr_rfc822_date(date_str, valid_at);
169
170    if (now < valid_at)
171    {
172        /* We don't know if our clock or that of the OCSP responder is
173         * out of sync, so warn but continue. */
174        ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EGENERAL, s,
175                     "OSCP response claims to be from future (%s), check "
176                     "time synchronization!", date_str);
177    }
178
179    if (next_update == (time_t) -1)
180        ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
181                     "OSCP response does not contain nextUpdate info.");
182    else
183    {
184        apr_time_t valid_to;
185        apr_time_ansi_put(&valid_to, next_update);
186        if (now > valid_to)
187        {
188            apr_rfc822_date(date_str, valid_to);
189            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
190                         "OCSP response has expired at %s!", date_str);
191            /* Do not send a stale response */
192            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
193            goto resp_cleanup;
194        }
195    }
196
197    /* What's the actual status? Will be one of
198     * gnutls_ocsp_cert_status_t as defined in gnutls/ocsp.h. */
199    if (cert_status == GNUTLS_OCSP_CERT_GOOD)
200    {
201        /* Yay, everything's good! */
202        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
203                     "CA flagged certificate as valid at %s.", date_str);
204    }
205    else
206    {
207        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
208                     "CA flagged certificate as %s at %s.",
209                     cert_status == GNUTLS_OCSP_CERT_REVOKED ?
210                     "revoked" : "unknown", date_str);
211        ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
212    }
213
214 resp_cleanup:
215    gnutls_ocsp_resp_deinit(resp);
216    return ret;
217}
218
219
220
221/*
222 * Returns the certificate fingerprint, memory is allocated from p.
223 */
224static gnutls_datum_t mgs_get_cert_fingerprint(apr_pool_t *p,
225                                               gnutls_x509_crt_t cert)
226{
227    gnutls_datum_t fingerprint = {NULL, 0};
228    size_t fplen;
229    gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, NULL, &fplen);
230    unsigned char * fp = apr_palloc(p, fplen);
231    gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, fp, &fplen);
232    /* Safe integer type conversion: The types of fingerprint.size
233     * (unsigned int) and fplen (size_t) may have different
234     * lengths. */
235    if (__builtin_add_overflow(fplen, 0, &fingerprint.size))
236        fingerprint.size = 0;
237    else
238        fingerprint.data = fp;
239    return fingerprint;
240}
241
242
243
244/* TODO: response should be fetched from sc->ocsp_uri */
245apr_status_t mgs_cache_ocsp_response(server_rec *s)
246{
247    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
248        ap_get_module_config(s->module_config, &gnutls_module);
249
250    if (sc->cache_type != mgs_cache_dbm && sc->cache_type != mgs_cache_gdbm)
251    {
252        /* experimental OCSP cache requires DBM cache */
253        return APR_ENOTIMPL;
254    }
255
256    apr_pool_t *tmp;
257    apr_status_t rv = apr_pool_create(&tmp, NULL);
258
259    /* the fingerprint will be used as cache key */
260    gnutls_datum_t fingerprint =
261        mgs_get_cert_fingerprint(tmp, sc->certs_x509_crt_chain[0]);
262    if (fingerprint.data == NULL)
263        return APR_EINVAL;
264
265    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
266                 "Loading OCSP response from %s",
267                 sc->ocsp_response_file);
268    apr_file_t *file;
269    apr_finfo_t finfo;
270    apr_size_t br = 0;
271    rv = apr_file_open(&file, sc->ocsp_response_file,
272                       APR_READ | APR_BINARY, APR_OS_DEFAULT, tmp);
273    if (rv != APR_SUCCESS)
274    {
275        apr_pool_destroy(tmp);
276        return rv;
277    }
278    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file);
279    if (rv != APR_SUCCESS)
280    {
281        apr_pool_destroy(tmp);
282        return rv;
283    }
284
285    gnutls_datum_t resp;
286    resp.data = apr_palloc(tmp, finfo.size);
287    rv = apr_file_read_full(file, resp.data, finfo.size, &br);
288    if (rv != APR_SUCCESS)
289    {
290        apr_pool_destroy(tmp);
291        return rv;
292    }
293    apr_file_close(file);
294    /* safe integer type conversion */
295    if (__builtin_add_overflow(br, 0, &resp.size))
296    {
297        apr_pool_destroy(tmp);
298        return APR_EINVAL;
299    }
300
301    if (check_ocsp_response(s, &resp) != GNUTLS_E_SUCCESS)
302    {
303        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, s,
304                     "OCSP response validation failed, cannot "
305                     "update cache.");
306        apr_pool_destroy(tmp);
307        return APR_EGENERAL;
308    }
309
310    /* TODO: make cache lifetime configurable, make sure expiration
311     * happens without storing new data */
312    int r = dbm_cache_store(s, fingerprint,
313                            resp, apr_time_now() + apr_time_from_sec(120));
314    /* destroy pool, and original copy of the OCSP response with it */
315    apr_pool_destroy(tmp);
316    if (r != 0)
317    {
318        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
319                      "Storing OCSP response in cache failed.");
320        return APR_EGENERAL;
321    }
322    return APR_SUCCESS;
323}
324
325
326
327int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
328                          void *ptr,
329                          gnutls_datum_t *ocsp_response)
330{
331    mgs_handle_t *ctxt = (mgs_handle_t *) ptr;
332
333    gnutls_datum_t fingerprint =
334        mgs_get_cert_fingerprint(ctxt->c->pool,
335                                 ctxt->sc->certs_x509_crt_chain[0]);
336    if (fingerprint.data == NULL)
337        return GNUTLS_E_NO_CERTIFICATE_STATUS;
338
339    *ocsp_response = dbm_cache_fetch(ctxt, fingerprint);
340    if (ocsp_response->size == 0)
341    {
342        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
343                      "Fetching OCSP response from cache failed.");
344    }
345    else
346    {
347        /* Succeed if response is present and valid. */
348        if (check_ocsp_response(ctxt->c->base_server, ocsp_response)
349            == GNUTLS_E_SUCCESS)
350            return GNUTLS_E_SUCCESS;
351    }
352    /* get rid of invalid response (if any) */
353    gnutls_free(ocsp_response->data);
354    ocsp_response->data = NULL;
355
356    /* If the cache had no response or an invalid one, try to update. */
357    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
358                  "No valid OCSP response in cache, trying to update.");
359    apr_status_t rv = mgs_cache_ocsp_response(ctxt->c->base_server);
360    if (rv != APR_SUCCESS)
361    {
362        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, ctxt->c,
363                      "Updating OCSP response cache failed");
364        goto fail_cleanup;
365    }
366
367    /* retry reading from cache */
368    *ocsp_response = dbm_cache_fetch(ctxt, fingerprint);
369    if (ocsp_response->size == 0)
370    {
371        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
372                      "Fetching OCSP response from cache failed on retry.");
373    }
374    else
375    {
376        /* Succeed if response is present and valid. */
377        if (check_ocsp_response(ctxt->c->base_server, ocsp_response)
378            == GNUTLS_E_SUCCESS)
379            return GNUTLS_E_SUCCESS;
380    }
381
382    /* failure, clean up response data */
383 fail_cleanup:
384    gnutls_free(ocsp_response->data);
385    ocsp_response->size = 0;
386    ocsp_response->data = NULL;
387    return GNUTLS_E_NO_CERTIFICATE_STATUS;
388}
389
390
391
392int mgs_create_ocsp_trust_list(gnutls_x509_trust_list_t *tl,
393                               const gnutls_x509_crt_t *chain,
394                               const int num)
395{
396    int added = 0;
397    int ret = gnutls_x509_trust_list_init(tl, num);
398
399    if (ret == GNUTLS_E_SUCCESS)
400        added = gnutls_x509_trust_list_add_cas(*tl, chain, num, 0);
401
402    if (added != num)
403        ret = GNUTLS_E_CERTIFICATE_ERROR;
404
405    /* Clean up trust list in case of error */
406    if (ret != GNUTLS_E_SUCCESS)
407        gnutls_x509_trust_list_deinit(*tl, 0);
408
409    return ret;
410}
411
412
413
414apr_status_t mgs_cleanup_trust_list(void *data)
415{
416    gnutls_x509_trust_list_t *tl = (gnutls_x509_trust_list_t *) data;
417    gnutls_x509_trust_list_deinit(*tl, 0);
418    return APR_SUCCESS;
419}
420
421
422
423apr_uri_t * mgs_cert_get_ocsp_uri(apr_pool_t *p, gnutls_x509_crt_t cert)
424{
425    apr_pool_t *tmp;
426    apr_status_t rv = apr_pool_create(&tmp, p);
427    if (rv != APR_SUCCESS)
428        return NULL;
429
430    apr_uri_t *ocsp_uri = NULL;
431
432    int ret = GNUTLS_E_SUCCESS;
433    /* search authority info access for OCSP URI */
434    for (int seq = 0; ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; seq++)
435    {
436        gnutls_datum_t ocsp_access_data;
437        ret = gnutls_x509_crt_get_authority_info_access(cert, seq,
438                                                        GNUTLS_IA_OCSP_URI,
439                                                        &ocsp_access_data,
440                                                        NULL);
441        if (ret == GNUTLS_E_SUCCESS)
442        {
443            /* create NULL terminated string */
444            char *ocsp_str =
445                apr_pstrndup(tmp, (const char*) ocsp_access_data.data,
446                             ocsp_access_data.size);
447            gnutls_free(ocsp_access_data.data);
448
449            ocsp_uri = apr_palloc(p, sizeof(apr_uri_t));
450            rv = apr_uri_parse(p, ocsp_str, ocsp_uri);
451            if (rv == APR_SUCCESS)
452                break;
453            else
454                ocsp_uri = NULL;
455        }
456    }
457
458    apr_pool_destroy(tmp);
459    return ocsp_uri;
460}
461
462
463
464/*
465 * Like in the general post_config hook the HTTP status codes for
466 * errors are just for fun. What matters is "neither OK nor DECLINED"
467 * to denote an error.
468 */
469int mgs_ocsp_post_config_server(apr_pool_t *pconf,
470                                apr_pool_t *ptemp __attribute__((unused)),
471                                server_rec *server)
472{
473    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
474        ap_get_module_config(server->module_config, &gnutls_module);
475
476    if (sc->certs_x509_chain_num < 2)
477    {
478        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
479                     "OCSP stapling is enabled but no CA certificate "
480                     "available for %s:%d, make sure it is included in "
481                     "GnuTLSCertificateFile!",
482                     server->server_hostname, server->addrs->host_port);
483        return HTTP_NOT_FOUND;
484    }
485
486    sc->ocsp_uri = mgs_cert_get_ocsp_uri(pconf, sc->certs_x509_crt_chain[0]);
487
488    sc->ocsp_trust = apr_palloc(pconf,
489                                sizeof(gnutls_x509_trust_list_t));
490     /* Only the direct issuer may sign the OCSP response or an OCSP
491      * signer. */
492    int ret = mgs_create_ocsp_trust_list(sc->ocsp_trust,
493                                         &(sc->certs_x509_crt_chain[1]),
494                                         1);
495    if (ret != GNUTLS_E_SUCCESS)
496    {
497        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
498                     "Could not create OCSP trust list: %s (%d)",
499                     gnutls_strerror(ret), ret);
500        return HTTP_INTERNAL_SERVER_ERROR;
501    }
502    /* deinit trust list when the config pool is destroyed */
503    apr_pool_cleanup_register(pconf, sc->ocsp_trust,
504                              mgs_cleanup_trust_list,
505                              apr_pool_cleanup_null);
506
507    return OK;
508}
Note: See TracBrowser for help on using the repository browser.