2010-07-05 13:26:35 +00:00
|
|
|
/*
|
2018-04-25 09:38:38 +00:00
|
|
|
* Copyright (C) 2015-2018 Tobias Brunner
|
2010-07-05 13:26:35 +00:00
|
|
|
* Copyright (C) 2010 Martin Willi
|
|
|
|
* Copyright (C) 2010 revosec AG
|
|
|
|
* Copyright (C) 2009 Andreas Steffen
|
2018-04-25 09:38:38 +00:00
|
|
|
* HSR Hochschule fuer Technik Rapperswil
|
2010-07-05 13:26:35 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
2018-04-25 09:38:38 +00:00
|
|
|
#include <time.h>
|
|
|
|
|
2010-07-05 13:26:35 +00:00
|
|
|
#include "revocation_validator.h"
|
|
|
|
|
2012-10-16 14:03:21 +00:00
|
|
|
#include <utils/debug.h>
|
2010-07-05 13:26:35 +00:00
|
|
|
#include <credentials/certificates/x509.h>
|
|
|
|
#include <credentials/certificates/crl.h>
|
|
|
|
#include <credentials/certificates/ocsp_request.h>
|
|
|
|
#include <credentials/certificates/ocsp_response.h>
|
|
|
|
#include <credentials/sets/ocsp_response_wrapper.h>
|
|
|
|
#include <selectors/traffic_selector.h>
|
2018-06-21 06:59:40 +00:00
|
|
|
#include <threading/spinlock.h>
|
2010-07-05 13:26:35 +00:00
|
|
|
|
|
|
|
typedef struct private_revocation_validator_t private_revocation_validator_t;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private data of an revocation_validator_t object.
|
|
|
|
*/
|
|
|
|
struct private_revocation_validator_t {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public revocation_validator_t interface.
|
|
|
|
*/
|
|
|
|
revocation_validator_t public;
|
2016-12-30 17:12:53 +00:00
|
|
|
|
|
|
|
/**
|
2017-01-25 15:17:38 +00:00
|
|
|
* Enable OCSP validation
|
2016-12-30 17:12:53 +00:00
|
|
|
*/
|
|
|
|
bool enable_ocsp;
|
|
|
|
|
|
|
|
/**
|
2017-01-25 15:17:38 +00:00
|
|
|
* Enable CRL validation
|
2016-12-30 17:12:53 +00:00
|
|
|
*/
|
|
|
|
bool enable_crl;
|
|
|
|
|
2018-06-21 06:59:40 +00:00
|
|
|
/**
|
|
|
|
* Lock to access flags
|
|
|
|
*/
|
|
|
|
spinlock_t *lock;
|
2010-07-05 13:26:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do an OCSP request
|
|
|
|
*/
|
|
|
|
static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
|
|
|
|
certificate_t *issuer)
|
|
|
|
{
|
|
|
|
certificate_t *request, *response;
|
2018-05-03 09:38:07 +00:00
|
|
|
chunk_t send, receive = chunk_empty;
|
2010-07-05 13:26:35 +00:00
|
|
|
|
|
|
|
/* TODO: requestor name, signature */
|
|
|
|
request = lib->creds->create(lib->creds,
|
|
|
|
CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
|
|
|
|
BUILD_CA_CERT, issuer,
|
|
|
|
BUILD_CERT, subject, BUILD_END);
|
|
|
|
if (!request)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "generating ocsp request failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-13 11:34:04 +00:00
|
|
|
if (!request->get_encoding(request, CERT_ASN1_DER, &send))
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "encoding ocsp request failed");
|
|
|
|
request->destroy(request);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-07-05 13:26:35 +00:00
|
|
|
request->destroy(request);
|
|
|
|
|
|
|
|
DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url);
|
|
|
|
if (lib->fetcher->fetch(lib->fetcher, url, &receive,
|
|
|
|
FETCH_REQUEST_DATA, send,
|
|
|
|
FETCH_REQUEST_TYPE, "application/ocsp-request",
|
|
|
|
FETCH_END) != SUCCESS)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "ocsp request to %s failed", url);
|
2018-05-03 09:38:07 +00:00
|
|
|
chunk_free(&receive);
|
2010-07-05 13:26:35 +00:00
|
|
|
chunk_free(&send);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
chunk_free(&send);
|
|
|
|
|
|
|
|
response = lib->creds->create(lib->creds,
|
|
|
|
CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
|
|
|
|
BUILD_BLOB_ASN1_DER, receive, BUILD_END);
|
|
|
|
chunk_free(&receive);
|
|
|
|
if (!response)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "parsing ocsp response failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* check the signature of an OCSP response
|
|
|
|
*/
|
2014-03-25 13:34:58 +00:00
|
|
|
static bool verify_ocsp(ocsp_response_t *response, certificate_t *ca)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
certificate_t *issuer, *subject;
|
|
|
|
identification_t *responder;
|
|
|
|
ocsp_response_wrapper_t *wrapper;
|
|
|
|
enumerator_t *enumerator;
|
2014-03-25 13:34:58 +00:00
|
|
|
x509_t *x509;
|
|
|
|
bool verified = FALSE, found = FALSE;
|
2010-07-05 13:26:35 +00:00
|
|
|
|
|
|
|
wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
|
2011-12-19 19:22:18 +00:00
|
|
|
lib->credmgr->add_local_set(lib->credmgr, &wrapper->set, FALSE);
|
2010-07-05 13:26:35 +00:00
|
|
|
|
|
|
|
subject = &response->certificate;
|
|
|
|
responder = subject->get_issuer(subject);
|
2014-03-25 13:34:58 +00:00
|
|
|
|
|
|
|
/* check OCSP response using CA or directly delegated OCSP signer */
|
|
|
|
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509,
|
2010-07-05 13:26:35 +00:00
|
|
|
KEY_ANY, responder, FALSE);
|
2014-03-25 13:34:58 +00:00
|
|
|
while (enumerator->enumerate(enumerator, &issuer))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2014-03-25 13:34:58 +00:00
|
|
|
x509 = (x509_t*)issuer;
|
|
|
|
if (!issuer->get_validity(issuer, NULL, NULL, NULL))
|
|
|
|
{ /* OCSP signer currently invalid */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!ca->equals(ca, issuer))
|
|
|
|
{ /* delegated OCSP signer? */
|
|
|
|
if (!lib->credmgr->issued_by(lib->credmgr, issuer, ca, NULL))
|
|
|
|
{ /* OCSP response not signed by CA, nor delegated OCSP signer */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(x509->get_flags(x509) & X509_OCSP_SIGNER))
|
|
|
|
{ /* delegated OCSP signer does not have OCSP signer flag */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2014-03-31 12:53:15 +00:00
|
|
|
found = TRUE;
|
2012-06-11 12:52:37 +00:00
|
|
|
if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
|
2014-03-25 13:34:58 +00:00
|
|
|
issuer->get_subject(issuer));
|
2010-07-05 13:26:35 +00:00
|
|
|
verified = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2014-03-25 13:34:58 +00:00
|
|
|
DBG1(DBG_CFG, "ocsp response verification failed, "
|
|
|
|
"invalid signature");
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
2014-03-25 13:34:58 +00:00
|
|
|
if (!verified)
|
|
|
|
{
|
|
|
|
/* as fallback, use any locally installed OCSP signer certificate */
|
|
|
|
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
|
|
|
|
CERT_X509, KEY_ANY, responder, TRUE);
|
|
|
|
while (enumerator->enumerate(enumerator, &issuer))
|
|
|
|
{
|
|
|
|
x509 = (x509_t*)issuer;
|
|
|
|
/* while issued_by() accepts both OCSP signer or CA basic
|
|
|
|
* constraint flags to verify OCSP responses, unrelated but trusted
|
|
|
|
* OCSP signers must explicitly have the OCSP signer flag set. */
|
|
|
|
if ((x509->get_flags(x509) & X509_OCSP_SIGNER) &&
|
|
|
|
issuer->get_validity(issuer, NULL, NULL, NULL))
|
|
|
|
{
|
|
|
|
found = TRUE;
|
|
|
|
if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
|
|
|
|
issuer->get_subject(issuer));
|
|
|
|
verified = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DBG1(DBG_CFG, "ocsp response verification failed, "
|
|
|
|
"invalid signature");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
|
2010-07-05 13:26:35 +00:00
|
|
|
lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
|
|
|
|
wrapper->destroy(wrapper);
|
2014-03-25 13:34:58 +00:00
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "ocsp response verification failed, "
|
|
|
|
"no signer certificate '%Y' found", responder);
|
|
|
|
}
|
2010-07-05 13:26:35 +00:00
|
|
|
return verified;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the better of two OCSP responses, and check for usable OCSP info
|
|
|
|
*/
|
|
|
|
static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
|
2014-03-27 09:59:29 +00:00
|
|
|
x509_t *subject, x509_t *issuer,
|
|
|
|
cert_validation_t *valid, bool cache)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
ocsp_response_t *response;
|
|
|
|
time_t revocation, this_update, next_update, valid_until;
|
|
|
|
crl_reason_t reason;
|
|
|
|
bool revoked = FALSE;
|
|
|
|
|
|
|
|
response = (ocsp_response_t*)cand;
|
|
|
|
|
|
|
|
/* check ocsp signature */
|
2014-03-25 13:34:58 +00:00
|
|
|
if (!verify_ocsp(response, &issuer->interface))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
cand->destroy(cand);
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
/* check if response contains our certificate */
|
|
|
|
switch (response->get_status(response, subject, issuer, &revocation, &reason,
|
|
|
|
&this_update, &next_update))
|
|
|
|
{
|
|
|
|
case VALIDATION_REVOKED:
|
|
|
|
/* subject has been revoked by a valid OCSP response */
|
|
|
|
DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
|
|
|
|
&revocation, TRUE, crl_reason_names, reason);
|
|
|
|
revoked = TRUE;
|
|
|
|
break;
|
|
|
|
case VALIDATION_GOOD:
|
|
|
|
/* results in either good or stale */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case VALIDATION_FAILED:
|
|
|
|
/* candidate unusable, does not contain our cert */
|
|
|
|
DBG1(DBG_CFG, " ocsp response contains no status on our certificate");
|
|
|
|
cand->destroy(cand);
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* select the better of the two responses */
|
|
|
|
if (best == NULL || certificate_is_newer(cand, best))
|
|
|
|
{
|
|
|
|
DESTROY_IF(best);
|
|
|
|
best = cand;
|
|
|
|
if (best->get_validity(best, NULL, NULL, &valid_until))
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " ocsp response is valid: until %T",
|
|
|
|
&valid_until, FALSE);
|
|
|
|
*valid = VALIDATION_GOOD;
|
|
|
|
if (cache)
|
|
|
|
{ /* cache non-stale only, stale certs get refetched */
|
|
|
|
lib->credmgr->cache_cert(lib->credmgr, best);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " ocsp response is stale: since %T",
|
|
|
|
&valid_until, FALSE);
|
|
|
|
*valid = VALIDATION_STALE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*valid = VALIDATION_STALE;
|
|
|
|
cand->destroy(cand);
|
|
|
|
}
|
|
|
|
if (revoked)
|
|
|
|
{ /* revoked always counts, even if stale */
|
|
|
|
*valid = VALIDATION_REVOKED;
|
|
|
|
}
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* validate a x509 certificate using OCSP
|
|
|
|
*/
|
|
|
|
static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
|
|
|
|
auth_cfg_t *auth)
|
|
|
|
{
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
cert_validation_t valid = VALIDATION_SKIPPED;
|
|
|
|
certificate_t *best = NULL, *current;
|
|
|
|
identification_t *keyid = NULL;
|
|
|
|
public_key_t *public;
|
|
|
|
chunk_t chunk;
|
|
|
|
char *uri = NULL;
|
|
|
|
|
|
|
|
/** lookup cache for valid OCSP responses */
|
|
|
|
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
|
|
|
|
CERT_X509_OCSP_RESPONSE, KEY_ANY, NULL, FALSE);
|
|
|
|
while (enumerator->enumerate(enumerator, ¤t))
|
|
|
|
{
|
|
|
|
current->get_ref(current);
|
2014-03-27 09:59:29 +00:00
|
|
|
best = get_better_ocsp(current, best, subject, issuer, &valid, FALSE);
|
2010-07-05 13:26:35 +00:00
|
|
|
if (best && valid != VALIDATION_STALE)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " using cached ocsp response");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
/* derive the authorityKeyIdentifier from the issuer's public key */
|
|
|
|
current = &issuer->interface;
|
|
|
|
public = current->get_public_key(current);
|
2010-07-13 09:28:04 +00:00
|
|
|
if (public && public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
|
|
|
|
}
|
|
|
|
/** fetch from configured OCSP responder URLs */
|
|
|
|
if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
|
|
|
|
{
|
|
|
|
enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
|
|
|
|
CERT_X509_OCSP_RESPONSE, keyid);
|
|
|
|
while (enumerator->enumerate(enumerator, &uri))
|
|
|
|
{
|
|
|
|
current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
|
|
|
|
if (current)
|
|
|
|
{
|
|
|
|
best = get_better_ocsp(current, best, subject, issuer,
|
2014-03-27 09:59:29 +00:00
|
|
|
&valid, TRUE);
|
2010-07-05 13:26:35 +00:00
|
|
|
if (best && valid != VALIDATION_STALE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
DESTROY_IF(public);
|
|
|
|
DESTROY_IF(keyid);
|
|
|
|
|
|
|
|
/* fallback to URL fetching from subject certificate's URIs */
|
|
|
|
if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
|
|
|
|
{
|
|
|
|
enumerator = subject->create_ocsp_uri_enumerator(subject);
|
|
|
|
while (enumerator->enumerate(enumerator, &uri))
|
|
|
|
{
|
|
|
|
current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
|
|
|
|
if (current)
|
|
|
|
{
|
|
|
|
best = get_better_ocsp(current, best, subject, issuer,
|
2014-03-27 09:59:29 +00:00
|
|
|
&valid, TRUE);
|
2010-07-05 13:26:35 +00:00
|
|
|
if (best && valid != VALIDATION_STALE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
/* an uri was found, but no result. switch validation state to failed */
|
|
|
|
if (valid == VALIDATION_SKIPPED && uri)
|
|
|
|
{
|
|
|
|
valid = VALIDATION_FAILED;
|
|
|
|
}
|
2018-05-03 09:07:59 +00:00
|
|
|
auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
|
|
|
|
if (valid == VALIDATION_GOOD)
|
|
|
|
{ /* successful OCSP check fulfills also CRL constraint */
|
|
|
|
auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
|
|
|
DESTROY_IF(best);
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* fetch a CRL from an URL
|
|
|
|
*/
|
|
|
|
static certificate_t* fetch_crl(char *url)
|
|
|
|
{
|
|
|
|
certificate_t *crl;
|
2018-05-03 09:38:07 +00:00
|
|
|
chunk_t chunk = chunk_empty;
|
2010-07-05 13:26:35 +00:00
|
|
|
|
|
|
|
DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
|
|
|
|
if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "crl fetching failed");
|
2018-05-03 09:38:07 +00:00
|
|
|
chunk_free(&chunk);
|
2010-07-05 13:26:35 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
|
2015-11-11 13:26:00 +00:00
|
|
|
BUILD_BLOB_PEM, chunk, BUILD_END);
|
2010-07-05 13:26:35 +00:00
|
|
|
chunk_free(&chunk);
|
|
|
|
if (!crl)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return crl;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* check the signature of an CRL
|
|
|
|
*/
|
2014-03-27 09:59:29 +00:00
|
|
|
static bool verify_crl(certificate_t *crl)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
certificate_t *issuer;
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
bool verified = FALSE;
|
|
|
|
|
|
|
|
enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
|
|
|
|
KEY_ANY, crl->get_issuer(crl), FALSE);
|
2014-03-27 09:59:29 +00:00
|
|
|
while (enumerator->enumerate(enumerator, &issuer, NULL))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2012-06-11 12:52:37 +00:00
|
|
|
if (lib->credmgr->issued_by(lib->credmgr, crl, issuer, NULL))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " crl correctly signed by \"%Y\"",
|
|
|
|
issuer->get_subject(issuer));
|
|
|
|
verified = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
return verified;
|
|
|
|
}
|
|
|
|
|
2016-10-03 10:40:24 +00:00
|
|
|
/**
|
|
|
|
* Report the given CRL's validity and cache it if valid and requested
|
|
|
|
*/
|
2018-04-25 09:38:38 +00:00
|
|
|
static bool is_crl_valid(certificate_t *crl, time_t now, bool cache)
|
2016-10-03 10:40:24 +00:00
|
|
|
{
|
|
|
|
time_t valid_until;
|
|
|
|
|
2018-04-25 09:38:38 +00:00
|
|
|
if (crl->get_validity(crl, &now, NULL, &valid_until))
|
2016-10-03 10:40:24 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " crl is valid: until %T", &valid_until, FALSE);
|
|
|
|
if (cache)
|
|
|
|
{
|
|
|
|
lib->credmgr->cache_cert(lib->credmgr, crl);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
DBG1(DBG_CFG, " crl is stale: since %T", &valid_until, FALSE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-04-25 09:38:38 +00:00
|
|
|
/**
|
|
|
|
* Check if the CRL should be used yet
|
|
|
|
*/
|
|
|
|
static bool is_crl_not_valid_yet(certificate_t *crl, time_t now)
|
|
|
|
{
|
|
|
|
time_t this_update;
|
|
|
|
|
|
|
|
if (!crl->get_validity(crl, &now, &this_update, NULL))
|
|
|
|
{
|
|
|
|
if (this_update > now)
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, " crl is not valid: until %T", &this_update, FALSE);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/* we accept stale CRLs */
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2010-07-05 13:26:35 +00:00
|
|
|
/**
|
|
|
|
* Get the better of two CRLs, and check for usable CRL info
|
|
|
|
*/
|
|
|
|
static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
|
2014-03-27 09:59:29 +00:00
|
|
|
x509_t *subject, cert_validation_t *valid,
|
2010-12-23 13:51:00 +00:00
|
|
|
bool cache, crl_t *base)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
enumerator_t *enumerator;
|
2018-04-25 09:38:38 +00:00
|
|
|
time_t now, revocation;
|
2010-07-05 13:26:35 +00:00
|
|
|
crl_reason_t reason;
|
2018-01-24 13:42:28 +00:00
|
|
|
chunk_t subject_serial, serial;
|
2010-12-23 13:51:00 +00:00
|
|
|
crl_t *crl = (crl_t*)cand;
|
|
|
|
|
|
|
|
if (base)
|
|
|
|
{
|
|
|
|
if (!crl->is_delta_crl(crl, &serial) ||
|
|
|
|
!chunk_equals(serial, base->get_serial(base)))
|
|
|
|
{
|
|
|
|
cand->destroy(cand);
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (crl->is_delta_crl(crl, NULL))
|
|
|
|
{
|
|
|
|
cand->destroy(cand);
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
}
|
2010-07-05 13:26:35 +00:00
|
|
|
|
|
|
|
/* check CRL signature */
|
2014-03-27 09:59:29 +00:00
|
|
|
if (!verify_crl(cand))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "crl response verification failed");
|
|
|
|
cand->destroy(cand);
|
|
|
|
return best;
|
|
|
|
}
|
2018-04-25 09:38:38 +00:00
|
|
|
now = time(NULL);
|
|
|
|
if (is_crl_not_valid_yet(cand, now))
|
|
|
|
{
|
|
|
|
cand->destroy(cand);
|
|
|
|
return best;
|
|
|
|
}
|
2010-07-05 13:26:35 +00:00
|
|
|
|
2018-01-24 13:42:28 +00:00
|
|
|
subject_serial = chunk_skip_zero(subject->get_serial(subject));
|
2010-07-05 13:26:35 +00:00
|
|
|
enumerator = crl->create_enumerator(crl);
|
|
|
|
while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
|
|
|
|
{
|
2018-01-24 13:42:28 +00:00
|
|
|
if (chunk_equals(subject_serial, chunk_skip_zero(serial)))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2011-11-04 08:25:05 +00:00
|
|
|
if (reason != CRL_REASON_CERTIFICATE_HOLD)
|
|
|
|
{
|
|
|
|
*valid = VALIDATION_REVOKED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* if the cert is on hold, a newer CRL might not contain it */
|
|
|
|
*valid = VALIDATION_ON_HOLD;
|
|
|
|
}
|
2018-04-25 09:38:38 +00:00
|
|
|
is_crl_valid(cand, now, cache);
|
2016-10-03 10:40:24 +00:00
|
|
|
DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
|
|
|
|
&revocation, TRUE, crl_reason_names, reason);
|
2010-07-05 13:26:35 +00:00
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
DESTROY_IF(best);
|
|
|
|
return cand;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
/* select the better of the two CRLs */
|
|
|
|
if (best == NULL || crl_is_newer(crl, (crl_t*)best))
|
|
|
|
{
|
|
|
|
DESTROY_IF(best);
|
|
|
|
best = cand;
|
2018-04-25 09:38:38 +00:00
|
|
|
if (is_crl_valid(best, now, cache))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
*valid = VALIDATION_GOOD;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*valid = VALIDATION_STALE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*valid = VALIDATION_STALE;
|
|
|
|
cand->destroy(cand);
|
|
|
|
}
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-12-02 14:38:44 +00:00
|
|
|
* Find or fetch a certificate for a given crlIssuer
|
2010-07-05 13:26:35 +00:00
|
|
|
*/
|
2010-12-02 14:38:44 +00:00
|
|
|
static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
|
2014-03-27 09:59:29 +00:00
|
|
|
crl_t *base, certificate_t **best,
|
|
|
|
bool *uri_found)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
cert_validation_t valid = VALIDATION_SKIPPED;
|
|
|
|
enumerator_t *enumerator;
|
2010-12-02 14:38:44 +00:00
|
|
|
certificate_t *current;
|
|
|
|
char *uri;
|
2010-07-05 13:26:35 +00:00
|
|
|
|
2010-12-23 13:51:00 +00:00
|
|
|
/* find a cached (delta) crl */
|
2010-12-02 14:38:44 +00:00
|
|
|
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
|
|
|
|
CERT_X509_CRL, KEY_ANY, issuer, FALSE);
|
|
|
|
while (enumerator->enumerate(enumerator, ¤t))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2010-12-02 14:38:44 +00:00
|
|
|
current->get_ref(current);
|
2014-03-27 09:59:29 +00:00
|
|
|
*best = get_better_crl(current, *best, subject, &valid, FALSE, base);
|
2010-12-02 14:38:44 +00:00
|
|
|
if (*best && valid != VALIDATION_STALE)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2010-12-02 14:38:44 +00:00
|
|
|
DBG1(DBG_CFG, " using cached crl");
|
|
|
|
break;
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
2010-12-02 14:38:44 +00:00
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
2010-07-05 13:26:35 +00:00
|
|
|
|
2010-12-02 14:38:44 +00:00
|
|
|
/* fallback to fetching crls from credential sets cdps */
|
2010-12-23 13:51:00 +00:00
|
|
|
if (!base && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
|
2010-12-02 14:38:44 +00:00
|
|
|
{
|
|
|
|
enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
|
|
|
|
CERT_X509_CRL, issuer);
|
|
|
|
while (enumerator->enumerate(enumerator, &uri))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2010-12-02 14:38:44 +00:00
|
|
|
*uri_found = TRUE;
|
|
|
|
current = fetch_crl(uri);
|
|
|
|
if (current)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2010-12-23 10:44:36 +00:00
|
|
|
if (!current->has_issuer(current, issuer))
|
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match CRL "
|
|
|
|
"issuer '%Y'", current->get_issuer(current), issuer);
|
|
|
|
current->destroy(current);
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-07 16:53:13 +00:00
|
|
|
*best = get_better_crl(current, *best, subject,
|
2014-03-27 09:59:29 +00:00
|
|
|
&valid, TRUE, base);
|
2010-12-02 14:38:44 +00:00
|
|
|
if (*best && valid != VALIDATION_STALE)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2010-12-02 14:38:44 +00:00
|
|
|
break;
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-12-02 14:38:44 +00:00
|
|
|
enumerator->destroy(enumerator);
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
2010-12-02 14:38:44 +00:00
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
2018-04-04 14:16:38 +00:00
|
|
|
/**
|
|
|
|
* Check if the issuer of the given CRL matches
|
|
|
|
*/
|
|
|
|
static bool check_issuer(certificate_t *crl, x509_t *issuer, x509_cdp_t *cdp)
|
|
|
|
{
|
|
|
|
certificate_t *cissuer = (certificate_t*)issuer;
|
|
|
|
identification_t *id;
|
|
|
|
chunk_t chunk;
|
|
|
|
bool matches = FALSE;
|
|
|
|
|
|
|
|
if (cdp->issuer)
|
|
|
|
{
|
|
|
|
return crl->has_issuer(crl, cdp->issuer);
|
|
|
|
}
|
|
|
|
/* check SKI/AKI first, but fall back to DN matching */
|
|
|
|
chunk = issuer->get_subjectKeyIdentifier(issuer);
|
|
|
|
if (chunk.len)
|
|
|
|
{
|
|
|
|
id = identification_create_from_encoding(ID_KEY_ID, chunk);
|
|
|
|
matches = crl->has_issuer(crl, id);
|
|
|
|
id->destroy(id);
|
|
|
|
}
|
|
|
|
return matches || crl->has_issuer(crl, cissuer->get_subject(cissuer));
|
|
|
|
}
|
|
|
|
|
2010-12-23 13:51:00 +00:00
|
|
|
/**
|
|
|
|
* Look for a delta CRL for a given base CRL
|
|
|
|
*/
|
|
|
|
static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
|
2014-03-27 09:59:29 +00:00
|
|
|
crl_t *base, cert_validation_t base_valid)
|
2010-12-23 13:51:00 +00:00
|
|
|
{
|
|
|
|
cert_validation_t valid = VALIDATION_SKIPPED;
|
2018-04-04 14:16:38 +00:00
|
|
|
certificate_t *best = NULL, *current, *cissuer = (certificate_t*)issuer;
|
2010-12-23 13:51:00 +00:00
|
|
|
enumerator_t *enumerator;
|
|
|
|
identification_t *id;
|
|
|
|
x509_cdp_t *cdp;
|
|
|
|
chunk_t chunk;
|
|
|
|
bool uri;
|
|
|
|
|
|
|
|
/* find cached delta CRL via subjectKeyIdentifier */
|
|
|
|
chunk = issuer->get_subjectKeyIdentifier(issuer);
|
|
|
|
if (chunk.len)
|
|
|
|
{
|
|
|
|
id = identification_create_from_encoding(ID_KEY_ID, chunk);
|
2014-03-27 09:59:29 +00:00
|
|
|
valid = find_crl(subject, id, base, &best, &uri);
|
2010-12-23 13:51:00 +00:00
|
|
|
id->destroy(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find delta CRL by CRLIssuer */
|
|
|
|
enumerator = subject->create_crl_uri_enumerator(subject);
|
|
|
|
while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
|
|
|
|
enumerator->enumerate(enumerator, &cdp))
|
|
|
|
{
|
|
|
|
if (cdp->issuer)
|
|
|
|
{
|
2014-03-27 09:59:29 +00:00
|
|
|
valid = find_crl(subject, cdp->issuer, base, &best, &uri);
|
2010-12-23 13:51:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
/* fetch from URIs found in Freshest CRL extension */
|
|
|
|
enumerator = base->create_delta_crl_uri_enumerator(base);
|
|
|
|
while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
|
|
|
|
enumerator->enumerate(enumerator, &cdp))
|
|
|
|
{
|
|
|
|
current = fetch_crl(cdp->uri);
|
|
|
|
if (current)
|
|
|
|
{
|
2018-04-04 14:16:38 +00:00
|
|
|
if (!check_issuer(current, issuer, cdp))
|
2010-12-23 13:51:00 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "issuer of fetched delta CRL '%Y' does not match "
|
2018-04-04 14:16:38 +00:00
|
|
|
"certificate's %sissuer '%Y'",
|
|
|
|
current->get_issuer(current), cdp->issuer ? "CRL " : "",
|
|
|
|
cdp->issuer ?: cissuer->get_subject(cissuer));
|
2010-12-23 13:51:00 +00:00
|
|
|
current->destroy(current);
|
|
|
|
continue;
|
|
|
|
}
|
2014-03-27 09:59:29 +00:00
|
|
|
best = get_better_crl(current, best, subject, &valid, TRUE, base);
|
2010-12-23 13:51:00 +00:00
|
|
|
if (best && valid != VALIDATION_STALE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
if (best)
|
|
|
|
{
|
|
|
|
best->destroy(best);
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
return base_valid;
|
|
|
|
}
|
|
|
|
|
2010-12-02 14:38:44 +00:00
|
|
|
/**
|
|
|
|
* validate a x509 certificate using CRL
|
|
|
|
*/
|
|
|
|
static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
|
|
|
|
auth_cfg_t *auth)
|
|
|
|
{
|
|
|
|
cert_validation_t valid = VALIDATION_SKIPPED;
|
2018-04-04 14:16:38 +00:00
|
|
|
certificate_t *best = NULL, *cissuer = (certificate_t*)issuer;
|
2010-12-17 14:52:15 +00:00
|
|
|
identification_t *id;
|
|
|
|
x509_cdp_t *cdp;
|
2010-12-02 14:38:44 +00:00
|
|
|
bool uri_found = FALSE;
|
|
|
|
certificate_t *current;
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
chunk_t chunk;
|
|
|
|
|
|
|
|
/* use issuers subjectKeyIdentifier to find a cached CRL / fetch from CDP */
|
|
|
|
chunk = issuer->get_subjectKeyIdentifier(issuer);
|
|
|
|
if (chunk.len)
|
|
|
|
{
|
|
|
|
id = identification_create_from_encoding(ID_KEY_ID, chunk);
|
2014-03-27 09:59:29 +00:00
|
|
|
valid = find_crl(subject, id, NULL, &best, &uri_found);
|
2010-12-02 14:38:44 +00:00
|
|
|
id->destroy(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find a cached CRL or fetch via configured CDP via CRLIssuer */
|
|
|
|
enumerator = subject->create_crl_uri_enumerator(subject);
|
|
|
|
while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
|
2010-12-17 14:52:15 +00:00
|
|
|
enumerator->enumerate(enumerator, &cdp))
|
2010-12-02 14:38:44 +00:00
|
|
|
{
|
2010-12-23 13:51:00 +00:00
|
|
|
if (cdp->issuer)
|
2010-12-02 14:38:44 +00:00
|
|
|
{
|
2014-03-27 09:59:29 +00:00
|
|
|
valid = find_crl(subject, cdp->issuer, NULL, &best, &uri_found);
|
2010-12-02 14:38:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
2010-07-05 13:26:35 +00:00
|
|
|
|
2010-12-02 14:38:44 +00:00
|
|
|
/* fallback to fetching CRLs from CDPs found in subjects certificate */
|
2010-07-05 13:26:35 +00:00
|
|
|
if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
|
|
|
|
{
|
|
|
|
enumerator = subject->create_crl_uri_enumerator(subject);
|
2010-12-17 14:52:15 +00:00
|
|
|
while (enumerator->enumerate(enumerator, &cdp))
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2010-12-02 14:38:44 +00:00
|
|
|
uri_found = TRUE;
|
2010-12-17 14:52:15 +00:00
|
|
|
current = fetch_crl(cdp->uri);
|
2010-07-05 13:26:35 +00:00
|
|
|
if (current)
|
|
|
|
{
|
2018-04-04 14:16:38 +00:00
|
|
|
if (!check_issuer(current, issuer, cdp))
|
2010-12-02 14:38:44 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match "
|
2018-04-04 14:16:38 +00:00
|
|
|
"certificate's %sissuer '%Y'",
|
|
|
|
current->get_issuer(current), cdp->issuer ? "CRL " : "",
|
|
|
|
cdp->issuer ?: cissuer->get_subject(cissuer));
|
2010-12-02 14:38:44 +00:00
|
|
|
current->destroy(current);
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-07 16:53:13 +00:00
|
|
|
best = get_better_crl(current, best, subject, &valid,
|
2014-03-27 09:59:29 +00:00
|
|
|
TRUE, NULL);
|
2010-07-05 13:26:35 +00:00
|
|
|
if (best && valid != VALIDATION_STALE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
}
|
|
|
|
|
2010-12-23 13:51:00 +00:00
|
|
|
/* look for delta CRLs */
|
|
|
|
if (best && (valid == VALIDATION_GOOD || valid == VALIDATION_STALE))
|
|
|
|
{
|
2014-03-27 09:59:29 +00:00
|
|
|
valid = check_delta_crl(subject, issuer, (crl_t*)best, valid);
|
2010-12-23 13:51:00 +00:00
|
|
|
}
|
|
|
|
|
2010-07-05 13:26:35 +00:00
|
|
|
/* an uri was found, but no result. switch validation state to failed */
|
2010-12-02 14:38:44 +00:00
|
|
|
if (valid == VALIDATION_SKIPPED && uri_found)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
valid = VALIDATION_FAILED;
|
|
|
|
}
|
2018-05-03 09:07:59 +00:00
|
|
|
if (valid == VALIDATION_SKIPPED)
|
|
|
|
{ /* if we skipped CRL validation, we use the result of OCSP for
|
|
|
|
* constraint checking */
|
|
|
|
auth->add(auth, AUTH_RULE_CRL_VALIDATION,
|
|
|
|
auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
|
|
|
|
}
|
|
|
|
else
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2018-05-03 09:07:59 +00:00
|
|
|
auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
|
|
|
DESTROY_IF(best);
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
|
|
|
METHOD(cert_validator_t, validate, bool,
|
|
|
|
private_revocation_validator_t *this, certificate_t *subject,
|
2011-02-10 14:51:18 +00:00
|
|
|
certificate_t *issuer, bool online, u_int pathlen, bool anchor,
|
2010-12-22 09:43:06 +00:00
|
|
|
auth_cfg_t *auth)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2018-06-21 06:59:40 +00:00
|
|
|
bool enable_ocsp, enable_crl;
|
|
|
|
|
|
|
|
this->lock->lock(this->lock);
|
|
|
|
enable_ocsp = this->enable_ocsp;
|
|
|
|
enable_crl = this->enable_crl;
|
|
|
|
this->lock->unlock(this->lock);
|
|
|
|
|
|
|
|
if (online && (enable_ocsp || enable_crl) &&
|
2017-01-25 15:17:38 +00:00
|
|
|
subject->get_type(subject) == CERT_X509 &&
|
|
|
|
issuer->get_type(issuer) == CERT_X509)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
|
|
|
DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
|
|
|
|
subject->get_subject(subject));
|
2016-12-30 17:12:53 +00:00
|
|
|
|
2018-06-21 06:59:40 +00:00
|
|
|
if (enable_ocsp)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2018-05-03 09:07:59 +00:00
|
|
|
switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth))
|
2016-12-30 17:12:53 +00:00
|
|
|
{
|
|
|
|
case VALIDATION_GOOD:
|
|
|
|
DBG1(DBG_CFG, "certificate status is good");
|
|
|
|
return TRUE;
|
|
|
|
case VALIDATION_REVOKED:
|
|
|
|
case VALIDATION_ON_HOLD:
|
|
|
|
/* has already been logged */
|
|
|
|
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
|
|
|
|
subject);
|
|
|
|
return FALSE;
|
|
|
|
case VALIDATION_SKIPPED:
|
|
|
|
DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
|
|
|
|
break;
|
|
|
|
case VALIDATION_STALE:
|
|
|
|
DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
|
|
|
|
break;
|
|
|
|
case VALIDATION_FAILED:
|
|
|
|
DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
|
|
|
|
break;
|
|
|
|
}
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
2018-05-03 09:19:18 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
auth->add(auth, AUTH_RULE_OCSP_VALIDATION, VALIDATION_SKIPPED);
|
|
|
|
}
|
2016-12-30 17:12:53 +00:00
|
|
|
|
2018-06-21 06:59:40 +00:00
|
|
|
if (enable_crl)
|
2010-07-05 13:26:35 +00:00
|
|
|
{
|
2018-05-03 09:07:59 +00:00
|
|
|
switch (check_crl((x509_t*)subject, (x509_t*)issuer, auth))
|
2016-12-30 17:12:53 +00:00
|
|
|
{
|
|
|
|
case VALIDATION_GOOD:
|
|
|
|
DBG1(DBG_CFG, "certificate status is good");
|
|
|
|
return TRUE;
|
|
|
|
case VALIDATION_REVOKED:
|
|
|
|
case VALIDATION_ON_HOLD:
|
|
|
|
/* has already been logged */
|
|
|
|
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
|
|
|
|
subject);
|
|
|
|
return FALSE;
|
|
|
|
case VALIDATION_FAILED:
|
|
|
|
case VALIDATION_SKIPPED:
|
|
|
|
DBG1(DBG_CFG, "certificate status is not available");
|
|
|
|
break;
|
|
|
|
case VALIDATION_STALE:
|
|
|
|
DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
|
|
|
|
break;
|
|
|
|
}
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
2018-05-03 09:19:18 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
auth->add(auth, AUTH_RULE_CRL_VALIDATION,
|
|
|
|
auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
|
|
|
|
}
|
2016-12-30 17:12:53 +00:00
|
|
|
|
2013-07-09 09:55:32 +00:00
|
|
|
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED,
|
|
|
|
subject);
|
2010-07-05 13:26:35 +00:00
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2018-06-21 06:59:40 +00:00
|
|
|
METHOD(revocation_validator_t, reload, void,
|
|
|
|
private_revocation_validator_t *this)
|
|
|
|
{
|
|
|
|
bool enable_ocsp, enable_crl;
|
|
|
|
|
|
|
|
enable_ocsp = lib->settings->get_bool(lib->settings,
|
|
|
|
"%s.plugins.revocation.enable_ocsp", TRUE, lib->ns);
|
|
|
|
enable_crl = lib->settings->get_bool(lib->settings,
|
|
|
|
"%s.plugins.revocation.enable_crl", TRUE, lib->ns);
|
|
|
|
|
|
|
|
this->lock->lock(this->lock);
|
|
|
|
this->enable_ocsp = enable_ocsp;
|
|
|
|
this->enable_crl = enable_crl;
|
|
|
|
this->lock->unlock(this->lock);
|
|
|
|
|
|
|
|
if (!enable_ocsp)
|
|
|
|
{
|
|
|
|
DBG1(DBG_LIB, "all OCSP validation disabled");
|
|
|
|
}
|
|
|
|
if (!enable_crl)
|
|
|
|
{
|
|
|
|
DBG1(DBG_LIB, "all CRL validation disabled");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-05 13:26:35 +00:00
|
|
|
METHOD(revocation_validator_t, destroy, void,
|
|
|
|
private_revocation_validator_t *this)
|
|
|
|
{
|
2018-06-21 06:59:40 +00:00
|
|
|
this->lock->destroy(this->lock);
|
2010-07-05 13:26:35 +00:00
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See header
|
|
|
|
*/
|
|
|
|
revocation_validator_t *revocation_validator_create()
|
|
|
|
{
|
|
|
|
private_revocation_validator_t *this;
|
|
|
|
|
|
|
|
INIT(this,
|
|
|
|
.public = {
|
|
|
|
.validator.validate = _validate,
|
2018-06-21 06:59:40 +00:00
|
|
|
.reload = _reload,
|
2010-07-05 13:26:35 +00:00
|
|
|
.destroy = _destroy,
|
|
|
|
},
|
2018-06-21 06:59:40 +00:00
|
|
|
.lock = spinlock_create(),
|
2010-07-05 13:26:35 +00:00
|
|
|
);
|
|
|
|
|
2018-06-21 06:59:40 +00:00
|
|
|
reload(this);
|
|
|
|
|
2010-07-05 13:26:35 +00:00
|
|
|
return &this->public;
|
|
|
|
}
|