Merge branch 'wireguard-fixes'
Jason A. Donenfeld says: ==================== wireguard fixes for 5.7-rc7 Hopefully these are the last fixes for 5.7: 1) A trivial bump in the selftest harness to support gcc-10. build.wireguard.com is still on gcc-9 but I'll probably switch to gcc-10 in the coming weeks. 2) A concurrency fix regarding userspace modifying the pre-shared key at the same time as packets are being processed, reported by Matt Dunwoodie. 3) We were previously clearing skb->hash on egress, which broke fq_codel, cake, and other things that actually make use of the flow hash for queueing, reported by Dave Taht and Toke Høiland-Jørgensen. 4) A fix for the increased memory usage caused by (3). This can be thought of as part of patch (3), but because of the separate reasoning and breadth of it I thought made it a bit cleaner to put in a standalone commit. Fixes (2), (3), and (4) are -stable material. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
53cb09953c
|
@ -32,7 +32,7 @@ enum cookie_values {
|
|||
};
|
||||
|
||||
enum counter_values {
|
||||
COUNTER_BITS_TOTAL = 2048,
|
||||
COUNTER_BITS_TOTAL = 8192,
|
||||
COUNTER_REDUNDANT_BITS = BITS_PER_LONG,
|
||||
COUNTER_WINDOW_SIZE = COUNTER_BITS_TOTAL - COUNTER_REDUNDANT_BITS
|
||||
};
|
||||
|
|
|
@ -104,6 +104,7 @@ static struct noise_keypair *keypair_create(struct wg_peer *peer)
|
|||
|
||||
if (unlikely(!keypair))
|
||||
return NULL;
|
||||
spin_lock_init(&keypair->receiving_counter.lock);
|
||||
keypair->internal_id = atomic64_inc_return(&keypair_counter);
|
||||
keypair->entry.type = INDEX_HASHTABLE_KEYPAIR;
|
||||
keypair->entry.peer = peer;
|
||||
|
@ -358,25 +359,16 @@ out:
|
|||
memzero_explicit(output, BLAKE2S_HASH_SIZE + 1);
|
||||
}
|
||||
|
||||
static void symmetric_key_init(struct noise_symmetric_key *key)
|
||||
{
|
||||
spin_lock_init(&key->counter.receive.lock);
|
||||
atomic64_set(&key->counter.counter, 0);
|
||||
memset(key->counter.receive.backtrack, 0,
|
||||
sizeof(key->counter.receive.backtrack));
|
||||
key->birthdate = ktime_get_coarse_boottime_ns();
|
||||
key->is_valid = true;
|
||||
}
|
||||
|
||||
static void derive_keys(struct noise_symmetric_key *first_dst,
|
||||
struct noise_symmetric_key *second_dst,
|
||||
const u8 chaining_key[NOISE_HASH_LEN])
|
||||
{
|
||||
u64 birthdate = ktime_get_coarse_boottime_ns();
|
||||
kdf(first_dst->key, second_dst->key, NULL, NULL,
|
||||
NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0,
|
||||
chaining_key);
|
||||
symmetric_key_init(first_dst);
|
||||
symmetric_key_init(second_dst);
|
||||
first_dst->birthdate = second_dst->birthdate = birthdate;
|
||||
first_dst->is_valid = second_dst->is_valid = true;
|
||||
}
|
||||
|
||||
static bool __must_check mix_dh(u8 chaining_key[NOISE_HASH_LEN],
|
||||
|
@ -715,6 +707,7 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src,
|
|||
u8 e[NOISE_PUBLIC_KEY_LEN];
|
||||
u8 ephemeral_private[NOISE_PUBLIC_KEY_LEN];
|
||||
u8 static_private[NOISE_PUBLIC_KEY_LEN];
|
||||
u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN];
|
||||
|
||||
down_read(&wg->static_identity.lock);
|
||||
|
||||
|
@ -733,6 +726,8 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src,
|
|||
memcpy(chaining_key, handshake->chaining_key, NOISE_HASH_LEN);
|
||||
memcpy(ephemeral_private, handshake->ephemeral_private,
|
||||
NOISE_PUBLIC_KEY_LEN);
|
||||
memcpy(preshared_key, handshake->preshared_key,
|
||||
NOISE_SYMMETRIC_KEY_LEN);
|
||||
up_read(&handshake->lock);
|
||||
|
||||
if (state != HANDSHAKE_CREATED_INITIATION)
|
||||
|
@ -750,7 +745,7 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src,
|
|||
goto fail;
|
||||
|
||||
/* psk */
|
||||
mix_psk(chaining_key, hash, key, handshake->preshared_key);
|
||||
mix_psk(chaining_key, hash, key, preshared_key);
|
||||
|
||||
/* {} */
|
||||
if (!message_decrypt(NULL, src->encrypted_nothing,
|
||||
|
@ -783,6 +778,7 @@ out:
|
|||
memzero_explicit(chaining_key, NOISE_HASH_LEN);
|
||||
memzero_explicit(ephemeral_private, NOISE_PUBLIC_KEY_LEN);
|
||||
memzero_explicit(static_private, NOISE_PUBLIC_KEY_LEN);
|
||||
memzero_explicit(preshared_key, NOISE_SYMMETRIC_KEY_LEN);
|
||||
up_read(&wg->static_identity.lock);
|
||||
return ret_peer;
|
||||
}
|
||||
|
|
|
@ -15,18 +15,14 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/kref.h>
|
||||
|
||||
union noise_counter {
|
||||
struct {
|
||||
u64 counter;
|
||||
unsigned long backtrack[COUNTER_BITS_TOTAL / BITS_PER_LONG];
|
||||
spinlock_t lock;
|
||||
} receive;
|
||||
atomic64_t counter;
|
||||
struct noise_replay_counter {
|
||||
u64 counter;
|
||||
spinlock_t lock;
|
||||
unsigned long backtrack[COUNTER_BITS_TOTAL / BITS_PER_LONG];
|
||||
};
|
||||
|
||||
struct noise_symmetric_key {
|
||||
u8 key[NOISE_SYMMETRIC_KEY_LEN];
|
||||
union noise_counter counter;
|
||||
u64 birthdate;
|
||||
bool is_valid;
|
||||
};
|
||||
|
@ -34,7 +30,9 @@ struct noise_symmetric_key {
|
|||
struct noise_keypair {
|
||||
struct index_hashtable_entry entry;
|
||||
struct noise_symmetric_key sending;
|
||||
atomic64_t sending_counter;
|
||||
struct noise_symmetric_key receiving;
|
||||
struct noise_replay_counter receiving_counter;
|
||||
__le32 remote_index;
|
||||
bool i_am_the_initiator;
|
||||
struct kref refcount;
|
||||
|
|
|
@ -87,12 +87,20 @@ static inline bool wg_check_packet_protocol(struct sk_buff *skb)
|
|||
return real_protocol && skb->protocol == real_protocol;
|
||||
}
|
||||
|
||||
static inline void wg_reset_packet(struct sk_buff *skb)
|
||||
static inline void wg_reset_packet(struct sk_buff *skb, bool encapsulating)
|
||||
{
|
||||
u8 l4_hash = skb->l4_hash;
|
||||
u8 sw_hash = skb->sw_hash;
|
||||
u32 hash = skb->hash;
|
||||
skb_scrub_packet(skb, true);
|
||||
memset(&skb->headers_start, 0,
|
||||
offsetof(struct sk_buff, headers_end) -
|
||||
offsetof(struct sk_buff, headers_start));
|
||||
if (encapsulating) {
|
||||
skb->l4_hash = l4_hash;
|
||||
skb->sw_hash = sw_hash;
|
||||
skb->hash = hash;
|
||||
}
|
||||
skb->queue_mapping = 0;
|
||||
skb->nohdr = 0;
|
||||
skb->peeked = 0;
|
||||
|
|
|
@ -245,20 +245,20 @@ static void keep_key_fresh(struct wg_peer *peer)
|
|||
}
|
||||
}
|
||||
|
||||
static bool decrypt_packet(struct sk_buff *skb, struct noise_symmetric_key *key)
|
||||
static bool decrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
|
||||
{
|
||||
struct scatterlist sg[MAX_SKB_FRAGS + 8];
|
||||
struct sk_buff *trailer;
|
||||
unsigned int offset;
|
||||
int num_frags;
|
||||
|
||||
if (unlikely(!key))
|
||||
if (unlikely(!keypair))
|
||||
return false;
|
||||
|
||||
if (unlikely(!READ_ONCE(key->is_valid) ||
|
||||
wg_birthdate_has_expired(key->birthdate, REJECT_AFTER_TIME) ||
|
||||
key->counter.receive.counter >= REJECT_AFTER_MESSAGES)) {
|
||||
WRITE_ONCE(key->is_valid, false);
|
||||
if (unlikely(!READ_ONCE(keypair->receiving.is_valid) ||
|
||||
wg_birthdate_has_expired(keypair->receiving.birthdate, REJECT_AFTER_TIME) ||
|
||||
keypair->receiving_counter.counter >= REJECT_AFTER_MESSAGES)) {
|
||||
WRITE_ONCE(keypair->receiving.is_valid, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_symmetric_key *key)
|
|||
|
||||
if (!chacha20poly1305_decrypt_sg_inplace(sg, skb->len, NULL, 0,
|
||||
PACKET_CB(skb)->nonce,
|
||||
key->key))
|
||||
keypair->receiving.key))
|
||||
return false;
|
||||
|
||||
/* Another ugly situation of pushing and pulling the header so as to
|
||||
|
@ -298,41 +298,41 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_symmetric_key *key)
|
|||
}
|
||||
|
||||
/* This is RFC6479, a replay detection bitmap algorithm that avoids bitshifts */
|
||||
static bool counter_validate(union noise_counter *counter, u64 their_counter)
|
||||
static bool counter_validate(struct noise_replay_counter *counter, u64 their_counter)
|
||||
{
|
||||
unsigned long index, index_current, top, i;
|
||||
bool ret = false;
|
||||
|
||||
spin_lock_bh(&counter->receive.lock);
|
||||
spin_lock_bh(&counter->lock);
|
||||
|
||||
if (unlikely(counter->receive.counter >= REJECT_AFTER_MESSAGES + 1 ||
|
||||
if (unlikely(counter->counter >= REJECT_AFTER_MESSAGES + 1 ||
|
||||
their_counter >= REJECT_AFTER_MESSAGES))
|
||||
goto out;
|
||||
|
||||
++their_counter;
|
||||
|
||||
if (unlikely((COUNTER_WINDOW_SIZE + their_counter) <
|
||||
counter->receive.counter))
|
||||
counter->counter))
|
||||
goto out;
|
||||
|
||||
index = their_counter >> ilog2(BITS_PER_LONG);
|
||||
|
||||
if (likely(their_counter > counter->receive.counter)) {
|
||||
index_current = counter->receive.counter >> ilog2(BITS_PER_LONG);
|
||||
if (likely(their_counter > counter->counter)) {
|
||||
index_current = counter->counter >> ilog2(BITS_PER_LONG);
|
||||
top = min_t(unsigned long, index - index_current,
|
||||
COUNTER_BITS_TOTAL / BITS_PER_LONG);
|
||||
for (i = 1; i <= top; ++i)
|
||||
counter->receive.backtrack[(i + index_current) &
|
||||
counter->backtrack[(i + index_current) &
|
||||
((COUNTER_BITS_TOTAL / BITS_PER_LONG) - 1)] = 0;
|
||||
counter->receive.counter = their_counter;
|
||||
counter->counter = their_counter;
|
||||
}
|
||||
|
||||
index &= (COUNTER_BITS_TOTAL / BITS_PER_LONG) - 1;
|
||||
ret = !test_and_set_bit(their_counter & (BITS_PER_LONG - 1),
|
||||
&counter->receive.backtrack[index]);
|
||||
&counter->backtrack[index]);
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&counter->receive.lock);
|
||||
spin_unlock_bh(&counter->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -472,19 +472,19 @@ int wg_packet_rx_poll(struct napi_struct *napi, int budget)
|
|||
if (unlikely(state != PACKET_STATE_CRYPTED))
|
||||
goto next;
|
||||
|
||||
if (unlikely(!counter_validate(&keypair->receiving.counter,
|
||||
if (unlikely(!counter_validate(&keypair->receiving_counter,
|
||||
PACKET_CB(skb)->nonce))) {
|
||||
net_dbg_ratelimited("%s: Packet has invalid nonce %llu (max %llu)\n",
|
||||
peer->device->dev->name,
|
||||
PACKET_CB(skb)->nonce,
|
||||
keypair->receiving.counter.receive.counter);
|
||||
keypair->receiving_counter.counter);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (unlikely(wg_socket_endpoint_from_skb(&endpoint, skb)))
|
||||
goto next;
|
||||
|
||||
wg_reset_packet(skb);
|
||||
wg_reset_packet(skb, false);
|
||||
wg_packet_consume_data_done(peer, skb, &endpoint);
|
||||
free = false;
|
||||
|
||||
|
@ -511,8 +511,8 @@ void wg_packet_decrypt_worker(struct work_struct *work)
|
|||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = ptr_ring_consume_bh(&queue->ring)) != NULL) {
|
||||
enum packet_state state = likely(decrypt_packet(skb,
|
||||
&PACKET_CB(skb)->keypair->receiving)) ?
|
||||
enum packet_state state =
|
||||
likely(decrypt_packet(skb, PACKET_CB(skb)->keypair)) ?
|
||||
PACKET_STATE_CRYPTED : PACKET_STATE_DEAD;
|
||||
wg_queue_enqueue_per_peer_napi(skb, state);
|
||||
if (need_resched())
|
||||
|
|
|
@ -6,18 +6,24 @@
|
|||
#ifdef DEBUG
|
||||
bool __init wg_packet_counter_selftest(void)
|
||||
{
|
||||
struct noise_replay_counter *counter;
|
||||
unsigned int test_num = 0, i;
|
||||
union noise_counter counter;
|
||||
bool success = true;
|
||||
|
||||
#define T_INIT do { \
|
||||
memset(&counter, 0, sizeof(union noise_counter)); \
|
||||
spin_lock_init(&counter.receive.lock); \
|
||||
counter = kmalloc(sizeof(*counter), GFP_KERNEL);
|
||||
if (unlikely(!counter)) {
|
||||
pr_err("nonce counter self-test malloc: FAIL\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#define T_INIT do { \
|
||||
memset(counter, 0, sizeof(*counter)); \
|
||||
spin_lock_init(&counter->lock); \
|
||||
} while (0)
|
||||
#define T_LIM (COUNTER_WINDOW_SIZE + 1)
|
||||
#define T(n, v) do { \
|
||||
++test_num; \
|
||||
if (counter_validate(&counter, n) != (v)) { \
|
||||
if (counter_validate(counter, n) != (v)) { \
|
||||
pr_err("nonce counter self-test %u: FAIL\n", \
|
||||
test_num); \
|
||||
success = false; \
|
||||
|
@ -99,6 +105,7 @@ bool __init wg_packet_counter_selftest(void)
|
|||
|
||||
if (success)
|
||||
pr_info("nonce counter self-tests: pass\n");
|
||||
kfree(counter);
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -129,7 +129,7 @@ static void keep_key_fresh(struct wg_peer *peer)
|
|||
rcu_read_lock_bh();
|
||||
keypair = rcu_dereference_bh(peer->keypairs.current_keypair);
|
||||
send = keypair && READ_ONCE(keypair->sending.is_valid) &&
|
||||
(atomic64_read(&keypair->sending.counter.counter) > REKEY_AFTER_MESSAGES ||
|
||||
(atomic64_read(&keypair->sending_counter) > REKEY_AFTER_MESSAGES ||
|
||||
(keypair->i_am_the_initiator &&
|
||||
wg_birthdate_has_expired(keypair->sending.birthdate, REKEY_AFTER_TIME)));
|
||||
rcu_read_unlock_bh();
|
||||
|
@ -167,6 +167,11 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
|
|||
struct sk_buff *trailer;
|
||||
int num_frags;
|
||||
|
||||
/* Force hash calculation before encryption so that flow analysis is
|
||||
* consistent over the inner packet.
|
||||
*/
|
||||
skb_get_hash(skb);
|
||||
|
||||
/* Calculate lengths. */
|
||||
padding_len = calculate_skb_padding(skb);
|
||||
trailer_len = padding_len + noise_encrypted_len(0);
|
||||
|
@ -295,7 +300,7 @@ void wg_packet_encrypt_worker(struct work_struct *work)
|
|||
skb_list_walk_safe(first, skb, next) {
|
||||
if (likely(encrypt_packet(skb,
|
||||
PACKET_CB(first)->keypair))) {
|
||||
wg_reset_packet(skb);
|
||||
wg_reset_packet(skb, true);
|
||||
} else {
|
||||
state = PACKET_STATE_DEAD;
|
||||
break;
|
||||
|
@ -344,7 +349,6 @@ void wg_packet_purge_staged_packets(struct wg_peer *peer)
|
|||
|
||||
void wg_packet_send_staged_packets(struct wg_peer *peer)
|
||||
{
|
||||
struct noise_symmetric_key *key;
|
||||
struct noise_keypair *keypair;
|
||||
struct sk_buff_head packets;
|
||||
struct sk_buff *skb;
|
||||
|
@ -364,10 +368,9 @@ void wg_packet_send_staged_packets(struct wg_peer *peer)
|
|||
rcu_read_unlock_bh();
|
||||
if (unlikely(!keypair))
|
||||
goto out_nokey;
|
||||
key = &keypair->sending;
|
||||
if (unlikely(!READ_ONCE(key->is_valid)))
|
||||
if (unlikely(!READ_ONCE(keypair->sending.is_valid)))
|
||||
goto out_nokey;
|
||||
if (unlikely(wg_birthdate_has_expired(key->birthdate,
|
||||
if (unlikely(wg_birthdate_has_expired(keypair->sending.birthdate,
|
||||
REJECT_AFTER_TIME)))
|
||||
goto out_invalid;
|
||||
|
||||
|
@ -382,7 +385,7 @@ void wg_packet_send_staged_packets(struct wg_peer *peer)
|
|||
*/
|
||||
PACKET_CB(skb)->ds = ip_tunnel_ecn_encap(0, ip_hdr(skb), skb);
|
||||
PACKET_CB(skb)->nonce =
|
||||
atomic64_inc_return(&key->counter.counter) - 1;
|
||||
atomic64_inc_return(&keypair->sending_counter) - 1;
|
||||
if (unlikely(PACKET_CB(skb)->nonce >= REJECT_AFTER_MESSAGES))
|
||||
goto out_invalid;
|
||||
}
|
||||
|
@ -394,7 +397,7 @@ void wg_packet_send_staged_packets(struct wg_peer *peer)
|
|||
return;
|
||||
|
||||
out_invalid:
|
||||
WRITE_ONCE(key->is_valid, false);
|
||||
WRITE_ONCE(keypair->sending.is_valid, false);
|
||||
out_nokey:
|
||||
wg_noise_keypair_put(keypair, false);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ endef
|
|||
$(eval $(call tar_download,MUSL,musl,1.2.0,.tar.gz,https://musl.libc.org/releases/,c6de7b191139142d3f9a7b5b702c9cae1b5ee6e7f57e582da9328629408fd4e8))
|
||||
$(eval $(call tar_download,IPERF,iperf,3.7,.tar.gz,https://downloads.es.net/pub/iperf/,d846040224317caf2f75c843d309a950a7db23f9b44b94688ccbe557d6d1710c))
|
||||
$(eval $(call tar_download,BASH,bash,5.0,.tar.gz,https://ftp.gnu.org/gnu/bash/,b4a80f2ac66170b2913efbfb9f2594f1f76c7b1afd11f799e22035d63077fb4d))
|
||||
$(eval $(call tar_download,IPROUTE2,iproute2,5.4.0,.tar.xz,https://www.kernel.org/pub/linux/utils/net/iproute2/,fe97aa60a0d4c5ac830be18937e18dc3400ca713a33a89ad896ff1e3d46086ae))
|
||||
$(eval $(call tar_download,IPROUTE2,iproute2,5.6.0,.tar.xz,https://www.kernel.org/pub/linux/utils/net/iproute2/,1b5b0e25ce6e23da7526ea1da044e814ad85ba761b10dd29c2b027c056b04692))
|
||||
$(eval $(call tar_download,IPTABLES,iptables,1.8.4,.tar.bz2,https://www.netfilter.org/projects/iptables/files/,993a3a5490a544c2cbf2ef15cf7e7ed21af1845baf228318d5c36ef8827e157c))
|
||||
$(eval $(call tar_download,NMAP,nmap,7.80,.tar.bz2,https://nmap.org/dist/,fcfa5a0e42099e12e4bf7a68ebe6fde05553383a682e816a7ec9256ab4773faa))
|
||||
$(eval $(call tar_download,IPUTILS,iputils,s20190709,.tar.gz,https://github.com/iputils/iputils/archive/s20190709.tar.gz/#,a15720dd741d7538dd2645f9f516d193636ae4300ff7dbc8bfca757bf166490a))
|
||||
|
|
Loading…
Reference in New Issue