]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
tcp ao: key change works
authorKaterina Kubecova <katerina.kubecova@nic.cz>
Fri, 23 Feb 2024 08:33:08 +0000 (09:33 +0100)
committerKaterina Kubecova <katerina.kubecova@nic.cz>
Fri, 23 Feb 2024 08:33:08 +0000 (09:33 +0100)
lib/socket.h
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c
sysdep/linux/sysio.h
sysdep/unix/io.c

index 044efdcd1571c6dbfe37fe6d32373fd036ec76ac..8cae88ee734dc5a94d2e18b5b404b878a7b1ae3e 100644 (file)
@@ -42,8 +42,7 @@ struct ao_key
   int remote_id;
   const char *cipher;
   const char *master_key;
-  int requested;
-  struct linpool *lp;
+  int required;
   struct ao_key *next_key;
 };
 
@@ -120,7 +119,9 @@ int sk_set_ttl(sock *s, int ttl);   /* Set transmit TTL for given socket */
 int sk_set_min_ttl(sock *s, int ttl);  /* Set minimal accepted TTL for given socket */
 int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey);
 
-int sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int passwd_id_loc, int passwd_id_rem, const char *cipher);
+int get_current_key_id(int sock_fd);
+int sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int passwd_id_loc, int passwd_id_rem, const char *cipher, int set_current);
+void ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_id_rem, int passwd_id_loc);
 void log_tcp_ao_info(int sock_fd);
 void log_tcp_ao_get_key(int sock_fd);
 void ao_try_change_master(int sock_fd, int next_key_id);
index 529076f0e521d08ecc05f1d013012cb95239ceea..321279993cfa5e63b0d99a8814e6a140fef53b24 100644 (file)
@@ -254,7 +254,7 @@ bgp_setup_auth(struct bgp_proto *p, int enable)
       do {
         rv = sk_set_ao_auth(p->sock->sk,
                             p->cf->local_ip, prefix, pxlen, p->cf->iface,
-                            key->master_key, key->local_id, key->remote_id, key->cipher);
+                            key->master_key, key->local_id, key->remote_id, key->cipher, 0);
        key = key->next_key;
 
       } while(key);
