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_ks_destroy_rcu(struct rcu_head * head)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
ovpn_crypto_key_slot_release(struct kref * kref)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 */
ovpn_crypto_state_release(struct ovpn_crypto_state * cs)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 */
ovpn_crypto_kill_key(struct ovpn_crypto_state * cs,u8 key_id)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 */
ovpn_crypto_state_reset(struct ovpn_crypto_state * cs,const struct ovpn_peer_key_reset * pkr)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
ovpn_crypto_key_slot_delete(struct ovpn_crypto_state * cs,enum ovpn_key_slot slot)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
ovpn_crypto_key_slots_swap(struct ovpn_crypto_state * cs)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 */
ovpn_crypto_config_get(struct ovpn_crypto_state * cs,enum ovpn_key_slot slot,struct ovpn_key_config * keyconf)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