From 5351d63c79850ba19eb81b3d26b9679826257179 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Nov 2011 15:24:24 +0100 Subject: [PATCH] Install negotiated IKEv1 CHILD_SA negotiated in quick mode --- src/libcharon/sa/tasks/quick_mode.c | 163 ++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/src/libcharon/sa/tasks/quick_mode.c b/src/libcharon/sa/tasks/quick_mode.c index aa6e4e6f1..987b8d5f4 100644 --- a/src/libcharon/sa/tasks/quick_mode.c +++ b/src/libcharon/sa/tasks/quick_mode.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,11 @@ struct private_quick_mode_t { */ ike_sa_t *ike_sa; + /** + * TRUE if we are initiating quick mode + */ + bool initiator; + /** * Traffic selector of initiator */ @@ -59,6 +65,16 @@ struct private_quick_mode_t { */ chunk_t nonce_r; + /** + * Initiators ESP SPI + */ + u_int32_t spi_i; + + /** + * Responder ESP SPI + */ + u_int32_t spi_r; + /** * selected CHILD_SA proposal */ @@ -74,6 +90,11 @@ struct private_quick_mode_t { */ child_sa_t *child_sa; + /** + * IKEv1 keymat + */ + keymat_v1_t *keymat; + /** states of quick mode */ enum { QM_INIT, @@ -81,6 +102,100 @@ struct private_quick_mode_t { } state; }; +/** + * Install negotiated CHILD_SA + */ +static bool install(private_quick_mode_t *this) +{ + status_t status, status_i, status_o; + chunk_t encr_i, encr_r, integ_i, integ_r; + linked_list_t *tsi, *tsr; + + this->child_sa->set_proposal(this->child_sa, this->proposal); + this->child_sa->set_state(this->child_sa, CHILD_INSTALLING); + this->child_sa->set_mode(this->child_sa, MODE_TUNNEL); + this->child_sa->set_protocol(this->child_sa, + this->proposal->get_protocol(this->proposal)); + + status_i = status_o = FAILED; + encr_i = encr_r = integ_i = integ_r = chunk_empty; + tsi = linked_list_create(); + tsr = linked_list_create(); + tsi->insert_last(tsi, this->tsi); + tsr->insert_last(tsr, this->tsr); + if (this->keymat->derive_child_keys(this->keymat, this->proposal, NULL, + this->nonce_i, this->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->spi_i, 0, TRUE, FALSE, tsi, tsr); + status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->spi_r, 0, FALSE, FALSE, tsi, tsr); + } + else + { + status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->spi_r, 0, TRUE, FALSE, tsr, tsi); + status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, + this->spi_i, 0, FALSE, FALSE, tsr, tsi); + } + } + chunk_clear(&integ_i); + chunk_clear(&integ_r); + chunk_clear(&encr_i); + chunk_clear(&encr_r); + + if (status_i != SUCCESS || status_o != SUCCESS) + { + 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 " : ""); + tsi->destroy(tsi); + tsr->destroy(tsr); + return FALSE; + } + + if (this->initiator) + { + status = this->child_sa->add_policies(this->child_sa, tsi, tsr); + } + else + { + status = this->child_sa->add_policies(this->child_sa, tsr, tsi); + } + tsi->destroy(tsi); + tsr->destroy(tsr); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); + return FALSE; + } + + charon->bus->child_keys(charon->bus, this->child_sa, this->initiator, + NULL, this->nonce_i, this->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); + + 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)); + + charon->bus->child_updown(charon->bus, this->child_sa, TRUE); + + this->child_sa = NULL; + + return TRUE; +} + METHOD(task_t, build_i, status_t, private_quick_mode_t *this, message_t *message) { @@ -88,14 +203,35 @@ METHOD(task_t, build_i, status_t, { case QM_INIT: { + enumerator_t *enumerator; sa_payload_t *sa_payload; nonce_payload_t *nonce_payload; id_payload_t *id_payload; traffic_selector_t *ts; linked_list_t *list; + proposal_t *proposal; rng_t *rng; + 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, 0, FALSE); + list = this->config->get_proposals(this->config, TRUE); + + this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); + if (!this->spi_i) + { + DBG1(DBG_IKE, "allocating SPI from kernel failed"); + return FAILED; + } + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &proposal)) + { + proposal->set_spi(proposal, this->spi_i); + } + enumerator->destroy(enumerator); + sa_payload = sa_payload_create_from_proposal_list( SECURITY_ASSOCIATION_V1, list); list->destroy_offset(list, offsetof(proposal_t, destroy)); @@ -234,6 +370,7 @@ METHOD(task_t, process_r, status_t, DBG1(DBG_IKE, "no matching proposal found"); return FAILED; } + this->spi_i = this->proposal->get_spi(this->proposal); nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1); @@ -246,12 +383,21 @@ METHOD(task_t, process_r, status_t, /* TODO-IKEv1: verify HASH(1) */ + 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, 0, FALSE); return NEED_MORE; } case QM_NEGOTIATED: { /* TODO-IKEv1: verify HASH(3) */ + if (!install(this)) + { + return FAILED; + } + return SUCCESS; } default: @@ -271,6 +417,14 @@ METHOD(task_t, build_r, status_t, id_payload_t *id_payload; rng_t *rng; + this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); + if (!this->spi_r) + { + DBG1(DBG_IKE, "allocating SPI from kernel failed"); + return FAILED; + } + this->proposal->set_spi(this->proposal, this->spi_r); + sa_payload = sa_payload_create_from_proposal( SECURITY_ASSOCIATION_V1, this->proposal); message->add_payload(message, &sa_payload->payload_interface); @@ -370,6 +524,8 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "no matching proposal found"); return FAILED; } + this->spi_r = this->proposal->get_spi(this->proposal); + nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1); if (!nonce_payload) @@ -381,6 +537,11 @@ METHOD(task_t, process_i, status_t, /* TODO-IKEv1: verify HASH(2) */ + if (!install(this)) + { + return FAILED; + } + this->state = QM_NEGOTIATED; return NEED_MORE; } @@ -431,7 +592,9 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, }, }, .ike_sa = ike_sa, + .initiator = config != NULL, .config = config, + .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa), .state = QM_INIT, );