fixed bug which prevented port float when nat is detected
[strongswan.git] / src / charon / sa / transactions / ike_sa_init.c
index 00ddbf4..6f52f42 100644 (file)
@@ -7,7 +7,8 @@
 
 /*
  * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005 Jan Hutter, Martin Willi
+ * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -33,6 +34,8 @@
 #include <encoding/payloads/nonce_payload.h>
 #include <sa/transactions/ike_auth.h>
 #include <queues/jobs/delete_half_open_ike_sa_job.h>
+#include <queues/jobs/delete_established_ike_sa_job.h>
+#include <queues/jobs/rekey_ike_sa_job.h>
 
 
 typedef struct private_ike_sa_init_t private_ike_sa_init_t;
@@ -89,11 +92,16 @@ struct private_ike_sa_init_t {
        chunk_t nonce_r;
        
        /**
-        * connection definition used
+        * connection definition used for initiation
         */
        connection_t *connection;
        
        /**
+        * policy definition forwarded to ike_auth transaction
+        */
+       policy_t *policy;
+       
+       /**
         * Negotiated proposal used for IKE_SA
         */
        proposal_t *proposal;
@@ -149,8 +157,6 @@ struct private_ike_sa_init_t {
  */
 static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_group)
 {
-       this->connection = this->ike_sa->get_connection(this->ike_sa);
-       
        if (this->connection->check_dh_group(this->connection, dh_group))
        {
                this->diffie_hellman = diffie_hellman_create(dh_group);
@@ -163,6 +169,16 @@ static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_
 }
 
 /**
+ * Implementation of ike_sa_init_t.set_config.
+ */
+static void set_config(private_ike_sa_init_t *this,
+                                          connection_t *connection, policy_t *policy)
+{
+       this->connection = connection;
+       this->policy = policy;
+}
+
+/**
  * Implementation of transaction_t.get_message_id.
  */
 static u_int32_t get_message_id(private_ike_sa_init_t *this)
@@ -218,7 +234,7 @@ static chunk_t generate_natd_hash(private_ike_sa_init_t *this,
        this->nat_hasher->allocate_hash(this->nat_hasher, natd_string, &natd_hash);
        
        sprintf(buf, "natd_hash(%016llx %016llx %s:%d) == SHA1(", spi_i, spi_r,
-                       host->get_address(host), host->get_port(host));
+                       host->get_string(host), host->get_port(host));
        chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_string);
        strcat(buf, ") == ");
        chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_hash);
@@ -249,12 +265,27 @@ static notify_payload_t *build_natd_payload(private_ike_sa_init_t *this,
 }
 
 /**
+ * destroy a list of proposals
+ */
+static void destroy_proposal_list(linked_list_t *list)
+{
+       proposal_t *proposal;
+       
+       while (list->remove_last(list, (void**)&proposal) == SUCCESS)
+       {
+               proposal->destroy(proposal);
+       }
+       list->destroy(list);
+}
+
+/**
  * Implementation of transaction_t.get_request.
  */
 static status_t get_request(private_ike_sa_init_t *this, message_t **result)
 {
        message_t *request;
        host_t *me, *other;
+       identification_t *my_id, *other_id;
        
        /* check if we already have built a message (retransmission) */
        if (this->message)
@@ -263,10 +294,16 @@ static status_t get_request(private_ike_sa_init_t *this, message_t **result)
                return SUCCESS;
        }
        
-       this->connection = this->ike_sa->get_connection(this->ike_sa);
        me = this->connection->get_my_host(this->connection);
        other = this->connection->get_other_host(this->connection);
        
+       /* we already set up the IDs. Mine is already fully qualified, other
+       * will be updated in the ike_auth transaction */
+       my_id = this->policy->get_my_id(this->policy);
+       other_id = this->policy->get_other_id(this->policy);
+       this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id));
+       this->ike_sa->set_other_id(this->ike_sa, other_id->clone(other_id));
+       
        /* build the request */
        request = message_create();
        request->set_source(request, me->clone(me));
@@ -302,6 +339,7 @@ static status_t get_request(private_ike_sa_init_t *this, message_t **result)
                
                proposal_list = this->connection->get_proposals(this->connection);
                sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+               destroy_proposal_list(proposal_list);
                
                request->add_payload(request, (payload_t*)sa_payload);
        }
@@ -347,8 +385,7 @@ static status_t get_request(private_ike_sa_init_t *this, message_t **result)
                request->add_payload(request, (payload_t*)notify);
        }
        
-       /* set new state */
-       this->ike_sa->set_state(this->ike_sa, SA_CONNECTING);
+       this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
        return SUCCESS;
 }
 
@@ -397,7 +434,10 @@ static status_t process_notifys(private_ike_sa_init_t *this, notify_payload_t *n
                                                                  "requested DH group not acceptable, aborting");
                                return DESTROY_ME;
                        }
