caching of CRLs
This commit is contained in:
@@ -356,8 +356,8 @@ static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
|
|||||||
/**
|
/**
|
||||||
* check the signature of an OCSP response
|
* check the signature of an OCSP response
|
||||||
*/
|
*/
|
||||||
static bool check_ocsp_response(private_credential_manager_t *this,
|
static bool verify_ocsp(private_credential_manager_t *this,
|
||||||
ocsp_response_t *response)
|
ocsp_response_t *response)
|
||||||
{
|
{
|
||||||
certificate_t *issuer, *subject;
|
certificate_t *issuer, *subject;
|
||||||
identification_t *responder;
|
identification_t *responder;
|
||||||
@@ -403,7 +403,7 @@ static certificate_t *get_better_ocsp(private_credential_manager_t *this,
|
|||||||
response = (ocsp_response_t*)cand;
|
response = (ocsp_response_t*)cand;
|
||||||
|
|
||||||
/* check ocsp signature */
|
/* check ocsp signature */
|
||||||
if (!check_ocsp_response(this, response))
|
if (!verify_ocsp(this, response))
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "OCSP response verification failed");
|
DBG1(DBG_CFG, "OCSP response verification failed");
|
||||||
cand->destroy(cand);
|
cand->destroy(cand);
|
||||||
@@ -418,6 +418,7 @@ static certificate_t *get_better_ocsp(private_credential_manager_t *this,
|
|||||||
DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
|
DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
|
||||||
&revocation, crl_reason_names, reason);
|
&revocation, crl_reason_names, reason);
|
||||||
DESTROY_IF(best);
|
DESTROY_IF(best);
|
||||||
|
*valid = VALIDATION_REVOKED;
|
||||||
return cand;
|
return cand;
|
||||||
case VALIDATION_GOOD:
|
case VALIDATION_GOOD:
|
||||||
/* results in either good or stale */
|
/* results in either good or stale */
|
||||||
@@ -476,7 +477,7 @@ static cert_validation_t check_ocsp(private_credential_manager_t *this,
|
|||||||
best = get_better_ocsp(this, current, best, subject, issuer, &valid);
|
best = get_better_ocsp(this, current, best, subject, issuer, &valid);
|
||||||
if (best && valid != VALIDATION_STALE)
|
if (best && valid != VALIDATION_STALE)
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, " used cached ocsp response");
|
DBG1(DBG_CFG, "found cached ocsp response");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,9 +548,7 @@ static cert_validation_t check_ocsp(private_credential_manager_t *this,
|
|||||||
*/
|
*/
|
||||||
static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
|
static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
|
||||||
{
|
{
|
||||||
certificate_t *crl, *issuer;
|
certificate_t *crl;
|
||||||
enumerator_t *enumerator;
|
|
||||||
bool verified = FALSE;
|
|
||||||
chunk_t chunk;
|
chunk_t chunk;
|
||||||
|
|
||||||
/* TODO: unlock the manager while fetching? */
|
/* TODO: unlock the manager while fetching? */
|
||||||
@@ -566,8 +565,18 @@ static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
|
|||||||
DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
|
DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
return crl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check the signature of an CRL
|
||||||
|
*/
|
||||||
|
static bool verify_crl(private_credential_manager_t *this, certificate_t *crl)
|
||||||
|
{
|
||||||
|
certificate_t *issuer;
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
bool verified = FALSE;
|
||||||
|
|
||||||
/* verify the signature of the fetched crl */
|
|
||||||
enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl),
|
enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl),
|
||||||
FALSE, FALSE);
|
FALSE, FALSE);
|
||||||
while (enumerator->enumerate(enumerator, &issuer, NULL))
|
while (enumerator->enumerate(enumerator, &issuer, NULL))
|
||||||
@@ -581,14 +590,70 @@ static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
return verified;
|
||||||
|
}
|
||||||
|
|
||||||
if (!verified)
|
/**
|
||||||
|
* Get the better of two CRLs, and check for usable CRL info
|
||||||
|
*/
|
||||||
|
static certificate_t *get_better_crl(private_credential_manager_t *this,
|
||||||
|
certificate_t *cand, certificate_t *best,
|
||||||
|
x509_t *subject, x509_t *issuer,
|
||||||
|
cert_validation_t *valid)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
time_t revocation, valid_until;
|
||||||
|
crl_reason_t reason;
|
||||||
|
chunk_t serial;
|
||||||
|
crl_t *crl;
|
||||||
|
|
||||||
|
/* check CRL signature */
|
||||||
|
if (!verify_crl(this, cand))
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "crl is untrusted: issuer certificate not found");
|
DBG1(DBG_CFG, "crl response verification failed");
|
||||||
crl->destroy(crl);
|
cand->destroy(cand);
|
||||||
return NULL;
|
return best;
|
||||||
}
|
}
|
||||||
return crl;
|
|
||||||
|
crl = (crl_t*)cand;
|
||||||
|
enumerator = crl->create_enumerator(crl);
|
||||||
|
while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
|
||||||
|
{
|
||||||
|
if (chunk_equals(serial, subject->get_serial(subject)))
|
||||||
|
{
|
||||||
|
DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
|
||||||
|
&revocation, crl_reason_names, reason);
|
||||||
|
*valid = VALIDATION_REVOKED;
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
DESTROY_IF(best);
|
||||||
|
return cand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
/* select the better of the two CRLs */
|
||||||
|
if (best == NULL || cand->is_newer(cand, best))
|
||||||
|
{
|
||||||
|
DESTROY_IF(best);
|
||||||
|
best = cand;
|
||||||
|
if (best->get_validity(best, NULL, NULL, &valid_until))
|
||||||
|
{
|
||||||
|
DBG1(DBG_CFG, " crl is valid: until %#T", &valid_until);
|
||||||
|
*valid = VALIDATION_GOOD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG1(DBG_CFG, " crl is stale: since %#T", &valid_until);
|
||||||
|
*valid = VALIDATION_STALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*valid = VALIDATION_STALE;
|
||||||
|
cand->destroy(cand);
|
||||||
|
}
|
||||||
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -598,16 +663,17 @@ static cert_validation_t check_crl(private_credential_manager_t *this,
|
|||||||
x509_t *subject, x509_t *issuer,
|
x509_t *subject, x509_t *issuer,
|
||||||
auth_info_t *auth)
|
auth_info_t *auth)
|
||||||
{
|
{
|
||||||
identification_t *keyid = NULL;
|
|
||||||
certificate_t *best_cert = NULL;
|
|
||||||
certificate_t *cert;
|
|
||||||
public_key_t *public;
|
|
||||||
cert_validation_t valid = VALIDATION_SKIPPED;
|
cert_validation_t valid = VALIDATION_SKIPPED;
|
||||||
bool stale = TRUE;
|
identification_t *keyid = NULL;
|
||||||
|
certificate_t *best = NULL;
|
||||||
|
certificate_t *current;
|
||||||
|
public_key_t *public;
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
char *uri;
|
||||||
|
|
||||||
/* derive the authorityKeyIdentifier from the issuer's public key */
|
/* derive the authorityKeyIdentifier from the issuer's public key */
|
||||||
cert = &issuer->interface;
|
current = &issuer->interface;
|
||||||
public = cert->get_public_key(cert);
|
public = current->get_public_key(current);
|
||||||
if (public)
|
if (public)
|
||||||
{
|
{
|
||||||
keyid = public->get_id(public, ID_PUBKEY_SHA1);
|
keyid = public->get_id(public, ID_PUBKEY_SHA1);
|
||||||
@@ -616,146 +682,66 @@ static cert_validation_t check_crl(private_credential_manager_t *this,
|
|||||||
/* find a cached crl by authorityKeyIdentifier */
|
/* find a cached crl by authorityKeyIdentifier */
|
||||||
if (keyid)
|
if (keyid)
|
||||||
{
|
{
|
||||||
enumerator_t *enumerator = create_cert_enumerator(this, CERT_X509_CRL,
|
enumerator = create_cert_enumerator(this, CERT_X509_CRL, KEY_ANY,
|
||||||
KEY_ANY, keyid, TRUE);
|
keyid, FALSE);
|
||||||
certificate_t *cert;
|
while (enumerator->enumerate(enumerator, ¤t))
|
||||||
|
|
||||||
while (enumerator->enumerate(enumerator, &cert))
|
|
||||||
{
|
{
|
||||||
/* select most recent crl */
|
current->get_ref(current);
|
||||||
if (best_cert == NULL || cert->is_newer(cert, best_cert))
|
best = get_better_crl(this, current, best, subject, issuer, &valid);
|
||||||
|
if (best && valid != VALIDATION_STALE)
|
||||||
{
|
{
|
||||||
DESTROY_IF(best_cert);
|
DBG1(DBG_CFG, "found cached crl");
|
||||||
best_cert = cert->get_ref(cert);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check the validity of the cached crl if one was found */
|
/* fallback to fetching crls from credential sets cdps */
|
||||||
if (best_cert)
|
if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
|
||||||
{
|
{
|
||||||
time_t nextUpdate;
|
enumerator = create_cdp_enumerator(this, CERT_X509_CRL, keyid);
|
||||||
|
|
||||||
stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
|
|
||||||
DBG1(DBG_CFG, "cached crl is %s %#T",
|
|
||||||
stale? "stale: since":"valid: until",
|
|
||||||
&nextUpdate, FALSE );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fallback to fetching crls from cdps defined in ca info sections */
|
|
||||||
if (stale && keyid)
|
|
||||||
{
|
|
||||||
enumerator_t *enumerator = create_cdp_enumerator(this, CERT_X509_CRL,
|
|
||||||
keyid);
|
|
||||||
char *uri;
|
|
||||||
|
|
||||||
while (enumerator->enumerate(enumerator, &uri))
|
while (enumerator->enumerate(enumerator, &uri))
|
||||||
{
|
{
|
||||||
certificate_t *cert = fetch_crl(this, uri);
|
current = fetch_crl(this, uri);
|
||||||
|
best = get_better_crl(this, current, best, subject, issuer, &valid);
|
||||||
/* redefine default since we have at least one uri */
|
if (best && valid != VALIDATION_STALE)
|
||||||
valid = VALIDATION_FAILED;
|
|
||||||
|
|
||||||
if (cert)
|
|
||||||
{
|
{
|
||||||
/* select most recent crl until valid one is found */
|
break;
|
||||||
if (best_cert == NULL || cert->is_newer(cert, best_cert))
|
|
||||||
{
|
|
||||||
time_t nextUpdate;
|
|
||||||
|
|
||||||
DESTROY_IF(best_cert);
|
|
||||||
best_cert = cert;
|
|
||||||
stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
|
|
||||||
DBG1(DBG_CFG, "fetched crl is %s %#T",
|
|
||||||
stale? "stale: since":"valid: until",
|
|
||||||
&nextUpdate, FALSE );
|
|
||||||
if (!stale)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cert->destroy(cert);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fallback to fetching crls from cdps defined in the subject's certificate */
|
|
||||||
if (stale)
|
|
||||||
{
|
|
||||||
enumerator_t *enumerator = subject->create_crl_uri_enumerator(subject);
|
|
||||||
char *uri;
|
|
||||||
|
|
||||||
while (enumerator->enumerate(enumerator, &uri))
|
|
||||||
{
|
|
||||||
certificate_t *cert = fetch_crl(this, uri);
|
|
||||||
|
|
||||||
/* redefine default since we have at least one uri */
|
|
||||||
valid = VALIDATION_FAILED;
|
|
||||||
|
|
||||||
if (cert)
|
|
||||||
{
|
|
||||||
/* select most recent crl until valid one is found */
|
|
||||||
if (best_cert == NULL || cert->is_newer(cert, best_cert))
|
|
||||||
{
|
|
||||||
time_t nextUpdate;
|
|
||||||
|
|
||||||
DESTROY_IF(best_cert);
|
|
||||||
best_cert = cert;
|
|
||||||
stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
|
|
||||||
DBG1(DBG_CFG, "fetched crl is %s %#T",
|
|
||||||
stale? "stale: since":"valid: until",
|
|
||||||
&nextUpdate, FALSE );
|
|
||||||
if (!stale)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cert->destroy(cert);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
}
|
}
|
||||||
DESTROY_IF(public);
|
DESTROY_IF(public);
|
||||||
|
|
||||||
/* if we have a crl, check the revocation status */
|
/* fallback to fetching crls from cdps from subject's certificate */
|
||||||
if (best_cert)
|
if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
|
||||||
{
|
{
|
||||||
chunk_t subject_serial = subject->get_serial(subject);
|
enumerator = subject->create_crl_uri_enumerator(subject);
|
||||||
chunk_t serial;
|
|
||||||
time_t revocation;
|
|
||||||
crl_reason_t reason;
|
|
||||||
crl_t *crl = (crl_t*)best_cert;
|
|
||||||
enumerator_t *enumerator = crl->create_enumerator(crl);
|
|
||||||
|
|
||||||
/* redefine default */
|
while (enumerator->enumerate(enumerator, &uri))
|
||||||
valid = stale ? VALIDATION_STALE : VALIDATION_GOOD;
|
|
||||||
|
|
||||||
while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
|
|
||||||
{
|
{
|
||||||
if (chunk_equals(serial, subject_serial))
|
current = fetch_crl(this, uri);
|
||||||
|
best = get_better_crl(this, current, best, subject, issuer, &valid);
|
||||||
|
if (best && valid != VALIDATION_STALE)
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
|
|
||||||
&revocation, crl_reason_names, reason);
|
|
||||||
valid = VALIDATION_REVOKED;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
best_cert->destroy(best_cert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* an uri was found, but no result. switch validation state to failed */
|
||||||
|
if (valid == VALIDATION_SKIPPED && uri)
|
||||||
|
{
|
||||||
|
valid = VALIDATION_FAILED;
|
||||||
|
}
|
||||||
if (auth)
|
if (auth)
|
||||||
{
|
{
|
||||||
auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
|
auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
|
||||||
}
|
}
|
||||||
|
DESTROY_IF(best);
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,17 @@ static bool certs_filter(cert_data_t *data, relation_t **in, certificate_t **out
|
|||||||
certificate_t *cert;
|
certificate_t *cert;
|
||||||
|
|
||||||
cert = (*in)->subject;
|
cert = (*in)->subject;
|
||||||
|
if (data->key == KEY_ANY && data->id &&
|
||||||
|
(data->cert == CERT_ANY || data->cert == CERT_X509_CRL) &&
|
||||||
|
cert->get_type(cert) == CERT_X509_CRL)
|
||||||
|
{ /* CRL lookup is done using issuer/authkeyidentifier */
|
||||||
|
if (cert->has_issuer(cert, data->id))
|
||||||
|
{
|
||||||
|
*out = cert;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((data->cert == CERT_ANY || cert->get_type(cert) == data->cert) &&
|
if ((data->cert == CERT_ANY || cert->get_type(cert) == data->cert) &&
|
||||||
(!data->id || cert->has_subject(cert, data->id)))
|
(!data->id || cert->has_subject(cert, data->id)))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user