]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Add support for IKEv2 OCSP extensions (RFC 4806)
authorJean-François Hren <jean-francois.hren@stormshield.eu>
Wed, 29 Nov 2023 15:51:48 +0000 (16:51 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 13 Mar 2024 14:10:50 +0000 (15:10 +0100)
Closes strongswan/strongswan#2016

Co-authored-by: Tobias Brunner <tobias@strongswan.org>
15 files changed:
src/libcharon/config/ike_cfg.c
src/libcharon/config/ike_cfg.h
src/libcharon/config/peer_cfg.c
src/libcharon/config/peer_cfg.h
src/libcharon/encoding/payloads/cert_payload.c
src/libcharon/encoding/payloads/certreq_payload.c
src/libcharon/plugins/vici/vici_config.c
src/libcharon/sa/ike_sa.h
src/libcharon/sa/ikev2/tasks/ike_cert_post.c
src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
src/libstrongswan/credentials/cert_validator.h
src/libstrongswan/credentials/credential_manager.c
src/libstrongswan/credentials/credential_manager.h
src/libstrongswan/plugins/revocation/revocation_validator.c
src/swanctl/swanctl.opt

index 792f8022a6c1856f1c1632464a832a7495d6960d..84959934c601d1ca65afdc25640501f47efd06df 100644 (file)
@@ -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,
index 873bd0d65eef20c0b37f3e0757d17923b2eec76c..f54824219258512cfe08c667d62e37a33fda8e43 100644 (file)
@@ -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 */
index f08eef502ebfaa8d649f7a73d438266bdad5b807..2fb3ccfd6aa5c5e6de6aab4b478b1a8f5fe51696 100644 (file)
@@ -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,
index 6b08343394fefee30c5533e1a2d01b9478de5614..80f3584a524e8208bc8383eddb5ff457a481b449 100644 (file)
@@ -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 */
index e18a70d664b8286f527b0a33ba4c4ee6df4165ba..73bfc55c72a2f9435351594d3754c8ea67e46698 100644 (file)
@@ -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));
index 0c68d8ce115554d9a242a79e028d6ad5bcc79fac..1b3c8cff245ba10cd86f867ae4027a9f9151bed8 100644 (file)
@@ -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);
index 522122562c3cd8cbdfe583552ce3fd9ecd9759b3..838fc360606b621aff8ce67930bf0e098e49e671 100644 (file)
@@ -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,
index 7d1e331521ee384905abdbaceab75265eb9a949b..47c37ff6422280abcc0f5b8b2799db1b1cdc75b0 100644 (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),
 };
 
 /**
index 1474b9dae3e107ff233dc06d245fc15b1ee122e3..5edc4ff9e0cef05ff6b0b6582eed101ff1452057 100644 (file)
@@ -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,
index 96d4477bbad64f4f95a36e2899dfb4283c0aa59a..6683c33ba4d470f8100860931a7224be6c61d42e 100644 (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))
+                       {
+                               add_certreqs(&req, auth);
+                       }
+                       enumerator->destroy(enumerator);
+               }
 
-       /* 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))
+               if (!req)
                {
-                       add_certreqs(&req, auth);
+                       /* 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;
-               }
        }
 }
 
index e6ea2465f581f2acae9717ef59027fce089cfc5c..40ad153c7b4fd93d454569b23326ff954f7c0999 100644 (file)
@@ -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_ @}*/
index d66a6e9a4a174797edcc8ef5201bcd99eba02275..f3b0af0cf6cbe259100f6d06be91519eb10cd359 100644 (file)
@@ -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,
index 1f40122dbfd13e584827be7cc82a50597b8a7a08..21d1e1f5f0274f354913703ba85c563eed37bb6e 100644 (file)
@@ -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.
         *
index 4bf2cfb5c6f9ae898228abacf9bc05560d96d2f6..2ef6fdeac8b774cb6d47b911aada7a0f0f5f5572 100644 (file)
@@ -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,
                },
index a1218552b1137491bd07cbd529470ec9b5a91c80..d9fd949ed1aa53d8fe09edd9a52e4f063e295819 100644 (file)
@@ -205,6 +205,19 @@ connections.<conn>.send_cert = ifasked
        certificate payloads altogether, _always_ causes certificate payloads to be
        sent unconditionally whenever certificate authentication is used.
 
+connections.<conn>.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.<conn>.ppk_id =
        String identifying the Postquantum Preshared Key (PPK) to be used.