1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2003-2005 Devicescape Software, Inc. |
4 | * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> |
5 | * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright (C) 2015 Intel Deutschland GmbH |
7 | * Copyright (C) 2021-2023 Intel Corporation |
8 | */ |
9 | |
10 | #include <linux/kobject.h> |
11 | #include <linux/slab.h> |
12 | #include "ieee80211_i.h" |
13 | #include "key.h" |
14 | #include "debugfs.h" |
15 | #include "debugfs_key.h" |
16 | |
17 | #define KEY_READ(name, prop, format_string) \ |
18 | static ssize_t key_##name##_read(struct file *file, \ |
19 | char __user *userbuf, \ |
20 | size_t count, loff_t *ppos) \ |
21 | { \ |
22 | struct ieee80211_key *key = file->private_data; \ |
23 | return mac80211_format_buffer(userbuf, count, ppos, \ |
24 | format_string, key->prop); \ |
25 | } |
26 | #define KEY_READ_X(name) KEY_READ(name, name, "0x%x\n") |
27 | |
28 | #define KEY_OPS(name) \ |
29 | static const struct file_operations key_ ##name## _ops = { \ |
30 | .read = key_##name##_read, \ |
31 | .open = simple_open, \ |
32 | .llseek = generic_file_llseek, \ |
33 | } |
34 | |
35 | #define KEY_OPS_W(name) \ |
36 | static const struct file_operations key_ ##name## _ops = { \ |
37 | .read = key_##name##_read, \ |
38 | .write = key_##name##_write, \ |
39 | .open = simple_open, \ |
40 | .llseek = generic_file_llseek, \ |
41 | } |
42 | |
43 | #define KEY_FILE(name, format) \ |
44 | KEY_READ_##format(name) \ |
45 | KEY_OPS(name) |
46 | |
47 | #define KEY_CONF_READ(name, format_string) \ |
48 | KEY_READ(conf_##name, conf.name, format_string) |
49 | #define KEY_CONF_READ_D(name) KEY_CONF_READ(name, "%d\n") |
50 | |
51 | #define KEY_CONF_OPS(name) \ |
52 | static const struct file_operations key_ ##name## _ops = { \ |
53 | .read = key_conf_##name##_read, \ |
54 | .open = simple_open, \ |
55 | .llseek = generic_file_llseek, \ |
56 | } |
57 | |
58 | #define KEY_CONF_FILE(name, format) \ |
59 | KEY_CONF_READ_##format(name) \ |
60 | KEY_CONF_OPS(name) |
61 | |
62 | KEY_CONF_FILE(keylen, D); |
63 | KEY_CONF_FILE(keyidx, D); |
64 | KEY_CONF_FILE(hw_key_idx, D); |
65 | KEY_FILE(flags, X); |
66 | KEY_READ(ifindex, sdata->name, "%s\n" ); |
67 | KEY_OPS(ifindex); |
68 | |
69 | static ssize_t key_algorithm_read(struct file *file, |
70 | char __user *userbuf, |
71 | size_t count, loff_t *ppos) |
72 | { |
73 | char buf[15]; |
74 | struct ieee80211_key *key = file->private_data; |
75 | u32 c = key->conf.cipher; |
76 | |
77 | sprintf(buf, fmt: "%.2x-%.2x-%.2x:%d\n" , |
78 | c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); |
79 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, strlen(buf)); |
80 | } |
81 | KEY_OPS(algorithm); |
82 | |
83 | static ssize_t key_tx_spec_write(struct file *file, const char __user *userbuf, |
84 | size_t count, loff_t *ppos) |
85 | { |
86 | struct ieee80211_key *key = file->private_data; |
87 | u64 pn; |
88 | int ret; |
89 | |
90 | switch (key->conf.cipher) { |
91 | case WLAN_CIPHER_SUITE_WEP40: |
92 | case WLAN_CIPHER_SUITE_WEP104: |
93 | return -EINVAL; |
94 | case WLAN_CIPHER_SUITE_TKIP: |
95 | /* not supported yet */ |
96 | return -EOPNOTSUPP; |
97 | case WLAN_CIPHER_SUITE_CCMP: |
98 | case WLAN_CIPHER_SUITE_CCMP_256: |
99 | case WLAN_CIPHER_SUITE_AES_CMAC: |
100 | case WLAN_CIPHER_SUITE_BIP_CMAC_256: |
101 | case WLAN_CIPHER_SUITE_BIP_GMAC_128: |
102 | case WLAN_CIPHER_SUITE_BIP_GMAC_256: |
103 | case WLAN_CIPHER_SUITE_GCMP: |
104 | case WLAN_CIPHER_SUITE_GCMP_256: |
105 | ret = kstrtou64_from_user(s: userbuf, count, base: 16, res: &pn); |
106 | if (ret) |
107 | return ret; |
108 | /* PN is a 48-bit counter */ |
109 | if (pn >= (1ULL << 48)) |
110 | return -ERANGE; |
111 | atomic64_set(v: &key->conf.tx_pn, i: pn); |
112 | return count; |
113 | default: |
114 | return 0; |
115 | } |
116 | } |
117 | |
118 | static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, |
119 | size_t count, loff_t *ppos) |
120 | { |
121 | u64 pn; |
122 | char buf[20]; |
123 | int len; |
124 | struct ieee80211_key *key = file->private_data; |
125 | |
126 | switch (key->conf.cipher) { |
127 | case WLAN_CIPHER_SUITE_WEP40: |
128 | case WLAN_CIPHER_SUITE_WEP104: |
129 | len = scnprintf(buf, size: sizeof(buf), fmt: "\n" ); |
130 | break; |
131 | case WLAN_CIPHER_SUITE_TKIP: |
132 | pn = atomic64_read(v: &key->conf.tx_pn); |
133 | len = scnprintf(buf, size: sizeof(buf), fmt: "%08x %04x\n" , |
134 | TKIP_PN_TO_IV32(pn), |
135 | TKIP_PN_TO_IV16(pn)); |
136 | break; |
137 | case WLAN_CIPHER_SUITE_CCMP: |
138 | case WLAN_CIPHER_SUITE_CCMP_256: |
139 | case WLAN_CIPHER_SUITE_AES_CMAC: |
140 | case WLAN_CIPHER_SUITE_BIP_CMAC_256: |
141 | case WLAN_CIPHER_SUITE_BIP_GMAC_128: |
142 | case WLAN_CIPHER_SUITE_BIP_GMAC_256: |
143 | case WLAN_CIPHER_SUITE_GCMP: |
144 | case WLAN_CIPHER_SUITE_GCMP_256: |
145 | pn = atomic64_read(v: &key->conf.tx_pn); |
146 | len = scnprintf(buf, size: sizeof(buf), fmt: "%02x%02x%02x%02x%02x%02x\n" , |
147 | (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24), |
148 | (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn); |
149 | break; |
150 | default: |
151 | return 0; |
152 | } |
153 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: len); |
154 | } |
155 | KEY_OPS_W(tx_spec); |
156 | |
157 | static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, |
158 | size_t count, loff_t *ppos) |
159 | { |
160 | struct ieee80211_key *key = file->private_data; |
161 | char buf[14*IEEE80211_NUM_TIDS+1], *p = buf; |
162 | int i, len; |
163 | const u8 *rpn; |
164 | |
165 | switch (key->conf.cipher) { |
166 | case WLAN_CIPHER_SUITE_WEP40: |
167 | case WLAN_CIPHER_SUITE_WEP104: |
168 | len = scnprintf(buf, size: sizeof(buf), fmt: "\n" ); |
169 | break; |
170 | case WLAN_CIPHER_SUITE_TKIP: |
171 | for (i = 0; i < IEEE80211_NUM_TIDS; i++) |
172 | p += scnprintf(buf: p, size: sizeof(buf)+buf-p, |
173 | fmt: "%08x %04x\n" , |
174 | key->u.tkip.rx[i].iv32, |
175 | key->u.tkip.rx[i].iv16); |
176 | len = p - buf; |
177 | break; |
178 | case WLAN_CIPHER_SUITE_CCMP: |
179 | case WLAN_CIPHER_SUITE_CCMP_256: |
180 | for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { |
181 | rpn = key->u.ccmp.rx_pn[i]; |
182 | p += scnprintf(buf: p, size: sizeof(buf)+buf-p, |
183 | fmt: "%02x%02x%02x%02x%02x%02x\n" , |
184 | rpn[0], rpn[1], rpn[2], |
185 | rpn[3], rpn[4], rpn[5]); |
186 | } |
187 | len = p - buf; |
188 | break; |
189 | case WLAN_CIPHER_SUITE_AES_CMAC: |
190 | case WLAN_CIPHER_SUITE_BIP_CMAC_256: |
191 | rpn = key->u.aes_cmac.rx_pn; |
192 | p += scnprintf(buf: p, size: sizeof(buf)+buf-p, |
193 | fmt: "%02x%02x%02x%02x%02x%02x\n" , |
194 | rpn[0], rpn[1], rpn[2], |
195 | rpn[3], rpn[4], rpn[5]); |
196 | len = p - buf; |
197 | break; |
198 | case WLAN_CIPHER_SUITE_BIP_GMAC_128: |
199 | case WLAN_CIPHER_SUITE_BIP_GMAC_256: |
200 | rpn = key->u.aes_gmac.rx_pn; |
201 | p += scnprintf(buf: p, size: sizeof(buf)+buf-p, |
202 | fmt: "%02x%02x%02x%02x%02x%02x\n" , |
203 | rpn[0], rpn[1], rpn[2], |
204 | rpn[3], rpn[4], rpn[5]); |
205 | len = p - buf; |
206 | break; |
207 | case WLAN_CIPHER_SUITE_GCMP: |
208 | case WLAN_CIPHER_SUITE_GCMP_256: |
209 | for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { |
210 | rpn = key->u.gcmp.rx_pn[i]; |
211 | p += scnprintf(buf: p, size: sizeof(buf)+buf-p, |
212 | fmt: "%02x%02x%02x%02x%02x%02x\n" , |
213 | rpn[0], rpn[1], rpn[2], |
214 | rpn[3], rpn[4], rpn[5]); |
215 | } |
216 | len = p - buf; |
217 | break; |
218 | default: |
219 | return 0; |
220 | } |
221 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: len); |
222 | } |
223 | KEY_OPS(rx_spec); |
224 | |
225 | static ssize_t key_replays_read(struct file *file, char __user *userbuf, |
226 | size_t count, loff_t *ppos) |
227 | { |
228 | struct ieee80211_key *key = file->private_data; |
229 | char buf[20]; |
230 | int len; |
231 | |
232 | switch (key->conf.cipher) { |
233 | case WLAN_CIPHER_SUITE_CCMP: |
234 | case WLAN_CIPHER_SUITE_CCMP_256: |
235 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , key->u.ccmp.replays); |
236 | break; |
237 | case WLAN_CIPHER_SUITE_AES_CMAC: |
238 | case WLAN_CIPHER_SUITE_BIP_CMAC_256: |
239 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , |
240 | key->u.aes_cmac.replays); |
241 | break; |
242 | case WLAN_CIPHER_SUITE_BIP_GMAC_128: |
243 | case WLAN_CIPHER_SUITE_BIP_GMAC_256: |
244 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , |
245 | key->u.aes_gmac.replays); |
246 | break; |
247 | case WLAN_CIPHER_SUITE_GCMP: |
248 | case WLAN_CIPHER_SUITE_GCMP_256: |
249 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , key->u.gcmp.replays); |
250 | break; |
251 | default: |
252 | return 0; |
253 | } |
254 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: len); |
255 | } |
256 | KEY_OPS(replays); |
257 | |
258 | static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, |
259 | size_t count, loff_t *ppos) |
260 | { |
261 | struct ieee80211_key *key = file->private_data; |
262 | char buf[20]; |
263 | int len; |
264 | |
265 | switch (key->conf.cipher) { |
266 | case WLAN_CIPHER_SUITE_AES_CMAC: |
267 | case WLAN_CIPHER_SUITE_BIP_CMAC_256: |
268 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , |
269 | key->u.aes_cmac.icverrors); |
270 | break; |
271 | case WLAN_CIPHER_SUITE_BIP_GMAC_128: |
272 | case WLAN_CIPHER_SUITE_BIP_GMAC_256: |
273 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , |
274 | key->u.aes_gmac.icverrors); |
275 | break; |
276 | default: |
277 | return 0; |
278 | } |
279 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: len); |
280 | } |
281 | KEY_OPS(icverrors); |
282 | |
283 | static ssize_t key_mic_failures_read(struct file *file, char __user *userbuf, |
284 | size_t count, loff_t *ppos) |
285 | { |
286 | struct ieee80211_key *key = file->private_data; |
287 | char buf[20]; |
288 | int len; |
289 | |
290 | if (key->conf.cipher != WLAN_CIPHER_SUITE_TKIP) |
291 | return -EINVAL; |
292 | |
293 | len = scnprintf(buf, size: sizeof(buf), fmt: "%u\n" , key->u.tkip.mic_failures); |
294 | |
295 | return simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: len); |
296 | } |
297 | KEY_OPS(mic_failures); |
298 | |
299 | static ssize_t key_key_read(struct file *file, char __user *userbuf, |
300 | size_t count, loff_t *ppos) |
301 | { |
302 | struct ieee80211_key *key = file->private_data; |
303 | int i, bufsize = 2 * key->conf.keylen + 2; |
304 | char *buf = kmalloc(size: bufsize, GFP_KERNEL); |
305 | char *p = buf; |
306 | ssize_t res; |
307 | |
308 | if (!buf) |
309 | return -ENOMEM; |
310 | |
311 | for (i = 0; i < key->conf.keylen; i++) |
312 | p += scnprintf(buf: p, size: bufsize + buf - p, fmt: "%02x" , key->conf.key[i]); |
313 | p += scnprintf(buf: p, size: bufsize+buf-p, fmt: "\n" ); |
314 | res = simple_read_from_buffer(to: userbuf, count, ppos, from: buf, available: p - buf); |
315 | kfree(objp: buf); |
316 | return res; |
317 | } |
318 | KEY_OPS(key); |
319 | |
320 | #define DEBUGFS_ADD(name) \ |
321 | debugfs_create_file(#name, 0400, key->debugfs.dir, \ |
322 | key, &key_##name##_ops) |
323 | #define DEBUGFS_ADD_W(name) \ |
324 | debugfs_create_file(#name, 0600, key->debugfs.dir, \ |
325 | key, &key_##name##_ops); |
326 | |
327 | void ieee80211_debugfs_key_add(struct ieee80211_key *key) |
328 | { |
329 | static int keycount; |
330 | char buf[100]; |
331 | struct sta_info *sta; |
332 | |
333 | if (!key->local->debugfs.keys) |
334 | return; |
335 | |
336 | sprintf(buf, fmt: "%d" , keycount); |
337 | key->debugfs.cnt = keycount; |
338 | keycount++; |
339 | key->debugfs.dir = debugfs_create_dir(name: buf, |
340 | parent: key->local->debugfs.keys); |
341 | |
342 | sta = key->sta; |
343 | if (sta) { |
344 | sprintf(buf, fmt: "../../netdev:%s/stations/%pM" , |
345 | sta->sdata->name, sta->sta.addr); |
346 | key->debugfs.stalink = |
347 | debugfs_create_symlink(name: "station" , parent: key->debugfs.dir, dest: buf); |
348 | } |
349 | |
350 | DEBUGFS_ADD(keylen); |
351 | DEBUGFS_ADD(flags); |
352 | DEBUGFS_ADD(keyidx); |
353 | DEBUGFS_ADD(hw_key_idx); |
354 | DEBUGFS_ADD(algorithm); |
355 | DEBUGFS_ADD_W(tx_spec); |
356 | DEBUGFS_ADD(rx_spec); |
357 | DEBUGFS_ADD(replays); |
358 | DEBUGFS_ADD(icverrors); |
359 | DEBUGFS_ADD(mic_failures); |
360 | DEBUGFS_ADD(key); |
361 | DEBUGFS_ADD(ifindex); |
362 | }; |
363 | |
364 | void ieee80211_debugfs_key_remove(struct ieee80211_key *key) |
365 | { |
366 | if (!key) |
367 | return; |
368 | |
369 | debugfs_remove_recursive(dentry: key->debugfs.dir); |
370 | key->debugfs.dir = NULL; |
371 | } |
372 | |
373 | void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) |
374 | { |
375 | char buf[50]; |
376 | struct ieee80211_key *key; |
377 | |
378 | if (!sdata->vif.debugfs_dir) |
379 | return; |
380 | |
381 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
382 | |
383 | debugfs_remove(dentry: sdata->debugfs.default_unicast_key); |
384 | sdata->debugfs.default_unicast_key = NULL; |
385 | |
386 | if (sdata->default_unicast_key) { |
387 | key = wiphy_dereference(sdata->local->hw.wiphy, |
388 | sdata->default_unicast_key); |
389 | sprintf(buf, fmt: "../keys/%d" , key->debugfs.cnt); |
390 | sdata->debugfs.default_unicast_key = |
391 | debugfs_create_symlink(name: "default_unicast_key" , |
392 | parent: sdata->vif.debugfs_dir, dest: buf); |
393 | } |
394 | |
395 | debugfs_remove(dentry: sdata->debugfs.default_multicast_key); |
396 | sdata->debugfs.default_multicast_key = NULL; |
397 | |
398 | if (sdata->deflink.default_multicast_key) { |
399 | key = wiphy_dereference(sdata->local->hw.wiphy, |
400 | sdata->deflink.default_multicast_key); |
401 | sprintf(buf, fmt: "../keys/%d" , key->debugfs.cnt); |
402 | sdata->debugfs.default_multicast_key = |
403 | debugfs_create_symlink(name: "default_multicast_key" , |
404 | parent: sdata->vif.debugfs_dir, dest: buf); |
405 | } |
406 | } |
407 | |
408 | void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) |
409 | { |
410 | char buf[50]; |
411 | struct ieee80211_key *key; |
412 | |
413 | if (!sdata->vif.debugfs_dir) |
414 | return; |
415 | |
416 | key = wiphy_dereference(sdata->local->hw.wiphy, |
417 | sdata->deflink.default_mgmt_key); |
418 | if (key) { |
419 | sprintf(buf, fmt: "../keys/%d" , key->debugfs.cnt); |
420 | sdata->debugfs.default_mgmt_key = |
421 | debugfs_create_symlink(name: "default_mgmt_key" , |
422 | parent: sdata->vif.debugfs_dir, dest: buf); |
423 | } else |
424 | ieee80211_debugfs_key_remove_mgmt_default(sdata); |
425 | } |
426 | |
427 | void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) |
428 | { |
429 | if (!sdata) |
430 | return; |
431 | |
432 | debugfs_remove(dentry: sdata->debugfs.default_mgmt_key); |
433 | sdata->debugfs.default_mgmt_key = NULL; |
434 | } |
435 | |
436 | void |
437 | ieee80211_debugfs_key_add_beacon_default(struct ieee80211_sub_if_data *sdata) |
438 | { |
439 | char buf[50]; |
440 | struct ieee80211_key *key; |
441 | |
442 | if (!sdata->vif.debugfs_dir) |
443 | return; |
444 | |
445 | key = wiphy_dereference(sdata->local->hw.wiphy, |
446 | sdata->deflink.default_beacon_key); |
447 | if (key) { |
448 | sprintf(buf, fmt: "../keys/%d" , key->debugfs.cnt); |
449 | sdata->debugfs.default_beacon_key = |
450 | debugfs_create_symlink(name: "default_beacon_key" , |
451 | parent: sdata->vif.debugfs_dir, dest: buf); |
452 | } else { |
453 | ieee80211_debugfs_key_remove_beacon_default(sdata); |
454 | } |
455 | } |
456 | |
457 | void |
458 | ieee80211_debugfs_key_remove_beacon_default(struct ieee80211_sub_if_data *sdata) |
459 | { |
460 | if (!sdata) |
461 | return; |
462 | |
463 | debugfs_remove(dentry: sdata->debugfs.default_beacon_key); |
464 | sdata->debugfs.default_beacon_key = NULL; |
465 | } |
466 | |
467 | void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, |
468 | struct sta_info *sta) |
469 | { |
470 | debugfs_remove(dentry: key->debugfs.stalink); |
471 | key->debugfs.stalink = NULL; |
472 | } |
473 | |