-                       retry = ike_sa_init_create(this->ike_sa, 0);
+                       retry = ike_sa_init_create(this->ike_sa);
+                       retry->set_config(retry, this->connection, this->policy);
+                       this->connection = NULL;
+                       this->policy = NULL;
                        retry->use_dh_group(retry, dh_group);
                        *this->next = (transaction_t*)retry;
                        return FAILED;
@@ -488,6 +528,7 @@ static status_t get_response(private_ike_sa_init_t *this,
        
        me = request->get_destination(request);
        other = request->get_source(request);
+       this->message_id = request->get_message_id(request);
        
        /* set up response */
        response = message_create();
@@ -519,10 +560,11 @@ static status_t get_response(private_ike_sa_init_t *this,
                
                this->logger->log(this->logger, AUDIT,
                                                  "no connection for hosts %s...%s found, deleting IKE_SA",
-                                                 me->get_address(me), other->get_address(other));
+                                                 me->get_string(me), other->get_string(other));
                return DESTROY_ME;
        }
-       this->ike_sa->set_connection(this->ike_sa, this->connection);
+       this->ike_sa->set_name(this->ike_sa, 
+                                                  this->connection->get_name(this->connection));
        
        /* Precompute NAT-D hashes for incoming NAT notify comparison */
        ike_sa_id = request->get_ike_sa_id(request);
