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