Add support for IKEv2 OCSP extensions (RFC 4806)

Closes strongswan/strongswan#2016

Co-authored-by: Tobias Brunner <[email protected]>
This commit is contained in:
Jean-François Hren
2024-03-13 15:10:50 +01:00
committed by Tobias Brunner
co-authored by Tobias Brunner
parent ddd926b698
commit 15612b3a42
15 changed files with 477 additions and 49 deletions
@@ -90,6 +90,16 @@ struct cert_validator_t {
bool (*validate_online)(cert_validator_t *this, certificate_t *subject,
certificate_t *issuer, u_int pathlen, bool anchor,
auth_cfg_t *auth);
/**
* Do OCSP checking for the given certificate.
*
* @param subject subject certificate to check
* @param issuer issuer of subject
* @return a valid OCSP response, NULL otherwise
*/
certificate_t* (*ocsp)(cert_validator_t *this, certificate_t *subject,
certificate_t *issuer);
};
#endif /** CERT_VALIDATOR_H_ @}*/
@@ -1352,6 +1352,33 @@ METHOD(credential_manager_t, get_private, private_key_t*,
return private;
}
METHOD(credential_manager_t, get_ocsp, certificate_t*,
private_credential_manager_t *this, certificate_t *subject,
certificate_t *issuer)
{
cert_validator_t *validator;
enumerator_t *enumerator;
certificate_t *response = NULL;
this->lock->read_lock(this->lock);
enumerator = this->validators->create_enumerator(this->validators);
while (enumerator->enumerate(enumerator, &validator))
{
if (validator->ocsp)
{
response = validator->ocsp(validator, subject, issuer);
if (response)
{
break;
}
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
return response;
}
METHOD(credential_manager_t, flush_cache, void,
private_credential_manager_t *this, certificate_type_t type)
{
@@ -1427,6 +1454,7 @@ credential_manager_t *credential_manager_create()
.get_cert = _get_cert,
.get_shared = _get_shared,
.get_private = _get_private,
.get_ocsp = _get_ocsp,
.create_trusted_enumerator = _create_trusted_enumerator,
.create_public_enumerator = _create_public_enumerator,
.flush_cache = _flush_cache,
@@ -149,6 +149,7 @@ struct credential_manager_t {
certificate_t *(*get_cert)(credential_manager_t *this,
certificate_type_t cert, key_type_t key,
identification_t *id, bool trusted);
/**
* Get the best matching shared key for two IDs.
*
@@ -176,6 +177,16 @@ struct credential_manager_t {
private_key_t* (*get_private)(credential_manager_t *this, key_type_t type,
identification_t *id, auth_cfg_t *auth);
/**
* Get an OCSP response for the given certificate.
*
* @param subject subject certificate to check
* @param issuer issuer of subject
* @return a valid OCSP response, NULL otherwise
*/
certificate_t* (*get_ocsp)(credential_manager_t *this, certificate_t *subject,
certificate_t *issuer);
/**
* Create an enumerator over trusted certificates.
*
@@ -141,6 +141,25 @@ static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
return response;
}
/**
* Verify OCSP response signature
*/
static bool verify_ocsp_sig(certificate_t *subject, certificate_t *issuer,
bool cached)
{
if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
{
if (!cached)
{
DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
issuer->get_subject(issuer));
}
return TRUE;
}
DBG1(DBG_CFG, "OCSP response verification failed, invalid signature");
return FALSE;
}
/**
* check the signature of an OCSP response
*/
@@ -182,18 +201,11 @@ static bool verify_ocsp(ocsp_response_t *response, certificate_t *ca,
}
}
found = TRUE;
if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
verified = verify_ocsp_sig(subject, issuer, cached);
if (verified)
{
if (!cached)
{
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);
@@ -212,18 +224,11 @@ static bool verify_ocsp(ocsp_response_t *response, certificate_t *ca,
issuer->get_validity(issuer, NULL, NULL, NULL))
{
found = TRUE;
if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
verified = verify_ocsp_sig(subject, issuer, cached);
if (verified)
{
if (!cached)
{
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);
@@ -323,7 +328,8 @@ static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
* validate a x509 certificate using OCSP
*/
static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
auth_cfg_t *auth, u_int timeout)
auth_cfg_t *auth, u_int timeout,
certificate_t **response)
{
enumerator_t *enumerator;
cert_validation_t valid = VALIDATION_SKIPPED;
@@ -409,7 +415,15 @@ static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
{ /* successful OCSP check fulfills also CRL constraint */
auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
}
DESTROY_IF(best);
if (response)
{
*response = best;
}
else
{
DESTROY_IF(best);
}
return valid;
}
@@ -866,7 +880,8 @@ METHOD(cert_validator_t, validate_online, bool,
if (enable_ocsp)
{
switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth, timeout))
switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth, timeout,
NULL))
{
case VALIDATION_GOOD:
DBG1(DBG_CFG, "certificate status is good");
@@ -927,6 +942,47 @@ METHOD(cert_validator_t, validate_online, bool,
return TRUE;
}
METHOD (cert_validator_t, ocsp, certificate_t *,
private_revocation_validator_t *this, certificate_t *subject,
certificate_t *issuer)
{
certificate_t *response = NULL;
auth_cfg_t *auth;
bool enable_ocsp;
u_int timeout;
this->lock->lock(this->lock);
enable_ocsp = this->enable_ocsp;
timeout = this->timeout;
this->lock->unlock(this->lock);
if (enable_ocsp &&
subject->get_type(subject) == CERT_X509 &&
issuer->get_type(issuer) == CERT_X509)
{
DBG1(DBG_CFG, "checking OCSP status of \"%Y\"",
subject->get_subject(subject));
auth = auth_cfg_create();
switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth, timeout,
&response))
{
case VALIDATION_GOOD:
case VALIDATION_ON_HOLD:
case VALIDATION_REVOKED:
break;
case VALIDATION_STALE:
case VALIDATION_SKIPPED:
case VALIDATION_FAILED:
DESTROY_IF(response);
response = NULL;
break;
}
auth->destroy(auth);
}
return response;
}
METHOD(revocation_validator_t, reload, void,
private_revocation_validator_t *this)
{
@@ -974,6 +1030,7 @@ revocation_validator_t *revocation_validator_create()
INIT(this,
.public = {
.validator.validate_online = _validate_online,
.validator.ocsp = _ocsp,
.reload = _reload,
.destroy = _destroy,
},