@@ -1117,10 +1117,9 @@ bgp_active(struct bgp_proto *p)
 void
 log_ao(int fd)
 {
-  log("the two ao logs");
+  //log("the two ao logs");
   log_tcp_ao_info(fd);
   log_tcp_ao_get_key(fd);
-  ao_try_change_master(fd, 101);
 }
 
 /**
@@ -2155,6 +2154,89 @@ bgp_postconfig(struct proto_config *CF)
   }
 }
 
+int compare_aos(struct ao_key *a, struct ao_key *b)
+{
+  if (a->local_id != b->local_id)
+    return 1;
+  if (a->remote_id != b->local_id)
+    return 1;
+  if (strcmp(a->cipher, b->cipher))
+    return 1;
+  return strcmp(a->master_key, b->master_key);
+}
+
+int reconfigure_tcp_ao(struct bgp_proto old_proto, struct bgp_config new)
+{
+  log("in reconf ao");
+  sock *s_passiv = old_proto.sock->sk;
+  sock *s_activ = old_proto.conn->sk;
+  int key_in_use = get_current_key_id(s_activ->fd);
+
+  if (key_in_use == -1)
+    {
+      log("Unable to detect currently used key");
+      return 0;
+    }
+
+  struct ao_key *old_aos[256];
+  memset(&old_aos, 0, sizeof(struct ao_key*)*256);
+  for(struct ao_key *ao_key = old_proto.cf->ao_key; ao_key; ao_key = ao_key->next_key)
+  {
+     old_aos[ao_key->local_id] = ao_key;
+  }
+  for(struct ao_key *ao_key = new.ao_key; ao_key; ao_key = ao_key->next_key)
+  {
+     if(old_aos[ao_key->local_id])
+     {
+       if(compare_aos(ao_key, old_aos[ao_key->local_id]))
+       {
+        struct ao_key *o = old_aos[ao_key->local_id];
+        log("%i %i %i %i %s %s %s %s", ao_key->local_id, o->local_id, ao_key->remote_id, o->remote_id, ao_key->cipher, o->cipher, ao_key->master_key, o->master_key);
+        if (ao_key->local_id == key_in_use)
+        {
+          //struct ao_key *o = old_aos[ao_key->local_id];
+          //log("%i %i %i %i %s %s %s %s", ao_key->local_id, o->local_id, ao_key->remote_id, o->remote_id, ao_key->cipher, o->cipher, ao_key->master_key, o->master_key);
+           log("Currently used master key part update. This is not allowed.");
+          return 0;
+        }
+         log("Reusing key id. Not nice. Lets try to update.");
+
+        struct ao_key *old_key = old_aos[ao_key->local_id];
+        ao_delete_key(s_activ,  old_proto.remote_ip, -1, s_activ->iface, old_key->local_id, old_key->remote_id);
+         ao_delete_key(s_passiv,  old_proto.remote_ip, -1, s_passiv->iface, old_key->local_id, old_key->remote_id);
+        sk_set_ao_auth(s_activ, old_proto.local_ip, old_proto.remote_ip, -1, s_activ->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1);
+         sk_set_ao_auth(s_passiv, old_proto.local_ip, old_proto.remote_ip, -1, s_passiv->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, 0);
+       }
+       old_aos[ao_key->local_id] = 0;
+     }
+     else
+     {
+       sk_set_ao_auth(s_activ, old_proto.local_ip, old_proto.remote_ip, -1, s_activ->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, ao_key->required == 1);
+       sk_set_ao_auth(s_passiv, old_proto.local_ip, old_proto.remote_ip, -1, s_passiv->iface, ao_key->master_key, ao_key->local_id, ao_key->remote_id, ao_key->cipher, 0);
+     }
+
+    if (ao_key->required == 1)
+    {
+      ao_try_change_master(s_activ->fd, ao_key->local_id); // or remote id?
+    }
+  }
+  for(int i = 0; i<256; i++)
+  {
+     if (old_aos[i])
+     {
+       if (i == key_in_use)
+       {
+         log("Currently used key deletion. This is not allowed.");
+        return 0;
+       } 
+       ao_delete_key(s_activ, old_proto.remote_ip, -1, s_activ->iface, old_aos[i]->local_id, old_aos[i]->remote_id);
+       ao_delete_key(s_passiv, old_proto.remote_ip, -1, s_passiv->iface, old_aos[i]->local_id, old_aos[i]->remote_id);
+     }
+  }
+  log("no changes in ao");
+  return 1;
+}
+
 static int
 bgp_reconfigure(struct proto *P, struct proto_config *CF)
 {
@@ -2170,6 +2252,7 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
                     // password item is last and must be checked separately
                     OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
     && !bstrcmp(old->password, new->password)
+    && reconfigure_tcp_ao(*p, *new)
     && ((!old->remote_range && !new->remote_range)
        || (old->remote_range && new->remote_range && net_equal(old->remote_range, new->remote_range)))
     && !bstrcmp(old->dynamic_name, new->dynamic_name)
index c57d2327ffb12a6937e7e5802db21f93722152a4..d117364b0f7c4ecbeb01c41e1b9af37ba7aa533b 100644 (file)
@@ -134,7 +134,8 @@ struct bgp_config {
   u32 disable_after_cease;             /* Disable it when cease is received, bitfield */
 
   const char *password;                        /* Password used for MD5 authentication */
-  struct ao_key *ao_key;               /* Keys for tcp ao authentication */
+  struct ao_key *ao_key;               /* Keys for tcp ao authentication TODO: copy to protocol? */
+  struct linpool *ao_lp;               /* Linpool for allocating ao keys */
   net_addr *remote_range;              /* Allowed neighbor range for dynamic BGP */
   const char *dynamic_name;            /* Name pattern for dynamic BGP */
   int dynamic_name_digits;             /* Minimum number of digits for dynamic names */
