source: mod_gnutls/src/gnutls_ocsp.c @ 4bf4ce2

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

Use GCC builtins to catch overflows with mixed integer types

Different libraries (here: GnuTLS and APR) use different integer types
for lengths in their internal data structures. When assigning integer
types of different size to each other, overflows are possible,
although extremely unlikely in this context. The GCC arithmetic
overflow checking builtins provide an easy way to catch overflows
before they can cause trouble. Requires GCC 5 or later.

  • Property mode set to 100644
File size: 15.2 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 */
298    int r = dbm_cache_store(s, fingerprint,
299                            resp, apr_time_now() + apr_time_from_sec(120));
300    /* destroy pool, and original copy of the OCSP response with it */
301    apr_pool_destroy(tmp);
302    if (r != 0)
303    {
304        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
305                      "Storing OCSP response in cache failed.");
306        return APR_EGENERAL;
307    }
308    return APR_SUCCESS;
309}
310
311
312
313int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
314                          void *ptr,
315                          gnutls_datum_t *ocsp_response)
316{
317    mgs_handle_t *ctxt = (mgs_handle_t *) ptr;
318
319    apr_status_t rv = mgs_cache_ocsp_response(ctxt->c->base_server);
320    if (rv != APR_SUCCESS)
321    {
322        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, ctxt->c,
323                      "Updating OCSP response cache failed");
324        return GNUTLS_E_NO_CERTIFICATE_STATUS;
325    }
326
327    gnutls_datum_t fingerprint =
328        mgs_get_cert_fingerprint(ctxt->c->pool,
329                                 ctxt->sc->certs_x509_crt_chain[0]);
330    if (fingerprint.data == NULL)
331        return GNUTLS_E_NO_CERTIFICATE_STATUS;
332
333    *ocsp_response = dbm_cache_fetch(ctxt, fingerprint);
334    if (ocsp_response->size == 0)
335    {
336        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
337                      "Fetching OCSP response from cache failed.");
338    }
339    else
340    {
341        /* Succeed if response is present and valid. */
342        if (check_ocsp_response(ctxt, ocsp_response) == GNUTLS_E_SUCCESS)
343            return GNUTLS_E_SUCCESS;
344    }
345
346    /* failure, clean up response data */
347    gnutls_free(ocsp_response->data);
348    ocsp_response->size = 0;
349    ocsp_response->data = NULL;
350    return GNUTLS_E_NO_CERTIFICATE_STATUS;
351}
352
353
354
355int mgs_create_ocsp_trust_list(gnutls_x509_trust_list_t *tl,
356                               const gnutls_x509_crt_t *chain,
357                               const int num)
358{
359    int added = 0;
360    int ret = gnutls_x509_trust_list_init(tl, num);
361
362    if (ret == GNUTLS_E_SUCCESS)
363        added = gnutls_x509_trust_list_add_cas(*tl, chain, num, 0);
364
365    if (added != num)
366        ret = GNUTLS_E_CERTIFICATE_ERROR;
367
368    /* Clean up trust list in case of error */
369    if (ret != GNUTLS_E_SUCCESS)
370        gnutls_x509_trust_list_deinit(*tl, 0);
371
372    return ret;
373}
374
375
376
377apr_status_t mgs_cleanup_trust_list(void *data)
378{
379    gnutls_x509_trust_list_t *tl = (gnutls_x509_trust_list_t *) data;
380    gnutls_x509_trust_list_deinit(*tl, 0);
381    return APR_SUCCESS;
382}
383
384
385
386apr_uri_t * mgs_cert_get_ocsp_uri(apr_pool_t *p, gnutls_x509_crt_t cert)
387{
388    apr_pool_t *tmp;
389    apr_status_t rv = apr_pool_create(&tmp, p);
390    if (rv != APR_SUCCESS)
391        return NULL;
392
393    apr_uri_t *ocsp_uri = NULL;
394
395    int ret = GNUTLS_E_SUCCESS;
396    /* search authority info access for OCSP URI */
397    for (int seq = 0; ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; seq++)
398    {
399        gnutls_datum_t ocsp_access_data;
400        ret = gnutls_x509_crt_get_authority_info_access(cert, seq,
401                                                        GNUTLS_IA_OCSP_URI,
402                                                        &ocsp_access_data,
403                                                        NULL);
404        if (ret == GNUTLS_E_SUCCESS)
405        {
406            /* create NULL terminated string */
407            char *ocsp_str =
408                apr_pstrndup(tmp, (const char*) ocsp_access_data.data,
409                             ocsp_access_data.size);
410            gnutls_free(ocsp_access_data.data);
411
412            ocsp_uri = apr_palloc(p, sizeof(apr_uri_t));
413            rv = apr_uri_parse(p, ocsp_str, ocsp_uri);
414            if (rv == APR_SUCCESS)
415                break;
416            else
417                ocsp_uri = NULL;
418        }
419    }
420
421    apr_pool_destroy(tmp);
422    return ocsp_uri;
423}
424
425
426
427/*
428 * Like in the general post_config hook the HTTP status codes for
429 * errors are just for fun. What matters is "neither OK nor DECLINED"
430 * to denote an error.
431 */
432int mgs_ocsp_post_config_server(apr_pool_t *pconf,
433                                apr_pool_t *ptemp __attribute__((unused)),
434                                server_rec *server)
435{
436    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
437        ap_get_module_config(server->module_config, &gnutls_module);
438
439    if (sc->certs_x509_chain_num < 2)
440    {
441        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
442                     "OCSP stapling is enabled but no CA certificate "
443                     "available for %s:%d, make sure it is included in "
444                     "GnuTLSCertificateFile!",
445                     server->server_hostname, server->addrs->host_port);
446        return HTTP_NOT_FOUND;
447    }
448
449    sc->ocsp_uri = mgs_cert_get_ocsp_uri(pconf, sc->certs_x509_crt_chain[0]);
450
451    sc->ocsp_trust = apr_palloc(pconf,
452                                sizeof(gnutls_x509_trust_list_t));
453     /* Only the direct issuer may sign the OCSP response or an OCSP
454      * signer. */
455    int ret = mgs_create_ocsp_trust_list(sc->ocsp_trust,
456                                         &(sc->certs_x509_crt_chain[1]),
457                                         1);
458    if (ret != GNUTLS_E_SUCCESS)
459    {
460        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
461                     "Could not create OCSP trust list: %s (%d)",
462                     gnutls_strerror(ret), ret);
463        return HTTP_INTERNAL_SERVER_ERROR;
464    }
465    /* deinit trust list when the config pool is destroyed */
466    apr_pool_cleanup_register(pconf, sc->ocsp_trust,
467                              mgs_cleanup_trust_list,
468                              apr_pool_cleanup_null);
469
470    return OK;
471}
Note: See TracBrowser for help on using the repository browser.