diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2019-01-02 10:45:36 +0100 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2019-01-02 11:07:05 +0100 |
commit | 918094fde55fa0dbfd59a5f88d576efb513a88db (patch) | |
tree | 61e31656c60a6cc928c50cd633568043673e2cbd /src/libcharon | |
parent | 69bc96f6b0b388d35e983f8d27224fa49d92918c (diff) | |
download | vyos-strongswan-918094fde55fa0dbfd59a5f88d576efb513a88db.tar.gz vyos-strongswan-918094fde55fa0dbfd59a5f88d576efb513a88db.zip |
New upstream version 5.7.2
Diffstat (limited to 'src/libcharon')
37 files changed, 723 insertions, 247 deletions
diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c index f4c01c22e..b7348f0f9 100644 --- a/src/libcharon/bus/bus.c +++ b/src/libcharon/bus/bus.c @@ -575,7 +575,7 @@ METHOD(bus_t, message, void, METHOD(bus_t, ike_keys, void, private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, - ike_sa_t *rekey, shared_key_t *shared) + ike_sa_t *rekey, shared_key_t *shared, auth_method_t method) { enumerator_t *enumerator; entry_t *entry; @@ -591,7 +591,8 @@ METHOD(bus_t, ike_keys, void, } entry->calling++; keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, dh_other, - nonce_i, nonce_r, rekey, shared); + nonce_i, nonce_r, rekey, shared, + method); entry->calling--; if (!keep) { diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h index df75683be..8a97e8dfc 100644 --- a/src/libcharon/bus/bus.h +++ b/src/libcharon/bus/bus.h @@ -353,10 +353,12 @@ struct bus_t { * @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) + * @param method auth method for key derivation (IKEv1-non-PSK only) */ void (*ike_keys)(bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, - ike_sa_t *rekey, shared_key_t *shared); + ike_sa_t *rekey, shared_key_t *shared, + auth_method_t method); /** * IKE_SA derived keys hook. diff --git a/src/libcharon/bus/listeners/listener.h b/src/libcharon/bus/listeners/listener.h index 06057eb73..0f3b8578a 100644 --- a/src/libcharon/bus/listeners/listener.h +++ b/src/libcharon/bus/listeners/listener.h @@ -88,11 +88,13 @@ struct listener_t { * @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) + * @param method auth method for key derivation (IKEv1-non-PSK only) * @return TRUE to stay registered, FALSE to unregister */ bool (*ike_keys)(listener_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, - ike_sa_t *rekey, shared_key_t *shared); + ike_sa_t *rekey, shared_key_t *shared, + auth_method_t method); /** * Hook called with derived IKE_SA keys. diff --git a/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c b/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c index 644cff029..1abbf7731 100644 --- a/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c +++ b/src/libcharon/plugins/bypass_lan/bypass_lan_listener.c @@ -64,6 +64,7 @@ typedef struct { private_bypass_lan_listener_t *listener; host_t *net; uint8_t mask; + char *iface; child_cfg_t *cfg; } bypass_policy_t; @@ -85,6 +86,7 @@ static void bypass_policy_destroy(bypass_policy_t *this) ts->destroy(ts); } this->net->destroy(this->net); + free(this->iface); free(this); } @@ -126,6 +128,7 @@ static job_requeue_t update_bypass(private_bypass_lan_listener_t *this) enumerator_t *enumerator; hashtable_t *seen; bypass_policy_t *found, *lookup; + traffic_selector_t *ts; host_t *net; uint8_t mask; char *iface; @@ -146,6 +149,7 @@ static job_requeue_t update_bypass(private_bypass_lan_listener_t *this) INIT(lookup, .net = net->clone(net), .mask = mask, + .iface = strdupnull(iface), ); found = seen->put(seen, lookup, lookup); if (found) @@ -160,7 +164,6 @@ static job_requeue_t update_bypass(private_bypass_lan_listener_t *this) .mode = MODE_PASS, }; child_cfg_t *cfg; - traffic_selector_t *ts; char name[128]; ts = traffic_selector_create_from_subnet(net->clone(net), mask, @@ -176,6 +179,7 @@ static job_requeue_t update_bypass(private_bypass_lan_listener_t *this) INIT(found, .net = net->clone(net), .mask = mask, + .iface = strdupnull(iface), .cfg = cfg, ); this->policies->put(this->policies, found, found); @@ -186,11 +190,29 @@ static job_requeue_t update_bypass(private_bypass_lan_listener_t *this) enumerator = this->policies->create_enumerator(this->policies); while (enumerator->enumerate(enumerator, NULL, &lookup)) { - if (!seen->get(seen, lookup)) + found = seen->get(seen, lookup); + if (!found) { this->policies->remove_at(this->policies, enumerator); bypass_policy_destroy(lookup); } + else if (!streq(lookup->iface, found->iface)) + { /* if the subnet is on multiple interfaces, we only get the last + * one (hopefully, they are enumerated in a consistent order) */ + ts = traffic_selector_create_from_subnet( + lookup->net->clone(lookup->net), + lookup->mask, 0, 0, 65535); + DBG1(DBG_IKE, "interface change for bypass policy for %R (from %s " + "to %s)", ts, lookup->iface, found->iface); + ts->destroy(ts); + free(lookup->iface); + lookup->iface = strdupnull(found->iface); + /* there is currently no API to update shunts, so we remove and + * reinstall it to update the route */ + charon->shunts->uninstall(charon->shunts, "bypass-lan", + lookup->cfg->get_name(lookup->cfg)); + charon->shunts->install(charon->shunts, "bypass-lan", lookup->cfg); + } } enumerator->destroy(enumerator); this->mutex->unlock(this->mutex); diff --git a/src/libcharon/plugins/dhcp/dhcp_socket.c b/src/libcharon/plugins/dhcp/dhcp_socket.c index 1e208d094..ecd92f2ef 100644 --- a/src/libcharon/plugins/dhcp/dhcp_socket.c +++ b/src/libcharon/plugins/dhcp/dhcp_socket.c @@ -489,6 +489,16 @@ static void handle_offer(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen) offer = host_create_from_chunk(AF_INET, chunk_from_thing(dhcp->your_address), 0); + if (offer->is_anyaddr(offer)) + { + server = host_create_from_chunk(AF_INET, + chunk_from_thing(dhcp->server_address), 0); + DBG1(DBG_CFG, "ignoring DHCP OFFER %+H from %H", offer, server); + server->destroy(server); + offer->destroy(offer); + return; + } + this->mutex->lock(this->mutex); enumerator = this->discover->create_enumerator(this->discover); while (enumerator->enumerate(enumerator, &transaction)) diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c index fbbf6da83..ae1371b45 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius.c +++ b/src/libcharon/plugins/eap_radius/eap_radius.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2009 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -156,7 +156,7 @@ void eap_radius_build_attributes(radius_message_t *request) { ike_sa_t *ike_sa; host_t *host; - char buf[40], *station_id_fmt;; + char buf[40], *station_id_fmt, *session_id; uint32_t value; chunk_t chunk; @@ -202,6 +202,14 @@ void eap_radius_build_attributes(radius_message_t *request) host = ike_sa->get_other_host(ike_sa); snprintf(buf, sizeof(buf), station_id_fmt, host); request->add(request, RAT_CALLING_STATION_ID, chunk_from_str(buf)); + + session_id = eap_radius_accounting_session_id(ike_sa); + if (session_id) + { + request->add(request, RAT_ACCT_SESSION_ID, + chunk_from_str(session_id)); + free(session_id); + } } } diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c index 92611492b..ecb2083c9 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017 Tobias Brunner + * Copyright (C) 2015-2018 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2012 Martin Willi @@ -17,6 +17,7 @@ */ #include "eap_radius_accounting.h" +#include "eap_radius_provider.h" #include "eap_radius_plugin.h" #include <time.h> @@ -461,6 +462,37 @@ static void add_ike_sa_parameters(private_eap_radius_accounting_t *this, } /** + * Add any unclaimed IP addresses to the message + */ +static void add_unclaimed_ips(radius_message_t *message, ike_sa_t *ike_sa) +{ + eap_radius_provider_t *provider; + enumerator_t *enumerator; + host_t *vip; + + provider = eap_radius_provider_get(); + enumerator = provider->clear_unclaimed(provider, + ike_sa->get_unique_id(ike_sa)); + while (enumerator->enumerate(enumerator, &vip)) + { + switch (vip->get_family(vip)) + { + case AF_INET: + message->add(message, RAT_FRAMED_IP_ADDRESS, + vip->get_address(vip)); + break; + case AF_INET6: + message->add(message, RAT_FRAMED_IPV6_ADDRESS, + vip->get_address(vip)); + break; + default: + break; + } + } + enumerator->destroy(enumerator); +} + +/** * Add the Class attributes received in the Access-Accept message to the * RADIUS accounting message */ @@ -790,6 +822,7 @@ static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) chunk_create(entry->sid, strlen(entry->sid))); add_class_attributes(message, entry); add_ike_sa_parameters(this, message, ike_sa); + add_unclaimed_ips(message, ike_sa); value = htonl(entry->usage.bytes.sent); message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value)); @@ -816,7 +849,6 @@ static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa) value = htonl(time_monotonic(NULL) - entry->created); message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value)); - value = htonl(entry->cause); message->add(message, RAT_ACCT_TERMINATE_CAUSE, chunk_from_thing(value)); @@ -1070,8 +1102,27 @@ eap_radius_accounting_t *eap_radius_accounting_create() return &this->public; } -/** - * See header +/* + * Described in header + */ +char *eap_radius_accounting_session_id(ike_sa_t *ike_sa) +{ + entry_t *entry; + char *sid = NULL; + + if (singleton) + { + singleton->mutex->lock(singleton->mutex); + entry = get_or_create_entry(singleton, ike_sa->get_id(ike_sa), + ike_sa->get_unique_id(ike_sa)); + sid = strdup(entry->sid); + singleton->mutex->unlock(singleton->mutex); + } + return sid; +} + +/* + * Described in header */ void eap_radius_accounting_start_interim(ike_sa_t *ike_sa, uint32_t interval) { diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h index dc1edcf54..1fe1107ea 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.h +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Tobias Brunner + * Copyright (C) 2017-2018 Tobias Brunner * HSR Hochschule fuer Technik Rapperswil * * Copyright (C) 2012 Martin Willi @@ -50,6 +50,14 @@ struct eap_radius_accounting_t { eap_radius_accounting_t *eap_radius_accounting_create(); /** + * Get the Accounting session ID for the given IKE_SA. + * + * @param ike_sa IKE_SA for which to determine the session ID + * @return allocated session ID + */ +char *eap_radius_accounting_session_id(ike_sa_t *ike_sa); + +/** * Schedule Accounting interim updates for the given IKE_SA. * * @param ike_sa IKE_SA to send updates for diff --git a/src/libcharon/plugins/eap_radius/eap_radius_provider.c b/src/libcharon/plugins/eap_radius/eap_radius_provider.c index 8188bb764..defabb782 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_provider.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_provider.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2013 Martin Willi * Copyright (C) 2013 revosec AG * @@ -131,7 +134,7 @@ static entry_t* get_or_create_entry(hashtable_t *hashtable, uintptr_t id) } /** - * Put an entry to hashtable, or destroy it ife empty + * Put an entry to hashtable, or destroy it if empty */ static void put_or_destroy_entry(hashtable_t *hashtable, entry_t *entry) { @@ -494,6 +497,24 @@ METHOD(eap_radius_provider_t, add_attribute, void, this->listener.mutex->unlock(this->listener.mutex); } +METHOD(eap_radius_provider_t, clear_unclaimed, enumerator_t*, + private_eap_radius_provider_t *this, uint32_t id) +{ + entry_t *entry; + + this->listener.mutex->lock(this->listener.mutex); + entry = this->listener.unclaimed->remove(this->listener.unclaimed, + (void*)(uintptr_t)id); + this->listener.mutex->unlock(this->listener.mutex); + if (!entry) + { + return enumerator_create_empty(); + } + return enumerator_create_cleaner( + entry->addrs->create_enumerator(entry->addrs), + (void*)destroy_entry, entry); +} + METHOD(eap_radius_provider_t, destroy, void, private_eap_radius_provider_t *this) { @@ -523,6 +544,7 @@ eap_radius_provider_t *eap_radius_provider_create() }, .add_framed_ip = _add_framed_ip, .add_attribute = _add_attribute, + .clear_unclaimed = _clear_unclaimed, .destroy = _destroy, }, .listener = { @@ -539,6 +561,14 @@ eap_radius_provider_t *eap_radius_provider_create() }, ); + if (lib->settings->get_bool(lib->settings, + "%s.plugins.eap-radius.accounting", FALSE, lib->ns)) + { + /* if RADIUS accounting is enabled, keep unclaimed IPs around until + * the Accounting-Stop message is sent */ + this->listener.public.message = NULL; + } + charon->bus->add_listener(charon->bus, &this->listener.public); singleton = &this->public; diff --git a/src/libcharon/plugins/eap_radius/eap_radius_provider.h b/src/libcharon/plugins/eap_radius/eap_radius_provider.h index 80971bddb..9f1121ca3 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_provider.h +++ b/src/libcharon/plugins/eap_radius/eap_radius_provider.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2018 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2013 Martin Willi * Copyright (C) 2013 revosec AG * @@ -56,6 +59,14 @@ struct eap_radius_provider_t { configuration_attribute_type_t type, chunk_t data); /** + * Clears any unclaimed IP addresses and attributes for the given IKE_SA. + * + * @param id IKE_SA unique identifier + * @return enumerator over unclaimed IP addresses, if any + */ + enumerator_t *(*clear_unclaimed)(eap_radius_provider_t *this, uint32_t id); + + /** * Destroy a eap_radius_provider_t. */ void (*destroy)(eap_radius_provider_t *this); diff --git a/src/libcharon/plugins/ha/ha_attribute.c b/src/libcharon/plugins/ha/ha_attribute.c index 34d6efc48..2553fd014 100644 --- a/src/libcharon/plugins/ha/ha_attribute.c +++ b/src/libcharon/plugins/ha/ha_attribute.c @@ -159,13 +159,13 @@ static pool_t* get_pool(private_ha_attribute_t *this, char *name) } /** - * Check if we are responsible for a bit in our bitmask + * Check if we are responsible for an offset */ -static bool responsible_for(private_ha_attribute_t *this, int bit) +static bool responsible_for(private_ha_attribute_t *this, int offset) { u_int segment; - segment = this->kernel->get_segment_int(this->kernel, bit); + segment = offset % this->segments->count(this->segments) + 1; return this->segments->is_active(this->segments, segment); } @@ -175,7 +175,7 @@ METHOD(attribute_provider_t, acquire_address, host_t*, { enumerator_t *enumerator; pool_t *pool = NULL; - int offset = -1, byte, bit; + int offset = -1, tmp_offset, byte, bit; host_t *address; char *name; @@ -199,10 +199,11 @@ METHOD(attribute_provider_t, acquire_address, host_t*, { for (bit = 0; bit < 8; bit++) { + tmp_offset = byte * 8 + bit; if (!(pool->mask[byte] & 1 << bit) && - responsible_for(this, bit)) + responsible_for(this, tmp_offset)) { - offset = byte * 8 + bit; + offset = tmp_offset; pool->mask[byte] |= 1 << bit; break; } diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 4e3803892..ab845317f 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -138,6 +138,7 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message chunk_t dh_local = chunk_empty, dh_remote = chunk_empty, psk = chunk_empty; host_t *other = NULL; bool ok = FALSE; + auth_method_t method = AUTH_RSA; enumerator = message->create_attribute_enumerator(message); while (enumerator->enumerate(enumerator, &attribute, &value)) @@ -197,6 +198,8 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message case HA_ALG_DH: dh_grp = value.u16; break; + case HA_AUTH_METHOD: + method = value.u16; default: break; } @@ -238,7 +241,6 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message { keymat_v1_t *keymat_v1 = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); shared_key_t *shared = NULL; - auth_method_t method = AUTH_RSA; if (psk.len) { diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index 2854ab76d..aae402d50 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -73,7 +73,7 @@ static ike_extension_t copy_extension(ike_sa_t *ike_sa, ike_extension_t ext) METHOD(listener_t, ike_keys, bool, private_ha_ike_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh, chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey, - shared_key_t *shared) + shared_key_t *shared, auth_method_t method) { ha_message_t *m; chunk_t secret; @@ -141,6 +141,10 @@ METHOD(listener_t, ike_keys, bool, { m->add_attribute(m, HA_PSK, shared->get_key(shared)); } + else + { + m->add_attribute(m, HA_AUTH_METHOD, method); + } } m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa)); diff --git a/src/libcharon/plugins/ha/ha_message.c b/src/libcharon/plugins/ha/ha_message.c index 7891b1654..28b7b0d5b 100644 --- a/src/libcharon/plugins/ha/ha_message.c +++ b/src/libcharon/plugins/ha/ha_message.c @@ -240,6 +240,7 @@ METHOD(ha_message_t, add_attribute, void, case HA_OUTBOUND_CPI: case HA_SEGMENT: case HA_ESN: + case HA_AUTH_METHOD: { uint16_t val; @@ -463,6 +464,7 @@ METHOD(enumerator_t, attribute_enumerate, bool, case HA_OUTBOUND_CPI: case HA_SEGMENT: case HA_ESN: + case HA_AUTH_METHOD: { if (this->buf.len < sizeof(uint16_t)) { diff --git a/src/libcharon/plugins/ha/ha_message.h b/src/libcharon/plugins/ha/ha_message.h index 3e43dc8dc..3c0058d99 100644 --- a/src/libcharon/plugins/ha/ha_message.h +++ b/src/libcharon/plugins/ha/ha_message.h @@ -156,6 +156,8 @@ enum ha_message_attribute_t { HA_PSK, /** chunk_t, IV for next IKEv1 message */ HA_IV, + /** uint16_t, auth_method_t for IKEv1 key derivation */ + HA_AUTH_METHOD, }; /** diff --git a/src/libcharon/plugins/ha/ha_segments.c b/src/libcharon/plugins/ha/ha_segments.c index 0a407f9ef..153534915 100644 --- a/src/libcharon/plugins/ha/ha_segments.c +++ b/src/libcharon/plugins/ha/ha_segments.c @@ -433,6 +433,12 @@ METHOD(ha_segments_t, is_active, bool, return (this->active & SEGMENTS_BIT(segment)) != 0; } +METHOD(ha_segments_t, count, u_int, + private_ha_segments_t *this) +{ + return this->count; +} + METHOD(ha_segments_t, destroy, void, private_ha_segments_t *this) { @@ -459,6 +465,7 @@ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel, .deactivate = _deactivate, .handle_status = _handle_status, .is_active = _is_active, + .count = _count, .destroy = _destroy, }, .socket = socket, diff --git a/src/libcharon/plugins/ha/ha_segments.h b/src/libcharon/plugins/ha/ha_segments.h index 10d5812c6..bc96a8d3e 100644 --- a/src/libcharon/plugins/ha/ha_segments.h +++ b/src/libcharon/plugins/ha/ha_segments.h @@ -83,6 +83,13 @@ struct ha_segments_t { bool (*is_active)(ha_segments_t *this, u_int segment); /** + * Return the number of segments + * + * @return number of segments + */ + u_int (*count)(ha_segments_t *this); + + /** * Destroy a ha_segments_t. */ void (*destroy)(ha_segments_t *this); diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index 1292e0895..40fff7e05 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -2257,6 +2257,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, uint32_t replay_esn_len = 0; kernel_ipsec_del_sa_t del = { 0 }; status_t status = FAILED; + traffic_selector_t *ts; char markstr[32] = ""; /* if IPComp is used, we first update the IPComp SA */ @@ -2360,10 +2361,26 @@ METHOD(kernel_ipsec_t, update_sa, status_t, if (!id->src->ip_equals(id->src, data->new_src)) { host2xfrm(data->new_src, &sa->saddr); + + ts = selector2ts(&sa->sel, TRUE); + if (ts && ts->is_host(ts, id->src)) + { + ts->set_address(ts, data->new_src); + ts2subnet(ts, &sa->sel.saddr, &sa->sel.prefixlen_s); + } + DESTROY_IF(ts); } if (!id->dst->ip_equals(id->dst, data->new_dst)) { host2xfrm(data->new_dst, &sa->id.daddr); + + ts = selector2ts(&sa->sel, FALSE); + if (ts && ts->is_host(ts, id->dst)) + { + ts->set_address(ts, data->new_dst); + ts2subnet(ts, &sa->sel.daddr, &sa->sel.prefixlen_d); + } + DESTROY_IF(ts); } rta = XFRM_RTA(out_hdr, struct xfrm_usersa_info); diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index dbe409a62..37170a310 100644 --- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Tobias Brunner + * Copyright (C) 2008-2018 Tobias Brunner * Copyright (C) 2008 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -1287,20 +1287,27 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this, return; } - index = response.x_policy->sadb_x_policy_id; - this->mutex->lock(this->mutex); - if (this->policies->find_first(this->policies, policy_entry_match_byindex, - (void**)&policy, index) && - policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS) + if (response.x_sa2) { - reqid = sa->sa->cfg.reqid; + reqid = response.x_sa2->sadb_x_sa2_reqid; } else { - DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no " - "matching policy found", index); + index = response.x_policy->sadb_x_policy_id; + this->mutex->lock(this->mutex); + if (this->policies->find_first(this->policies, policy_entry_match_byindex, + (void**)&policy, index) && + policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS) + { + reqid = sa->sa->cfg.reqid; + } + else + { + DBG1(DBG_KNL, "received an SADB_ACQUIRE with policy id %d but no " + "matching policy found", index); + } + this->mutex->unlock(this->mutex); } - this->mutex->unlock(this->mutex); src_ts = sadb_address2ts(response.src); dst_ts = sadb_address2ts(response.dst); diff --git a/src/libcharon/plugins/vici/libvici.h b/src/libcharon/plugins/vici/libvici.h index d69597881..964752f53 100644 --- a/src/libcharon/plugins/vici/libvici.h +++ b/src/libcharon/plugins/vici/libvici.h @@ -86,6 +86,10 @@ #include <stdio.h> +#ifdef __cplusplus +extern "C" { +#endif + /** * Opaque vici connection contex. */ @@ -465,4 +469,8 @@ void vici_init(); */ void vici_deinit(); +#ifdef __cplusplus +} +#endif + #endif /** LIBVICI_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c index 10c62dc89..ace7a4528 100644 --- a/src/libcharon/plugins/vici/vici_config.c +++ b/src/libcharon/plugins/vici/vici_config.c @@ -733,7 +733,7 @@ CALLBACK(parse_ts, bool, if (host_create_from_range(buf, &lower, &upper)) { type = (lower->get_family(lower) == AF_INET) ? - TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; ts = traffic_selector_create_from_bytes(proto, type, lower->get_address(lower), from, upper->get_address(upper), to); @@ -2494,7 +2494,10 @@ CALLBACK(config_sn, bool, if (peer.mediated_by) { cfg.mediated_by = peer.mediated_by; - cfg.peer_id = peer.peer_id->clone(peer.peer_id); + if (peer.peer_id) + { + cfg.peer_id = peer.peer_id->clone(peer.peer_id); + } } #endif /* ME */ peer_cfg = peer_cfg_create(name, ike_cfg, &cfg); diff --git a/src/libcharon/processing/jobs/adopt_children_job.c b/src/libcharon/processing/jobs/adopt_children_job.c index 998af0d3f..e2a7f6b20 100644 --- a/src/libcharon/processing/jobs/adopt_children_job.c +++ b/src/libcharon/processing/jobs/adopt_children_job.c @@ -53,6 +53,36 @@ METHOD(job_t, destroy, void, free(this); } +METHOD(adopt_children_job_t, queue_task, void, + private_adopt_children_job_t *this, task_t *task) +{ + array_insert_create(&this->tasks, ARRAY_TAIL, task); +} + +/** + * Adopt child-creating tasks from the given IKE_SA + */ +static u_int adopt_child_tasks(private_adopt_children_job_t *this, + ike_sa_t *ike_sa, task_queue_t queue) +{ + enumerator_t *tasks; + task_t *task; + u_int count = 0; + + tasks = ike_sa->create_task_enumerator(ike_sa, queue); + while (tasks->enumerate(tasks, &task)) + { + if (task->get_type(task) == TASK_QUICK_MODE) + { + ike_sa->remove_task(ike_sa, tasks); + queue_task(this, task); + count++; + } + } + tasks->destroy(tasks); + return count; +} + METHOD(job_t, execute, job_requeue_t, private_adopt_children_job_t *this) { @@ -65,6 +95,7 @@ METHOD(job_t, execute, job_requeue_t, ike_sa_t *ike_sa; child_sa_t *child_sa; uint32_t unique; + u_int tasks = 0; ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->id); if (ike_sa) @@ -127,11 +158,17 @@ METHOD(job_t, execute, job_requeue_t, * it does trigger an assign_vips(FALSE) event, so we also * trigger one below */ ike_sa->clear_virtual_ips(ike_sa, FALSE); - if (children->get_count(children) || vips->get_count(vips)) + + tasks += adopt_child_tasks(this, ike_sa, TASK_QUEUE_ACTIVE); + tasks += adopt_child_tasks(this, ike_sa, TASK_QUEUE_QUEUED); + + if (children->get_count(children) || tasks || + vips->get_count(vips)) { DBG1(DBG_IKE, "detected reauth of existing IKE_SA, " - "adopting %d children and %d virtual IPs", - children->get_count(children), vips->get_count(vips)); + "adopting %d children, %d child tasks, and %d " + "virtual IPs", children->get_count(children), + tasks, vips->get_count(vips)); } if (ike_sa->get_state(ike_sa) == IKE_PASSIVE) { @@ -152,7 +189,8 @@ METHOD(job_t, execute, job_requeue_t, charon->ike_sa_manager->checkin( charon->ike_sa_manager, ike_sa); } - if (children->get_count(children) || vips->get_count(vips)) + if (children->get_count(children) || tasks || + vips->get_count(vips)) { break; } @@ -237,12 +275,6 @@ METHOD(job_t, get_priority, job_priority_t, return JOB_PRIO_HIGH; } -METHOD(adopt_children_job_t, queue_task, void, - private_adopt_children_job_t *this, task_t *task) -{ - array_insert_create(&this->tasks, ARRAY_TAIL, task); -} - /** * See header */ diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index c33398bee..bdc96a4bc 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -978,7 +978,7 @@ static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, } /** - * Install inbound policie(s): in, fwd + * Install inbound policies: in, fwd */ static status_t install_policies_inbound(private_child_sa_t *this, host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, @@ -1012,7 +1012,7 @@ static status_t install_policies_inbound(private_child_sa_t *this, } /** - * Install outbound policie(s): out, [fwd] + * Install outbound policies: out, [fwd] */ static status_t install_policies_outbound(private_child_sa_t *this, host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index a4ad866d3..3d576a0e8 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1996,8 +1996,7 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new, /* adopt any active or queued CHILD-creating tasks */ if (status != DESTROY_ME) { - task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; - other_tasks->adopt_child_tasks(other_tasks, this->task_manager); + new->adopt_child_tasks(new, &this->public); if (new->get_state(new) == IKE_CREATED) { status = new->initiate(new, NULL, 0, NULL, NULL); @@ -2404,7 +2403,9 @@ METHOD(ike_sa_t, retransmit, status_t, } case IKE_DELETING: DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding"); - if (has_condition(this, COND_REAUTHENTICATING)) + if (has_condition(this, COND_REAUTHENTICATING) && + !lib->settings->get_bool(lib->settings, + "%s.make_before_break", FALSE, lib->ns)) { DBG1(DBG_IKE, "delete during reauthentication failed, " "trying to reestablish IKE_SA anyway"); @@ -2719,6 +2720,12 @@ METHOD(ike_sa_t, create_task_enumerator, enumerator_t*, return this->task_manager->create_task_enumerator(this->task_manager, queue); } +METHOD(ike_sa_t, remove_task, void, + private_ike_sa_t *this, enumerator_t *enumerator) +{ + return this->task_manager->remove_task(this->task_manager, enumerator); +} + METHOD(ike_sa_t, flush_queue, void, private_ike_sa_t *this, task_queue_t queue) { @@ -2737,6 +2744,36 @@ METHOD(ike_sa_t, queue_task_delayed, void, this->task_manager->queue_task_delayed(this->task_manager, task, delay); } +/** + * Migrate and queue child-creating tasks from another IKE_SA + */ +static void migrate_child_tasks(private_ike_sa_t *this, ike_sa_t *other, + task_queue_t queue) +{ + enumerator_t *enumerator; + task_t *task; + + enumerator = other->create_task_enumerator(other, queue); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_CHILD_CREATE || + task->get_type(task) == TASK_QUICK_MODE) + { + other->remove_task(other, enumerator); + task->migrate(task, &this->public); + queue_task(this, task); + } + } + enumerator->destroy(enumerator); +} + +METHOD(ike_sa_t, adopt_child_tasks, void, + private_ike_sa_t *this, ike_sa_t *other) +{ + migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE); + migrate_child_tasks(this, other, TASK_QUEUE_QUEUED); +} + METHOD(ike_sa_t, inherit_pre, void, private_ike_sa_t *this, ike_sa_t *other_public) { @@ -3052,9 +3089,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .create_attribute_enumerator = _create_attribute_enumerator, .set_kmaddress = _set_kmaddress, .create_task_enumerator = _create_task_enumerator, + .remove_task = _remove_task, .flush_queue = _flush_queue, .queue_task = _queue_task, .queue_task_delayed = _queue_task_delayed, + .adopt_child_tasks = _adopt_child_tasks, #ifdef ME .act_as_mediation_server = _act_as_mediation_server, .get_server_reflexive_host = _get_server_reflexive_host, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index c1d3e1d7a..be480eac8 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1125,6 +1125,16 @@ struct ike_sa_t { enumerator_t* (*create_task_enumerator)(ike_sa_t *this, task_queue_t queue); /** + * Remove the task the given enumerator points to. + * + * @note This should be used with caution, in partciular, for tasks in the + * active and passive queues. + * + * @param enumerator enumerator created with the method above + */ + void (*remove_task)(ike_sa_t *this, enumerator_t *enumerator); + + /** * Flush a task queue, cancelling all tasks in it. * * @param queue queue type to flush @@ -1148,6 +1158,13 @@ struct ike_sa_t { void (*queue_task_delayed)(ike_sa_t *this, task_t *task, uint32_t delay); /** + * Adopt child creating tasks from the given IKE_SA. + * + * @param other other IKE_SA to adopt tasks from + */ + void (*adopt_child_tasks)(ike_sa_t *this, ike_sa_t *other); + + /** * Inherit required attributes to new SA before rekeying. * * Some properties of the SA must be applied before starting IKE_SA diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index c50c70860..3bac4b109 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -1967,6 +1967,8 @@ static void adopt_children_and_vips(ike_sa_t *old, ike_sa_t *new) } enumerator->destroy(enumerator); + new->adopt_child_tasks(new, old); + enumerator = old->create_virtual_ip_enumerator(old, FALSE); while (enumerator->enumerate(enumerator, &vip)) { diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index b99d75142..ac2899f11 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -251,7 +251,8 @@ METHOD(phase1_t, derive_keys, bool, return FALSE; } charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, this->dh_value, - this->nonce_i, this->nonce_r, NULL, shared_key); + this->nonce_i, this->nonce_r, NULL, shared_key, + method); DESTROY_IF(shared_key); return TRUE; } diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 5f6c3bbe8..f76471e78 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2016 Tobias Brunner + * Copyright (C) 2007-2018 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -544,20 +544,20 @@ METHOD(task_manager_t, initiate, status_t, new_mid = TRUE; break; } - if (!mode_config_expected(this) && - activate_task(this, TASK_QUICK_MODE)) + if (activate_task(this, TASK_ISAKMP_DPD)) { - exchange = QUICK_MODE; + exchange = INFORMATIONAL_V1; new_mid = TRUE; break; } - if (activate_task(this, TASK_INFORMATIONAL)) + if (!mode_config_expected(this) && + activate_task(this, TASK_QUICK_MODE)) { - exchange = INFORMATIONAL_V1; + exchange = QUICK_MODE; new_mid = TRUE; break; } - if (activate_task(this, TASK_ISAKMP_DPD)) + if (activate_task(this, TASK_INFORMATIONAL)) { exchange = INFORMATIONAL_V1; new_mid = TRUE; @@ -1121,7 +1121,15 @@ static status_t process_request(private_task_manager_t *this, } } else - { /* We don't send a response, so don't retransmit one if we get + { + if (this->responding.retransmitted > 1) + { + packet_t *packet = NULL; + array_get(this->responding.packets, 0, &packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_CLEARED, + packet); + } + /* We don't send a response, so don't retransmit one if we get * the same message again. */ clear_packets(this->responding.packets); } @@ -1883,39 +1891,6 @@ METHOD(task_manager_t, adopt_tasks, void, } } -/** - * Migrates child-creating tasks from src to dst - */ -static void migrate_child_tasks(private_task_manager_t *this, - linked_list_t *src, linked_list_t *dst) -{ - enumerator_t *enumerator; - task_t *task; - - enumerator = src->create_enumerator(src); - while (enumerator->enumerate(enumerator, &task)) - { - if (task->get_type(task) == TASK_QUICK_MODE) - { - src->remove_at(src, enumerator); - task->migrate(task, this->ike_sa); - dst->insert_last(dst, task); - } - } - enumerator->destroy(enumerator); -} - -METHOD(task_manager_t, adopt_child_tasks, void, - private_task_manager_t *this, task_manager_t *other_public) -{ - private_task_manager_t *other = (private_task_manager_t*)other_public; - - /* move active child tasks from other to this */ - migrate_child_tasks(this, other->active_tasks, this->queued_tasks); - /* do the same for queued tasks */ - migrate_child_tasks(this, other->queued_tasks, this->queued_tasks); -} - METHOD(task_manager_t, busy, bool, private_task_manager_t *this) { @@ -1976,19 +1951,86 @@ METHOD(task_manager_t, reset, void, } } +/** + * Data for a task queue enumerator + */ +typedef struct { + enumerator_t public; + task_queue_t queue; + enumerator_t *inner; +} task_enumerator_t; + +METHOD(enumerator_t, task_enumerator_destroy, void, + task_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +METHOD(enumerator_t, task_enumerator_enumerate, bool, + task_enumerator_t *this, va_list args) +{ + task_t **task; + + VA_ARGS_VGET(args, task); + return this->inner->enumerate(this->inner, task); +} + METHOD(task_manager_t, create_task_enumerator, enumerator_t*, private_task_manager_t *this, task_queue_t queue) { + task_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = enumerator_enumerate_default, + .venumerate = _task_enumerator_enumerate, + .destroy = _task_enumerator_destroy, + }, + .queue = queue, + ); switch (queue) { case TASK_QUEUE_ACTIVE: - return this->active_tasks->create_enumerator(this->active_tasks); + enumerator->inner = this->active_tasks->create_enumerator( + this->active_tasks); + break; + case TASK_QUEUE_PASSIVE: + enumerator->inner = this->passive_tasks->create_enumerator( + this->passive_tasks); + break; + case TASK_QUEUE_QUEUED: + enumerator->inner = this->queued_tasks->create_enumerator( + this->queued_tasks); + break; + default: + enumerator->inner = enumerator_create_empty(); + break; + } + return &enumerator->public; +} + +METHOD(task_manager_t, remove_task, void, + private_task_manager_t *this, enumerator_t *enumerator_public) +{ + task_enumerator_t *enumerator = (task_enumerator_t*)enumerator_public; + + switch (enumerator->queue) + { + case TASK_QUEUE_ACTIVE: + this->active_tasks->remove_at(this->active_tasks, + enumerator->inner); + break; case TASK_QUEUE_PASSIVE: - return this->passive_tasks->create_enumerator(this->passive_tasks); + this->passive_tasks->remove_at(this->passive_tasks, + enumerator->inner); + break; case TASK_QUEUE_QUEUED: - return this->queued_tasks->create_enumerator(this->queued_tasks); + this->queued_tasks->remove_at(this->queued_tasks, + enumerator->inner); + break; default: - return enumerator_create_empty(); + break; } } @@ -2039,9 +2081,9 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, - .adopt_child_tasks = _adopt_child_tasks, .busy = _busy, .create_task_enumerator = _create_task_enumerator, + .remove_task = _remove_task, .flush = _flush, .flush_queue = _flush_queue, .destroy = _destroy, diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c index 7dbbdc92f..b652d926f 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c @@ -287,7 +287,6 @@ METHOD(task_t, process_i, status_t, default: return FAILED; } - break; } case AGGRESSIVE: { diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c index 58f856e3f..566bfe83a 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c @@ -605,7 +605,6 @@ METHOD(task_t, process_i, status_t, default: return FAILED; } - break; } case AGGRESSIVE: { diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 007e94d96..b0a42b8bd 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -1110,14 +1110,17 @@ METHOD(task_t, process_r, status_t, this->tsi = select_ts(this, FALSE, tsi); this->tsr = select_ts(this, TRUE, tsr); } - tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); - tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); if (!this->config || !this->tsi || !this->tsr || this->mode != this->config->get_mode(this->config)) { - DBG1(DBG_IKE, "no matching CHILD_SA config found"); + DBG1(DBG_IKE, "no matching CHILD_SA config found for " + "%#R === %#R", tsi, tsr); + tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); + tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); return send_notify(this, INVALID_ID_INFORMATION); } + tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); + tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); if (this->config->has_option(this->config, OPT_IPCOMP)) { diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 1fcef03cc..97d33a89e 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -111,6 +111,40 @@ static bool build_signature_auth_data(chunk_t *auth_data, } /** + * Check if the given scheme is supported by the key and, if so, add it to the + * first array (we add the scheme supported by the key in case the parameters + * are different) + */ +static void add_scheme_if_supported(array_t *selected, array_t *supported, + signature_params_t *config) +{ + signature_params_t *sup; + int i; + + if (!supported) + { + array_insert(selected, ARRAY_TAIL, signature_params_clone(config)); + return; + } + + for (i = 0; i < array_count(supported); i++) + { + array_get(supported, i, &sup); + if (signature_params_comply(sup, config)) + { + array_insert(selected, ARRAY_TAIL, signature_params_clone(sup)); + return; + } + } +} + +CALLBACK(destroy_scheme, void, + signature_params_t *params, int idx, void *user) +{ + signature_params_destroy(params); +} + +/** * Selects possible signature schemes based on our configuration, the other * peer's capabilities and the private key */ @@ -123,10 +157,32 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, auth_rule_t rule; key_type_t key_type; bool have_config = FALSE; - array_t *selected; + array_t *supported = NULL, *selected; selected = array_create(0, 0); key_type = private->get_type(private); + + if (private->supported_signature_schemes) + { + enumerator = private->supported_signature_schemes(private); + while (enumerator->enumerate(enumerator, &config)) + { + if (keymat->hash_algorithm_supported(keymat, + hasher_from_signature_scheme(config->scheme, + config->params))) + { + array_insert_create(&supported, ARRAY_TAIL, + signature_params_clone(config)); + } + } + enumerator->destroy(enumerator); + + if (!supported) + { + return selected; + } + } + enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &rule, &config)) { @@ -134,21 +190,32 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, { continue; } - have_config = TRUE; if (key_type == key_type_from_signature_scheme(config->scheme) && keymat->hash_algorithm_supported(keymat, hasher_from_signature_scheme(config->scheme, config->params))) { - array_insert(selected, ARRAY_TAIL, signature_params_clone(config)); + add_scheme_if_supported(selected, supported, config); } + have_config = TRUE; } enumerator->destroy(enumerator); - if (!have_config) + if (have_config) { - /* if no specific configuration, find schemes appropriate for the key - * and supported by the other peer */ + array_destroy_function(supported, destroy_scheme, NULL); + } + else + { + /* if we have no config, return either whatever schemes the key (and + * peer) support or.. */ + if (supported) + { + array_destroy(selected); + return supported; + } + + /* ...find schemes appropriate for the key and supported by the peer */ enumerator = signature_schemes_for_key(key_type, private->get_keysize(private)); while (enumerator->enumerate(enumerator, &config)) @@ -207,12 +274,6 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, return selected; } -CALLBACK(destroy_scheme, void, - signature_params_t *params, int idx, void *user) -{ - signature_params_destroy(params); -} - /** * Adds the given auth data to the message, either in an AUTH payload or * a NO_PPK_AUTH notify. @@ -310,9 +371,9 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, if (params->scheme == SIGN_RSA_EMSA_PSS) { rsa_pss_params_t *pss = params->params; - DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N_%N %s", id, - signature_scheme_names, params->scheme, - hash_algorithm_short_names_upper, pss->hash, + DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N_%N_SALT_%zd " + "%s", id, signature_scheme_names, params->scheme, + hash_algorithm_short_names_upper, pss->hash, pss->salt_len, status == SUCCESS ? "successful" : "failed"); } else @@ -586,9 +647,9 @@ METHOD(authenticator_t, process, status_t, else if (params->scheme == SIGN_RSA_EMSA_PSS) { rsa_pss_params_t *pss = params->params; - DBG1(DBG_IKE, "authentication of '%Y' with %N_%N successful", - id, signature_scheme_names, params->scheme, - hash_algorithm_short_names_upper, pss->hash); + DBG1(DBG_IKE, "authentication of '%Y' with %N_%N_SALT_%zd " + "successful", id, signature_scheme_names, params->scheme, + hash_algorithm_short_names_upper, pss->hash, pss->salt_len); } else { diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 910c77a2d..e9142d79b 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1459,6 +1459,59 @@ static bool looks_like_mid_sync(private_task_manager_t *this, message_t *msg, } /** + * Check whether we should reject the given request message + */ +static inline bool reject_request(private_task_manager_t *this, + message_t *msg) +{ + ike_sa_state_t state; + exchange_type_t type; + ike_sa_id_t *ike_sa_id; + bool reject = FALSE; + + state = this->ike_sa->get_state(this->ike_sa); + type = msg->get_exchange_type(msg); + + /* reject initial messages if not received in specific states */ + switch (type) + { + case IKE_SA_INIT: + reject = state != IKE_CREATED; + break; + case IKE_AUTH: + reject = state != IKE_CONNECTING; + break; + default: + break; + } + + if (!reject) + { + switch (state) + { + /* after rekeying we only expect a DELETE in an INFORMATIONAL */ + case IKE_REKEYED: + reject = type != INFORMATIONAL; + break; + /* also reject requests for half-open IKE_SAs as initiator */ + case IKE_CREATED: + case IKE_CONNECTING: + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + reject = ike_sa_id->is_initiator(ike_sa_id); + break; + default: + break; + } + } + + if (reject) + { + DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N", exchange_type_names, + type, ike_sa_state_names, state); + } + return reject; +} +/** * Check if a message with message ID 0 looks like it is used to synchronize * the message IDs and we are prepared to process it. * @@ -1483,8 +1536,6 @@ METHOD(task_manager_t, process_message, status_t, status_t status; uint32_t mid; bool schedule_delete_job = FALSE; - ike_sa_state_t state; - exchange_type_t type; charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); @@ -1517,24 +1568,14 @@ METHOD(task_manager_t, process_message, status_t, /* add a timeout if peer does not establish it completely */ schedule_delete_job = TRUE; } - this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, - time_monotonic(NULL)); mid = msg->get_message_id(msg); if (msg->get_request(msg)) { if (mid == this->responding.mid || (mid == 0 && is_mid_sync(this, msg))) { - /* reject initial messages if not received in specific states, - * after rekeying we only expect a DELETE in an INFORMATIONAL */ - type = msg->get_exchange_type(msg); - state = this->ike_sa->get_state(this->ike_sa); - if ((type == IKE_SA_INIT && state != IKE_CREATED) || - (type == IKE_AUTH && state != IKE_CONNECTING) || - (state == IKE_REKEYED && type != INFORMATIONAL)) + if (reject_request(this, msg)) { - DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N", - exchange_type_names, type, ike_sa_state_names, state); return FAILED; } if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) @@ -1544,6 +1585,11 @@ METHOD(task_manager_t, process_message, status_t, status = handle_fragment(this, &this->responding.defrag, msg); if (status != SUCCESS) { + if (status == NEED_MORE) + { + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + } return status; } charon->bus->message(charon->bus, msg, TRUE, TRUE); @@ -1554,6 +1600,8 @@ METHOD(task_manager_t, process_message, status_t, switch (process_request(this, msg)) { case SUCCESS: + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); this->responding.mid++; break; case NEED_MORE: @@ -1570,10 +1618,17 @@ METHOD(task_manager_t, process_message, status_t, status = handle_fragment(this, &this->responding.defrag, msg); if (status != SUCCESS) { + if (status == NEED_MORE) + { + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + } return status; } DBG1(DBG_IKE, "received retransmit of request with ID %d, " "retransmitting response", mid); + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg); send_packets(this, this->responding.packets, msg->get_destination(msg), msg->get_source(msg)); @@ -1603,6 +1658,11 @@ METHOD(task_manager_t, process_message, status_t, status = handle_fragment(this, &this->initiating.defrag, msg); if (status != SUCCESS) { + if (status == NEED_MORE) + { + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + } return status; } charon->bus->message(charon->bus, msg, TRUE, TRUE); @@ -1615,6 +1675,8 @@ METHOD(task_manager_t, process_message, status_t, flush(this); return DESTROY_ME; } + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); } else { @@ -2014,61 +2076,6 @@ METHOD(task_manager_t, adopt_tasks, void, } } -/** - * Migrates child-creating tasks from other to this - */ -static void migrate_child_tasks(private_task_manager_t *this, - private_task_manager_t *other, - task_queue_t queue) -{ - enumerator_t *enumerator; - array_t *array; - task_t *task; - - switch (queue) - { - case TASK_QUEUE_ACTIVE: - array = other->active_tasks; - break; - case TASK_QUEUE_QUEUED: - array = other->queued_tasks; - break; - default: - return; - } - - enumerator = array_create_enumerator(array); - while (enumerator->enumerate(enumerator, &task)) - { - queued_task_t *queued = NULL; - - if (queue == TASK_QUEUE_QUEUED) - { - queued = (queued_task_t*)task; - task = queued->task; - } - if (task->get_type(task) == TASK_CHILD_CREATE) - { - array_remove_at(array, enumerator); - task->migrate(task, this->ike_sa); - queue_task(this, task); - free(queued); - } - } - enumerator->destroy(enumerator); -} - -METHOD(task_manager_t, adopt_child_tasks, void, - private_task_manager_t *this, task_manager_t *other_public) -{ - private_task_manager_t *other = (private_task_manager_t*)other_public; - - /* move active child tasks from other to this */ - migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE); - /* do the same for queued tasks */ - migrate_child_tasks(this, other, TASK_QUEUE_QUEUED); -} - METHOD(task_manager_t, busy, bool, private_task_manager_t *this) { @@ -2124,17 +2131,39 @@ METHOD(task_manager_t, reset, void, this->reset = TRUE; } -CALLBACK(filter_queued, bool, - void *unused, enumerator_t *orig, va_list args) -{ +/** + * Data for a task queue enumerator + */ +typedef struct { + enumerator_t public; + task_queue_t queue; + enumerator_t *inner; queued_task_t *queued; +} task_enumerator_t; + +METHOD(enumerator_t, task_enumerator_destroy, void, + task_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +METHOD(enumerator_t, task_enumerator_enumerate, bool, + task_enumerator_t *this, va_list args) +{ task_t **task; VA_ARGS_VGET(args, task); - - if (orig->enumerate(orig, &queued)) + if (this->queue == TASK_QUEUE_QUEUED) + { + if (this->inner->enumerate(this->inner, &this->queued)) + { + *task = this->queued->task; + return TRUE; + } + } + else if (this->inner->enumerate(this->inner, task)) { - *task = queued->task; return TRUE; } return FALSE; @@ -2143,18 +2172,54 @@ CALLBACK(filter_queued, bool, METHOD(task_manager_t, create_task_enumerator, enumerator_t*, private_task_manager_t *this, task_queue_t queue) { + task_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = enumerator_enumerate_default, + .venumerate = _task_enumerator_enumerate, + .destroy = _task_enumerator_destroy, + }, + .queue = queue, + ); switch (queue) { case TASK_QUEUE_ACTIVE: - return array_create_enumerator(this->active_tasks); + enumerator->inner = array_create_enumerator(this->active_tasks); + break; case TASK_QUEUE_PASSIVE: - return array_create_enumerator(this->passive_tasks); + enumerator->inner = array_create_enumerator(this->passive_tasks); + break; case TASK_QUEUE_QUEUED: - return enumerator_create_filter( - array_create_enumerator(this->queued_tasks), - filter_queued, NULL, NULL); + enumerator->inner = array_create_enumerator(this->queued_tasks); + break; default: - return enumerator_create_empty(); + enumerator->inner = enumerator_create_empty(); + break; + } + return &enumerator->public; +} + +METHOD(task_manager_t, remove_task, void, + private_task_manager_t *this, enumerator_t *enumerator_public) +{ + task_enumerator_t *enumerator = (task_enumerator_t*)enumerator_public; + + switch (enumerator->queue) + { + case TASK_QUEUE_ACTIVE: + array_remove_at(this->active_tasks, enumerator->inner); + break; + case TASK_QUEUE_PASSIVE: + array_remove_at(this->passive_tasks, enumerator->inner); + break; + case TASK_QUEUE_QUEUED: + array_remove_at(this->queued_tasks, enumerator->inner); + free(enumerator->queued); + enumerator->queued = NULL; + break; + default: + break; } } @@ -2204,9 +2269,9 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, - .adopt_child_tasks = _adopt_child_tasks, .busy = _busy, .create_task_enumerator = _create_task_enumerator, + .remove_task = _remove_task, .flush = _flush, .flush_queue = _flush_queue, .destroy = _destroy, diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 6c8b29018..0e3711898 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -174,6 +174,11 @@ static void install_outbound(private_child_delete_t *this, linked_list_t *my_ts, *other_ts; status_t status; + if (!spi) + { + return; + } + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE); if (!child_sa) @@ -312,7 +317,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) child_sa_t *child_sa; child_cfg_t *child_cfg; protocol_id_t protocol; - uint32_t spi, reqid, rekey_spi; + uint32_t spi, reqid; action_t action; status_t status = SUCCESS; time_t now, expire; @@ -335,11 +340,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) } else { - rekey_spi = child_sa->get_rekey_spi(child_sa); - if (rekey_spi) - { - install_outbound(this, protocol, rekey_spi); - } + install_outbound(this, protocol, child_sa->get_rekey_spi(child_sa)); /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't * immediately destroy it, by default, so we can process delayed * packets */ @@ -459,6 +460,17 @@ METHOD(task_t, build_i, status_t, this->spi = child_sa->get_spi(child_sa, TRUE); } + if (this->expired && child_sa->get_state(child_sa) == CHILD_REKEYED) + { /* the peer was expected to delete this SA, but if we send a DELETE + * we might cause a collision there if the CREATE_CHILD_SA response + * is delayed (the peer wouldn't know if we deleted this SA due to an + * expire or because of a forced delete by the user and might then + * ignore the CREATE_CHILD_SA response once it arrives) */ + child_sa->set_state(child_sa, CHILD_DELETED); + install_outbound(this, this->protocol, + child_sa->get_rekey_spi(child_sa)); + } + if (child_sa->get_state(child_sa) == CHILD_DELETED) { /* DELETEs for this CHILD_SA were already exchanged, but it was not yet * destroyed to allow delayed packets to get processed */ diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 307d99264..b570904e2 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -773,7 +773,7 @@ static bool derive_keys(private_ike_init_t *this, return FALSE; } charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty, - nonce_i, nonce_r, this->old_sa, NULL); + nonce_i, nonce_r, this->old_sa, NULL, AUTH_NONE); return TRUE; } @@ -890,6 +890,20 @@ METHOD(task_t, pre_process_i, status_t, switch (type) { + case COOKIE: + { + chunk_t cookie; + + cookie = notify->get_notification_data(notify); + if (chunk_equals(cookie, this->cookie)) + { + DBG1(DBG_IKE, "ignore response with duplicate COOKIE " + "notify"); + enumerator->destroy(enumerator); + return FAILED; + } + break; + } case REDIRECT: { identification_t *gateway; diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 9545da4f3..c357d5035 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2016 Tobias Brunner + * Copyright (C) 2013-2018 Tobias Brunner * Copyright (C) 2006 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -228,13 +228,6 @@ struct task_manager_t { void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); /** - * Migrate all active or queued CHILD_SA-creating tasks from other to this. - * - * @param other manager which gives away its tasks - */ - void (*adopt_child_tasks) (task_manager_t *this, task_manager_t *other); - - /** * Increment a message ID counter, in- or outbound. * * If a message is processed outside of the manager, this call increments @@ -285,6 +278,16 @@ struct task_manager_t { task_queue_t queue); /** + * Remove the task the given enumerator points to. + * + * @note This should be used with caution, in partciular, for tasks in the + * active and passive queues. + * + * @param enumerator enumerator created with the method above + */ + void (*remove_task)(task_manager_t *this, enumerator_t *enumerator); + + /** * Flush all tasks, regardless of the queue. */ void (*flush)(task_manager_t *this); diff --git a/src/libcharon/tests/suites/test_child_rekey.c b/src/libcharon/tests/suites/test_child_rekey.c index 51d577cd8..b9f6ea0bc 100644 --- a/src/libcharon/tests/suites/test_child_rekey.c +++ b/src/libcharon/tests/suites/test_child_rekey.c @@ -370,8 +370,8 @@ END_TEST /** * Check that the responder handles hard expires properly while waiting for the - * delete after a rekeying (e.g. if the initiator of the rekeying fails to - * delete the CHILD_SA for some reason). + * delete after a rekeying (e.g. if the rekey settings are tight or the + * CREATE_CHILD_SA response is delayed). */ START_TEST(test_regular_responder_handle_hard_expire) { @@ -405,28 +405,22 @@ START_TEST(test_regular_responder_handle_hard_expire) /* we don't expect this to get called anymore */ assert_hook_not_called(child_rekey); - /* this is similar to a regular delete collision */ - assert_single_payload(OUT, PLV2_DELETE); + /* this is similar to a regular delete collision, but we don't actually + * want to send a delete back as that might conflict with a delayed + * CREATE_CHILD_SA response */ call_ikesa(b, delete_child_sa, PROTO_ESP, 2, TRUE); - assert_child_sa_state(b, 2, CHILD_DELETING, CHILD_OUTBOUND_INSTALLED); - assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED); - /* since the SAs expired they would not actually be installed in the kernel - * anymore and since we have not yet installed a new outbound SA this - * will result in dropped packets and possibly acquires */ - assert_ipsec_sas_installed(b, 1, 2, 4); + assert_child_sa_count(b, 1); + assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED); + /* the expire causes the outbound SA to get installed */ + assert_ipsec_sas_installed(b, 3, 4); /* INFORMATIONAL { D } --> */ + assert_no_jobs_scheduled(); assert_single_payload(IN, PLV2_DELETE); exchange_test_helper->process_message(exchange_test_helper, b, NULL); - assert_child_sa_state(b, 2, CHILD_DELETING, CHILD_OUTBOUND_INSTALLED); - assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED); - assert_ipsec_sas_installed(b, 1, 2, 4); - /* <-- INFORMATIONAL { D } */ - assert_single_payload(IN, PLV2_DELETE); - exchange_test_helper->process_message(exchange_test_helper, a, NULL); - assert_child_sa_state(a, 1, CHILD_DELETING, CHILD_OUTBOUND_INSTALLED); - assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED); - assert_ipsec_sas_installed(a, 1, 2, 3, 4); + assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED); + assert_ipsec_sas_installed(b, 3, 4); + assert_scheduler(); /* <-- INFORMATIONAL { } */ assert_jobs_scheduled(1); assert_message_empty(IN); @@ -436,23 +430,11 @@ START_TEST(test_regular_responder_handle_hard_expire) assert_child_sa_count(a, 2); assert_ipsec_sas_installed(a, 1, 3, 4); assert_scheduler(); - /* INFORMATIONAL { } --> */ - assert_jobs_scheduled(1); - assert_message_empty(IN); - exchange_test_helper->process_message(exchange_test_helper, b, NULL); - assert_child_sa_state(b, 2, CHILD_DELETED, CHILD_OUTBOUND_NONE); - assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED); - assert_child_sa_count(b, 2); - assert_ipsec_sas_installed(b, 2, 3, 4); - assert_scheduler(); - /* simulate the execution of the scheduled jobs */ + /* simulate the execution of the scheduled job */ destroy_rekeyed(a, 1); assert_child_sa_count(a, 1); assert_ipsec_sas_installed(a, 3, 4); - destroy_rekeyed(b, 2); - assert_child_sa_count(b, 1); - assert_ipsec_sas_installed(b, 3, 4); /* child_rekey/child_updown */ assert_hook(); |