diff --git a/doc/bird.sgml b/doc/bird.sgml index 09da89df..a48500c3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -2433,6 +2433,38 @@ protocol bfd [<name>] { offers better resistance to replay attacks but may require more computation. + authentication keyed tcp AO + This authentication is similar to md5, but enables changing keys on living connection. + Key change is done via reconfiguring. + + Key configuration of one key consists of two ids - one for local and one for remote machine. + The ids may, but does not have to be the same and must be in range 0 - 255. Among keys + on one protocol the local ids must be unique and the remote ids must be unique. + + Used cryphtographic algorithm must be specified for each key. + Possible ciphers are "cmac(aes128)", "hmac(md5)" "hmac(sha1)", "hmac(sha224)", + "hmac(sha256)", "hmac(sha384)" and "hmac(sha512)". And, of course, there must + be specified a string password. + + One key must be marked as "required". This key will be send as rnext key. + That means, if the other site know the required key, it uses the key for next packet. + + In order to delete a currently used key (key which is required by the other site), + it is possible to mark the key as "deprecated". This key will be deleted first time + the other site requires another key. + + Deleting a curently used key in config causes restart of the protocol. + The problem of directly deleting current key is, that we could treat such deleted key as deprecated, + but only until the protocol restarts. If it restarts, key is lost. + For example, connection is established. Then one side decides to remove current key and requires a newly added key. + The other side does not know the new key yet. For now, this is not problem, they still use the old key. + But, at this moment, some unexpected error occures at the first site. + It restarts, but it does not have the old key in config. One site does not have old key, + the other new key and triing other keys than required ones is not supported. + + Editing existing keys (except of marking them "required" or "deprecated") + is not reccomended and leads to restarting protocol. + password "text" Specifies a password used for authentication. See common option for detailed description. Note that diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 2d1ea8d3..147ffd2f 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1296,19 +1296,23 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED) if (p->ao_key) { - if (check_ao_keys_id(sk->fd, p->ao_key) == 0) - { - sk->use_ao = 1; - for (struct bgp_ao_key *key = p->ao_key; key; key = key->next_key) - { - key->activ_alive = key->passiv_alive; - if (key->key.required == 1) - { - sk->desired_ao_key = key->key.remote_id; - ao_try_change_master(sk, key->key.local_id, key->key.remote_id); - } - } - } + if (check_ao_keys_id(sk->fd, p->ao_key) == 0) + { + sk->use_ao = 1; + for (struct bgp_ao_key *key = p->ao_key; key; key = key->next_key) + { + key->activ_alive = key->passiv_alive; + if (key->key.required == 1) + { + sk->desired_ao_key = key->key.remote_id; + ao_try_change_master(sk, key->key.local_id, key->key.remote_id); + } + else if (key->key.required == -1) + { + sk->proto_del_ao_key = p; + } + } + } } if (p->cf->enable_extended_messages) { @@ -2272,14 +2276,16 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new) if (compare_aos(&old_ao->key, &cf_ao->key)) return 0; if (old_ao->activ_alive == 0 && cf_ao->key.required >= 0) - { - if (sk_set_ao_auth(s_activ, old_proto->local_ip, old_proto->remote_ip, -1, s_activ->iface, old_ao->key.master_key, old_ao->key.local_id, old_ao->key.remote_id, old_ao->key.cipher, 0)) + { + if (sk_set_ao_auth(s_activ, old_proto->local_ip, old_proto->remote_ip, -1, s_activ->iface, + old_ao->key.master_key, old_ao->key.local_id, old_ao->key.remote_id, old_ao->key.cipher, 0)) return 0; old_ao->activ_alive = 1; - } + } if (old_ao->passiv_alive == 0 && cf_ao->key.required >= 0) { - if (sk_set_ao_auth(s_passiv, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, old_ao->key.master_key, old_ao->key.local_id, old_ao->key.remote_id, old_ao->key.cipher, 0)) + if (sk_set_ao_auth(s_passiv, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, + old_ao->key.master_key, old_ao->key.local_id, old_ao->key.remote_id, old_ao->key.cipher, 0)) return 0; old_ao->passiv_alive = 1; } @@ -2290,11 +2296,12 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new) s_passiv->desired_ao_key = old_ao->key.remote_id; ao_try_change_master(s_activ, old_ao->key.local_id, old_ao->key.remote_id); if (old_proto->conn->hold_timer->expires != 0) - bgp_schedule_packet(old_proto->conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. RFC says we should wait, but since reconfiguration is rare, this is harmless. + bgp_schedule_packet(old_proto->conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. + // RFC says we should wait, but since reconfiguration is rare, this is harmless. } old_ao->key = cf_ao->key; - old_ao->to_delete = 0; - found = old_ao; + old_ao->to_delete = 0; + found = old_ao; } } if (!found) @@ -2305,10 +2312,12 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new) key->passiv_alive = 0; key->next_key = first; old_proto->ao_key = key; - if (sk_set_ao_auth(s_passiv, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, cf_ao->key.master_key, cf_ao->key.local_id, cf_ao->key.remote_id, cf_ao->key.cipher, 0)) + if (sk_set_ao_auth(s_passiv, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, + cf_ao->key.master_key, cf_ao->key.local_id, cf_ao->key.remote_id, cf_ao->key.cipher, 0)) return 0; key->passiv_alive = 1; - if (sk_set_ao_auth(s_activ, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, cf_ao->key.master_key, cf_ao->key.local_id, cf_ao->key.remote_id, cf_ao->key.cipher, 0)) + if (sk_set_ao_auth(s_activ, old_proto->local_ip, old_proto->remote_ip, -1, s_passiv->iface, + cf_ao->key.master_key, cf_ao->key.local_id, cf_ao->key.remote_id, cf_ao->key.cipher, 0)) return 0; key->activ_alive = 1; key->to_delete = 0; @@ -2320,7 +2329,8 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new) s_passiv->desired_ao_key = found->key.remote_id; ao_try_change_master(s_activ, found->key.local_id, found->key.remote_id); if (old_proto->conn->hold_timer->expires != 0) - bgp_schedule_packet(old_proto->conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. RFC says we should wait, but since reconfiguration is rare, this is harmless. + bgp_schedule_packet(old_proto->conn, NULL, PKT_KEEPALIVE); // We might send this keepalive shortly after another. + // RFC says we should wait, but since reconfiguration is rare, this is harmless. } } } @@ -2334,7 +2344,7 @@ int reconfigure_tcp_ao(struct bgp_proto *old_proto, struct bgp_config new) if (old_ao->key.remote_id == key_in_use_rem) { log(L_WARN "TCP AO: deleting currently used key"); - return 0; + return 0; } if (ao_delete_key(s_activ, old_proto->remote_ip, -1, s_activ->iface, old_ao->key.local_id, old_ao->key.remote_id)) return 0; diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 360b6124..25542af9 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -3410,22 +3410,30 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len) } } -void -delete_deprecated_key(sock *sk, struct bgp_proto *p, int key_rem_id) +int +delete_deprecated_keys(sock *sk, struct bgp_proto *p, int new_rnext) { struct bgp_ao_key *key = p->ao_key; - while (key->key.remote_id != key_rem_id) + int ret = 1; + while (key) { + if (key->key.required == -1) + { + if (new_rnext == key->key.remote_id) + ret = 0; + else + { + if (ao_delete_key(sk, p->remote_ip, -1, sk->iface, key->key.local_id, key->key.remote_id)) + bug("TCP AO: Can not delete deprecated key %i %i on socket %i", key->key.local_id, key->key.remote_id, sk->fd); + key->activ_alive = 0; + if (ao_delete_key(p->sock->sk, p->remote_ip, -1, p->sock->sk->iface, key->key.local_id, key->key.remote_id)) + bug("TCP AO: Can not delete deprecated key %i %i on socket %i", key->key.local_id, key->key.remote_id, p->sock->sk->fd); + key->passiv_alive = 0; + } + } key = key->next_key; } - if (key->key.required != -1) - bug("TCP AO: unexpected key management error"); - if (ao_delete_key(sk, p->remote_ip, -1, sk->iface, key->key.local_id, key->key.remote_id)) - bug("TCP AO: Can not delete deprecated key %i %i on socket %i", key->key.local_id, key->key.remote_id, sk->fd); - key->activ_alive = 0; - if (ao_delete_key(p->sock->sk, p->remote_ip, -1, p->sock->sk->iface, key->key.local_id, key->key.remote_id)) - bug("TCP AO: Can not delete deprecated key %i %i on socket %i", key->key.local_id, key->key.remote_id, p->sock->sk->fd); - key->passiv_alive = 0; + return ret; } /** @@ -3454,8 +3462,8 @@ bgp_rx(sock *sk, uint size) if (sk->proto_del_ao_key && sk->desired_ao_key == new_rnext) { - delete_deprecated_key(sk, sk->proto_del_ao_key, sk->last_used_ao_key); - sk->proto_del_ao_key = NULL; + if (delete_deprecated_keys(sk, sk->proto_del_ao_key, new_rnext)) + sk->proto_del_ao_key = NULL; } sk->last_used_ao_key = new_rnext; } diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index a6f0fe12..53c82018 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -217,7 +217,6 @@ void log_tcp_ao_info(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); - log("socket: fd %i", sock_fd); if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len)) { @@ -225,8 +224,8 @@ void log_tcp_ao_info(int sock_fd) return; } else - log(L_INFO "TCP AO:\ncurrent key id %i (rem), next key %i (loc),\n set current %i, is ao required %i\n good packets %i, bad packets %i", - tmp.current_key, tmp.rnext, tmp.set_current, tmp.ao_required, tmp.pkt_good, tmp.pkt_bad); + log(L_INFO "TCP AO on socket %i:\ncurrent key id %i (rem), next key %i (loc),\n set current %i, is ao required %i\n good packets %i, bad packets %i", + 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) @@ -237,8 +236,8 @@ int get_current_key_id(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_INFO, &tmp, &len)) { - log(L_WARN "TCP AO: Getting current ao key for socket file descriptor %i failed with errno %i", sock_fd, errno); - return -1; + log(L_WARN "TCP AO: Getting current ao key for socket file descriptor %i failed with errno %i", sock_fd, errno); + return -1; } else return tmp.current_key; @@ -269,8 +268,8 @@ int get_num_ao_keys(int sock_fd) if (getsockopt(sock_fd, IPPROTO_TCP, TCP_AO_GET_KEYS, &tmp, &len)) { - log(L_WARN "TCP AO: get keys on socket fd %i failed with err code %i", sock_fd, errno); - return -1; + log(L_WARN "TCP AO: get keys on socket fd %i failed with err code %i", sock_fd, errno); + return -1; } return tmp.nkeys; } @@ -288,13 +287,15 @@ log_tcp_ao_get_key(int sock_fd) 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(L_WARN "TCP AO: getting keys on socket fd %i failed with err code %i", sock_fd, errno); - return; + log(L_WARN "TCP AO: getting keys on socket fd %i failed with err code %i", sock_fd, errno); + return; } log(L_INFO "TCP AO on socket fd %i has %i keys", tm_all[0].nkeys); for (int i = 0; i < nkeys; i++) { - log(L_INFO "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); + log(L_INFO "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); } } @@ -411,7 +412,7 @@ int check_ao_keys_id(int sock_fd, struct bgp_ao_key *keys) for (struct bgp_ao_key *key = keys; key; key = key->next_key) expected_keys[key->key.local_id] = key->key.remote_id + 1; // the + 1 because we do not want 0 id be 0 int nkeys = get_num_ao_keys(sock_fd); - if(nkeys == -1) + if (nkeys == -1) { log(L_WARN "TCP AO: unable to get num of keys"); return 1; @@ -434,7 +435,7 @@ int check_ao_keys_id(int sock_fd, struct bgp_ao_key *keys) if (expected_keys[sock_key.rcvid] == 0) log(L_WARN "TCP AO: unexpected ao key %i %i", sock_key.rcvid, sock_key.sndid); else - log(L_WARN "TCP AO: expected key local id %i has different remote id than expected (%i vs %i)", sock_key.rcvid, expected_keys[sock_key.rcvid] - 1, sock_key.sndid); + log(L_WARN "TCP AO: expected key local id %i has different remote id than expected (%i vs %i)", sock_key.rcvid, expected_keys[sock_key.rcvid] - 1, sock_key.sndid); errors++; } expected_keys[sock_key.rcvid] = 0;