diff options
author | Tobias Brunner <tobias@strongswan.org> | 2016-03-10 11:48:12 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2016-03-10 11:50:57 +0100 |
commit | f893b47e3d854f4051b8d1549cdc0d6d661aa235 (patch) | |
tree | 9d3cf491012ee2cce5426c219e9f07d187316f3f | |
parent | 819da83fccf99acf7af1ed2bf61a498425c375e1 (diff) | |
parent | b4337c5b027871d6bb076b85d9a8699f86a74fa6 (diff) | |
download | strongswan-f893b47e3d854f4051b8d1549cdc0d6d661aa235.tar.bz2 strongswan-f893b47e3d854f4051b8d1549cdc0d6d661aa235.tar.xz |
Merge branch 'mbb-reauth-online-revocation'
With these changes initiators of make-before-break reauthentications
suspend online revocation checks until after the new IKE_SA and all
CHILD_SAs are established. See f1cbacc5d1be for details why that's
necessary.
28 files changed, 477 insertions, 14 deletions
@@ -11,6 +11,15 @@ strongswan-5.4.0 constraints against IKEv2 authentication in rightauth, which allows the use of different signature schemes for trustchain verification and authentication. +- The initiator of an IKEv2 make-before-break reauthentication now suspends + online certificate revocation checks (OCSP, CRLs) until the new IKE_SA and all + CHILD_SAs are established. This is required if the checks are done over the + CHILD_SA established with the new IKE_SA. This is not possible until the + initiator installs this SA and that only happens after the authentication is + completed successfully. So we suspend the checks during the reauthentication + and do them afterwards, if they fail the IKE_SA is closed. This change has no + effect on the behavior during the authentication of the initial IKE_SA. + - For the vici plugin a Vici:Session Perl CPAN module has been added to allow Perl applications to control and/or monitor the IKE daemon using the VICI interface, similar to the existing Python egg or Ruby gem. diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 0b09d100d..8f8c96588 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -111,7 +111,8 @@ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ -sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h +sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h \ +sa/ikev2/tasks/ike_verify_peer_cert.c sa/ikev2/tasks/ike_verify_peer_cert.h libcharon_la_SOURCES += \ sa/ikev1/keymat_v1.c sa/ikev1/keymat_v1.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 062b96aff..61e57c1cb 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -111,7 +111,8 @@ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ -sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h +sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h \ +sa/ikev2/tasks/ike_verify_peer_cert.c sa/ikev2/tasks/ike_verify_peer_cert.h endif if USE_IKEV1 diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index b07ff0e74..bcbff3211 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -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> @@ -482,6 +483,113 @@ static void flush_auth_cfgs(private_ike_sa_t *this) } } +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 (!this->flush_auth_cfg && + lib->settings->get_bool(lib->settings, + "%s.flush_auth_cfg", FALSE, lib->ns)) + { /* we can do this check only once if auth configs are flushed */ + DBG1(DBG_IKE, "unable to verify peer certificate as authentication " + "information has been flushed"); + return FALSE; + } + this->public.set_condition(&this->public, COND_ONLINE_VALIDATION_SUSPENDED, + 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, ¬_before, ¬_after)) + { + DBG1(DBG_IKE, "peer certificate invalid (valid from %T to %T)", + ¬_before, FALSE, ¬_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); + + if (this->flush_auth_cfg) + { + this->flush_auth_cfg = FALSE; + flush_auth_cfgs(this); + } + return valid; +} + METHOD(ike_sa_t, get_proposal, proposal_t*, private_ike_sa_t *this) { @@ -1441,9 +1549,14 @@ METHOD(ike_sa_t, process_message, status_t, status = this->task_manager->process_message(this->task_manager, message); if (this->flush_auth_cfg && this->state == IKE_ESTABLISHED) { - /* authentication completed */ - this->flush_auth_cfg = FALSE; - flush_auth_cfgs(this); + /* authentication completed but if the online validation is suspended we + * need the auth cfgs until we did the delayed verification, we flush + * them afterwards */ + if (!has_condition(this, COND_ONLINE_VALIDATION_SUSPENDED)) + { + this->flush_auth_cfg = FALSE; + flush_auth_cfgs(this); + } } return status; } @@ -2750,6 +2863,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, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 158a690df..836360e3c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -217,6 +217,11 @@ enum ike_condition_t { * This IKE_SA has been redirected */ COND_REDIRECTED = (1<<11), + + /** + * Online certificate revocation checking is suspended for this IKE_SA + */ + COND_ONLINE_VALIDATION_SUSPENDED = (1<<12), }; /** @@ -522,6 +527,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 diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c index 793e6d5c1..eee7dd10b 100644 --- a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c +++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c @@ -173,7 +173,7 @@ METHOD(authenticator_t, process, status_t, sig = sig_payload->get_hash(sig_payload); auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, this->type, - id, auth); + id, auth, TRUE); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, scheme, hash, sig)) diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 110c50973..04ccd4f4f 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -365,6 +365,7 @@ METHOD(authenticator_t, process, status_t, status_t status = NOT_FOUND; keymat_v2_t *keymat; const char *reason = "unsupported"; + bool online; auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); if (!auth_payload) @@ -408,8 +409,10 @@ METHOD(authenticator_t, process, status_t, return FAILED; } auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); + online = !this->ike_sa->has_condition(this->ike_sa, + COND_ONLINE_VALIDATION_SUSPENDED); enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - key_type, id, auth); + key_type, id, auth, online); while (enumerator->enumerate(enumerator, &public, ¤t_auth)) { if (public->verify(public, scheme, octets, auth_data)) @@ -421,6 +424,10 @@ METHOD(authenticator_t, process, status_t, auth->merge(auth, current_auth, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); auth->add(auth, AUTH_RULE_IKE_SIGNATURE_SCHEME, (uintptr_t)scheme); + if (!online) + { + auth->add(auth, AUTH_RULE_CERT_VALIDATION_SUSPENDED, TRUE); + } break; } else diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 8ed86302b..c2f972ab1 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -35,6 +35,7 @@ #include <sa/ikev2/tasks/ike_config.h> #include <sa/ikev2/tasks/ike_dpd.h> #include <sa/ikev2/tasks/ike_vendor.h> +#include <sa/ikev2/tasks/ike_verify_peer_cert.h> #include <sa/ikev2/tasks/child_create.h> #include <sa/ikev2/tasks/child_rekey.h> #include <sa/ikev2/tasks/child_delete.h> @@ -527,6 +528,11 @@ METHOD(task_manager_t, initiate, status_t, exchange = INFORMATIONAL; break; } + if (activate_task(this, TASK_IKE_VERIFY_PEER_CERT)) + { + exchange = INFORMATIONAL; + break; + } case IKE_REKEYING: if (activate_task(this, TASK_IKE_DELETE)) { @@ -624,7 +630,7 @@ METHOD(task_manager_t, initiate, status_t, if (this->initiating.type == EXCHANGE_TYPE_UNDEFINED) { message->destroy(message); - return SUCCESS; + return initiate(this); } if (!generate_message(this, message, &this->initiating.packets)) @@ -1650,8 +1656,12 @@ static void trigger_mbb_reauth(private_task_manager_t *this) } enumerator->destroy(enumerator); + /* suspend online revocation checking until the SA is established */ + new->set_condition(new, COND_ONLINE_VALIDATION_SUSPENDED, TRUE); + if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME) { + new->queue_task(new, (task_t*)ike_verify_peer_cert_create(new)); new->queue_task(new, (task_t*)ike_reauth_complete_create(new, this->ike_sa->get_id(this->ike_sa))); charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); diff --git a/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c new file mode 100644 index 000000000..069d51d00 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_verify_peer_cert.h" + +#include <daemon.h> +#include <sa/ikev2/tasks/ike_delete.h> + +typedef struct private_ike_verify_peer_cert_t private_ike_verify_peer_cert_t; + +/** + * Private members + */ +struct private_ike_verify_peer_cert_t { + + /** + * Public methods and task_t interface. + */ + ike_verify_peer_cert_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Child ike_delete task, if necessary + */ + ike_delete_t *ike_delete; +}; + +METHOD(task_t, build_i, status_t, + private_ike_verify_peer_cert_t *this, message_t *message) +{ + if (!this->ike_sa->verify_peer_certificate(this->ike_sa)) + { + DBG1(DBG_IKE, "peer certificate verification failed, deleting SA"); + this->ike_delete = ike_delete_create(this->ike_sa, TRUE); + return this->ike_delete->task.build(&this->ike_delete->task, message); + } + DBG1(DBG_IKE, "peer certificate successfully verified"); + message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED); + return SUCCESS; +} + +METHOD(task_t, process_i, status_t, + private_ike_verify_peer_cert_t *this, message_t *message) +{ + if (this->ike_delete) + { + this->ike_delete->task.process(&this->ike_delete->task, message); + /* try to reestablish the IKE_SA and all children */ + this->ike_sa->reestablish(this->ike_sa); + } + return DESTROY_ME; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_verify_peer_cert_t *this) +{ + return TASK_IKE_VERIFY_PEER_CERT; +} + +METHOD(task_t, migrate, void, + private_ike_verify_peer_cert_t *this, ike_sa_t *ike_sa) +{ + if (this->ike_delete) + { + this->ike_delete->task.migrate(&this->ike_delete->task, ike_sa); + } + this->ike_sa = ike_sa; +} + +METHOD(task_t, destroy, void, + private_ike_verify_peer_cert_t *this) +{ + if (this->ike_delete) + { + this->ike_delete->task.destroy(&this->ike_delete->task); + } + free(this); +} + +/* + * Described in header. + */ +ike_verify_peer_cert_t *ike_verify_peer_cert_create(ike_sa_t *ike_sa) +{ + private_ike_verify_peer_cert_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .migrate = _migrate, + .build = _build_i, + .process = _process_i, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + ); + + return &this->public; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h new file mode 100644 index 000000000..3d9aae0b3 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup ike_verify_peer_cert ike_verify_peer_cert + * @{ @ingroup tasks_v2 + */ + +#ifndef IKE_VERIFY_PEER_CERT_H_ +#define IKE_VERIFY_PEER_CERT_H_ + +typedef struct ike_verify_peer_cert_t ike_verify_peer_cert_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/task.h> + +/** + * Task of type ike_verify_peer_cert, verifies a peer's certificate. + * + * This task (re-)verifies the peer's certificate explicitly including online + * OCSP and CRL checks. + */ +struct ike_verify_peer_cert_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new ike_verify_peer_cert task. + * + * This task is initiator only. + * + * @param ike_sa IKE_SA this task works for + * @return ike_verify_peer_cert task to handle by the task_manager + */ +ike_verify_peer_cert_t *ike_verify_peer_cert_create(ike_sa_t *ike_sa); + +#endif /** IKE_VERIFY_PEER_CERT_H_ @}*/ diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c index 4bd2ba221..405eda66b 100644 --- a/src/libcharon/sa/task.c +++ b/src/libcharon/sa/task.c @@ -29,6 +29,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST, "IKE_REAUTH", "IKE_REAUTH_COMPLETE", "IKE_REDIRECT", + "IKE_VERIFY_PEER_CERT", "IKE_DELETE", "IKE_DPD", "IKE_VENDOR", diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index b2e9d8886..31d70fb3b 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -59,6 +59,8 @@ enum task_type_t { TASK_IKE_REAUTH_COMPLETE, /** redirect an active IKE_SA */ TASK_IKE_REDIRECT, + /** verify a peer's certificate */ + TASK_IKE_VERIFY_PEER_CERT, /** delete an IKE_SA */ TASK_IKE_DELETE, /** liveness check */ diff --git a/src/libstrongswan/credentials/auth_cfg.c b/src/libstrongswan/credentials/auth_cfg.c index 5466a2e08..956ce08c9 100644 --- a/src/libstrongswan/credentials/auth_cfg.c +++ b/src/libstrongswan/credentials/auth_cfg.c @@ -46,6 +46,7 @@ ENUM(auth_rule_names, AUTH_RULE_IDENTITY, AUTH_HELPER_AC_CERT, "RULE_SUBJECT_CERT", "RULE_CRL_VALIDATION", "RULE_OCSP_VALIDATION", + "RULE_CERT_VALIDATION_SUSPENDED", "RULE_GROUP", "RULE_RSA_STRENGTH", "RULE_ECDSA_STRENGTH", @@ -80,6 +81,7 @@ static inline bool is_multi_value_rule(auth_rule_t type) case AUTH_RULE_AAA_IDENTITY: case AUTH_RULE_XAUTH_IDENTITY: case AUTH_RULE_XAUTH_BACKEND: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: case AUTH_HELPER_SUBJECT_CERT: case AUTH_HELPER_SUBJECT_HASH_URL: case AUTH_RULE_MAX: @@ -214,6 +216,7 @@ static void init_entry(entry_t *this, auth_rule_t type, va_list args) case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_IKE_SIGNATURE_SCHEME: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: /* integer type */ this->value = (void*)(uintptr_t)va_arg(args, u_int); break; @@ -264,6 +267,7 @@ static bool entry_equals(entry_t *e1, entry_t *e2) case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_IKE_SIGNATURE_SCHEME: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: { return e1->value == e2->value; } @@ -356,6 +360,7 @@ static void destroy_entry_value(entry_t *entry) case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_IKE_SIGNATURE_SCHEME: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: case AUTH_RULE_MAX: break; } @@ -389,6 +394,7 @@ static void replace(private_auth_cfg_t *this, entry_enumerator_t *enumerator, case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_IKE_SIGNATURE_SCHEME: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: /* integer type */ entry->value = (void*)(uintptr_t)va_arg(args, u_int); break; @@ -471,6 +477,7 @@ METHOD(auth_cfg_t, get, void*, case AUTH_RULE_OCSP_VALIDATION: return (void*)VALIDATION_FAILED; case AUTH_RULE_IDENTITY_LOOSE: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: return (void*)FALSE; case AUTH_RULE_IDENTITY: case AUTH_RULE_EAP_IDENTITY: @@ -757,6 +764,11 @@ METHOD(auth_cfg_t, complies, bool, { uintptr_t validated; + if (get(this, AUTH_RULE_CERT_VALIDATION_SUSPENDED)) + { /* skip validation, may happen later */ + break; + } + e2 = create_enumerator(this); while (e2->enumerate(e2, &t2, &validated)) { @@ -934,6 +946,8 @@ METHOD(auth_cfg_t, complies, bool, /* just an indication when verifying AUTH_RULE_IDENTITY */ case AUTH_RULE_XAUTH_BACKEND: /* not enforced, just a hint for local authentication */ + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: + /* not a constraint */ case AUTH_HELPER_IM_CERT: case AUTH_HELPER_SUBJECT_CERT: case AUTH_HELPER_IM_HASH_URL: @@ -1086,6 +1100,7 @@ static void merge(private_auth_cfg_t *this, private_auth_cfg_t *other, bool copy case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_IKE_SIGNATURE_SCHEME: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: { add(this, type, (uintptr_t)value); break; @@ -1257,6 +1272,7 @@ METHOD(auth_cfg_t, clone_, auth_cfg_t*, case AUTH_RULE_BLISS_STRENGTH: case AUTH_RULE_SIGNATURE_SCHEME: case AUTH_RULE_IKE_SIGNATURE_SCHEME: + case AUTH_RULE_CERT_VALIDATION_SUSPENDED: clone->add(clone, type, (uintptr_t)value); break; case AUTH_RULE_MAX: diff --git a/src/libstrongswan/credentials/auth_cfg.h b/src/libstrongswan/credentials/auth_cfg.h index 75bc7e97b..6940069de 100644 --- a/src/libstrongswan/credentials/auth_cfg.h +++ b/src/libstrongswan/credentials/auth_cfg.h @@ -94,6 +94,8 @@ enum auth_rule_t { AUTH_RULE_CRL_VALIDATION, /** result of a OCSP validation, cert_validation_t */ AUTH_RULE_OCSP_VALIDATION, + /** CRL/OCSP validation is disabled, bool */ + AUTH_RULE_CERT_VALIDATION_SUSPENDED, /** subject is member of a group, identification_t* * The group membership constraint is fulfilled if the subject is member of * one group defined in the constraints. */ diff --git a/src/libstrongswan/credentials/credential_manager.c b/src/libstrongswan/credentials/credential_manager.c index 371e6404d..95c5cd777 100644 --- a/src/libstrongswan/credentials/credential_manager.c +++ b/src/libstrongswan/credentials/credential_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -917,6 +918,8 @@ METHOD(enumerator_t, trusted_destroy, void, DESTROY_IF(this->auth); DESTROY_IF(this->candidates); this->failed->destroy_offset(this->failed, offsetof(certificate_t, destroy)); + /* check for delayed certificate cache queue */ + cache_queue(this->this); free(this); } @@ -985,7 +988,6 @@ METHOD(enumerator_t, public_destroy, void, this->wrapper->destroy(this->wrapper); } this->this->lock->unlock(this->this->lock); - /* check for delayed certificate cache queue */ cache_queue(this->this); free(this); @@ -993,7 +995,7 @@ METHOD(enumerator_t, public_destroy, void, METHOD(credential_manager_t, create_public_enumerator, enumerator_t*, private_credential_manager_t *this, key_type_t type, identification_t *id, - auth_cfg_t *auth) + auth_cfg_t *auth, bool online) { public_enumerator_t *enumerator; @@ -1002,7 +1004,7 @@ METHOD(credential_manager_t, create_public_enumerator, enumerator_t*, .enumerate = (void*)_public_enumerate, .destroy = _public_destroy, }, - .inner = create_trusted_enumerator(this, type, id, TRUE), + .inner = create_trusted_enumerator(this, type, id, online), .this = this, ); if (auth) diff --git a/src/libstrongswan/credentials/credential_manager.h b/src/libstrongswan/credentials/credential_manager.h index 445ea3f9c..022ca566c 100644 --- a/src/libstrongswan/credentials/credential_manager.h +++ b/src/libstrongswan/credentials/credential_manager.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2007-2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -202,14 +203,18 @@ struct credential_manager_t { * where the auth config helper contains rules for constraint checks. * This function is very similar to create_trusted_enumerator(), but * gets public keys directly. + * If online is set, revocations are checked online for the whole + * trustchain. * * @param type type of the key to get * @param id owner of the key, signer of the signature * @param auth authentication infos + * @param online whether revocations should be checked online * @return enumerator */ enumerator_t* (*create_public_enumerator)(credential_manager_t *this, - key_type_t type, identification_t *id, auth_cfg_t *auth); + key_type_t type, identification_t *id, auth_cfg_t *auth, + bool online); /** * Cache a certificate by invoking cache_cert() on all registered sets. diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 000dda43b..8087e2e2d 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -320,7 +320,8 @@ static public_key_t *find_public_key(private_tls_peer_t *this) if (cert) { enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - KEY_ANY, cert->get_subject(cert), this->server_auth); + KEY_ANY, cert->get_subject(cert), + this->server_auth, TRUE); while (enumerator->enumerate(enumerator, ¤t, &auth)) { found = auth->get(auth, AUTH_RULE_SUBJECT_CERT); diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index f9295a160..cfbe02037 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -548,7 +548,7 @@ static status_t process_cert_verify(private_tls_server_t *this, bio_reader_t *sig; enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, - KEY_ANY, this->peer, this->peer_auth); + KEY_ANY, this->peer, this->peer_auth, TRUE); while (enumerator->enumerate(enumerator, &public, &auth)) { sig = bio_reader_create(reader->peek(reader)); diff --git a/testing/hosts/winnetou/etc/openssl/generate-crl b/testing/hosts/winnetou/etc/openssl/generate-crl index 842c3a1b2..de3c13dcf 100755 --- a/testing/hosts/winnetou/etc/openssl/generate-crl +++ b/testing/hosts/winnetou/etc/openssl/generate-crl @@ -24,6 +24,9 @@ openssl crl -in crl.pem -outform der -out strongswan.crl cp strongswan.crl ${ROOT} cp strongswanCert.pem ${ROOT} cp index.html ${ROOT} +# revoke moon's current CERT +pki --signcrl --cacert strongswanCert.pem --cakey strongswanKey.pem --lifetime 30 --reason key-compromise --cert newcerts/2B.pem --lastcrl strongswan.crl > strongswan_moon_revoked.crl +cp strongswan_moon_revoked.crl ${ROOT} cd /etc/openssl/research openssl ca -gencrl -crldays 15 -config /etc/openssl/research/openssl.cnf -out crl.pem openssl crl -in crl.pem -outform der -out research.crl diff --git a/testing/tests/ikev2/reauth-mbb-revoked/description.txt b/testing/tests/ikev2/reauth-mbb-revoked/description.txt new file mode 100644 index 000000000..4e27a0b82 --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/description.txt @@ -0,0 +1,15 @@ +This scenario tests <b>make-before-break reauthentication</b> using overlapping +IKE_SAs by setting the <i>make_before_break</i> strongswan.conf option. The +initiator <b>carol</b> reauthenticates the IKE_SA with host <b>moon</b> using +<b>ikelifetime=10s</b>, but does not close the old IKE_SA before the replacement +CHILD_SA is in place. A constant ping from <b>carol</b> to client <b>alice</b> +hiding in the subnet behind <b>moon</b> tests if the CHILD_SA works during the +whole procedure. +<p/> +Because the responder is always able to install CHILD_SAs before the initiator +is, some traffic sent by the responder over such a CHILD_SA might get dropped by +the initiator (until it also installed the CHILD_SA). This is particularly +problematic if OCSP/CRL checks are delayed or if they can also be done via the +IPsec tunnel once it's established. Therefore, online OCSP/CRL checks are +suspended during the reauthentication and done afterwards. This is verified here +by revoking the responder's certificate after the SA got initially established. diff --git a/testing/tests/ikev2/reauth-mbb-revoked/evaltest.dat b/testing/tests/ikev2/reauth-mbb-revoked/evaltest.dat new file mode 100644 index 000000000..8fe9a2360 --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/evaltest.dat @@ -0,0 +1,10 @@ +winnetou::cp /var/www/strongswan.crl /var/www/strongswan.crl.bak +winnetou::cp /var/www/strongswan_moon_revoked.crl /var/www/strongswan.crl +carol::ipsec purgecrls +moon:: ipsec status 2> /dev/null::rw\[1]: ESTABLISHED.*moon.strongswan.org.*carol@strongswan.org::YES +carol::ipsec status 2> /dev/null::home\[1]: ESTABLISHED.*carol@strongswan.org.*moon.strongswan.org::YES +carol::sleep 6 +carol::cat /var/log/daemon.log::certificate was revoked.*key compromise::YES +carol::cat /var/log/daemon.log::peer certificate verification failed, deleting SA::YES +moon:: ipsec status 2> /dev/null::rw\[2]: ESTABLISHED.*moon.strongswan.org.*carol@strongswan.org::NO +carol::ipsec status 2> /dev/null::home\[2]: ESTABLISHED.*carol@strongswan.org.*moon.strongswan.org::NO diff --git a/testing/tests/ikev2/reauth-mbb-revoked/hosts/carol/etc/ipsec.conf b/testing/tests/ikev2/reauth-mbb-revoked/hosts/carol/etc/ipsec.conf new file mode 100644 index 000000000..ec2b41d29 --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/hosts/carol/etc/ipsec.conf @@ -0,0 +1,21 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + strictcrlpolicy=yes + +conn %default + keylife=20m + ikelifetime=10s + rekeymargin=5s + rekeyfuzz=0% + keyingtries=1 + +conn home + left=PH_IP_CAROL + leftcert=carolCert.pem + leftid=carol@strongswan.org + right=PH_IP_MOON + rightid=@moon.strongswan.org + rightsubnet=10.1.0.0/16 + keyexchange=ikev2 + auto=add diff --git a/testing/tests/ikev2/reauth-mbb-revoked/hosts/carol/etc/strongswan.conf b/testing/tests/ikev2/reauth-mbb-revoked/hosts/carol/etc/strongswan.conf new file mode 100644 index 000000000..f89437e43 --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/hosts/carol/etc/strongswan.conf @@ -0,0 +1,7 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc stroke kernel-netlink socket-default updown + + make_before_break = yes +} diff --git a/testing/tests/ikev2/reauth-mbb-revoked/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/reauth-mbb-revoked/hosts/moon/etc/ipsec.conf new file mode 100644 index 000000000..93ae34cf7 --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/hosts/moon/etc/ipsec.conf @@ -0,0 +1,19 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + strictcrlpolicy=yes + +conn %default + ikelifetime=30m + keylife=20m + rekeymargin=0s + keyingtries=1 + +conn rw + left=PH_IP_MOON + leftcert=moonCert.pem + leftid=@moon.strongswan.org + leftsubnet=10.1.0.0/16 + right=%any + keyexchange=ikev2 + auto=add diff --git a/testing/tests/ikev2/reauth-mbb-revoked/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/reauth-mbb-revoked/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..f585edfca --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/hosts/moon/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/ikev2/reauth-mbb-revoked/posttest.dat b/testing/tests/ikev2/reauth-mbb-revoked/posttest.dat new file mode 100644 index 000000000..d0d591585 --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/posttest.dat @@ -0,0 +1,3 @@ +winnetou::cp /var/www/strongswan.crl.bak /var/www/strongswan.crl +moon::ipsec stop +carol::ipsec stop diff --git a/testing/tests/ikev2/reauth-mbb-revoked/pretest.dat b/testing/tests/ikev2/reauth-mbb-revoked/pretest.dat new file mode 100644 index 000000000..3a1982f8a --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/pretest.dat @@ -0,0 +1,4 @@ +moon::ipsec start +carol::ipsec start +carol::expect-connection home +carol::ipsec up home diff --git a/testing/tests/ikev2/reauth-mbb-revoked/test.conf b/testing/tests/ikev2/reauth-mbb-revoked/test.conf new file mode 100644 index 000000000..4a5fc470f --- /dev/null +++ b/testing/tests/ikev2/reauth-mbb-revoked/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon carol winnetou" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol" |