From c60c4e540ac3787efc3b7a444364ca21ebb45951 Mon Sep 17 00:00:00 2001 From: Katerina Kubecova Date: Fri, 23 Feb 2024 09:33:08 +0100 Subject: [PATCH] tcp ao: key change works --- lib/socket.h | 7 ++-- proto/bgp/bgp.c | 89 ++++++++++++++++++++++++++++++++++++++++++-- proto/bgp/bgp.h | 4 +- proto/bgp/config.Y | 52 +++++++++++++++++++------- proto/bgp/packets.c | 3 +- sysdep/linux/sysio.h | 77 +++++++++++++++++++++++++++++++------- sysdep/unix/io.c | 6 +-- 7 files changed, 200 insertions(+), 38 deletions(-) diff --git a/lib/socket.h b/lib/socket.h index 044efdcd..8cae88ee 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -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); diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 529076f0..5bbc96c3 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -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->remote_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) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index c57d2327..d117364b 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -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 */ }; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 4e3d5856..f3cac939 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -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)); + | CIPHER text ';' { char *c = lp_alloc(BGP_CFG->ao_lp, strlen($2)); memcpy(c, $2, strlen($2)); BGP_CFG->ao_key->cipher = c; log("ciph[%s]", $2); } | MASTER KEY text ';' { - char *k = lp_alloc(BGP_CFG->ao_key->lp, strlen($3)); + char *k = lp_alloc(BGP_CFG->ao_lp, strlen($3)); memcpy(k, $3, strlen($3)); 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); diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 8d963ba8..97c1102e 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -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; diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 09b7d3f3..a2e43182 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -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 diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 18d13172..12986ee8 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -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