📄 wlc_key.c
字号:
/* * Key Management routines for * Broadcom 802.11abg Networking Device Driver * * Copyright 2005-2006, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * * $Id$ */#include <wlc_cfg.h>#include <typedefs.h>#include <bcmdefs.h>#include <osl.h>#include <sbutils.h>#include <bcmutils.h>#include <wlioctl.h>#include <bcmwpa.h>#include <proto/802.11.h>#include <sbhndpio.h>#include <sbhnddma.h>#include <hnddma.h>#include <d11.h>#include <wlc_rate.h>#include <wlc_rate_sel.h>#include <wlc_key.h>#include <wlc_bsscfg.h>#include <wlc_channel.h>#include <wlc_pub.h>#include <wlc_bsscfg.h>#include <wlc_pio.h>#include <wlc.h>#include <wlc_scb.h>#include <wl_export.h>#define WLC_CHECK_WEP_UPDATE(wlc, update) (update)#define RC4_STATE_NBYTES 256 static int wlc_key_allocate(wlc_info_t *wlc);static void wlc_key_reset(wlc_info_t *wlc, wsec_key_t *key);static void wlc_key_hw_reallocate(wlc_info_t *wlc, int hw_index);static void wlc_key_hw_clear_all(wlc_info_t *wlc);static void wlc_key_write_addr(wlc_info_t *wlc, int i, const struct ether_addr *ea);/* * Find a free key slot in the wsec_keys array, and allocate a new key entry. */static intwlc_key_allocate(wlc_info_t *wlc){ int max_keys = WLC_MAX_WSEC_KEYS(wlc); int i; /* find a free key */ for (i = WSEC_MAX_DEFAULT_KEYS; i < max_keys; i++) { if (wlc->wsec_keys[i] == NULL) break; } if (i >= max_keys) { WL_WSEC(("wl%d: out of per station key slots\n", wlc->pub.unit)); return BCME_NORESOURCE; } wlc->wsec_keys[i] = MALLOC(wlc->pub.osh, sizeof(wsec_key_t)); if (wlc->wsec_keys[i] == NULL) { WL_ERROR(("wl%d: wlc_key_allocate: out of memory, malloced %d bytes\n", wlc->pub.unit, MALLOCED(wlc->pub.osh))); return BCME_NOMEM; } return i;}/* * Insert a key. At exit the key can be used as an rx key * but not tx. wlc_set_crypto_index() needs to be called for that. */intwlc_key_insert(wlc_info_t *wlc, wlc_bsscfg_t *bsscfg, uint32 key_len, uint32 key_id, uint32 key_flags, uint32 key_algo, uint8 *key_data, struct ether_addr *key_ea, wsec_iv_t *initial_iv){ struct scb *scb = NULL; struct scb *scb_otherband = NULL; wsec_key_t *key; int wsec_idx; WL_WSEC(("wl%d: wlc_key_insert: index %d keylen %d addr %s\n", wlc->pub.unit, key_id, key_len, bcm_ether_ntoa(key_ea, eabuf))); ASSERT(bsscfg); /* check length and IV index */ if (key_len != WEP1_KEY_SIZE && key_len != WEP128_KEY_SIZE && key_len != TKIP_KEY_SIZE && key_len != AES_KEY_SIZE) { WL_ERROR(("wl%d: wlc_key_insert: unsupported key size %d\n", wlc->pub.unit, key_len)); return BCME_BADLEN; } if (key_id >= WSEC_MAX_DEFAULT_KEYS) { WL_ERROR(("wl%d: wlc_key_insert: illegal key index %d\n", wlc->pub.unit, key_id)); return BCME_BADKEYIDX; } /* set a default key */ if (ETHER_ISNULLADDR(key_ea) || ETHER_ISBCAST(key_ea)) { if (BSSCFG_STA(bsscfg)) { /* * If the bsscfg key pointer has not already been set, set it to point to * the statically allocated memory here. */ if (bsscfg->bss_def_keys[key_id] == NULL) { bsscfg->bss_def_keys[key_id] = &wlc->wsec_def_keys[key_id]; } ASSERT(bsscfg->bss_def_keys[key_id]->idx == key_id); wsec_idx = key_id; } else { if (bsscfg->bss_def_keys[key_id]) { wsec_idx = WSEC_KEY_INDEX(wlc, bsscfg->bss_def_keys[key_id]); ASSERT((wsec_idx >= 0) && (wsec_idx < WLC_MAX_WSEC_KEYS(wlc))); ASSERT(bsscfg->bss_def_keys[key_id] == wlc->wsec_keys[wsec_idx]); } else { /* Get a new key. */ if ((wsec_idx = wlc_key_allocate(wlc)) < 0) return wsec_idx; bsscfg->bss_def_keys[key_id] = wlc->wsec_keys[wsec_idx]; } } } /* set a per station key */ else { if (wlc->pub.associated && wlc->band->bandunit != CHANNEL_BANDUNIT(wlc, wlc->pub.current_bss.channel)) { } if (!(scb = wlc_scblookup(wlc, key_ea))) { WL_ERROR(("wl%d: out of scbs\n", wlc->pub.unit)); return BCME_NOTFOUND; } if (scb->key) { /* replace the existing scb key */ wsec_idx = WSEC_KEY_INDEX(wlc, scb->key); ASSERT((wsec_idx >= WSEC_MAX_DEFAULT_KEYS) && (wsec_idx < WLC_MAX_WSEC_KEYS(wlc))); ASSERT(scb->key == wlc->wsec_keys[wsec_idx]); } else if (NBANDS(wlc) > 1 && (scb_otherband = wlc_scbfindband(wlc, key_ea, OTHERBANDUNIT(wlc))) && scb_otherband->key) { /* use the same key for scbs with the same MAC in each band */ wsec_idx = WSEC_KEY_INDEX(wlc, scb_otherband->key); ASSERT((wsec_idx >= WSEC_MAX_DEFAULT_KEYS) && (wsec_idx < WLC_MAX_WSEC_KEYS(wlc))); ASSERT(scb_otherband->key == wlc->wsec_keys[wsec_idx]); scb->key = wlc->wsec_keys[wsec_idx]; } else { /* Get a new key. */ if ((wsec_idx = wlc_key_allocate(wlc)) < 0) return wsec_idx; scb->key = wlc->wsec_keys[wsec_idx]; } } WL_WSEC(("wlc_key_insert: using the key idx %d, key_id is %d\n", wsec_idx, key_id)); /* update the key */ /* The following line can't use WSEC_KEY macro, since the len might be 0. */ key = wlc->wsec_keys[wsec_idx]; bzero((char*)key, sizeof(wsec_key_t)); key->idx = (uint8)wsec_idx; bcopy((char*)key_data, (char*)key->data, key_len); key->len = (uint8)key_len; key->id = (uint8)key_id; bcopy((char*)key_ea, (char*)&key->ea, ETHER_ADDR_LEN); key->aes_mode = AES_MODE_NONE; switch (key_len) { case WEP1_KEY_SIZE: WL_WSEC(("wl%d: wlc_key_insert: WEP (40-bit)\n", wlc->pub.unit)); key->algo = CRYPTO_ALGO_WEP1; key->algo_hw = WSEC_ALGO_WEP1; key->iv_len = DOT11_IV_LEN; key->icv_len = DOT11_ICV_LEN; break; case WEP128_KEY_SIZE: WL_WSEC(("wl%d: wlc_key_insert: WEP (128-bit)\n", wlc->pub.unit)); key->algo = CRYPTO_ALGO_WEP128; key->algo_hw = WSEC_ALGO_WEP128; key->iv_len = DOT11_IV_LEN; key->icv_len = DOT11_ICV_LEN; break; case TKIP_KEY_SIZE: WL_WSEC(("wl%d: wlc_key_insert: TKIP\n", wlc->pub.unit)); key->algo = CRYPTO_ALGO_TKIP; key->algo_hw = WSEC_ALGO_TKIP; key->iv_len = DOT11_IV_TKIP_LEN; key->icv_len = DOT11_ICV_LEN; break; case AES_KEY_SIZE: switch (key_algo) { case CRYPTO_ALGO_AES_OCB_MSDU : case CRYPTO_ALGO_AES_OCB_MPDU: WL_WSEC(("wl%d: wlc_key_insert: AES\n", wlc->pub.unit)); key->algo = (uint8)key_algo; key->algo_hw = WSEC_ALGO_AES; key->iv_len = DOT11_IV_AES_OCB_LEN; key->icv_len = DOT11_ICV_AES_LEN; if (key->algo == CRYPTO_ALGO_AES_OCB_MSDU) key->aes_mode = AES_MODE_OCB_MSDU; else key->aes_mode = AES_MODE_OCB_MPDU; break; case CRYPTO_ALGO_AES_CCM: default: WL_WSEC(("wl%d: wlc_key_insert: AES\n", wlc->pub.unit)); key->algo = CRYPTO_ALGO_AES_CCM; if (scb && (scb->flags & SCB_LEGACY_AES)) { WL_WSEC(("wl%d: wlc_key_insert: setting pairwise key for Legacy" " AES\n", wlc->pub.unit)); key->algo_hw = WSEC_ALGO_AES_LEGACY; } else { /* * If this STA is talking to a legacy AP, mark the group key * as legacy also. */ if (BSSCFG_STA(bsscfg)) { struct scb *apscb = NULL; if (!(apscb = wlc_scbfind(wlc, &wlc->pub.current_bss.BSSID))) { WL_ERROR(("wl%d: wlc_key_insert: scb for associated" " AP not found\n", wlc->pub.unit)); return BCME_NOTFOUND; } if (apscb->flags & SCB_LEGACY_AES) { WL_WSEC(("wl%d: wlc_key_insert: setting group key" " for Legacy AES\n", wlc->pub.unit)); key->algo_hw = WSEC_ALGO_AES_LEGACY; } else key->algo_hw = WSEC_ALGO_AES; } else key->algo_hw = WSEC_ALGO_AES; } key->iv_len = DOT11_IV_AES_CCM_LEN; key->icv_len = DOT11_ICV_AES_LEN; key->aes_mode = AES_MODE_CCM; break; } break; } /* set new default key */ if (ETHER_ISNULLADDR(key_ea) || ( (key->algo == CRYPTO_ALGO_WEP1 || key->algo == CRYPTO_ALGO_WEP128) && ETHER_ISBCAST(key_ea))) { if (key_flags & WL_PRIMARY_KEY) { if (WSEC_BSS_DEFAULT_KEY(bsscfg)) { WSEC_BSS_DEFAULT_KEY(bsscfg)->flags &= ~WSEC_PRIMARY_KEY; } bsscfg->wsec_index = key_id; key->flags |= WSEC_PRIMARY_KEY; } else if ((uint32)bsscfg->wsec_index == key_id) { /* this key was the primary, but the key insert cleared the flag */ bsscfg->wsec_index = -1; } } /* call the tkip module for the key setup */ if (key->algo == CRYPTO_ALGO_TKIP) { wl_tkip_keyset(wlc->wl, key); } WL_WSEC(("wl%d: wlc_key_insert: key algo %d algo_hw %d flags %d\n", wlc->pub.unit, key->algo, key->algo_hw, key->flags)); /* check for a provided IV init value */ wlc_key_iv_init(wlc, key, initial_iv); wlc_key_update(wlc, bsscfg, wsec_idx); if (BSSCFG_STA(bsscfg) && (wsec_idx < WSEC_MAX_DEFAULT_KEYS)) { bool prev_psallowed = PS_ALLOWED(wlc); /* * A default key is used as an indication of the presence of a group * key, which in turn indicates whether the port is considered open * or closed */ wlc->wsec_portopen = TRUE; /* * If PS_ALLOWED was waiting on the portopen state then * initiate the PM state change */ if (!prev_psallowed && PS_ALLOWED(wlc)) wlc_set_pmstate(wlc, TRUE); } return 0;}/* * If the MSSID indication flag has been changed, the default config must * adapt its default key table to reflect the current status of Multi-SSID: * if Multi-SSID is enabled, the four statically allocated keys which * correspond to the hardware default keys cannot be used, whereas if * Multi-SSID is not enabled, it is better to use the hardware default keys. * Thus, if the mssid flag is changed, the type of keys used must also be * changed, and any pre-existing keys must be copied over. */voidwlc_key_mssid_change(wlc_info_t *wlc, bool old_mssid, bool new_mssid){ int ii; int index; int wsec_index; wsec_key_t *key; WL_WSEC(("\nwl%d: wlc_key_mssid_change: old mssid val: %d, new mssid val: %d\n", wlc->pub.unit, old_mssid, new_mssid)); if (old_mssid == new_mssid) return; wsec_index = wlc->bsscfg[0]->wsec_index; for (ii = 0; ii < WLC_DEFAULT_KEYS; ii++) { if (wlc->bsscfg[0]->bss_def_keys[ii]) { key = wlc->bsscfg[0]->bss_def_keys[ii]; wlc->bsscfg[0]->bss_def_keys[ii] = NULL; ASSERT(key->idx >= WSEC_MAX_DEFAULT_KEYS); index = ii; wlc->bsscfg[0]->bss_def_keys[ii] = &wlc->wsec_def_keys[ii]; bcopy((char*)key, (char*)wlc->bsscfg[0]->bss_def_keys[ii], sizeof(wsec_key_t)); wlc->bsscfg[0]->bss_def_keys[ii]->idx = (uint8)index; if (key->idx >= WSEC_MAX_DEFAULT_KEYS) wlc_key_delete(wlc, wlc->bsscfg[0], key->idx); } } wlc->bsscfg[0]->wsec_index = wsec_index;}voidwlc_key_remove_all(wlc_info_t *wlc){ int i; for (i = 0; i < WLC_MAX_WSEC_KEYS(wlc); i++) wlc_key_delete(wlc, wlc->bsscfg[0], i);}voidwlc_key_remove(wlc_info_t *wlc, wlc_bsscfg_t *bsscfg, wl_wsec_key_t *remove){ struct scb *scb; wsec_key_t *key = NULL; /* remove a default key */ if (ETHER_ISNULLADDR(&remove->ea) || ETHER_ISBCAST(&remove->ea)) { if (remove->index < WSEC_MAX_DEFAULT_KEYS) { key = bsscfg->bss_def_keys[remove->index];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -