ikev2: fix DNS resolution overflow
[vpp.git] / src / plugins / ikev2 / ikev2.c
index aaebf62..f4bba15 100644 (file)
@@ -26,6 +26,7 @@
 #include <vnet/ipip/ipip.h>
 #include <plugins/ikev2/ikev2.h>
 #include <plugins/ikev2/ikev2_priv.h>
+#include <plugins/dns/dns.h>
 #include <openssl/sha.h>
 #include <vnet/ipsec/ipsec_punt.h>
 #include <plugins/ikev2/ikev2.api_enum.h>
@@ -109,14 +110,14 @@ typedef enum
 
 typedef u32 ikev2_non_esp_marker;
 
-static_always_inline u16
-ikev2_get_port (ikev2_sa_t * sa)
+static u16
+ikev2_get_port (ikev2_sa_t *sa)
 {
   return ikev2_natt_active (sa) ? IKEV2_PORT_NATT : IKEV2_PORT;
 }
 
-static_always_inline int
-ikev2_insert_non_esp_marker (ike_header_t * ike, int len)
+static int
+ikev2_insert_non_esp_marker (ike_header_t *ike, int len)
 {
   memmove ((u8 *) ike + sizeof (ikev2_non_esp_marker), ike, len);
   clib_memset (ike, 0, sizeof (ikev2_non_esp_marker));
@@ -637,8 +638,8 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
   vec_free (keymat);
 }
 
-static_always_inline u8 *
-ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t * ia, u16 port)
+static u8 *
+ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t *ia, u16 port)
 {
   const u32 max_buf_size =
     sizeof (ispi) + sizeof (rspi) + sizeof (ip6_address_t) + sizeof (u16);
@@ -1020,8 +1021,8 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike,
   return plaintext;
 }
 
-static_always_inline int
-ikev2_is_id_equal (ikev2_id_t * i1, ikev2_id_t * i2)
+static int
+ikev2_is_id_equal (ikev2_id_t *i1, ikev2_id_t *i2)
 {
   if (i1->type != i2->type)
     return 0;
@@ -1799,9 +1800,9 @@ ikev2_sa_auth_init (ikev2_sa_t * sa)
 
   if (sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC)
     {
-      vec_free (sa->i_auth.data);
       key_pad = format (0, "%s", IKEV2_KEY_PAD);
       psk = ikev2_calc_prf (tr_prf, sa->i_auth.data, key_pad);
+      vec_free (sa->i_auth.data);
       sa->i_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg);
       sa->i_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC;
       vec_free (psk);
@@ -2201,7 +2202,7 @@ typedef struct
   u32 sw_if_index;
 } ikev2_del_ipsec_tunnel_args_t;
 
-static_always_inline u32
+static u32
 ikev2_flip_alternate_sa_bit (u32 id)
 {
   u32 mask = 0x800;
@@ -2795,8 +2796,8 @@ ikev2_del_sa_init (u64 ispi)
                               sizeof (ispi));
 }
 
-static_always_inline void
-ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih)
+static void
+ikev2_rewrite_v6_addrs (ikev2_sa_t *sa, ip6_header_t *ih)
 {
   if (sa->is_initiator)
     {
@@ -2810,8 +2811,8 @@ ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih)
     }
 }
 
-static_always_inline void
-ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih)
+static void
+ikev2_rewrite_v4_addrs (ikev2_sa_t *sa, ip4_header_t *ih)
 {
   if (sa->is_initiator)
     {
@@ -2825,7 +2826,7 @@ ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih)
     }
 }
 
-static_always_inline void
+static void
 ikev2_set_ip_address (ikev2_sa_t *sa, const void *iaddr, const void *raddr,
                      const ip_address_family_t af)
 {
@@ -2880,7 +2881,7 @@ ikev2_update_stats (vlib_main_t *vm, u32 node_index, ikev2_stats_t *s)
                               s->n_sa_auth_req);
 }
 
-static_always_inline uword
+static uword
 ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                     vlib_frame_t *frame, u8 is_ip4, u8 natt)
 {
@@ -3720,16 +3721,16 @@ ikev2_set_local_key (vlib_main_t * vm, u8 * file)
   return 0;
 }
 
-static_always_inline vnet_api_error_t
-ikev2_register_udp_port (ikev2_profile_t * p, u16 port)
+static vnet_api_error_t
+ikev2_register_udp_port (ikev2_profile_t *p, u16 port)
 {
   ipsec_register_udp_port (port);
   p->ipsec_over_udp_port = port;
   return 0;
 }
 
