1 // SPDX-License-Identifier: GPL-2.0 2 /* OpenVPN data channel offload 3 * 4 * Copyright (C) 2020-2025 OpenVPN, Inc. 5 * 6 * Author: James Yonan <james@openvpn.net> 7 * Antonio Quartulli <antonio@openvpn.net> 8 */ 9 10 #include <linux/types.h> 11 #include <linux/net.h> 12 #include <linux/netdevice.h> 13 #include <uapi/linux/ovpn.h> 14 15 #include "ovpnpriv.h" 16 #include "main.h" 17 #include "pktid.h" 18 #include "crypto_aead.h" 19 #include "crypto.h" 20 21 static void ovpn_ks_destroy_rcu(struct rcu_head *head) 22 { 23 struct ovpn_crypto_key_slot *ks; 24 25 ks = container_of(head, struct ovpn_crypto_key_slot, rcu); 26 ovpn_aead_crypto_key_slot_destroy(ks); 27 } 28 29 void ovpn_crypto_key_slot_release(struct kref *kref) 30 { 31 struct ovpn_crypto_key_slot *ks; 32 33 ks = container_of(kref, struct ovpn_crypto_key_slot, refcount); 34 call_rcu(&ks->rcu, ovpn_ks_destroy_rcu); 35 } 36 37 /* can only be invoked when all peer references have been dropped (i.e. RCU 38 * release routine) 39 */ 40 void ovpn_crypto_state_release(struct ovpn_crypto_state *cs) 41 { 42 struct ovpn_crypto_key_slot *ks; 43 44 ks = rcu_access_pointer(cs->slots[0]); 45 if (ks) { 46 RCU_INIT_POINTER(cs->slots[0], NULL); 47 ovpn_crypto_key_slot_put(ks); 48 } 49 50 ks = rcu_access_pointer(cs->slots[1]); 51 if (ks) { 52 RCU_INIT_POINTER(cs->slots[1], NULL); 53 ovpn_crypto_key_slot_put(ks); 54 } 55 } 56 57 /* removes the key matching the specified id from the crypto context */ 58 bool ovpn_crypto_kill_key(struct ovpn_crypto_state *cs, u8 key_id) 59 { 60 struct ovpn_crypto_key_slot *ks = NULL; 61 62 spin_lock_bh(&cs->lock); 63 if (rcu_access_pointer(cs->slots[0])->key_id == key_id) { 64 ks = rcu_replace_pointer(cs->slots[0], NULL, 65 lockdep_is_held(&cs->lock)); 66 } else if (rcu_access_pointer(cs->slots[1])->key_id == key_id) { 67 ks = rcu_replace_pointer(cs->slots[1], NULL, 68 lockdep_is_held(&cs->lock)); 69 } 70 spin_unlock_bh(&cs->lock); 71 72 if (ks) 73 ovpn_crypto_key_slot_put(ks); 74 75 /* let the caller know if a key was actually killed */ 76 return ks; 77 } 78 79 /* Reset the ovpn_crypto_state object in a way that is atomic 80 * to RCU readers. 81 */ 82 int ovpn_crypto_state_reset(struct ovpn_crypto_state *cs, 83 const struct ovpn_peer_key_reset *pkr) 84 { 85 struct ovpn_crypto_key_slot *old = NULL, *new; 86 u8 idx; 87 88 if (pkr->slot != OVPN_KEY_SLOT_PRIMARY && 89 pkr->slot != OVPN_KEY_SLOT_SECONDARY) 90 return -EINVAL; 91 92 new = ovpn_aead_crypto_key_slot_new(&pkr->key); 93 if (IS_ERR(new)) 94 return PTR_ERR(new); 95 96 spin_lock_bh(&cs->lock); 97 idx = cs->primary_idx; 98 switch (pkr->slot) { 99 case OVPN_KEY_SLOT_PRIMARY: 100 old = rcu_replace_pointer(cs->slots[idx], new, 101 lockdep_is_held(&cs->lock)); 102 break; 103 case OVPN_KEY_SLOT_SECONDARY: 104 old = rcu_replace_pointer(cs->slots[!idx], new, 105 lockdep_is_held(&cs->lock)); 106 break; 107 } 108 spin_unlock_bh(&cs->lock); 109 110 if (old) 111 ovpn_crypto_key_slot_put(old); 112 113 return 0; 114 } 115 116 void ovpn_crypto_key_slot_delete(struct ovpn_crypto_state *cs, 117 enum ovpn_key_slot slot) 118 { 119 struct ovpn_crypto_key_slot *ks = NULL; 120 u8 idx; 121 122 if (slot != OVPN_KEY_SLOT_PRIMARY && 123 slot != OVPN_KEY_SLOT_SECONDARY) { 124 pr_warn("Invalid slot to release: %u\n", slot); 125 return; 126 } 127 128 spin_lock_bh(&cs->lock); 129 idx = cs->primary_idx; 130 switch (slot) { 131 case OVPN_KEY_SLOT_PRIMARY: 132 ks = rcu_replace_pointer(cs->slots[idx], NULL, 133 lockdep_is_held(&cs->lock)); 134 break; 135 case OVPN_KEY_SLOT_SECONDARY: 136 ks = rcu_replace_pointer(cs->slots[!idx], NULL, 137 lockdep_is_held(&cs->lock)); 138 break; 139 } 140 spin_unlock_bh(&cs->lock); 141 142 if (!ks) { 143 pr_debug("Key slot already released: %u\n", slot); 144 return; 145 } 146 147 pr_debug("deleting key slot %u, key_id=%u\n", slot, ks->key_id); 148 ovpn_crypto_key_slot_put(ks); 149 } 150 151 void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs) 152 { 153 const struct ovpn_crypto_key_slot *old_primary, *old_secondary; 154 u8 idx; 155 156 spin_lock_bh(&cs->lock); 157 idx = cs->primary_idx; 158 old_primary = rcu_dereference_protected(cs->slots[idx], 159 lockdep_is_held(&cs->lock)); 160 old_secondary = rcu_dereference_protected(cs->slots[!idx], 161 lockdep_is_held(&cs->lock)); 162 /* perform real swap by switching the index of the primary key */ 163 WRITE_ONCE(cs->primary_idx, !cs->primary_idx); 164 165 pr_debug("key swapped: (old primary) %d <-> (new primary) %d\n", 166 old_primary ? old_primary->key_id : -1, 167 old_secondary ? old_secondary->key_id : -1); 168 169 spin_unlock_bh(&cs->lock); 170 } 171 172 /** 173 * ovpn_crypto_config_get - populate keyconf object with non-sensible key data 174 * @cs: the crypto state to extract the key data from 175 * @slot: the specific slot to inspect 176 * @keyconf: the output object to populate 177 * 178 * Return: 0 on success or a negative error code otherwise 179 */ 180 int ovpn_crypto_config_get(struct ovpn_crypto_state *cs, 181 enum ovpn_key_slot slot, 182 struct ovpn_key_config *keyconf) 183 { 184 struct ovpn_crypto_key_slot *ks; 185 int idx; 186 187 switch (slot) { 188 case OVPN_KEY_SLOT_PRIMARY: 189 idx = cs->primary_idx; 190 break; 191 case OVPN_KEY_SLOT_SECONDARY: 192 idx = !cs->primary_idx; 193 break; 194 default: 195 return -EINVAL; 196 } 197 198 rcu_read_lock(); 199 ks = rcu_dereference(cs->slots[idx]); 200 if (!ks) { 201 rcu_read_unlock(); 202 return -ENOENT; 203 } 204 205 keyconf->cipher_alg = ovpn_aead_crypto_alg(ks); 206 keyconf->key_id = ks->key_id; 207 rcu_read_unlock(); 208 209 return 0; 210 } 211