[Openvpn-devel,RFC,net-next,03/14] ovpn: limit AEAD decrypt verification failures
Commit Message
OpenVPN userspace tracks AEAD tag verification failures per decrypt key.
It asks for a new key after more than 2^35 failures and stops attempting
AEAD decryption with that key after more than 2^36 failures.
Mirror that behavior in ovpn. Count only crypto_aead_decrypt
authentication failures, reported by the crypto API as -EBADMSG, and
notify userspace once when the soft threshold is crossed. Once the hard
threshold is exceeded, reject new decrypt attempts with the same key
before submitting more crypto work.
This applies to all ovpn AEAD ciphers, not only AES-GCM, and is
independent from the AES-GCM packet/block usage limits.
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
---
drivers/net/ovpn/crypto.h | 2 ++
drivers/net/ovpn/crypto_aead.c | 24 ++++++++++++++++++++++++
drivers/net/ovpn/crypto_aead.h | 1 +
drivers/net/ovpn/io.c | 6 ++++++
4 files changed, 33 insertions(+)
@@ -43,6 +43,8 @@ struct ovpn_crypto_key_slot {
struct crypto_aead *encrypt;
struct crypto_aead *decrypt;
+ atomic64_t decrypt_failures;
+ unsigned long decrypt_failure_flags;
u8 nonce_tail_xmit[OVPN_NONCE_TAIL_SIZE];
u8 nonce_tail_recv[OVPN_NONCE_TAIL_SIZE];
@@ -29,6 +29,25 @@
#define ALG_NAME_AES "gcm(aes)"
#define ALG_NAME_CHACHAPOLY "rfc7539(chacha20,poly1305)"
+#define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY BIT_ULL(35)
+#define OVPN_AEAD_DECRYPT_FAILURE_LIMIT BIT_ULL(36)
+#define OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT 0
+
+static bool
+ovpn_aead_decrypt_failure_exceeded(const struct ovpn_crypto_key_slot *ks)
+{
+ return atomic64_read(&ks->decrypt_failures) >
+ OVPN_AEAD_DECRYPT_FAILURE_LIMIT;
+}
+
+bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks)
+{
+ u64 failures = atomic64_inc_return(&ks->decrypt_failures);
+
+ return failures > OVPN_AEAD_DECRYPT_FAILURE_NOTIFY &&
+ !test_and_set_bit(OVPN_AEAD_DECRYPT_FAILURE_NOTIFY_BIT,
+ &ks->decrypt_failure_flags);
+}
static int ovpn_aead_encap_overhead(const struct ovpn_crypto_key_slot *ks)
{
@@ -270,6 +289,9 @@ int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
if (unlikely(payload_len < 0))
return -EINVAL;
+ if (unlikely(ovpn_aead_decrypt_failure_exceeded(ks)))
+ return -EKEYREJECTED;
+
/* Prepare the skb data buffer to be accessed up until the auth tag.
* This is required because this area is directly mapped into the sg
* list.
@@ -436,6 +458,8 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc)
ovpn_key_usage_limit_init(&ks->usage_limit, kc->cipher_alg);
ovpn_key_usage_init(&ks->usage_xmit);
ovpn_key_usage_init(&ks->usage_recv);
+ atomic64_set(&ks->decrypt_failures, 0);
+ ks->decrypt_failure_flags = 0;
ks->encrypt = ovpn_aead_init("encrypt", alg_name,
kc->encrypt.cipher_key,
@@ -25,5 +25,6 @@ ovpn_aead_crypto_key_slot_new(const struct ovpn_key_config *kc);
void ovpn_aead_crypto_key_slot_destroy(struct ovpn_crypto_key_slot *ks);
enum ovpn_cipher_alg ovpn_aead_crypto_alg(struct ovpn_crypto_key_slot *ks);
+bool ovpn_aead_decrypt_failure_record(struct ovpn_crypto_key_slot *ks);
#endif /* _NET_OVPN_OVPNAEAD_H_ */
@@ -129,6 +129,12 @@ void ovpn_decrypt_post(void *data, int ret)
/* crypto is done, cleanup skb CB and its members */
kfree(ovpn_skb_cb(skb)->crypto_tmp);
+ if (unlikely(ret == -EBADMSG)) {
+ if (unlikely(ovpn_aead_decrypt_failure_record(ks)))
+ ovpn_nl_key_swap_notify(peer, ks->key_id);
+ goto drop;
+ }
+
if (unlikely(ret < 0))
goto drop;