diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index 792f8022a6..84959934c6 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -96,6 +96,11 @@ struct private_ike_cfg_t { */ bool certreq; + /** + * should we send an OCSP status request? + */ + bool ocsp_certreq; + /** * enforce UDP encapsulation */ @@ -134,6 +139,12 @@ METHOD(ike_cfg_t, send_certreq, bool, return this->certreq; } +METHOD(ike_cfg_t, send_ocsp_certreq, bool, + private_ike_cfg_t *this) +{ + return this->ocsp_certreq; +} + METHOD(ike_cfg_t, force_encap_, bool, private_ike_cfg_t *this) { @@ -388,6 +399,7 @@ METHOD(ike_cfg_t, equals, bool, return this->version == other->version && this->certreq == other->certreq && + this->ocsp_certreq == other->ocsp_certreq && this->force_encap == other->force_encap && this->fragmentation == other->fragmentation && this->childless == other->childless && @@ -587,6 +599,7 @@ ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data) .public = { .get_version = _get_version, .send_certreq = _send_certreq, + .send_ocsp_certreq = _send_ocsp_certreq, .force_encap = _force_encap_, .fragmentation = _fragmentation, .childless = _childless, @@ -611,6 +624,7 @@ ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data) .refcount = 1, .version = data->version, .certreq = !data->no_certreq, + .ocsp_certreq = !data->no_ocsp_certreq, .force_encap = data->force_encap, .fragmentation = data->fragmentation, .childless = data->childless, diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index 873bd0d65e..f548242192 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -210,6 +210,13 @@ struct ike_cfg_t { */ bool (*send_certreq) (ike_cfg_t *this); + /** + * Should we send an OCSP status request in IKE_SA_INIT? + * + * @return OCSP status request sending policy + */ + bool (*send_ocsp_certreq) (ike_cfg_t *this); + /** * Enforce UDP encapsulation by faking NATD notifies? * @@ -288,6 +295,8 @@ struct ike_cfg_create_t { uint16_t remote_port; /** TRUE to not send any certificate requests */ bool no_certreq; + /** TRUE to not send OCSP status requests */ + bool no_ocsp_certreq; /** Enforce UDP encapsulation by faking NATD notify */ bool force_encap; /** Use IKE fragmentation */ diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index f08eef502e..2fb3ccfd6a 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -32,6 +32,13 @@ ENUM(cert_policy_names, CERT_ALWAYS_SEND, CERT_NEVER_SEND, "CERT_NEVER_SEND", ); +ENUM(ocsp_policy_names, OCSP_SEND_BOTH, OCSP_SEND_NEVER, + "OCSP_SEND_BOTH", + "OCSP_SEND_REPLY", + "OCSP_SEND_REQUEST", + "OCSP_SEND_NEVER", +); + ENUM(unique_policy_names, UNIQUE_NEVER, UNIQUE_KEEP, "UNIQUE_NEVER", "UNIQUE_NO", @@ -81,6 +88,11 @@ struct private_peer_cfg_t { */ cert_policy_t cert_policy; + /** + * should we send OCSP status request/response + */ + ocsp_policy_t ocsp_policy; + /** * uniqueness of an IKE_SA */ @@ -495,6 +507,12 @@ METHOD(peer_cfg_t, get_cert_policy, cert_policy_t, return this->cert_policy; } +METHOD(peer_cfg_t, get_ocsp_policy, ocsp_policy_t, + private_peer_cfg_t *this) +{ + return this->ocsp_policy; +} + METHOD(peer_cfg_t, get_unique_policy, unique_policy_t, private_peer_cfg_t *this) { @@ -741,6 +759,7 @@ METHOD(peer_cfg_t, equals, bool, return ( get_ike_version(this) == get_ike_version(other) && this->cert_policy == other->cert_policy && + this->ocsp_policy == other->ocsp_policy && this->unique == other->unique && this->keyingtries == other->keyingtries && this->use_mobike == other->use_mobike && @@ -828,6 +847,7 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .create_child_cfg_enumerator = _create_child_cfg_enumerator, .select_child_cfg = _select_child_cfg, .get_cert_policy = _get_cert_policy, + .get_ocsp_policy = _get_ocsp_policy, .get_unique_policy = _get_unique_policy, .get_keyingtries = _get_keyingtries, .get_rekey_time = _get_rekey_time, @@ -861,6 +881,7 @@ peer_cfg_t *peer_cfg_create(char *name, ike_cfg_t *ike_cfg, .child_cfgs = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .cert_policy = data->cert_policy, + .ocsp_policy = data->ocsp_policy, .unique = data->unique, .keyingtries = data->keyingtries, .rekey_time = data->rekey_time, diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 6b08343394..80f3584a52 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -25,6 +25,7 @@ #define PEER_CFG_H_ typedef enum cert_policy_t cert_policy_t; +typedef enum ocsp_policy_t ocsp_policy_t; typedef enum unique_policy_t unique_policy_t; typedef struct peer_cfg_t peer_cfg_t; typedef struct peer_cfg_create_t peer_cfg_create_t; @@ -61,6 +62,25 @@ enum cert_policy_t { */ extern enum_name_t *cert_policy_names; +/** + * OCSP status request/response sending policy. + */ +enum ocsp_policy_t { + /** request OCSP status and reply to OCSP status requests */ + OCSP_SEND_BOTH = 0, + /** send OCSP status upon OCSP status request */ + OCSP_SEND_REPLY = 1, + /** send OCSP status request */ + OCSP_SEND_REQUEST = 2, + /** never send OCSP status request or response */ + OCSP_SEND_NEVER = 3, +}; + +/** + * enum strings for ocsp_policy_t + */ +extern enum_name_t *ocsp_policy_names; + /** * Uniqueness of an IKE_SA, used to drop multiple connections with one peer. */ @@ -213,6 +233,13 @@ struct peer_cfg_t { */ cert_policy_t (*get_cert_policy) (peer_cfg_t *this); + /** + * Should an OCSP status request/response be sent for this connection? + * + * @return OCSP sending policy + */ + ocsp_policy_t (*get_ocsp_policy) (peer_cfg_t *this); + /** * How to handle uniqueness of IKE_SAs? * @@ -397,6 +424,8 @@ struct peer_cfg_t { struct peer_cfg_create_t { /** Whether to send a certificate payload */ cert_policy_t cert_policy; + /** Whether to send OCSP status request/response */ + ocsp_policy_t ocsp_policy; /** Uniqueness of an IKE_SA */ unique_policy_t unique; /** How many keying tries should be done before giving up */ diff --git a/src/libcharon/encoding/payloads/cert_payload.c b/src/libcharon/encoding/payloads/cert_payload.c index e18a70d664..73bfc55c72 100644 --- a/src/libcharon/encoding/payloads/cert_payload.c +++ b/src/libcharon/encoding/payloads/cert_payload.c @@ -230,6 +230,9 @@ METHOD(cert_payload_t, get_cert, certificate_t*, case ENC_CRL: type = CERT_X509_CRL; break; + case ENC_OCSP_CONTENT: + type = CERT_X509_OCSP_RESPONSE; + break; default: return NULL; } @@ -339,6 +342,9 @@ cert_payload_t *cert_payload_create_from_cert(payload_type_t type, case CERT_X509_AC: this->encoding = ENC_X509_ATTRIBUTE; break; + case CERT_X509_OCSP_RESPONSE: + this->encoding = ENC_OCSP_CONTENT; + break; default: DBG1(DBG_ENC, "embedding %N certificate in payload failed", certificate_type_names, cert->get_type(cert)); diff --git a/src/libcharon/encoding/payloads/certreq_payload.c b/src/libcharon/encoding/payloads/certreq_payload.c index 0c68d8ce11..1b3c8cff24 100644 --- a/src/libcharon/encoding/payloads/certreq_payload.c +++ b/src/libcharon/encoding/payloads/certreq_payload.c @@ -244,6 +244,8 @@ METHOD(certreq_payload_t, get_cert_type, certificate_type_t, { case ENC_X509_SIGNATURE: return CERT_X509; + case ENC_OCSP_CONTENT: + return CERT_X509_OCSP_REQUEST; default: return CERT_ANY; } @@ -302,6 +304,9 @@ certreq_payload_t *certreq_payload_create_type(certificate_type_t type) case CERT_X509: this->encoding = ENC_X509_SIGNATURE; break; + case CERT_X509_OCSP_REQUEST: + this->encoding = ENC_OCSP_CONTENT; + break; default: DBG1(DBG_ENC, "certificate type %N not supported in requests", certificate_type_names, type); diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 522122562c..838fc36060 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -314,6 +314,7 @@ typedef struct { identification_t *ppk_id; bool ppk_required; cert_policy_t send_cert; + ocsp_policy_t ocsp; uint64_t dpd_delay; uint64_t dpd_timeout; fragmentation_t fragmentation; @@ -425,6 +426,7 @@ static void log_peer_data(peer_data_t *data) DBG2(DBG_CFG, " remote_port = %u", data->remote_port); DBG2(DBG_CFG, " send_certreq = %u", data->send_certreq); DBG2(DBG_CFG, " send_cert = %N", cert_policy_names, data->send_cert); + DBG2(DBG_CFG, " ocsp = %N", ocsp_policy_names, data->ocsp); DBG2(DBG_CFG, " ppk_id = %Y", data->ppk_id); DBG2(DBG_CFG, " ppk_required = %u", data->ppk_required); DBG2(DBG_CFG, " mobike = %u", data->mobike); @@ -1678,6 +1680,28 @@ CALLBACK(parse_send_cert, bool, return FALSE; } +/** + * Parse an ocsp_policy_t + */ +CALLBACK(parse_ocsp, bool, + ocsp_policy_t *out, chunk_t v) +{ + enum_map_t map[] = { + { "both", OCSP_SEND_BOTH }, + { "reply", OCSP_SEND_REPLY }, + { "request", OCSP_SEND_REQUEST }, + { "never", OCSP_SEND_NEVER }, + }; + int d; + + if (parse_map(map, countof(map), &d, v)) + { + *out = d; + return TRUE; + } + return FALSE; +} + /** * Parse a unique_policy_t */ @@ -1879,6 +1903,7 @@ CALLBACK(peer_kv, bool, { "childless", parse_childless, &peer->childless }, { "send_certreq", parse_bool, &peer->send_certreq }, { "send_cert", parse_send_cert, &peer->send_cert }, + { "ocsp", parse_ocsp, &peer->ocsp }, { "keyingtries", parse_uint32, &peer->keyingtries }, { "unique", parse_unique, &peer->unique }, { "local_port", parse_uint32, &peer->local_port }, @@ -2498,6 +2523,7 @@ CALLBACK(config_sn, bool, .send_certreq = TRUE, .pull = TRUE, .send_cert = CERT_SEND_IF_ASKED, + .ocsp = OCSP_SEND_REPLY, .version = IKE_ANY, .remote_port = IKEV2_UDP_PORT, .fragmentation = FRAGMENTATION_YES, @@ -2646,6 +2672,8 @@ CALLBACK(config_sn, bool, .remote = peer.remote_addrs, .remote_port = peer.remote_port, .no_certreq = !peer.send_certreq, + .no_ocsp_certreq = peer.ocsp != OCSP_SEND_BOTH && + peer.ocsp != OCSP_SEND_REQUEST, .force_encap = peer.encap, .fragmentation = peer.fragmentation, .childless = peer.childless, @@ -2655,6 +2683,7 @@ CALLBACK(config_sn, bool, cfg = (peer_cfg_create_t){ .cert_policy = peer.send_cert, + .ocsp_policy = peer.ocsp, .unique = peer.unique, .keyingtries = peer.keyingtries, .rekey_time = peer.rekey_time, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 7d1e331521..47c37ff642 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -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), }; /** diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_post.c b/src/libcharon/sa/ikev2/tasks/ike_cert_post.c index 1474b9dae3..5edc4ff9e0 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_cert_post.c +++ b/src/libcharon/sa/ikev2/tasks/ike_cert_post.c @@ -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, diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c index 96d4477bba..6683c33ba4 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c +++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c @@ -15,6 +15,8 @@ * for more details. */ +#include + #include "ike_cert_pre.h" #include @@ -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; - } } } diff --git a/src/libstrongswan/credentials/cert_validator.h b/src/libstrongswan/credentials/cert_validator.h index e6ea2465f5..40ad153c7b 100644 --- a/src/libstrongswan/credentials/cert_validator.h +++ b/src/libstrongswan/credentials/cert_validator.h @@ -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_ @}*/ diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c index d66a6e9a4a..f3b0af0cf6 100644 --- a/src/libstrongswan/credentials/credential_manager.c +++ b/src/libstrongswan/credentials/credential_manager.c @@ -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, diff --git a/src/libstrongswan/credentials/credential_manager.h b/src/libstrongswan/credentials/credential_manager.h index 1f40122dbf..21d1e1f5f0 100644 --- a/src/libstrongswan/credentials/credential_manager.h +++ b/src/libstrongswan/credentials/credential_manager.h @@ -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. * diff --git a/src/libstrongswan/plugins/revocation/revocation_validator.c b/src/libstrongswan/plugins/revocation/revocation_validator.c index 4bf2cfb5c6..2ef6fdeac8 100644 --- a/src/libstrongswan/plugins/revocation/revocation_validator.c +++ b/src/libstrongswan/plugins/revocation/revocation_validator.c @@ -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, }, diff --git a/src/swanctl/swanctl.opt b/src/swanctl/swanctl.opt index a1218552b1..d9fd949ed1 100644 --- a/src/swanctl/swanctl.opt +++ b/src/swanctl/swanctl.opt @@ -205,6 +205,19 @@ connections..send_cert = ifasked certificate payloads altogether, _always_ causes certificate payloads to be sent unconditionally whenever certificate authentication is used. +connections..ocsp = reply + Request and send OCSP status in certificate request or certificate payloads + (_never_, _reply, _request_ or _both_). + + Send OCSP status requests in certificate request payloads and/or send OCSP + status response in certificate payloads when using certificate + authentication. With the default of _reply_ the daemon sends OCSP status + responses in certificate payloads if an OCSP status request has been + received in a certificate request, _never_ disables sending of OCSP status + requests and responses altogether, _request_ causes OCSP status requests in + certificate request payloads to be sent whenever certificate authentication + is used, _both_ combines _reply_ and _request_. + connections..ppk_id = String identifying the Postquantum Preshared Key (PPK) to be used.