summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-07-11 16:34:42 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2019-07-11 17:21:54 +0200
commit361df1f39aef70bb8ad8ab71f68e417685a53792 (patch)
tree35e6bdfdf629c45a0265a48bd9baa1c79d8dd6f1
parentcb0804a5f0ea650a8ab4068058801d7f17917294 (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.c4
-rw-r--r--src/netlink.c10
-rw-r--r--src/noise.c19
-rw-r--r--src/noise.h7
-rw-r--r--src/peer.c4
-rwxr-xr-xsrc/tests/netns.sh10
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,
diff --git a/src/peer.c b/src/peer.c
index b66e8b5..ffb911f 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -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