ike: For NATted connections use host from traffic selector
authorThomas Egerer <thomas.egerer@secunet.com>
Thu, 22 Mar 2018 17:17:48 +0000 (18:17 +0100)
committerTobias Brunner <tobias@strongswan.org>
Tue, 3 Apr 2018 11:51:15 +0000 (13:51 +0200)
When resolving dynamic hosts with no pool configured, the code used to
replace the remote IP with the peer's perceived address. This fails for road
warriors behind NAT. Hence this patch uses the address from the remote
traffic selector proposed by the peer if NAT was detected and the
address(es) proposed are /32 or /128 subnets to the same host.

Signed-off-by: Thomas Egerer <thomas.egerer@secunet.com>
src/libcharon/sa/ikev1/tasks/quick_mode.c
src/libcharon/sa/ikev2/tasks/child_create.c

index 77592e5..1ec88be 100644 (file)
@@ -245,13 +245,32 @@ static bool have_pool(ike_sa_t *ike_sa)
 }
 
 /**
+ * Get the host from the first (only) traffc selector if peer is behind a NAT
+ */
+static host_t *get_nat_host(linked_list_t *proposed)
+{
+       host_t *host = NULL;
+       traffic_selector_t *ts;
+       uint8_t mask;
+
+       if ((proposed->get_first(proposed, (void**)&ts) == SUCCESS) &&
+               ts->is_host(ts, NULL))
+       {
+               ts->to_subnet(ts, &host, &mask);
+       }
+       return host;
+}
+
+/**
  * Get hosts to use for dynamic traffic selectors
  */
-static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
+static linked_list_t *get_dynamic_hosts(private_quick_mode_t *this,
+                                                                           bool local, linked_list_t *proposed)
 {
+       ike_sa_t *ike_sa = this->ike_sa;
        enumerator_t *enumerator;
        linked_list_t *list;
-       host_t *host;
+       host_t *host = NULL;
 
        list = linked_list_create();
        enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
@@ -261,16 +280,26 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
        }
        enumerator->destroy(enumerator);
 
-       if (list->get_count(list) == 0)
+       if (!list->get_count(list))
        {       /* no virtual IPs assigned */
                if (local)
                {
                        host = ike_sa->get_my_host(ike_sa);
-                       list->insert_last(list, host);
                }
                else if (!have_pool(ike_sa))
                {       /* use host only if we don't have a pool configured */
-                       host = ike_sa->get_other_host(ike_sa);
+                       if (proposed && this->mode == MODE_TUNNEL &&
+                               ike_sa->has_condition(ike_sa, COND_NAT_THERE))
+                       {
+                               host = get_nat_host(proposed);
+                       }
+                       if (!host)
+                       {
+                               host = ike_sa->get_other_host(ike_sa);
+                       }
+               }
+               if (host)
+               {
                        list->insert_last(list, host);
                }
        }
@@ -542,7 +571,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
        traffic_selector_t *ts;
        linked_list_t *list, *hosts;
 
-       hosts = get_dynamic_hosts(this->ike_sa, local);
+       hosts = get_dynamic_hosts(this, local, supplied);
        list = this->config->get_traffic_selectors(this->config,
                                                                                           local, supplied, hosts);
        hosts->destroy(hosts);
@@ -1088,8 +1117,8 @@ METHOD(task_t, process_r, status_t,
                        tsi = linked_list_create_with_items(this->tsi, NULL);
                        tsr = linked_list_create_with_items(this->tsr, NULL);
                        this->tsi = this->tsr = NULL;
-                       hostsi = get_dynamic_hosts(this->ike_sa, FALSE);
-                       hostsr = get_dynamic_hosts(this->ike_sa, TRUE);
+                       hostsi = get_dynamic_hosts(this, FALSE, tsi);
+                       hostsr = get_dynamic_hosts(this, TRUE, NULL);
                        this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
                                                                                                          hostsr, hostsi);
                        hostsi->destroy(hostsi);
