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