ike-sa: Add method to verify certificates in completed authentication rounds
authorTobias Brunner <tobias@strongswan.org>
Tue, 27 Oct 2015 16:25:22 +0000 (17:25 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 10 Mar 2016 10:07:14 +0000 (11:07 +0100)
src/libcharon/sa/ike_sa.c
src/libcharon/sa/ike_sa.h

index b07ff0e..3384c02 100644 (file)
@@ -58,6 +58,7 @@
 #include <sa/ikev2/tasks/ike_auth_lifetime.h>
 #include <sa/ikev2/tasks/ike_reauth_complete.h>
 #include <sa/ikev2/tasks/ike_redirect.h>
+#include <credentials/sets/auth_cfg_wrapper.h>
 
 #ifdef ME
 #include <sa/ikev2/tasks/ike_me.h>
@@ -462,6 +463,107 @@ METHOD(ike_sa_t, create_auth_cfg_enumerator, enumerator_t*,
        return array_create_enumerator(this->other_auths);
 }
 
+METHOD(ike_sa_t, verify_peer_certificate, bool,
+       private_ike_sa_t *this)
+{
+       enumerator_t *e1, *e2, *certs;
+       auth_cfg_t *cfg, *cfg_done;
+       certificate_t *peer, *cert;
+       public_key_t *key;
+       auth_cfg_t *auth;
+       auth_cfg_wrapper_t *wrapper;
+       time_t not_before, not_after;
+       bool valid = TRUE, found;
+
+       if (this->state != IKE_ESTABLISHED)
+       {
+               DBG1(DBG_IKE, "unable to verify peer certificate in state %N",
+                        ike_sa_state_names, this->state);
+               return FALSE;
+       }
+
+       if (lib->settings->get_bool(lib->settings,
+                                                               "%s.flush_auth_cfg", FALSE, lib->ns))
+       {
+               DBG1(DBG_IKE, "unable to verify peer certificate as authentication "
+                        "information has been flushed");
+               return FALSE;
+       }
+
+       e1 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, FALSE);
+       e2 = array_create_enumerator(this->other_auths);
+       while (e1->enumerate(e1, &cfg))
+       {
+               if (!e2->enumerate(e2, &cfg_done))
+               {       /* this should not happen as the authentication should never have
+                        * succeeded */
+                       valid = FALSE;
+                       break;
+               }
+               if ((uintptr_t)cfg_done->get(cfg_done,
+                                                                        AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_PUBKEY)
+               {
+                       continue;
+               }
+               peer = cfg_done->get(cfg_done, AUTH_RULE_SUBJECT_CERT);
+               if (!peer)
+               {
+                       DBG1(DBG_IKE, "no subject certificate found, skipping certificate "
+                                "verification");
+                       continue;
+               }
+               if (!peer->get_validity(peer, NULL, &not_before, &not_after))
+               {
+                       /* FIXME: theoretically we could find a newer cert with the same
+                        * identity and public key below...but it's not the cert used by
+                        * the peer during the original authentication so... */
+                       DBG1(DBG_IKE, "peer certificate invalid (valid from %T to %T)",
+                                &not_before, FALSE, &not_after, FALSE);
+                       valid = FALSE;
+                       break;
+               }
+               key = peer->get_public_key(peer);
+               if (!key)
+               {
+                       DBG1(DBG_IKE, "unable to retrieve public key, skipping certificate "
+                                "verification");
+                       continue;
+               }
+               DBG1(DBG_IKE, "verifying peer certificate");
+               /* serve received certificates */
+               wrapper = auth_cfg_wrapper_create(cfg_done);
+               lib->credmgr->add_local_set(lib->credmgr, &wrapper->set, FALSE);
+               certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
+                                                       key->get_type(key), peer->get_subject(peer), TRUE);
+               key->destroy(key);
+
+               found = FALSE;
+               while (certs->enumerate(certs, &cert, &auth))
+               {
+                       if (peer->equals(peer, cert))
+                       {
+                               cfg_done->add(cfg_done, AUTH_RULE_CERT_VALIDATION_SUSPENDED,
+                                                         FALSE);
+                               cfg_done->merge(cfg_done, auth, FALSE);
+                               valid = cfg_done->complies(cfg_done, cfg, TRUE);
+                               found = TRUE;
+                               break;
+                       }
+               }
+               certs->destroy(certs);
+               lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
+               wrapper->destroy(wrapper);
+               if (!found || !valid)
+               {
+                       valid = FALSE;
+                       break;
+               }
+       }
+       e1->destroy(e1);
+       e2->destroy(e2);
+       return valid;
+}
+
 /**
  * Flush the stored authentication round information
  */
@@ -2750,6 +2852,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                        .set_peer_cfg = _set_peer_cfg,
                        .get_auth_cfg = _get_auth_cfg,
                        .create_auth_cfg_enumerator = _create_auth_cfg_enumerator,
+                       .verify_peer_certificate = _verify_peer_certificate,
                        .add_auth_cfg = _add_auth_cfg,
                        .get_proposal = _get_proposal,
                        .set_proposal = _set_proposal,
index 158a690..0f47ad3 100644 (file)
@@ -522,6 +522,14 @@ struct ike_sa_t {
        enumerator_t* (*create_auth_cfg_enumerator)(ike_sa_t *this, bool local);
 
        /**
+        * Verify the trustchains (validity, revocation) in completed public key
+        * auth rounds.
+        *
+        * @return                              TRUE if certificates were valid, FALSE otherwise
+        */
+       bool (*verify_peer_certificate)(ike_sa_t *this);
+
+       /**
         * Get the selected proposal of this IKE_SA.
         *
         * @return                              selected proposal