@@ -306,6 +307,7 @@ struct bgp_conn {
   u8 last_channel_count;               /* Number of times the last channel was used in succession */
   int notify_code, notify_subcode, notify_size;
   byte *notify_data;
+  int last_used_ao_key;                        /* ID of last ao authentication key, which was used */
 
   uint hold_time, keepalive_time;      /* Times calculated from my and neighbor's requirements */
 };
index 4e3d585649d46a3378389fdf5fdcbb152bb6c761..927c4cae2b40f9e9b947ca5f47e28c78a164d546 100644 (file)
@@ -193,7 +193,7 @@ bgp_proto:
  | bgp_proto ADVERTISE HOSTNAME bool ';' { BGP_CFG->enable_hostname = $4; }
  | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
  | bgp_proto PASSWORD text ';' { log("%s", $3); BGP_CFG->password = $3; }
- | bgp_proto AUTHENTICATE MANUAL '{' ao_keys '}'
+ | bgp_proto AUTHENTICATE MANUAL '{' ao_keys '}' tcp_ao_end
  | bgp_proto SETKEY bool ';' { BGP_CFG->setkey = $3; }
  | bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
  | bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
@@ -245,35 +245,61 @@ ao_key:
 
 ao_first_item:
     LOCAL ID expr ';' {
-      struct linpool *lp;
-      if (BGP_CFG->ao_key)
-        lp = BGP_CFG->ao_key->lp;
-      else
-        lp = lp_new(rp_new(&root_pool, "ao struct pool"));
-      struct ao_key *new_key = lp_alloc(lp, sizeof(struct ao_key));
-      new_key->lp = lp;
+      if (!BGP_CFG->ao_lp)
+        BGP_CFG->ao_lp = lp_new(rp_new(&root_pool, "ao struct pool"));
+      struct ao_key *new_key = lp_alloc(BGP_CFG->ao_lp, sizeof(struct ao_key));
       new_key->next_key = BGP_CFG->ao_key;
       BGP_CFG->ao_key = new_key;
-      BGP_CFG->ao_key->requested = 0;
+      BGP_CFG->ao_key->required = 0;
       BGP_CFG->ao_key->local_id = $3;
     }
 ;
 
 ao_item:
       REMOTE ID expr ';' {log("remote id %i", $3); BGP_CFG->ao_key->remote_id = $3; }
