diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2018-09-24 15:11:14 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2018-09-24 15:11:14 +0200 |
commit | e0e280b7669435b991b7e457abd8aa450930b3e8 (patch) | |
tree | 3e6084f13b14ad2df104e2ce6e589eb96c5f7ac9 /src/libcharon/sa | |
parent | 51a71ee15c1bcf0e82f363a16898f571e211f9c3 (diff) | |
download | vyos-strongswan-e0e280b7669435b991b7e457abd8aa450930b3e8.tar.gz vyos-strongswan-e0e280b7669435b991b7e457abd8aa450930b3e8.zip |
New upstream version 5.7.0
Diffstat (limited to 'src/libcharon/sa')
31 files changed, 939 insertions, 204 deletions
diff --git a/src/libcharon/sa/authenticator.h b/src/libcharon/sa/authenticator.h index 42d9ce32e..58a8ca04f 100644 --- a/src/libcharon/sa/authenticator.h +++ b/src/libcharon/sa/authenticator.h @@ -1,6 +1,6 @@ /* + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi - * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil * @@ -157,6 +157,17 @@ struct authenticator_t { status_t (*build)(authenticator_t *this, message_t *message); /** + * Optional method to set a Postquantum Preshared Key (PPK) to be used + * during authentication. + * + * Has to be called before the final call to process()/build(). + * + * @param ppk PPK to use + * @param no_ppk_auth whether to add a NO_PPK_AUTH notify in build() + */ + void (*use_ppk)(authenticator_t *this, chunk_t ppk, bool no_ppk_auth); + + /** * Check if the authenticator is capable of mutual authentication. * * Some authenticator authenticate both peers, e.g. EAP. To support diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 7eeb578f3..c33398bee 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -890,12 +890,21 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, .cpi = cpi, .encap = this->encap, .hw_offload = this->config->get_hw_offload(this->config), + .mark = this->config->get_set_mark(this->config, inbound), .esn = esn, + .copy_df = !this->config->has_option(this->config, OPT_NO_COPY_DF), + .copy_ecn = !this->config->has_option(this->config, OPT_NO_COPY_ECN), + .copy_dscp = this->config->get_copy_dscp(this->config), .initiator = initiator, .inbound = inbound, .update = update, }; + if (sa.mark.value == MARK_SAME) + { + sa.mark.value = inbound ? this->mark_in.value : this->mark_out.value; + } + status = charon->kernel->add_sa(charon->kernel, &id, &sa); my_ts->destroy(my_ts); @@ -1723,7 +1732,7 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) traffic_selector_t *ts; list = linked_list_create_with_items(ike, NULL); - ts_list = config->get_traffic_selectors(config, local, NULL, list); + ts_list = config->get_traffic_selectors(config, local, NULL, list, FALSE); list->destroy(list); enumerator = ts_list->create_enumerator(ts_list); diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index f39fed6f0..a4ad866d3 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -674,6 +674,7 @@ METHOD(ike_sa_t, get_ike_cfg, ike_cfg_t*, METHOD(ike_sa_t, set_ike_cfg, void, private_ike_sa_t *this, ike_cfg_t *ike_cfg) { + DESTROY_IF(this->ike_cfg); ike_cfg->get_ref(ike_cfg); this->ike_cfg = ike_cfg; } diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 316b713ee..c1d3e1d7a 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -156,6 +156,11 @@ enum ike_extension_t { * IKEv2 Message ID sync, RFC 6311 */ EXT_IKE_MESSAGE_ID_SYNC = (1<<14), + + /** + * Postquantum Preshared Keys, draft-ietf-ipsecme-qr-ikev2 + */ + EXT_PPK = (1<<15), }; /** @@ -227,6 +232,11 @@ enum ike_condition_t { * Online certificate revocation checking is suspended for this IKE_SA */ COND_ONLINE_VALIDATION_SUSPENDED = (1<<12), + + /** + * A Postquantum Preshared Key was used when this IKE_SA was created + */ + COND_PPK = (1<<13), }; /** diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 2a499db40..c50c70860 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -2,7 +2,7 @@ * Copyright (C) 2005-2011 Martin Willi * Copyright (C) 2011 revosec AG * - * Copyright (C) 2008-2017 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil * @@ -1620,17 +1620,6 @@ METHOD(ike_sa_manager_t, new_initiator_spi, bool, unlock_single_segment(this, segment); return FALSE; } - /* threads waiting for this entry do so using the (soon) wrong IKE_SA - * ID and, therefore, likely on the wrong segment, so drive them out */ - entry->driveout_waiting_threads = TRUE; - entry->driveout_new_threads = TRUE; - while (entry->waiting_threads) - { - entry->condvar->broadcast(entry->condvar); - entry->condvar->wait(entry->condvar, this->segments[segment].mutex); - } - remove_entry(this, entry); - unlock_single_segment(this, segment); } else { @@ -1638,7 +1627,19 @@ METHOD(ike_sa_manager_t, new_initiator_spi, bool, return FALSE; } + /* the hashtable row and segment are determined by the local SPI as + * initiator, so if we change it the row and segment derived from it might + * change as well. This could be a problem for threads waiting for the + * entry (in particular those enumerating entries to check them out by + * unique ID or name). In order to avoid having to drive them out and thus + * preventing them from checking out the entry (even though the ID or name + * will not change and enumerating it is also fine), we mask the new SPI and + * merge it with the old SPI so the entry ends up in the same row/segment. + * Since SPIs are 64-bit and the number of rows/segments is usually + * relatively low this should not be a problem. */ spi = ike_sa_id->get_initiator_spi(ike_sa_id); + new_spi = (spi & (uint64_t)this->table_mask) | + (new_spi & ~(uint64_t)this->table_mask); DBG2(DBG_MGR, "change initiator SPI of IKE_SA %s[%u] from %.16"PRIx64" to " "%.16"PRIx64, ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), @@ -1647,10 +1648,7 @@ METHOD(ike_sa_manager_t, new_initiator_spi, bool, ike_sa_id->set_initiator_spi(ike_sa_id, new_spi); entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa_id); - entry->driveout_waiting_threads = FALSE; - entry->driveout_new_threads = FALSE; - - segment = put_entry(this, entry); + entry->condvar->signal(entry->condvar); unlock_single_segment(this, segment); return TRUE; } @@ -2017,6 +2015,8 @@ static status_t enforce_replace(private_ike_sa_manager_t *this, * CHILD_SAs to keep connectivity up. */ lib->scheduler->schedule_job(lib->scheduler, (job_t*) delete_ike_sa_job_create(duplicate->get_id(duplicate), TRUE), 10); + DBG1(DBG_IKE, "schedule delete of duplicate IKE_SA for peer '%Y' due " + "to uniqueness policy and suspected reauthentication", other); return SUCCESS; } DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer '%Y' due to " diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c index 1de05b4ec..bcea1f388 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.c +++ b/src/libcharon/sa/ikev1/keymat_v1.c @@ -219,7 +219,6 @@ static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e, encryption_algorithm_names, alg, key_size); return NULL; } - key_size = crypter->get_key_size(crypter); if (!expand_skeyid_e(skeyid_e, crypter->get_key_size(crypter), prf, ka)) { return NULL; diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index 5856f829e..b99d75142 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -311,7 +311,7 @@ static void save_auth_cfg(private_phase1_t *this, return; } auth = auth_cfg_create(); - /* for local config, we _copy_ entires from the config, as it contains + /* for local config, we _copy_ entries from the config, as it contains * certificates we must send later. */ auth->merge(auth, this->ike_sa->get_auth_cfg(this->ike_sa, local), local); this->ike_sa->add_auth_cfg(this->ike_sa, local, auth); diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 3472d2c35..5f6c3bbe8 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -721,6 +721,7 @@ METHOD(task_manager_t, initiate, status_t, { case IKE_CONNECTING: /* close after sending an INFORMATIONAL when unestablished */ + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return FAILED; case IKE_DELETING: /* close after sending a DELETE */ @@ -920,15 +921,16 @@ static bool process_dpd(private_task_manager_t *this, message_t *message) } else /* DPD_R_U_THERE_ACK */ { - if (seq == this->dpd_send - 1) + if (seq == this->dpd_send) { + this->dpd_send++; this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, time_monotonic(NULL)); } else { DBG1(DBG_IKE, "received invalid DPD sequence number %u " - "(expected %u), ignored", seq, this->dpd_send - 1); + "(expected %u), ignored", seq, this->dpd_send); } } return TRUE; @@ -1843,7 +1845,7 @@ METHOD(task_manager_t, queue_dpd, void, uint32_t t, retransmit; queue_task(this, (task_t*)isakmp_dpd_create(this->ike_sa, DPD_R_U_THERE, - this->dpd_send++)); + this->dpd_send)); peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); /* compute timeout in milliseconds */ diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 82d647a6c..023119dd4 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -270,11 +270,6 @@ METHOD(task_t, build_i, status_t, return FAILED; } id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE); - if (!id) - { - DBG1(DBG_CFG, "own identity not known"); - return FAILED; - } this->ike_sa->set_my_id(this->ike_sa, id->clone(id)); id_payload = id_payload_create_from_identification(PLV1_ID, id); this->id_data = id_payload->get_encoded(id_payload); @@ -302,6 +297,7 @@ METHOD(task_t, build_i, status_t, this->id_data)) { this->id_data = chunk_empty; + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } this->id_data = chunk_empty; @@ -330,6 +326,7 @@ METHOD(task_t, build_i, status_t, } if (!establish(this)) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } break; @@ -428,6 +425,7 @@ METHOD(task_t, process_r, status_t, { DBG1(DBG_IKE, "Aggressive Mode PSK disabled for " "security reasons"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } break; @@ -455,6 +453,7 @@ METHOD(task_t, process_r, status_t, if (!id_payload) { DBG1(DBG_IKE, "IDii payload missing"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, INVALID_PAYLOAD_TYPE); } @@ -465,6 +464,7 @@ METHOD(task_t, process_r, status_t, this->method, TRUE, id); if (!this->peer_cfg) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg); @@ -493,6 +493,7 @@ METHOD(task_t, process_r, status_t, this->method, TRUE, NULL); if (!this->peer_cfg) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg); @@ -502,6 +503,7 @@ METHOD(task_t, process_r, status_t, { DBG1(DBG_IKE, "Aggressive Mode authorization hook forbids " "IKE_SA, cancelling"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } @@ -528,6 +530,7 @@ METHOD(task_t, process_r, status_t, } if (!establish(this)) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } job = adopt_children_job_create( @@ -602,11 +605,6 @@ METHOD(task_t, build_r, status_t, } id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE); - if (!id) - { - DBG1(DBG_CFG, "own identity not known"); - return send_notify(this, INVALID_ID_INFORMATION); - } this->ike_sa->set_my_id(this->ike_sa, id->clone(id)); id_payload = id_payload_create_from_identification(PLV1_ID, id); @@ -615,6 +613,7 @@ METHOD(task_t, build_r, status_t, if (!this->ph1->build_auth(this->ph1, this->method, message, id_payload->get_encoded(id_payload))) { + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } return NEED_MORE; @@ -679,6 +678,7 @@ METHOD(task_t, process_i, status_t, if (!id_payload) { DBG1(DBG_IKE, "IDir payload missing"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } id = id_payload->get_identification(id_payload); @@ -687,6 +687,7 @@ METHOD(task_t, process_i, status_t, { DBG1(DBG_IKE, "IDir '%Y' does not match to '%Y'", id, cid); id->destroy(id); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, INVALID_ID_INFORMATION); } this->ike_sa->set_other_id(this->ike_sa, id); @@ -698,6 +699,7 @@ METHOD(task_t, process_i, status_t, if (!this->ph1->verify_auth(this->ph1, this->method, message, id_payload->get_encoded(id_payload))) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } if (!charon->bus->authorize(charon->bus, FALSE)) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 6a296f221..b26a11bb4 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -59,7 +59,7 @@ struct private_isakmp_vendor_t { ike_sa_t *ike_sa; /** - * Are we the inititator of this task + * Are we the initiator of this task */ bool initiator; diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 1f764e547..b60c84992 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -332,11 +332,6 @@ METHOD(task_t, build_i, status_t, identification_t *id; id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE); - if (!id) - { - DBG1(DBG_CFG, "own identity not known"); - return send_notify(this, INVALID_ID_INFORMATION); - } this->ike_sa->set_my_id(this->ike_sa, id->clone(id)); id_payload = id_payload_create_from_identification(PLV1_ID, id); message->add_payload(message, &id_payload->payload_interface); @@ -344,6 +339,7 @@ METHOD(task_t, build_i, status_t, if (!this->ph1->build_auth(this->ph1, this->method, message, id_payload->get_encoded(id_payload))) { + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } @@ -445,6 +441,7 @@ METHOD(task_t, process_r, status_t, if (!id_payload) { DBG1(DBG_IKE, "IDii payload missing"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, INVALID_PAYLOAD_TYPE); } id = id_payload->get_identification(id_payload); @@ -457,6 +454,7 @@ METHOD(task_t, process_r, status_t, this->method, FALSE, id); if (!this->peer_cfg) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg); @@ -472,6 +470,7 @@ METHOD(task_t, process_r, status_t, { DBG1(DBG_IKE, "Main Mode authorization hook forbids IKE_SA, " "cancelling"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } @@ -523,11 +522,6 @@ METHOD(task_t, build_r, status_t, xauth_t *xauth = NULL; id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE); - if (!id) - { - DBG1(DBG_CFG, "own identity not known"); - return send_notify(this, INVALID_ID_INFORMATION); - } this->ike_sa->set_my_id(this->ike_sa, id->clone(id)); id_payload = id_payload_create_from_identification(PLV1_ID, id); @@ -536,6 +530,7 @@ METHOD(task_t, build_r, status_t, if (!this->ph1->build_auth(this->ph1, this->method, message, id_payload->get_encoded(id_payload))) { + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } @@ -562,6 +557,7 @@ METHOD(task_t, build_r, status_t, } if (!establish(this)) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_notify(this, AUTHENTICATION_FAILED); } job = adopt_children_job_create( @@ -688,6 +684,7 @@ METHOD(task_t, process_i, status_t, if (!id_payload) { DBG1(DBG_IKE, "IDir payload missing"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } id = id_payload->get_identification(id_payload); @@ -696,6 +693,7 @@ METHOD(task_t, process_i, status_t, { DBG1(DBG_IKE, "IDir '%Y' does not match to '%Y'", id, cid); id->destroy(id); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } this->ike_sa->set_other_id(this->ike_sa, id); @@ -703,12 +701,14 @@ METHOD(task_t, process_i, status_t, if (!this->ph1->verify_auth(this->ph1, this->method, message, id_payload->get_encoded(id_payload))) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } if (!charon->bus->authorize(charon->bus, FALSE)) { DBG1(DBG_IKE, "Main Mode authorization hook forbids IKE_SA, " "cancelling"); + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } @@ -736,6 +736,7 @@ METHOD(task_t, process_i, status_t, } if (!establish(this)) { + charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); return send_delete(this); } break; diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index 43897c304..9b692588d 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -583,7 +583,6 @@ static status_t build_ack(private_mode_config_t *this, message_t *message) enumerator = this->vips->create_enumerator(this->vips); while (enumerator->enumerate(enumerator, &host)) { - type = INTERNAL_IP6_ADDRESS; if (host->get_family(host) == AF_INET6) { type = INTERNAL_IP6_ADDRESS; diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 5e5b61e7f..007e94d96 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -544,7 +544,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, hosts = get_dynamic_hosts(this->ike_sa, local); list = this->config->get_traffic_selectors(this->config, - local, supplied, hosts); + local, supplied, hosts, TRUE); hosts->destroy(hosts); if (list->get_first(list, (void**)&ts) == SUCCESS) { diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c index 968b4386c..bec2cfe7d 100644 --- a/src/libcharon/sa/ikev1/tasks/xauth.c +++ b/src/libcharon/sa/ikev1/tasks/xauth.c @@ -226,7 +226,7 @@ static bool select_compliant_config(private_xauth_t *this) { /* current config is fine */ return TRUE; } - DBG1(DBG_CFG, "selected peer config '%s' inacceptable", + DBG1(DBG_CFG, "selected peer config '%s' unacceptable", old->get_name(old)); aggressive = old->use_aggressive(old); diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index bcf262725..e1e6cd7ee 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2006-2009 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -65,6 +65,16 @@ struct private_eap_authenticator_t { char reserved[3]; /** + * PPK to use + */ + chunk_t ppk; + + /** + * Add a NO_PPK_AUTH notify + */ + bool no_ppk_auth; + + /** * Current EAP method processing */ eap_method_t *method; @@ -444,6 +454,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, chunk_t nonce, chunk_t init) { auth_payload_t *auth_payload; + notify_payload_t *notify; chunk_t auth_data, recv_auth_data; identification_t *other_id; auth_cfg_t *auth; @@ -458,14 +469,26 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, DBG1(DBG_IKE, "AUTH payload missing"); return FALSE; } + recv_auth_data = auth_payload->get_data(auth_payload); + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && + !this->ppk.ptr) + { /* look for a NO_PPK_AUTH notify if we have no PPK */ + notify = message->get_notify(message, NO_PPK_AUTH); + if (notify) + { + DBG1(DBG_IKE, "no PPK available, using NO_PPK_AUTH notify"); + recv_auth_data = notify->get_notification_data(notify); + } + } + other_id = this->ike_sa->get_other_id(this->ike_sa); keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); - if (!keymat->get_psk_sig(keymat, TRUE, init, nonce, - this->msk, other_id, this->reserved, &auth_data)) + if (!keymat->get_psk_sig(keymat, TRUE, init, nonce, this->msk, this->ppk, + other_id, this->reserved, &auth_data)) { return FALSE; } - recv_auth_data = auth_payload->get_data(auth_payload); if (!auth_data.len || !chunk_equals_const(auth_data, recv_auth_data)) { DBG1(DBG_IKE, "verification of AUTH payload with%s EAP MSK failed", @@ -507,8 +530,8 @@ static bool build_auth(private_eap_authenticator_t *this, message_t *message, DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_class_names, AUTH_CLASS_EAP); - if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, - this->msk, my_id, this->reserved, &auth_data)) + if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, this->ppk, + my_id, this->reserved, &auth_data)) { return FALSE; } @@ -517,6 +540,18 @@ static bool build_auth(private_eap_authenticator_t *this, message_t *message, auth_payload->set_data(auth_payload, auth_data); message->add_payload(message, (payload_t*)auth_payload); chunk_free(&auth_data); + + if (this->no_ppk_auth) + { + if (!keymat->get_psk_sig(keymat, FALSE, init, nonce, this->msk, + chunk_empty, my_id, this->reserved, &auth_data)) + { + DBG1(DBG_IKE, "failed adding NO_PPK_AUTH notify"); + return FALSE; + } + message->add_notify(message, FALSE, NO_PPK_AUTH, auth_data); + chunk_free(&auth_data); + } return TRUE; } @@ -698,6 +733,13 @@ METHOD(authenticator_t, is_mutual, bool, return TRUE; } +METHOD(authenticator_t, use_ppk, void, + private_eap_authenticator_t *this, chunk_t ppk, bool no_ppk_auth) +{ + this->ppk = ppk; + this->no_ppk_auth = no_ppk_auth; +} + METHOD(authenticator_t, destroy, void, private_eap_authenticator_t *this) { @@ -723,6 +765,7 @@ eap_authenticator_t *eap_authenticator_create_builder(ike_sa_t *ike_sa, .authenticator = { .build = _build_client, .process = _process_client, + .use_ppk = _use_ppk, .is_mutual = _is_mutual, .destroy = _destroy, }, @@ -753,6 +796,7 @@ eap_authenticator_t *eap_authenticator_create_verifier(ike_sa_t *ike_sa, .authenticator = { .build = _build_server, .process = _process_server, + .use_ppk = _use_ppk, .is_mutual = _is_mutual, .destroy = _destroy, }, diff --git a/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c index c1decb130..76571e702 100644 --- a/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -51,6 +52,16 @@ struct private_psk_authenticator_t { * Reserved bytes of ID payload */ char reserved[3]; + + /** + * PPK to use + */ + chunk_t ppk; + + /** + * Add a NO_PPK_AUTH notify + */ + bool no_ppk_auth; }; METHOD(authenticator_t, build, status_t, @@ -68,18 +79,19 @@ METHOD(authenticator_t, build, status_t, DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N", my_id, auth_method_names, AUTH_PSK); key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE, my_id, other_id); - if (key == NULL) + if (!key) { DBG1(DBG_IKE, "no shared key found for '%Y' - '%Y'", my_id, other_id); return NOT_FOUND; } if (!keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init, this->nonce, - key->get_key(key), my_id, this->reserved, &auth_data)) + key->get_key(key), this->ppk, my_id, + this->reserved, &auth_data)) { key->destroy(key); return FAILED; } - key->destroy(key); + DBG2(DBG_IKE, "successfully created shared key MAC"); auth_payload = auth_payload_create(); auth_payload->set_auth_method(auth_payload, AUTH_PSK); @@ -87,6 +99,21 @@ METHOD(authenticator_t, build, status_t, chunk_free(&auth_data); message->add_payload(message, (payload_t*)auth_payload); + if (this->no_ppk_auth) + { + if (!keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init, this->nonce, + key->get_key(key), chunk_empty, my_id, + this->reserved, &auth_data)) + { + DBG1(DBG_IKE, "failed adding NO_PPK_AUTH notify"); + key->destroy(key); + return SUCCESS; + } + DBG2(DBG_IKE, "successfully created shared key MAC without PPK"); + message->add_notify(message, FALSE, NO_PPK_AUTH, auth_data); + chunk_free(&auth_data); + } + key->destroy(key); return SUCCESS; } @@ -96,6 +123,7 @@ METHOD(authenticator_t, process, status_t, chunk_t auth_data, recv_auth_data; identification_t *my_id, *other_id; auth_payload_t *auth_payload; + notify_payload_t *notify; auth_cfg_t *auth; shared_key_t *key; enumerator_t *enumerator; @@ -108,8 +136,20 @@ METHOD(authenticator_t, process, status_t, { return FAILED; } - keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); recv_auth_data = auth_payload->get_data(auth_payload); + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && + !this->ppk.ptr) + { /* look for a NO_PPK_AUTH notify if we have no PPK */ + notify = message->get_notify(message, NO_PPK_AUTH); + if (notify) + { + DBG1(DBG_IKE, "no PPK available, using NO_PPK_AUTH notify"); + recv_auth_data = notify->get_notification_data(notify); + } + } + + keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, @@ -119,7 +159,8 @@ METHOD(authenticator_t, process, status_t, keys_found++; if (!keymat->get_psk_sig(keymat, TRUE, this->ike_sa_init, this->nonce, - key->get_key(key), other_id, this->reserved, &auth_data)) + key->get_key(key), this->ppk, other_id, + this->reserved, &auth_data)) { continue; } @@ -150,6 +191,13 @@ METHOD(authenticator_t, process, status_t, return SUCCESS; } +METHOD(authenticator_t, use_ppk, void, + private_psk_authenticator_t *this, chunk_t ppk, bool no_ppk_auth) +{ + this->ppk = ppk; + this->no_ppk_auth = no_ppk_auth; +} + METHOD(authenticator_t, destroy, void, private_psk_authenticator_t *this) { @@ -170,6 +218,7 @@ psk_authenticator_t *psk_authenticator_create_builder(ike_sa_t *ike_sa, .authenticator = { .build = _build, .process = (void*)return_failed, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, @@ -197,6 +246,7 @@ psk_authenticator_t *psk_authenticator_create_verifier(ike_sa_t *ike_sa, .authenticator = { .build = (void*)return_failed, .process = _process, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 652b837fe..1fcef03cc 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -56,6 +56,16 @@ struct private_pubkey_authenticator_t { * Reserved bytes of ID payload */ char reserved[3]; + + /** + * PPK to use + */ + chunk_t ppk; + + /** + * Add a NO_PPK_AUTH notify + */ + bool no_ppk_auth; }; /** @@ -204,17 +214,42 @@ CALLBACK(destroy_scheme, void, } /** + * Adds the given auth data to the message, either in an AUTH payload or + * a NO_PPK_AUTH notify. + * + * The data is freed. + */ +static void add_auth_to_message(message_t *message, auth_method_t method, + chunk_t data, bool notify) +{ + auth_payload_t *auth_payload; + + if (notify) + { + message->add_notify(message, FALSE, NO_PPK_AUTH, data); + } + else + { + auth_payload = auth_payload_create(); + auth_payload->set_auth_method(auth_payload, method); + auth_payload->set_data(auth_payload, data); + message->add_payload(message, (payload_t*)auth_payload); + } + chunk_free(&data); +} + +/** * Create a signature using RFC 7427 signature authentication */ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, - auth_cfg_t *auth, private_key_t *private, - identification_t *id, chunk_t *auth_data) + auth_cfg_t *auth, private_key_t *private, + identification_t *id, message_t *message) { enumerator_t *enumerator; keymat_v2_t *keymat; signature_params_t *params = NULL; array_t *schemes; - chunk_t octets = chunk_empty; + chunk_t octets = chunk_empty, auth_data; status_t status = FAILED; keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); @@ -227,26 +262,46 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, return FAILED; } - if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, - this->nonce, id, this->reserved, &octets, - schemes)) + if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, this->nonce, + this->ppk, id, this->reserved, &octets, schemes)) { enumerator = array_create_enumerator(schemes); while (enumerator->enumerate(enumerator, ¶ms)) { - if (private->sign(private, params->scheme, params->params, octets, - auth_data) && - build_signature_auth_data(auth_data, params)) - { - status = SUCCESS; - break; - } - else + if (!private->sign(private, params->scheme, params->params, octets, + &auth_data) || + !build_signature_auth_data(&auth_data, params)) { DBG2(DBG_IKE, "unable to create %N signature for %N key", signature_scheme_names, params->scheme, key_type_names, private->get_type(private)); + continue; } + add_auth_to_message(message, AUTH_DS, auth_data, FALSE); + status = SUCCESS; + + if (this->no_ppk_auth) + { + chunk_free(&octets); + + if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init, + this->nonce, chunk_empty, id, + this->reserved, &octets, schemes) && + private->sign(private, params->scheme, params->params, + octets, &auth_data) && + build_signature_auth_data(&auth_data, params)) + { + add_auth_to_message(message, AUTH_DS, auth_data, TRUE); + } + else + { + DBG2(DBG_IKE, "unable to create %N signature for %N key " + "without PPK", signature_scheme_names, params->scheme, + key_type_names, private->get_type(private)); + status = FAILED; + } + } + break; } enumerator->destroy(enumerator); } @@ -281,8 +336,8 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, * keymat). */ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, - bool verify, identification_t *id, - chunk_t *octets, signature_params_t **scheme) + bool verify, identification_t *id, chunk_t ppk, + chunk_t *octets, signature_params_t **scheme) { keymat_v2_t *keymat; array_t *schemes; @@ -293,7 +348,8 @@ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); if (keymat->get_auth_octets(keymat, verify, this->ike_sa_init, this->nonce, - id, this->reserved, octets, schemes) && + ppk, id, this->reserved, octets, + schemes) && array_remove(schemes, 0, scheme)) { success = TRUE; @@ -311,19 +367,19 @@ static bool get_auth_octets_scheme(private_pubkey_authenticator_t *this, */ static status_t sign_classic(private_pubkey_authenticator_t *this, auth_cfg_t *auth, private_key_t *private, - identification_t *id, auth_method_t *auth_method, - chunk_t *auth_data) + identification_t *id, message_t *message) { signature_scheme_t scheme; signature_params_t *params; - chunk_t octets = chunk_empty; + auth_method_t auth_method = AUTH_NONE; + chunk_t octets = chunk_empty, auth_data; status_t status = FAILED; switch (private->get_type(private)) { case KEY_RSA: scheme = SIGN_RSA_EMSA_PKCS1_SHA1; - *auth_method = AUTH_RSA; + auth_method = AUTH_RSA; break; case KEY_ECDSA: /* deduct the signature scheme from the keysize */ @@ -331,15 +387,15 @@ static status_t sign_classic(private_pubkey_authenticator_t *this, { case 256: scheme = SIGN_ECDSA_256; - *auth_method = AUTH_ECDSA_256; + auth_method = AUTH_ECDSA_256; break; case 384: scheme = SIGN_ECDSA_384; - *auth_method = AUTH_ECDSA_384; + auth_method = AUTH_ECDSA_384; break; case 521: scheme = SIGN_ECDSA_521; - *auth_method = AUTH_ECDSA_521; + auth_method = AUTH_ECDSA_521; break; default: DBG1(DBG_IKE, "%d bit ECDSA private key size not supported", @@ -356,17 +412,34 @@ static status_t sign_classic(private_pubkey_authenticator_t *this, INIT(params, .scheme = scheme, ); - if (get_auth_octets_scheme(this, FALSE, id, &octets, ¶ms) && - private->sign(private, params->scheme, NULL, octets, auth_data)) + if (get_auth_octets_scheme(this, FALSE, id, this->ppk, &octets, ¶ms) && + private->sign(private, params->scheme, NULL, octets, &auth_data)) { + add_auth_to_message(message, auth_method, auth_data, FALSE); status = SUCCESS; + + if (this->no_ppk_auth) + { + chunk_free(&octets); + if (get_auth_octets_scheme(this, FALSE, id, chunk_empty, &octets, + ¶ms) && + private->sign(private, params->scheme, NULL, octets, + &auth_data)) + { + add_auth_to_message(message, auth_method, auth_data, TRUE); + } + else + { + status = FAILED; + } + } } if (params) { signature_params_destroy(params); } DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id, - auth_method_names, *auth_method, + auth_method_names, auth_method, status == SUCCESS ? "successful" : "failed"); chunk_free(&octets); return status; @@ -378,10 +451,7 @@ METHOD(authenticator_t, build, status_t, private_key_t *private; identification_t *id; auth_cfg_t *auth; - chunk_t auth_data; status_t status; - auth_payload_t *auth_payload; - auth_method_t auth_method = AUTH_NONE; id = this->ike_sa->get_my_id(this->ike_sa); auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); @@ -394,24 +464,13 @@ METHOD(authenticator_t, build, status_t, if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH)) { - auth_method = AUTH_DS; - status = sign_signature_auth(this, auth, private, id, &auth_data); + status = sign_signature_auth(this, auth, private, id, message); } else { - status = sign_classic(this, auth, private, id, &auth_method, - &auth_data); + status = sign_classic(this, auth, private, id, message); } private->destroy(private); - - if (status == SUCCESS) - { - auth_payload = auth_payload_create(); - auth_payload->set_auth_method(auth_payload, auth_method); - auth_payload->set_data(auth_payload, auth_data); - chunk_free(&auth_data); - message->add_payload(message, (payload_t*)auth_payload); - } return status; } @@ -444,6 +503,7 @@ METHOD(authenticator_t, process, status_t, public_key_t *public; auth_method_t auth_method; auth_payload_t *auth_payload; + notify_payload_t *notify; chunk_t auth_data, octets; identification_t *id; auth_cfg_t *auth, *current_auth; @@ -459,9 +519,21 @@ METHOD(authenticator_t, process, status_t, { return FAILED; } - INIT(params); auth_method = auth_payload->get_auth_method(auth_payload); auth_data = auth_payload->get_data(auth_payload); + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK) && + !this->ppk.ptr) + { /* look for a NO_PPK_AUTH notify if we have no PPK */ + notify = message->get_notify(message, NO_PPK_AUTH); + if (notify) + { + DBG1(DBG_IKE, "no PPK available, using NO_PPK_AUTH notify"); + auth_data = notify->get_notification_data(notify); + } + } + + INIT(params); switch (auth_method) { case AUTH_RSA: @@ -491,7 +563,7 @@ METHOD(authenticator_t, process, status_t, return INVALID_ARG; } id = this->ike_sa->get_other_id(this->ike_sa); - if (!get_auth_octets_scheme(this, TRUE, id, &octets, ¶ms)) + if (!get_auth_octets_scheme(this, TRUE, id, this->ppk, &octets, ¶ms)) { return FAILED; } @@ -551,6 +623,13 @@ METHOD(authenticator_t, process, status_t, return status; } +METHOD(authenticator_t, use_ppk, void, + private_pubkey_authenticator_t *this, chunk_t ppk, bool no_ppk_auth) +{ + this->ppk = ppk; + this->no_ppk_auth = no_ppk_auth; +} + METHOD(authenticator_t, destroy, void, private_pubkey_authenticator_t *this) { @@ -571,6 +650,7 @@ pubkey_authenticator_t *pubkey_authenticator_create_builder(ike_sa_t *ike_sa, .authenticator = { .build = _build, .process = (void*)return_failed, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, @@ -598,6 +678,7 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa, .authenticator = { .build = (void*)return_failed, .process = _process, + .use_ppk = _use_ppk, .is_mutual = (void*)return_false, .destroy = _destroy, }, diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index f8b23b66e..db46b816b 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -491,6 +491,93 @@ failure: return this->skp_build.len && this->skp_verify.len; } +/** + * Derives a key from the given key and a PRF that was initialized with a PPK + */ +static bool derive_ppk_key(prf_t *prf, char *name, chunk_t key, + chunk_t *new_key) +{ + prf_plus_t *prf_plus; + + prf_plus = prf_plus_create(prf, TRUE, key); + if (!prf_plus || + !prf_plus->allocate_bytes(prf_plus, key.len, new_key)) + { + DBG1(DBG_IKE, "unable to derive %s with PPK", name); + DESTROY_IF(prf_plus); + return FALSE; + } + prf_plus->destroy(prf_plus); + return TRUE; +} + +/** + * Use the given PPK to derive a new SK_pi/r + */ +static bool derive_skp_ppk(private_keymat_v2_t *this, chunk_t ppk, chunk_t skp, + chunk_t *new_skp) +{ + if (!this->prf->set_key(this->prf, ppk)) + { + DBG1(DBG_IKE, "unable to set PPK in PRF"); + return FALSE; + } + return derive_ppk_key(this->prf, "SK_p", skp, new_skp); +} + +METHOD(keymat_v2_t, derive_ike_keys_ppk, bool, + private_keymat_v2_t *this, chunk_t ppk) +{ + chunk_t skd = chunk_empty, new_skpi = chunk_empty, new_skpr = chunk_empty; + chunk_t *skpi, *skpr; + + if (!this->skd.ptr) + { + return FALSE; + } + + if (this->initiator) + { + skpi = &this->skp_build; + skpr = &this->skp_verify; + } + else + { + skpi = &this->skp_verify; + skpr = &this->skp_build; + } + + DBG4(DBG_IKE, "derive keys using PPK %B", &ppk); + + if (!this->prf->set_key(this->prf, ppk)) + { + DBG1(DBG_IKE, "unable to set PPK in PRF"); + return FALSE; + } + if (!derive_ppk_key(this->prf, "Sk_d", this->skd, &skd) || + !derive_ppk_key(this->prf, "Sk_pi", *skpi, &new_skpi) || + !derive_ppk_key(this->prf, "Sk_pr", *skpr, &new_skpr)) + { + chunk_clear(&skd); + chunk_clear(&new_skpi); + chunk_clear(&new_skpr); + return FALSE; + } + + DBG4(DBG_IKE, "Sk_d secret %B", &skd); + chunk_clear(&this->skd); + this->skd = skd; + + DBG4(DBG_IKE, "Sk_pi secret %B", &new_skpi); + chunk_clear(skpi); + *skpi = new_skpi; + + DBG4(DBG_IKE, "Sk_pr secret %B", &new_skpr); + chunk_clear(skpr); + *skpr = new_skpr; + return TRUE; +} + METHOD(keymat_v2_t, derive_child_keys, bool, private_keymat_v2_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, @@ -632,13 +719,23 @@ METHOD(keymat_t, get_aead, aead_t*, METHOD(keymat_v2_t, get_auth_octets, bool, private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets, - array_t *schemes) + chunk_t nonce, chunk_t ppk, identification_t *id, char reserved[3], + chunk_t *octets, array_t *schemes) { chunk_t chunk, idx; + chunk_t skp_ppk = chunk_empty; chunk_t skp; skp = verify ? this->skp_verify : this->skp_build; + if (ppk.ptr) + { + DBG4(DBG_IKE, "PPK %B", &ppk); + if (!derive_skp_ppk(this, ppk, skp, &skp_ppk)) + { + return FALSE; + } + skp = skp_ppk; + } chunk = chunk_alloca(4); chunk.ptr[0] = id->get_type(id); @@ -650,8 +747,10 @@ METHOD(keymat_v2_t, get_auth_octets, bool, if (!this->prf->set_key(this->prf, skp) || !this->prf->allocate_bytes(this->prf, idx, &chunk)) { + chunk_clear(&skp_ppk); return FALSE; } + chunk_clear(&skp_ppk); *octets = chunk_cat("ccm", ike_sa_init, nonce, chunk); DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", octets); return TRUE; @@ -665,41 +764,53 @@ METHOD(keymat_v2_t, get_auth_octets, bool, METHOD(keymat_v2_t, get_psk_sig, bool, private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce, - chunk_t secret, identification_t *id, char reserved[3], chunk_t *sig) + chunk_t secret, chunk_t ppk, identification_t *id, char reserved[3], + chunk_t *sig) { - chunk_t key_pad, key, octets; + chunk_t skp_ppk = chunk_empty, key = chunk_empty, octets = chunk_empty; + chunk_t key_pad; + bool success = FALSE; if (!secret.len) { /* EAP uses SK_p if no MSK has been established */ secret = verify ? this->skp_verify : this->skp_build; + if (ppk.ptr) + { + if (!derive_skp_ppk(this, ppk, secret, &skp_ppk)) + { + return FALSE; + } + secret = skp_ppk; + } } - if (!get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved, + if (!get_auth_octets(this, verify, ike_sa_init, nonce, ppk, id, reserved, &octets, NULL)) { - return FALSE; + goto failure; } /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */ key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH); if (!this->prf->set_key(this->prf, secret) || !this->prf->allocate_bytes(this->prf, key_pad, &key)) { - chunk_free(&octets); - return FALSE; + goto failure; } if (!this->prf->set_key(this->prf, key) || !this->prf->allocate_bytes(this->prf, octets, sig)) { - chunk_free(&key); - chunk_free(&octets); - return FALSE; + goto failure; } DBG4(DBG_IKE, "secret %B", &secret); DBG4(DBG_IKE, "prf(secret, keypad) %B", &key); DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", sig); + success = TRUE; + +failure: + chunk_clear(&skp_ppk); chunk_free(&octets); chunk_free(&key); + return success; - return TRUE; } METHOD(keymat_v2_t, hash_algorithm_supported, bool, @@ -752,6 +863,7 @@ keymat_v2_t *keymat_v2_create(bool initiator) .destroy = _destroy, }, .derive_ike_keys = _derive_ike_keys, + .derive_ike_keys_ppk = _derive_ike_keys_ppk, .derive_child_keys = _derive_child_keys, .get_skd = _get_skd, .get_auth_octets = _get_auth_octets, diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h index 5dc9cda38..3cc071aeb 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.h +++ b/src/libcharon/sa/ikev2/keymat_v2.h @@ -58,6 +58,16 @@ struct keymat_v2_t { chunk_t rekey_skd); /** + * Derive SK_d, SK_pi and SK_pr after authentication using the given + * Postquantum Preshared Key and the previous values of these keys that + * were derived by derive_ike_keys(). + * + * @param ppk the postquantum preshared key + * @return TRUE on success + */ + bool (*derive_ike_keys_ppk)(keymat_v2_t *this, chunk_t ppk); + + /** * Derive keys for a CHILD_SA. * * The keys for the CHILD_SA are allocated in the integ and encr chunks. @@ -95,9 +105,10 @@ struct keymat_v2_t { * key. PSK and EAP authentication include a secret into the data, use * the get_psk_sig() method instead. * - * @param verify TRUE to create for verfification, FALSE to sign + * @param verify TRUE to create for verification, FALSE to sign * @param ike_sa_init encoded ike_sa_init message * @param nonce nonce value + * @param ppk optional postquantum preshared key * @param id identity * @param reserved reserved bytes of id_payload * @param octests chunk receiving allocated auth octets @@ -107,7 +118,7 @@ struct keymat_v2_t { * @return TRUE if octets created successfully */ bool (*get_auth_octets)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, + chunk_t nonce, chunk_t ppk, identification_t *id, char reserved[3], chunk_t *octets, array_t *schemes); /** @@ -117,17 +128,18 @@ struct keymat_v2_t { * includes the secret into the signature. If no secret is given, SK_p is * used as secret (used for EAP methods without MSK). * - * @param verify TRUE to create for verfification, FALSE to sign + * @param verify TRUE to create for verification, FALSE to sign * @param ike_sa_init encoded ike_sa_init message * @param nonce nonce value * @param secret optional secret to include into signature + * @param ppk optional postquantum preshared key * @param id identity * @param reserved reserved bytes of id_payload * @param sign chunk receiving allocated signature octets * @return TRUE if signature created successfully */ bool (*get_psk_sig)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init, - chunk_t nonce, chunk_t secret, + chunk_t nonce, chunk_t secret, chunk_t ppk, identification_t *id, char reserved[3], chunk_t *sig); /** diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index fff567233..910c77a2d 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -109,7 +109,7 @@ struct private_task_manager_t { array_t *packets; /** - * type of the initated exchange + * type of the initiated exchange */ exchange_type_t type; @@ -1946,8 +1946,7 @@ METHOD(task_manager_t, queue_dpd, void, { ike_mobike_t *mobike; - if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) && - this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) + if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) { #ifdef ME peer_cfg_t *cfg = this->ike_sa->get_peer_cfg(this->ike_sa); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index c90af23b9..c7eb0c854 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -481,12 +481,14 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, this->ike_sa->has_condition(this->ike_sa, cond)) { nat = get_transport_nat_ts(this, local, in); - ts = this->config->get_traffic_selectors(this->config, local, nat, hosts); + ts = this->config->get_traffic_selectors(this->config, local, nat, + hosts, TRUE); nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy)); } else { - ts = this->config->get_traffic_selectors(this->config, local, in, hosts); + ts = this->config->get_traffic_selectors(this->config, local, in, + hosts, TRUE); } hosts->destroy(hosts); @@ -497,8 +499,8 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, /** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal - * - INVALID_ARG: diffie hellman group inacceptable - * - NOT_FOUND: TS inacceptable + * - INVALID_ARG: diffie hellman group unacceptable + * - NOT_FOUND: TS unacceptable */ static status_t select_and_install(private_child_create_t *this, bool no_dh, bool ike_auth) @@ -559,7 +561,7 @@ static status_t select_and_install(private_child_create_t *this, if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &group, NULL)) { - DBG1(DBG_IKE, "DH group %N inacceptable, requesting %N", + DBG1(DBG_IKE, "DH group %N unacceptable, requesting %N", diffie_hellman_group_names, this->dh_group, diffie_hellman_group_names, group); this->dh_group = group; @@ -1075,7 +1077,7 @@ METHOD(task_t, build_i, status_t, if (list->get_count(list)) { this->tsi = this->config->get_traffic_selectors(this->config, - TRUE, NULL, list); + TRUE, NULL, list, TRUE); list->destroy_offset(list, offsetof(host_t, destroy)); } else @@ -1083,12 +1085,12 @@ METHOD(task_t, build_i, status_t, list->destroy(list); list = get_dynamic_hosts(this->ike_sa, TRUE); this->tsi = this->config->get_traffic_selectors(this->config, - TRUE, NULL, list); + TRUE, NULL, list, TRUE); list->destroy(list); } list = get_dynamic_hosts(this->ike_sa, FALSE); this->tsr = this->config->get_traffic_selectors(this->config, - FALSE, NULL, list); + FALSE, NULL, list, TRUE); list->destroy(list); if (this->packet_tsi) @@ -1356,7 +1358,7 @@ METHOD(task_t, build_r, status_t, } if (this->config == NULL) { - DBG1(DBG_IKE, "traffic selectors %#R === %#R inacceptable", + DBG1(DBG_IKE, "traffic selectors %#R === %#R unacceptable", this->tsr, this->tsi); charon->bus->alert(charon->bus, ALERT_TS_MISMATCH, this->tsi, this->tsr); message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 6b63197d5..b055ff064 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2015 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -24,6 +24,7 @@ #include <encoding/payloads/auth_payload.h> #include <encoding/payloads/eap_payload.h> #include <encoding/payloads/nonce_payload.h> +#include <sa/ikev2/keymat_v2.h> #include <sa/ikev2/authenticators/eap_authenticator.h> #include <processing/jobs/delete_ike_sa_job.h> @@ -60,6 +61,16 @@ struct private_ike_auth_t { chunk_t other_nonce; /** + * PPK_ID sent or received + */ + identification_t *ppk_id; + + /** + * Optional PPK to use + */ + chunk_t ppk; + + /** * IKE_SA_INIT message sent by us */ packet_t *my_packet; @@ -144,7 +155,7 @@ static status_t collect_my_init_data(private_ike_auth_t *this, /* get the nonce that was generated in ike_init */ nonce = (nonce_payload_t*)message->get_payload(message, PLV2_NONCE); - if (nonce == NULL) + if (!nonce) { return FAILED; } @@ -170,7 +181,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this, /* get the nonce that was generated in ike_init */ nonce = (nonce_payload_t*)message->get_payload(message, PLV2_NONCE); - if (nonce == NULL) + if (!nonce) { return FAILED; } @@ -279,19 +290,47 @@ static bool do_another_auth(private_ike_auth_t *this) } /** + * Check if this is the first authentication round + */ +static bool is_first_round(private_ike_auth_t *this, bool local) +{ + enumerator_t *done; + auth_cfg_t *cfg; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH)) + { + return TRUE; + } + + done = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, local); + if (done->enumerate(done, &cfg)) + { + done->destroy(done); + return FALSE; + } + done->destroy(done); + return TRUE; +} + +/** * Get peer configuration candidates from backends */ static bool load_cfg_candidates(private_ike_auth_t *this) { enumerator_t *enumerator; peer_cfg_t *peer_cfg; + ike_cfg_t *ike_cfg; host_t *me, *other; identification_t *my_id, *other_id; + proposal_t *ike_proposal; + bool private; me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); + ike_proposal = this->ike_sa->get_proposal(this->ike_sa); + private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); DBG1(DBG_CFG, "looking for peer configs matching %H[%Y]...%H[%Y]", me, my_id, other, other_id); @@ -299,11 +338,18 @@ static bool load_cfg_candidates(private_ike_auth_t *this) me, other, my_id, other_id, IKEV2); while (enumerator->enumerate(enumerator, &peer_cfg)) { + /* ignore all configs that have no matching IKE proposal */ + ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); + if (!ike_cfg->has_proposal(ike_cfg, ike_proposal, private)) + { + DBG2(DBG_CFG, "ignore candidate '%s' without matching IKE proposal", + peer_cfg->get_name(peer_cfg)); + continue; + } peer_cfg->get_ref(peer_cfg); - if (this->peer_cfg == NULL) + if (!this->peer_cfg) { /* best match */ this->peer_cfg = peer_cfg; - this->ike_sa->set_peer_cfg(this->ike_sa, peer_cfg); } else { @@ -313,6 +359,7 @@ static bool load_cfg_candidates(private_ike_auth_t *this) enumerator->destroy(enumerator); if (this->peer_cfg) { + this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg); DBG1(DBG_CFG, "selected peer config '%s'", this->peer_cfg->get_name(this->peer_cfg)); return TRUE; @@ -369,7 +416,7 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) { break; } - DBG1(DBG_CFG, "selected peer config '%s' inacceptable: %s", + DBG1(DBG_CFG, "selected peer config '%s' unacceptable: %s", this->peer_cfg->get_name(this->peer_cfg), comply_error); this->peer_cfg->destroy(this->peer_cfg); } @@ -391,6 +438,149 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) return this->peer_cfg != NULL; } +/** + * Currently defined PPK_ID types + */ +#define PPK_ID_OPAQUE 1 +#define PPK_ID_FIXED 2 + +/** + * Parse the payload data of the given PPK_IDENTITY notify + */ +static bool parse_ppk_identity(notify_payload_t *notify, identification_t **id) +{ + chunk_t data; + + data = notify->get_notification_data(notify); + if (data.len < 2) + { + return FALSE; + } + switch (data.ptr[0]) + { + case PPK_ID_FIXED: + data = chunk_skip(data, 1); + break; + default: + return FALSE; + } + *id = identification_create_from_data(data); + return TRUE; +} + +/** + * Add a PPK_IDENTITY with the given PPK_ID to the given message + */ +static void add_ppk_identity(identification_t *id, message_t *msg) +{ + chunk_t data; + uint8_t type = PPK_ID_FIXED; + + /* we currently only support one type */ + data = chunk_cata("cc", chunk_from_thing(type), id->get_encoding(id)); + msg->add_notify(msg, FALSE, PPK_IDENTITY, data); +} + +/** + * Use the given PPK_ID to find a PPK and store it and the ID in the task + */ +static bool get_ppk(private_ike_auth_t *this, identification_t *ppk_id) +{ + shared_key_t *key; + + key = lib->credmgr->get_shared(lib->credmgr, SHARED_PPK, ppk_id, NULL); + if (!key) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but no PPK found for '%Y'", ppk_id); + return FALSE; + } + DBG1(DBG_CFG, "no PPK for '%Y' found, ignored because PPK is not " + "required", ppk_id); + return TRUE; + } + this->ppk = chunk_clone(key->get_key(key)); + this->ppk_id = ppk_id->clone(ppk_id); + key->destroy(key); + return TRUE; +} + +/** + * Check if we have a PPK available and, if not, whether we require one as + * initiator + */ +static bool get_ppk_i(private_ike_auth_t *this) +{ + identification_t *ppk_id; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_PPK)) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but peer does not support PPK"); + return FALSE; + } + return TRUE; + } + + ppk_id = this->peer_cfg->get_ppk_id(this->peer_cfg); + if (!ppk_id) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but no PPK_ID configured"); + return FALSE; + } + return TRUE; + } + return get_ppk(this, ppk_id); +} + +/** + * Check if we have a PPK available and if not whether we require one as + * responder + */ +static bool get_ppk_r(private_ike_auth_t *this, message_t *msg) +{ + notify_payload_t *notify; + identification_t *ppk_id, *ppk_id_cfg; + bool result; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_PPK)) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but peer does not support PPK"); + return FALSE; + } + return TRUE; + } + + notify = msg->get_notify(msg, PPK_IDENTITY); + if (!notify || !parse_ppk_identity(notify, &ppk_id)) + { + if (this->peer_cfg->ppk_required(this->peer_cfg)) + { + DBG1(DBG_CFG, "PPK required but no PPK_IDENTITY received"); + return FALSE; + } + return TRUE; + } + + ppk_id_cfg = this->peer_cfg->get_ppk_id(this->peer_cfg); + if (ppk_id_cfg && !ppk_id->matches(ppk_id, ppk_id_cfg)) + { + DBG1(DBG_CFG, "received PPK_ID '%Y', but require '%Y'", ppk_id, + ppk_id_cfg); + ppk_id->destroy(ppk_id); + return FALSE; + } + result = get_ppk(this, ppk_id); + ppk_id->destroy(ppk_id); + return result; +} + METHOD(task_t, build_i, status_t, private_ike_auth_t *this, message_t *message) { @@ -401,7 +591,7 @@ METHOD(task_t, build_i, status_t, return collect_my_init_data(this, message); } - if (this->peer_cfg == NULL) + if (!this->peer_cfg) { this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); this->peer_cfg->get_ref(this->peer_cfg); @@ -420,6 +610,12 @@ METHOD(task_t, build_i, status_t, /* indicate support for RFC 6311 Message ID synchronization */ message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED, chunk_empty); + /* only use a PPK in the first round */ + if (!get_ppk_i(this)) + { + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + return FAILED; + } } if (!this->do_another_auth && !this->my_auth) @@ -428,7 +624,7 @@ METHOD(task_t, build_i, status_t, } /* check if an authenticator is in progress */ - if (this->my_auth == NULL) + if (!this->my_auth) { identification_t *idi, *idr = NULL; id_payload_t *id_payload; @@ -495,6 +691,14 @@ METHOD(task_t, build_i, status_t, return FAILED; } } + /* for authentication methods that return NEED_MORE, the PPK will be reset + * in process_i() for messages without PPK_ID notify, so we always set it + * during the first round (afterwards the PPK won't be available) */ + if (this->ppk.ptr && this->my_auth->use_ppk) + { + this->my_auth->use_ppk(this->my_auth, this->ppk, + !this->peer_cfg->ppk_required(this->peer_cfg)); + } switch (this->my_auth->build(this->my_auth, message)) { case SUCCESS: @@ -509,6 +713,12 @@ METHOD(task_t, build_i, status_t, return FAILED; } + /* add a PPK_IDENTITY notify to the message that contains AUTH */ + if (this->ppk_id && message->get_payload(message, PLV2_AUTH)) + { + add_ppk_identity(this->ppk_id, message); + } + /* check for additional authentication rounds */ if (do_another_auth(this)) { @@ -536,7 +746,7 @@ METHOD(task_t, process_r, status_t, return collect_other_init_data(this, message); } - if (this->my_auth == NULL && this->do_another_auth) + if (!this->my_auth && this->do_another_auth) { /* handle (optional) IDr payload, apply proposed identity */ id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_RESPONDER); @@ -573,7 +783,7 @@ METHOD(task_t, process_r, status_t, } } - if (this->other_auth == NULL) + if (!this->other_auth) { /* handle IDi payload */ id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_INITIATOR); @@ -588,7 +798,7 @@ METHOD(task_t, process_r, status_t, cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id)); - if (this->peer_cfg == NULL) + if (!this->peer_cfg) { if (!load_cfg_candidates(this)) { @@ -596,14 +806,14 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } } - if (message->get_payload(message, PLV2_AUTH) == NULL) + if (!message->get_payload(message, PLV2_AUTH)) { /* before authenticating with EAP, we need a EAP config */ cand = get_auth_cfg(this, FALSE); while (!cand || ( (uintptr_t)cand->get(cand, AUTH_RULE_EAP_TYPE) == EAP_NAK && (uintptr_t)cand->get(cand, AUTH_RULE_EAP_VENDOR) == 0)) { /* peer requested EAP, but current config does not match */ - DBG1(DBG_IKE, "peer requested EAP, config inacceptable"); + DBG1(DBG_IKE, "peer requested EAP, config unacceptable"); this->peer_cfg->destroy(this->peer_cfg); this->peer_cfg = NULL; if (!update_cfg_candidates(this, FALSE)) @@ -642,6 +852,19 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } } + if (message->get_payload(message, PLV2_AUTH) && + is_first_round(this, FALSE)) + { + if (!get_ppk_r(this, message)) + { + this->authentication_failed = TRUE; + return NEED_MORE; + } + else if (this->ppk.ptr && this->other_auth->use_ppk) + { + this->other_auth->use_ppk(this->other_auth, this->ppk, FALSE); + } + } switch (this->other_auth->process(this->other_auth, message)) { case SUCCESS: @@ -675,7 +898,7 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } - if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL) + if (!message->get_notify(message, ANOTHER_AUTH_FOLLOWS)) { this->expect_another_auth = FALSE; if (!update_cfg_candidates(this, TRUE)) @@ -687,6 +910,37 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } +/** + * Clear the PPK and PPK_ID + */ +static void clear_ppk(private_ike_auth_t *this) +{ + DESTROY_IF(this->ppk_id); + this->ppk_id = NULL; + chunk_clear(&this->ppk); +} + +/** + * Derive new keys and clear the PPK + */ +static bool apply_ppk(private_ike_auth_t *this) +{ + keymat_v2_t *keymat; + + if (this->ppk.ptr) + { + keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa); + if (!keymat->derive_ike_keys_ppk(keymat, this->ppk)) + { + return FALSE; + } + DBG1(DBG_CFG, "using PPK for PPK_ID '%Y'", this->ppk_id); + this->ike_sa->set_condition(this->ike_sa, COND_PPK, TRUE); + } + clear_ppk(this); + return TRUE; +} + METHOD(task_t, build_r, status_t, private_ike_auth_t *this, message_t *message) { @@ -703,12 +957,12 @@ METHOD(task_t, build_r, status_t, return collect_my_init_data(this, message); } - if (this->authentication_failed || this->peer_cfg == NULL) + if (this->authentication_failed || !this->peer_cfg) { goto peer_auth_failed; } - if (this->my_auth == NULL && this->do_another_auth) + if (!this->my_auth && this->do_another_auth) { identification_t *id, *id_cfg; id_payload_t *id_payload; @@ -793,6 +1047,10 @@ METHOD(task_t, build_r, status_t, } if (this->my_auth) { + if (this->ppk.ptr && this->my_auth->use_ppk) + { + this->my_auth->use_ppk(this->my_auth, this->ppk, FALSE); + } switch (this->my_auth->build(this->my_auth, message)) { case SUCCESS: @@ -807,6 +1065,16 @@ METHOD(task_t, build_r, status_t, } } + /* add a PPK_IDENTITY notify and derive new keys and clear the PPK */ + if (this->ppk.ptr) + { + message->add_notify(message, FALSE, PPK_IDENTITY, chunk_empty); + if (!apply_ppk(this)) + { + goto local_auth_failed; + } + } + /* check for additional authentication rounds */ if (do_another_auth(this)) { @@ -942,7 +1210,7 @@ METHOD(task_t, process_i, status_t, enumerator_t *enumerator; payload_t *payload; auth_cfg_t *cfg; - bool mutual_eap = FALSE; + bool mutual_eap = FALSE, ppk_id_received = FALSE; if (message->get_exchange_type(message) == IKE_SA_INIT) { @@ -998,6 +1266,9 @@ METHOD(task_t, process_i, status_t, this->ike_sa->enable_extension(this->ike_sa, EXT_IKE_MESSAGE_ID_SYNC); break; + case PPK_IDENTITY: + ppk_id_received = TRUE; + break; default: { if (type <= 16383) @@ -1019,7 +1290,7 @@ METHOD(task_t, process_i, status_t, if (this->expect_another_auth) { - if (this->other_auth == NULL) + if (!this->other_auth) { id_payload_t *id_payload; identification_t *id; @@ -1059,6 +1330,11 @@ METHOD(task_t, process_i, status_t, } if (this->other_auth) { + if (ppk_id_received && is_first_round(this, FALSE) && + this->other_auth->use_ppk) + { + this->other_auth->use_ppk(this->other_auth, this->ppk, FALSE); + } switch (this->other_auth->process(this->other_auth, message)) { case SUCCESS: @@ -1094,6 +1370,14 @@ METHOD(task_t, process_i, status_t, if (this->my_auth) { + /* while we already set the PPK in build_i(), we MUST not use it if + * the peer did not reply with a PPK_ID notify */ + if (this->ppk.ptr && this->my_auth->use_ppk) + { + this->my_auth->use_ppk(this->my_auth, + ppk_id_received ? this->ppk : chunk_empty, + FALSE); + } switch (this->my_auth->process(this->my_auth, message)) { case SUCCESS: @@ -1109,11 +1393,29 @@ METHOD(task_t, process_i, status_t, case NEED_MORE: break; default: - charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); - send_auth_failed_informational(this, message); - return FAILED; + goto local_auth_failed; + } + } + + /* change keys and clear PPK after we are done with our authentication, so + * we only explicitly use it for the first round, afterwards we just use the + * changed SK_p keys implicitly */ + if (!this->my_auth && this->ppk_id) + { + if (ppk_id_received) + { + if (!apply_ppk(this)) + { + goto local_auth_failed; + } + } + else + { + DBG1(DBG_CFG, "peer didn't use PPK for PPK_ID '%Y'", this->ppk_id); } + clear_ppk(this); } + if (mutual_eap) { if (!this->my_auth || !this->my_auth->is_mutual(this->my_auth)) @@ -1124,7 +1426,7 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "allow mutual EAP-only authentication"); } - if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL) + if (!message->get_notify(message, ANOTHER_AUTH_FOLLOWS)) { this->expect_another_auth = FALSE; } @@ -1162,6 +1464,10 @@ peer_auth_failed: charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); send_auth_failed_informational(this, message); return FAILED; +local_auth_failed: + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + send_auth_failed_informational(this, message); + return FAILED; } METHOD(task_t, get_type, task_type_t, @@ -1173,6 +1479,7 @@ METHOD(task_t, get_type, task_type_t, METHOD(task_t, migrate, void, private_ike_auth_t *this, ike_sa_t *ike_sa) { + clear_ppk(this); chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); DESTROY_IF(this->my_packet); @@ -1199,6 +1506,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_ike_auth_t *this) { + clear_ppk(this); chunk_free(&this->my_nonce); chunk_free(&this->other_nonce); DESTROY_IF(this->my_packet); diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h index f6862ca27..fd14e9faf 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h +++ b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h @@ -45,7 +45,7 @@ struct ike_auth_lifetime_t { * Create a new TASK_IKE_AUTH_LIFETIME task. * * @param ike_sa IKE_SA this task works for - * @param initiator TRUE if taks is initiated by us + * @param initiator TRUE if task is initiated by us * @return ike_auth_lifetime task to handle by the task_manager */ ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 3d73d728b..307d99264 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -55,11 +55,6 @@ struct private_ike_init_t { bool initiator; /** - * IKE config to establish - */ - ike_cfg_t *config; - - /** * diffie hellman group to use */ diffie_hellman_group_t dh_group; @@ -275,6 +270,38 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this, } /** + * Check whether to send a USE_PPK notify + */ +static bool send_use_ppk(private_ike_init_t *this) +{ + peer_cfg_t *peer; + enumerator_t *keys; + shared_key_t *key; + bool use_ppk = FALSE; + + if (this->initiator) + { + peer = this->ike_sa->get_peer_cfg(this->ike_sa); + if (peer->get_ppk_id(peer)) + { + use_ppk = TRUE; + } + } + else if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK)) + { + /* check if we have at least one PPK available */ + keys = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PPK, + NULL, NULL); + if (keys->enumerate(keys, &key, NULL, NULL)) + { + use_ppk = TRUE; + } + keys->destroy(keys); + } + return use_ppk; +} + +/** * build the payloads for the message */ static bool build_payloads(private_ike_init_t *this, message_t *message) @@ -286,14 +313,15 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) ike_sa_id_t *id; proposal_t *proposal; enumerator_t *enumerator; + ike_cfg_t *ike_cfg; id = this->ike_sa->get_id(this->ike_sa); - this->config = this->ike_sa->get_ike_cfg(this->ike_sa); + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); if (this->initiator) { - proposal_list = this->config->get_proposals(this->config); + proposal_list = ike_cfg->get_proposals(ike_cfg); other_dh_groups = linked_list_create(); enumerator = proposal_list->create_enumerator(proposal_list); while (enumerator->enumerate(enumerator, (void**)&proposal)) @@ -334,8 +362,6 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) } message->add_payload(message, (payload_t*)sa_payload); - nonce_payload = nonce_payload_create(PLV2_NONCE); - nonce_payload->set_nonce(nonce_payload, this->my_nonce); ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE, this->dh); if (!ke_payload) @@ -343,6 +369,8 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) DBG1(DBG_IKE, "creating KE payload failed"); return FALSE; } + nonce_payload = nonce_payload_create(PLV2_NONCE); + nonce_payload->set_nonce(nonce_payload, this->my_nonce); if (this->old_sa) { /* payload order differs if we are rekeying */ @@ -357,7 +385,7 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) /* negotiate fragmentation if we are not rekeying */ if (!this->old_sa && - this->config->fragmentation(this->config) != FRAGMENTATION_NO) + ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO) { if (this->initiator || this->ike_sa->supports_extension(this->ike_sa, @@ -400,10 +428,77 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) chunk_empty); } } + /* notify the peer if we want to use/support PPK */ + if (!this->old_sa && send_use_ppk(this)) + { + message->add_notify(message, FALSE, USE_PPK, chunk_empty); + } return TRUE; } /** + * Process the SA payload and select a proposal + */ +static void process_sa_payload(private_ike_init_t *this, message_t *message, + sa_payload_t *sa_payload) +{ + ike_cfg_t *ike_cfg, *cfg, *alt_cfg = NULL; + enumerator_t *enumerator; + linked_list_t *proposal_list; + host_t *me, *other; + bool private, prefer_configured; + + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + + proposal_list = sa_payload->get_proposals(sa_payload); + private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); + + this->proposal = ike_cfg->select_proposal(ike_cfg, proposal_list, private, + prefer_configured); + if (!this->proposal) + { + if (!this->initiator && !this->old_sa) + { + me = message->get_destination(message); + other = message->get_source(message); + enumerator = charon->backends->create_ike_cfg_enumerator( + charon->backends, me, other, IKEV2); + while (enumerator->enumerate(enumerator, &cfg)) + { + if (ike_cfg == cfg) + { /* already tried and failed */ + continue; + } + DBG1(DBG_IKE, "no matching proposal found, trying alternative " + "config"); + this->proposal = cfg->select_proposal(cfg, proposal_list, + private, prefer_configured); + if (this->proposal) + { + alt_cfg = cfg->get_ref(cfg); + break; + } + } + enumerator->destroy(enumerator); + } + if (alt_cfg) + { + this->ike_sa->set_ike_cfg(this->ike_sa, alt_cfg); + alt_cfg->destroy(alt_cfg); + } + else + { + charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, + proposal_list); + } + } + proposal_list->destroy_offset(proposal_list, + offsetof(proposal_t, destroy)); +} + +/** * Read payloads from message */ static void process_payloads(private_ike_init_t *this, message_t *message) @@ -419,24 +514,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message) { case PLV2_SECURITY_ASSOCIATION: { - sa_payload_t *sa_payload = (sa_payload_t*)payload; - linked_list_t *proposal_list; - bool private, prefer_configured; - - proposal_list = sa_payload->get_proposals(sa_payload); - private = this->ike_sa->supports_extension(this->ike_sa, - EXT_STRONGSWAN); - prefer_configured = lib->settings->get_bool(lib->settings, - "%s.prefer_configured_proposals", TRUE, lib->ns); - this->proposal = this->config->select_proposal(this->config, - proposal_list, private, prefer_configured); - if (!this->proposal) - { - charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, - proposal_list); - } - proposal_list->destroy_offset(proposal_list, - offsetof(proposal_t, destroy)); + process_sa_payload(this, message, (sa_payload_t*)payload); break; } case PLV2_KEY_EXCHANGE: @@ -469,6 +547,13 @@ static void process_payloads(private_ike_init_t *this, message_t *message) handle_supported_hash_algorithms(this, notify); } break; + case USE_PPK: + if (!this->old_sa) + { + this->ike_sa->enable_extension(this->ike_sa, + EXT_PPK); + } + break; case REDIRECTED_FROM: { identification_t *gateway; @@ -533,7 +618,10 @@ static void process_payloads(private_ike_init_t *this, message_t *message) METHOD(task_t, build_i, status_t, private_ike_init_t *this, message_t *message) { - this->config = this->ike_sa->get_ike_cfg(this->ike_sa); + ike_cfg_t *ike_cfg; + + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H", this->ike_sa->get_name(this->ike_sa), this->ike_sa->get_unique_id(this->ike_sa), @@ -563,12 +651,12 @@ METHOD(task_t, build_i, status_t, } else { /* this shouldn't happen, but let's be safe */ - this->dh_group = this->config->get_dh_group(this->config); + this->dh_group = ike_cfg->get_dh_group(ike_cfg); } } else { - this->dh_group = this->config->get_dh_group(this->config); + this->dh_group = ike_cfg->get_dh_group(ike_cfg); } this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, this->dh_group); @@ -627,7 +715,6 @@ METHOD(task_t, build_i, status_t, METHOD(task_t, process_r, status_t, private_ike_init_t *this, message_t *message) { - this->config = this->ike_sa->get_ike_cfg(this->ike_sa); DBG0(DBG_IKE, "%H is initiating an IKE_SA", message->get_source(message)); this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); @@ -699,7 +786,7 @@ METHOD(task_t, build_r, status_t, if (this->proposal == NULL || this->other_nonce.len == 0 || this->my_nonce.len == 0) { - DBG1(DBG_IKE, "received proposals inacceptable"); + DBG1(DBG_IKE, "received proposals unacceptable"); message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return FAILED; } @@ -728,7 +815,7 @@ METHOD(task_t, build_r, status_t, if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &group, NULL)) { - DBG1(DBG_IKE, "DH group %N inacceptable, requesting %N", + DBG1(DBG_IKE, "DH group %N unacceptable, requesting %N", diffie_hellman_group_names, this->dh_group, diffie_hellman_group_names, group); this->dh_group = group; @@ -770,12 +857,14 @@ METHOD(task_t, build_r, status_t, */ static void raise_alerts(private_ike_init_t *this, notify_type_t type) { + ike_cfg_t *ike_cfg; linked_list_t *list; switch (type) { case NO_PROPOSAL_CHOSEN: - list = this->config->get_proposals(this->config); + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + list = ike_cfg->get_proposals(ike_cfg); charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, list); list->destroy_offset(list, offsetof(proposal_t, destroy)); break; diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index fe41a1cac..b2ad0a02a 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -193,7 +193,7 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) case NAT_DETECTION_DESTINATION_IP: { /* NAT check in this MOBIKE exchange, create subtask for it */ - if (this->natd == NULL) + if (!this->natd) { this->natd = ike_natd_create(this->ike_sa, this->initiator); } @@ -648,7 +648,7 @@ METHOD(ike_mobike_t, roam, void, METHOD(ike_mobike_t, dpd, void, private_ike_mobike_t *this) { - if (!this->natd) + if (!this->natd && this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) { this->natd = ike_natd_create(this->ike_sa, this->initiator); } diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.h b/src/libcharon/sa/ikev2/tasks/ike_mobike.h index 288b87178..8789ac0af 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.h +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.h @@ -91,7 +91,7 @@ struct ike_mobike_t { * Create a new ike_mobike task. * * @param ike_sa IKE_SA this task works for - * @param initiator TRUE if taks is initiated by us + * @param initiator TRUE if task is initiated by us * @return ike_mobike task to handle by the task_manager */ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator); diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c index 11123b415..57f9a797e 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c @@ -259,7 +259,7 @@ METHOD(task_t, build_r, status_t, } if (this->new_sa == NULL) { - /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */ + /* IKE_SA/a CHILD_SA is in an unacceptable state, deny rekeying */ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } diff --git a/src/libcharon/sa/ikev2/tasks/ike_vendor.c b/src/libcharon/sa/ikev2/tasks/ike_vendor.c index 8d8969ea0..e81a18a14 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_vendor.c +++ b/src/libcharon/sa/ikev2/tasks/ike_vendor.c @@ -59,7 +59,7 @@ struct private_ike_vendor_t { ike_sa_t *ike_sa; /** - * Are we the inititator of this task + * Are we the initiator of this task */ bool initiator; }; diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index a83da0480..d66e70937 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -117,8 +117,10 @@ static bool install_shunt_policy(child_cfg_t *child) host_any6 = host_create_any(AF_INET6); hosts = linked_list_create_with_items(host_any, host_any6, NULL); - my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts); - other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); + my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts, + FALSE); + other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts, + FALSE); hosts->destroy(hosts); manual_prio = child->get_manual_prio(child); @@ -287,8 +289,10 @@ static void uninstall_shunt_policy(child_cfg_t *child) host_any6 = host_create_any(AF_INET6); hosts = linked_list_create_with_items(host_any, host_any6, NULL); - my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts); - other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); + my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts, + FALSE); + other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts, + FALSE); hosts->destroy(hosts); manual_prio = child->get_manual_prio(child); diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index 1a0a1acfa..987ac489d 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -115,7 +115,7 @@ extern enum_name_t *task_type_names; /** * Interface for a task, an operation handled within exchanges. * - * A task is an elemantary operation. It may be handled by a single or by + * A task is an elementary operation. It may be handled by a single or by * multiple exchanges. An exchange may even complete multiple tasks. * A task has a build() and an process() operation. The build() operation * creates payloads and adds it to the message. The process() operation @@ -128,7 +128,7 @@ extern enum_name_t *task_type_names; * that the task completed, even when the task completed unsuccessfully. The * manager then removes the task from the list. A NEED_MORE is returned when * the task needs further build()/process() calls to complete, the manager - * leaves the taks in the queue. A returned FAILED indicates a critical failure. + * leaves the task in the queue. A returned FAILED indicates a critical failure. * The manager closes the IKE_SA whenever a task returns FAILED. */ struct task_t { @@ -180,7 +180,7 @@ struct task_t { * Migrate a task to a new IKE_SA. * * After migrating a task, it goes back to a state where it can be - * used again to initate an exchange. This is useful when a task + * used again to initiate an exchange. This is useful when a task * has to get migrated to a new IKE_SA. * A special usage is when a INVALID_KE_PAYLOAD is received. A call * to reset resets the task, but uses another DH group for the next diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 979f9290a..148df3923 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -168,7 +168,7 @@ static bool dynamic_remote_ts(child_cfg_t *child) traffic_selector_t *ts; bool found = FALSE; - other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL); + other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL, FALSE); enumerator = other_ts->create_enumerator(other_ts); while (enumerator->enumerate(enumerator, &ts)) { @@ -296,11 +296,11 @@ METHOD(trap_manager_t, install, bool, child_sa = child_sa_create(me, other, child, 0, FALSE, 0, 0); list = linked_list_create_with_items(me, NULL); - my_ts = child->get_traffic_selectors(child, TRUE, NULL, list); + my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); list->destroy_offset(list, offsetof(host_t, destroy)); list = linked_list_create_with_items(other, NULL); - other_ts = child->get_traffic_selectors(child, FALSE, NULL, list); + other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE); list->destroy_offset(list, offsetof(host_t, destroy)); /* We don't know the finally negotiated protocol (ESP|AH), we install |