Changeset d35b98e in mod_gnutls


Ignore:
Timestamp:
May 30, 2016, 5:21:21 PM (2 years ago)
Author:
Thomas Klute <thomas2.klute@…>
Branches:
debian/master, debian/stretch-backports, master, upstream
Children:
2a1ffd6
Parents:
c05780e
Message:

Check OCSP response and staple it only if good

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/gnutls_ocsp.c

    rc05780e rd35b98e  
    1616
    1717#include "gnutls_ocsp.h"
    18 
    1918#include "mod_gnutls.h"
    20 #include "apr_lib.h"
     19
     20#include <apr_lib.h>
     21#include <apr_time.h>
     22#include <gnutls/ocsp.h>
     23#include <time.h>
    2124
    2225#ifdef APLOG_USE_MODULE
     
    2427#endif
    2528
     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
    2663const char *mgs_store_ocsp_response_path(cmd_parms *parms,
    2764                                         void *dummy __attribute__((unused)),
     
    3471    return NULL;
    3572}
     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
    36238
    37239int mgs_get_ocsp_response(gnutls_session_t session __attribute__((unused)),
     
    50252                      "Loading OCSP response failed: %s (%d)",
    51253                      gnutls_strerror(ret), ret);
    52         gnutls_free(ocsp_response->data);
    53         ocsp_response->size = 0;
    54         ocsp_response->data = NULL;
    55         return GNUTLS_E_NO_CERTIFICATE_STATUS;
    56     }
    57 
    58     return GNUTLS_E_SUCCESS;
    59 }
     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 TracChangeset for help on using the changeset viewer.