-    | CIPHER text ';' { char *c = lp_alloc(BGP_CFG->ao_key->lp, strlen($2));
-       memcpy(c, $2, strlen($2));
+    | CIPHER text ';' { char *c = lp_alloc(BGP_CFG->ao_lp, strlen($2)+1);
+       memcpy(c, $2, strlen($2)+1);
        BGP_CFG->ao_key->cipher = c;
        log("ciph[%s]", $2); }
     | MASTER KEY text ';' {
-       char *k = lp_alloc(BGP_CFG->ao_key->lp, strlen($3));
-       memcpy(k, $3, strlen($3));
+       char *k = lp_alloc(BGP_CFG->ao_lp, strlen($3)+1);
+       memcpy(k, $3, strlen($3)+1);
        BGP_CFG->ao_key->master_key = k;
        log("key[%s]", BGP_CFG->ao_key->master_key);}
-    | DEPRECATED ';' { BGP_CFG->ao_key->requested = -1; }
-    | REQUIRED ';' { BGP_CFG->ao_key->requested = 1; }
+    | DEPRECATED ';' { BGP_CFG->ao_key->required = -1; }
+    | REQUIRED ';' { BGP_CFG->ao_key->required = 1; }
  ;
 
+tcp_ao_end:
+{ //TODO this is not all what we need to check  - old current master same, cipher, key exist...
+  char used_aos_id_loc[256];
+  char used_aos_id_rem[256];
+  memset(used_aos_id_loc, 0, sizeof(char)*256);
+  memset(used_aos_id_rem, 0, sizeof(char)*256);
+
+  int required_found = 0;
+
+  struct ao_key *key = BGP_CFG->ao_key;
+  while (key)
+  {
+    if (used_aos_id_loc[key->local_id])
+      cf_error("Reused local key id");
+    used_aos_id_loc[key->local_id] = 1;
+    if (used_aos_id_rem[key->remote_id])
+      cf_error("Reused remote key id");
+    used_aos_id_rem[key->remote_id] = 1;
+    if (key->required == 1)
+    {
+      if (required_found)
+        cf_error("How do you want to use two keys at once? Check 'REQUIRED'");
+      required_found = 1;
+    }
+    key = key->next_key;
+  }
+  if (required_found == 0)
+    cf_error("Missing 'REQUIRED'. Which key should be used?");
+}
+
 bgp_channel_start: bgp_afi
 {
   const struct bgp_af_desc *desc = bgp_get_af_desc($1);
index 8d963ba81a5f644c8ebe024769d32f4faa4d5c01..97c1102e2a7c9fdf80a7d854bd045aa0dfa3c01f 100644 (file)
@@ -3423,8 +3423,9 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len)
 int
 bgp_rx(sock *sk, uint size)
 {
-  log_ao(sk->fd);
   struct bgp_conn *conn = sk->data;
+  //if (get_current_key_id(sk->fd) == conn->last_used_ao_key)  TODO: uncoment after debug
+    log_ao(sk->fd);
   byte *pkt_start = sk->rbuf;
   byte *end = pkt_start + size;
   uint i, len;
index 09b7d3f32dd7542103b9456555146ca05d90aa51..04d9315466d04b2e2ce46b85fda062205d29ee74 100644 (file)
@@ -238,6 +238,21 @@ void log_tcp_ao_info(int sock_fd)
                    tmp.current_key, tmp.rnext, tmp.set_current, tmp.ao_required, tmp.pkt_good, tmp.pkt_bad);
 }
 
+int get_current_key_id(int sock_fd)
+{
+  struct tcp_ao_info_opt_ext tmp;
+  memset(&tmp, 0, sizeof(struct tcp_ao_info_opt_ext));
+  socklen_t len = sizeof(tmp);
+
+  if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, len))
+  {
+     log("get current ao key failed %i", errno);
+     return -1;
+  }
+  else
+    return tmp.current_key;
+}
+
 void
 log_tcp_ao_get_key(int sock_fd)
 {
@@ -252,29 +267,43 @@ log_tcp_ao_get_key(int sock_fd)
      log("log tcp ao get keys failed with err code %i", errno);
      return;
   }
-  else
-    log("cipher %s key %s num of keys %i", tmp.alg_name, tmp.key, tmp.nkeys);
-
+  
+  int nkeys = tmp.nkeys;
+  struct tcp_ao_getsockopt_ext tm_all[nkeys];
+  memset(tm_all, 0, sizeof(struct tcp_ao_getsockopt_ext)*nkeys);
+  tm_all[0].nkeys = nkeys;
+  tm_all[0].get_all = 1;
+  if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, tm_all, &len))  // len should be still size of one struct. Because kernel net/ipv4/tcp_ao.c line 2165
+  {
+     log("log tcp ao get keys failed with err code %i", errno);
+     return;
+  }
+  log("keys %i %i", nkeys, tm_all[0].nkeys);
+  for (int i = 0; i < nkeys; i++)
+  {
+    log("sndid %i rcvid %i, %s %s, cipher %s key %s (%i/%i)", tm_all[i].sndid, tm_all[i].rcvid, tm_all[i].is_current ? "current" : "", tm_all[i].is_rnext ? "rnext" : "", tm_all[i].alg_name, tm_all[i].key, i+1, tm_all[0].nkeys);
+  }
 }
 
 int
-sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int passwd_id_loc, int passwd_id_rem, const char* cipher)
+sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int passwd_id_loc, int passwd_id_rem, const char* cipher, int set_current)
 {
   struct tcp_ao_add_ext ao;
   memset(&ao, 0, sizeof(struct tcp_ao_add_ext));
-  log("in sk set ao");
-  log("%s %i %i", passwd, passwd_id_loc, passwd_id_rem);
-  log("af %i %I %I (%i or %i) %s %i", s->af, remote, local, AF_INET, AF_INET6, passwd, passwd[0]);
+  log("in sk set ao, pass %s", passwd);
  /* int af;
   if (ipa_is_ip4(remote))
     af = AF_INET;
   else
-    af = AF_INET6;*/
+    a = AF_INET6;*/
   sockaddr_fill((sockaddr *) &ao.addr, s->af, remote, ifa, 0);
-  ao.set_current       = 0;
-  ao.set_rnext = 0;
+  if (set_current)
+  {
+    ao.set_rnext = 1;
+    ao.set_current = 1;
+  }
   if (pxlen >= 0)
-    ao.prefix  = pxlen;
+    ao.prefix = pxlen;
   else if(s->af == AF_INET)
     ao.prefix = 32;
   else
@@ -294,22 +323,42 @@ sk_set_ao_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *
   if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_ADD_KEY, &ao, sizeof(ao)) < 0)
     bug("tcp ao err %i", errno);
   
-  log_tcp_ao_info(s->fd);
+  log_tcp_ao_get_key(s->fd);
   return 0;
 }
 
+void
+ao_delete_key(sock *s, ip_addr remote, int pxlen, struct iface *ifa, int passwd_id_loc, int passwd_id_rem)
+{
+  struct tcp_ao_del_ext del;
+  memset(&del, 0, sizeof(struct tcp_ao_del_ext));
+  sockaddr_fill((sockaddr *) &del.addr, s->af, remote, ifa, 0);
+  del.sndid = passwd_id_rem;
+  del.rcvid = passwd_id_loc;
+  if (pxlen >= 0)
+    del.prefix = pxlen;
+  else if(s->af == AF_INET)
+    del.prefix = 32;
+  else
+    del.prefix = 128;
+  int IPPROTO_TCP_ = 6;
+  if (setsockopt(s->fd, IPPROTO_TCP, TCP_AO_DEL_KEY, &del, sizeof(del)) < 0)
+    bug("tcp ao deletion err %i", errno);
+  log("tcp ao key %i %i deleted", passwd_id_loc, passwd_id_rem);
+}
+
 void
 ao_try_change_master(int sock_fd, int next_master_id )
 {
   struct tcp_ao_info_opt_ext tmp;
   memset(&tmp, 0, sizeof(struct tcp_ao_info_opt_ext));
-  socklen_t len = sizeof(tmp);
   tmp.set_rnext = 1;
   tmp.rnext = next_master_id;
 
-  if (setsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len))
+  if (setsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, sizeof(tmp)))
   {
      log(" tcp ao change master key failed with err code %i", errno);
+     log_tcp_ao_get_key(sock_fd);
      return;
   }
   else
index 18d1317269fa72cb527bbcfa2d9201d77bedd23f..12986ee8a2fb570ffa638ac1951b93d6363fde1a 100644 (file)
@@ -1465,15 +1465,15 @@ sk_open(sock *s)
     log("set ao, %s", s->ao_key->cipher);
     struct ao_key *key = s->ao_key;
     do {
-      if (sk_set_ao_auth(s, s->saddr, s->daddr, -1, s->iface, key->master_key, key->local_id, key->remote_id, key->cipher) < 0)
+      if (sk_set_ao_auth(s, s->saddr, s->daddr, -1, s->iface, key->master_key, key->local_id, key->remote_id, key->cipher, key->required == 1) < 0)
         goto err;
       key = key->next_key;
     } while (key);
   }
-  if (s->password)
+  else if (s->password)
   {
     log("set md5");
-    if (sk_set_ao_auth(s, s->saddr, s->daddr, -1, s->iface, s->password, 123, 123, 0) < 0)
+    if (sk_set_md5_auth(s, s->saddr, s->daddr, -1, s->iface, s->password, 0) < 0)
       goto err;
   }
   else