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
+5
View File
@@ -250,6 +250,11 @@ enum ike_condition_t {
* All authentication rounds have been completed successfully
*/
COND_AUTHENTICATED = (1<<14),
/**
* An OCSP status request was received
*/
COND_OCSP_REQUEST = (1<<15),
};
/**
@@ -215,6 +215,66 @@ static void add_attribute_certs(private_ike_cert_post_t *this,
}
}
/**
* Build CERT payload with OCSP status for the given cert
*/
static cert_payload_t *build_cert_ocsp_payload(certificate_t *cert,
certificate_t *issuer)
{
certificate_t *response;
cert_payload_t *payload;
response = lib->credmgr->get_ocsp(lib->credmgr, cert, issuer);
if (!response)
{
DBG2(DBG_IKE, "no OCSP status for certificate \"%Y\"",
cert->get_subject(cert));
return NULL;
}
payload = cert_payload_create_from_cert(PLV2_CERTIFICATE, response);
response->destroy(response);
return payload;
}
/**
* Add subject certificate and intermediate CA certificates OCSP status to message
*/
static void add_cert_ocsp(private_ike_cert_post_t *this, auth_cfg_t *auth,
message_t *message)
{
auth_rule_t type;
cert_payload_t *payload;
certificate_t *cert, *issuer;
enumerator_t *enumerator;
cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
if (!cert)
{
return;
}
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &type, &issuer))
{
if (type == AUTH_RULE_CA_CERT || type == AUTH_RULE_IM_CERT)
{
payload = build_cert_ocsp_payload(cert, issuer);
if (payload)
{
DBG1(DBG_IKE, "sending OCSP status for certificate \"%Y\"",
cert->get_subject(cert));
message->add_payload(message, (payload_t*)payload);
}
if (type == AUTH_RULE_CA_CERT)
{
break;
}
cert = issuer;
}
}
enumerator->destroy(enumerator);
}
/**
* add certificates to message
*/
@@ -250,6 +310,23 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message)
}
break;
}
switch (peer_cfg->get_ocsp_policy(peer_cfg))
{
case OCSP_SEND_NEVER:
case OCSP_SEND_REQUEST:
break;
case OCSP_SEND_REPLY:
case OCSP_SEND_BOTH:
if (this->ike_sa->has_condition(this->ike_sa, COND_OCSP_REQUEST) &&
!this->ike_sa->has_condition(this->ike_sa,
COND_ONLINE_VALIDATION_SUSPENDED))
{
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
add_cert_ocsp(this, auth, message);
}
break;
}
}
METHOD(task_t, build_i, status_t,
+142 -28
View File
@@ -15,6 +15,8 @@
* for more details.
*/
#include <time.h>
#include "ike_cert_pre.h"
#include <daemon.h>
@@ -59,9 +61,51 @@ static void process_certreq(private_ike_cert_pre_t *this,
certreq_payload_t *certreq, auth_cfg_t *auth)
{
enumerator_t *enumerator;
u_int unknown = 0;
u_int unknown = 0, known = 0;
chunk_t keyid;
if (certreq->get_cert_type(certreq) == CERT_X509_OCSP_REQUEST)
{
this->ike_sa->set_condition(this->ike_sa, COND_OCSP_REQUEST, TRUE);
enumerator = certreq->create_keyid_enumerator(certreq);
while (enumerator->enumerate(enumerator, &keyid))
{
identification_t *id;
certificate_t *cert;
id = identification_create_from_encoding(ID_KEY_ID, keyid);
cert = lib->credmgr->get_cert(lib->credmgr,
CERT_X509, KEY_ANY, id, TRUE);
if (cert)
{
DBG1(DBG_IKE, "received OCSP cert request claiming trust "
"for \"%Y\"", cert->get_subject(cert));
cert->destroy(cert);
known++;
}
else
{
DBG2(DBG_IKE, "received OCSP cert request claiming trust for "
"unknown certificate with keyid %Y", id);
unknown++;
}
id->destroy(id);
}
if (unknown)
{
DBG1(DBG_IKE, "received OCSP cert request with %u unknown trusted "
"certificates", unknown);
}
else if (!known)
{
DBG1(DBG_IKE, "received empty OCSP cert request");
}
enumerator->destroy(enumerator);
return;
}
this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE);
if (certreq->get_cert_type(certreq) != CERT_X509)
@@ -255,6 +299,30 @@ static void process_crl(cert_payload_t *payload, auth_cfg_t *auth)
}
}
/**
* Process an OCSP certificate payload
*/
static void process_ocsp(cert_payload_t *payload, auth_cfg_t *auth,
ike_cfg_t *ike_cfg)
{
certificate_t *cert;
if (!ike_cfg->send_ocsp_certreq(ike_cfg))
{
DBG1(DBG_IKE, "received OCSP response, but we didn't request any, "
"ignore");
return;
}
cert = payload->get_cert(payload);
if (cert)
{
DBG1(DBG_IKE, "received OCSP response issued by \"%Y\"",
cert->get_issuer(cert));
auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert);
}
}
/**
* Process an attribute certificate payload
*/
@@ -318,6 +386,10 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message)
case ENC_CRL:
process_crl(cert_payload, auth);
break;
case ENC_OCSP_CONTENT:
process_ocsp(cert_payload, auth,
this->ike_sa->get_ike_cfg(this->ike_sa));
break;
case ENC_X509_ATTRIBUTE:
process_ac(cert_payload, auth);
break;
@@ -329,7 +401,6 @@ static void process_certs(private_ike_cert_pre_t *this, message_t *message)
case ENC_SPKI:
case ENC_RAW_RSA_KEY:
case ENC_X509_HASH_AND_URL_BUNDLE:
case ENC_OCSP_CONTENT:
default:
DBG1(DBG_ENC, "certificate encoding %N not supported",
cert_encoding_names, encoding);
@@ -403,6 +474,36 @@ static void add_certreqs(certreq_payload_t **req, auth_cfg_t *auth)
enumerator->destroy(enumerator);
}
/**
* add the keyid of a self-signed OCSP signer to the certificate request payload
*/
static void add_certreq_ocsp(certreq_payload_t *req, certificate_t *cert)
{
public_key_t *public;
chunk_t keyid;
x509_t *x509 = (x509_t*)cert;
if (cert->get_type(cert) != CERT_X509 ||
!(x509->get_flags(x509) & X509_OCSP_SIGNER &&
x509->get_flags(x509) & X509_SELF_SIGNED))
{
/* no self-signed OCSP-signer cert, skip */
return;
}
public = cert->get_public_key(cert);
if (!public)
{
return;
}
if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &keyid))
{
req->add_keyid(req, keyid);
DBG1(DBG_IKE, "sending OCSP cert request with self-signed "
"OCSP-signer \"%Y\"", cert->get_subject(cert));
}
public->destroy(public);
}
/**
* build certificate requests
*/
@@ -416,46 +517,59 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message)
certreq_payload_t *req = NULL;
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
if (!ike_cfg->send_certreq(ike_cfg))
if (ike_cfg->send_certreq(ike_cfg))
{
return;
}
/* check if we require a specific CA for that peer */
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
if (peer_cfg)
{
enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
while (enumerator->enumerate(enumerator, &auth))
/* check if we require a specific CA for that peer */
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
if (peer_cfg)
{
add_certreqs(&req, auth);
enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
while (enumerator->enumerate(enumerator, &auth))
{
add_certreqs(&req, auth);
}
enumerator->destroy(enumerator);
}
if (!req)
{
/* otherwise add all trusted CA certificates */
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
CERT_ANY, KEY_ANY, NULL, TRUE);
while (enumerator->enumerate(enumerator, &cert))
{
add_certreq(&req, cert);
}
enumerator->destroy(enumerator);
}
if (req)
{
message->add_payload(message, (payload_t*)req);
if (lib->settings->get_bool(lib->settings,
"%s.hash_and_url", FALSE, lib->ns))
{
message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED,
chunk_empty);
this->do_http_lookup = TRUE;
}
}
enumerator->destroy(enumerator);
}
if (!req)
if (ike_cfg->send_ocsp_certreq(ike_cfg))
{
/* otherwise add all trusted CA certificates */
req = certreq_payload_create_type(CERT_X509_OCSP_REQUEST);
enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
CERT_ANY, KEY_ANY, NULL, TRUE);
while (enumerator->enumerate(enumerator, &cert))
{
add_certreq(&req, cert);
add_certreq_ocsp(req, cert);
}
enumerator->destroy(enumerator);
}
if (req)
{
message->add_payload(message, (payload_t*)req);
if (lib->settings->get_bool(lib->settings,
"%s.hash_and_url", FALSE, lib->ns))
{
message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED,
chunk_empty);
this->do_http_lookup = TRUE;
}
}
}