xref: /linux/drivers/net/ovpn/crypto.c (revision c36461825469a9ceee2346a2e89286c522525da7)
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 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  */
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 */
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  */
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 
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 
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  */
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