-static_always_inline void
-ikev2_unregister_udp_port (ikev2_profile_t * p)
+static void
+ikev2_unregister_udp_port (ikev2_profile_t *p)
 {
   if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE)
     return;
@@ -3856,6 +3857,12 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p)
   vec_free (del_sai);
 }
 
+static void
+ikev2_profile_responder_free (ikev2_responder_t *r)
+{
+  vec_free (r->hostname);
+}
+
 static void
 ikev2_profile_free (ikev2_profile_t * p)
 {
@@ -3865,6 +3872,8 @@ ikev2_profile_free (ikev2_profile_t * p)
   if (p->auth.key)
     EVP_PKEY_free (p->auth.key);
 
+  ikev2_profile_responder_free (&p->responder);
+
   vec_free (p->loc_id.data);
   vec_free (p->rem_id.data);
 }
@@ -3986,8 +3995,8 @@ ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data,
   return 0;
 }
 
-static_always_inline void
-ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr)
+static void
+ikev2_set_ts_type (ikev2_ts_t *ts, const ip_address_t *addr)
 {
   if (ip_addr_version (addr) == AF_IP4)
     ts->ts_type = TS_IPV4_ADDR_RANGE;
@@ -3995,9 +4004,9 @@ ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr)
     ts->ts_type = TS_IPV6_ADDR_RANGE;
 }
 
-static_always_inline void
-ikev2_set_ts_addrs (ikev2_ts_t * ts, const ip_address_t * start,
-                   const ip_address_t * end)
+static void
+ikev2_set_ts_addrs (ikev2_ts_t *ts, const ip_address_t *start,
+                   const ip_address_t *end)
 {
   ip_address_copy (&ts->start_addr, start);
   ip_address_copy (&ts->end_addr, end);
@@ -4042,6 +4051,27 @@ ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id,
   return 0;
 }
 
+clib_error_t *
+ikev2_set_profile_responder_hostname (vlib_main_t *vm, u8 *name, u8 *hostname,
+                                     u32 sw_if_index)
+{
+  ikev2_profile_t *p;
+  clib_error_t *r;
+
+  p = ikev2_profile_index_by_name (name);
+
+  if (!p)
+    {
+      r = clib_error_return (0, "unknown profile %v", name);
+      return r;
+    }
+
+  p->responder.is_resolved = 0;
+  p->responder.sw_if_index = sw_if_index;
+  p->responder.hostname = vec_dup (hostname);
+
+  return 0;
+}
 
 clib_error_t *
 ikev2_set_profile_responder (vlib_main_t * vm, u8 * name,
@@ -4058,6 +4088,7 @@ ikev2_set_profile_responder (vlib_main_t * vm, u8 * name,
       return r;
     }
 
+  p->responder.is_resolved = 1;
   p->responder.sw_if_index = sw_if_index;
   ip_address_copy (&p->responder.addr, &addr);
 
@@ -4226,6 +4257,37 @@ ikev2_get_if_address (u32 sw_if_index, ip_address_family_t af,
   return 0;
 }
 
+static clib_error_t *
+ikev2_resolve_responder_hostname (vlib_main_t *vm, ikev2_responder_t *r)
+{
+  ikev2_main_t *km = &ikev2_main;
+  dns_cache_entry_t *ep = 0;
+  dns_pending_request_t _t0, *t0 = &_t0;
+  dns_resolve_name_t _rn, *rn = &_rn;
+  u8 *name;
+  int rv;
+
+  if (!km->dns_resolve_name)
+    return clib_error_return (0, "cannot load symbols from dns plugin");
+
+  t0->request_type = DNS_API_PENDING_NAME_TO_IP;
+  /* VPP main curse: IKEv2 uses only non-NULL terminated vectors internally
+   * whereas DNS resolver expects a NULL-terminated C-string */
+  name = vec_dup (r->hostname);
+  vec_terminate_c_string (name);
+  rv = km->dns_resolve_name (name, &ep, t0, rn);
+  vec_free (name);
+  if (rv < 0)
+    return clib_error_return (0, "dns lookup failure");
+
+  if (ep == 0)
+    return 0;
+
+  ip_address_copy (&r->addr, &rn->address);
+  r->is_resolved = 1;
+  return 0;
+}
+
 clib_error_t *
 ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
 {
@@ -4236,7 +4298,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
   ike_header_t *ike0;
   u32 bi0 = 0;
   int len = sizeof (ike_header_t), valid_ip = 0;
-  ip_address_t if_ip = ip_address_initializer;
+  ip_address_t src_if_ip = ip_address_initializer;
 
   p = ikev2_profile_index_by_name (name);
 
@@ -4246,15 +4308,26 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
       return r;
     }
 
-  if (p->responder.sw_if_index == ~0
-      || ip_address_is_zero (&p->responder.addr))
+  if (p->responder.sw_if_index == ~0 ||
+      (ip_address_is_zero (&p->responder.addr) &&
+       vec_len (p->responder.hostname) == 0))
     {
       r = clib_error_return (0, "responder not set for profile %v", name);
       return r;
     }
 
-  if (ikev2_get_if_address (p->responder.sw_if_index,
-                           ip_addr_version (&p->responder.addr), &if_ip))
+  if (!p->responder.is_resolved)
+    {
+      /* try to resolve using dns plugin
+       * success does not mean we have resolved the name */
+      r = ikev2_resolve_responder_hostname (vm, &p->responder);
+      if (r)
+       return r;
+    }
+
+  if (p->responder.is_resolved &&
+      ikev2_get_if_address (p->responder.sw_if_index,
+                           ip_addr_version (&p->responder.addr), &src_if_ip))
     {
       valid_ip = 1;
     }
@@ -4308,10 +4381,9 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
              sizeof (sa.childs[0].i_proposals[0].spi));
 
   /* Add NAT detection notification messages (mandatory) */
