File Index
Files and directories
Sign in
FILE

crypto.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ovpn/crypto.c C
  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
 21static 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
 29void 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 */
 40void 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 */
 58bool 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 */
 82int 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
116void 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
151void 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 */
180int 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}
~/ Read-only file preview.