diff options
Diffstat (limited to 'src/charon/sa/tasks/child_create.c')
-rw-r--r-- | src/charon/sa/tasks/child_create.c | 1351 |
1 files changed, 0 insertions, 1351 deletions
diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c deleted file mode 100644 index 3f002f263..000000000 --- a/src/charon/sa/tasks/child_create.c +++ /dev/null @@ -1,1351 +0,0 @@ -/* - * Copyright (C) 2008 Tobias Brunner - * Copyright (C) 2005-2008 Martin Willi - * Copyright (C) 2005 Jan Hutter - * 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 "child_create.h" - -#include <daemon.h> -#include <crypto/diffie_hellman.h> -#include <credentials/certificates/x509.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <encoding/payloads/ts_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/notify_payload.h> -#include <processing/jobs/delete_ike_sa_job.h> -#include <processing/jobs/inactivity_job.h> - - -typedef struct private_child_create_t private_child_create_t; - -/** - * Private members of a child_create_t task. - */ -struct private_child_create_t { - - /** - * Public methods and task_t interface. - */ - child_create_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Are we the initiator? - */ - bool initiator; - - /** - * nonce chosen by us - */ - chunk_t my_nonce; - - /** - * nonce chosen by peer - */ - chunk_t other_nonce; - - /** - * config to create the CHILD_SA from - */ - child_cfg_t *config; - - /** - * list of proposal candidates - */ - linked_list_t *proposals; - - /** - * selected proposal to use for CHILD_SA - */ - proposal_t *proposal; - - /** - * traffic selectors for initiators side - */ - linked_list_t *tsi; - - /** - * traffic selectors for responders side - */ - linked_list_t *tsr; - - /** - * source of triggering packet - */ - traffic_selector_t *packet_tsi; - - /** - * destination of triggering packet - */ - traffic_selector_t *packet_tsr; - - /** - * optional diffie hellman exchange - */ - diffie_hellman_t *dh; - - /** - * group used for DH exchange - */ - diffie_hellman_group_t dh_group; - - /** - * IKE_SAs keymat - */ - keymat_t *keymat; - - /** - * mode the new CHILD_SA uses (transport/tunnel/beet) - */ - ipsec_mode_t mode; - - /** - * IPComp transform to use - */ - ipcomp_transform_t ipcomp; - - /** - * IPComp transform proposed or accepted by the other peer - */ - ipcomp_transform_t ipcomp_received; - - /** - * Own allocated SPI - */ - u_int32_t my_spi; - - /** - * SPI received in proposal - */ - u_int32_t other_spi; - - /** - * Own allocated Compression Parameter Index (CPI) - */ - u_int16_t my_cpi; - - /** - * Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED - */ - u_int16_t other_cpi; - - /** - * reqid to use if we are rekeying - */ - u_int32_t reqid; - - /** - * CHILD_SA which gets established - */ - child_sa_t *child_sa; - - /** - * successfully established the CHILD? - */ - bool established; - - /** - * whether the CHILD_SA rekeys an existing one - */ - bool rekey; -}; - -/** - * get the nonce from a message - */ -static status_t get_nonce(message_t *message, chunk_t *nonce) -{ - nonce_payload_t *payload; - - payload = (nonce_payload_t*)message->get_payload(message, NONCE); - if (payload == NULL) - { - return FAILED; - } - *nonce = payload->get_nonce(payload); - return NEED_MORE; -} - -/** - * generate a new nonce to include in a CREATE_CHILD_SA message - */ -static status_t generate_nonce(chunk_t *nonce) -{ - rng_t *rng; - - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng) - { - DBG1(DBG_IKE, "error generating nonce value, no RNG found"); - return FAILED; - } - rng->allocate_bytes(rng, NONCE_SIZE, nonce); - rng->destroy(rng); - return SUCCESS; -} - -/** - * Check a list of traffic selectors if any selector belongs to host - */ -static bool ts_list_is_host(linked_list_t *list, host_t *host) -{ - traffic_selector_t *ts; - bool is_host = TRUE; - iterator_t *iterator = list->create_iterator(list, TRUE); - - while (is_host && iterator->iterate(iterator, (void**)&ts)) - { - is_host = is_host && ts->is_host(ts, host); - } - iterator->destroy(iterator); - return is_host; -} - -/** - * Allocate SPIs and update proposals - */ -static bool allocate_spi(private_child_create_t *this) -{ - enumerator_t *enumerator; - proposal_t *proposal; - - /* TODO: allocate additional SPI for AH if we have such proposals */ - this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); - if (this->my_spi) - { - if (this->initiator) - { - enumerator = this->proposals->create_enumerator(this->proposals); - while (enumerator->enumerate(enumerator, &proposal)) - { - proposal->set_spi(proposal, this->my_spi); - } - enumerator->destroy(enumerator); - } - else - { - this->proposal->set_spi(this->proposal, this->my_spi); - } - return TRUE; - } - return FALSE; -} - -/** - * Schedule inactivity timeout for CHILD_SA with reqid, if enabled - */ -static void schedule_inactivity_timeout(private_child_create_t *this) -{ - u_int32_t timeout; - bool close_ike; - - timeout = this->config->get_inactivity(this->config); - if (timeout) - { - close_ike = lib->settings->get_bool(lib->settings, - "charon.inactivity_close_ike", FALSE); - charon->scheduler->schedule_job(charon->scheduler, (job_t*) - inactivity_job_create(this->child_sa->get_reqid(this->child_sa), - timeout, close_ike), timeout); - } -} - -/** - * Install a CHILD_SA for usage, return value: - * - FAILED: no acceptable proposal - * - INVALID_ARG: diffie hellman group inacceptable - * - NOT_FOUND: TS inacceptable - */ -static status_t select_and_install(private_child_create_t *this, bool no_dh) -{ - status_t status, status_i, status_o; - chunk_t nonce_i, nonce_r; - chunk_t encr_i = chunk_empty, encr_r = chunk_empty; - chunk_t integ_i = chunk_empty, integ_r = chunk_empty; - linked_list_t *my_ts, *other_ts; - host_t *me, *other, *other_vip, *my_vip; - bool private; - - if (this->proposals == NULL) - { - DBG1(DBG_IKE, "SA payload missing in message"); - return FAILED; - } - if (this->tsi == NULL || this->tsr == NULL) - { - DBG1(DBG_IKE, "TS payloads missing in message"); - return NOT_FOUND; - } - - me = this->ike_sa->get_my_host(this->ike_sa); - other = this->ike_sa->get_other_host(this->ike_sa); - my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - - private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); - this->proposal = this->config->select_proposal(this->config, - this->proposals, no_dh, private); - if (this->proposal == NULL) - { - DBG1(DBG_IKE, "no acceptable proposal found"); - return FAILED; - } - this->other_spi = this->proposal->get_spi(this->proposal); - - if (!this->initiator && !allocate_spi(this)) - { /* responder has no SPI allocated yet */ - DBG1(DBG_IKE, "allocating SPI failed"); - return FAILED; - } - this->child_sa->set_proposal(this->child_sa, this->proposal); - - if (!this->proposal->has_dh_group(this->proposal, this->dh_group)) - { - u_int16_t group; - - if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, - &group, NULL)) - { - DBG1(DBG_IKE, "DH group %N inacceptable, requesting %N", - diffie_hellman_group_names, this->dh_group, - diffie_hellman_group_names, group); - this->dh_group = group; - return INVALID_ARG; - } - else - { - DBG1(DBG_IKE, "no acceptable proposal found"); - return FAILED; - } - } - - if (my_vip == NULL) - { - my_vip = me; - } - if (other_vip == NULL) - { - other_vip = other; - } - - if (this->initiator) - { - nonce_i = this->my_nonce; - nonce_r = this->other_nonce; - my_ts = this->tsi; - other_ts = this->tsr; - } - else - { - nonce_r = this->my_nonce; - nonce_i = this->other_nonce; - my_ts = this->tsr; - other_ts = this->tsi; - } - my_ts = this->config->get_traffic_selectors(this->config, TRUE, my_ts, - my_vip); - other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts, - other_vip); - - if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0) - { - my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); - DBG1(DBG_IKE, "no acceptable traffic selectors found"); - return NOT_FOUND; - } - - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - if (this->initiator) - { - this->tsi = my_ts; - this->tsr = other_ts; - } - else - { - this->tsr = my_ts; - this->tsi = other_ts; - } - - if (!this->initiator) - { - /* check if requested mode is acceptable, downgrade if required */ - switch (this->mode) - { - case MODE_TRANSPORT: - if (!this->config->use_proxy_mode(this->config) && - (!ts_list_is_host(this->tsi, other) || - !ts_list_is_host(this->tsr, me)) - ) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using transport mode, not host-to-host"); - } - else if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using transport mode, connection NATed"); - } - break; - case MODE_BEET: - if (!ts_list_is_host(this->tsi, NULL) || - !ts_list_is_host(this->tsr, NULL)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); - } - break; - default: - break; - } - } - - /* check for any certificate-based IP address block constraints */ - if (this->mode == MODE_BEET || this->mode == MODE_TUNNEL) - { - auth_cfg_t *auth; - enumerator_t *auth_enum; - certificate_t *cert = NULL; - - auth_enum = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE); - while (auth_enum->enumerate(auth_enum, &auth)) - { - cert = auth->get(auth, AUTH_HELPER_SUBJECT_CERT); - if (cert) - { - break; - } - } - auth_enum->destroy(auth_enum); - - if (cert && cert->get_type(cert) == CERT_X509) - { - x509_t *x509 = (x509_t*)cert; - - if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS) - { - enumerator_t *enumerator, *block_enum; - traffic_selector_t *ts, *block_ts; - - DBG1(DBG_IKE, "checking certificate-based traffic selector " - "constraints [RFC 3779]"); - enumerator = other_ts->create_enumerator(other_ts); - while (enumerator->enumerate(enumerator, &ts)) - { - bool contained = FALSE; - - block_enum = x509->create_ipAddrBlock_enumerator(x509); - while (block_enum->enumerate(block_enum, &block_ts)) - { - if (ts->is_contained_in(ts, block_ts)) - { - DBG1(DBG_IKE, " TS %R is contained in address block" - " constraint %R", ts, block_ts); - contained = TRUE; - break; - } - } - block_enum->destroy(block_enum); - - if (!contained) - { - DBG1(DBG_IKE, " TS %R is not contained in any" - " address block constraint", ts); - enumerator->destroy(enumerator); - return FAILED; - } - } - enumerator->destroy(enumerator); - } - } - } - - this->child_sa->set_state(this->child_sa, CHILD_INSTALLING); - this->child_sa->set_ipcomp(this->child_sa, this->ipcomp); - this->child_sa->set_mode(this->child_sa, this->mode); - this->child_sa->set_protocol(this->child_sa, - this->proposal->get_protocol(this->proposal)); - - if (this->my_cpi == 0 || this->other_cpi == 0 || this->ipcomp == IPCOMP_NONE) - { - this->my_cpi = this->other_cpi = 0; - this->ipcomp = IPCOMP_NONE; - } - status_i = status_o = FAILED; - if (this->keymat->derive_child_keys(this->keymat, this->proposal, - this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) - { - if (this->initiator) - { - status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, - this->my_spi, this->my_cpi, TRUE, my_ts, other_ts); - status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->other_spi, this->other_cpi, FALSE, my_ts, other_ts); - } - else - { - status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->my_spi, this->my_cpi, TRUE, my_ts, other_ts); - status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, - this->other_spi, this->other_cpi, FALSE, my_ts, other_ts); - } - } - chunk_clear(&integ_i); - chunk_clear(&integ_r); - chunk_clear(&encr_i); - chunk_clear(&encr_r); - - if (status_i != SUCCESS || status_o != SUCCESS) - { - DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel", - (status_i != SUCCESS) ? "inbound " : "", - (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "", - (status_o != SUCCESS) ? "outbound " : ""); - return FAILED; - } - - status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts); - if (status != SUCCESS) - { - DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); - return NOT_FOUND; - } - - charon->bus->child_keys(charon->bus, this->child_sa, this->dh, - nonce_i, nonce_r); - - /* add to IKE_SA, and remove from task */ - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - this->established = TRUE; - - if (!this->rekey) - { /* a rekeyed SA uses the same reqid, no need for a new job */ - schedule_inactivity_timeout(this); - } - return SUCCESS; -} - -/** - * build the payloads for the message - */ -static void build_payloads(private_child_create_t *this, message_t *message) -{ - sa_payload_t *sa_payload; - nonce_payload_t *nonce_payload; - ke_payload_t *ke_payload; - ts_payload_t *ts_payload; - - /* add SA payload */ - if (this->initiator) - { - sa_payload = sa_payload_create_from_proposal_list(this->proposals); - } - else - { - sa_payload = sa_payload_create_from_proposal(this->proposal); - } - message->add_payload(message, (payload_t*)sa_payload); - - /* add nonce payload if not in IKE_AUTH */ - if (message->get_exchange_type(message) == CREATE_CHILD_SA) - { - nonce_payload = nonce_payload_create(); - nonce_payload->set_nonce(nonce_payload, this->my_nonce); - message->add_payload(message, (payload_t*)nonce_payload); - } - - /* diffie hellman exchange, if PFS enabled */ - if (this->dh) - { - ke_payload = ke_payload_create_from_diffie_hellman(this->dh); - message->add_payload(message, (payload_t*)ke_payload); - } - - /* add TSi/TSr payloads */ - ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); - message->add_payload(message, (payload_t*)ts_payload); - ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); - message->add_payload(message, (payload_t*)ts_payload); - - /* add a notify if we are not in tunnel mode */ - switch (this->mode) - { - case MODE_TRANSPORT: - message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty); - break; - case MODE_BEET: - message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty); - break; - default: - break; - } -} - -/** - * Adds an IPCOMP_SUPPORTED notify to the message, allocating a CPI - */ -static void add_ipcomp_notify(private_child_create_t *this, - message_t *message, u_int8_t ipcomp) -{ - if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) - { - DBG1(DBG_IKE, "IPComp is not supported if either peer is natted, " - "IPComp disabled"); - return; - } - - this->my_cpi = this->child_sa->alloc_cpi(this->child_sa); - if (this->my_cpi) - { - this->ipcomp = ipcomp; - message->add_notify(message, FALSE, IPCOMP_SUPPORTED, - chunk_cata("cc", chunk_from_thing(this->my_cpi), - chunk_from_thing(ipcomp))); - } - else - { - DBG1(DBG_IKE, "unable to allocate a CPI from kernel, IPComp disabled"); - } -} - -/** - * handle a received notify payload - */ -static void handle_notify(private_child_create_t *this, notify_payload_t *notify) -{ - switch (notify->get_notify_type(notify)) - { - case USE_TRANSPORT_MODE: - this->mode = MODE_TRANSPORT; - break; - case USE_BEET_MODE: - if (this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN)) - { /* handle private use notify only if we know its meaning */ - this->mode = MODE_BEET; - } - else - { - DBG1(DBG_IKE, "received a notify strongSwan uses for BEET " - "mode, but peer implementation unknown, skipped"); - } - break; - case IPCOMP_SUPPORTED: - { - ipcomp_transform_t ipcomp; - u_int16_t cpi; - chunk_t data; - - data = notify->get_notification_data(notify); - cpi = *(u_int16_t*)data.ptr; - ipcomp = (ipcomp_transform_t)(*(data.ptr + 2)); - switch (ipcomp) - { - case IPCOMP_DEFLATE: - this->other_cpi = cpi; - this->ipcomp_received = ipcomp; - break; - case IPCOMP_LZS: - case IPCOMP_LZJH: - default: - DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a " - "transform ID we don't support %N", - ipcomp_transform_names, ipcomp); - break; - } - } - default: - break; - } -} - -/** - * Read payloads from message - */ -static void process_payloads(private_child_create_t *this, message_t *message) -{ - enumerator_t *enumerator; - payload_t *payload; - sa_payload_t *sa_payload; - ke_payload_t *ke_payload; - ts_payload_t *ts_payload; - - /* defaults to TUNNEL mode */ - this->mode = MODE_TUNNEL; - - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) - { - switch (payload->get_type(payload)) - { - case SECURITY_ASSOCIATION: - sa_payload = (sa_payload_t*)payload; - this->proposals = sa_payload->get_proposals(sa_payload); - break; - case KEY_EXCHANGE: - ke_payload = (ke_payload_t*)payload; - if (!this->initiator) - { - this->dh_group = ke_payload->get_dh_group_number(ke_payload); - this->dh = this->keymat->create_dh(this->keymat, this->dh_group); - } - if (this->dh) - { - this->dh->set_other_public_value(this->dh, - ke_payload->get_key_exchange_data(ke_payload)); - } - break; - case TRAFFIC_SELECTOR_INITIATOR: - ts_payload = (ts_payload_t*)payload; - this->tsi = ts_payload->get_traffic_selectors(ts_payload); - break; - case TRAFFIC_SELECTOR_RESPONDER: - ts_payload = (ts_payload_t*)payload; - this->tsr = ts_payload->get_traffic_selectors(ts_payload); - break; - case NOTIFY: - handle_notify(this, (notify_payload_t*)payload); - break; - default: - break; - } - } - enumerator->destroy(enumerator); -} - -/** - * Implementation of task_t.build for initiator - */ -static status_t build_i(private_child_create_t *this, message_t *message) -{ - host_t *me, *other, *vip; - peer_cfg_t *peer_cfg; - - switch (message->get_exchange_type(message)) - { - case IKE_SA_INIT: - return get_nonce(message, &this->my_nonce); - case CREATE_CHILD_SA: - if (generate_nonce(&this->my_nonce) != SUCCESS) - { - message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); - return SUCCESS; - } - if (this->dh_group == MODP_NONE) - { - this->dh_group = this->config->get_dh_group(this->config); - } - break; - case IKE_AUTH: - if (message->get_message_id(message) != 1) - { - /* send only in the first request, not in subsequent rounds */ - return NEED_MORE; - } - break; - default: - break; - } - - if (this->reqid) - { - DBG0(DBG_IKE, "establishing CHILD_SA %s{%d}", - this->config->get_name(this->config), this->reqid); - } - else - { - DBG0(DBG_IKE, "establishing CHILD_SA %s", - this->config->get_name(this->config)); - } - - /* reuse virtual IP if we already have one */ - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (me == NULL) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (other == NULL) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } - - /* check if we want a virtual IP, but don't have one */ - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = peer_cfg->get_virtual_ip(peer_cfg); - if (!this->reqid && vip) - { - /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */ - vip = host_create_any(vip->get_family(vip)); - this->tsi = this->config->get_traffic_selectors(this->config, TRUE, - NULL, vip); - vip->destroy(vip); - } - else - { /* but narrow it for host2host / if we already have a vip */ - this->tsi = this->config->get_traffic_selectors(this->config, TRUE, - NULL, me); - } - this->tsr = this->config->get_traffic_selectors(this->config, FALSE, - NULL, other); - - if (this->packet_tsi) - { - this->tsi->insert_first(this->tsi, - this->packet_tsi->clone(this->packet_tsi)); - } - if (this->packet_tsr) - { - this->tsr->insert_first(this->tsr, - this->packet_tsr->clone(this->packet_tsr)); - } - this->proposals = this->config->get_proposals(this->config, - this->dh_group == MODP_NONE); - this->mode = this->config->get_mode(this->config); - if (this->mode == MODE_TRANSPORT && - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using transport mode, connection NATed"); - } - - this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); - - if (!allocate_spi(this)) - { - DBG1(DBG_IKE, "unable to allocate SPIs from kernel"); - return FAILED; - } - - if (this->dh_group != MODP_NONE) - { - this->dh = this->keymat->create_dh(this->keymat, this->dh_group); - } - - if (this->config->use_ipcomp(this->config)) - { - /* IPCOMP_DEFLATE is the only transform we support at the moment */ - add_ipcomp_notify(this, message, IPCOMP_DEFLATE); - } - - build_payloads(this, message); - - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); - this->tsi = NULL; - this->tsr = NULL; - this->proposals = NULL; - - return NEED_MORE; -} - -/** - * Implementation of task_t.process for responder - */ -static status_t process_r(private_child_create_t *this, message_t *message) -{ - switch (message->get_exchange_type(message)) - { - case IKE_SA_INIT: - return get_nonce(message, &this->other_nonce); - case CREATE_CHILD_SA: - get_nonce(message, &this->other_nonce); - break; - case IKE_AUTH: - if (message->get_message_id(message) != 1) - { - /* only handle first AUTH payload, not additional rounds */ - return NEED_MORE; - } - default: - break; - } - - process_payloads(this, message); - - return NEED_MORE; -} - -/** - * handle CHILD_SA setup failure - */ -static void handle_child_sa_failure(private_child_create_t *this, - message_t *message) -{ - if (message->get_exchange_type(message) == IKE_AUTH && - lib->settings->get_bool(lib->settings, - "charon.close_ike_on_child_failure", FALSE)) - { - /* we delay the delete for 100ms, as the IKE_AUTH response must arrive - * first */ - DBG1(DBG_IKE, "closing IKE_SA due CHILD_SA setup failure"); - charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*) - delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE), - 100); - } -} - -/** - * Implementation of task_t.build for responder - */ -static status_t build_r(private_child_create_t *this, message_t *message) -{ - peer_cfg_t *peer_cfg; - payload_t *payload; - enumerator_t *enumerator; - bool no_dh = TRUE; - - switch (message->get_exchange_type(message)) - { - case IKE_SA_INIT: - return get_nonce(message, &this->my_nonce); - case CREATE_CHILD_SA: - if (generate_nonce(&this->my_nonce) != SUCCESS) - { - message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, - chunk_empty); - return SUCCESS; - } - no_dh = FALSE; - break; - case IKE_AUTH: - if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) - { /* wait until all authentication round completed */ - return NEED_MORE; - } - default: - break; - } - - if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) - { - DBG1(DBG_IKE, "unable to create CHILD_SA while rekeying IKE_SA"); - message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty); - return SUCCESS; - } - - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (peer_cfg && this->tsi && this->tsr) - { - host_t *me, *other; - - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (me == NULL) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (other == NULL) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } - this->config = peer_cfg->select_child_cfg(peer_cfg, this->tsr, - this->tsi, me, other); - } - - if (this->config == NULL) - { - DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable", - this->tsr, this->tsi); - message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); - handle_child_sa_failure(this, message); - return SUCCESS; - } - - /* check if ike_config_t included non-critical error notifies */ - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) - { - if (payload->get_type(payload) == NOTIFY) - { - notify_payload_t *notify = (notify_payload_t*)payload; - - switch (notify->get_notify_type(notify)) - { - case INTERNAL_ADDRESS_FAILURE: - case FAILED_CP_REQUIRED: - { - DBG1(DBG_IKE,"configuration payload negotation " - "failed, no CHILD_SA built"); - enumerator->destroy(enumerator); - handle_child_sa_failure(this, message); - return SUCCESS; - } - default: - break; - } - } - } - enumerator->destroy(enumerator); - - this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); - - if (this->ipcomp_received != IPCOMP_NONE) - { - if (this->config->use_ipcomp(this->config)) - { - add_ipcomp_notify(this, message, this->ipcomp_received); - } - else - { - DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring", - notify_type_names, IPCOMP_SUPPORTED); - } - } - - switch (select_and_install(this, no_dh)) - { - case SUCCESS: - break; - case NOT_FOUND: - message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); - handle_child_sa_failure(this, message); - return SUCCESS; - case INVALID_ARG: - { - u_int16_t group = htons(this->dh_group); - message->add_notify(message, FALSE, INVALID_KE_PAYLOAD, - chunk_from_thing(group)); - handle_child_sa_failure(this, message); - return SUCCESS; - } - case FAILED: - default: - message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty); - handle_child_sa_failure(this, message); - return SUCCESS; - } - - build_payloads(this, message); - - DBG0(DBG_IKE, "CHILD_SA %s{%d} established " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", - this->child_sa->get_name(this->child_sa), - this->child_sa->get_reqid(this->child_sa), - ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), - ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), - this->child_sa->get_traffic_selectors(this->child_sa, TRUE), - this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); - - if (!this->rekey) - { /* invoke the child_up() hook if we are not rekeying */ - charon->bus->child_updown(charon->bus, this->child_sa, TRUE); - } - return SUCCESS; -} - -/** - * Implementation of task_t.process for initiator - */ -static status_t process_i(private_child_create_t *this, message_t *message) -{ - enumerator_t *enumerator; - payload_t *payload; - bool no_dh = TRUE; - - switch (message->get_exchange_type(message)) - { - case IKE_SA_INIT: - return get_nonce(message, &this->other_nonce); - case CREATE_CHILD_SA: - get_nonce(message, &this->other_nonce); - no_dh = FALSE; - break; - case IKE_AUTH: - if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) - { /* wait until all authentication round completed */ - return NEED_MORE; - } - default: - break; - } - - /* check for erronous notifies */ - enumerator = message->create_payload_enumerator(message); - while (enumerator->enumerate(enumerator, &payload)) - { - if (payload->get_type(payload) == NOTIFY) - { - notify_payload_t *notify = (notify_payload_t*)payload; - notify_type_t type = notify->get_notify_type(notify); - - switch (type) - { - /* handle notify errors related to CHILD_SA only */ - case NO_PROPOSAL_CHOSEN: - case SINGLE_PAIR_REQUIRED: - case NO_ADDITIONAL_SAS: - case INTERNAL_ADDRESS_FAILURE: - case FAILED_CP_REQUIRED: - case TS_UNACCEPTABLE: - case INVALID_SELECTORS: - { - DBG1(DBG_IKE, "received %N notify, no CHILD_SA built", - notify_type_names, type); - enumerator->destroy(enumerator); - handle_child_sa_failure(this, message); - /* an error in CHILD_SA creation is not critical */ - return SUCCESS; - } - case INVALID_KE_PAYLOAD: - { - chunk_t data; - u_int16_t group = MODP_NONE; - - data = notify->get_notification_data(notify); - if (data.len == sizeof(group)) - { - memcpy(&group, data.ptr, data.len); - group = ntohs(group); - } - DBG1(DBG_IKE, "peer didn't accept DH group %N, " - "it requested %N", diffie_hellman_group_names, - this->dh_group, diffie_hellman_group_names, group); - this->dh_group = group; - this->public.task.migrate(&this->public.task, this->ike_sa); - enumerator->destroy(enumerator); - return NEED_MORE; - } - default: - break; - } - } - } - enumerator->destroy(enumerator); - - process_payloads(this, message); - - if (this->ipcomp == IPCOMP_NONE && this->ipcomp_received != IPCOMP_NONE) - { - DBG1(DBG_IKE, "received an IPCOMP_SUPPORTED notify without requesting" - " one, no CHILD_SA built"); - handle_child_sa_failure(this, message); - return SUCCESS; - } - else if (this->ipcomp != IPCOMP_NONE && this->ipcomp_received == IPCOMP_NONE) - { - DBG1(DBG_IKE, "peer didn't accept our proposed IPComp transforms, " - "IPComp is disabled"); - this->ipcomp = IPCOMP_NONE; - } - else if (this->ipcomp != IPCOMP_NONE && this->ipcomp != this->ipcomp_received) - { - DBG1(DBG_IKE, "received an IPCOMP_SUPPORTED notify we didn't propose, " - "no CHILD_SA built"); - handle_child_sa_failure(this, message); - return SUCCESS; - } - - if (select_and_install(this, no_dh) == SUCCESS) - { - DBG0(DBG_IKE, "CHILD_SA %s{%d} established " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", - this->child_sa->get_name(this->child_sa), - this->child_sa->get_reqid(this->child_sa), - ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), - ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), - this->child_sa->get_traffic_selectors(this->child_sa, TRUE), - this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); - - if (!this->rekey) - { /* invoke the child_up() hook if we are not rekeying */ - charon->bus->child_updown(charon->bus, this->child_sa, TRUE); - } - } - else - { - handle_child_sa_failure(this, message); - } - return SUCCESS; -} - -/** - * Implementation of task_t.get_type - */ -static task_type_t get_type(private_child_create_t *this) -{ - return CHILD_CREATE; -} - -/** - * Implementation of child_create_t.use_reqid - */ -static void use_reqid(private_child_create_t *this, u_int32_t reqid) -{ - this->reqid = reqid; -} - -/** - * Implementation of child_create_t.get_child - */ -static child_sa_t* get_child(private_child_create_t *this) -{ - return this->child_sa; -} - -/** - * Implementation of child_create_t.get_lower_nonce - */ -static chunk_t get_lower_nonce(private_child_create_t *this) -{ - if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr, - min(this->my_nonce.len, this->other_nonce.len)) < 0) - { - return this->my_nonce; - } - else - { - return this->other_nonce; - } -} - -/** - * Implementation of task_t.migrate - */ -static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) -{ - chunk_free(&this->my_nonce); - chunk_free(&this->other_nonce); - if (this->tsi) - { - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - } - if (this->tsr) - { - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - } - DESTROY_IF(this->child_sa); - DESTROY_IF(this->proposal); - DESTROY_IF(this->dh); - if (this->proposals) - { - this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); - } - - this->ike_sa = ike_sa; - this->keymat = ike_sa->get_keymat(ike_sa); - this->proposal = NULL; - this->proposals = NULL; - this->tsi = NULL; - this->tsr = NULL; - this->dh = NULL; - this->child_sa = NULL; - this->mode = MODE_TUNNEL; - this->ipcomp = IPCOMP_NONE; - this->ipcomp_received = IPCOMP_NONE; - this->other_cpi = 0; - this->reqid = 0; - this->established = FALSE; -} - -/** - * Implementation of task_t.destroy - */ -static void destroy(private_child_create_t *this) -{ - chunk_free(&this->my_nonce); - chunk_free(&this->other_nonce); - if (this->tsr) - { - this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy)); - } - if (this->tsi) - { - this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); - } - if (!this->established) - { - DESTROY_IF(this->child_sa); - } - DESTROY_IF(this->packet_tsi); - DESTROY_IF(this->packet_tsr); - DESTROY_IF(this->proposal); - DESTROY_IF(this->dh); - if (this->proposals) - { - this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); - } - - DESTROY_IF(this->config); - free(this); -} - -/* - * Described in header. - */ -child_create_t *child_create_create(ike_sa_t *ike_sa, - child_cfg_t *config, bool rekey, - traffic_selector_t *tsi, traffic_selector_t *tsr) -{ - private_child_create_t *this = malloc_thing(private_child_create_t); - - this->public.get_child = (child_sa_t*(*)(child_create_t*))get_child; - this->public.get_lower_nonce = (chunk_t(*)(child_create_t*))get_lower_nonce; - this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid; - this->public.task.get_type = (task_type_t(*)(task_t*))get_type; - this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; - this->public.task.destroy = (void(*)(task_t*))destroy; - if (config) - { - this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; - this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; - this->initiator = TRUE; - config->get_ref(config); - } - else - { - this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; - this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; - this->initiator = FALSE; - } - - this->ike_sa = ike_sa; - this->config = config; - this->my_nonce = chunk_empty; - this->other_nonce = chunk_empty; - this->proposals = NULL; - this->proposal = NULL; - this->tsi = NULL; - this->tsr = NULL; - this->packet_tsi = tsi ? tsi->clone(tsi) : NULL; - this->packet_tsr = tsr ? tsr->clone(tsr) : NULL; - this->dh = NULL; - this->dh_group = MODP_NONE; - this->keymat = ike_sa->get_keymat(ike_sa); - this->child_sa = NULL; - this->mode = MODE_TUNNEL; - this->ipcomp = IPCOMP_NONE; - this->ipcomp_received = IPCOMP_NONE; - this->my_spi = 0; - this->other_spi = 0; - this->my_cpi = 0; - this->other_cpi = 0; - this->reqid = 0; - this->established = FALSE; - this->rekey = rekey; - - return &this->public; -} |