source: mod_gnutls/src/gnutls_ocsp.c @ fd6bb19

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

Extract OCSP access URI from the server certificate

  • Property mode set to 100644
File size: 12.1 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
20#include <apr_lib.h>
21#include <apr_time.h>
22#include <gnutls/ocsp.h>
23#include <time.h>
24
25#ifdef APLOG_USE_MODULE
26APLOG_USE_MODULE(gnutls);
27#endif
28
29
30
31#define _log_one_ocsp_fail(s, c)                                      \
32    ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_EGENERAL, (c),           \
33                  "Reason for failed OCSP response verification: %s", (s))
34/*
35 * Log all matching reasons for verification failure
36 */
37static void _log_verify_fail_reason(const unsigned int verify, conn_rec *c)
38{
39    if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
40        _log_one_ocsp_fail("Signer cert not found", c);
41
42    if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
43        _log_one_ocsp_fail("Signer cert keyusage error", c);
44
45    if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
46        _log_one_ocsp_fail("Signer cert is not trusted", c);
47
48    if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
49        _log_one_ocsp_fail("Insecure algorithm", c);
50
51    if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
52        _log_one_ocsp_fail("Signature failure", c);
53
54    if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
55        _log_one_ocsp_fail("Signer cert not yet activated", c);
56
57    if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
58        _log_one_ocsp_fail("Signer cert expired", c);
59}
60
61
62
63const char *mgs_store_ocsp_response_path(cmd_parms *parms,
64                                         void *dummy __attribute__((unused)),
65                                         const char *arg)
66{
67    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
68        ap_get_module_config(parms->server->module_config, &gnutls_module);
69
70    sc->ocsp_response_file = ap_server_root_relative(parms->pool, arg);
71    return NULL;
72}
73
74
75
76/**
77 * Check if the provided OCSP response is usable for stapling in this
78 * connection context. Returns GNUTLS_E_SUCCESS if yes.
79 */
80int check_ocsp_response(mgs_handle_t *ctxt, const gnutls_datum_t *ocsp_response)
81{
82    if (ctxt->sc->ocsp_trust == NULL)
83    {
84        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
85                      "No OCSP trust list available for server \"%s\"!",
86                      ctxt->c->base_server->server_hostname);
87        return GNUTLS_E_NO_CERTIFICATE_FOUND;
88    }
89
90    gnutls_ocsp_resp_t resp;
91    int ret = gnutls_ocsp_resp_init(&resp);
92    if (ret != GNUTLS_E_SUCCESS)
93    {
94        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
95                      "Could not initialize OCSP response structure: %s (%d)",
96                      gnutls_strerror(ret), ret);
97        goto resp_cleanup;
98    }
99    ret = gnutls_ocsp_resp_import(resp, ocsp_response);
100    if (ret != GNUTLS_E_SUCCESS)
101    {
102        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
103                      "Importing OCSP response failed: %s (%d)",
104                      gnutls_strerror(ret), ret);
105        goto resp_cleanup;
106    }
107
108    ret = gnutls_ocsp_resp_check_crt(resp, 0,
109                                     ctxt->sc->certs_x509_crt_chain[0]);
110    if (ret != GNUTLS_E_SUCCESS)
111    {
112        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
113                      "OCSP response is not for server certificate: %s (%d)",
114                      gnutls_strerror(ret), ret);
115        goto resp_cleanup;
116    }
117
118    unsigned int verify;
119    ret = gnutls_ocsp_resp_verify(resp, *(ctxt->sc->ocsp_trust), &verify, 0);
120    if (ret != GNUTLS_E_SUCCESS)
121    {
122        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
123                      "OCSP response verification failed: %s (%d)",
124                      gnutls_strerror(ret), ret);
125        goto resp_cleanup;
126    }
127    else
128    {
129        /* verification worked, check the result */
130        if (verify != 0)
131        {
132            _log_verify_fail_reason(verify, ctxt->c);
133            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
134            goto resp_cleanup;
135        }
136        else
137            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
138                          "OCSP response is valid.");
139    }
140
141    /* OK, response is for our certificate and valid, let's get the
142     * actual response data. */
143    unsigned int cert_status;
144    time_t this_update;
145    time_t next_update;
146    ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
147                                      &cert_status, &this_update,
148                                      &next_update, NULL, NULL);
149    if (ret != GNUTLS_E_SUCCESS)
150    {
151        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
152                      "Could not get OCSP response data: %s (%d)",
153                      gnutls_strerror(ret), ret);
154        goto resp_cleanup;
155    }
156
157    apr_time_t now = apr_time_now();
158    apr_time_t valid_at;
159    apr_time_ansi_put(&valid_at, this_update);
160    /* Buffer for human-readable times produced by apr_rfc822_date,
161     * see apr_time.h */
162    char date_str[APR_RFC822_DATE_LEN];
163    apr_rfc822_date(date_str, valid_at);
164
165    if (now < valid_at)
166    {
167        /* We don't know if our clock or that of the OCSP responder is
168         * out of sync, so warn but continue. */
169        ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EGENERAL, ctxt->c,
170                      "OSCP response claims to be from future (%s), check "
171                      "time synchronization!", date_str);
172    }
173
174    if (next_update == (time_t) -1)
175        ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ctxt->c,
176                      "OSCP response does not contain nextUpdate info.");
177    else
178    {
179        apr_time_t valid_to;
180        apr_time_ansi_put(&valid_to, next_update);
181        if (now > valid_to)
182        {
183            apr_rfc822_date(date_str, valid_to);
184            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
185                          "OCSP response has expired at %s!", date_str);
186            /* Do not send a stale response */
187            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
188            goto resp_cleanup;
189        }
190    }
191
192    /* What's the actual status? Will be one of
193     * gnutls_ocsp_cert_status_t as defined in gnutls/ocsp.h. */
194    if (cert_status == GNUTLS_OCSP_CERT_GOOD)
195    {
196        /* Yay, everything's good! */
197        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
198                      "CA flagged certificate as valid at %s.", date_str);
199    }
200    else
201    {
202        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
203                      "CA flagged certificate as %s at %s.",
204                      cert_status == GNUTLS_OCSP_CERT_REVOKED ?
205                      "revoked" : "unknown", date_str);
206        ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
207    }
208
209 resp_cleanup:
210    gnutls_ocsp_resp_deinit(resp);
211    return ret;
212}
213
214
215
216int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
217                          void *ptr,
218                          gnutls_datum_t *ocsp_response)
219{
220    mgs_handle_t *ctxt = (mgs_handle_t *) ptr;
221    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
222                  "Loading OCSP response from %s",
223                  ctxt->sc->ocsp_response_file);
224
225    /* TODO: response should come from cache, which must be filled
226     * from sc->ocsp_uri */
227    int ret = gnutls_load_file(ctxt->sc->ocsp_response_file, ocsp_response);
228    if (ret != GNUTLS_E_SUCCESS)
229    {
230        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
231                      "Loading OCSP response failed: %s (%d)",
232                      gnutls_strerror(ret), ret);
233    }
234    else
235    {
236        /* Succeed if response is present and valid. */
237        if (check_ocsp_response(ctxt, ocsp_response) == GNUTLS_E_SUCCESS)
238            return GNUTLS_E_SUCCESS;
239    }
240
241    /* failure, clean up response data */
242    gnutls_free(ocsp_response->data);
243    ocsp_response->size = 0;
244    ocsp_response->data = NULL;
245    return GNUTLS_E_NO_CERTIFICATE_STATUS;
246}
247
248
249
250int mgs_create_ocsp_trust_list(gnutls_x509_trust_list_t *tl,
251                               const gnutls_x509_crt_t *chain,
252                               const int num)
253{
254    int added = 0;
255    int ret = gnutls_x509_trust_list_init(tl, num);
256
257    if (ret == GNUTLS_E_SUCCESS)
258        added = gnutls_x509_trust_list_add_cas(*tl, chain, num, 0);
259
260    if (added != num)
261        ret = GNUTLS_E_CERTIFICATE_ERROR;
262
263    /* Clean up trust list in case of error */
264    if (ret != GNUTLS_E_SUCCESS)
265        gnutls_x509_trust_list_deinit(*tl, 0);
266
267    return ret;
268}
269
270
271
272apr_status_t mgs_cleanup_trust_list(void *data)
273{
274    gnutls_x509_trust_list_t *tl = (gnutls_x509_trust_list_t *) data;
275    gnutls_x509_trust_list_deinit(*tl, 0);
276    return APR_SUCCESS;
277}
278
279
280
281apr_uri_t * mgs_cert_get_ocsp_uri(apr_pool_t *p, gnutls_x509_crt_t cert)
282{
283    apr_pool_t *tmp;
284    apr_status_t rv = apr_pool_create(&tmp, p);
285    if (rv != APR_SUCCESS)
286        return NULL;
287
288    apr_uri_t *ocsp_uri = NULL;
289
290    int ret = GNUTLS_E_SUCCESS;
291    /* search authority info access for OCSP URI */
292    for (int seq = 0; ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; seq++)
293    {
294        gnutls_datum_t ocsp_access_data;
295        ret = gnutls_x509_crt_get_authority_info_access(cert, seq,
296                                                        GNUTLS_IA_OCSP_URI,
297                                                        &ocsp_access_data,
298                                                        NULL);
299        if (ret == GNUTLS_E_SUCCESS)
300        {
301            /* create NULL terminated string */
302            char *ocsp_str =
303                apr_pstrndup(tmp, (const char*) ocsp_access_data.data,
304                             ocsp_access_data.size);
305            gnutls_free(ocsp_access_data.data);
306
307            ocsp_uri = apr_palloc(p, sizeof(apr_uri_t));
308            rv = apr_uri_parse(p, ocsp_str, ocsp_uri);
309            if (rv == APR_SUCCESS)
310                break;
311            else
312                ocsp_uri = NULL;
313        }
314    }
315
316    apr_pool_destroy(tmp);
317    return ocsp_uri;
318}
319
320
321
322/*
323 * Like in the general post_config hook the HTTP status codes for
324 * errors are just for fun. What matters is "neither OK nor DECLINED"
325 * to denote an error.
326 */
327int mgs_ocsp_post_config_server(apr_pool_t *pconf,
328                                apr_pool_t *ptemp __attribute__((unused)),
329                                server_rec *server)
330{
331    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
332        ap_get_module_config(server->module_config, &gnutls_module);
333
334    if (sc->certs_x509_chain_num < 2)
335    {
336        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
337                     "OCSP stapling is enabled but no CA certificate "
338                     "available for %s:%d, make sure it is included in "
339                     "GnuTLSCertificateFile!",
340                     server->server_hostname, server->addrs->host_port);
341        return HTTP_NOT_FOUND;
342    }
343
344    sc->ocsp_uri = mgs_cert_get_ocsp_uri(pconf, sc->certs_x509_crt_chain[0]);
345
346    sc->ocsp_trust = apr_palloc(pconf,
347                                sizeof(gnutls_x509_trust_list_t));
348     /* Only the direct issuer may sign the OCSP response or an OCSP
349      * signer. */
350    int ret = mgs_create_ocsp_trust_list(sc->ocsp_trust,
351                                         &(sc->certs_x509_crt_chain[1]),
352                                         1);
353    if (ret != GNUTLS_E_SUCCESS)
354    {
355        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, server,
356                     "Could not create OCSP trust list: %s (%d)",
357                     gnutls_strerror(ret), ret);
358        return HTTP_INTERNAL_SERVER_ERROR;
359    }
360    /* deinit trust list when the config pool is destroyed */
361    apr_pool_cleanup_register(pconf, sc->ocsp_trust,
362                              mgs_cleanup_trust_list,
363                              apr_pool_cleanup_null);
364
365    return OK;
366}
Note: See TracBrowser for help on using the repository browser.