source: mod_gnutls/src/gnutls_ocsp.c @ 5559aa6

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

Rely on cache for OCSP response expiration

With fixed DBM cache expiry, memcache support, and OCSP grace time we
should be able to rely on cache expiration instead of parsing the DER
response on every session start just to get the expiration time.

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