diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-12-18 20:03:29 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 17:59:48 -0500 |
commit | 41ade00f21a72d30911c6351a93823a491fffa39 (patch) | |
tree | d7e4e29c0d757414a5bad9089b1509fd5352ed8f /net/wireless | |
parent | a1464ab61e66c96f9cffea335755de850fe8bdbd (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>
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 3 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 289 |
2 files changed, 292 insertions, 0 deletions
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 | ||
346 | struct get_key_cookie { | ||
347 | struct sk_buff *msg; | ||
348 | int error; | ||
349 | }; | ||
350 | |||
351 | static 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 | |||
372 | static 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 | |||
448 | static 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 | |||
486 | static 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(¶ms, 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, ¶ms); | ||
559 | rtnl_unlock(); | ||
560 | |||
561 | out: | ||
562 | cfg80211_put_dev(drv); | ||
563 | dev_put(dev); | ||
564 | return err; | ||
565 | } | ||
566 | |||
567 | static 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 | |||
338 | static struct genl_ops nl80211_ops[] = { | 603 | static 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 */ |