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