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