source: mod_gnutls/src/gnutls_ocsp.c @ 16ad0eb

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

Perform OCSP request over HTTP

Finally the whole stack is there! Quite a bit of polishing left to do
(especially regarding timing/timeouts), but sending a request and
caching the response work.

  • Property mode set to 100644
File size: 26.5 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#include "gnutls_util.h"
21
22#include <apr_escape.h>
23#include <apr_lib.h>
24#include <apr_time.h>
25#include <gnutls/ocsp.h>
26#include <time.h>
27
28#ifdef APLOG_USE_MODULE
29APLOG_USE_MODULE(gnutls);
30#endif
31
32/* maximum supported OCSP response size, 8K should be plenty */
33#define OCSP_RESP_SIZE_MAX (8 * 1024)
34#define OCSP_REQ_TYPE "application/ocsp-request"
35#define OCSP_RESP_TYPE "application/ocsp-response"
36
37
38
39#define _log_one_ocsp_fail(str, srv)                                    \
40    ap_log_error(APLOG_MARK, APLOG_INFO, APR_EGENERAL, (srv),           \
41                 "Reason for failed OCSP response verification: %s", (str))
42/*
43 * Log all matching reasons for verification failure
44 */
45static void _log_verify_fail_reason(const unsigned int verify, server_rec *s)
46{
47    if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
48        _log_one_ocsp_fail("Signer cert not found", s);
49
50    if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
51        _log_one_ocsp_fail("Signer cert keyusage error", s);
52
53    if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
54        _log_one_ocsp_fail("Signer cert is not trusted", s);
55
56    if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
57        _log_one_ocsp_fail("Insecure algorithm", s);
58
59    if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
60        _log_one_ocsp_fail("Signature failure", s);
61
62    if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
63        _log_one_ocsp_fail("Signer cert not yet activated", s);
64
65    if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
66        _log_one_ocsp_fail("Signer cert expired", s);
67}
68
69
70
71const char *mgs_store_ocsp_response_path(cmd_parms *parms,
72                                         void *dummy __attribute__((unused)),
73                                         const char *arg)
74{
75    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
76        ap_get_module_config(parms->server->module_config, &gnutls_module);
77
78    sc->ocsp_response_file = ap_server_root_relative(parms->pool, arg);
79    return NULL;
80}
81
82
83
84/**
85 * Create an OCSP request for the certificate of the given server. The
86 * DER encoded request is stored in 'req' (must be released with
87 * gnutls_free() when no longer needed), its nonce in 'nonce' (same,
88 * if not NULL).
89 *
90 * Returns GNUTLS_E_SUCCESS, or a GnuTLS error code.
91 */
92static int mgs_create_ocsp_request(server_rec *s, gnutls_datum_t *req,
93                            gnutls_datum_t *nonce)
94    __attribute__((nonnull(1, 2)));
95static int mgs_create_ocsp_request(server_rec *s, gnutls_datum_t *req,
96                            gnutls_datum_t *nonce)
97{
98    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
99        ap_get_module_config(s->module_config, &gnutls_module);
100
101    gnutls_ocsp_req_t r;
102    int ret = gnutls_ocsp_req_init(&r);
103    if (ret != GNUTLS_E_SUCCESS)
104    {
105        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
106                     "Could not initialize OCSP request structure: %s (%d)",
107                     gnutls_strerror(ret), ret);
108        return ret;
109    }
110
111    /* GnuTLS doc says that the digest is "normally"
112     * GNUTLS_DIG_SHA1. */
113    ret = gnutls_ocsp_req_add_cert(r, GNUTLS_DIG_SHA256,
114                                   sc->certs_x509_crt_chain[1],
115                                   sc->certs_x509_crt_chain[0]);
116
117    if (ret != GNUTLS_E_SUCCESS)
118    {
119        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
120                     "Adding certificate to OCSP request for %s:%d "
121                     "failed: %s (%d)",
122                     s->server_hostname, s->addrs->host_port,
123                     gnutls_strerror(ret), ret);
124        gnutls_ocsp_req_deinit(r);
125        return ret;
126    }
127
128    ret = gnutls_ocsp_req_randomize_nonce(r);
129    if (ret != GNUTLS_E_SUCCESS)
130    {
131        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
132                     "OCSP nonce creation failed: %s (%d)",
133                     gnutls_strerror(ret), ret);
134        gnutls_ocsp_req_deinit(r);
135        return ret;
136    }
137
138    if (nonce != NULL)
139    {
140        ret = gnutls_ocsp_req_get_nonce(r, NULL, nonce);
141        if (ret != GNUTLS_E_SUCCESS)
142        {
143            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
144                         "Could not get nonce: %s (%d)",
145                         gnutls_strerror(ret), ret);
146            gnutls_free(nonce->data);
147            nonce->data = NULL;
148            nonce->size = 0;
149            gnutls_ocsp_req_deinit(r);
150            return ret;
151        }
152    }
153
154    ret = gnutls_ocsp_req_export(r, req);
155    if (ret != GNUTLS_E_SUCCESS)
156    {
157        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
158                     "OCSP request export failed: %s (%d)",
159                     gnutls_strerror(ret), ret);
160        gnutls_free(req->data);
161        req->data = NULL;
162        req->size = 0;
163        if (nonce != NULL)
164        {
165            gnutls_free(nonce->data);
166            nonce->data = NULL;
167            nonce->size = 0;
168        }
169        gnutls_ocsp_req_deinit(r);
170        return ret;
171    }
172
173    gnutls_ocsp_req_deinit(r);
174    return ret;
175}
176
177
178
179/**
180 * Check if the provided OCSP response is usable for stapling in
181 * connections to this server. Returns GNUTLS_E_SUCCESS if yes.
182 *
183 * Supports only one certificate status per response.
184 *
185 * If expiry is not NULL, it will be set to the nextUpdate time
186 * contained in the response, or zero if the response does not contain
187 * a nextUpdate field.
188 */
189int check_ocsp_response(server_rec *s, const gnutls_datum_t *ocsp_response,
190                        apr_time_t* expiry)
191{
192    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
193        ap_get_module_config(s->module_config, &gnutls_module);
194
195    if (sc->ocsp->trust == NULL)
196    {
197        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
198                     "No OCSP trust list available for server \"%s\"!",
199                     s->server_hostname);
200        return GNUTLS_E_NO_CERTIFICATE_FOUND;
201    }
202
203    gnutls_ocsp_resp_t resp;
204    int ret = gnutls_ocsp_resp_init(&resp);
205    if (ret != GNUTLS_E_SUCCESS)
206    {
207        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
208                     "Could not initialize OCSP response structure: %s (%d)",
209                     gnutls_strerror(ret), ret);
210        goto resp_cleanup;
211    }
212    ret = gnutls_ocsp_resp_import(resp, ocsp_response);
213    if (ret != GNUTLS_E_SUCCESS)
214    {
215        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
216                     "Importing OCSP response failed: %s (%d)",
217                     gnutls_strerror(ret), ret);
218        goto resp_cleanup;
219    }
220
221    ret = gnutls_ocsp_resp_check_crt(resp, 0, sc->certs_x509_crt_chain[0]);
222    if (ret != GNUTLS_E_SUCCESS)
223    {
224        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
225                     "OCSP response is not for server certificate: %s (%d)",
226                     gnutls_strerror(ret), ret);
227        goto resp_cleanup;
228    }
229
230    unsigned int verify;
231    ret = gnutls_ocsp_resp_verify(resp, *(sc->ocsp->trust), &verify, 0);
232    if (ret != GNUTLS_E_SUCCESS)
233    {
234        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
235                     "OCSP response verification failed: %s (%d)",
236                     gnutls_strerror(ret), ret);
237        goto resp_cleanup;
238    }
239    else
240    {
241        /* verification worked, check the result */
242        if (verify != 0)
243        {
244            _log_verify_fail_reason(verify, s);
245            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
246            goto resp_cleanup;
247        }
248        else
249            ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
250                         "OCSP response is valid.");
251    }
252
253    /* OK, response is for our certificate and valid, let's get the
254     * actual response data. */
255    unsigned int cert_status;
256    time_t this_update;
257    time_t next_update;
258    ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
259                                      &cert_status, &this_update,
260                                      &next_update, NULL, NULL);
261    if (ret != GNUTLS_E_SUCCESS)
262    {
263        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
264                     "Could not get OCSP response data: %s (%d)",
265                     gnutls_strerror(ret), ret);
266        goto resp_cleanup;
267    }
268
269    apr_time_t now = apr_time_now();
270    apr_time_t valid_at;
271    apr_time_ansi_put(&valid_at, this_update);
272    /* Buffer for human-readable times produced by apr_rfc822_date,
273     * see apr_time.h */
274    char date_str[APR_RFC822_DATE_LEN];
275    apr_rfc822_date(date_str, valid_at);
276
277    if (now < valid_at)
278    {
279        /* We don't know if our clock or that of the OCSP responder is
280         * out of sync, so warn but continue. */
281        ap_log_error(APLOG_MARK, APLOG_WARNING, APR_EGENERAL, s,
282                     "OSCP response claims to be from future (%s), check "
283                     "time synchronization!", date_str);
284    }
285
286    if (next_update == (time_t) -1)
287    {
288        ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s,
289                     "OSCP response does not contain nextUpdate info.");
290        if (expiry != NULL)
291            *expiry = 0;
292    }
293    else
294    {
295        apr_time_t valid_to;
296        apr_time_ansi_put(&valid_to, next_update);
297        if (expiry != NULL)
298            *expiry = valid_to;
299        if (now > valid_to)
300        {
301            apr_rfc822_date(date_str, valid_to);
302            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
303                         "OCSP response has expired at %s!", date_str);
304            /* Do not send a stale response */
305            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
306            goto resp_cleanup;
307        }
308    }
309
310    /* What's the actual status? Will be one of
311     * gnutls_ocsp_cert_status_t as defined in gnutls/ocsp.h. */
312    if (cert_status == GNUTLS_OCSP_CERT_GOOD)
313    {
314        /* Yay, everything's good! */
315        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
316                     "CA flagged certificate as valid at %s.", date_str);
317    }
318    else
319    {
320        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
321                     "CA flagged certificate as %s at %s.",
322                     cert_status == GNUTLS_OCSP_CERT_REVOKED ?
323                     "revoked" : "unknown", date_str);
324        ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
325    }
326
327 resp_cleanup:
328    gnutls_ocsp_resp_deinit(resp);
329    return ret;
330}
331
332
333
334/*
335 * Returns the certificate fingerprint, memory is allocated from p.
336 */
337static gnutls_datum_t mgs_get_cert_fingerprint(apr_pool_t *p,
338                                               gnutls_x509_crt_t cert)
339{
340    gnutls_datum_t fingerprint = {NULL, 0};
341    size_t fplen;
342    gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, NULL, &fplen);
343    unsigned char * fp = apr_palloc(p, fplen);
344    gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, fp, &fplen);
345    /* Safe integer type conversion: The types of fingerprint.size
346     * (unsigned int) and fplen (size_t) may have different
347     * lengths. */
348    if (__builtin_add_overflow(fplen, 0, &fingerprint.size))
349        fingerprint.size = 0;
350    else
351        fingerprint.data = fp;
352    return fingerprint;
353}
354
355
356
357static apr_status_t do_ocsp_request(apr_pool_t *p, server_rec *s,
358                                    gnutls_datum_t *request,
359                                    gnutls_datum_t *response)
360    __attribute__((nonnull));
361static apr_status_t do_ocsp_request(apr_pool_t *p, server_rec *s,
362                                    gnutls_datum_t *request,
363                                    gnutls_datum_t *response)
364{
365    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
366        ap_get_module_config(s->module_config, &gnutls_module);
367
368    if (apr_strnatcmp(sc->ocsp->uri->scheme, "http"))
369    {
370        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
371                     "Scheme \"%s\" is not supported for OCSP requests!",
372                     sc->ocsp->uri->scheme);
373        return APR_EINVAL;
374    }
375
376    const char* header = http_post_header(p, sc->ocsp->uri,
377                                          OCSP_REQ_TYPE, OCSP_RESP_TYPE,
378                                          request->size);
379    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
380                 "OCSP POST header: %s", header);
381
382    /* Find correct port */
383    apr_port_t port = sc->ocsp->uri->port ?
384        sc->ocsp->uri->port : apr_uri_port_of_scheme(sc->ocsp->uri->scheme);
385
386    apr_sockaddr_t *sa;
387    apr_status_t rv = apr_sockaddr_info_get(&sa, sc->ocsp->uri->hostname,
388                                            APR_UNSPEC, port, 0, p);
389    if (rv != APR_SUCCESS)
390    {
391        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
392                     "Address resolution for OCSP responder %s failed.",
393                     sc->ocsp->uri->hostinfo);
394    }
395
396    /* There may be multiple answers, try them in order until one
397     * works. */
398    apr_socket_t *sock;
399    /* TODO: configurable timeout */
400    apr_interval_time_t timeout = apr_time_from_sec(2);
401    while (sa)
402    {
403        rv = apr_socket_create(&sock, sa->family, SOCK_STREAM,
404                               APR_PROTO_TCP, p);
405        if (rv == APR_SUCCESS)
406        {
407            apr_socket_timeout_set(sock, timeout);
408            rv = apr_socket_connect(sock, sa);
409            if (rv == APR_SUCCESS)
410                /* Connected! */
411                break;
412            apr_socket_close(sock);
413        }
414        sa = sa->next;
415    }
416    /* If the socket is connected, 'sa' points at the matching
417     * address. */
418    if (sa == NULL)
419    {
420        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
421                     "Connecting to OCSP responder %s failed.",
422                     sc->ocsp->uri->hostinfo);
423        return rv;
424    }
425
426    /* Header is generated locally, so strlen() is safe. */
427    rv = sock_send_buf(sock, header, strlen(header));
428    if (rv == APR_SUCCESS)
429        rv = sock_send_buf(sock, (char*) request->data, request->size);
430    /* catches errors from both header and request */
431    if (rv != APR_SUCCESS)
432    {
433        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
434                     "Sending OCSP request failed.");
435        goto exit;
436    }
437
438    /* Prepare bucket brigades to read the response header. BBs make
439     * it easy to split the header into lines. */
440    apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p);
441    apr_bucket_brigade *bb = apr_brigade_create(p, ba);
442    /* will carry split response headers */
443    apr_bucket_brigade *rh = apr_brigade_create(p, ba);
444
445    APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_socket_create(sock, ba));
446    /* The first line in the response header must be the status, check
447     * for OK status code. Line looks similar to "HTTP/1.0 200 OK". */
448    const char *h = read_line(p, bb, rh);
449    const char *code = 0;
450    if (h == NULL
451        || strncmp(h, "HTTP/", 5)
452        || (code = ap_strchr(h, ' ')) == NULL
453        || apr_atoi64(code + 1) != HTTP_OK)
454    {
455        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
456                     "Invalid HTTP response status from %s: %s",
457                     sc->ocsp->uri->hostinfo, h);
458        rv = APR_ECONNRESET;
459        goto exit;
460    }
461    /* Read remaining header lines */
462    for (h = read_line(p, bb, rh); h != NULL && apr_strnatcmp(h, "") != 0;
463         h = read_line(p, bb, rh))
464    {
465        ap_log_error(APLOG_MARK, APLOG_TRACE2, APR_SUCCESS, s,
466                     "Received header: %s", h);
467    }
468    /* The last header line should be empty (""), NULL indicates an
469     * error. */
470    if (h == NULL)
471    {
472        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
473                     "Error while reading HTTP response header from %s",
474                     sc->ocsp->uri->hostinfo);
475        rv = APR_ECONNRESET;
476        goto exit;
477    }
478
479    /* Headers have been consumed, the rest of the available data
480     * should be the actual response. */
481    apr_size_t len = OCSP_RESP_SIZE_MAX;
482    char buf[OCSP_RESP_SIZE_MAX];
483    /* apr_brigade_pflatten() can allocate directly from the pool, but
484     * the documentation does not describe a way to limit the size of
485     * the buffer, which is necessary here to prevent DoS by endless
486     * response. Use apr_brigade_flatten() to read to a stack pool,
487     * then create a copy to return. */
488    rv = apr_brigade_flatten(bb, buf, &len);
489    if (rv != APR_SUCCESS)
490    {
491        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
492                     "Failed to read OCSP response.");
493        goto exit;
494    }
495
496    /* With the length restriction this really should not happen. */
497    if (__builtin_add_overflow(len, 0, &response->size))
498    {
499        response->data = NULL;
500        rv = APR_ENOMEM;
501    }
502    else
503    {
504        response->data = apr_pmemdup(p, buf, len);
505    }
506
507 exit:
508    apr_socket_close(sock);
509    return rv;
510}
511
512
513
514apr_status_t mgs_cache_ocsp_response(server_rec *s)
515{
516    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
517        ap_get_module_config(s->module_config, &gnutls_module);
518
519    if (sc->cache == NULL)
520    {
521        /* OCSP caching requires a cache. */
522        return APR_ENOTIMPL;
523    }
524
525    apr_pool_t *tmp;
526    apr_status_t rv = apr_pool_create(&tmp, NULL);
527    if (rv != APR_SUCCESS)
528    {
529        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
530                     "could not create temporary pool for %s",
531                     __func__);
532        return rv;
533    }
534
535    gnutls_datum_t req;
536    int ret = mgs_create_ocsp_request(s, &req, NULL);
537    if (ret == GNUTLS_E_SUCCESS)
538    {
539        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, s,
540                     "created OCSP request for %s:%d: %s",
541                     s->server_hostname, s->addrs->host_port,
542                     apr_pescape_hex(tmp, req.data, req.size, 0));
543        gnutls_free(req.data);
544    }
545
546    gnutls_datum_t resp;
547    rv = do_ocsp_request(tmp, s, &req, &resp);
548    if (rv != APR_SUCCESS)
549    {
550        /* do_ocsp_request() does its own error logging. */
551        apr_pool_destroy(tmp);
552        return rv;
553    }
554    /* TODO: check nonce */
555
556    /* TODO: separate option to enable/disable OCSP stapling, restore
557     * reading response from file for debugging/expert use. */
558
559    apr_time_t expiry;
560    if (check_ocsp_response(s, &resp, &expiry) != GNUTLS_E_SUCCESS)
561    {
562        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, s,
563                     "OCSP response validation failed, cannot "
564                     "update cache.");
565        apr_pool_destroy(tmp);
566        return APR_EGENERAL;
567    }
568    /* If expiry is zero, the response does not contain a nextUpdate
569     * field. Use the default cache timeout. */
570    if (expiry == 0)
571        expiry = apr_time_now() + sc->cache_timeout;
572    /* Apply grace time otherwise. */
573    else
574        expiry -= sc->ocsp_grace_time;
575
576    int r = sc->cache->store(s, sc->ocsp->fingerprint, resp, expiry);
577    /* destroy pool, and original copy of the OCSP response with it */
578    apr_pool_destroy(tmp);
579    if (r != 0)
580    {
581        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
582                      "Storing OCSP response in cache failed.");
583        return APR_EGENERAL;
584    }
585    return APR_SUCCESS;
586}
587
588
589
590int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
591                          void *ptr,
592                          gnutls_datum_t *ocsp_response)
593{
594    mgs_handle_t *ctxt = (mgs_handle_t *) ptr;
595    if (ctxt->sc->cache == NULL)
596    {
597        /* OCSP caching requires a cache. */
598        return GNUTLS_E_NO_CERTIFICATE_STATUS;
599    }
600
601    *ocsp_response = ctxt->sc->cache->fetch(ctxt,
602                                            ctxt->sc->ocsp->fingerprint);
603    if (ocsp_response->size == 0)
604    {
605        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, ctxt->c,
606                      "Fetching OCSP response from cache failed.");
607    }
608    else
609    {
610        return GNUTLS_E_SUCCESS;
611    }
612    /* get rid of invalid response (if any) */
613    gnutls_free(ocsp_response->data);
614    ocsp_response->data = NULL;
615
616    /* If the cache had no response or an invalid one, try to update. */
617    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
618                  "No valid OCSP response in cache, trying to update.");
619
620    /* TODO: Once sending OCSP requests is implemented we need a rate
621     * limit for retries on error. If the responder is overloaded or
622     * buggy we don't want to add too much more load, and if a MITM is
623     * messing with requests a repetition loop might end up being a
624     * self-inflicted denial of service. */
625    apr_status_t rv = apr_global_mutex_trylock(ctxt->sc->ocsp_mutex);
626    if (APR_STATUS_IS_EBUSY(rv))
627    {
628        /* Another thread is currently holding the mutex, wait. */
629        apr_global_mutex_lock(ctxt->sc->ocsp_mutex);
630        /* Check if this other thread updated the response we need. It
631         * would be better to have a vhost specific mutex, but at the
632         * moment there's no good way to integrate that with the
633         * Apache Mutex directive. */
634        *ocsp_response = ctxt->sc->cache->fetch(ctxt,
635                                                ctxt->sc->ocsp->fingerprint);
636        if (ocsp_response->size > 0)
637        {
638            /* Got a valid response now, unlock mutex and return. */
639            apr_global_mutex_unlock(ctxt->sc->ocsp_mutex);
640            return GNUTLS_E_SUCCESS;
641        }
642        else
643        {
644            gnutls_free(ocsp_response->data);
645            ocsp_response->data = NULL;
646        }
647    }
648
649    rv = mgs_cache_ocsp_response(ctxt->c->base_server);
650    if (rv != APR_SUCCESS)
651    {
652        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, ctxt->c,
653                      "Updating OCSP response cache failed");
654        apr_global_mutex_unlock(ctxt->sc->ocsp_mutex);
655        goto fail_cleanup;
656    }
657    apr_global_mutex_unlock(ctxt->sc->ocsp_mutex);
658
659    /* retry reading from cache */
660    *ocsp_response = ctxt->sc->cache->fetch(ctxt,
661                                            ctxt->sc->ocsp->fingerprint);
662    if (ocsp_response->size == 0)
663    {
664        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
665                      "Fetching OCSP response from cache failed on retry.");
666    }
667    else
668    {
669        return GNUTLS_E_SUCCESS;
670    }
671
672    /* failure, clean up response data */
673 fail_cleanup:
674    gnutls_free(ocsp_response->data);
675    ocsp_response->size = 0;
676    ocsp_response->data = NULL;
677    return GNUTLS_E_NO_CERTIFICATE_STATUS;
678}
679
680
681
682int mgs_create_ocsp_trust_list(gnutls_x509_trust_list_t *tl,
683                               const gnutls_x509_crt_t *chain,
684                               const int num)
685{
686    int added = 0;
687    int ret = gnutls_x509_trust_list_init(tl, num);
688
689    if (ret == GNUTLS_E_SUCCESS)
690        added = gnutls_x509_trust_list_add_cas(*tl, chain, num, 0);
691
692    if (added != num)
693        ret = GNUTLS_E_CERTIFICATE_ERROR;
694
695    /* Clean up trust list in case of error */
696    if (ret != GNUTLS_E_SUCCESS)
697        gnutls_x509_trust_list_deinit(*tl, 0);
698
699    return ret;
700}
701
702
703
704apr_status_t mgs_cleanup_trust_list(void *data)
705{
706    gnutls_x509_trust_list_t *tl = (gnutls_x509_trust_list_t *) data;
707    gnutls_x509_trust_list_deinit(*tl, 0);
708    return APR_SUCCESS;
709}
710
711
712
713apr_uri_t * mgs_cert_get_ocsp_uri(apr_pool_t *p, gnutls_x509_crt_t cert)
714{
715    apr_pool_t *tmp;
716    apr_status_t rv = apr_pool_create(&tmp, p);
717    if (rv != APR_SUCCESS)
718        return NULL;
719
720    apr_uri_t *ocsp_uri = NULL;
721
722    int ret = GNUTLS_E_SUCCESS;
723    /* search authority info access for OCSP URI */
724    for (int seq = 0; ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; seq++)
725    {
726        gnutls_datum_t ocsp_access_data;
727        ret = gnutls_x509_crt_get_authority_info_access(cert, seq,
728                                                        GNUTLS_IA_OCSP_URI,
729                                                        &ocsp_access_data,
730                                                        NULL);
731        if (ret == GNUTLS_E_SUCCESS)
732        {
733            /* create NULL terminated string */
734            char *ocsp_str =
735                apr_pstrndup(tmp, (const char*) ocsp_access_data.data,
736                             ocsp_access_data.size);
737            gnutls_free(ocsp_access_data.data);
738
739            ocsp_uri = apr_palloc(p, sizeof(apr_uri_t));
740            rv = apr_uri_parse(p, ocsp_str, ocsp_uri);
741            if (rv == APR_SUCCESS)
742                break;
743            else
744                ocsp_uri = NULL;
745        }
746    }
747
748    apr_pool_destroy(tmp);
749    return ocsp_uri;
750}
751
752
753
754/*
755 * Like in the general post_config hook the HTTP status codes for
756 * errors are just for fun. What matters is "neither OK nor DECLINED"
757 * to denote an error.
758 */
759int mgs_ocsp_post_config_server(apr_pool_t *pconf,
760                                apr_pool_t *ptemp __attribute__((unused)),
761                                server_rec *server)
762{
763    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
764        ap_get_module_config(server->module_config, &gnutls_module);
765
766    if (sc->certs_x509_chain_num < 2)
767    {
768        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
769                     "OCSP stapling is enabled but no CA certificate "
770                     "available for %s:%d, make sure it is included in "
771                     "GnuTLSCertificateFile!",
772                     server->server_hostname, server->addrs->host_port);
773        return HTTP_NOT_FOUND;
774    }
775
776    sc->ocsp = apr_palloc(pconf, sizeof(struct mgs_ocsp_data));
777
778    sc->ocsp->fingerprint =
779        mgs_get_cert_fingerprint(pconf, sc->certs_x509_crt_chain[0]);
780    if (sc->ocsp->fingerprint.data == NULL)
781        return HTTP_INTERNAL_SERVER_ERROR;
782
783    sc->ocsp->uri = mgs_cert_get_ocsp_uri(pconf,
784                                          sc->certs_x509_crt_chain[0]);
785
786    sc->ocsp->trust = apr_palloc(pconf,
787                                 sizeof(gnutls_x509_trust_list_t));
788     /* Only the direct issuer may sign the OCSP response or an OCSP
789      * signer. */
790    int ret = mgs_create_ocsp_trust_list(sc->ocsp->trust,
791                                         &(sc->certs_x509_crt_chain[1]),
792                                         1);
793    if (ret != GNUTLS_E_SUCCESS)
794    {
795        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
796                     "Could not create OCSP trust list: %s (%d)",
797                     gnutls_strerror(ret), ret);
798        return HTTP_INTERNAL_SERVER_ERROR;
799    }
800    /* deinit trust list when the config pool is destroyed */
801    apr_pool_cleanup_register(pconf, sc->ocsp->trust,
802                              mgs_cleanup_trust_list,
803                              apr_pool_cleanup_null);
804
805    return OK;
806}
Note: See TracBrowser for help on using the repository browser.