From: Clavister OpenSource Date: Tue, 29 Nov 2011 10:04:36 +0000 (+0100) Subject: IKEv1 XAuth + CfgMode: Added ability to process CfgMode messages in the xauth task... X-Git-Tag: 5.0.0~338^2~9^2~347 X-Git-Url: https://git.strongswan.org/?p=strongswan.git;a=commitdiff_plain;h=c961d110abbb29df9898d97f2f3d5c849e1f96bb;ds=inline IKEv1 XAuth + CfgMode: Added ability to process CfgMode messages in the xauth task. Migrated away from using the MIGRATE method to switch queues. --- diff --git a/src/libcharon/sa/tasks/xauth_request.c b/src/libcharon/sa/tasks/xauth_request.c index 0a35889..1c9f115 100644 --- a/src/libcharon/sa/tasks/xauth_request.c +++ b/src/libcharon/sa/tasks/xauth_request.c @@ -43,27 +43,53 @@ struct private_xauth_request_t { linked_list_t *requested; /** - * The user name + * The current and next state of the task */ - chunk_t user_name; + enum { + TASK_XAUTH_INIT, + TASK_XAUTH_PASS_VERIFY, + TASK_XAUTH_COMPLETE, + } state, next_state; /** - * The user pass + * The status of the XAuth request */ - chunk_t user_pass; + status_t status; /** - * The current state of the task + * The current auth config */ - enum { - TASK_XAUTH_INIT, - TASK_XAUTH_PASS_DONE, - } state; + auth_cfg_t *auth_cfg; /** - * The status of the XAuth request + * The received XAuth Status */ - status_t status; + u_int16_t xauth_status_data; + + /** + * The received XAuth user name + */ + chunk_t xauth_user_name; + + /** + * The received XAuth user pass + */ + chunk_t xauth_user_pass; + + /** + * Whether the user name attribute was received + */ + bool xauth_user_name_recv; + + /** + * Whether the user pass attribute was received + */ + bool xauth_user_pass_recv; + + /** + * Whether the XAuth status attribute was received + */ + bool xauth_status_recv; }; /** @@ -76,124 +102,560 @@ typedef struct { attribute_handler_t *handler; } entry_t; +/** + * Get the first authentcation config from peer config + */ +static auth_cfg_t *get_auth_cfg(private_xauth_request_t *this, bool local) +{ + enumerator_t *enumerator; + auth_cfg_t *cfg = NULL; + peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + + enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, + local); + enumerator->enumerate(enumerator, &cfg); + enumerator->destroy(enumerator); + return cfg; +} + +/** + * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip + */ +static configuration_attribute_t *build_vip(host_t *vip) +{ + configuration_attribute_type_t type; + chunk_t chunk, prefix; + + if (vip->get_family(vip) == AF_INET) + { + type = INTERNAL_IP4_ADDRESS; + if (vip->is_anyaddr(vip)) + { + chunk = chunk_empty; + } + else + { + chunk = vip->get_address(vip); + } + } + else + { + type = INTERNAL_IP6_ADDRESS; + if (vip->is_anyaddr(vip)) + { + chunk = chunk_empty; + } + else + { + prefix = chunk_alloca(1); + *prefix.ptr = 64; + chunk = vip->get_address(vip); + chunk = chunk_cata("cc", chunk, prefix); + } + } + return configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE, + type, chunk); +} + +/** + * Handle a received attribute as initiator + */ +static void handle_attribute(private_xauth_request_t *this, + configuration_attribute_t *ca) +{ + attribute_handler_t *handler = NULL; + enumerator_t *enumerator; + entry_t *entry; + + /* find the handler which requested this attribute */ + enumerator = this->requested->create_enumerator(this->requested); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->type == ca->get_type(ca)) + { + handler = entry->handler; + this->requested->remove_at(this->requested, enumerator); + free(entry); + break; + } + } + enumerator->destroy(enumerator); + + /* and pass it to the handle function */ + handler = hydra->attributes->handle(hydra->attributes, + this->ike_sa->get_other_id(this->ike_sa), handler, + ca->get_type(ca), ca->get_chunk(ca)); + if (handler) + { + this->ike_sa->add_configuration_attribute(this->ike_sa, + handler, ca->get_type(ca), ca->get_chunk(ca)); + } +} + +/** + * process a single configuration attribute + */ +static void process_attribute(private_xauth_request_t *this, + configuration_attribute_t *ca) +{ + host_t *ip; + chunk_t addr; + int family = AF_INET6; + + switch (ca->get_type(ca)) + { + case XAUTH_USER_NAME: + this->xauth_user_name = ca->get_chunk(ca); + this->xauth_user_name_recv = TRUE; + break; + case XAUTH_USER_PASSWORD: + this->xauth_user_pass = ca->get_chunk(ca); + this->xauth_user_pass_recv = TRUE; + break; + case XAUTH_STATUS: + this->xauth_status_data = ca->get_value(ca); + this->xauth_status_recv = TRUE; + break; + case INTERNAL_IP4_ADDRESS: + family = AF_INET; + /* fall */ + case INTERNAL_IP6_ADDRESS: + { + addr = ca->get_chunk(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + /* skip prefix byte in IPv6 payload*/ + if (family == AF_INET6) + { + addr.len--; + } + ip = host_create_from_chunk(family, addr, 0); + } + if (ip) + { + DESTROY_IF(this->virtual_ip); + this->virtual_ip = ip; + } + break; + } + case INTERNAL_IP4_SERVER: + case INTERNAL_IP6_SERVER: + /* assume it's a Windows client if we see proprietary attributes */ + this->ike_sa->enable_extension(this->ike_sa, EXT_MS_WINDOWS); + /* fall */ + default: + { + if (this->initiator) + { + handle_attribute(this, ca); + } + } + } +} + +/** + * Scan for configuration payloads and attributes + */ +static status_t process_payloads(private_xauth_request_t *this, message_t *message) +{ + enumerator_t *enumerator, *attributes; + payload_t *payload; + + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + switch(payload->get_type(payload)) + { + case CONFIGURATION: + case CONFIGURATION_V1: + { + cp_payload_t *cp = (cp_payload_t*)payload; + configuration_attribute_t *ca; + + switch (cp->get_type(cp)) + { + case CFG_REQUEST: + case CFG_REPLY: + case CFG_SET: + case CFG_ACK: + { + attributes = cp->create_attribute_enumerator(cp); + while (attributes->enumerate(attributes, &ca)) + { + DBG2(DBG_IKE, "processing %N attribute", + configuration_attribute_type_names, ca->get_type(ca)); + process_attribute(this, ca); + } + attributes->destroy(attributes); + break; + } + default: + DBG1(DBG_IKE, "ignoring %N config payload", + config_type_names, cp->get_type(cp)); + break; + } + + switch(this->state) + { + case TASK_XAUTH_INIT: + if(((cp->get_type(cp) != CFG_REQUEST) && (cp->get_type(cp) != CFG_REPLY)) || + (this->xauth_user_name_recv != TRUE) || + (this->xauth_user_pass_recv != TRUE)) + { + /* Didn't get an XAuth message, assume we're a ConfigMode message, set state appropriately */ + this->state = TASK_XAUTH_COMPLETE; + this->next_state = TASK_XAUTH_COMPLETE; + this->status = SUCCESS; + break; + } + this->next_state = TASK_XAUTH_PASS_VERIFY; + break; + case TASK_XAUTH_PASS_VERIFY: + if(((cp->get_type(cp) != CFG_SET) && (cp->get_type(cp) != CFG_ACK)) || + (this->xauth_status_recv != TRUE)) + { + DBG1(DBG_IKE, "Didn't receive XAuth status."); + return FAILED; + } + /* Set the return status for the build call */ + if(cp->get_type(cp) != CFG_ACK) + { + this->status = (this->xauth_status_data == XAUTH_STATUS_OK ? SUCCESS : FAILED); + } + else + { + this->status = SUCCESS; + } + this->next_state = TASK_XAUTH_COMPLETE; + break; + default: + this->next_state = TASK_XAUTH_COMPLETE; + this->status = SUCCESS; + break; + } + } + default: + break; + } + } + enumerator->destroy(enumerator); + return NEED_MORE; +} + METHOD(task_t, build_i, status_t, private_xauth_request_t *this, message_t *message) { - cp_payload_t *cp; + cp_payload_t *cp = NULL; chunk_t chunk = chunk_empty; + ike_version_t version; + payload_type_t cp_type; + payload_type_t ca_type; + host_t *vip; + peer_cfg_t *config; + enumerator_t *enumerator; + attribute_handler_t *handler; + configuration_attribute_type_t type; + chunk_t data; + + DBG1(DBG_IKE, "%s: state %d", __func__, this->state); + version = this->ike_sa->get_version(this->ike_sa); + if(version == IKEV1) + { + if(this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) + { + DBG1(DBG_IKE, "!!!!!!!!!!!!!!!!!!!!!!!!!NEED_MORE!!!!!!!!!!!!!!!!!!!!!"); + return NEED_MORE; + } + + if(!this->auth_cfg) + { + this->auth_cfg = get_auth_cfg(this, TRUE); + } + switch((uintptr_t)this->auth_cfg->get(this->auth_cfg, AUTH_RULE_AUTH_CLASS)) + { + case AUTH_CLASS_XAUTH_PSK: + case AUTH_CLASS_XAUTH_PUBKEY: + break; + default: + /* We aren't XAuth, so do nothing */ + DBG1(DBG_IKE, "!!!!!!!!!!!!!!!!!!!!!!!!!SUCCESS!!!!!!!!!!!!!!!!!!!!!"); + return SUCCESS; + } + cp_type = CONFIGURATION_V1; + ca_type = CONFIGURATION_ATTRIBUTE_V1; + } + else /* IKEv2 */ + { + /* IKEv2 does not support XAuth, skip those states. */ + this->state = TASK_XAUTH_COMPLETE; + if (message->get_message_id(message) == 1) + { /* in first IKE_AUTH only */ + DBG1(DBG_IKE, "!!!!!!!!!!!!!!!!!!!!!!!!!NEED_MORE!!!!!!!!!!!!!!!!!!!!!"); + return NEED_MORE; + } + cp_type = CONFIGURATION; + ca_type = CONFIGURATION_ATTRIBUTE; + } switch(this->state) { case TASK_XAUTH_INIT: - cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST); + cp = cp_payload_create_type(cp_type, CFG_REQUEST); cp->add_attribute(cp, configuration_attribute_create_chunk( - CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_NAME, chunk)); + ca_type, XAUTH_USER_NAME, chunk)); cp->add_attribute(cp, configuration_attribute_create_chunk( - CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_PASSWORD, chunk)); + ca_type, XAUTH_USER_PASSWORD, chunk)); break; - case TASK_XAUTH_PASS_DONE: - cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET); + case TASK_XAUTH_PASS_VERIFY: + cp = cp_payload_create_type(cp_type, CFG_SET); cp->add_attribute(cp, configuration_attribute_create_value( XAUTH_STATUS, (this->status == FAILED ? XAUTH_STATUS_FAIL : XAUTH_STATUS_OK))); break; + case TASK_XAUTH_COMPLETE: + /* ConfigMode stuff */ + /* reuse virtual IP if we already have one */ + vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); + if (!vip) + { + config = this->ike_sa->get_peer_cfg(this->ike_sa); + vip = config->get_virtual_ip(config); + } + if (vip) + { + cp = cp_payload_create_type(cp_type, CFG_REQUEST); + cp->add_attribute(cp, build_vip(vip)); + } + + enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes, + this->ike_sa->get_other_id(this->ike_sa), vip); + while (enumerator->enumerate(enumerator, &handler, &type, &data)) + { + configuration_attribute_t *ca; + entry_t *entry; + + /* create configuration attribute */ + DBG2(DBG_IKE, "building %N attribute", + configuration_attribute_type_names, type); + ca = configuration_attribute_create_chunk(ca_type, + type, data); + if (!cp) + { + cp = cp_payload_create_type(cp_type, CFG_REQUEST); + } + cp->add_attribute(cp, ca); + + /* save handler along with requested type */ + entry = malloc_thing(entry_t); + entry->type = type; + entry->handler = handler; + + this->requested->insert_last(this->requested, entry); + } + enumerator->destroy(enumerator); + + break; default: + DBG1(DBG_IKE, "!!!!!!!!!!!!!!!!!!!!!!!!!FAILED!!!!!!!!!!!!!!!!!!!!!"); return FAILED; } /* Add the payloads into the message */ - message->add_payload(message, (payload_t *)cp); - + if(cp) + { + message->add_payload(message, (payload_t *)cp); + } + DBG1(DBG_IKE, "!!!!!!!!!!!!!!!!!!!!!!!!!NEED_MORE!!!!!!!!!!!!!!!!!!!!!"); return NEED_MORE; } METHOD(task_t, process_r, status_t, private_xauth_request_t *this, message_t *message) { - return NEED_MORE; -} + ike_version_t version; + payload_type_t cp_type; + DBG1(DBG_IKE, "%s: state %d", __func__, this->state); -METHOD(task_t, build_r, status_t, - private_xauth_request_t *this, message_t *message) -{ - return NEED_MORE; + version = this->ike_sa->get_version(this->ike_sa); + if(version == IKEV1) + { + if(this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) + { + return NEED_MORE; + } + if(!this->auth_cfg) + { + this->auth_cfg = get_auth_cfg(this, TRUE); + } + switch((uintptr_t)this->auth_cfg->get(this->auth_cfg, AUTH_RULE_AUTH_CLASS)) + { + case AUTH_CLASS_XAUTH_PSK: + case AUTH_CLASS_XAUTH_PUBKEY: + break; + default: + /* We aren't XAuth, so do we should expect ConfigMode stuff */ + return SUCCESS; + } + cp_type = CONFIGURATION_V1; + } + else /* IKEv2 */ + { + /* IKEv2 does not support XAuth, skip those states. */ + this->state = TASK_XAUTH_COMPLETE; + if (message->get_message_id(message) == 1) + { /* in first IKE_AUTH only */ + return NEED_MORE; + } + cp_type = CONFIGURATION; + } + + return process_payloads(this, message); } -METHOD(task_t, process_i, status_t, +METHOD(task_t, build_r, status_t, private_xauth_request_t *this, message_t *message) { - cp_payload_t *cp_payload; + chunk_t user_name = chunk_from_chars('j', 'o', 's', 't'); + chunk_t user_pass = chunk_from_chars('j', 'o', 's', 't'); + status_t status; + cp_payload_t *cp = NULL; + payload_type_t cp_type = CONFIGURATION; + payload_type_t ca_type = CONFIGURATION_ATTRIBUTE; + ike_version_t version; + identification_t *id; enumerator_t *enumerator; - configuration_attribute_t *ca; - chunk_t status_chunk = chunk_empty; + configuration_attribute_type_t type; + chunk_t value; + host_t *vip = NULL; + peer_cfg_t *config; - cp_payload = (cp_payload_t *)message->get_payload(message, CONFIGURATION_V1); - enumerator = cp_payload->create_attribute_enumerator(cp_payload); - while (enumerator->enumerate(enumerator, &ca)) + DBG1(DBG_IKE, "%s: state %d", __func__, this->state); + if(this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) + { + return NEED_MORE; + } + version = this->ike_sa->get_version(this->ike_sa); + if(version == IKEV1) { - switch(ca->get_type(ca)) + if(!this->auth_cfg) { - case XAUTH_USER_NAME: - this->user_name = ca->get_chunk(ca); - break; - case XAUTH_USER_PASSWORD: - this->user_pass = ca->get_chunk(ca); - break; - case XAUTH_STATUS: - status_chunk = ca->get_chunk(ca); + this->auth_cfg = get_auth_cfg(this, TRUE); + } + switch((uintptr_t)this->auth_cfg->get(this->auth_cfg, AUTH_RULE_AUTH_CLASS)) + { + case AUTH_CLASS_XAUTH_PSK: + case AUTH_CLASS_XAUTH_PUBKEY: break; default: - DBG3(DBG_IKE, "Unknown config attribute type %d, ignored", ca->get_type(ca)); + this->state = TASK_XAUTH_COMPLETE; + return SUCCESS; } + cp_type = CONFIGURATION_V1; + ca_type = CONFIGURATION_ATTRIBUTE_V1; } - enumerator->destroy(enumerator); switch(this->state) { case TASK_XAUTH_INIT: + /* TODO-IKEv1: Fetch the user/pass from an authenticator */ + cp = cp_payload_create_type(cp_type, CFG_REPLY); + cp->add_attribute(cp, configuration_attribute_create_chunk( + ca_type, XAUTH_USER_NAME, user_name)); + cp->add_attribute(cp, configuration_attribute_create_chunk( + ca_type, XAUTH_USER_PASSWORD, user_pass)); + chunk_clear(&user_name); + chunk_clear(&user_pass); + + this->state = TASK_XAUTH_PASS_VERIFY; + status = NEED_MORE; + break; + case TASK_XAUTH_PASS_VERIFY: + cp = cp_payload_create_type(cp_type, CFG_ACK); + cp->add_attribute(cp, configuration_attribute_create_value( + XAUTH_STATUS, XAUTH_STATUS_OK)); + status = this->status; + this->state = TASK_XAUTH_COMPLETE; + break; + case TASK_XAUTH_COMPLETE: + id = this->ike_sa->get_other_eap_id(this->ike_sa); - if(cp_payload->get_type(cp_payload) != CFG_REPLY) + config = this->ike_sa->get_peer_cfg(this->ike_sa); + if (this->virtual_ip) { - DBG1(DBG_IKE, "ERROR: ConfigMode payload is not a reply"); - return FAILED; + DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); + if (config->get_pool(config)) + { + vip = hydra->attributes->acquire_address(hydra->attributes, + config->get_pool(config), id, this->virtual_ip); + } + if (vip == NULL) + { + DBG1(DBG_IKE, "no virtual IP found, sending %N", + notify_type_names, INTERNAL_ADDRESS_FAILURE); + message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, + chunk_empty); + return SUCCESS; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id); + this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); + + cp = cp_payload_create_type(cp_type, CFG_REPLY); + cp->add_attribute(cp, build_vip(vip)); } - this->state = TASK_XAUTH_PASS_DONE; - if((this->user_name.len == 0) || (this->user_pass.len == 0)) + /* query registered providers for additional attributes to include */ + enumerator = hydra->attributes->create_responder_enumerator( + hydra->attributes, config->get_pool(config), id, vip); + while (enumerator->enumerate(enumerator, &type, &value)) { - DBG1(DBG_IKE, "ERROR: Did not get user name or user pass, aborting"); - this->status = FAILED; - /* We should close out the XAuth negotiation cleanly by sending a "failed" message */ - return NEED_MORE; + if (!cp) + { + cp = cp_payload_create_type(cp_type, CFG_REPLY); + } + DBG2(DBG_IKE, "building %N attribute", + configuration_attribute_type_names, type); + cp->add_attribute(cp, + configuration_attribute_create_chunk(ca_type, + type, value)); } + enumerator->destroy(enumerator); + status = SUCCESS; + break; + default: + return FAILED; + } + return status; +} + +METHOD(task_t, process_i, status_t, + private_xauth_request_t *this, message_t *message) +{ + status_t status; + DBG1(DBG_IKE, "%s: state %d", __func__, this->state); + if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) + { /* in last IKE_AUTH exchange */ - /* TODO-IKEv1: Do actual user/pass verification */ -// if(!chunk_compare(this->user_name, this->user_pass)) -// { -// this->status = FAILED; -// DBG1(DBG_IKE, "ERROR: user/pass verification failure"); - /* We should close out the XAuth negotiation cleanly by sending a "failed" message */ -// return NEED_MORE; -// } + status = process_payloads(this, message); + this->state = this->next_state; - this->status = SUCCESS; - return NEED_MORE; - case TASK_XAUTH_PASS_DONE: - if(cp_payload->get_type(cp_payload) != CFG_ACK) - { - DBG1(DBG_IKE, "ERROR: ConfigMode payload is not a status ack"); - return FAILED; - } - if(status_chunk.len != 0) - { - DBG1(DBG_IKE, "Status payload of an ack had data, hmm...."); - } + DBG1(DBG_IKE, "state %d, complete state %d", this->state, TASK_XAUTH_COMPLETE); + DBG1(DBG_IKE, "status %d SUCCESS %d", this->status, SUCCESS); - DBG1(DBG_IKE, "Done with XAUTH!!!"); + if (this->virtual_ip) + { + this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + } + if(this->state == TASK_XAUTH_COMPLETE) return this->status; + return status; } - return FAILED; + return NEED_MORE; + } METHOD(task_t, get_type, task_type_t, @@ -257,9 +719,14 @@ xauth_request_t *xauth_request_create(ike_sa_t *ike_sa, bool initiator) .initiator = initiator, .ike_sa = ike_sa, .requested = linked_list_create(), - .user_name = chunk_empty, - .user_pass = chunk_empty, .state = TASK_XAUTH_INIT, + .next_state = TASK_XAUTH_INIT, + .xauth_status_data = XAUTH_STATUS_FAIL, + .xauth_user_name = chunk_empty, + .xauth_user_pass = chunk_empty, + .xauth_user_name_recv = FALSE, + .xauth_user_pass_recv = FALSE, + .xauth_status_recv = FALSE, ); if (initiator)