From e3311e9b87b7f393cceb4e4f898e55061b6ab054 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 19 Jul 2013 15:58:15 +0200 Subject: [PATCH] ikev1: implement mode config push mode --- src/libcharon/sa/ikev1/task_manager_v1.c | 34 ++- .../sa/ikev1/tasks/aggressive_mode.c | 67 ++++- src/libcharon/sa/ikev1/tasks/main_mode.c | 66 ++++- src/libcharon/sa/ikev1/tasks/mode_config.c | 269 +++++++++++++++--- src/libcharon/sa/ikev1/tasks/mode_config.h | 3 +- 5 files changed, 363 insertions(+), 76 deletions(-) diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index dfceb54468..d97ef0ebe9 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -539,23 +539,40 @@ static bool mode_config_expected(private_task_manager_t *this) enumerator_t *enumerator; peer_cfg_t *peer_cfg; char *pool; + bool local; host_t *host; peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (peer_cfg) { - enumerator = peer_cfg->create_pool_enumerator(peer_cfg); - if (!enumerator->enumerate(enumerator, &pool)) - { /* no pool configured */ + if (peer_cfg->use_pull_mode(peer_cfg)) + { + enumerator = peer_cfg->create_pool_enumerator(peer_cfg); + if (!enumerator->enumerate(enumerator, &pool)) + { /* no pool configured */ + enumerator->destroy(enumerator); + return FALSE; + } enumerator->destroy(enumerator); - return FALSE; + + local = FALSE; } - enumerator->destroy(enumerator); + else + { + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + if (!enumerator->enumerate(enumerator, &host)) + { /* not requesting a vip */ + enumerator->destroy(enumerator); + return FALSE; + } + enumerator->destroy(enumerator); + local = TRUE; + } enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, - FALSE); + local); if (!enumerator->enumerate(enumerator, &host)) - { /* have a pool, but no VIP assigned yet */ + { /* expecting a VIP exchange, but no VIP assigned yet */ enumerator->destroy(enumerator); return TRUE; } @@ -1087,7 +1104,8 @@ static status_t process_request(private_task_manager_t *this, case TRANSACTION: if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) { - task = (task_t *)mode_config_create(this->ike_sa, FALSE); + task = (task_t *)mode_config_create(this->ike_sa, + FALSE, TRUE); } else { diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 6b00706bf4..46cbb879ba 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -196,6 +196,17 @@ static status_t send_delete(private_aggressive_mode_t *this) return ALREADY_DONE; } +/** + * Schedule a timeout for the IKE_SA should it not establish + */ +static void schedule_timeout(ike_sa_t *ike_sa) +{ + job_t *job; + + job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE); + lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); +} + METHOD(task_t, build_i, status_t, private_aggressive_mode_t *this, message_t *message) { @@ -300,20 +311,15 @@ METHOD(task_t, build_i, status_t, case AUTH_XAUTH_INIT_PSK: case AUTH_XAUTH_INIT_RSA: case AUTH_HYBRID_INIT_RSA: - { /* wait for XAUTH request, since this may never come, - * we queue a timeout */ - job_t *job = (job_t*)delete_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - lib->scheduler->schedule_job(lib->scheduler, job, - HALF_OPEN_IKE_SA_TIMEOUT); + /* wait for XAUTH request */ + schedule_timeout(this->ike_sa); break; - } case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; default: if (charon->ike_sa_manager->check_uniqueness( charon->ike_sa_manager, this->ike_sa, FALSE)) @@ -328,10 +334,30 @@ METHOD(task_t, build_i, status_t, } break; } + /* check for and prepare mode config push/pull */ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + else + { + schedule_timeout(this->ike_sa); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) + { + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + schedule_timeout(this->ike_sa); + } + else + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } @@ -482,7 +508,7 @@ METHOD(task_t, process_r, status_t, case AUTH_HYBRID_INIT_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: @@ -505,11 +531,22 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && - this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) + /* check for and prepare mode config push/pull */ + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) + { + if (!this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 441bd7a78b..81638169a6 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -504,7 +504,7 @@ METHOD(task_t, build_r, status_t, case AUTH_HYBRID_INIT_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: @@ -527,11 +527,21 @@ METHOD(task_t, build_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && - this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) + { + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (!this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } @@ -540,6 +550,17 @@ METHOD(task_t, build_r, status_t, } } +/** + * Schedule a timeout for the IKE_SA should it not establish + */ +static void schedule_timeout(ike_sa_t *ike_sa) +{ + job_t *job; + + job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE); + lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); +} + METHOD(task_t, process_i, status_t, private_main_mode_t *this, message_t *message) { @@ -639,20 +660,15 @@ METHOD(task_t, process_i, status_t, case AUTH_XAUTH_INIT_PSK: case AUTH_XAUTH_INIT_RSA: case AUTH_HYBRID_INIT_RSA: - { /* wait for XAUTH request, since this may never come, - * we queue a timeout */ - job_t *job = (job_t*)delete_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - lib->scheduler->schedule_job(lib->scheduler, job, - HALF_OPEN_IKE_SA_TIMEOUT); + /* wait for XAUTH request */ + schedule_timeout(this->ike_sa); break; - } case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; default: if (charon->ike_sa_manager->check_uniqueness( charon->ike_sa_manager, this->ike_sa, FALSE)) @@ -667,10 +683,30 @@ METHOD(task_t, process_i, status_t, } break; } + /* check for and prepare mode config push/pull */ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + else + { + schedule_timeout(this->ike_sa); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) + { + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + schedule_timeout(this->ike_sa); + } + else + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index ce897727a3..17fe02538d 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -41,15 +41,20 @@ struct private_mode_config_t { */ bool initiator; + /** + * Use pull (CFG_REQUEST/RESPONSE) or push (CFG_SET/ACK)? + */ + bool pull; + /** * Received list of virtual IPs, host_t* */ linked_list_t *vips; /** - * list of attributes requested and its handler, entry_t + * Requested/received list of attributes, entry_t */ - linked_list_t *requested; + linked_list_t *attributes; /** * Identifier to include in response @@ -58,12 +63,12 @@ struct private_mode_config_t { }; /** - * Entry for a requested attribute and the requesting handler + * Entry for a attribute and associated handler */ typedef struct { - /** attribute requested */ + /** attribute type */ configuration_attribute_type_t type; - /** handler requesting this attribute */ + /** handler for this attribute */ attribute_handler_t *handler; } entry_t; @@ -117,13 +122,13 @@ static void handle_attribute(private_mode_config_t *this, entry_t *entry; /* find the handler which requested this attribute */ - enumerator = this->requested->create_enumerator(this->requested); + enumerator = this->attributes->create_enumerator(this->attributes); while (enumerator->enumerate(enumerator, &entry)) { if (entry->type == ca->get_type(ca)) { handler = entry->handler; - this->requested->remove_at(this->requested, enumerator); + this->attributes->remove_at(this->attributes, enumerator); free(entry); break; } @@ -180,7 +185,7 @@ static void process_attribute(private_mode_config_t *this, } default: { - if (this->initiator) + if (this->initiator == this->pull) { handle_attribute(this, ca); } @@ -188,6 +193,24 @@ static void process_attribute(private_mode_config_t *this, } } +/** + * Check if config allows push mode when acting as task responder + */ +static bool accept_push(private_mode_config_t *this) +{ + enumerator_t *enumerator; + peer_cfg_t *config; + bool vip; + host_t *host; + + config = this->ike_sa->get_peer_cfg(this->ike_sa); + enumerator = config->create_virtual_ip_enumerator(config); + vip = enumerator->enumerate(enumerator, &host); + enumerator->destroy(enumerator); + + return vip && !config->use_pull_mode(config); +} + /** * Scan for configuration payloads and attributes */ @@ -206,6 +229,15 @@ static void process_payloads(private_mode_config_t *this, message_t *message) switch (cp->get_type(cp)) { + case CFG_SET: + /* when acting as a responder, we detect the mode using + * the type of configuration payload. But we should double + * check the peer is allowed to use push mode on us. */ + if (!this->initiator && accept_push(this)) + { + this->pull = FALSE; + } + /* FALL */ case CFG_REQUEST: this->identifier = cp->get_identifier(cp); /* FALL */ @@ -219,6 +251,8 @@ static void process_payloads(private_mode_config_t *this, message_t *message) } attributes->destroy(attributes); break; + case CFG_ACK: + break; default: DBG1(DBG_IKE, "ignoring %N config payload", config_type_names, cp->get_type(cp)); @@ -229,8 +263,29 @@ static void process_payloads(private_mode_config_t *this, message_t *message) enumerator->destroy(enumerator); } -METHOD(task_t, build_i, status_t, - private_mode_config_t *this, message_t *message) +/** + * Add an attribute to a configuration payload, and store it in task + */ +static void add_attribute(private_mode_config_t *this, cp_payload_t *cp, + configuration_attribute_type_t type, chunk_t data, + attribute_handler_t *handler) +{ + entry_t *entry; + + cp->add_attribute(cp, + configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, + type, data)); + INIT(entry, + .type = type, + .handler = handler, + ); + this->attributes->insert_last(this->attributes, entry); +} + +/** + * Build a CFG_REQUEST as initiator + */ +static status_t build_request(private_mode_config_t *this, message_t *message) { cp_payload_t *cp; enumerator_t *enumerator; @@ -279,18 +334,7 @@ METHOD(task_t, build_i, status_t, this->ike_sa->get_other_id(this->ike_sa), vips); while (enumerator->enumerate(enumerator, &handler, &type, &data)) { - entry_t *entry; - - DBG2(DBG_IKE, "building %N attribute", - configuration_attribute_type_names, type); - cp->add_attribute(cp, - configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, - type, data)); - INIT(entry, - .type = type, - .handler = handler, - ); - this->requested->insert_last(this->requested, entry); + add_attribute(this, cp, type, data, handler); } enumerator->destroy(enumerator); @@ -301,15 +345,121 @@ METHOD(task_t, build_i, status_t, return NEED_MORE; } +/** + * Build a CFG_SET as initiator + */ +static status_t build_set(private_mode_config_t *this, message_t *message) +{ + enumerator_t *enumerator; + configuration_attribute_type_t type; + chunk_t value; + cp_payload_t *cp; + peer_cfg_t *config; + identification_t *id; + linked_list_t *pools; + host_t *any4, *any6, *found; + char *name; + + cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET); + + id = this->ike_sa->get_other_eap_id(this->ike_sa); + config = this->ike_sa->get_peer_cfg(this->ike_sa); + any4 = host_create_any(AF_INET); + any6 = host_create_any(AF_INET6); + + this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE); + + /* in push mode, we ask each configured pool for an address */ + enumerator = config->create_pool_enumerator(config); + while (enumerator->enumerate(enumerator, &name)) + { + pools = linked_list_create_with_items(name, NULL); + /* try IPv4, then IPv6 */ + found = hydra->attributes->acquire_address(hydra->attributes, + pools, id, any4); + if (!found) + { + found = hydra->attributes->acquire_address(hydra->attributes, + pools, id, any6); + } + pools->destroy(pools); + if (found) + { + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id); + this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found); + cp->add_attribute(cp, build_vip(found)); + this->vips->insert_last(this->vips, found); + } + } + enumerator->destroy(enumerator); + + any4->destroy(any4); + any6->destroy(any6); + + /* query registered providers for additional attributes to include */ + pools = linked_list_create_from_enumerator( + config->create_pool_enumerator(config)); + enumerator = hydra->attributes->create_responder_enumerator( + hydra->attributes, pools, id, this->vips); + while (enumerator->enumerate(enumerator, &type, &value)) + { + add_attribute(this, cp, type, value, NULL); + } + enumerator->destroy(enumerator); + pools->destroy(pools); + + message->add_payload(message, (payload_t*)cp); + + return SUCCESS; +} + +METHOD(task_t, build_i, status_t, + private_mode_config_t *this, message_t *message) +{ + if (this->pull) + { + return build_request(this, message); + } + return build_set(this, message); +} + +/** + * Store received virtual IPs to the IKE_SA, install them + */ +static void install_vips(private_mode_config_t *this) +{ + enumerator_t *enumerator; + host_t *host; + + this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE); + + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &host)) + { + if (!host->is_anyaddr(host)) + { + this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + } + } + enumerator->destroy(enumerator); +} + METHOD(task_t, process_r, status_t, private_mode_config_t *this, message_t *message) { process_payloads(this, message); + + if (!this->pull) + { + install_vips(this); + } return NEED_MORE; } -METHOD(task_t, build_r, status_t, - private_mode_config_t *this, message_t *message) +/** + * Build CFG_REPLY message after receiving CFG_REQUEST + */ +static status_t build_reply(private_mode_config_t *this, message_t *message) { enumerator_t *enumerator; configuration_attribute_type_t type; @@ -360,8 +510,6 @@ METHOD(task_t, build_r, status_t, hydra->attributes, pools, id, vips); while (enumerator->enumerate(enumerator, &type, &value)) { - DBG2(DBG_IKE, "building %N attribute", - configuration_attribute_type_names, type); cp->add_attribute(cp, configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, type, value)); @@ -376,26 +524,72 @@ METHOD(task_t, build_r, status_t, return SUCCESS; } -METHOD(task_t, process_i, status_t, - private_mode_config_t *this, message_t *message) +/** + * Build CFG_ACK for a received CFG_SET + */ +static status_t build_ack(private_mode_config_t *this, message_t *message) { + cp_payload_t *cp; enumerator_t *enumerator; host_t *host; + configuration_attribute_type_t type; + entry_t *entry; - process_payloads(this, message); + cp = cp_payload_create_type(CONFIGURATION_V1, CFG_ACK); - this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE); + /* return empty attributes for installed IPs */ enumerator = this->vips->create_enumerator(this->vips); while (enumerator->enumerate(enumerator, &host)) { - if (!host->is_anyaddr(host)) + type = INTERNAL_IP6_ADDRESS; + if (host->get_family(host) == AF_INET6) { - this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + type = INTERNAL_IP6_ADDRESS; } + else + { + type = INTERNAL_IP4_ADDRESS; + } + cp->add_attribute(cp, configuration_attribute_create_chunk( + CONFIGURATION_ATTRIBUTE_V1, type, chunk_empty)); } enumerator->destroy(enumerator); + enumerator = this->attributes->create_enumerator(this->attributes); + while (enumerator->enumerate(enumerator, &entry)) + { + cp->add_attribute(cp, + configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, + entry->type, chunk_empty)); + } + enumerator->destroy(enumerator); + + cp->set_identifier(cp, this->identifier); + message->add_payload(message, (payload_t*)cp); + + return SUCCESS; +} + +METHOD(task_t, build_r, status_t, + private_mode_config_t *this, message_t *message) +{ + if (this->pull) + { + return build_reply(this, message); + } + return build_ack(this, message); +} + +METHOD(task_t, process_i, status_t, + private_mode_config_t *this, message_t *message) +{ + process_payloads(this, message); + + if (this->pull) + { + install_vips(this); + } return SUCCESS; } @@ -411,22 +605,22 @@ METHOD(task_t, migrate, void, this->ike_sa = ike_sa; this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->vips = linked_list_create(); - this->requested->destroy_function(this->requested, free); - this->requested = linked_list_create(); + this->attributes->destroy_function(this->attributes, free); + this->attributes = linked_list_create(); } METHOD(task_t, destroy, void, private_mode_config_t *this) { this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); - this->requested->destroy_function(this->requested, free); + this->attributes->destroy_function(this->attributes, free); free(this); } /* * Described in header. */ -mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator) +mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull) { private_mode_config_t *this; @@ -439,8 +633,9 @@ mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator) }, }, .initiator = initiator, + .pull = initiator ? pull : TRUE, .ike_sa = ike_sa, - .requested = linked_list_create(), + .attributes = linked_list_create(), .vips = linked_list_create(), ); diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.h b/src/libcharon/sa/ikev1/tasks/mode_config.h index 462bee3743..c2da7a086b 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.h +++ b/src/libcharon/sa/ikev1/tasks/mode_config.h @@ -43,8 +43,9 @@ struct mode_config_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE for initiator + * @param pull TRUE to pull, FALSE to push (applies if initiator only) * @return mode_config task to handle by the task_manager */ -mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator); +mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull); #endif /** MODE_CONFIG_H_ @}*/ -- 2.47.2