X-Git-Url: https://git.strongswan.org/?p=strongswan.git;a=blobdiff_plain;f=src%2Flibcharon%2Fsa%2Ftask_manager_v1.c;h=e899b0687c1700274643ff6fd14584d9c40a06aa;hp=e672879ea7eb33cf1b051da527315e07a63f7824;hb=156b8662a64a4e251470de1e42213bc66f8e772e;hpb=52ac2cebe2136980e372bc435f4318746bbaf8e4;ds=sidebyside diff --git a/src/libcharon/sa/task_manager_v1.c b/src/libcharon/sa/task_manager_v1.c old mode 100644 new mode 100755 index e672879..e899b06 --- a/src/libcharon/sa/task_manager_v1.c +++ b/src/libcharon/sa/task_manager_v1.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 Tobias Brunner - * Copyright (C) 2007-2010 Martin Willi + * Copyright (C) 2007-2011 Tobias Brunner + * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -19,12 +19,19 @@ #include #include -#include +#include #include #include -#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include typedef struct exchange_t exchange_t; @@ -97,9 +104,9 @@ struct private_task_manager_t { u_int32_t mid; /** - * Hash of a previously received message + * Sequence number of the last sent message */ - u_int32_t hash; + u_int32_t seqnr; /** * how many times we have retransmitted so far @@ -191,9 +198,10 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) } METHOD(task_manager_t, retransmit, status_t, - private_task_manager_t *this, u_int32_t message_id) + private_task_manager_t *this, u_int32_t message_seqnr) { - if (message_id == this->initiating.mid) + /* this.initiating packet used as marker for received response */ + if (message_seqnr == this->initiating.seqnr && this->initiating.packet ) { u_int32_t timeout; packet_t *packet; @@ -217,39 +225,20 @@ METHOD(task_manager_t, retransmit, status_t, if (this->initiating.retransmitted) { - DBG1(DBG_IKE, "retransmit %d of request with message ID %d", - this->initiating.retransmitted, message_id); + DBG1(DBG_IKE, "retransmit %d of request with message ID %d seqnr (%d)", + this->initiating.retransmitted, this->initiating.mid, message_seqnr); } packet = this->initiating.packet->clone(this->initiating.packet); charon->sender->send(charon->sender, packet); this->initiating.retransmitted++; - job = (job_t*)retransmit_job_create(this->initiating.mid, + job = (job_t*)retransmit_job_create(this->initiating.seqnr, this->ike_sa->get_id(this->ike_sa)); lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout); } return SUCCESS; } -void migrate_tasks(linked_list_t *from, linked_list_t *to) -{ - enumerator_t *enumerator; - task_t *task; - - enumerator = from->create_enumerator(from); - while(enumerator->enumerate(enumerator, (void**)&task)) - { - DBG4(DBG_IKE, " Migrating %N task to new queue", task_type_names, task->get_type(task)); - if(task->swap_initiator) - { - task->swap_initiator(task); - } - to->insert_last(to, task); - from->remove_at(from, enumerator); - } - enumerator->destroy(enumerator); -} - METHOD(task_manager_t, initiate, status_t, private_task_manager_t *this) { @@ -260,6 +249,7 @@ METHOD(task_manager_t, initiate, status_t, status_t status; exchange_type_t exchange = EXCHANGE_TYPE_UNDEFINED; bool new_mid = FALSE; + bool expect_response = FALSE; if (!this->rng) { @@ -282,21 +272,31 @@ METHOD(task_manager_t, initiate, status_t, { case IKE_CREATED: activate_task(this, TASK_VENDOR_V1); + activate_task(this, TASK_IKE_CERT_PRE_V1); if (activate_task(this, TASK_MAIN_MODE)) { exchange = ID_PROT; + activate_task(this, TASK_IKE_CERT_POST_V1); + activate_task(this, TASK_IKE_NATD_V1); + } + break; + case IKE_CONNECTING: + if (activate_task(this, TASK_XAUTH)) + { + exchange = TRANSACTION; + new_mid = TRUE; } break; case IKE_ESTABLISHED: - if (activate_task(this, TASK_QUICK_MODE)) + if (activate_task(this, TASK_MODE_CONFIG)) { - exchange = QUICK_MODE; + exchange = TRANSACTION; new_mid = TRUE; break; } - if (activate_task(this, TASK_XAUTH_REQUEST)) + if (activate_task(this, TASK_QUICK_MODE)) { - exchange = TRANSACTION; + exchange = QUICK_MODE; new_mid = TRUE; break; } @@ -320,7 +320,7 @@ METHOD(task_manager_t, initiate, status_t, case TASK_QUICK_MODE: exchange = QUICK_MODE; break; - case TASK_XAUTH_REQUEST: + case TASK_XAUTH: exchange = TRANSACTION; new_mid = TRUE; break; @@ -365,14 +365,8 @@ METHOD(task_manager_t, initiate, status_t, this->active_tasks->remove_at(this->active_tasks, enumerator); task->destroy(task); break; - case MIGRATE: - /* task completed, remove it */ - this->active_tasks->remove_at(this->active_tasks, enumerator); - task->destroy(task); - /* migrate the remaining active tasks to the passive queue */ - migrate_tasks(this->active_tasks, this->passive_tasks); - break; case NEED_MORE: + expect_response = TRUE; /* processed, but task needs another exchange */ break; case FAILED: @@ -394,6 +388,7 @@ METHOD(task_manager_t, initiate, status_t, /* update exchange type if a task changed it */ this->initiating.type = message->get_exchange_type(message); + this->initiating.seqnr++; status = this->ike_sa->generate_message(this->ike_sa, message, &this->initiating.packet); @@ -408,9 +403,14 @@ METHOD(task_manager_t, initiate, status_t, } message->destroy(message); + if (expect_response) + { + return retransmit(this, this->initiating.seqnr); + } charon->sender->send(charon->sender, this->initiating.packet->clone(this->initiating.packet)); - + this->initiating.packet->destroy(this->initiating.packet); + this->initiating.packet = NULL; return SUCCESS; } @@ -433,7 +433,6 @@ static status_t build_response(private_task_manager_t *this, message_t *request) host_t *me, *other; bool delete = FALSE; status_t status; - bool migrate = FALSE; me = request->get_destination(request); other = request->get_source(request); @@ -451,9 +450,6 @@ static status_t build_response(private_task_manager_t *this, message_t *request) { switch (task->build(task, message)) { - case MIGRATE: - migrate = TRUE; - /* FALL */ case SUCCESS: /* task completed, remove it */ this->passive_tasks->remove_at(this->passive_tasks, enumerator); @@ -500,19 +496,71 @@ static status_t build_response(private_task_manager_t *this, message_t *request) charon->sender->send(charon->sender, this->responding.packet->clone(this->responding.packet)); + if (delete) + { + return DESTROY_ME; + } + return SUCCESS; +} + +/** + * Send a notify in a separate INFORMATIONAL exchange back to the sender. + */ +static void send_notify_response(private_task_manager_t *this, + message_t *request, notify_type_t type, + chunk_t data, task_t *task) +{ + message_t *response; + packet_t *packet; + host_t *me, *other; + u_int32_t mid; + + if (request && request->get_exchange_type(request) == INFORMATIONAL_V1) + { /* don't respond to INFORMATIONAL requests to avoid a notify war */ + DBG1(DBG_IKE, "ignore malformed INFORMATIONAL request"); + return; + } - if (migrate) + response = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); + response->set_exchange_type(response, INFORMATIONAL_V1); + response->set_request(response, TRUE); + this->rng->get_bytes(this->rng, sizeof(mid), (void*)&mid); + response->set_message_id(response, mid); + + if (task) + { + /* Let the task build the response */ + if (task->build(task,response) != SUCCESS) + { + response->destroy(response); + return; + } + } + else { - migrate_tasks(this->passive_tasks, this->queued_tasks); - /* Kick off the newly installed tasks */ - initiate(this); + response->add_notify(response, FALSE, type, data); } - if (delete) + me = this->ike_sa->get_my_host(this->ike_sa); + if (me->is_anyaddr(me)) { - return DESTROY_ME; + me = request->get_destination(request); + this->ike_sa->set_my_host(this->ike_sa, me->clone(me)); } - return SUCCESS; + 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); } /** @@ -523,6 +571,9 @@ static status_t process_request(private_task_manager_t *this, { enumerator_t *enumerator; task_t *task = NULL; + bool send_response = FALSE; + payload_t *payload; + notify_payload_t *notify; if (this->passive_tasks->get_count(this->passive_tasks) == 0) { /* create tasks depending on request type, if not already some queued */ @@ -531,22 +582,91 @@ static status_t process_request(private_task_manager_t *this, case ID_PROT: task = (task_t *)ike_vendor_v1_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_cert_pre_v1_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t *)main_mode_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); - task = (task_t *)xauth_request_create(this->ike_sa, FALSE); + task = (task_t*)ike_cert_post_v1_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t *)ike_natd_v1_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); break; case AGGRESSIVE: /* TODO-IKEv1: agressive mode */ return FAILED; case QUICK_MODE: + if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) + { + DBG1(DBG_IKE, "received quick mode request for " + "unestablished IKE_SA, ignored"); + return FAILED; + } task = (task_t *)quick_mode_create(this->ike_sa, NULL, NULL, NULL); this->passive_tasks->insert_last(this->passive_tasks, task); break; case INFORMATIONAL_V1: - /* TODO-IKEv1: informational */ - return FAILED; + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + switch (payload->get_type(payload)) + { + case NOTIFY_V1: + { + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + /* TODO-IKEv1: Add notification types here as needed */ + case INITIAL_CONTACT_IKEV1: + break; + default: + if(notify->get_notify_type(notify) < 16384) + { + DBG1(DBG_IKE, "Received %N error notification.", notify_type_names, notify->get_notify_type(notify)); + return FAILED; + } + break; + } + break; + } + case DELETE_V1: + { + delete_payload_t *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); + } + break; + } + default: + break; + } + if (task) + { + this->passive_tasks->insert_last(this->passive_tasks, task); + } + } + enumerator->destroy(enumerator); + break; + case TRANSACTION: + if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) + { + task = (task_t *)mode_config_create(this->ike_sa, FALSE); + } + else + { + task = (task_t *)xauth_create(this->ike_sa, FALSE); + } + this->passive_tasks->insert_last(this->passive_tasks, task); + break; default: return FAILED; } @@ -561,21 +681,13 @@ static status_t process_request(private_task_manager_t *this, /* task completed, remove it */ this->passive_tasks->remove_at(this->passive_tasks, enumerator); task->destroy(task); - enumerator->destroy(enumerator); - return SUCCESS; - case MIGRATE: - /* task completed, remove it */ - this->passive_tasks->remove_at(this->passive_tasks, enumerator); - task->destroy(task); - enumerator->destroy(enumerator); - /* migrate the remaining tasks */ - migrate_tasks(this->passive_tasks, this->queued_tasks); - /* Kick off the newly installed tasks */ - initiate(this); - return SUCCESS; + break; case NEED_MORE: /* processed, but task needs at least another call to build() */ + send_response = TRUE; break; + case FAILED_SEND_ERROR: + send_notify_response(this, NULL, 0, chunk_empty, task); case FAILED: default: charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); @@ -590,7 +702,21 @@ static status_t process_request(private_task_manager_t *this, } enumerator->destroy(enumerator); - return build_response(this, message); + if (send_response) + { + if (build_response(this, message) != SUCCESS) + { + return DESTROY_ME; + } + } + if (this->passive_tasks->get_count(this->passive_tasks) == 0 && + this->queued_tasks->get_count(this->queued_tasks) > 0) + { + /* passive tasks completed, check if an active task has been queued, + * such as XAUTH or modeconfig push */ + return initiate(this); + } + return SUCCESS; } /** @@ -645,22 +771,83 @@ static status_t process_response(private_task_manager_t *this, return initiate(this); } +/** + * 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; + + status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa)); + + if (status != SUCCESS) + { + switch (status) + { + case NOT_SUPPORTED: + DBG1(DBG_IKE, "unsupported exchange type"); + send_notify_response(this, msg, + INVALID_EXCHANGE_TYPE, chunk_empty, NULL); + break; + case PARSE_ERROR: + DBG1(DBG_IKE, "message parsing failed"); + send_notify_response(this, msg, + PAYLOAD_MALFORMED, chunk_empty, NULL); + break; + case VERIFY_ERROR: + DBG1(DBG_IKE, "message verification failed"); + send_notify_response(this, msg, + PAYLOAD_MALFORMED, chunk_empty, NULL); + break; + case FAILED: + DBG1(DBG_IKE, "integrity check failed"); + send_notify_response(this, msg, + INVALID_HASH_INFORMATION, chunk_empty, NULL); + break; + case INVALID_STATE: + DBG1(DBG_IKE, "found encrypted message, but no keys available"); + send_notify_response(this, msg, + PAYLOAD_MALFORMED, chunk_empty, NULL); + default: + break; + } + DBG1(DBG_IKE, "%N %s with message ID %d processing failed", + exchange_type_names, msg->get_exchange_type(msg), + msg->get_request(msg) ? "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) { u_int32_t hash, mid; host_t *me, *other; - - mid = msg->get_message_id(msg); + status_t status; /* TODO-IKEv1: update hosts more selectively */ me = msg->get_destination(msg); other = msg->get_source(msg); + mid = msg->get_message_id(msg); if ((mid && mid == this->initiating.mid) || (this->initiating.mid == 0 && this->active_tasks->get_count(this->active_tasks))) { + msg->set_request(msg, FALSE); + status = parse_message(this, msg); + if (status != SUCCESS) + { + return status; + } + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE); charon->bus->message(charon->bus, msg, FALSE); if (process_response(this, msg) != SUCCESS) @@ -680,6 +867,39 @@ METHOD(task_manager_t, process_message, status_t, this->responding.packet->clone(this->responding.packet)); return SUCCESS; } + msg->set_request(msg, TRUE); + status = parse_message(this, msg); + if (status != SUCCESS) + { + return status; + } + /* 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, NULL); + 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)); this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE); charon->bus->message(charon->bus, msg, TRUE); if (process_request(this, msg) != SUCCESS)