-  u8 *nat_detection_sha1 =
-    ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi),
-                           clib_host_to_net_u64 (sa.rspi),
-                           &if_ip, clib_host_to_net_u16 (IKEV2_PORT));
+  u8 *nat_detection_sha1 = ikev2_compute_nat_sha1 (
+    clib_host_to_net_u64 (sa.ispi), clib_host_to_net_u64 (sa.rspi), &src_if_ip,
+    clib_host_to_net_u16 (IKEV2_PORT));
 
   ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP,
                            nat_detection_sha1);
@@ -4365,7 +4437,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
   vec_add (sa.last_sa_init_req_packet_data, ike0, len);
 
   /* add data to the SA then add it to the pool */
-  ip_address_copy (&sa.iaddr, &if_ip);
+  ip_address_copy (&sa.iaddr, &src_if_ip);
   ip_address_copy (&sa.raddr, &p->responder.addr);
   sa.i_id.type = p->loc_id.type;
   sa.i_id.data = vec_dup (p->loc_id.data);
@@ -4388,8 +4460,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
 
   if (valid_ip)
     {
-      ikev2_send_ike (vm, &if_ip, &p->responder.addr, bi0, len,
-                     IKEV2_PORT, sa.dst_port, sa.sw_if_index);
+      ikev2_send_ike (vm, &src_if_ip, &p->responder.addr, bi0, len, IKEV2_PORT,
+                     sa.dst_port, sa.sw_if_index);
 
       ikev2_elog_exchange
        ("ispi %lx rspi %lx IKEV2_EXCHANGE_SA_INIT sent to ",
@@ -4397,14 +4469,6 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
         ip_addr_v4 (&p->responder.addr).as_u32,
         ip_addr_version (&p->responder.addr) == AF_IP4);
     }
-  else
-    {
-      r =
-       clib_error_return (0, "interface  %U does not have any IP address!",
-                          format_vnet_sw_if_index_name, vnet_get_main (),
-                          p->responder.sw_if_index);
-      return r;
-    }
 
   return 0;
 }
@@ -4732,15 +4796,19 @@ ikev2_init (vlib_main_t * vm)
                      "ikev2-ip4-natt");
   ikev2_cli_reference ();
 
+  km->dns_resolve_name =
+    vlib_get_plugin_symbol ("dns_plugin.so", "dns_resolve_name");
+  if (!km->dns_resolve_name)
+    ikev2_log_error ("cannot load symbols from dns plugin");
+
   km->log_level = IKEV2_LOG_ERROR;
   km->log_class = vlib_log_register_class ("ikev2", 0);
   return 0;
 }
 
 /* *INDENT-OFF* */
-VLIB_INIT_FUNCTION (ikev2_init) =
-{
-  .runs_after = VLIB_INITS("ipsec_init", "ipsec_punt_init"),
+VLIB_INIT_FUNCTION (ikev2_init) = {
+  .runs_after = VLIB_INITS ("ipsec_init", "ipsec_punt_init", "dns_init"),
 };
 /* *INDENT-ON* */
 
@@ -4928,15 +4996,44 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
 }
 
 static void
-ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa)
+ikev2_process_pending_sa_init_one (vlib_main_t *vm, ikev2_main_t *km,
+                                  ikev2_sa_t *sa)
 {
   ikev2_profile_t *p;
   u32 bi0;
   u8 *nat_sha, *np;
+  p = pool_elt_at_index (km->profiles, sa->profile_index);
+
+  if (!p->responder.is_resolved)
+    {
+      clib_error_t *r = ikev2_resolve_responder_hostname (vm, &p->responder);
+      if (r)
+       {
+         clib_error_free (r);
+         return;
+       }
+
+      if (!p->responder.is_resolved)
+       return;
+
+      ip_address_copy (&sa->raddr, &p->responder.addr);
+
+      /* update nat detection destination hash */
+      np = ikev2_find_ike_notify_payload (
+       (ike_header_t *) sa->last_sa_init_req_packet_data,
+       IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP);
+      if (np)
+       {
+         nat_sha = ikev2_compute_nat_sha1 (
+           clib_host_to_net_u64 (sa->ispi), clib_host_to_net_u64 (sa->rspi),
+           &sa->raddr, clib_host_to_net_u16 (sa->dst_port));
+         clib_memcpy_fast (np, nat_sha, vec_len (nat_sha));
+         vec_free (nat_sha);
+       }
+    }
 
   if (ip_address_is_zero (&sa->iaddr))
     {
-      p = pool_elt_at_index (km->profiles, sa->profile_index);
       if (!ikev2_get_if_address (p->responder.sw_if_index,
                                 ip_addr_version (&p->responder.addr),
                                 &sa->iaddr))
@@ -4973,7 +5070,7 @@ ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa)
 }
 
 static void
-ikev2_process_pending_sa_init (ikev2_main_t * km)
+ikev2_process_pending_sa_init (vlib_main_t *vm, ikev2_main_t *km)
 {
   u32 sai;
   u64 ispi;
@@ -4986,7 +5083,7 @@ ikev2_process_pending_sa_init (ikev2_main_t * km)
     if (sa->init_response_received)
       continue;
 
-    ikev2_process_pending_sa_init_one (km, sa);
+    ikev2_process_pending_sa_init_one (vm, km, sa);
   }));
   /* *INDENT-ON* */
 }
@@ -5046,8 +5143,8 @@ ikev2_disable_dpd (void)
   km->dpd_disabled = 1;
 }
 
-static_always_inline int
-ikev2_mngr_process_responder_sas (ikev2_sa_t * sa)
+static int
+ikev2_mngr_process_responder_sas (ikev2_sa_t *sa)
 {
   ikev2_main_t *km = &ikev2_main;
   vlib_main_t *vm = km->vlib_main;
@@ -5074,7 +5171,6 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
                       vlib_frame_t * f)
 {
   ikev2_main_t *km = &ikev2_main;
-  ipsec_main_t *im = &ipsec_main;
   ikev2_profile_t *p;
   ikev2_child_sa_t *c;
   u32 *sai;
@@ -5118,7 +5214,8 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
        vec_foreach (sai, to_be_deleted)
        {
          sa = pool_elt_at_index (tkm->sas, sai[0]);
-         u8 reinitiate = (sa->is_initiator && sa->profile_index != ~0);
+         const u32 profile_index = sa->profile_index;
+         const int reinitiate = (sa->is_initiator && profile_index != ~0);
          vec_foreach (c, sa->childs)
          {
            ikev2_delete_tunnel_interface (km->vnet_main, sa, c);
@@ -5130,7 +5227,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
 
          if (reinitiate)
            {
-             p = pool_elt_at_index (km->profiles, sa->profile_index);
+             p = pool_elt_at_index (km->profiles, profile_index);
              if (p)
                {
                  clib_error_t *e = ikev2_initiate_sa_init (vm, p->name);
@@ -5148,12 +5245,13 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
       /* process ipsec sas */
       ipsec_sa_t *sa;
       /* *INDENT-OFF* */
-      pool_foreach (sa, im->sad)  {
-        ikev2_mngr_process_ipsec_sa(sa);
-      }
+      pool_foreach (sa, ipsec_sa_pool)
+       {
+         ikev2_mngr_process_ipsec_sa (sa);
+       }
       /* *INDENT-ON* */
 
-      ikev2_process_pending_sa_init (km);
+      ikev2_process_pending_sa_init (vm, km);
     }
   return 0;
 }