aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-12-18 20:03:29 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:59:48 -0500
commit41ade00f21a72d30911c6351a93823a491fffa39 (patch)
treed7e4e29c0d757414a5bad9089b1509fd5352ed8f
parenta1464ab61e66c96f9cffea335755de850fe8bdbd (diff)
cfg80211/nl80211: introduce key handling
This introduces key handling to cfg80211/nl80211. Default and group keys can be added, changed and removed; sequence counters for each key can be retrieved. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/nl80211.h34
-rw-r--r--include/net/cfg80211.h44
-rw-r--r--net/wireless/core.c3
-rw-r--r--net/wireless/nl80211.c289
4 files changed, 370 insertions, 0 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 538ee1dd3d0a..8dc807d9c29a 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -37,6 +37,16 @@
37 * userspace to request deletion of a virtual interface, then requires 37 * userspace to request deletion of a virtual interface, then requires
38 * attribute %NL80211_ATTR_IFINDEX. 38 * attribute %NL80211_ATTR_IFINDEX.
39 * 39 *
40 * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
41 * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
42 * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT or
43 * %NL80211_ATTR_KEY_THRESHOLD.
44 * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
45 * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC and %NL80211_ATTR_KEY_CIPHER
46 * attributes.
47 * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
48 * or %NL80211_ATTR_MAC.
49 *
40 * @NL80211_CMD_MAX: highest used command number 50 * @NL80211_CMD_MAX: highest used command number
41 * @__NL80211_CMD_AFTER_LAST: internal use 51 * @__NL80211_CMD_AFTER_LAST: internal use
42 */ 52 */
@@ -54,6 +64,11 @@ enum nl80211_commands {
54 NL80211_CMD_NEW_INTERFACE, 64 NL80211_CMD_NEW_INTERFACE,
55 NL80211_CMD_DEL_INTERFACE, 65 NL80211_CMD_DEL_INTERFACE,
56 66
67 NL80211_CMD_GET_KEY,
68 NL80211_CMD_SET_KEY,
69 NL80211_CMD_NEW_KEY,
70 NL80211_CMD_DEL_KEY,
71
57 /* add commands here */ 72 /* add commands here */
58 73
59 /* used to define NL80211_CMD_MAX below */ 74 /* used to define NL80211_CMD_MAX below */
@@ -75,6 +90,17 @@ enum nl80211_commands {
75 * @NL80211_ATTR_IFNAME: network interface name 90 * @NL80211_ATTR_IFNAME: network interface name
76 * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype 91 * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
77 * 92 *
93 * @NL80211_ATTR_MAC: MAC address (various uses)
94 *
95 * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
96 * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC
97 * keys
98 * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3)
99 * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
100 * section 7.3.2.25.1, e.g. 0x000FAC04)
101 * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
102 * CCMP keys, each six bytes in little endian
103 *
78 * @NL80211_ATTR_MAX: highest attribute number currently defined 104 * @NL80211_ATTR_MAX: highest attribute number currently defined
79 * @__NL80211_ATTR_AFTER_LAST: internal use 105 * @__NL80211_ATTR_AFTER_LAST: internal use
80 */ 106 */
@@ -89,6 +115,14 @@ enum nl80211_attrs {
89 NL80211_ATTR_IFNAME, 115 NL80211_ATTR_IFNAME,
90 NL80211_ATTR_IFTYPE, 116 NL80211_ATTR_IFTYPE,
91 117
118 NL80211_ATTR_MAC,
119
120 NL80211_ATTR_KEY_DATA,
121 NL80211_ATTR_KEY_IDX,
122 NL80211_ATTR_KEY_CIPHER,
123 NL80211_ATTR_KEY_SEQ,
124 NL80211_ATTR_KEY_DEFAULT,
125
92 /* add attributes here, update the policy in nl80211.c */ 126 /* add attributes here, update the policy in nl80211.c */
93 127
94 __NL80211_ATTR_AFTER_LAST, 128 __NL80211_ATTR_AFTER_LAST,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index d30960e1755c..3db7dfa53b6f 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -49,6 +49,26 @@ extern int ieee80211_radiotap_iterator_next(
49 struct ieee80211_radiotap_iterator *iterator); 49 struct ieee80211_radiotap_iterator *iterator);
50 50
51 51
52 /**
53 * struct key_params - key information
54 *
55 * Information about a key
56 *
57 * @key: key material
58 * @key_len: length of key material
59 * @cipher: cipher suite selector
60 * @seq: sequence counter (IV/PN) for TKIP and CCMP keys, only used
61 * with the get_key() callback, must be in little endian,
62 * length given by @seq_len.
63 */
64struct key_params {
65 u8 *key;
66 u8 *seq;
67 int key_len;
68 int seq_len;
69 u32 cipher;
70};
71
52/* from net/wireless.h */ 72/* from net/wireless.h */
53struct wiphy; 73struct wiphy;
54 74
@@ -71,6 +91,18 @@ struct wiphy;
71 * 91 *
72 * @change_virtual_intf: change type of virtual interface 92 * @change_virtual_intf: change type of virtual interface
73 * 93 *
94 * @add_key: add a key with the given parameters. @mac_addr will be %NULL
95 * when adding a group key.
96 *
97 * @get_key: get information about the key with the given parameters.
98 * @mac_addr will be %NULL when requesting information for a group
99 * key. All pointers given to the @callback function need not be valid
100 * after it returns.
101 *
102 * @del_key: remove a key given the @mac_addr (%NULL for a group key)
103 * and @key_index
104 *
105 * @set_default_key: set the default key on an interface
74 */ 106 */
75struct cfg80211_ops { 107struct cfg80211_ops {
76 int (*add_virtual_intf)(struct wiphy *wiphy, char *name, 108 int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -78,6 +110,18 @@ struct cfg80211_ops {
78 int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); 110 int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex);
79 int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex, 111 int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex,
80 enum nl80211_iftype type); 112 enum nl80211_iftype type);
113
114 int (*add_key)(struct wiphy *wiphy, struct net_device *netdev,
115 u8 key_index, u8 *mac_addr,
116 struct key_params *params);
117 int (*get_key)(struct wiphy *wiphy, struct net_device *netdev,
118 u8 key_index, u8 *mac_addr, void *cookie,
119 void (*callback)(void *cookie, struct key_params*));
120 int (*del_key)(struct wiphy *wiphy, struct net_device *netdev,
121 u8 key_index, u8 *mac_addr);
122 int (*set_default_key)(struct wiphy *wiphy,
123 struct net_device *netdev,
124 u8 key_index);
81}; 125};
82 126
83#endif /* __NET_CFG80211_H */ 127#endif /* __NET_CFG80211_H */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index febc33bc9c09..cfc5fc5f9e75 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -184,6 +184,9 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
184 struct cfg80211_registered_device *drv; 184 struct cfg80211_registered_device *drv;
185 int alloc_size; 185 int alloc_size;
186 186
187 WARN_ON(!ops->add_key && ops->del_key);
188 WARN_ON(ops->add_key && !ops->del_key);
189
187 alloc_size = sizeof(*drv) + sizeof_priv; 190 alloc_size = sizeof(*drv) + sizeof_priv;
188 191
189 drv = kzalloc(alloc_size, GFP_KERNEL); 192 drv = kzalloc(alloc_size, GFP_KERNEL);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 48b0d453e4e1..090936388528 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -61,6 +61,14 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
61 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, 61 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
62 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, 62 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
63 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, 63 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
64
65 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
66
67 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
68 .len = WLAN_MAX_KEY_LEN },
69 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
70 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
71 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
64}; 72};
65 73
66/* message building helper */ 74/* message building helper */
@@ -335,6 +343,263 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
335 return err; 343 return err;
336} 344}
337 345
346struct get_key_cookie {
347 struct sk_buff *msg;
348 int error;
349};
350
351static void get_key_callback(void *c, struct key_params *params)
352{
353 struct get_key_cookie *cookie = c;
354
355 if (params->key)
356 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
357 params->key_len, params->key);
358
359 if (params->seq)
360 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
361 params->seq_len, params->seq);
362
363 if (params->cipher)
364 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
365 params->cipher);
366
367 return;
368 nla_put_failure:
369 cookie->error = 1;
370}
371
372static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
373{
374 struct cfg80211_registered_device *drv;
375 int err;
376 struct net_device *dev;
377 u8 key_idx = 0;
378 u8 *mac_addr = NULL;
379 struct get_key_cookie cookie = {
380 .error = 0,
381 };
382 void *hdr;
383 struct sk_buff *msg;
384
385 if (info->attrs[NL80211_ATTR_KEY_IDX])
386 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
387
388 if (key_idx > 3)
389 return -EINVAL;
390
391 if (info->attrs[NL80211_ATTR_MAC])
392 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
393
394 err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
395 if (err)
396 return err;
397
398 if (!drv->ops->get_key) {
399 err = -EOPNOTSUPP;
400 goto out;
401 }
402
403 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
404 if (!msg) {
405 err = -ENOMEM;
406 goto out;
407 }
408
409 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
410 NL80211_CMD_NEW_KEY);
411
412 if (IS_ERR(hdr)) {
413 err = PTR_ERR(hdr);
414 goto out;
415 }
416
417 cookie.msg = msg;
418
419 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
420 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
421 if (mac_addr)
422 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
423
424 rtnl_lock();
425 err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
426 &cookie, get_key_callback);
427 rtnl_unlock();
428
429 if (err)
430 goto out;
431
432 if (cookie.error)
433 goto nla_put_failure;
434
435 genlmsg_end(msg, hdr);
436 err = genlmsg_unicast(msg, info->snd_pid);
437 goto out;
438
439 nla_put_failure:
440 err = -ENOBUFS;
441 nlmsg_free(msg);
442 out:
443 cfg80211_put_dev(drv);
444 dev_put(dev);
445 return err;
446}
447
448static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
449{
450 struct cfg80211_registered_device *drv;
451 int err;
452 struct net_device *dev;
453 u8 key_idx;
454
455 if (!info->attrs[NL80211_ATTR_KEY_IDX])
456 return -EINVAL;
457
458 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
459
460 if (key_idx > 3)
461 return -EINVAL;
462
463 /* currently only support setting default key */
464 if (!info->attrs[NL80211_ATTR_KEY_DEFAULT])
465 return -EINVAL;
466
467 err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
468 if (err)
469 return err;
470
471 if (!drv->ops->set_default_key) {
472 err = -EOPNOTSUPP;
473 goto out;
474 }
475
476 rtnl_lock();
477 err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx);
478 rtnl_unlock();
479
480 out:
481 cfg80211_put_dev(drv);
482 dev_put(dev);
483 return err;
484}
485
486static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
487{
488 struct cfg80211_registered_device *drv;
489 int err;
490 struct net_device *dev;
491 struct key_params params;
492 u8 key_idx = 0;
493 u8 *mac_addr = NULL;
494
495 memset(&params, 0, sizeof(params));
496
497 if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
498 return -EINVAL;
499
500 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
501 params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
502 params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
503 }
504
505 if (info->attrs[NL80211_ATTR_KEY_IDX])
506 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
507
508 params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
509
510 if (info->attrs[NL80211_ATTR_MAC])
511 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
512
513 if (key_idx > 3)
514 return -EINVAL;
515
516 /*
517 * Disallow pairwise keys with non-zero index unless it's WEP
518 * (because current deployments use pairwise WEP keys with
519 * non-zero indizes but 802.11i clearly specifies to use zero)
520 */
521 if (mac_addr && key_idx &&
522 params.cipher != WLAN_CIPHER_SUITE_WEP40 &&
523 params.cipher != WLAN_CIPHER_SUITE_WEP104)
524 return -EINVAL;
525
526 /* TODO: add definitions for the lengths to linux/ieee80211.h */
527 switch (params.cipher) {
528 case WLAN_CIPHER_SUITE_WEP40:
529 if (params.key_len != 5)
530 return -EINVAL;
531 break;
532 case WLAN_CIPHER_SUITE_TKIP:
533 if (params.key_len != 32)
534 return -EINVAL;
535 break;
536 case WLAN_CIPHER_SUITE_CCMP:
537 if (params.key_len != 16)
538 return -EINVAL;
539 break;
540 case WLAN_CIPHER_SUITE_WEP104:
541 if (params.key_len != 13)
542 return -EINVAL;
543 break;
544 default:
545 return -EINVAL;
546 }
547
548 err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
549 if (err)
550 return err;
551
552 if (!drv->ops->add_key) {
553 err = -EOPNOTSUPP;
554 goto out;
555 }
556
557 rtnl_lock();
558 err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
559 rtnl_unlock();
560
561 out:
562 cfg80211_put_dev(drv);
563 dev_put(dev);
564 return err;
565}
566
567static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
568{
569 struct cfg80211_registered_device *drv;
570 int err;
571 struct net_device *dev;
572 u8 key_idx = 0;
573 u8 *mac_addr = NULL;
574
575 if (info->attrs[NL80211_ATTR_KEY_IDX])
576 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
577
578 if (key_idx > 3)
579 return -EINVAL;
580
581 if (info->attrs[NL80211_ATTR_MAC])
582 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
583
584 err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
585 if (err)
586 return err;
587
588 if (!drv->ops->del_key) {
589 err = -EOPNOTSUPP;
590 goto out;
591 }
592
593 rtnl_lock();
594 err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
595 rtnl_unlock();
596
597 out:
598 cfg80211_put_dev(drv);
599 dev_put(dev);
600 return err;
601}
602
338static struct genl_ops nl80211_ops[] = { 603static struct genl_ops nl80211_ops[] = {
339 { 604 {
340 .cmd = NL80211_CMD_GET_WIPHY, 605 .cmd = NL80211_CMD_GET_WIPHY,
@@ -374,6 +639,30 @@ static struct genl_ops nl80211_ops[] = {
374 .policy = nl80211_policy, 639 .policy = nl80211_policy,
375 .flags = GENL_ADMIN_PERM, 640 .flags = GENL_ADMIN_PERM,
376 }, 641 },
642 {
643 .cmd = NL80211_CMD_GET_KEY,
644 .doit = nl80211_get_key,
645 .policy = nl80211_policy,
646 .flags = GENL_ADMIN_PERM,
647 },
648 {
649 .cmd = NL80211_CMD_SET_KEY,
650 .doit = nl80211_set_key,
651 .policy = nl80211_policy,
652 .flags = GENL_ADMIN_PERM,
653 },
654 {
655 .cmd = NL80211_CMD_NEW_KEY,
656 .doit = nl80211_new_key,
657 .policy = nl80211_policy,
658 .flags = GENL_ADMIN_PERM,
659 },
660 {
661 .cmd = NL80211_CMD_DEL_KEY,
662 .doit = nl80211_del_key,
663 .policy = nl80211_policy,
664 .flags = GENL_ADMIN_PERM,
665 },
377}; 666};
378 667
379/* multicast groups */ 668/* multicast groups */