source: mod_gnutls/src/gnutls_ocsp.c @ 64856fd

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

Move trust list generation for OCSP to a separate function

The new function is supposed be used during server configuration
later, instead of recreating the trust list every time an OCSP
response is checked.

  • Property mode set to 100644
File size: 9.4 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->certs_x509_chain_num < 2)
83    {
84        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
85                      "No CA certificates in store, cannot verify response.");
86        return GNUTLS_E_NO_CERTIFICATE_FOUND;
87    }
88
89    /* Only the direct issuer may sign the OCSP response or an OCSP
90     * signer. Assuming the certificate file is properly ordered, it
91     * should be the one directly after the server's. */
92    gnutls_x509_trust_list_t issuer;
93    int ret = mgs_create_ocsp_trust_list(&issuer,
94                                         &(ctxt->sc->certs_x509_crt_chain[1]),
95                                         1);
96    if (ret != GNUTLS_E_SUCCESS)
97    {
98        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
99                      "Could not create issuer trust list: %s (%d)",
100                      gnutls_strerror(ret), ret);
101        return ret;
102    }
103
104    gnutls_ocsp_resp_t resp;
105    ret = gnutls_ocsp_resp_init(&resp);
106    if (ret != GNUTLS_E_SUCCESS)
107    {
108        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
109                      "Could not initialize OCSP response structure: %s (%d)",
110                      gnutls_strerror(ret), ret);
111        goto resp_cleanup;
112    }
113    ret = gnutls_ocsp_resp_import(resp, ocsp_response);
114    if (ret != GNUTLS_E_SUCCESS)
115    {
116        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
117                      "Importing OCSP response failed: %s (%d)",
118                      gnutls_strerror(ret), ret);
119        goto resp_cleanup;
120    }
121
122    ret = gnutls_ocsp_resp_check_crt(resp, 0,
123                                     ctxt->sc->certs_x509_crt_chain[0]);
124    if (ret != GNUTLS_E_SUCCESS)
125    {
126        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
127                      "OCSP response is not for server certificate: %s (%d)",
128                      gnutls_strerror(ret), ret);
129        goto resp_cleanup;
130    }
131
132    unsigned int verify;
133    ret = gnutls_ocsp_resp_verify(resp, issuer, &verify, 0);
134    if (ret != GNUTLS_E_SUCCESS)
135    {
136        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
137                      "OCSP response verification failed: %s (%d)",
138                      gnutls_strerror(ret), ret);
139        goto resp_cleanup;
140    }
141    else
142    {
143        /* verification worked, check the result */
144        if (verify != 0)
145        {
146            _log_verify_fail_reason(verify, ctxt->c);
147            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
148            goto resp_cleanup;
149        }
150        else
151            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
152                          "OCSP response is valid.");
153    }
154
155    /* OK, response is for our certificate and valid, let's get the
156     * actual response data. */
157    unsigned int cert_status;
158    time_t this_update;
159    time_t next_update;
160    ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
161                                      &cert_status, &this_update,
162                                      &next_update, NULL, NULL);
163    if (ret != GNUTLS_E_SUCCESS)
164    {
165        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
166                      "Could not get OCSP response data: %s (%d)",
167                      gnutls_strerror(ret), ret);
168        goto resp_cleanup;
169    }
170
171    apr_time_t now = apr_time_now();
172    apr_time_t valid_at;
173    apr_time_ansi_put(&valid_at, this_update);
174    /* Buffer for human-readable times produced by apr_rfc822_date,
175     * see apr_time.h */
176    char date_str[APR_RFC822_DATE_LEN];
177    apr_rfc822_date(date_str, valid_at);
178
179    if (now < valid_at)
180    {
181        /* We don't know if our clock or that of the OCSP responder is
182         * out of sync, so warn but continue. */
183        ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EGENERAL, ctxt->c,
184                      "OSCP response claims to be from future (%s), check "
185                      "time synchronization!", date_str);
186    }
187
188    if (next_update == (time_t) -1)
189        ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ctxt->c,
190                      "OSCP response does not contain nextUpdate info.");
191    else
192    {
193        apr_time_t valid_to;
194        apr_time_ansi_put(&valid_to, next_update);
195        if (now > valid_to)
196        {
197            apr_rfc822_date(date_str, valid_to);
198            ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
199                          "OCSP response has expired at %s!", date_str);
200            /* Do not send a stale response */
201            ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
202            goto resp_cleanup;
203        }
204    }
205
206    /* What's the actual status? Will be one of
207     * gnutls_ocsp_cert_status_t as defined in gnutls/ocsp.h. */
208    if (cert_status == GNUTLS_OCSP_CERT_GOOD)
209    {
210        /* Yay, everything's good! */
211        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
212                      "CA flagged certificate as valid at %s.", date_str);
213    }
214    else
215    {
216        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
217                      "CA flagged certificate as %s at %s.",
218                      cert_status == GNUTLS_OCSP_CERT_REVOKED ?
219                      "revoked" : "unknown", date_str);
220        ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
221    }
222
223 resp_cleanup:
224    gnutls_ocsp_resp_deinit(resp);
225    /* deinit trust list, but not the certificates */
226    gnutls_x509_trust_list_deinit(issuer, 0);
227    return ret;
228}
229
230
231
232int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
233                          void *ptr,
234                          gnutls_datum_t *ocsp_response)
235{
236    mgs_handle_t *ctxt = (mgs_handle_t *) ptr;
237    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
238                  "Loading OCSP response from %s",
239                  ctxt->sc->ocsp_response_file);
240
241    int ret = gnutls_load_file(ctxt->sc->ocsp_response_file, ocsp_response);
242    if (ret != GNUTLS_E_SUCCESS)
243    {
244        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, ctxt->c,
245                      "Loading OCSP response failed: %s (%d)",
246                      gnutls_strerror(ret), ret);
247        goto resp_cleanup;
248    }
249
250    /* succeed if response is present and valid, fail otherwise. */
251    if (check_ocsp_response(ctxt, ocsp_response) == GNUTLS_E_SUCCESS)
252        return GNUTLS_E_SUCCESS;
253
254 resp_cleanup:
255    gnutls_free(ocsp_response->data);
256    ocsp_response->size = 0;
257    ocsp_response->data = NULL;
258    return GNUTLS_E_NO_CERTIFICATE_STATUS;
259}
260
261
262
263int mgs_create_ocsp_trust_list(gnutls_x509_trust_list_t *tl,
264                               const gnutls_x509_crt_t *chain,
265                               const int num)
266{
267    int added = 0;
268    int ret = gnutls_x509_trust_list_init(tl, num);
269
270    if (ret == GNUTLS_E_SUCCESS)
271        added = gnutls_x509_trust_list_add_cas(*tl, chain, num, 0);
272
273    if (added != num)
274        ret = GNUTLS_E_CERTIFICATE_ERROR;
275
276    /* Clean up trust list in case of error */
277    if (ret != GNUTLS_E_SUCCESS)
278        gnutls_x509_trust_list_deinit(*tl, 0);
279
280    return ret;
281}
Note: See TracBrowser for help on using the repository browser.