@@ -594,15 +636,10 @@ static status_t get_response(private_ike_sa_init_t *this,
                 */
                sa_payload_t* sa_response;
                linked_list_t *proposal_list;
-               proposal_t *proposal;
        
                proposal_list = sa_request->get_proposals(sa_request);
                this->proposal = this->connection->select_proposal(this->connection, proposal_list);
-               while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
-               {
-                       proposal->destroy(proposal);
-               }
-               proposal_list->destroy(proposal_list);
+               destroy_proposal_list(proposal_list);
                if (this->proposal == NULL)
                {
                        notify_payload_t *notify = notify_payload_create();
@@ -735,10 +772,10 @@ static status_t get_response(private_ike_sa_init_t *this,
        }
 
        /* derive all the keys used in the IKE_SA */
-       if (this->ike_sa->build_transforms(this->ike_sa, this->proposal, 
-                                                                          this->diffie_hellman, 
-                                                                          this->nonce_i, this->nonce_r,
-                                                                          FALSE) != SUCCESS)
+       if (this->ike_sa->derive_keys(this->ike_sa, this->proposal, 
+                                                                 this->diffie_hellman, 
+                                                                 this->nonce_i, this->nonce_r,
+                                                                 FALSE, NULL, NULL) != SUCCESS)
        {
                notify_payload_t *notify = notify_payload_create();
                notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
@@ -748,6 +785,10 @@ static status_t get_response(private_ike_sa_init_t *this,
                return DESTROY_ME;
        }
        
+       this->ike_sa->set_lifetimes(this->ike_sa, 
+                                       this->connection->get_soft_lifetime(this->connection),
+                                       this->connection->get_hard_lifetime(this->connection));
+       
        {       /* create ike_auth transaction, which will store informations for us */
                packet_t *response_packet;
                chunk_t request_chunk, response_chunk;
@@ -767,7 +808,10 @@ static status_t get_response(private_ike_sa_init_t *this,
                response_chunk = response->get_packet_data(response);
                
                /* create next transaction, for which we except a message */
-               ike_auth = ike_auth_create(this->ike_sa, 1);
+               ike_auth = ike_auth_create(this->ike_sa);
+               ike_auth->set_config(ike_auth, this->connection, this->policy);
+               this->connection = NULL;
+               this->policy = NULL;
                ike_auth->set_nonces(ike_auth,
                                                         chunk_clone(this->nonce_i),
                                                         chunk_clone(this->nonce_r));
@@ -783,7 +827,8 @@ static status_t get_response(private_ike_sa_init_t *this,
                charon->event_queue->add_relative(charon->event_queue, job, timeout);
        }
        /* set new state */
-       this->ike_sa->set_state(this->ike_sa, SA_CONNECTING);
+       this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+       
        return SUCCESS;
 }
 
@@ -801,7 +846,6 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
        sa_payload_t *sa_payload = NULL;
        ke_payload_t *ke_payload = NULL;
        nonce_payload_t *nonce_payload = NULL;
-       policy_t *policy;
        status_t status;
        
        /* check message type */
@@ -815,7 +859,6 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
        /* allow setting of next transaction in other functions */
        this->next = next;
        
-       this->connection = this->ike_sa->get_connection(this->ike_sa);
        me = this->connection->get_my_host(this->connection);
        other = this->connection->get_other_host(this->connection);
        
@@ -915,11 +958,7 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
                
                /* we have to re-check if the others selection is valid */
                this->proposal = this->connection->select_proposal(this->connection, proposal_list);
-               while (proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
-               {
-                       proposal->destroy(proposal);
-               }
-               proposal_list->destroy(proposal_list);
+               destroy_proposal_list(proposal_list);
                
                if (this->proposal == NULL)
                {
@@ -966,13 +1005,18 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
                }
                if (this->ike_sa->is_natt_enabled(this->ike_sa))
                {
+                       /* update host in IKE_SA */
+                       me = this->ike_sa->get_my_host(this->ike_sa);
+                       me = me->clone(me);
                        me->set_port(me, IKEV2_NATT_PORT);
+                       this->ike_sa->set_my_host(this->ike_sa, me);
+                       other = this->ike_sa->get_other_host(this->ike_sa);
+                       other = other->clone(other);
                        other->set_port(other, IKEV2_NATT_PORT);
+                       this->ike_sa->set_other_host(this->ike_sa, other);
+                       
                        this->logger->log(this->logger, CONTROL|LEVEL1, "switching to port %d", IKEV2_NATT_PORT);
                }
-               policy = this->ike_sa->get_policy(this->ike_sa);
-               policy->update_my_ts(policy, me);
-               policy->update_other_ts(policy, other);
        }
        
        /* because we are original initiator we have to update the responder SPI to the new one */
@@ -980,16 +1024,20 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
        ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
        
        /* derive all the keys used in the IKE_SA */
-       if (this->ike_sa->build_transforms(this->ike_sa, this->proposal, 
-                                                                          this->diffie_hellman, 
-                                                                          this->nonce_i, this->nonce_r,
-                                                                          TRUE) != SUCCESS)
+       if (this->ike_sa->derive_keys(this->ike_sa, this->proposal, 
+                                                                 this->diffie_hellman, 
+                                                                 this->nonce_i, this->nonce_r,
+                                                                 TRUE, NULL, NULL) != SUCCESS)
        {
                this->logger->log(this->logger, AUDIT, 
                                                  "transform objects could not be created from selected proposal, deleting IKE_SA");
                return DESTROY_ME;
        }
        
+       this->ike_sa->set_lifetimes(this->ike_sa, 
+                                       this->connection->get_soft_lifetime(this->connection),
+                                       this->connection->get_hard_lifetime(this->connection));
+       
        {       /* create ike_auth transaction, which will continue IKE_SA setup */
                chunk_t request_chunk, response_chunk;
                ike_auth_t *ike_auth;
@@ -998,7 +1046,10 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
                response_chunk = response->get_packet_data(response);
                
                /* create next transaction, for which we except a message */
-               ike_auth = ike_auth_create(this->ike_sa, this->message_id + 1);
+               ike_auth = ike_auth_create(this->ike_sa);
+               ike_auth->set_config(ike_auth, this->connection, this->policy);
+               this->connection = NULL;
+               this->policy = NULL;
                ike_auth->set_nonces(ike_auth,
                                                         chunk_clone(this->nonce_i),
                                                         chunk_clone(this->nonce_r));
@@ -1011,18 +1062,11 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response,
 
 static void destroy(private_ike_sa_init_t *this)
 {
-       if (this->message)
-       {
-               this->message->destroy(this->message);
-       }
-       if (this->diffie_hellman)
-       {
-               this->diffie_hellman->destroy(this->diffie_hellman);
-       }
-       if (this->proposal)
-       {
-               this->proposal->destroy(this->proposal);
-       }
+       DESTROY_IF(this->message);
+       DESTROY_IF(this->diffie_hellman);
+       DESTROY_IF(this->proposal);
+       DESTROY_IF(this->connection);
+       DESTROY_IF(this->policy);
        chunk_free(&this->nonce_i);
        chunk_free(&this->nonce_r);
        this->randomizer->destroy(this->randomizer);
@@ -1035,7 +1079,7 @@ static void destroy(private_ike_sa_init_t *this)
 /*
  * Described in header.
  */
-ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa, u_int32_t message_id)
+ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa)
 {
        private_ike_sa_init_t *this = malloc_thing(private_ike_sa_init_t);
 
@@ -1048,17 +1092,19 @@ ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa, u_int32_t message_id)
        this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
        
        /* public functions */
+       this->public.set_config = (void(*)(ike_sa_init_t*,connection_t*,policy_t*))set_config;
        this->public.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group;
        
        /* private data */
        this->ike_sa = ike_sa;
-       this->message_id = message_id;
+       this->message_id = 0;
        this->message = NULL;
        this->requested = 0;
        this->diffie_hellman = NULL;
        this->nonce_i = CHUNK_INITIALIZER;
        this->nonce_r = CHUNK_INITIALIZER;
        this->connection = NULL;
+       this->policy = NULL;
        this->proposal = NULL;
        this->randomizer = randomizer_create();
        this->nat_hasher = hasher_create(HASH_SHA1);
@@ -1069,6 +1115,6 @@ ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa, u_int32_t message_id)
        this->natd_src_matched = FALSE;
        this->natd_dst_matched = FALSE;
        this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
-
+       
        return &this->public;
 }