source: mod_gnutls/src/gnutls_ocsp.c @ a372379

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

Store server certificate fingerprint in OCSP config

It's not like it's going to change without a server reload, so just
calculate the fingerprint once instead of for every connection that
uses OCSP stapling.

  • Property mode set to 100644
File size: 17.8 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
[cc74801e]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;
[cc74801e]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
[cc74801e]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    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
278                 "Loading OCSP response from %s",
279                 sc->ocsp_response_file);
280    apr_file_t *file;
281    apr_finfo_t finfo;
282    apr_size_t br = 0;
283    rv = apr_file_open(&file, sc->ocsp_response_file,
284                       APR_READ | APR_BINARY, APR_OS_DEFAULT, tmp);
285    if (rv != APR_SUCCESS)
286    {
287        apr_pool_destroy(tmp);
288        return rv;
289    }
290    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, file);
291    if (rv != APR_SUCCESS)
292    {
293        apr_pool_destroy(tmp);
294        return rv;
295    }
296
297    gnutls_datum_t resp;
298    resp.data = apr_palloc(tmp, finfo.size);
299    rv = apr_file_read_full(file, resp.data, finfo.size, &br);
300    if (rv != APR_SUCCESS)
301    {
302        apr_pool_destroy(tmp);
303        return rv;
304    }
305    apr_file_close(file);
[4bf4ce2]306    /* safe integer type conversion */
307    if (__builtin_add_overflow(br, 0, &resp.size))
308    {
309        apr_pool_destroy(tmp);
310        return APR_EINVAL;
311    }
[6b4136c]312
[366d1a1]313    apr_time_t expiry;
314    if (check_ocsp_response(s, &resp, &expiry) != GNUTLS_E_SUCCESS)
[08817d0]315    {
316        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, s,
317                     "OCSP response validation failed, cannot "
318                     "update cache.");
319        apr_pool_destroy(tmp);
320        return APR_EGENERAL;
321    }
[c6572ec]322    /* If expiry is zero, the response does not contain a nextUpdate
323     * field. Use the default cache timeout. */
[366d1a1]324    if (expiry == 0)
[c6572ec]325        expiry = apr_time_now() + sc->cache_timeout;
[70a1e5a]326    /* Apply grace time otherwise. */
327    else
328        expiry -= sc->ocsp_grace_time;
[366d1a1]329
[a372379]330    int r = sc->cache->store(s, sc->ocsp->fingerprint, resp, expiry);
[6b4136c]331    /* destroy pool, and original copy of the OCSP response with it */
332    apr_pool_destroy(tmp);
333    if (r != 0)
334    {
335        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
336                      "Storing OCSP response in cache failed.");
337        return APR_EGENERAL;
338    }
339    return APR_SUCCESS;
340}
341
342
343
[94cb972]344int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
345                          void *ptr,
346                          gnutls_datum_t *ocsp_response)
347{
348    mgs_handle_t *ctxt = (mgs_handle_t *) ptr;
[e809fb3]349    if (ctxt->sc->cache == NULL)
350    {
351        /* OCSP caching requires a cache. */
352        return GNUTLS_E_NO_CERTIFICATE_STATUS;
353    }
[94cb972]354
[a372379]355    *ocsp_response = ctxt->sc->cache->fetch(ctxt,
356                                            ctxt->sc->ocsp->fingerprint);
[6b4136c]357    if (ocsp_response->size == 0)
[94cb972]358    {
[d18afb8]359        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, ctxt->c,
[6b4136c]360                      "Fetching OCSP response from cache failed.");
[68ce93c]361    }
362    else
363    {
[5559aa6]364        return GNUTLS_E_SUCCESS;
[94cb972]365    }
[368e581]366    /* get rid of invalid response (if any) */
367    gnutls_free(ocsp_response->data);
[08817d0]368    ocsp_response->data = NULL;
[368e581]369
370    /* If the cache had no response or an invalid one, try to update. */
371    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
372                  "No valid OCSP response in cache, trying to update.");
[d6834e0]373
374    apr_status_t rv = apr_global_mutex_trylock(ctxt->sc->ocsp_mutex);
375    if (APR_STATUS_IS_EBUSY(rv))
376    {
377        /* Another thread is currently holding the mutex, wait. */
378        apr_global_mutex_lock(ctxt->sc->ocsp_mutex);
379        /* Check if this other thread updated the response we need. It
380         * would be better to have a vhost specific mutex, but at the
381         * moment there's no good way to integrate that with the
382         * Apache Mutex directive. */
[a372379]383        *ocsp_response = ctxt->sc->cache->fetch(ctxt,
384                                                ctxt->sc->ocsp->fingerprint);
[5559aa6]385        if (ocsp_response->size > 0)
[d6834e0]386        {
387            /* Got a valid response now, unlock mutex and return. */
388            apr_global_mutex_unlock(ctxt->sc->ocsp_mutex);
389            return GNUTLS_E_SUCCESS;
390        }
391        else
392        {
393            gnutls_free(ocsp_response->data);
394            ocsp_response->data = NULL;
395        }
396    }
397
398    rv = mgs_cache_ocsp_response(ctxt->c->base_server);
[368e581]399    if (rv != APR_SUCCESS)
400    {
401        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, ctxt->c,
402                      "Updating OCSP response cache failed");
[d6834e0]403        apr_global_mutex_unlock(ctxt->sc->ocsp_mutex);
[368e581]404        goto fail_cleanup;
405    }
[d6834e0]406    apr_global_mutex_unlock(ctxt->sc->ocsp_mutex);
[368e581]407
408    /* retry reading from cache */
[a372379]409    *ocsp_response = ctxt->sc->cache->fetch(ctxt,
410                                            ctxt->sc->ocsp->fingerprint);
[368e581]411    if (ocsp_response->size == 0)
412    {
413        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
414                      "Fetching OCSP response from cache failed on retry.");
415    }
416    else
417    {
[5559aa6]418        return GNUTLS_E_SUCCESS;
[368e581]419    }
[94cb972]420
[68ce93c]421    /* failure, clean up response data */
[368e581]422 fail_cleanup:
[d35b98e]423    gnutls_free(ocsp_response->data);
424    ocsp_response->size = 0;
425    ocsp_response->data = NULL;
426    return GNUTLS_E_NO_CERTIFICATE_STATUS;
[94cb972]427}
[2a1ffd6]428
429
430
431int mgs_create_ocsp_trust_list(gnutls_x509_trust_list_t *tl,
432                               const gnutls_x509_crt_t *chain,
433                               const int num)
434{
435    int added = 0;
436    int ret = gnutls_x509_trust_list_init(tl, num);
437
438    if (ret == GNUTLS_E_SUCCESS)
439        added = gnutls_x509_trust_list_add_cas(*tl, chain, num, 0);
440
441    if (added != num)
442        ret = GNUTLS_E_CERTIFICATE_ERROR;
443
444    /* Clean up trust list in case of error */
445    if (ret != GNUTLS_E_SUCCESS)
446        gnutls_x509_trust_list_deinit(*tl, 0);
447
448    return ret;
449}
[fad7695]450
451
452
453apr_status_t mgs_cleanup_trust_list(void *data)
454{
455    gnutls_x509_trust_list_t *tl = (gnutls_x509_trust_list_t *) data;
456    gnutls_x509_trust_list_deinit(*tl, 0);
457    return APR_SUCCESS;
458}
459
460
461
[fd6bb19]462apr_uri_t * mgs_cert_get_ocsp_uri(apr_pool_t *p, gnutls_x509_crt_t cert)
463{
464    apr_pool_t *tmp;
465    apr_status_t rv = apr_pool_create(&tmp, p);
466    if (rv != APR_SUCCESS)
467        return NULL;
468
469    apr_uri_t *ocsp_uri = NULL;
470
471    int ret = GNUTLS_E_SUCCESS;
472    /* search authority info access for OCSP URI */
473    for (int seq = 0; ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; seq++)
474    {
475        gnutls_datum_t ocsp_access_data;
476        ret = gnutls_x509_crt_get_authority_info_access(cert, seq,
477                                                        GNUTLS_IA_OCSP_URI,
478                                                        &ocsp_access_data,
479                                                        NULL);
480        if (ret == GNUTLS_E_SUCCESS)
481        {
482            /* create NULL terminated string */
483            char *ocsp_str =
484                apr_pstrndup(tmp, (const char*) ocsp_access_data.data,
485                             ocsp_access_data.size);
486            gnutls_free(ocsp_access_data.data);
487
488            ocsp_uri = apr_palloc(p, sizeof(apr_uri_t));
489            rv = apr_uri_parse(p, ocsp_str, ocsp_uri);
490            if (rv == APR_SUCCESS)
491                break;
492            else
493                ocsp_uri = NULL;
494        }
495    }
496
497    apr_pool_destroy(tmp);
498    return ocsp_uri;
499}
500
501
502
[fad7695]503/*
504 * Like in the general post_config hook the HTTP status codes for
505 * errors are just for fun. What matters is "neither OK nor DECLINED"
506 * to denote an error.
507 */
[fd6bb19]508int mgs_ocsp_post_config_server(apr_pool_t *pconf,
509                                apr_pool_t *ptemp __attribute__((unused)),
510                                server_rec *server)
[fad7695]511{
[fd6bb19]512    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
513        ap_get_module_config(server->module_config, &gnutls_module);
[fad7695]514
515    if (sc->certs_x509_chain_num < 2)
516    {
517        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
518                     "OCSP stapling is enabled but no CA certificate "
[4ae7810]519                     "available for %s:%d, make sure it is included in "
520                     "GnuTLSCertificateFile!",
521                     server->server_hostname, server->addrs->host_port);
[fad7695]522        return HTTP_NOT_FOUND;
523    }
[fd6bb19]524
[cc74801e]525    sc->ocsp = apr_palloc(pconf, sizeof(struct mgs_ocsp_data));
[fd6bb19]526
[a372379]527    sc->ocsp->fingerprint =
528        mgs_get_cert_fingerprint(pconf, sc->certs_x509_crt_chain[0]);
529    if (sc->ocsp->fingerprint.data == NULL)
530        return HTTP_INTERNAL_SERVER_ERROR;
531
[cc74801e]532    sc->ocsp->uri = mgs_cert_get_ocsp_uri(pconf,
533                                          sc->certs_x509_crt_chain[0]);
534
535    sc->ocsp->trust = apr_palloc(pconf,
536                                 sizeof(gnutls_x509_trust_list_t));
[fad7695]537     /* Only the direct issuer may sign the OCSP response or an OCSP
538      * signer. */
[cc74801e]539    int ret = mgs_create_ocsp_trust_list(sc->ocsp->trust,
[fad7695]540                                         &(sc->certs_x509_crt_chain[1]),
541                                         1);
542    if (ret != GNUTLS_E_SUCCESS)
543    {
544        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
545                     "Could not create OCSP trust list: %s (%d)",
546                     gnutls_strerror(ret), ret);
547        return HTTP_INTERNAL_SERVER_ERROR;
548    }
549    /* deinit trust list when the config pool is destroyed */
[cc74801e]550    apr_pool_cleanup_register(pconf, sc->ocsp->trust,
[fad7695]551                              mgs_cleanup_trust_list,
552                              apr_pool_cleanup_null);
553
554    return OK;
555}
Note: See TracBrowser for help on using the repository browser.