ovpn_crypto_kill_key assumes both crypto slots are populated and
dereferences each slot before checking it. That is not guaranteed: a
peer can have only one installed key, and the kill path may be asked to
remove a key that is not present.

Read each slot once while holding the crypto state lock, check for NULL
before looking at key_id, and only replace the slot that actually
matches.

Fixes: 89d3c0e4612a ("ovpn: kill key and notify userspace in case of IV 
exhaustion")
Signed-off-by: Ralf Lici <[email protected]>
---
 drivers/net/ovpn/crypto.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ovpn/crypto.c b/drivers/net/ovpn/crypto.c
index 90580e32052f..577a22b4e810 100644
--- a/drivers/net/ovpn/crypto.c
+++ b/drivers/net/ovpn/crypto.c
@@ -58,12 +58,18 @@ void ovpn_crypto_state_release(struct ovpn_crypto_state *cs)
 bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id)
 {
        struct ovpn_crypto_key_slot *ks = NULL;
+       struct ovpn_crypto_key_slot *tmp;
 
        spin_lock_bh(&cs->lock);
-       if (rcu_access_pointer(cs->slots[0])->key_id == key_id) {
+       tmp = rcu_access_pointer(cs->slots[0]);
+       if (tmp && tmp->key_id == key_id) {
                ks = rcu_replace_pointer(cs->slots[0], NULL,
                                         lockdep_is_held(&cs->lock));
-       } else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) {
+       } else {
+               tmp = rcu_access_pointer(cs->slots[1]);
+       }
+
+       if (!ks && tmp && tmp->key_id == key_id) {
                ks = rcu_replace_pointer(cs->slots[1], NULL,
                                         lockdep_is_held(&cs->lock));
        }
-- 
2.54.0



_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to