index c90af23..78603d4 100644 (file)
@@ -389,13 +389,43 @@ static bool have_pool(ike_sa_t *ike_sa)
 }
 
 /**
+ * Return the single host proposed by the peer if it is behind a NAT
+ */
+static host_t *get_nat_host(linked_list_t *supplied)
+{
+       enumerator_t *enumerator;
+       traffic_selector_t *ts;
+       host_t *host = NULL;
+       uint8_t mask;
+
+       enumerator = supplied->create_enumerator(supplied);
+       while (enumerator->enumerate(enumerator, &ts))
+       {       /* all supplied traffic selectors must be to the same host */
+               if (!ts->is_host(ts, host))
+               {
+                       DESTROY_IF(host);
+                       host = NULL;
+                       break;
+               }
+               if (!host)
+               {
+                       ts->to_subnet(ts, &host, &mask);
+               }
+       }
+       enumerator->destroy(enumerator);
+       return host;
+}
+
+/**
  * Get hosts to use for dynamic traffic selectors
  */
-static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
+static linked_list_t *get_dynamic_hosts(private_child_create_t *this,
+                                                                           bool local, linked_list_t *supplied)
 {
+       ike_sa_t *ike_sa = this->ike_sa;
        enumerator_t *enumerator;
        linked_list_t *list;
-       host_t *host;
+       host_t *host = NULL;
 
        list = linked_list_create();
        enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
@@ -405,16 +435,26 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
        }
        enumerator->destroy(enumerator);
 
-       if (list->get_count(list) == 0)
+       if (!list->get_count(list))
        {       /* no virtual IPs assigned */
                if (local)
                {
                        host = ike_sa->get_my_host(ike_sa);
-                       list->insert_last(list, host);
                }
                else if (!have_pool(ike_sa))
                {       /* use host only if we don't have a pool configured */
-                       host = ike_sa->get_other_host(ike_sa);
+                       if (supplied && this->mode == MODE_TUNNEL &&
+                               ike_sa->has_condition(ike_sa, COND_NAT_THERE))
+                       {
+                               host = get_nat_host(supplied);
+                       }
+                       if (!host)
+                       {
+                               host = ike_sa->get_other_host(ike_sa);
+                       }
+               }
+               if (host)
+               {
                        list->insert_last(list, host);
                }
        }
@@ -475,7 +515,7 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
        ike_condition_t cond;
 
        cond = local ? COND_NAT_HERE : COND_NAT_THERE;
-       hosts = get_dynamic_hosts(this->ike_sa, local);
+       hosts = get_dynamic_hosts(this, local, in);
 
        if (this->mode == MODE_TRANSPORT &&
                this->ike_sa->has_condition(this->ike_sa, cond))
@@ -1081,12 +1121,12 @@ METHOD(task_t, build_i, status_t,
        else
        {       /* no virtual IPs configured */
                list->destroy(list);
-               list = get_dynamic_hosts(this->ike_sa, TRUE);
+               list = get_dynamic_hosts(this, TRUE, NULL);
                this->tsi = this->config->get_traffic_selectors(this->config,
                                                                                                                TRUE, NULL, list);
                list->destroy(list);
        }
-       list = get_dynamic_hosts(this->ike_sa, FALSE);
+       list = get_dynamic_hosts(this, FALSE, NULL);
        this->tsr = this->config->get_traffic_selectors(this->config,
                                                                                                        FALSE, NULL, list);
        list->destroy(list);
@@ -1269,8 +1309,8 @@ static child_cfg_t* select_child_cfg(private_child_create_t *this)
                tsr = get_ts_if_nat_transport(this, TRUE, this->tsr);
                tsi = get_ts_if_nat_transport(this, FALSE, this->tsi);
 
-               listr = get_dynamic_hosts(this->ike_sa, TRUE);
-               listi = get_dynamic_hosts(this->ike_sa, FALSE);
+               listr = get_dynamic_hosts(this, TRUE, NULL);
+               listi = get_dynamic_hosts(this, FALSE, this->tsi);
                child_cfg = peer_cfg->select_child_cfg(peer_cfg,
                                                                                        tsr ?: this->tsr, tsi ?: this->tsi,
                                                                                        listr, listi);