diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-07-11 16:34:42 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-07-11 17:21:54 +0200 |
commit | 361df1f39aef70bb8ad8ab71f68e417685a53792 (patch) | |
tree | 35e6bdfdf629c45a0265a48bd9baa1c79d8dd6f1 | |
parent | cb0804a5f0ea650a8ab4068058801d7f17917294 (diff) |
noise: immediately rekey all peers after changing device private key
Reported-by: Derrick Pallas <derrick@pallas.us>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r-- | src/device.c | 4 | ||||
-rw-r--r-- | src/netlink.c | 10 | ||||
-rw-r--r-- | src/noise.c | 19 | ||||
-rw-r--r-- | src/noise.h | 7 | ||||
-rw-r--r-- | src/peer.c | 4 | ||||
-rwxr-xr-x | src/tests/netns.sh | 10 |
6 files changed, 47 insertions, 7 deletions
diff --git a/src/device.c b/src/device.c index d17dbf7..532d399 100644 --- a/src/device.c +++ b/src/device.c @@ -112,9 +112,7 @@ static int wg_stop(struct net_device *dev) wg_timers_stop(peer); wg_noise_handshake_clear(&peer->handshake); wg_noise_keypairs_clear(&peer->keypairs); - atomic64_set(&peer->last_sent_handshake, - ktime_get_coarse_boottime_ns() - - (u64)(REKEY_TIMEOUT + 1) * NSEC_PER_SEC); + wg_noise_reset_last_sent_handshake(&peer->last_sent_handshake); } mutex_unlock(&wg->device_update_lock); skb_queue_purge(&wg->incoming_handshakes); diff --git a/src/netlink.c b/src/netlink.c index a50eaa7..eb94f4d 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -13,6 +13,7 @@ #include <linux/if.h> #include <net/genetlink.h> #include <net/sock.h> +#include <crypto/algapi.h> static struct genl_family genl_family; @@ -546,6 +547,10 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info) u8 public_key[NOISE_PUBLIC_KEY_LEN]; struct wg_peer *peer, *temp; + if (!crypto_memneq(wg->static_identity.static_private, + private_key, NOISE_PUBLIC_KEY_LEN)) + goto skip_set_private_key; + /* We remove before setting, to prevent race, which means doing * two 25519-genpub ops. */ @@ -563,12 +568,15 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info) private_key); list_for_each_entry_safe(peer, temp, &wg->peer_list, peer_list) { - if (!wg_noise_precompute_static_static(peer)) + if (wg_noise_precompute_static_static(peer)) + wg_noise_expire_current_peer_keypairs(peer); + else wg_peer_remove(peer); } wg_cookie_checker_precompute_device_keys(&wg->cookie_checker); up_write(&wg->static_identity.lock); } +skip_set_private_key: if (info->attrs[WGDEVICE_A_PEERS]) { struct nlattr *attr, *peer[WGPEER_A_MAX + 1]; diff --git a/src/noise.c b/src/noise.c index a8f86df..57f749c 100644 --- a/src/noise.c +++ b/src/noise.c @@ -183,6 +183,25 @@ void wg_noise_keypairs_clear(struct noise_keypairs *keypairs) spin_unlock_bh(&keypairs->keypair_update_lock); } +void wg_noise_expire_current_peer_keypairs(struct wg_peer *peer) +{ + struct noise_keypair *keypair; + + wg_noise_handshake_clear(&peer->handshake); + wg_noise_reset_last_sent_handshake(&peer->last_sent_handshake); + + spin_lock_bh(&peer->keypairs.keypair_update_lock); + keypair = rcu_dereference_protected(peer->keypairs.next_keypair, + lockdep_is_held(&peer->keypairs.keypair_update_lock)); + if (keypair) + keypair->sending.is_valid = false; + keypair = rcu_dereference_protected(peer->keypairs.current_keypair, + lockdep_is_held(&peer->keypairs.keypair_update_lock)); + if (keypair) + keypair->sending.is_valid = false; + spin_unlock_bh(&peer->keypairs.keypair_update_lock); +} + static void add_new_keypair(struct noise_keypairs *keypairs, struct noise_keypair *new_keypair) { diff --git a/src/noise.h b/src/noise.h index 2533237..138a07b 100644 --- a/src/noise.h +++ b/src/noise.h @@ -100,11 +100,18 @@ bool wg_noise_handshake_init(struct noise_handshake *handshake, const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], struct wg_peer *peer); void wg_noise_handshake_clear(struct noise_handshake *handshake); +static inline void wg_noise_reset_last_sent_handshake(atomic64_t *handshake_ns) +{ + atomic64_set(handshake_ns, ktime_get_coarse_boottime_ns() - + (u64)(REKEY_TIMEOUT + 1) * NSEC_PER_SEC); +} + void wg_noise_keypair_put(struct noise_keypair *keypair, bool unreference_now); struct noise_keypair *wg_noise_keypair_get(struct noise_keypair *keypair); void wg_noise_keypairs_clear(struct noise_keypairs *keypairs); bool wg_noise_received_with_keypair(struct noise_keypairs *keypairs, struct noise_keypair *received_keypair); +void wg_noise_expire_current_peer_keypairs(struct wg_peer *peer); void wg_noise_set_static_identity_private_key( struct noise_static_identity *static_identity, @@ -56,9 +56,7 @@ struct wg_peer *wg_peer_create(struct wg_device *wg, rwlock_init(&peer->endpoint_lock); kref_init(&peer->refcount); skb_queue_head_init(&peer->staged_packet_queue); - atomic64_set(&peer->last_sent_handshake, - ktime_get_coarse_boottime_ns() - - (u64)(REKEY_TIMEOUT + 1) * NSEC_PER_SEC); + wg_noise_reset_last_sent_handshake(&peer->last_sent_handshake); set_bit(NAPI_STATE_NO_BUSY_POLL, &peer->napi.state); netif_napi_add(wg->dev, &peer->napi, wg_packet_rx_poll, NAPI_POLL_WEIGHT); diff --git a/src/tests/netns.sh b/src/tests/netns.sh index 7cbbfce..9edf12b 100755 --- a/src/tests/netns.sh +++ b/src/tests/netns.sh @@ -76,8 +76,10 @@ ip0 link add dev wg0 type wireguard ip0 link set wg0 netns $netns2 key1="$(pp wg genkey)" key2="$(pp wg genkey)" +key3="$(pp wg genkey)" pub1="$(pp wg pubkey <<<"$key1")" pub2="$(pp wg pubkey <<<"$key2")" +pub3="$(pp wg pubkey <<<"$key3")" psk="$(pp wg genpsk)" [[ -n $key1 && -n $key2 && -n $psk ]] @@ -221,6 +223,14 @@ kill $ncat_pid n1 wg set wg0 peer "$more_specific_key" remove [[ $(n1 wg show wg0 endpoints) == "$pub2 [::1]:9997" ]] +# Test that we can change private keys keys and immediately handshake +n1 wg set wg0 private-key <(echo "$key1") peer "$pub2" preshared-key <(echo "$psk") allowed-ips 192.168.241.2/32 endpoint 127.0.0.1:2 +n2 wg set wg0 private-key <(echo "$key2") listen-port 2 peer "$pub1" preshared-key <(echo "$psk") allowed-ips 192.168.241.1/32 +n1 ping -W 1 -c 1 192.168.241.2 +n1 wg set wg0 private-key <(echo "$key3") +n2 wg set wg0 peer "$pub3" preshared-key <(echo "$psk") allowed-ips 192.168.241.1/32 peer "$pub1" remove +n1 ping -W 1 -c 1 192.168.241.2 + ip1 link del wg0 ip2 link del wg0 |