xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/key.c (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * Copyright (C) 2024 Intel Corporation
4  */
5 #include "key.h"
6 #include "iface.h"
7 #include "sta.h"
8 #include "fw/api/datapath.h"
9 
iwl_mld_get_key_flags(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)10 static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
11 				 struct ieee80211_vif *vif,
12 				 struct ieee80211_sta *sta,
13 				 struct ieee80211_key_conf *key)
14 {
15 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
16 	bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE;
17 	bool igtk = key->keyidx == 4 || key->keyidx == 5;
18 	u32 flags = 0;
19 
20 	if (!pairwise)
21 		flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;
22 
23 	switch (key->cipher) {
24 	case WLAN_CIPHER_SUITE_TKIP:
25 		flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;
26 		break;
27 	case WLAN_CIPHER_SUITE_AES_CMAC:
28 	case WLAN_CIPHER_SUITE_CCMP:
29 		flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;
30 		break;
31 	case WLAN_CIPHER_SUITE_GCMP_256:
32 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
33 		flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;
34 		fallthrough;
35 	case WLAN_CIPHER_SUITE_GCMP:
36 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
37 		flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;
38 		break;
39 	}
40 
41 	if (!sta && vif->type == NL80211_IFTYPE_STATION)
42 		sta = mld_vif->ap_sta;
43 
44 	/* If we are installing an iGTK (in AP or STA mode), we need to tell
45 	 * the firmware this key will en/decrypt MGMT frames.
46 	 * Same goes if we are installing a pairwise key for an MFP station.
47 	 * In case we're installing a groupwise key (which is not an iGTK),
48 	 * then, we will not use this key for MGMT frames.
49 	 */
50 	if ((sta && sta->mfp && pairwise) || igtk)
51 		flags |= IWL_SEC_KEY_FLAG_MFP;
52 
53 	if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)
54 		flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;
55 
56 	return flags;
57 }
58 
iwl_mld_get_key_sta_mask(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)59 static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
60 				    struct ieee80211_vif *vif,
61 				    struct ieee80211_sta *sta,
62 				    struct ieee80211_key_conf *key)
63 {
64 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
65 	struct ieee80211_link_sta *link_sta;
66 	int sta_id;
67 
68 	lockdep_assert_wiphy(mld->wiphy);
69 
70 	/* AP group keys are per link and should be on the mcast/bcast STA */
71 	if (vif->type == NL80211_IFTYPE_AP &&
72 	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
73 		struct iwl_mld_link *link = NULL;
74 
75 		if (key->link_id >= 0)
76 			link = iwl_mld_link_dereference_check(mld_vif,
77 							      key->link_id);
78 
79 		if (WARN_ON(!link))
80 			return 0;
81 
82 		/* In this stage we should have both the bcast and mcast STAs */
83 		if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA ||
84 			    link->mcast_sta.sta_id == IWL_INVALID_STA))
85 			return 0;
86 
87 		/* IGTK/BIGTK to bcast STA */
88 		if (key->keyidx >= 4)
89 			return BIT(link->bcast_sta.sta_id);
90 
91 		/* GTK for data to mcast STA */
92 		return BIT(link->mcast_sta.sta_id);
93 	}
94 
95 	/* for client mode use the AP STA also for group keys */
96 	if (!sta && vif->type == NL80211_IFTYPE_STATION)
97 		sta = mld_vif->ap_sta;
98 
99 	/* STA should be non-NULL now */
100 	if (WARN_ON(!sta))
101 		return 0;
102 
103 	/* Key is not per-link, get the full sta mask */
104 	if (key->link_id < 0)
105 		return iwl_mld_fw_sta_id_mask(mld, sta);
106 
107 	/* The link_sta shouldn't be NULL now, but this is checked in
108 	 * iwl_mld_fw_sta_id_mask
109 	 */
110 	link_sta = link_sta_dereference_check(sta, key->link_id);
111 
112 	sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
113 	if (sta_id < 0)
114 		return 0;
115 
116 	return BIT(sta_id);
117 }
118 
iwl_mld_add_key_to_fw(struct iwl_mld * mld,u32 sta_mask,u32 key_flags,struct ieee80211_key_conf * key)119 static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask,
120 				 u32 key_flags, struct ieee80211_key_conf *key)
121 {
122 	struct iwl_sec_key_cmd cmd = {
123 		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
124 		.u.add.sta_mask = cpu_to_le32(sta_mask),
125 		.u.add.key_id = cpu_to_le32(key->keyidx),
126 		.u.add.key_flags = cpu_to_le32(key_flags),
127 		.u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)),
128 	};
129 	bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP;
130 	int max_key_len = sizeof(cmd.u.add.key);
131 
132 	if (WARN_ON(!sta_mask))
133 		return -EINVAL;
134 
135 	if (WARN_ON(key->keylen > max_key_len))
136 		return -EINVAL;
137 
138 	memcpy(cmd.u.add.key, key->key, key->keylen);
139 
140 	if (tkip) {
141 		memcpy(cmd.u.add.tkip_mic_rx_key,
142 		       key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
143 		       8);
144 		memcpy(cmd.u.add.tkip_mic_tx_key,
145 		       key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
146 		       8);
147 	}
148 
149 	return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
150 				    &cmd);
151 }
152 
iwl_mld_remove_key_from_fw(struct iwl_mld * mld,u32 sta_mask,u32 key_flags,u32 keyidx)153 static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,
154 				       u32 key_flags, u32 keyidx)
155 {
156 	struct iwl_sec_key_cmd cmd = {
157 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
158 		.u.remove.sta_mask = cpu_to_le32(sta_mask),
159 		.u.remove.key_id = cpu_to_le32(keyidx),
160 		.u.remove.key_flags = cpu_to_le32(key_flags),
161 	};
162 
163 	if (WARN_ON(!sta_mask))
164 		return;
165 
166 	iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);
167 }
168 
iwl_mld_remove_key(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)169 void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif,
170 			struct ieee80211_sta *sta,
171 			struct ieee80211_key_conf *key)
172 {
173 	u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
174 	u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
175 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
176 
177 	lockdep_assert_wiphy(mld->wiphy);
178 
179 	if (!sta_mask)
180 		return;
181 
182 	if (key->keyidx == 4 || key->keyidx == 5) {
183 		struct iwl_mld_link *mld_link;
184 		unsigned int link_id = 0;
185 
186 		/* set to -1 for non-MLO right now */
187 		if (key->link_id >= 0)
188 			link_id = key->link_id;
189 
190 		mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
191 		if (WARN_ON(!mld_link))
192 			return;
193 
194 		if (mld_link->igtk == key)
195 			mld_link->igtk = NULL;
196 
197 		mld->num_igtks--;
198 	}
199 
200 	iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx);
201 
202 	/* no longer in HW */
203 	key->hw_key_idx = STA_KEY_IDX_INVALID;
204 }
205 
iwl_mld_add_key(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)206 int iwl_mld_add_key(struct iwl_mld *mld,
207 		    struct ieee80211_vif *vif,
208 		    struct ieee80211_sta *sta,
209 		    struct ieee80211_key_conf *key)
210 {
211 	u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key);
212 	u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key);
213 	struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
214 	struct iwl_mld_link *mld_link = NULL;
215 	bool igtk = key->keyidx == 4 || key->keyidx == 5;
216 	int ret;
217 
218 	lockdep_assert_wiphy(mld->wiphy);
219 
220 	if (!sta_mask)
221 		return -EINVAL;
222 
223 	if (igtk) {
224 		if (mld->num_igtks == IWL_MAX_NUM_IGTKS)
225 			return -EOPNOTSUPP;
226 
227 		u8 link_id = 0;
228 
229 		/* set to -1 for non-MLO right now */
230 		if (key->link_id >= 0)
231 			link_id = key->link_id;
232 
233 		mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
234 
235 		if (WARN_ON(!mld_link))
236 			return -EINVAL;
237 
238 		if (mld_link->igtk) {
239 			IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",
240 					   mld_link->igtk->keyidx);
241 			iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);
242 		}
243 
244 		WARN_ON(mld_link->igtk);
245 	}
246 
247 	ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);
248 	if (ret)
249 		return ret;
250 
251 	if (mld_link) {
252 		mld_link->igtk = key;
253 		mld->num_igtks++;
254 	}
255 
256 	/* We don't really need this, but need it to be not invalid,
257 	 * so we will know if the key is in fw.
258 	 */
259 	key->hw_key_idx = 0;
260 
261 	return 0;
262 }
263 
264 struct remove_ap_keys_iter_data {
265 	u8 link_id;
266 	struct ieee80211_sta *sta;
267 };
268 
iwl_mld_remove_ap_keys_iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * _data)269 static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw,
270 					struct ieee80211_vif *vif,
271 					struct ieee80211_sta *sta,
272 					struct ieee80211_key_conf *key,
273 					void *_data)
274 {
275 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
276 	struct remove_ap_keys_iter_data *data = _data;
277 
278 	if (key->hw_key_idx == STA_KEY_IDX_INVALID)
279 		return;
280 
281 	/* All the pairwise keys should have been removed by now */
282 	if (WARN_ON(sta))
283 		return;
284 
285 	if (key->link_id >= 0 && key->link_id != data->link_id)
286 		return;
287 
288 	iwl_mld_remove_key(mld, vif, data->sta, key);
289 }
290 
iwl_mld_remove_ap_keys(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,unsigned int link_id)291 void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif,
292 			    struct ieee80211_sta *sta, unsigned int link_id)
293 {
294 	struct remove_ap_keys_iter_data iter_data = {
295 		.link_id = link_id,
296 		.sta = sta,
297 	};
298 
299 	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
300 		return;
301 
302 	ieee80211_iter_keys(mld->hw, vif,
303 			    iwl_mld_remove_ap_keys_iter,
304 			    &iter_data);
305 }
306 
307 struct iwl_mvm_sta_key_update_data {
308 	struct ieee80211_sta *sta;
309 	u32 old_sta_mask;
310 	u32 new_sta_mask;
311 	int err;
312 };
313 
iwl_mld_update_sta_key_iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * _data)314 static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw,
315 					struct ieee80211_vif *vif,
316 					struct ieee80211_sta *sta,
317 					struct ieee80211_key_conf *key,
318 					void *_data)
319 {
320 	struct iwl_mvm_sta_key_update_data *data = _data;
321 	struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
322 	struct iwl_sec_key_cmd cmd = {
323 		.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
324 		.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
325 		.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
326 		.u.modify.key_id = cpu_to_le32(key->keyidx),
327 		.u.modify.key_flags =
328 			cpu_to_le32(iwl_mld_get_key_flags(mld, vif, sta, key)),
329 	};
330 	int err;
331 
332 	/* only need to do this for pairwise keys (link_id == -1) */
333 	if (sta != data->sta || key->link_id >= 0)
334 		return;
335 
336 	err = iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD),
337 				   &cmd);
338 
339 	if (err)
340 		data->err = err;
341 }
342 
iwl_mld_update_sta_keys(struct iwl_mld * mld,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u32 old_sta_mask,u32 new_sta_mask)343 int iwl_mld_update_sta_keys(struct iwl_mld *mld,
344 			    struct ieee80211_vif *vif,
345 			    struct ieee80211_sta *sta,
346 			    u32 old_sta_mask,
347 			    u32 new_sta_mask)
348 {
349 	struct iwl_mvm_sta_key_update_data data = {
350 		.sta = sta,
351 		.old_sta_mask = old_sta_mask,
352 		.new_sta_mask = new_sta_mask,
353 	};
354 
355 	ieee80211_iter_keys(mld->hw, vif, iwl_mld_update_sta_key_iter,
356 			    &data);
357 	return data.err;
358 }
359