diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c index ba44a22f2..6b3cea880 100644 --- a/src/libcharon/bus/bus.c +++ b/src/libcharon/bus/bus.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Tobias Brunner + * Copyright (C) 2011-2016 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -601,6 +601,38 @@ METHOD(bus_t, ike_keys, void, this->mutex->unlock(this->mutex); } +METHOD(bus_t, ike_derived_keys, void, + private_bus_t *this, chunk_t sk_ei, chunk_t sk_er, chunk_t sk_ai, + chunk_t sk_ar) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->ike_derived_keys) + { + continue; + } + entry->calling++; + keep = entry->listener->ike_derived_keys(entry->listener, ike_sa, sk_ei, + sk_er, sk_ai, sk_ar); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + METHOD(bus_t, child_keys, void, private_bus_t *this, child_sa_t *child_sa, bool initiator, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r) @@ -633,6 +665,39 @@ METHOD(bus_t, child_keys, void, this->mutex->unlock(this->mutex); } +METHOD(bus_t, child_derived_keys, void, + private_bus_t *this, child_sa_t *child_sa, bool initiator, + chunk_t encr_i, chunk_t encr_r, chunk_t integ_i, chunk_t integ_r) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + entry_t *entry; + bool keep; + + ike_sa = this->thread_sa->get(this->thread_sa); + + this->mutex->lock(this->mutex); + enumerator = this->listeners->create_enumerator(this->listeners); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->calling || !entry->listener->child_derived_keys) + { + continue; + } + entry->calling++; + keep = entry->listener->child_derived_keys(entry->listener, ike_sa, + child_sa, initiator, encr_i, encr_r, + integ_i, integ_r); + entry->calling--; + if (!keep) + { + unregister_listener(this, entry, enumerator); + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); +} + METHOD(bus_t, child_updown, void, private_bus_t *this, child_sa_t *child_sa, bool up) { @@ -1069,7 +1134,9 @@ bus_t *bus_create() .child_state_change = _child_state_change, .message = _message, .ike_keys = _ike_keys, + .ike_derived_keys = _ike_derived_keys, .child_keys = _child_keys, + .child_derived_keys = _child_derived_keys, .ike_updown = _ike_updown, .ike_rekey = _ike_rekey, .ike_update = _ike_update, diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h index 305cbe4ae..1e810a499 100644 --- a/src/libcharon/bus/bus.h +++ b/src/libcharon/bus/bus.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Tobias Brunner + * Copyright (C) 2012-2016 Tobias Brunner * Copyright (C) 2006-2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -349,8 +349,8 @@ struct bus_t { * @param ike_sa IKE_SA this keymat belongs to * @param dh diffie hellman shared secret * @param dh_other others DH public value (IKEv1 only) - * @param nonce_i initiators nonce - * @param nonce_r responders nonce + * @param nonce_i initiator's nonce + * @param nonce_r responder's nonce * @param rekey IKE_SA we are rekeying, if any (IKEv2 only) * @param shared shared key used for key derivation (IKEv1-PSK only) */ @@ -358,18 +358,43 @@ struct bus_t { chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey, shared_key_t *shared); + /** + * IKE_SA derived keys hook. + * + * @param sk_ei SK_ei, or Ka for IKEv1 + * @param sk_er SK_er + * @param sk_ai SK_ai, or SKEYID_a for IKEv1 + * @param sk_ar SK_ar + */ + void (*ike_derived_keys)(bus_t *this, chunk_t sk_ei, chunk_t sk_er, + chunk_t sk_ai, chunk_t sk_ar); + /** * CHILD_SA keymat hook. * * @param child_sa CHILD_SA this keymat is used for * @param initiator initiator of the CREATE_CHILD_SA exchange * @param dh diffie hellman shared secret - * @param nonce_i initiators nonce - * @param nonce_r responders nonce + * @param nonce_i initiator's nonce + * @param nonce_r responder's nonce */ void (*child_keys)(bus_t *this, child_sa_t *child_sa, bool initiator, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r); + /** + * CHILD_SA derived keys hook. + * + * @param child_sa CHILD_SA these keys are used for + * @param initiator initiator of the CREATE_CHILD_SA exchange + * @param encr_i initiator's encryption key + * @param encr_o responder's encryption key + * @param integ_i initiator's integrity key + * @param integ_r responder's integrity key + */ + void (*child_derived_keys)(bus_t *this, child_sa_t *child_sa, + bool initiator, chunk_t encr_i, chunk_t encr_r, + chunk_t integ_i, chunk_t integ_r); + /** * IKE_SA up/down hook. * diff --git a/src/libcharon/bus/listeners/listener.h b/src/libcharon/bus/listeners/listener.h index be2726ede..be0dfbe21 100644 --- a/src/libcharon/bus/listeners/listener.h +++ b/src/libcharon/bus/listeners/listener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 Tobias Brunner + * Copyright (C) 2011-2016 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -84,8 +84,8 @@ struct listener_t { * @param ike_sa IKE_SA this keymat belongs to * @param dh diffie hellman shared secret * @param dh_other others DH public value (IKEv1 only) - * @param nonce_i initiators nonce - * @param nonce_r responders nonce + * @param nonce_i initiator's nonce + * @param nonce_r responder's nonce * @param rekey IKE_SA we are rekeying, if any (IKEv2 only) * @param shared shared key used for key derivation (IKEv1-PSK only) * @return TRUE to stay registered, FALSE to unregister @@ -94,6 +94,18 @@ struct listener_t { chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey, shared_key_t *shared); + /** + * Hook called with derived IKE_SA keys. + * + * @param ike_sa IKE_SA these keys belong to + * @param sk_ei SK_ei, or Ka for IKEv1 + * @param sk_er SK_er + * @param sk_ai SK_ai, or SKEYID_a for IKEv1 + * @param sk_ar SK_ar + */ + bool (*ike_derived_keys)(listener_t *this, ike_sa_t *ike_sa, chunk_t sk_ei, + chunk_t sk_er, chunk_t sk_ai, chunk_t sk_ar); + /** * Hook called with CHILD_SA key material. * @@ -101,14 +113,30 @@ struct listener_t { * @param child_sa CHILD_SA this keymat is used for * @param initiator initiator of the CREATE_CHILD_SA exchange * @param dh diffie hellman shared secret - * @param nonce_i initiators nonce - * @param nonce_r responders nonce + * @param nonce_i initiator's nonce + * @param nonce_r responder's nonce * @return TRUE to stay registered, FALSE to unregister */ bool (*child_keys)(listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool initiator, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r); + /** + * Hook called with derived CHILD_SA keys. + * + * @param ike_sa IKE_SA the child sa belongs to + * @param child_sa CHILD_SA these keys are used for + * @param initiator initiator of the CREATE_CHILD_SA exchange + * @param encr_i initiator's encryption key + * @param encr_o responder's encryption key + * @param integ_i initiator's integrity key + * @param integ_r responder's integrity key + */ + bool (*child_derived_keys)(listener_t *this, ike_sa_t *ike_sa, + child_sa_t *child_sa, bool initiator, + chunk_t encr_i, chunk_t encr_r, + chunk_t integ_i, chunk_t integ_r); + /** * Hook called if an IKE_SA gets up or down. * diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c index be6b03bef..d1d4cbd9b 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.c +++ b/src/libcharon/sa/ikev1/keymat_v1.c @@ -74,11 +74,6 @@ struct private_keymat_v1_t { */ hasher_t *hasher; - /** - * Key used for authentication during main mode - */ - chunk_t skeyid; - /** * Key to derive key material from for non-ISAKMP SAs, rekeying */ @@ -269,12 +264,12 @@ static bool expand_skeyid_e(chunk_t skeyid_e, size_t key_size, prf_t *prf, * Create a simple implementation of the aead_t interface which only encrypts * or decrypts data. */ -static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e) +static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e, + chunk_t *ka) { private_aead_t *this; uint16_t alg, key_size; crypter_t *crypter; - chunk_t ka; if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size)) @@ -292,17 +287,16 @@ static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e) return NULL; } key_size = crypter->get_key_size(crypter); - if (!expand_skeyid_e(skeyid_e, crypter->get_key_size(crypter), prf, &ka)) + if (!expand_skeyid_e(skeyid_e, crypter->get_key_size(crypter), prf, ka)) { return NULL; } - DBG4(DBG_IKE, "encryption key Ka %B", &ka); - if (!crypter->set_key(crypter, ka)) + DBG4(DBG_IKE, "encryption key Ka %B", ka); + if (!crypter->set_key(crypter, *ka)) { - chunk_clear(&ka); + chunk_clear(ka); return NULL; } - chunk_clear(&ka); INIT(this, .aead = { @@ -392,7 +386,7 @@ METHOD(keymat_v1_t, derive_ike_keys, bool, auth_method_t auth, shared_key_t *shared_key) { chunk_t g_xy, g_xi, g_xr, dh_me, spi_i, spi_r, nonces, data, skeyid_e; - chunk_t skeyid; + chunk_t skeyid, ka; uint16_t alg; spi_i = chunk_alloca(sizeof(uint64_t)); @@ -550,11 +544,14 @@ METHOD(keymat_v1_t, derive_ike_keys, bool, } chunk_clear(&skeyid); - this->aead = create_aead(proposal, this->prf, skeyid_e); + this->aead = create_aead(proposal, this->prf, skeyid_e, &ka); if (!this->aead) { return FALSE; } + charon->bus->ike_derived_keys(charon->bus, ka, chunk_empty, this->skeyid_a, + chunk_empty); + chunk_clear(&ka); if (!this->hasher && !this->public.create_hasher(&this->public, proposal)) { return FALSE; diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index bbd1cb09f..6b896416a 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -348,10 +348,6 @@ static bool install(private_quick_mode_t *this) this->initiator, FALSE, FALSE, tsr, tsi); } } - chunk_clear(&integ_i); - chunk_clear(&integ_r); - chunk_clear(&encr_i); - chunk_clear(&encr_r); if (status_i != SUCCESS || status_o != SUCCESS) { @@ -361,22 +357,38 @@ static bool install(private_quick_mode_t *this) (status_o != SUCCESS) ? "outbound " : ""); tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); - return FALSE; - } - - if (this->initiator) - { - status = this->child_sa->add_policies(this->child_sa, tsi, tsr); + status = FAILED; } else { - status = this->child_sa->add_policies(this->child_sa, tsr, tsi); + if (this->initiator) + { + status = this->child_sa->add_policies(this->child_sa, tsi, tsr); + } + else + { + status = this->child_sa->add_policies(this->child_sa, tsr, tsi); + } + tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); + tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); + } + else + { + charon->bus->child_derived_keys(charon->bus, this->child_sa, + this->initiator, encr_i, encr_r, + integ_i, integ_r); + } } - tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); - tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); + chunk_clear(&integ_i); + chunk_clear(&integ_r); + chunk_clear(&encr_i); + chunk_clear(&encr_r); + if (status != SUCCESS) { - DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); return FALSE; } diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index e37399841..58efdbabe 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -103,7 +103,7 @@ static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg, uint16_t key_size, prf_plus_t *prf_plus) { aead_t *aead_i, *aead_r; - chunk_t key = chunk_empty; + chunk_t sk_ei = chunk_empty, sk_er = chunk_empty; u_int salt_size; switch (alg) @@ -146,23 +146,22 @@ static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg, { goto failure; } - if (!prf_plus->allocate_bytes(prf_plus, key_size, &key)) + if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ei)) { goto failure; } - DBG4(DBG_IKE, "Sk_ei secret %B", &key); - if (!aead_i->set_key(aead_i, key)) + DBG4(DBG_IKE, "Sk_ei secret %B", &sk_ei); + if (!aead_i->set_key(aead_i, sk_ei)) { goto failure; } - chunk_clear(&key); - if (!prf_plus->allocate_bytes(prf_plus, key_size, &key)) + if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_er)) { goto failure; } - DBG4(DBG_IKE, "Sk_er secret %B", &key); - if (!aead_r->set_key(aead_r, key)) + DBG4(DBG_IKE, "Sk_er secret %B", &sk_er); + if (!aead_r->set_key(aead_r, sk_er)) { goto failure; } @@ -178,11 +177,14 @@ static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg, this->aead_out = aead_r; } aead_i = aead_r = NULL; + charon->bus->ike_derived_keys(charon->bus, sk_ei, sk_er, chunk_empty, + chunk_empty); failure: DESTROY_IF(aead_i); DESTROY_IF(aead_r); - chunk_clear(&key); + chunk_clear(&sk_ei); + chunk_clear(&sk_er); return this->aead_in && this->aead_out; } @@ -196,7 +198,8 @@ static bool derive_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg, signer_t *signer_i, *signer_r; iv_gen_t *ivg_i, *ivg_r; size_t key_size; - chunk_t key = chunk_empty; + chunk_t sk_ei = chunk_empty, sk_er = chunk_empty, + sk_ai = chunk_empty, sk_ar = chunk_empty; signer_i = lib->crypto->create_signer(lib->crypto, int_alg); signer_r = lib->crypto->create_signer(lib->crypto, int_alg); @@ -220,48 +223,45 @@ static bool derive_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg, /* SK_ai/SK_ar used for integrity protection */ key_size = signer_i->get_key_size(signer_i); - if (!prf_plus->allocate_bytes(prf_plus, key_size, &key)) + if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ai)) { goto failure; } - DBG4(DBG_IKE, "Sk_ai secret %B", &key); - if (!signer_i->set_key(signer_i, key)) + DBG4(DBG_IKE, "Sk_ai secret %B", &sk_ai); + if (!signer_i->set_key(signer_i, sk_ai)) { goto failure; } - chunk_clear(&key); - if (!prf_plus->allocate_bytes(prf_plus, key_size, &key)) + if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ar)) { goto failure; } - DBG4(DBG_IKE, "Sk_ar secret %B", &key); - if (!signer_r->set_key(signer_r, key)) + DBG4(DBG_IKE, "Sk_ar secret %B", &sk_ar); + if (!signer_r->set_key(signer_r, sk_ar)) { goto failure; } - chunk_clear(&key); /* SK_ei/SK_er used for encryption */ key_size = crypter_i->get_key_size(crypter_i); - if (!prf_plus->allocate_bytes(prf_plus, key_size, &key)) + if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_ei)) { goto failure; } - DBG4(DBG_IKE, "Sk_ei secret %B", &key); - if (!crypter_i->set_key(crypter_i, key)) + DBG4(DBG_IKE, "Sk_ei secret %B", &sk_ei); + if (!crypter_i->set_key(crypter_i, sk_ei)) { goto failure; } - chunk_clear(&key); - if (!prf_plus->allocate_bytes(prf_plus, key_size, &key)) + if (!prf_plus->allocate_bytes(prf_plus, key_size, &sk_er)) { goto failure; } - DBG4(DBG_IKE, "Sk_er secret %B", &key); - if (!crypter_r->set_key(crypter_r, key)) + DBG4(DBG_IKE, "Sk_er secret %B", &sk_er); + if (!crypter_r->set_key(crypter_r, sk_er)) { goto failure; } @@ -284,9 +284,13 @@ static bool derive_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg, } signer_i = signer_r = NULL; crypter_i = crypter_r = NULL; + charon->bus->ike_derived_keys(charon->bus, sk_ei, sk_er, sk_ai, sk_ar); failure: - chunk_clear(&key); + chunk_clear(&sk_ai); + chunk_clear(&sk_ar); + chunk_clear(&sk_ei); + chunk_clear(&sk_er); DESTROY_IF(signer_i); DESTROY_IF(signer_r); DESTROY_IF(crypter_i); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 64a82850b..71cb6b8ea 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -666,10 +666,6 @@ static status_t select_and_install(private_child_create_t *this, FALSE, this->tfcv3, my_ts, other_ts); } } - chunk_clear(&integ_i); - chunk_clear(&integ_r); - chunk_clear(&encr_i); - chunk_clear(&encr_r); if (status_i != SUCCESS || status_o != SUCCESS) { @@ -679,41 +675,62 @@ static status_t select_and_install(private_child_create_t *this, (status_o != SUCCESS) ? "outbound " : ""); charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED, this->child_sa); - return FAILED; - } - - if (this->initiator) - { - status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts); + status = FAILED; } else { - /* use a copy of the traffic selectors, as the POST hook should not - * change payloads */ - my_ts = this->tsr->clone_offset(this->tsr, - offsetof(traffic_selector_t, clone)); - other_ts = this->tsi->clone_offset(this->tsi, - offsetof(traffic_selector_t, clone)); - charon->bus->narrow(charon->bus, this->child_sa, - NARROW_RESPONDER_POST, my_ts, other_ts); - if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0) + if (this->initiator) { - status = FAILED; + status = this->child_sa->add_policies(this->child_sa, + my_ts, other_ts); } else { - status = this->child_sa->add_policies(this->child_sa, - my_ts, other_ts); + /* use a copy of the traffic selectors, as the POST hook should not + * change payloads */ + my_ts = this->tsr->clone_offset(this->tsr, + offsetof(traffic_selector_t, clone)); + other_ts = this->tsi->clone_offset(this->tsi, + offsetof(traffic_selector_t, clone)); + charon->bus->narrow(charon->bus, this->child_sa, + NARROW_RESPONDER_POST, my_ts, other_ts); + if (my_ts->get_count(my_ts) == 0 || + other_ts->get_count(other_ts) == 0) + { + status = FAILED; + } + else + { + status = this->child_sa->add_policies(this->child_sa, + my_ts, other_ts); + } + my_ts->destroy_offset(my_ts, + offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, + offsetof(traffic_selector_t, destroy)); + } + if (status != SUCCESS) + { + DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); + charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_POLICY_FAILED, + this->child_sa); + status = NOT_FOUND; + } + else + { + charon->bus->child_derived_keys(charon->bus, this->child_sa, + this->initiator, encr_i, encr_r, + integ_i, integ_r); } - my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); } + chunk_clear(&integ_i); + chunk_clear(&integ_r); + chunk_clear(&encr_i); + chunk_clear(&encr_r); + if (status != SUCCESS) { - DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); - charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_POLICY_FAILED, - this->child_sa); - return NOT_FOUND; + return status; } charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,