source: mod_gnutls/src/gnutls_ocsp.c @ d35b98e

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

Check OCSP response and staple it only if good

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