diff options
Diffstat (limited to 'src/charon/sa/ike_sa_manager.c')
-rw-r--r-- | src/charon/sa/ike_sa_manager.c | 475 |
1 files changed, 203 insertions, 272 deletions
diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index 31972e0bc..6e9d867fd 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -297,13 +297,134 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this) } /** + * Implementation of of ike_sa_manager.checkout. + */ +static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + bool responder_spi_set; + bool initiator_spi_set; + bool original_initiator; + ike_sa_t *ike_sa = NULL; + + DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id); + + DBG2(DBG_MGR, "%d IKE_SAs in manager", + this->ike_sa_list->get_count(this->ike_sa_list)); + + /* each access is locked */ + pthread_mutex_lock(&(this->mutex)); + + responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id); + initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id); + original_initiator = ike_sa_id->is_initiator(ike_sa_id); + + if ((initiator_spi_set && responder_spi_set) || + ((initiator_spi_set && !responder_spi_set) && (original_initiator))) + { + /* we SHOULD have an IKE_SA for these SPIs in the list, + * if not, we can't handle the request... + */ + entry_t *entry; + /* look for the entry */ + if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + if (wait_for_entry(this, entry)) + { + DBG2(DBG_MGR, "IKE_SA successfully checked out"); + /* ok, this IKE_SA is finally ours */ + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + /* update responder SPI when it's not set */ + if (entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) + { + ike_sa_id_t *ike_sa_ike_sa_id = ike_sa->get_id(ike_sa); + u_int64_t spi = ike_sa_id->get_responder_spi(ike_sa_id); + + ike_sa_ike_sa_id->set_responder_spi(ike_sa_ike_sa_id, spi); + entry->ike_sa_id->set_responder_spi(entry->ike_sa_id, spi); + } + } + else + { + DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out"); + } + } + else + { + DBG2(DBG_MGR, "IKE_SA not stored in list"); + /* looks like there is no such IKE_SA, better luck next time... */ + } + } + else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator)) + { + /* an IKE_SA_INIT from an another endpoint, + * he is the initiator. + * For simplicity, we do NOT check for retransmitted + * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- + * Request (even a retransmitted one) will result in a + * IKE_SA. This could be improved... + */ + u_int64_t responder_spi; + entry_t *new_entry; + + /* set SPIs, we are the responder */ + responder_spi = get_next_spi(this); + + /* we also set arguments spi, so its still valid */ + ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); + + /* create entry */ + new_entry = entry_create(ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); + + /* check ike_sa out */ + DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs"); + new_entry->checked_out = TRUE; + ike_sa = new_entry->ike_sa; + } + else if (!initiator_spi_set && !responder_spi_set) + { + /* checkout of a new and unused IKE_SA, used for rekeying */ + entry_t *new_entry; + + if (original_initiator) + { + ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this)); + } + else + { + ike_sa_id->set_responder_spi(ike_sa_id, get_next_spi(this)); + } + /* create entry */ + new_entry = entry_create(ike_sa_id); + DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id); + + this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); + + /* check ike_sa out */ + new_entry->checked_out = TRUE; + ike_sa = new_entry->ike_sa; + } + else + { + /* responder set, initiator not: here is something seriously wrong! */ + DBG2(DBG_MGR, "invalid IKE_SA SPIs"); + } + + pthread_mutex_unlock(&(this->mutex)); + + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; +} + +/** * Implementation of of ike_sa_manager.checkout_by_id. */ -static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, - host_t *my_host, - host_t *other_host, - identification_t *my_id, - identification_t *other_id) +static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this, + host_t *my_host, host_t *other_host, + identification_t *my_id, + identification_t *other_id) { iterator_t *iterator; entry_t *entry; @@ -387,105 +508,53 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, } /** - * Implementation of of ike_sa_manager.checkout. + * Implementation of of ike_sa_manager.checkout_by_id. */ -static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, + bool child) { - bool responder_spi_set; - bool initiator_spi_set; - bool original_initiator; + iterator_t *iterator, *children; + entry_t *entry; ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; - DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id); - - DBG2(DBG_MGR, "%d IKE_SAs in manager", - this->ike_sa_list->get_count(this->ike_sa_list)); - - /* each access is locked */ pthread_mutex_lock(&(this->mutex)); - responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id); - initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id); - original_initiator = ike_sa_id->is_initiator(ike_sa_id); - - if ((initiator_spi_set && responder_spi_set) || - ((initiator_spi_set && !responder_spi_set) && (original_initiator))) + iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); + while (iterator->iterate(iterator, (void**)&entry)) { - /* we SHOULD have an IKE_SA for these SPIs in the list, - * if not, we can't handle the request... - */ - entry_t *entry; - /* look for the entry */ - if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + if (wait_for_entry(this, entry)) { - if (wait_for_entry(this, entry)) + /* look for a child with such a reqid ... */ + if (child) { - DBG2(DBG_MGR, "IKE_SA successfully checked out"); - /* ok, this IKE_SA is finally ours */ - entry->checked_out = TRUE; - ike_sa = entry->ike_sa; + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (child_sa->get_reqid(child_sa) == id) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); } - else + else /* ... or for a IKE_SA with such a unique id */ { - DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out"); + if (entry->ike_sa->get_unique_id(entry->ike_sa) == id) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) + { + entry->checked_out = TRUE; + break; } - } - else - { - DBG2(DBG_MGR, "IKE_SA not stored in list"); - /* looks like there is no such IKE_SA, better luck next time... */ } } - else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator)) - { - /* an IKE_SA_INIT from an another endpoint, - * he is the initiator. - * For simplicity, we do NOT check for retransmitted - * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- - * Request (even a retransmitted one) will result in a - * IKE_SA. This could be improved... - */ - u_int64_t responder_spi; - entry_t *new_entry; - - /* set SPIs, we are the responder */ - responder_spi = get_next_spi(this); - - /* we also set arguments spi, so its still valid */ - ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); - - /* create entry */ - new_entry = entry_create(ike_sa_id); - - this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); - - /* check ike_sa out */ - DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs"); - new_entry->checked_out = TRUE; - ike_sa = new_entry->ike_sa; - } - else if (!initiator_spi_set && !responder_spi_set && original_initiator) - { - /* checkout of a new and unused IKE_SA, used for rekeying */ - entry_t *new_entry; - - ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this)); - /* create entry */ - new_entry = entry_create(ike_sa_id); - DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id); - - this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); - - /* check ike_sa out */ - new_entry->checked_out = TRUE; - ike_sa = new_entry->ike_sa; - } - else - { - /* responder set, initiator not: here is something seriously wrong! */ - DBG2(DBG_MGR, "invalid IKE_SA SPIs"); - } - + iterator->destroy(iterator); pthread_mutex_unlock(&(this->mutex)); charon->bus->set_sa(charon->bus, ike_sa); @@ -493,14 +562,15 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id } /** - * Implementation of of ike_sa_manager.checkout_by_child. + * Implementation of of ike_sa_manager.checkout_by_name. */ -static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, - u_int32_t reqid) +static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, + bool child) { - iterator_t *iterator; + iterator_t *iterator, *children; entry_t *entry; ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; pthread_mutex_lock(&(this->mutex)); @@ -509,12 +579,31 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, { if (wait_for_entry(this, entry)) { - /* ok, access is exclusive for us, check for child */ - if (entry->ike_sa->has_child_sa(entry->ike_sa, reqid)) + /* look for a child with such a policy name ... */ + if (child) + { + children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if (streq(child_sa->get_name(child_sa), name)) + { + ike_sa = entry->ike_sa; + break; + } + } + children->destroy(children); + } + else /* ... or for a IKE_SA with such a connection name */ + { + if (streq(entry->ike_sa->get_name(entry->ike_sa), name)) + { + ike_sa = entry->ike_sa; + } + } + /* got one, return */ + if (ike_sa) { - /* match */ entry->checked_out = TRUE; - ike_sa = entry->ike_sa; break; } } @@ -529,9 +618,16 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, /** * Iterator hook for iterate, gets ike_sas instead of entries */ -static void* iterator_hook(void *value) +static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in, + ike_sa_t **out) { - return ((entry_t*)value)->ike_sa; + /* check out entry */ + if (wait_for_entry(this, in)) + { + *out = in->ike_sa; + return TRUE; + } + return FALSE; } /** @@ -540,9 +636,9 @@ static void* iterator_hook(void *value) static iterator_t *create_iterator(private_ike_sa_manager_t* this) { iterator_t *iterator = this->ike_sa_list->create_iterator_locked( - this->ike_sa_list, &this->mutex); + this->ike_sa_list, &this->mutex); /* register hook to iterator over ike_sas, not entries */ - iterator->set_iterator_hook(iterator, iterator_hook); + iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this); return iterator; } @@ -634,170 +730,6 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik } /** - * Implementation of ike_sa_manager_t.delete. - */ -static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) -{ - /* deletion is a bit complex, we must garant that no thread is waiting for - * this SA. - * We take this SA from the list, and start signaling while threads - * are in the condvar. - */ - entry_t *entry; - status_t retval; - - DBG2(DBG_MGR, "delete IKE_SA: %J", ike_sa_id); - - pthread_mutex_lock(&(this->mutex)); - - if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) - { - /* we try a delete. If it succeeds, our job is done here. The - * other peer will reply, and the IKE SA gets the finally deleted... - */ - if (entry->ike_sa->delete(entry->ike_sa) == SUCCESS) - { - DBG2(DBG_MGR, "initiated delete for IKE_SA"); - } - /* but if the IKE SA is not in a state where the deletion is - * negotiated with the other peer, we can destroy the IKE SA on our own. - */ - else - { - - } - retval = SUCCESS; - } - else - { - DBG2(DBG_MGR, "tried to delete nonexisting IKE_SA"); - retval = NOT_FOUND; - } - - pthread_mutex_unlock(&(this->mutex)); - return retval; -} - -/** - * Implementation of ike_sa_manager_t.delete_by_name. - */ -static status_t delete_by_name(private_ike_sa_manager_t *this, char *name) -{ - iterator_t *iterator; - iterator_t *child_iter; - entry_t *entry; - size_t name_len = strlen(name); - - pthread_mutex_lock(&(this->mutex)); - - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) - { - if (wait_for_entry(this, entry)) - { - /* delete ike_sa if: - * name{x} matches completely - * name{} matches by name - * name matches by name - */ - bool del = FALSE; - char *ike_name; - char *child_name; - child_sa_t *child_sa; - - ike_name = entry->ike_sa->get_name(entry->ike_sa); - /* check if "name{x}" matches completely */ - if (strcmp(name, ike_name) == 0) - { - del = TRUE; - } - /* check if name is in form of "name{}" and matches to ike_name */ - else if (name_len > 1 && - name[name_len - 2] == '{' && name[name_len - 1] == '}' && - strlen(ike_name) > name_len && - ike_name[name_len - 2] == '{' && - strncmp(name, ike_name, name_len - 2) == 0) - { - del = TRUE; - } - /* finally, check if name is "name" and matches ike_name */ - else if (name_len == strchr(ike_name, '{') - ike_name && - strncmp(name, ike_name, name_len) == 0) - { - del = TRUE; - } - - if (del) - { - if (entry->ike_sa->delete(entry->ike_sa) == DESTROY_ME) - { - delete_entry(this, entry); - iterator->reset(iterator); - } - /* no need to check children, as we delete all */ - continue; - } - - /* and now the same game for all children. delete child_sa if: - * name[x] matches completely - * name[] matches by name - * name matches by name - */ - child_iter = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); - while (child_iter->iterate(child_iter, (void**)&child_sa)) - { - /* skip ROUTED children, they have their "unroute" command */ - if (child_sa->get_state(child_sa) == CHILD_ROUTED) - { - continue; - } - - child_name = child_sa->get_name(child_sa); - del = FALSE; - /* check if "name[x]" matches completely */ - if (strcmp(name, child_name) == 0) - { - del = TRUE; - } - /* check if name is in form of "name[]" and matches to child_name */ - else if (name_len > 1 && - name[name_len - 2] == '[' && name[name_len - 1] == ']' && - strlen(child_name) > name_len && - child_name[name_len - 2] == '[' && - strncmp(name, child_name, name_len - 2) == 0) - { - del = TRUE; - } - /* finally, check if name is "name" and matches child_name */ - else if (name_len == strchr(child_name, '[') - child_name && - strncmp(name, child_name, name_len) == 0) - { - del = TRUE; - } - if (del) - { - if (entry->ike_sa->delete_child_sa(entry->ike_sa, - child_sa->get_protocol(child_sa), - child_sa->get_spi(child_sa, TRUE)) == DESTROY_ME) - { - /* when a fatal error occurs, we are responsible to - * remove the IKE_SA */ - delete_entry(this, entry); - iterator->reset(iterator); - break; - } - } - } - child_iter->destroy(child_iter); - } - } - iterator->destroy(iterator); - pthread_mutex_unlock(&(this->mutex)); - - return SUCCESS; -} - -/** * Implementation of ike_sa_manager_t.destroy. */ static void destroy(private_ike_sa_manager_t *this) @@ -859,19 +791,18 @@ ike_sa_manager_t *ike_sa_manager_create() /* assign public functions */ this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; - this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_id; this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; - this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child; + this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer; + this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; + this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator; this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; - this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete_; - this->public.delete_by_name = (status_t(*)(ike_sa_manager_t*,char*))delete_by_name; this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; /* initialize private variables */ this->ike_sa_list = linked_list_create(); - pthread_mutex_init(&(this->mutex), NULL); + pthread_mutex_init(&this->mutex, NULL); this->randomizer = randomizer_create(); - return (ike_sa_manager_t*)this; + return &this->public; } |