source: mod_gnutls/src/gnutls_ocsp.c @ 368e581

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

Update OCSP response cache only if response is missing or invalid

"Invalid" includes expiration, so responses that contain a nextUpdate
field will be updated after that time has passed even if their cache
lifetime has not yet expired, or cache objects haven't been expired
since.

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