ARG_ENABL_SET([eap-peap], [enable EAP PEAP authentication module.])
ARG_ENABL_SET([eap-tnc], [enable EAP TNC trusted network connect module.])
ARG_ENABL_SET([eap-radius], [enable RADIUS proxy authentication module.])
+ ARG_ENABL_SET([xauth-generic], [enable generic XAuth backend.])
+ ARG_ENABL_SET([xauth-eap], [enable XAuth backend using EAP methods to verify passwords.])
ARG_ENABL_SET([tnc-ifmap], [enable TNC IF-MAP module.])
+ARG_ENABL_SET([tnc-pdp], [enable TNC policy decision point module.])
ARG_ENABL_SET([tnc-imc], [enable TNC IMC module.])
ARG_ENABL_SET([tnc-imv], [enable TNC IMV module.])
ARG_ENABL_SET([tnccs-11], [enable TNCCS 1.1 protocol module.])
ADD_PLUGIN([eap-ttls], [c libcharon])
ADD_PLUGIN([eap-peap], [c libcharon])
ADD_PLUGIN([eap-tnc], [c libcharon])
+ ADD_PLUGIN([xauth-generic], [c libcharon])
+ ADD_PLUGIN([xauth-eap], [c libcharon])
ADD_PLUGIN([tnc-ifmap], [c libcharon])
+ADD_PLUGIN([tnc-pdp], [c libcharon])
ADD_PLUGIN([tnc-imc], [c libcharon])
ADD_PLUGIN([tnc-imv], [c libcharon])
ADD_PLUGIN([tnc-tnccs], [c libcharon])
AM_CONDITIONAL(USE_EAP_PEAP, test x$eap_peap = xtrue)
AM_CONDITIONAL(USE_EAP_TNC, test x$eap_tnc = xtrue)
AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue)
+ AM_CONDITIONAL(USE_XAUTH_GENERIC, test x$xauth_generic = xtrue)
+ AM_CONDITIONAL(USE_XAUTH_EAP, test x$xauth_eap = xtrue)
AM_CONDITIONAL(USE_TNC_IFMAP, test x$tnc_ifmap = xtrue)
+AM_CONDITIONAL(USE_TNC_PDP, test x$tnc_pdp = xtrue)
AM_CONDITIONAL(USE_TNC_IMC, test x$tnc_imc = xtrue)
AM_CONDITIONAL(USE_TNC_IMV, test x$tnc_imv = xtrue)
AM_CONDITIONAL(USE_TNC_TNCCS, test x$tnc_tnccs = xtrue)
src/libcharon/plugins/eap_peap/Makefile
src/libcharon/plugins/eap_tnc/Makefile
src/libcharon/plugins/eap_radius/Makefile
+ src/libcharon/plugins/xauth_generic/Makefile
+ src/libcharon/plugins/xauth_eap/Makefile
src/libcharon/plugins/tnc_ifmap/Makefile
+ src/libcharon/plugins/tnc_pdp/Makefile
src/libcharon/plugins/tnc_imc/Makefile
src/libcharon/plugins/tnc_imv/Makefile
src/libcharon/plugins/tnc_tnccs/Makefile
.BR passthrough ,
signifying that no IPsec processing should be done at all;
.BR drop ,
- signifying that packets should be discarded; and
- .BR reject ,
- signifying that packets should be discarded and a diagnostic ICMP returned
- .RB ( reject
- is currently not supported by the NETKEY stack of the Linux 2.6 kernel).
+ signifying that packets should be discarded.
.TP
.BR xauth " = " client " | server"
-specifies the role in the XAUTH protocol if activated by
+specifies the role in the XAuth protocol if activated by
.B authby=xauthpsk
or
.B authby=xauthrsasig.
DESTROY_IF(this->public.ike_sa_manager);
DESTROY_IF(this->public.controller);
DESTROY_IF(this->public.eap);
-#ifdef ME
- DESTROY_IF(this->public.connect_manager);
- DESTROY_IF(this->public.mediation_manager);
-#endif /* ME */
+ DESTROY_IF(this->public.xauth);
DESTROY_IF(this->public.backends);
DESTROY_IF(this->public.socket);
"IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED",
"IKEV2_MESSAGE_ID_SYNC",
"IPSEC_REPLAY_COUNTER_SYNC");
- ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, IPSEC_REPLAY_COUNTER_SYNC,
+ ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
+ "INITIAL_CONTACT");
+ ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1,
+ "DPD_R_U_THERE",
+ "DPD_R_U_THERE_ACK");
+ ENUM_NEXT(notify_type_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK,
+ "UNITY_LOAD_BALANCE");
+ ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE,
"USE_BEET_MODE");
-ENUM_NEXT(notify_type_names, ME_MEDIATION, ME_RESPONSE, USE_BEET_MODE,
+ENUM_NEXT(notify_type_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE,
"ME_MEDIATION",
"ME_ENDPOINT",
"ME_CALLBACK",
"RPL_CTR_SYN_SUP",
"MSG_ID_SYN",
"RPL_CTR_SYN");
- ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, IPSEC_REPLAY_COUNTER_SYNC,
+ ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
+ "INITIAL_CONTACT");
+ ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1,
+ "DPD",
+ "DPD_ACK");
+ ENUM_NEXT(notify_type_short_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK,
+ "UNITY_LB");
+ ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE,
"BEET_MODE");
-ENUM_NEXT(notify_type_short_names, ME_MEDIATION, ME_RESPONSE, USE_BEET_MODE,
+ENUM_NEXT(notify_type_short_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE,
"ME_MED",
"ME_EP",
"ME_CB",
--- /dev/null
- message_t *message, bool incoming)
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "eap_radius_accounting.h"
+#include "eap_radius_plugin.h"
+
+#include <time.h>
+
+#include <radius_message.h>
+#include <radius_client.h>
+#include <daemon.h>
+#include <utils/hashtable.h>
+#include <threading/mutex.h>
+
+typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t;
+
+/**
+ * Private data of an eap_radius_accounting_t object.
+ */
+struct private_eap_radius_accounting_t {
+
+ /**
+ * Public eap_radius_accounting_t interface.
+ */
+ eap_radius_accounting_t public;
+
+ /**
+ * Hashtable with sessions, IKE_SA unique id => entry_t
+ */
+ hashtable_t *sessions;
+
+ /**
+ * Mutex to lock sessions
+ */
+ mutex_t *mutex;
+
+ /**
+ * Session ID prefix
+ */
+ u_int32_t prefix;
+};
+
+/**
+ * Hashtable entry with usage stats
+ */
+typedef struct {
+ /** RADIUS accounting session ID */
+ char sid[16];
+ /** number of octets sent */
+ u_int64_t sent;
+ /** number of octets received */
+ u_int64_t received;
+ /** session creation time */
+ time_t created;
+} entry_t;
+
+/**
+ * Accounting message status types
+ */
+typedef enum {
+ ACCT_STATUS_START = 1,
+ ACCT_STATUS_STOP = 2,
+ ACCT_STATUS_INTERIM_UPDATE = 3,
+ ACCT_STATUS_ACCOUNTING_ON = 7,
+ ACCT_STATUS_ACCOUNTING_OFF = 8,
+} radius_acct_status_t;
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(uintptr_t key)
+{
+ return key;
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(uintptr_t a, uintptr_t b)
+{
+ return a == b;
+}
+
+/**
+ * Update usage counter when a CHILD_SA rekeys/goes down
+ */
+static void update_usage(private_eap_radius_accounting_t *this,
+ ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ u_int64_t sent, received;
+ entry_t *entry;
+
+ child_sa->get_usestats(child_sa, FALSE, NULL, &sent);
+ child_sa->get_usestats(child_sa, TRUE, NULL, &received);
+
+ this->mutex->lock(this->mutex);
+ entry = this->sessions->get(this->sessions,
+ (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
+ if (entry)
+ {
+ entry->sent += sent;
+ entry->received += received;
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Send a RADIUS message, wait for response
+ */
+static bool send_message(private_eap_radius_accounting_t *this,
+ radius_message_t *request)
+{
+ radius_message_t *response;
+ radius_client_t *client;
+ bool ack = FALSE;
+
+ client = eap_radius_create_client();
+ if (client)
+ {
+ response = client->request(client, request);
+ if (response)
+ {
+ ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
+ response->destroy(response);
+ }
+ else
+ {
+ charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING);
+ }
+ client->destroy(client);
+ }
+ return ack;
+}
+
+/**
+ * Add common IKE_SA parameters to RADIUS account message
+ */
+static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa)
+{
+ host_t *vip;
+ char buf[64];
+ chunk_t data;
+
+ snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
+ message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf)));
+ snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa));
+ message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf)));
+ vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
+ if (vip && vip->get_family(vip) == AF_INET)
+ {
+ message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip));
+ }
+ if (vip && vip->get_family(vip) == AF_INET6)
+ {
+ /* we currently assign /128 prefixes, only (reserved, length) */
+ data = chunk_from_chars(0, 128);
+ data = chunk_cata("cc", data, vip->get_address(vip));
+ message->add(message, RAT_FRAMED_IPV6_PREFIX, data);
+ }
+}
+
+/**
+ * Send an accounting start message
+ */
+static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
+{
+ radius_message_t *message;
+ entry_t *entry;
+ u_int32_t id, value;
+
+ id = ike_sa->get_unique_id(ike_sa);
+ INIT(entry,
+ .created = time_monotonic(NULL),
+ );
+ snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id);
+
+ message = radius_message_create(RMC_ACCOUNTING_REQUEST);
+ value = htonl(ACCT_STATUS_START);
+ message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
+ message->add(message, RAT_ACCT_SESSION_ID,
+ chunk_create(entry->sid, strlen(entry->sid)));
+ add_ike_sa_parameters(message, ike_sa);
+ if (send_message(this, message))
+ {
+ this->mutex->lock(this->mutex);
+ entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry);
+ this->mutex->unlock(this->mutex);
+ free(entry);
+ }
+ message->destroy(message);
+}
+
+/**
+ * Send an account stop message
+ */
+static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
+{
+ radius_message_t *message;
+ entry_t *entry;
+ u_int32_t id, value;
+
+ id = ike_sa->get_unique_id(ike_sa);
+ this->mutex->lock(this->mutex);
+ entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id);
+ this->mutex->unlock(this->mutex);
+ if (entry)
+ {
+ message = radius_message_create(RMC_ACCOUNTING_REQUEST);
+ value = htonl(ACCT_STATUS_STOP);
+ message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
+ message->add(message, RAT_ACCT_SESSION_ID,
+ chunk_create(entry->sid, strlen(entry->sid)));
+ add_ike_sa_parameters(message, ike_sa);
+ value = htonl(entry->sent);
+ message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
+ value = htonl(entry->sent >> 32);
+ if (value)
+ {
+ message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
+ chunk_from_thing(value));
+ }
+ value = htonl(entry->received);
+ message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
+ value = htonl(entry->received >> 32);
+ if (value)
+ {
+ message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
+ chunk_from_thing(value));
+ }
+ value = htonl(time_monotonic(NULL) - entry->created);
+ message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
+
+ send_message(this, message);
+ message->destroy(message);
+ free(entry);
+ }
+}
+
+METHOD(listener_t, ike_updown, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
+{
+ if (!up)
+ {
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+
+ /* update usage for all children just before sending stop */
+ enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
+ while (enumerator->enumerate(enumerator, &child_sa))
+ {
+ update_usage(this, ike_sa, child_sa);
+ }
+ enumerator->destroy(enumerator);
+
+ send_stop(this, ike_sa);
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, message_hook, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
- if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
++ message_t *message, bool incoming, bool plain)
+{
+ /* start accounting here, virtual IP now is set */
++ if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
+ message->get_exchange_type(message) == IKE_AUTH &&
+ !incoming && !message->get_request(message))
+ {
+ send_start(this, ike_sa);
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, child_rekey, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
+ child_sa_t *old, child_sa_t *new)
+{
+ update_usage(this, ike_sa, old);
+
+ return TRUE;
+}
+
+METHOD(listener_t, child_updown, bool,
+ private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
+ child_sa_t *child_sa, bool up)
+{
+ if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
+ {
+ update_usage(this, ike_sa, child_sa);
+ }
+ return TRUE;
+}
+
+METHOD(eap_radius_accounting_t, destroy, void,
+ private_eap_radius_accounting_t *this)
+{
+ this->mutex->destroy(this->mutex);
+ this->sessions->destroy(this->sessions);
+ free(this);
+}
+
+/**
+ * See header
+ */
+eap_radius_accounting_t *eap_radius_accounting_create()
+{
+ private_eap_radius_accounting_t *this;
+
+ INIT(this,
+ .public = {
+ .listener = {
+ .ike_updown = _ike_updown,
+ .message = _message_hook,
+ .child_updown = _child_updown,
+ .child_rekey = _child_rekey,
+ },
+ .destroy = _destroy,
+ },
+ /* use system time as Session ID prefix */
+ .prefix = (u_int32_t)time(NULL),
+ .sessions = hashtable_create((hashtable_hash_t)hash,
+ (hashtable_equals_t)equals, 32),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
--- /dev/null
- ike_sa_t *ike_sa, message_t *message, bool incoming)
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "eap_radius_forward.h"
+
+#include <daemon.h>
+#include <utils/linked_list.h>
+#include <utils/hashtable.h>
+#include <threading/mutex.h>
+
+typedef struct private_eap_radius_forward_t private_eap_radius_forward_t;
+
+/**
+ * Private data of an eap_radius_forward_t object.
+ */
+struct private_eap_radius_forward_t {
+
+ /**
+ * Public eap_radius_forward_t interface.
+ */
+ eap_radius_forward_t public;
+
+ /**
+ * List of attribute types to copy from IKE, as attr_t
+ */
+ linked_list_t *from_attr;
+
+ /**
+ * List of attribute types to copy to IKE, as attr_t
+ */
+ linked_list_t *to_attr;
+
+ /**
+ * Queued to forward from IKE, unique_id => linked_list_t of chunk_t
+ */
+ hashtable_t *from;
+
+ /**
+ * Queued to forward to IKE, unique_id => linked_list_t of chunk_t
+ */
+ hashtable_t *to;
+
+ /**
+ * Mutex to lock concurrent access to hashtables
+ */
+ mutex_t *mutex;
+};
+
+/**
+ * RADIUS attribute selector
+ */
+typedef struct {
+ /** vendor ID, 0 for standard attributes */
+ u_int32_t vendor;
+ /** attribute type */
+ u_int8_t type;
+} attr_t;
+
+/**
+ * Single instance of this
+ */
+static private_eap_radius_forward_t *singleton = NULL;
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(uintptr_t key)
+{
+ return key;
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(uintptr_t a, uintptr_t b)
+{
+ return a == b;
+}
+
+/**
+ * Free a queue entry
+ */
+static void free_attribute(chunk_t *chunk)
+{
+ free(chunk->ptr);
+ free(chunk);
+}
+
+/**
+ * Lookup/create an attribute queue from a table
+ */
+static linked_list_t *lookup_queue(private_eap_radius_forward_t *this,
+ hashtable_t *table)
+{
+ linked_list_t *queue = NULL;
+ ike_sa_t *ike_sa;
+ uintptr_t id;
+
+ ike_sa = charon->bus->get_sa(charon->bus);
+ if (ike_sa && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN))
+ {
+ id = ike_sa->get_unique_id(ike_sa);
+ this->mutex->lock(this->mutex);
+ queue = table->get(table, (void*)id);
+ if (!queue)
+ {
+ queue = linked_list_create();
+ table->put(table, (void*)id, queue);
+ }
+ this->mutex->unlock(this->mutex);
+ }
+ return queue;
+}
+
+/**
+ * Remove attribute queue from table
+ */
+static void remove_queue(private_eap_radius_forward_t *this,
+ hashtable_t *table, ike_sa_t *ike_sa)
+{
+ linked_list_t *queue;
+
+ this->mutex->lock(this->mutex);
+ queue = table->remove(table, (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
+ this->mutex->unlock(this->mutex);
+ if (queue)
+ {
+ queue->destroy_function(queue, (void*)free_attribute);
+ }
+}
+
+/**
+ * Check if RADIUS attribute is contained in selector
+ */
+static bool is_attribute_selected(linked_list_t *selector,
+ radius_attribute_type_t type, chunk_t data)
+{
+ enumerator_t *enumerator;
+ u_int32_t vendor = 0;
+ attr_t *sel;
+ bool found = FALSE;
+
+ if (type == RAT_VENDOR_SPECIFIC)
+ {
+ if (data.len < 4)
+ {
+ return FALSE;
+ }
+ vendor = untoh32(data.ptr);
+ }
+ enumerator = selector->create_enumerator(selector);
+ while (!found && enumerator->enumerate(enumerator, &sel))
+ {
+ if (sel->vendor == vendor)
+ {
+ if (vendor)
+ {
+ if (sel->type == 0)
+ { /* any of that vendor is fine */
+ found = TRUE;
+ }
+ else if (data.len > 4 && data.ptr[4] == sel->type)
+ { /* vendor specific type field, as defined in RFC 2865 */
+ found = TRUE;
+ }
+ }
+ else
+ {
+ if (sel->type == type)
+ {
+ found = TRUE;
+ }
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return found;
+}
+
+/**
+ * Copy RADIUS attributes from queue to a RADIUS message
+ */
+static void queue2radius(linked_list_t *queue, radius_message_t *message)
+{
+ chunk_t *data;
+
+ while (queue->remove_last(queue, (void**)&data) == SUCCESS)
+ {
+ if (data->len >= 2)
+ {
+ message->add(message, data->ptr[0], chunk_skip(*data, 2));
+ }
+ free_attribute(data);
+ }
+}
+
+/**
+ * Copy RADIUS attributes from a RADIUS message to the queue
+ */
+static void radius2queue(radius_message_t *message, linked_list_t *queue,
+ linked_list_t *selector)
+{
+ enumerator_t *enumerator;
+ int type;
+ chunk_t data, hdr, *ptr;
+
+ enumerator = message->create_enumerator(message);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ if (is_attribute_selected(selector, type, data))
+ {
+ hdr = chunk_alloc(2);
+ hdr.ptr[0] = type;
+ hdr.ptr[1] = data.len + 2;
+
+ INIT(ptr);
+ *ptr = chunk_cat("mc", hdr, data);
+ queue->insert_last(queue, ptr);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Copy RADIUS attribute nofifies from IKE message to queue
+ */
+static void ike2queue(message_t *message, linked_list_t *queue,
+ linked_list_t *selector)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ notify_payload_t *notify;
+ chunk_t data, *ptr;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify = (notify_payload_t*)payload;
+ if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE)
+ {
+ data = notify->get_notification_data(notify);
+ if (data.len >= 2 && is_attribute_selected(selector,
+ data.ptr[0], chunk_skip(data, 2)))
+ {
+ INIT(ptr);
+ *ptr = chunk_clone(data);
+ queue->insert_last(queue, ptr);
+ }
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Copy RADUIS attributes from queue to IKE message notifies
+ */
+static void queue2ike(linked_list_t *queue, message_t *message)
+{
+ chunk_t *data;
+
+ while (queue->remove_last(queue, (void**)&data) == SUCCESS)
+ {
+ message->add_notify(message, FALSE, RADIUS_ATTRIBUTE, *data);
+ free_attribute(data);
+ }
+}
+
+/**
+ * See header.
+ */
+void eap_radius_forward_from_ike(radius_message_t *request)
+{
+ private_eap_radius_forward_t *this = singleton;
+ linked_list_t *queue;
+
+ if (this)
+ {
+ queue = lookup_queue(this, this->from);
+ if (queue)
+ {
+ queue2radius(queue, request);
+ }
+ }
+}
+
+/**
+ * See header.
+ */
+void eap_radius_forward_to_ike(radius_message_t *response)
+{
+ private_eap_radius_forward_t *this = singleton;
+ linked_list_t *queue;
+
+ if (this)
+ {
+ queue = lookup_queue(this, this->to);
+ if (queue)
+ {
+ radius2queue(response, queue, this->to_attr);
+ }
+ }
+}
+
+METHOD(listener_t, message, bool,
+ private_eap_radius_forward_t *this,
- if (message->get_exchange_type(message) == IKE_AUTH)
++ ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain)
+{
+ linked_list_t *queue;
+
++ if (plain && message->get_exchange_type(message) == IKE_AUTH)
+ {
+ if (incoming)
+ {
+ queue = lookup_queue(this, this->from);
+ if (queue)
+ {
+ ike2queue(message, queue, this->from_attr);
+ }
+ }
+ else
+ {
+ queue = lookup_queue(this, this->to);
+ if (queue)
+ {
+ queue2ike(queue, message);
+ }
+ }
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, ike_updown, bool,
+ private_eap_radius_forward_t *this, ike_sa_t *ike_sa, bool up)
+{
+ /* up or down, we don't need the state anymore */
+ remove_queue(this, this->from, ike_sa);
+ remove_queue(this, this->to, ike_sa);
+ return TRUE;
+}
+
+/**
+ * Parse a selector string to a list of attr_t selectors
+ */
+static linked_list_t* parse_selector(char *selector)
+{
+ enumerator_t *enumerator;
+ linked_list_t *list;
+ char *token, *pos;
+
+ list = linked_list_create();
+ enumerator = enumerator_create_token(selector, ",", " ");
+ while (enumerator->enumerate(enumerator, &token))
+ {
+ int type, vendor = 0;
+ attr_t *attr;
+
+ pos = strchr(token, ':');
+ if (pos)
+ {
+ *(pos++) = 0;
+ vendor = atoi(token);
+ token = pos;
+ }
+ type = enum_from_name(radius_attribute_type_names, token);
+ if (type == -1)
+ {
+ type = atoi(token);
+ }
+ if (vendor == 0 && type == 0)
+ {
+ DBG1(DBG_CFG, "ignoring unknown RADIUS attribute type '%s'", token);
+ }
+ else
+ {
+ INIT(attr,
+ .type = type,
+ .vendor = vendor,
+ );
+ list->insert_last(list, attr);
+ if (!vendor)
+ {
+ DBG1(DBG_IKE, "forwarding RADIUS attribute %N",
+ radius_attribute_type_names, type);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "forwarding RADIUS VSA %d-%d", vendor, type);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ return list;
+}
+
+METHOD(eap_radius_forward_t, destroy, void,
+ private_eap_radius_forward_t *this)
+{
+ this->from_attr->destroy_function(this->from_attr, free);
+ this->to_attr->destroy_function(this->to_attr, free);
+ this->from->destroy(this->from);
+ this->to->destroy(this->to);
+ this->mutex->destroy(this->mutex);
+ free(this);
+ singleton = NULL;
+}
+
+/**
+ * See header
+ */
+eap_radius_forward_t *eap_radius_forward_create()
+{
+ private_eap_radius_forward_t *this;
+
+ INIT(this,
+ .public = {
+ .listener = {
+ .message = _message,
+ .ike_updown = _ike_updown,
+ },
+ .destroy = _destroy,
+ },
+ .from_attr = parse_selector(lib->settings->get_str(lib->settings,
+ "charon.plugins.eap-radius.forward.ike_to_radius", "")),
+ .to_attr = parse_selector(lib->settings->get_str(lib->settings,
+ "charon.plugins.eap-radius.forward.radius_to_ike", "")),
+ .from = hashtable_create((hashtable_hash_t)hash,
+ (hashtable_equals_t)equals, 8),
+ .to = hashtable_create((hashtable_hash_t)hash,
+ (hashtable_equals_t)equals, 8),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ if (this->from_attr->get_count(this->from_attr) == 0 &&
+ this->to_attr->get_count(this->to_attr) == 0)
+ {
+ destroy(this);
+ return NULL;
+ }
+
+ singleton = this;
+ return &this->public;
+}
#include <debug.h>
#include <daemon.h>
-
+#include <radius_message.h>
- #include <sa/authenticators/eap/eap_method.h>
+ #include <sa/eap/eap_method.h>
typedef struct private_eap_ttls_peer_t private_eap_ttls_peer_t;
--- /dev/null
- ike_sa_t *ike_sa, message_t *message, bool incoming)
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "radattr_listener.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include <daemon.h>
+
+#include <radius_message.h>
+
+/**
+ * Maximum size of an attribute to add
+ */
+#define MAX_ATTR_SIZE 1024
+
+typedef struct private_radattr_listener_t private_radattr_listener_t;
+
+/**
+ * Private data of an radattr_listener_t object.
+ */
+struct private_radattr_listener_t {
+
+ /**
+ * Public radattr_listener_t interface.
+ */
+ radattr_listener_t public;
+
+ /**
+ * Directory to look for attribute files
+ */
+ char *dir;
+
+ /**
+ * IKE_AUTH message ID to attribute
+ */
+ int mid;
+};
+
+/**
+ * Print RADIUS attributes found in IKE message notifies
+ */
+static void print_radius_attributes(private_radattr_listener_t *this,
+ message_t *message)
+{
+ radius_attribute_type_t type;
+ enumerator_t *enumerator;
+ notify_payload_t *notify;
+ payload_t *payload;
+ chunk_t data;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify = (notify_payload_t*)payload;
+ if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE)
+ {
+ data = notify->get_notification_data(notify);
+ if (data.len >= 2)
+ {
+ type = data.ptr[0];
+ data = chunk_skip(data, 2);
+ if (chunk_printable(data, NULL, 0))
+ {
+ DBG1(DBG_IKE, "received RADIUS %N: %.*s",
+ radius_attribute_type_names, type,
+ (int)data.len, data.ptr);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received RADIUS %N: %#B",
+ radius_attribute_type_names, type, &data);
+
+ }
+ }
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Add a RADIUS attribute from a client-ID specific file to an IKE message
+ */
+static void add_radius_attribute(private_radattr_listener_t *this,
+ ike_sa_t *ike_sa, message_t *message)
+{
+ if (this->dir &&
+ (this->mid == -1 || message->get_message_id(message) == this->mid))
+ {
+ identification_t *id;
+ auth_cfg_t *auth;
+ char path[PATH_MAX];
+ chunk_t data;
+ struct stat sb;
+ void *addr;
+ int fd;
+
+ auth = ike_sa->get_auth_cfg(ike_sa, TRUE);
+ id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
+ if (!id)
+ {
+ id = ike_sa->get_my_id(ike_sa);
+ }
+
+ snprintf(path, sizeof(path), "%s/%Y", this->dir, id);
+ fd = open(path, O_RDONLY);
+ if (fd != -1)
+ {
+ if (fstat(fd, &sb) != -1)
+ {
+ if (sb.st_size <= MAX_ATTR_SIZE)
+ {
+ addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (addr != MAP_FAILED)
+ {
+ data = chunk_create(addr, sb.st_size);
+ if (data.len >= 2)
+ {
+ DBG1(DBG_CFG, "adding RADIUS %N attribute",
+ radius_attribute_type_names, data.ptr[0]);
+ message->add_notify(message, FALSE,
+ RADIUS_ATTRIBUTE, data);
+ }
+ munmap(addr, sb.st_size);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "mapping RADIUS attribute '%s' failed: %s",
+ path, strerror(errno));
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, "RADIUS attribute '%s' exceeds size limit",
+ path);
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, "fstat RADIUS attribute '%s' failed: %s",
+ path, strerror(errno));
+ }
+ close(fd);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "reading RADIUS attribute '%s' failed: %s",
+ path, strerror(errno));
+ }
+ }
+}
+
+METHOD(listener_t, message, bool,
+ private_radattr_listener_t *this,
- if (ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN) &&
++ ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain)
+{
++ if (plain && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN) &&
+ message->get_exchange_type(message) == IKE_AUTH &&
+ message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
+ {
+ if (incoming)
+ {
+ print_radius_attributes(this, message);
+ }
+ else
+ {
+ add_radius_attribute(this, ike_sa, message);
+ }
+ }
+ return TRUE;
+}
+
+
+METHOD(radattr_listener_t, destroy, void,
+ private_radattr_listener_t *this)
+{
+ free(this);
+}
+
+/**
+ * See header
+ */
+radattr_listener_t *radattr_listener_create()
+{
+ private_radattr_listener_t *this;
+
+ INIT(this,
+ .public = {
+ .listener = {
+ .message = _message,
+ },
+ .destroy = _destroy,
+ },
+ .dir = lib->settings->get_str(lib->settings,
+ "charon.plugins.radattr.dir", NULL),
+ .mid = lib->settings->get_int(lib->settings,
+ "charon.plugins.radattr.message_id", -1),
+ );
+
+ return &this->public;
+}
#include <processing/jobs/send_dpd_job.h>
#include <processing/jobs/send_keepalive_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
- #include <encoding/payloads/unknown_payload.h>
++#include <sa/ikev2/tasks/ike_auth_lifetime.h>
#ifdef ME
- #include <sa/tasks/ike_me.h>
+ #include <sa/ikev2/tasks/ike_me.h>
#include <processing/jobs/initiate_mediation_job.h>
#endif
}
else
{
- DBG1(DBG_IKE, "reauthenticating actively");
+ DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d] actively",
+ get_name(this), this->unique_id);
}
}
- task = (task_t*)ike_reauth_create(&this->public);
- this->task_manager->queue_task(this->task_manager, task);
-
+ else
+ {
+ DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d]",
+ get_name(this), this->unique_id);
+ }
+ this->task_manager->queue_ike_reauth(this->task_manager);
return this->task_manager->initiate(this->task_manager);
}
DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)",
this->keyingtry + 1, tries);
reset(this);
- requeue_init_tasks(this);
+ resolve_hosts(this);
+ this->task_manager->queue_ike(this->task_manager);
return this->task_manager->initiate(this->task_manager);
}
DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
return SUCCESS;
}
-METHOD(ike_sa_t, set_auth_lifetime, void,
+METHOD(ike_sa_t, set_auth_lifetime, status_t,
private_ike_sa_t *this, u_int32_t lifetime)
{
- u_int32_t reduction = this->peer_cfg->get_over_time(this->peer_cfg);
- u_int32_t reauth_time = time_monotonic(NULL) + lifetime - reduction;
+ u_int32_t diff, hard, soft, now;
+ ike_auth_lifetime_t *task;
+ bool send_update;
+
+ diff = this->peer_cfg->get_over_time(this->peer_cfg);
+ now = time_monotonic(NULL);
+ hard = now + lifetime;
+ soft = hard - diff;
- if (lifetime < reduction)
+ /* check if we have to send an AUTH_LIFETIME to enforce the new lifetime.
+ * We send the notify in IKE_AUTH if not yet ESTABLISHED. */
- send_update = this->state == IKE_ESTABLISHED &&
++ send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 &&
+ !has_condition(this, COND_ORIGINAL_INITIATOR) &&
+ (this->other_virtual_ip != NULL ||
+ has_condition(this, COND_EAP_AUTHENTICATED));
+
+ if (lifetime < diff)
{
- DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, starting reauthentication",
- lifetime);
- lib->processor->queue_job(lib->processor,
+ this->stats[STAT_REAUTH] = now;
+
+ if (!send_update)
+ {
+ DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, "
+ "starting reauthentication", lifetime);
+ lib->processor->queue_job(lib->processor,
(job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE));
+ }
}
else if (this->stats[STAT_REAUTH] == 0 ||
- this->stats[STAT_REAUTH] > reauth_time)
+ this->stats[STAT_REAUTH] > soft)
{
- this->stats[STAT_REAUTH] = reauth_time;
- DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication"
- " in %ds", lifetime, lifetime - reduction);
- lib->scheduler->schedule_job(lib->scheduler,
+ this->stats[STAT_REAUTH] = soft;
+ if (!send_update)
+ {
+ DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling "
+ "reauthentication in %ds", lifetime, lifetime - diff);
+ lib->scheduler->schedule_job(lib->scheduler,
(job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE),
- lifetime - reduction);
+ lifetime - diff);
+ }
}
else
{
--- /dev/null
- this->queued_tasks->destroy_offset(this->queued_tasks,
- offsetof(task_t, destroy));
- this->queued_tasks = linked_list_create();
+ /*
+ * Copyright (C) 2007-2011 Tobias Brunner
+ * Copyright (C) 2007-2010 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+ #include "task_manager_v2.h"
+
+ #include <math.h>
+
+ #include <daemon.h>
+ #include <sa/ikev2/tasks/ike_init.h>
+ #include <sa/ikev2/tasks/ike_natd.h>
+ #include <sa/ikev2/tasks/ike_mobike.h>
+ #include <sa/ikev2/tasks/ike_auth.h>
+ #include <sa/ikev2/tasks/ike_auth_lifetime.h>
+ #include <sa/ikev2/tasks/ike_cert_pre.h>
+ #include <sa/ikev2/tasks/ike_cert_post.h>
+ #include <sa/ikev2/tasks/ike_rekey.h>
+ #include <sa/ikev2/tasks/ike_reauth.h>
+ #include <sa/ikev2/tasks/ike_delete.h>
+ #include <sa/ikev2/tasks/ike_config.h>
+ #include <sa/ikev2/tasks/ike_dpd.h>
+ #include <sa/ikev2/tasks/ike_vendor.h>
+ #include <sa/ikev2/tasks/child_create.h>
+ #include <sa/ikev2/tasks/child_rekey.h>
+ #include <sa/ikev2/tasks/child_delete.h>
+ #include <encoding/payloads/delete_payload.h>
+ #include <encoding/payloads/unknown_payload.h>
+ #include <processing/jobs/retransmit_job.h>
+ #include <processing/jobs/delete_ike_sa_job.h>
+
+ #ifdef ME
+ #include <sa/ikev2/tasks/ike_me.h>
+ #endif
+
+ typedef struct exchange_t exchange_t;
+
+ /**
+ * An exchange in the air, used do detect and handle retransmission
+ */
+ struct exchange_t {
+
+ /**
+ * Message ID used for this transaction
+ */
+ u_int32_t mid;
+
+ /**
+ * generated packet for retransmission
+ */
+ packet_t *packet;
+ };
+
+ typedef struct private_task_manager_t private_task_manager_t;
+
+ /**
+ * private data of the task manager
+ */
+ struct private_task_manager_t {
+
+ /**
+ * public functions
+ */
+ task_manager_v2_t public;
+
+ /**
+ * associated IKE_SA we are serving
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Exchange we are currently handling as responder
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ } responding;
+
+ /**
+ * Exchange we are currently handling as initiator
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * how many times we have retransmitted so far
+ */
+ u_int retransmitted;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ /**
+ * type of the initated exchange
+ */
+ exchange_type_t type;
+
+ } initiating;
+
+ /**
+ * List of queued tasks not yet in action
+ */
+ linked_list_t *queued_tasks;
+
+ /**
+ * List of active tasks, initiated by ourselve
+ */
+ linked_list_t *active_tasks;
+
+ /**
+ * List of tasks initiated by peer
+ */
+ linked_list_t *passive_tasks;
+
+ /**
+ * the task manager has been reset
+ */
+ bool reset;
+
+ /**
+ * Number of times we retransmit messages before giving up
+ */
+ u_int retransmit_tries;
+
+ /**
+ * Retransmission timeout
+ */
+ double retransmit_timeout;
+
+ /**
+ * Base to calculate retransmission timeout
+ */
+ double retransmit_base;
+ };
+
+ /**
+ * flush all tasks in the task manager
+ */
+ static void flush(private_task_manager_t *this)
+ {
- charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
- /* FALL */
- case DESTROY_ME:
+ this->passive_tasks->destroy_offset(this->passive_tasks,
+ offsetof(task_t, destroy));
+ this->passive_tasks = linked_list_create();
+ this->active_tasks->destroy_offset(this->active_tasks,
+ offsetof(task_t, destroy));
+ this->active_tasks = linked_list_create();
++ this->queued_tasks->destroy_offset(this->queued_tasks,
++ offsetof(task_t, destroy));
++ this->queued_tasks = linked_list_create();
+ }
+
+ /**
+ * move a task of a specific type from the queue to the active list
+ */
+ static bool activate_task(private_task_manager_t *this, task_type_t type)
+ {
+ enumerator_t *enumerator;
+ task_t *task;
+ bool found = FALSE;
+
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, (void**)&task))
+ {
+ if (task->get_type(task) == type)
+ {
+ DBG2(DBG_IKE, " activating %N task", task_type_names, type);
+ this->queued_tasks->remove_at(this->queued_tasks, enumerator);
+ this->active_tasks->insert_last(this->active_tasks, task);
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+ }
+
+ METHOD(task_manager_t, retransmit, status_t,
+ private_task_manager_t *this, u_int32_t message_id)
+ {
+ if (this->initiating.packet && message_id == this->initiating.mid)
+ {
+ u_int32_t timeout;
+ job_t *job;
+ enumerator_t *enumerator;
+ packet_t *packet;
+ task_t *task;
+ ike_mobike_t *mobike = NULL;
+
+ /* check if we are retransmitting a MOBIKE routability check */
+ enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, (void*)&task))
+ {
+ if (task->get_type(task) == TASK_IKE_MOBIKE)
+ {
+ mobike = (ike_mobike_t*)task;
+ if (!mobike->is_probing(mobike))
+ {
+ mobike = NULL;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (mobike == NULL)
+ {
+ if (this->initiating.retransmitted <= this->retransmit_tries)
+ {
+ timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 *
+ pow(this->retransmit_base, this->initiating.retransmitted));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "giving up after %d retransmits",
+ this->initiating.retransmitted - 1);
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+ {
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ }
+ return DESTROY_ME;
+ }
+
+ if (this->initiating.retransmitted)
+ {
+ DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
+ this->initiating.retransmitted, message_id);
+ }
+ packet = this->initiating.packet->clone(this->initiating.packet);
+ charon->sender->send(charon->sender, packet);
+ }
+ else
+ { /* for routeability checks, we use a more aggressive behavior */
+ if (this->initiating.retransmitted <= ROUTEABILITY_CHECK_TRIES)
+ {
+ timeout = ROUTEABILITY_CHECK_INTERVAL;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "giving up after %d path probings",
+ this->initiating.retransmitted - 1);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return DESTROY_ME;
+ }
+
+ if (this->initiating.retransmitted)
+ {
+ DBG1(DBG_IKE, "path probing attempt %d",
+ this->initiating.retransmitted);
+ }
+ mobike->transmit(mobike, this->initiating.packet);
+ }
+
+ this->initiating.retransmitted++;
+ job = (job_t*)retransmit_job_create(this->initiating.mid,
+ this->ike_sa->get_id(this->ike_sa));
+ lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout);
+ }
+ return SUCCESS;
+ }
+
+ METHOD(task_manager_t, initiate, status_t,
+ private_task_manager_t *this)
+ {
+ enumerator_t *enumerator;
+ task_t *task;
+ message_t *message;
+ host_t *me, *other;
+ status_t status;
+ exchange_type_t exchange = 0;
+
+ if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
+ {
+ DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
+ exchange_type_names, this->initiating.type);
+ /* do not initiate if we already have a message in the air */
+ return SUCCESS;
+ }
+
+ if (this->active_tasks->get_count(this->active_tasks) == 0)
+ {
+ DBG2(DBG_IKE, "activating new tasks");
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_CREATED:
+ activate_task(this, TASK_IKE_VENDOR);
+ if (activate_task(this, TASK_IKE_INIT))
+ {
+ this->initiating.mid = 0;
+ exchange = IKE_SA_INIT;
+ activate_task(this, TASK_IKE_NATD);
+ activate_task(this, TASK_IKE_CERT_PRE);
+ #ifdef ME
+ /* this task has to be activated before the TASK_IKE_AUTH
+ * task, because that task pregenerates the packet after
+ * which no payloads can be added to the message anymore.
+ */
+ activate_task(this, TASK_IKE_ME);
+ #endif /* ME */
+ activate_task(this, TASK_IKE_AUTH);
+ activate_task(this, TASK_IKE_CERT_POST);
+ activate_task(this, TASK_IKE_CONFIG);
+ activate_task(this, TASK_CHILD_CREATE);
+ activate_task(this, TASK_IKE_AUTH_LIFETIME);
+ activate_task(this, TASK_IKE_MOBIKE);
+ }
+ break;
+ case IKE_ESTABLISHED:
+ if (activate_task(this, TASK_CHILD_CREATE))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, TASK_CHILD_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, TASK_CHILD_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, TASK_IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, TASK_IKE_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, TASK_IKE_REAUTH))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, TASK_IKE_MOBIKE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, TASK_IKE_DPD))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
++ if (activate_task(this, TASK_IKE_AUTH_LIFETIME))
++ {
++ exchange = INFORMATIONAL;
++ break;
++ }
+ #ifdef ME
+ if (activate_task(this, TASK_IKE_ME))
+ {
+ exchange = ME_CONNECT;
+ break;
+ }
+ #endif /* ME */
+ case IKE_REKEYING:
+ if (activate_task(this, TASK_IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_DELETING:
+ default:
+ break;
+ }
+ }
+ else
+ {
+ DBG2(DBG_IKE, "reinitiating already active tasks");
+ enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, (void**)&task))
+ {
+ DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task));
+ switch (task->get_type(task))
+ {
+ case TASK_IKE_INIT:
+ exchange = IKE_SA_INIT;
+ break;
+ case TASK_IKE_AUTH:
+ exchange = IKE_AUTH;
+ break;
+ case TASK_CHILD_CREATE:
+ case TASK_CHILD_REKEY:
+ case TASK_IKE_REKEY:
+ exchange = CREATE_CHILD_SA;
+ break;
+ case TASK_IKE_MOBIKE:
+ exchange = INFORMATIONAL;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ if (exchange == 0)
+ {
+ DBG2(DBG_IKE, "nothing to initiate");
+ /* nothing to do yet... */
+ return SUCCESS;
+ }
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+
+ message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+ message->set_message_id(message, this->initiating.mid);
+ message->set_source(message, me->clone(me));
+ message->set_destination(message, other->clone(other));
+ message->set_exchange_type(message, exchange);
+ this->initiating.type = exchange;
+ this->initiating.retransmitted = 0;
+
+ enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ this->active_tasks->remove_at(this->active_tasks, enumerator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+ {
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ }
+ /* FALL */
+ case DESTROY_ME:
+ /* critical failure, destroy IKE_SA */
+ enumerator->destroy(enumerator);
+ message->destroy(message);
+ flush(this);
+ return DESTROY_ME;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* update exchange type if a task changed it */
+ this->initiating.type = message->get_exchange_type(message);
+
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->initiating.packet);
+ if (status != SUCCESS)
+ {
+ /* message generation failed. There is nothing more to do than to
+ * close the SA */
+ message->destroy(message);
+ flush(this);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return DESTROY_ME;
+ }
+ message->destroy(message);
+
+ return retransmit(this, this->initiating.mid);
+ }
+
+ /**
+ * handle an incoming response message
+ */
+ static status_t process_response(private_task_manager_t *this,
+ message_t *message)
+ {
+ enumerator_t *enumerator;
+ task_t *task;
+
+ if (message->get_exchange_type(message) != this->initiating.type)
+ {
+ DBG1(DBG_IKE, "received %N response, but expected %N",
+ exchange_type_names, message->get_exchange_type(message),
+ exchange_type_names, this->initiating.type);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return DESTROY_ME;
+ }
+
+ /* catch if we get resetted while processing */
+ this->reset = FALSE;
+ enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ this->active_tasks->remove_at(this->active_tasks, enumerator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ /* FALL */
+ case DESTROY_ME:
+ /* critical failure, destroy IKE_SA */
+ this->active_tasks->remove_at(this->active_tasks, enumerator);
+ enumerator->destroy(enumerator);
+ task->destroy(task);
+ return DESTROY_ME;
+ }
+ if (this->reset)
+ { /* start all over again if we were reset */
+ this->reset = FALSE;
+ enumerator->destroy(enumerator);
+ return initiate(this);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ this->initiating.mid++;
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ this->initiating.packet->destroy(this->initiating.packet);
+ this->initiating.packet = NULL;
+
+ return initiate(this);
+ }
+
+ /**
+ * handle exchange collisions
+ */
+ static bool handle_collisions(private_task_manager_t *this, task_t *task)
+ {
+ enumerator_t *enumerator;
+ task_t *active;
+ task_type_t type;
+
+ type = task->get_type(task);
+
+ /* do we have to check */
+ if (type == TASK_IKE_REKEY || type == TASK_CHILD_REKEY ||
+ type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE ||
+ type == TASK_IKE_REAUTH)
+ {
+ /* find an exchange collision, and notify these tasks */
+ enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, (void**)&active))
+ {
+ switch (active->get_type(active))
+ {
+ case TASK_IKE_REKEY:
+ if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE ||
+ type == TASK_IKE_REAUTH)
+ {
+ ike_rekey_t *rekey = (ike_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ case TASK_CHILD_REKEY:
+ if (type == TASK_CHILD_REKEY || type == TASK_CHILD_DELETE)
+ {
+ child_rekey_t *rekey = (child_rekey_t*)active;
+ rekey->collide(rekey, task);
+ break;
+ }
+ continue;
+ default:
+ continue;
+ }
+ enumerator->destroy(enumerator);
+ return TRUE;
+ }
+ enumerator->destroy(enumerator);
+ }
+ return FALSE;
+ }
+
+ /**
+ * build a response depending on the "passive" task list
+ */
+ static status_t build_response(private_task_manager_t *this, message_t *request)
+ {
+ enumerator_t *enumerator;
+ task_t *task;
+ message_t *message;
+ host_t *me, *other;
+ bool delete = FALSE;
+ status_t status;
+
+ me = request->get_destination(request);
+ other = request->get_source(request);
+
+ message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+ message->set_exchange_type(message, request->get_exchange_type(request));
+ /* send response along the path the request came in */
+ message->set_source(message, me->clone(me));
+ message->set_destination(message, other->clone(other));
+ message->set_message_id(message, this->responding.mid);
+ message->set_request(message, FALSE);
+
+ enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+ while (enumerator->enumerate(enumerator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+ if (!handle_collisions(this, task))
+ {
+ task->destroy(task);
+ }
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ if (handle_collisions(this, task))
+ {
+ this->passive_tasks->remove_at(this->passive_tasks,
+ enumerator);
+ }
+ break;
++ case DESTROY_ME:
+ case FAILED:
+ default:
+ /* destroy IKE_SA, but SEND response first */
+ delete = TRUE;
+ break;
+ }
+ if (delete)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* remove resonder SPI if IKE_SA_INIT failed */
+ if (delete && request->get_exchange_type(request) == IKE_SA_INIT)
+ {
+ ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
+ id->set_responder_spi(id, 0);
+ }
+
+ /* message complete, send it */
+ DESTROY_IF(this->responding.packet);
+ this->responding.packet = NULL;
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->responding.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return DESTROY_ME;
+ }
+
+ charon->sender->send(charon->sender,
+ this->responding.packet->clone(this->responding.packet));
+ if (delete)
+ {
++ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+ }
+
+ /**
+ * handle an incoming request message
+ */
+ static status_t process_request(private_task_manager_t *this,
+ message_t *message)
+ {
+ enumerator_t *enumerator;
+ task_t *task = NULL;
+ payload_t *payload;
+ notify_payload_t *notify;
+ delete_payload_t *delete;
+
+ if (this->passive_tasks->get_count(this->passive_tasks) == 0)
+ { /* create tasks depending on request type, if not already some queued */
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ {
+ task = (task_t*)ike_vendor_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ #ifdef ME
+ task = (task_t*)ike_me_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ #endif /* ME */
+ task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_cert_post_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_config_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE,
+ NULL, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_mobike_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ case CREATE_CHILD_SA:
+ { /* FIXME: we should prevent this on mediation connections */
+ bool notify_found = FALSE, ts_found = FALSE;
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY:
+ { /* if we find a rekey notify, its CHILD_SA rekeying */
+ notify = (notify_payload_t*)payload;
+ if (notify->get_notify_type(notify) == REKEY_SA &&
+ (notify->get_protocol_id(notify) == PROTO_AH ||
+ notify->get_protocol_id(notify) == PROTO_ESP))
+ {
+ notify_found = TRUE;
+ }
+ break;
+ }
+ case TRAFFIC_SELECTOR_INITIATOR:
+ case TRAFFIC_SELECTOR_RESPONDER:
+ { /* if we don't find a TS, its IKE rekeying */
+ ts_found = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (ts_found)
+ {
+ if (notify_found)
+ {
+ task = (task_t*)child_rekey_create(this->ike_sa,
+ PROTO_NONE, 0);
+ }
+ else
+ {
+ task = (task_t*)child_create_create(this->ike_sa, NULL,
+ FALSE, NULL, NULL);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
+ }
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ case INFORMATIONAL:
+ {
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY:
+ {
+ notify = (notify_payload_t*)payload;
+ switch (notify->get_notify_type(notify))
+ {
+ case ADDITIONAL_IP4_ADDRESS:
+ case ADDITIONAL_IP6_ADDRESS:
+ case NO_ADDITIONAL_ADDRESSES:
+ case UPDATE_SA_ADDRESSES:
+ case NO_NATS_ALLOWED:
+ case UNACCEPTABLE_ADDRESSES:
+ case UNEXPECTED_NAT_DETECTED:
+ case COOKIE2:
+ case NAT_DETECTION_SOURCE_IP:
+ case NAT_DETECTION_DESTINATION_IP:
+ task = (task_t*)ike_mobike_create(
+ this->ike_sa, FALSE);
+ break;
+ case AUTH_LIFETIME:
+ task = (task_t*)ike_auth_lifetime_create(
+ this->ike_sa, FALSE);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case DELETE:
+ {
+ delete = (delete_payload_t*)payload;
+ if (delete->get_protocol_id(delete) == PROTO_IKE)
+ {
+ task = (task_t*)ike_delete_create(this->ike_sa,
+ FALSE);
+ }
+ else
+ {
+ task = (task_t*)child_delete_create(this->ike_sa,
+ PROTO_NONE, 0, FALSE);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (task)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (task == NULL)
+ {
+ task = (task_t*)ike_dpd_create(FALSE);
+ }
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ #ifdef ME
+ case ME_CONNECT:
+ {
+ task = (task_t*)ike_me_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ #endif /* ME */
+ default:
+ break;
+ }
+ }
+
+ /* let the tasks process the message */
+ enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+ while (enumerator->enumerate(enumerator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs at least another call to build() */
+ break;
+ case FAILED:
+ default:
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ /* FALL */
+ case DESTROY_ME:
+ /* critical failure, destroy IKE_SA */
+ this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+ enumerator->destroy(enumerator);
+ task->destroy(task);
+ return DESTROY_ME;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return build_response(this, message);
+ }
+
+ METHOD(task_manager_t, incr_mid, void,
+ private_task_manager_t *this, bool initiate)
+ {
+ if (initiate)
+ {
+ this->initiating.mid++;
+ }
+ else
+ {
+ this->responding.mid++;
+ }
+ }
+
+ /**
+ * Send a notify back to the sender
+ */
+ static void send_notify_response(private_task_manager_t *this,
+ message_t *request, notify_type_t type,
+ chunk_t data)
+ {
+ message_t *response;
+ packet_t *packet;
+ host_t *me, *other;
+
+ response = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+ response->set_exchange_type(response, request->get_exchange_type(request));
+ response->set_request(response, FALSE);
+ response->set_message_id(response, request->get_message_id(request));
+ response->add_notify(response, FALSE, type, data);
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ if (me->is_anyaddr(me))
+ {
+ me = request->get_destination(request);
+ this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
+ }
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ if (other->is_anyaddr(other))
+ {
+ other = request->get_source(request);
+ this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+ }
+ response->set_source(response, me->clone(me));
+ response->set_destination(response, other->clone(other));
+ if (this->ike_sa->generate_message(this->ike_sa, response,
+ &packet) == SUCCESS)
+ {
+ charon->sender->send(charon->sender, packet);
+ }
+ response->destroy(response);
+ }
+
+ /**
+ * Parse the given message and verify that it is valid.
+ */
+ static status_t parse_message(private_task_manager_t *this, message_t *msg)
+ {
+ status_t status;
+ u_int8_t type = 0;
+
+ status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
+
+ if (status == SUCCESS)
+ { /* check for unsupported critical payloads */
+ enumerator_t *enumerator;
+ unknown_payload_t *unknown;
+ payload_t *payload;
+
+ enumerator = msg->create_payload_enumerator(msg);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ unknown = (unknown_payload_t*)payload;
+ type = payload->get_type(payload);
+ if (!payload_is_known(type) &&
+ unknown->is_critical(unknown))
+ {
+ DBG1(DBG_ENC, "payload type %N is not supported, "
+ "but its critical!", payload_type_names, type);
+ status = NOT_SUPPORTED;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ if (status != SUCCESS)
+ {
+ bool is_request = msg->get_request(msg);
+
+ switch (status)
+ {
+ case NOT_SUPPORTED:
+ DBG1(DBG_IKE, "critical unknown payloads found");
+ if (is_request)
+ {
+ send_notify_response(this, msg,
+ UNSUPPORTED_CRITICAL_PAYLOAD,
+ chunk_from_thing(type));
+ incr_mid(this, FALSE);
+ }
+ break;
+ case PARSE_ERROR:
+ DBG1(DBG_IKE, "message parsing failed");
+ if (is_request)
+ {
+ send_notify_response(this, msg,
+ INVALID_SYNTAX, chunk_empty);
+ incr_mid(this, FALSE);
+ }
+ break;
+ case VERIFY_ERROR:
+ DBG1(DBG_IKE, "message verification failed");
+ if (is_request)
+ {
+ send_notify_response(this, msg,
+ INVALID_SYNTAX, chunk_empty);
+ incr_mid(this, FALSE);
+ }
+ break;
+ case FAILED:
+ DBG1(DBG_IKE, "integrity check failed");
+ /* ignored */
+ break;
+ case INVALID_STATE:
+ DBG1(DBG_IKE, "found encrypted message, but no keys available");
+ default:
+ break;
+ }
+ DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
+ exchange_type_names, msg->get_exchange_type(msg),
+ is_request ? "request" : "response",
+ msg->get_message_id(msg));
+
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED)
+ { /* invalid initiation attempt, close SA */
+ return DESTROY_ME;
+ }
+ }
+ return status;
+ }
+
+
+ METHOD(task_manager_t, process_message, status_t,
+ private_task_manager_t *this, message_t *msg)
+ {
+ host_t *me, *other;
+ status_t status;
+ u_int32_t mid;
+
+ charon->bus->message(charon->bus, msg, TRUE, FALSE);
+ status = parse_message(this, msg);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+
+ me = msg->get_destination(msg);
+ other = msg->get_source(msg);
+
+ /* if this IKE_SA is virgin, we check for a config */
+ if (this->ike_sa->get_ike_cfg(this->ike_sa) == NULL)
+ {
+ ike_sa_id_t *ike_sa_id;
+ ike_cfg_t *ike_cfg;
+ job_t *job;
+ ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other);
+ if (ike_cfg == NULL)
+ {
+ /* no config found for these hosts, destroy */
+ DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N",
+ me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
+ send_notify_response(this, msg,
+ NO_PROPOSAL_CHOSEN, chunk_empty);
+ return DESTROY_ME;
+ }
+ this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg);
+ ike_cfg->destroy(ike_cfg);
+ /* add a timeout if peer does not establish it completely */
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job,
+ lib->settings->get_int(lib->settings,
+ "charon.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT));
+ }
+ 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)
+ {
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+ this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+ msg->get_exchange_type(msg) != IKE_SA_INIT)
+ { /* only do host updates based on verified messages */
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+ { /* with MOBIKE, we do no implicit updates */
+ this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
+ }
+ }
+ charon->bus->message(charon->bus, msg, TRUE, TRUE);
+ if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+ { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+ return SUCCESS;
+ }
+ if (process_request(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ this->responding.mid++;
+ }
+ else if ((mid == this->responding.mid - 1) && this->responding.packet)
+ {
+ packet_t *clone;
+ host_t *host;
+
+ DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+ "retransmitting response", mid);
+ clone = this->responding.packet->clone(this->responding.packet);
+ host = msg->get_destination(msg);
+ clone->set_source(clone, host->clone(host));
+ host = msg->get_source(msg);
+ clone->set_destination(clone, host->clone(host));
+ charon->sender->send(charon->sender, clone);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+ mid, this->responding.mid);
+ }
+ }
+ else
+ {
+ if (mid == this->initiating.mid)
+ {
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+ this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+ msg->get_exchange_type(msg) != IKE_SA_INIT)
+ { /* only do host updates based on verified messages */
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+ { /* with MOBIKE, we do no implicit updates */
+ this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE);
+ }
+ }
+ charon->bus->message(charon->bus, msg, TRUE, TRUE);
+ if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+ { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+ return SUCCESS;
+ }
+ if (process_response(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+ mid, this->initiating.mid);
+ return SUCCESS;
+ }
+ }
+ return SUCCESS;
+ }
+
+ METHOD(task_manager_t, queue_task, void,
+ private_task_manager_t *this, task_t *task)
+ {
+ if (task->get_type(task) == TASK_IKE_MOBIKE)
+ { /* there is no need to queue more than one mobike task */
+ enumerator_t *enumerator;
+ task_t *current;
+
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, (void**)¤t))
+ {
+ if (current->get_type(current) == TASK_IKE_MOBIKE)
+ {
+ enumerator->destroy(enumerator);
+ task->destroy(task);
+ return;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+ this->queued_tasks->insert_last(this->queued_tasks, task);
+ }
+
+ /**
+ * Check if a given task has been queued already
+ */
+ static bool has_queued(private_task_manager_t *this, task_type_t type)
+ {
+ enumerator_t *enumerator;
+ bool found = FALSE;
+ task_t *task;
+
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (task->get_type(task) == type)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+ }
+
+ METHOD(task_manager_t, queue_ike, void,
+ private_task_manager_t *this)
+ {
+ if (!has_queued(this, TASK_IKE_VENDOR))
+ {
+ queue_task(this, (task_t*)ike_vendor_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_INIT))
+ {
+ queue_task(this, (task_t*)ike_init_create(this->ike_sa, TRUE, NULL));
+ }
+ if (!has_queued(this, TASK_IKE_NATD))
+ {
+ queue_task(this, (task_t*)ike_natd_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_CERT_PRE))
+ {
+ queue_task(this, (task_t*)ike_cert_pre_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_AUTH))
+ {
+ queue_task(this, (task_t*)ike_auth_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_CERT_POST))
+ {
+ queue_task(this, (task_t*)ike_cert_post_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_CONFIG))
+ {
+ queue_task(this, (task_t*)ike_config_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_AUTH_LIFETIME))
+ {
+ queue_task(this, (task_t*)ike_auth_lifetime_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_MOBIKE))
+ {
+ peer_cfg_t *peer_cfg;
+
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer_cfg->use_mobike(peer_cfg))
+ {
+ queue_task(this, (task_t*)ike_mobike_create(this->ike_sa, TRUE));
+ }
+ }
+ #ifdef ME
+ if (!has_queued(this, TASK_IKE_ME))
+ {
+ queue_task(this, (task_t*)ike_me_create(this->ike_sa, TRUE));
+ }
+ #endif /* ME */
+ }
+
+ METHOD(task_manager_t, queue_ike_rekey, void,
+ private_task_manager_t *this)
+ {
+ queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE));
+ }
+
+ METHOD(task_manager_t, queue_ike_reauth, void,
+ private_task_manager_t *this)
+ {
+ queue_task(this, (task_t*)ike_reauth_create(this->ike_sa));
+ }
+
+ METHOD(task_manager_t, queue_ike_delete, void,
+ private_task_manager_t *this)
+ {
+ queue_task(this, (task_t*)ike_delete_create(this->ike_sa, TRUE));
+ }
+
+ METHOD(task_manager_t, queue_mobike, void,
+ private_task_manager_t *this, bool roam, bool address)
+ {
+ ike_mobike_t *mobike;
+
+ mobike = ike_mobike_create(this->ike_sa, TRUE);
+ if (roam)
+ {
+ mobike->roam(mobike, address);
+ }
+ else
+ {
+ mobike->addresses(mobike);
+ }
+ queue_task(this, &mobike->task);
+ }
+
+ METHOD(task_manager_t, queue_child, void,
+ private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid,
+ traffic_selector_t *tsi, traffic_selector_t *tsr)
+ {
+ child_create_t *task;
+
+ task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr);
+ if (reqid)
+ {
+ task->use_reqid(task, reqid);
+ }
+ queue_task(this, &task->task);
+ }
+
+ METHOD(task_manager_t, queue_child_rekey, void,
+ private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi)
+ {
+ queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi));
+ }
+
+ METHOD(task_manager_t, queue_child_delete, void,
+ private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+ bool expired)
+ {
+ queue_task(this, (task_t*)child_delete_create(this->ike_sa,
+ protocol, spi, expired));
+ }
+
+ METHOD(task_manager_t, queue_dpd, void,
+ private_task_manager_t *this)
+ {
+ 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))
+ {
+ /* use mobike enabled DPD to detect NAT mapping changes */
+ mobike = ike_mobike_create(this->ike_sa, TRUE);
+ mobike->dpd(mobike);
+ queue_task(this, &mobike->task);
+ }
+ else
+ {
+ queue_task(this, (task_t*)ike_dpd_create(TRUE));
+ }
+ }
+
+ METHOD(task_manager_t, adopt_tasks, void,
+ private_task_manager_t *this, task_manager_t *other_public)
+ {
+ private_task_manager_t *other = (private_task_manager_t*)other_public;
+ task_t *task;
+
+ /* move queued tasks from other to this */
+ while (other->queued_tasks->remove_last(other->queued_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+ }
+
+ METHOD(task_manager_t, busy, bool,
+ private_task_manager_t *this)
+ {
+ return (this->active_tasks->get_count(this->active_tasks) > 0);
+ }
+
+ METHOD(task_manager_t, reset, void,
+ private_task_manager_t *this, u_int32_t initiate, u_int32_t respond)
+ {
+ enumerator_t *enumerator;
+ task_t *task;
+
+ /* reset message counters and retransmit packets */
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ if (initiate != UINT_MAX)
+ {
+ this->initiating.mid = initiate;
+ }
+ if (respond != UINT_MAX)
+ {
+ this->responding.mid = respond;
+ }
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+
+ /* reset queued tasks */
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ task->migrate(task, this->ike_sa);
+ }
+ enumerator->destroy(enumerator);
+
+ /* reset active tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+
+ this->reset = TRUE;
+ }
+
+ METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
+ private_task_manager_t *this, task_queue_t queue)
+ {
+ switch (queue)
+ {
+ case TASK_QUEUE_ACTIVE:
+ return this->active_tasks->create_enumerator(this->active_tasks);
+ case TASK_QUEUE_PASSIVE:
+ return this->passive_tasks->create_enumerator(this->passive_tasks);
+ case TASK_QUEUE_QUEUED:
+ return this->queued_tasks->create_enumerator(this->queued_tasks);
+ default:
+ return enumerator_create_empty();
+ }
+ }
+
+ METHOD(task_manager_t, destroy, void,
+ private_task_manager_t *this)
+ {
+ flush(this);
+
+ this->active_tasks->destroy(this->active_tasks);
+ this->queued_tasks->destroy(this->queued_tasks);
+ this->passive_tasks->destroy(this->passive_tasks);
+
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ free(this);
+ }
+
+ /*
+ * see header file
+ */
+ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
+ {
+ private_task_manager_t *this;
+
+ INIT(this,
+ .public = {
+ .task_manager = {
+ .process_message = _process_message,
+ .queue_task = _queue_task,
+ .queue_ike = _queue_ike,
+ .queue_ike_rekey = _queue_ike_rekey,
+ .queue_ike_reauth = _queue_ike_reauth,
+ .queue_ike_delete = _queue_ike_delete,
+ .queue_mobike = _queue_mobike,
+ .queue_child = _queue_child,
+ .queue_child_rekey = _queue_child_rekey,
+ .queue_child_delete = _queue_child_delete,
+ .queue_dpd = _queue_dpd,
+ .initiate = _initiate,
+ .retransmit = _retransmit,
+ .incr_mid = _incr_mid,
+ .reset = _reset,
+ .adopt_tasks = _adopt_tasks,
+ .busy = _busy,
+ .create_task_enumerator = _create_task_enumerator,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .initiating.type = EXCHANGE_TYPE_UNDEFINED,
+ .queued_tasks = linked_list_create(),
+ .active_tasks = linked_list_create(),
+ .passive_tasks = linked_list_create(),
+ .retransmit_tries = lib->settings->get_int(lib->settings,
+ "charon.retransmit_tries", RETRANSMIT_TRIES),
+ .retransmit_timeout = lib->settings->get_double(lib->settings,
+ "charon.retransmit_timeout", RETRANSMIT_TIMEOUT),
+ .retransmit_base = lib->settings->get_double(lib->settings,
+ "charon.retransmit_base", RETRANSMIT_BASE),
+ );
+
+ return &this->public;
+ }
return 0;
}
- METHOD(keymat_t, create_dh, diffie_hellman_t*,
- private_keymat_t *this, diffie_hellman_group_t group)
- {
- return lib->crypto->create_dh(lib->crypto, group);;
- }
-
- /**
- * Derive IKE keys for a combined AEAD algorithm
- */
- static bool derive_ike_aead(private_keymat_t *this, u_int16_t alg,
- u_int16_t key_size, prf_plus_t *prf_plus)
- {
- aead_t *aead_i, *aead_r;
- chunk_t key;
-
- /* SK_ei/SK_er used for encryption */
- aead_i = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
- aead_r = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
- if (aead_i == NULL || aead_r == NULL)
- {
- DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
- transform_type_names, ENCRYPTION_ALGORITHM,
- encryption_algorithm_names, alg, key_size);
- return FALSE;
- }
- key_size = aead_i->get_key_size(aead_i);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ei secret %B", &key);
- aead_i->set_key(aead_i, key);
- chunk_clear(&key);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_er secret %B", &key);
- aead_r->set_key(aead_r, key);
- chunk_clear(&key);
-
- if (this->initiator)
- {
- this->aead_in = aead_r;
- this->aead_out = aead_i;
- }
- else
- {
- this->aead_in = aead_i;
- this->aead_out = aead_r;
- }
- return TRUE;
- }
-
/**
- * Derive IKE keys for traditional encryption and MAC algorithms
+ * See header.
*/
- static bool derive_ike_traditional(private_keymat_t *this, u_int16_t enc_alg,
- u_int16_t enc_size, u_int16_t int_alg, prf_plus_t *prf_plus)
- {
- crypter_t *crypter_i, *crypter_r;
- signer_t *signer_i, *signer_r;
- size_t key_size;
- chunk_t key;
-
- /* SK_ai/SK_ar used for integrity protection */
- signer_i = lib->crypto->create_signer(lib->crypto, int_alg);
- signer_r = lib->crypto->create_signer(lib->crypto, int_alg);
- if (signer_i == NULL || signer_r == NULL)
- {
- DBG1(DBG_IKE, "%N %N not supported!",
- transform_type_names, INTEGRITY_ALGORITHM,
- integrity_algorithm_names, int_alg);
- return FALSE;
- }
- key_size = signer_i->get_key_size(signer_i);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ai secret %B", &key);
- signer_i->set_key(signer_i, key);
- chunk_clear(&key);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ar secret %B", &key);
- signer_r->set_key(signer_r, key);
- chunk_clear(&key);
-
- /* SK_ei/SK_er used for encryption */
- crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
- crypter_r = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
- if (crypter_i == NULL || crypter_r == NULL)
- {
- DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
- transform_type_names, ENCRYPTION_ALGORITHM,
- encryption_algorithm_names, enc_alg, enc_size);
- signer_i->destroy(signer_i);
- signer_r->destroy(signer_r);
- return FALSE;
- }
- key_size = crypter_i->get_key_size(crypter_i);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ei secret %B", &key);
- crypter_i->set_key(crypter_i, key);
- chunk_clear(&key);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_er secret %B", &key);
- crypter_r->set_key(crypter_r, key);
- chunk_clear(&key);
-
- if (this->initiator)
- {
- this->aead_in = aead_create(crypter_r, signer_r);
- this->aead_out = aead_create(crypter_i, signer_i);
- }
- else
- {
- this->aead_in = aead_create(crypter_i, signer_i);
- this->aead_out = aead_create(crypter_r, signer_r);
- }
- return TRUE;
- }
-
- METHOD(keymat_t, derive_ike_keys, bool,
- private_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh,
- chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
- pseudo_random_function_t rekey_function, chunk_t rekey_skd)
- {
- chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed;
- chunk_t spi_i, spi_r;
- prf_plus_t *prf_plus;
- u_int16_t alg, key_size, int_alg;
- prf_t *rekey_prf = NULL;
-
- spi_i = chunk_alloca(sizeof(u_int64_t));
- spi_r = chunk_alloca(sizeof(u_int64_t));
-
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
- {
- return FALSE;
- }
-
- /* Create SAs general purpose PRF first, we may use it here */
- if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
- {
- DBG1(DBG_IKE, "no %N selected",
- transform_type_names, PSEUDO_RANDOM_FUNCTION);
- return FALSE;
- }
- this->prf_alg = alg;
- this->prf = lib->crypto->create_prf(lib->crypto, alg);
- if (this->prf == NULL)
- {
- DBG1(DBG_IKE, "%N %N not supported!",
- transform_type_names, PSEUDO_RANDOM_FUNCTION,
- pseudo_random_function_names, alg);
- return FALSE;
- }
- DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
- /* full nonce is used as seed for PRF+ ... */
- full_nonce = chunk_cat("cc", nonce_i, nonce_r);
- /* but the PRF may need a fixed key which only uses the first bytes of
- * the nonces. */
- switch (alg)
- {
- case PRF_AES128_XCBC:
- /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does
- * not and therefore fixed key semantics apply to XCBC for key
- * derivation. */
- case PRF_CAMELLIA128_XCBC:
- /* draft-kanno-ipsecme-camellia-xcbc refers to rfc 4434, we
- * assume fixed key length. */
- key_size = this->prf->get_key_size(this->prf)/2;
- nonce_i.len = min(nonce_i.len, key_size);
- nonce_r.len = min(nonce_r.len, key_size);
- break;
- default:
- /* all other algorithms use variable key length, full nonce */
- break;
- }
- fixed_nonce = chunk_cat("cc", nonce_i, nonce_r);
- *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
- *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
- prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r);
-
- /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr)
- *
- * if we are rekeying, SKEYSEED is built on another way
- */
- if (rekey_function == PRF_UNDEFINED) /* not rekeying */
- {
- /* SKEYSEED = prf(Ni | Nr, g^ir) */
- this->prf->set_key(this->prf, fixed_nonce);
- this->prf->allocate_bytes(this->prf, secret, &skeyseed);
- this->prf->set_key(this->prf, skeyseed);
- prf_plus = prf_plus_create(this->prf, prf_plus_seed);
- }
- else
- {
- /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
- * use OLD SAs PRF functions for both prf_plus and prf */
- rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function);
- if (!rekey_prf)
- {
- DBG1(DBG_IKE, "PRF of old SA %N not supported!",
- pseudo_random_function_names, rekey_function);
- chunk_free(&full_nonce);
- chunk_free(&fixed_nonce);
- chunk_clear(&prf_plus_seed);
- return FALSE;
- }
- secret = chunk_cat("mc", secret, full_nonce);
- rekey_prf->set_key(rekey_prf, rekey_skd);
- rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed);
- rekey_prf->set_key(rekey_prf, skeyseed);
- prf_plus = prf_plus_create(rekey_prf, prf_plus_seed);
- }
- DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
-
- chunk_clear(&skeyseed);
- chunk_clear(&secret);
- chunk_free(&full_nonce);
- chunk_free(&fixed_nonce);
- chunk_clear(&prf_plus_seed);
-
- /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */
-
- /* SK_d is used for generating CHILD_SA key mat => store for later use */
- key_size = this->prf->get_key_size(this->prf);
- prf_plus->allocate_bytes(prf_plus, key_size, &this->skd);
- DBG4(DBG_IKE, "Sk_d secret %B", &this->skd);
-
- if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size))
- {
- DBG1(DBG_IKE, "no %N selected",
- transform_type_names, ENCRYPTION_ALGORITHM);
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
-
- if (encryption_algorithm_is_aead(alg))
- {
- if (!derive_ike_aead(this, alg, key_size, prf_plus))
- {
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
- }
- else
- {
- if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
- &int_alg, NULL))
- {
- DBG1(DBG_IKE, "no %N selected",
- transform_type_names, INTEGRITY_ALGORITHM);
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
- if (!derive_ike_traditional(this, alg, key_size, int_alg, prf_plus))
- {
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
- }
-
- /* SK_pi/SK_pr used for authentication => stored for later */
- key_size = this->prf->get_key_size(this->prf);
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_pi secret %B", &key);
- if (this->initiator)
- {
- this->skp_build = key;
- }
- else
- {
- this->skp_verify = key;
- }
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_pr secret %B", &key);
- if (this->initiator)
- {
- this->skp_verify = key;
- }
- else
- {
- this->skp_build = key;
- }
-
- /* all done, prf_plus not needed anymore */
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
-
- return TRUE;
- }
-
- METHOD(keymat_t, derive_child_keys, bool,
- private_keymat_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,
- chunk_t *encr_r, chunk_t *integ_r)
+ int keymat_get_keylen_integ(integrity_algorithm_t alg)
{
- u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
- chunk_t seed, secret = chunk_empty;
- prf_plus_t *prf_plus;
-
- if (dh)
- {
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
- {
- return FALSE;
- }
- DBG4(DBG_CHD, "DH secret %B", &secret);
- }
- seed = chunk_cata("mcc", secret, nonce_i, nonce_r);
- DBG4(DBG_CHD, "seed %B", &seed);
-
- if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
- &enc_alg, &enc_size))
- {
- DBG2(DBG_CHD, " using %N for encryption",
- encryption_algorithm_names, enc_alg);
-
- if (!enc_size)
- {
- enc_size = lookup_keylen(keylen_enc, enc_alg);
- }
- if (enc_alg != ENCR_NULL && !enc_size)
- {
- DBG1(DBG_CHD, "no keylength defined for %N",
- encryption_algorithm_names, enc_alg);
- return FALSE;
- }
- /* to bytes */
- enc_size /= 8;
-
- /* CCM/GCM/CTR/GMAC needs additional bytes */
- switch (enc_alg)
- {
- case ENCR_AES_CCM_ICV8:
- case ENCR_AES_CCM_ICV12:
- case ENCR_AES_CCM_ICV16:
- case ENCR_CAMELLIA_CCM_ICV8:
- case ENCR_CAMELLIA_CCM_ICV12:
- case ENCR_CAMELLIA_CCM_ICV16:
- enc_size += 3;
- break;
- case ENCR_AES_GCM_ICV8:
- case ENCR_AES_GCM_ICV12:
- case ENCR_AES_GCM_ICV16:
- case ENCR_AES_CTR:
- case ENCR_NULL_AUTH_AES_GMAC:
- enc_size += 4;
- break;
- default:
- break;
- }
- }
-
- if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
- &int_alg, &int_size))
- {
- DBG2(DBG_CHD, " using %N for integrity",
- integrity_algorithm_names, int_alg);
-
- if (!int_size)
+ keylen_entry_t map[] = {
+ {AUTH_HMAC_MD5_96, 128},
++ {AUTH_HMAC_MD5_128, 128},
+ {AUTH_HMAC_SHA1_96, 160},
++ {AUTH_HMAC_SHA1_160, 160},
+ {AUTH_HMAC_SHA2_256_96, 256},
+ {AUTH_HMAC_SHA2_256_128, 256},
+ {AUTH_HMAC_SHA2_384_192, 384},
+ {AUTH_HMAC_SHA2_512_256, 512},
+ {AUTH_AES_XCBC_96, 128},
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].alg == alg)
{
- int_size = lookup_keylen(keylen_int, int_alg);
+ return map[i].len;
}
- if (!int_size)
- {
- DBG1(DBG_CHD, "no keylength defined for %N",
- integrity_algorithm_names, int_alg);
- return FALSE;
- }
- /* to bytes */
- int_size /= 8;
- }
-
- this->prf->set_key(this->prf, this->skd);
- prf_plus = prf_plus_create(this->prf, seed);
-
- prf_plus->allocate_bytes(prf_plus, enc_size, encr_i);
- prf_plus->allocate_bytes(prf_plus, int_size, integ_i);
- prf_plus->allocate_bytes(prf_plus, enc_size, encr_r);
- prf_plus->allocate_bytes(prf_plus, int_size, integ_r);
-
- prf_plus->destroy(prf_plus);
-
- if (enc_size)
- {
- DBG4(DBG_CHD, "encryption initiator key %B", encr_i);
- DBG4(DBG_CHD, "encryption responder key %B", encr_r);
- }
- if (int_size)
- {
- DBG4(DBG_CHD, "integrity initiator key %B", integ_i);
- DBG4(DBG_CHD, "integrity responder key %B", integ_r);
}
- return TRUE;
- }
-
- METHOD(keymat_t, get_skd, pseudo_random_function_t,
- private_keymat_t *this, chunk_t *skd)
- {
- *skd = this->skd;
- return this->prf_alg;
- }
-
- METHOD(keymat_t, get_aead, aead_t*,
- private_keymat_t *this, bool in)
- {
- return in ? this->aead_in : this->aead_out;
- }
-
- METHOD(keymat_t, get_auth_octets, chunk_t,
- private_keymat_t *this, bool verify, chunk_t ike_sa_init,
- chunk_t nonce, identification_t *id, char reserved[3])
- {
- chunk_t chunk, idx, octets;
- chunk_t skp;
-
- skp = verify ? this->skp_verify : this->skp_build;
-
- chunk = chunk_alloca(4);
- chunk.ptr[0] = id->get_type(id);
- memcpy(chunk.ptr + 1, reserved, 3);
- idx = chunk_cata("cc", chunk, id->get_encoding(id));
-
- DBG3(DBG_IKE, "IDx' %B", &idx);
- DBG3(DBG_IKE, "SK_p %B", &skp);
- this->prf->set_key(this->prf, skp);
- this->prf->allocate_bytes(this->prf, idx, &chunk);
-
- octets = chunk_cat("ccm", ike_sa_init, nonce, chunk);
- DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets);
- return octets;
- }
-
- /**
- * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
- */
- #define IKEV2_KEY_PAD "Key Pad for IKEv2"
- #define IKEV2_KEY_PAD_LENGTH 17
-
- METHOD(keymat_t, get_psk_sig, chunk_t,
- private_keymat_t *this, bool verify, chunk_t ike_sa_init,
- chunk_t nonce, chunk_t secret, identification_t *id, char reserved[3])
- {
- chunk_t key_pad, key, sig, octets;
-
- if (!secret.len)
- { /* EAP uses SK_p if no MSK has been established */
- secret = verify ? this->skp_verify : this->skp_build;
- }
- octets = get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved);
- /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
- key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH);
- this->prf->set_key(this->prf, secret);
- this->prf->allocate_bytes(this->prf, key_pad, &key);
- this->prf->set_key(this->prf, key);
- this->prf->allocate_bytes(this->prf, octets, &sig);
- 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);
- chunk_free(&octets);
- chunk_free(&key);
-
- return sig;
- }
-
- METHOD(keymat_t, destroy, void,
- private_keymat_t *this)
- {
- DESTROY_IF(this->aead_in);
- DESTROY_IF(this->aead_out);
- DESTROY_IF(this->prf);
- chunk_clear(&this->skd);
- chunk_clear(&this->skp_verify);
- chunk_clear(&this->skp_build);
- free(this);
- }
-
- /**
- * See header
- */
- keymat_t *keymat_create(bool initiator)
- {
- private_keymat_t *this;
-
- INIT(this,
- .public = {
- .create_dh = _create_dh,
- .derive_ike_keys = _derive_ike_keys,
- .derive_child_keys = _derive_child_keys,
- .get_skd = _get_skd,
- .get_aead = _get_aead,
- .get_auth_octets = _get_auth_octets,
- .get_psk_sig = _get_psk_sig,
- .destroy = _destroy,
- },
- .initiator = initiator,
- .prf_alg = PRF_UNDEFINED,
- );
-
- return &this->public;
+ return 0;
}
-
if (!found)
{
DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
+ this->lock->unlock(this->lock);
+ return;
}
- else if (found->pending)
+ if (!cas_bool(&found->pending, FALSE, TRUE))
{
DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
+ this->lock->unlock(this->lock);
+ return;
}
- else
+ peer = found->peer_cfg->get_ref(found->peer_cfg);
+ child = found->child_sa->get_config(found->child_sa);
+ child = child->get_ref(child);
+ reqid = found->child_sa->get_reqid(found->child_sa);
+ /* don't hold the lock while checking out the IKE_SA */
+ this->lock->unlock(this->lock);
+
+ ike_sa = charon->ike_sa_manager->checkout_by_config(
+ charon->ike_sa_manager, peer);
- if (ike_sa->get_peer_cfg(ike_sa) == NULL)
++ if (ike_sa)
{
- ike_sa->set_peer_cfg(ike_sa, peer);
- }
- if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
- {
- /* make sure the entry is still there */
- this->lock->read_lock(this->lock);
- if (this->traps->find_first(this->traps, NULL,
- (void**)&found) == SUCCESS)
- child = found->child_sa->get_config(found->child_sa);
- peer = found->peer_cfg;
- ike_sa = charon->ike_sa_manager->checkout_by_config(
- charon->ike_sa_manager, peer);
- if (ike_sa)
++ if (ike_sa->get_peer_cfg(ike_sa) == NULL)
{
- found->ike_sa = ike_sa;
- if (ike_sa->get_peer_cfg(ike_sa) == NULL)
- {
- ike_sa->set_peer_cfg(ike_sa, peer);
- }
- child->get_ref(child);
- reqid = found->child_sa->get_reqid(found->child_sa);
- if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
++ ike_sa->set_peer_cfg(ike_sa, peer);
++ }
++ if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
++ {
++ /* make sure the entry is still there */
++ this->lock->read_lock(this->lock);
++ if (this->traps->find_first(this->traps, NULL,
++ (void**)&found) == SUCCESS)
+ {
- found->pending = ike_sa;
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
++ found->ike_sa = ike_sa;
+ }
- else
- {
- charon->ike_sa_manager->checkin_and_destroy(
++ this->lock->unlock(this->lock);
++ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
++ }
++ else
++ {
++ charon->ike_sa_manager->checkin_and_destroy(
+ charon->ike_sa_manager, ike_sa);
- }
}
- this->lock->unlock(this->lock);
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
- }
- else
- {
- charon->ike_sa_manager->checkin_and_destroy(
- charon->ike_sa_manager, ike_sa);
}
- this->lock->unlock(this->lock);
+ peer->destroy(peer);
}
/**
*
* To add a credential set for the current trustchain verification
* operation, sets may be added for the calling thread only. This
- * does not require a write lock and is therefore a much less expensive
+ * does not require a write lock and is therefore a much cheaper
* operation.
+ * The exclusive option allows to disable all other credential sets
+ * until the set is deregistered.
*
* @param set set to register
+ * @param exclusive TRUE to disable all other sets for this thread
*/
- void (*add_local_set)(credential_manager_t *this, credential_set_t *set);
+ void (*add_local_set)(credential_manager_t *this, credential_set_t *set,
+ bool exclusive);
/**
* Unregister a thread local credential set from the manager.