if (key->flags & KEY_FLAG_TAINTED) { /* If we get here, it's during resume and the key is *taintedsoshouldn'tbeused/programmedanymore. *However,itsflagsmaystillindicatethatitwas *programmedintothedevice(sincewe'reinresume) *soclearthatflagnowtoavoidtryingtoremove *itagainlater.
*/ if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
!(key->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
increment_tailroom_need_count(sdata);
if (ret != -ENOSPC && ret != -EOPNOTSUPP && ret != 1)
sdata_err(sdata, "failed to set key (%d, %pM) to hardware (%d)\n",
key->conf.keyidx,
sta ? sta->sta.addr : bcast_addr, ret);
out_unsupported: switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256: case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_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: /* all of these we can do in software - if driver can */ if (ret == 1) return0; if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL)) return -EINVAL; return0; default: return -EINVAL;
}
}
if (new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) { /* Extended Key ID key install, initial one or rekey */
if (sta->ptk_idx != INVALID_PTK_KEYIDX &&
!ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT)) { /* Aggregation Sessions with Extended Key ID must not *mixMPDUswithdifferentkeyIDswithinoneA-MPDU. *TeardownrunningTxaggregationsessionsandblock *newRx/Txaggregationrequestsduringrekeyto *ensuretherearenoA-MPDUswhenthedriverisnot *supportingA-MPDUkeyborders.(BlockingTxonly *wouldbesufficientbutWLAN_STA_BLOCK_BAgetsthe *jobdoneforthefewmsweneedit.)
*/
set_sta_flag(sta, WLAN_STA_BLOCK_BA); for (i = 0; i < IEEE80211_NUM_TIDS; i++)
__ieee80211_stop_tx_ba_session(sta, i,
AGG_STOP_LOCAL_REQUEST);
}
} elseif (old) { /* Rekey without Extended Key ID. *AggregationsessionsareOKwhenrunningonSWcrypto. *AbrokenremoteSTAmaycauseissuesnotobservedwithHW *crypto,though.
*/ if (!(old->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) return;
/* Stop Tx till we are on the new key */
old->flags |= KEY_FLAG_TAINTED;
ieee80211_clear_fast_xmit(sta); if (ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION)) {
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
ieee80211_sta_tear_down_BA_sessions(sta,
AGG_STOP_LOCAL_REQUEST);
} if (!wiphy_ext_feature_isset(local->hw.wiphy,
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0)) {
pr_warn_ratelimited("Rekeying PTK for STA %pM but driver can't safely do that.",
sta->sta.addr); /* Flushing the driver queues *may* help prevent *thecleartextleaksandfreezes.
*/
ieee80211_flush_queues(local, old->sdata, false);
}
}
}
if (WARN(old && old->conf.link_id != link_id, "old link ID %d doesn't match new link ID %d\n",
old->conf.link_id, link_id)) return -EINVAL;
if (link_id >= 0) { if (!link) {
link = sdata_dereference(sdata->link[link_id], sdata); if (!link) return -ENOLINK;
}
if (sta) {
link_sta = rcu_dereference_protected(sta->link[link_id],
lockdep_is_held(&sta->local->hw.wiphy->mtx)); if (!link_sta) return -ENOLINK;
}
} else {
link = &sdata->deflink;
}
if ((is_wep || pairwise) && idx >= NUM_DEFAULT_KEYS) return -EINVAL;
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
if (new && sta && pairwise) { /* Unicast rekey needs special handling. With Extended Key ID *oldisstillNULLforthefirstrekey.
*/
ieee80211_pairwise_rekey(old, new);
}
if (old) { if (old->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
ieee80211_key_disable_hw_accel(old);
if (new)
ret = ieee80211_key_enable_hw_accel(new);
}
} else { if (!new->local->wowlan) {
ret = ieee80211_key_enable_hw_accel(new);
} elseif (link_id < 0 || !sdata->vif.active_links ||
BIT(link_id) & sdata->vif.active_links) { new->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; if (!(new->conf.flags & (IEEE80211_KEY_FLAG_GENERATE_MMIC |
IEEE80211_KEY_FLAG_PUT_MIC_SPACE |
IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
decrease_tailroom_need_count(sdata, 1);
}
}
if (ret) return ret;
if (new)
list_add_tail_rcu(&new->list, &sdata->key_list);
if (sta) { if (pairwise) {
rcu_assign_pointer(sta->ptk[idx], new); if (new &&
!(new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX))
_ieee80211_set_tx_key(new, true);
} else {
rcu_assign_pointer(link_sta->gtk[idx], new);
} /* Only needed for transition from no key -> key. *StilltriggersunnecessarywhenusingExtendedKeyID *andinstallingthesecondkeyIDthefirsttime.
*/ if (new && !old)
ieee80211_check_fast_rx(sta);
} else {
defunikey = old &&
old == wiphy_dereference(sdata->local->hw.wiphy,
sdata->default_unicast_key);
defmultikey = old &&
old == wiphy_dereference(sdata->local->hw.wiphy,
link->default_multicast_key);
defmgmtkey = old &&
old == wiphy_dereference(sdata->local->hw.wiphy,
link->default_mgmt_key);
defbeaconkey = old &&
old == wiphy_dereference(sdata->local->hw.wiphy,
link->default_beacon_key);
if (defunikey && !new)
__ieee80211_set_default_key(link, -1, true, false); if (defmultikey && !new)
__ieee80211_set_default_key(link, -1, false, true); if (defmgmtkey && !new)
__ieee80211_set_default_mgmt_key(link, -1); if (defbeaconkey && !new)
__ieee80211_set_default_beacon_key(link, -1);
if (is_wep || pairwise)
rcu_assign_pointer(sdata->keys[idx], new); else
rcu_assign_pointer(link->gtk[idx], new);
if (defunikey && new)
__ieee80211_set_default_key(link, new->conf.keyidx, true, false); if (defmultikey && new)
__ieee80211_set_default_key(link, new->conf.keyidx, false, true); if (defmgmtkey && new)
__ieee80211_set_default_mgmt_key(link, new->conf.keyidx); if (defbeaconkey && new)
__ieee80211_set_default_beacon_key(link, new->conf.keyidx);
}
if (old)
list_del_rcu(&old->list);
return0;
}
struct ieee80211_key *
ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, const u8 *key_data,
size_t seq_len, const u8 *seq)
{ struct ieee80211_key *key; int i, j, err;
key->conf.link_id = -1;
key->conf.cipher = cipher;
key->conf.keyidx = idx;
key->conf.keylen = key_len; switch (cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104:
key->conf.iv_len = IEEE80211_WEP_IV_LEN;
key->conf.icv_len = IEEE80211_WEP_ICV_LEN; break; case WLAN_CIPHER_SUITE_TKIP:
key->conf.iv_len = IEEE80211_TKIP_IV_LEN;
key->conf.icv_len = IEEE80211_TKIP_ICV_LEN; if (seq) { for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
key->u.tkip.rx[i].iv32 =
get_unaligned_le32(&seq[2]);
key->u.tkip.rx[i].iv16 =
get_unaligned_le16(seq);
}
}
spin_lock_init(&key->u.tkip.txlock); break; case WLAN_CIPHER_SUITE_CCMP:
key->conf.iv_len = IEEE80211_CCMP_HDR_LEN;
key->conf.icv_len = IEEE80211_CCMP_MIC_LEN; if (seq) { for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) for (j = 0; j < IEEE80211_CCMP_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
seq[IEEE80211_CCMP_PN_LEN - j - 1];
} /* *InitializeAESkeystatehereasanoptimizationsothat *itdoesnotneedtobeinitializedforeverypacket.
*/
key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
key_data, key_len, IEEE80211_CCMP_MIC_LEN); if (IS_ERR(key->u.ccmp.tfm)) {
err = PTR_ERR(key->u.ccmp.tfm);
kfree(key); return ERR_PTR(err);
} break; case WLAN_CIPHER_SUITE_CCMP_256:
key->conf.iv_len = IEEE80211_CCMP_256_HDR_LEN;
key->conf.icv_len = IEEE80211_CCMP_256_MIC_LEN; for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++) for (j = 0; j < IEEE80211_CCMP_256_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
seq[IEEE80211_CCMP_256_PN_LEN - j - 1]; /* Initialize AES key state here as an optimization so that *itdoesnotneedtobeinitializedforeverypacket.
*/
key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
key_data, key_len, IEEE80211_CCMP_256_MIC_LEN); if (IS_ERR(key->u.ccmp.tfm)) {
err = PTR_ERR(key->u.ccmp.tfm);
kfree(key); return ERR_PTR(err);
} break; case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_CMAC_256:
key->conf.iv_len = 0; if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
key->conf.icv_len = sizeof(struct ieee80211_mmie); else
key->conf.icv_len = sizeof(struct ieee80211_mmie_16); if (seq) for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++)
key->u.aes_cmac.rx_pn[j] =
seq[IEEE80211_CMAC_PN_LEN - j - 1]; /* *InitializeAESkeystatehereasanoptimizationsothat *itdoesnotneedtobeinitializedforeverypacket.
*/
key->u.aes_cmac.tfm =
ieee80211_aes_cmac_key_setup(key_data, key_len); if (IS_ERR(key->u.aes_cmac.tfm)) {
err = PTR_ERR(key->u.aes_cmac.tfm);
kfree(key); return ERR_PTR(err);
} break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256:
key->conf.iv_len = 0;
key->conf.icv_len = sizeof(struct ieee80211_mmie_16); if (seq) for (j = 0; j < IEEE80211_GMAC_PN_LEN; j++)
key->u.aes_gmac.rx_pn[j] =
seq[IEEE80211_GMAC_PN_LEN - j - 1]; /* Initialize AES key state here as an optimization so that *itdoesnotneedtobeinitializedforeverypacket.
*/
key->u.aes_gmac.tfm =
ieee80211_aes_gmac_key_setup(key_data, key_len); if (IS_ERR(key->u.aes_gmac.tfm)) {
err = PTR_ERR(key->u.aes_gmac.tfm);
kfree(key); return ERR_PTR(err);
} break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
key->conf.iv_len = IEEE80211_GCMP_HDR_LEN;
key->conf.icv_len = IEEE80211_GCMP_MIC_LEN; for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++) for (j = 0; j < IEEE80211_GCMP_PN_LEN; j++)
key->u.gcmp.rx_pn[i][j] =
seq[IEEE80211_GCMP_PN_LEN - j - 1]; /* Initialize AES key state here as an optimization so that *itdoesnotneedtobeinitializedforeverypacket.
*/
key->u.gcmp.tfm = ieee80211_aes_gcm_key_setup_encrypt(key_data,
key_len); if (IS_ERR(key->u.gcmp.tfm)) {
err = PTR_ERR(key->u.gcmp.tfm);
kfree(key); return ERR_PTR(err);
} break;
}
memcpy(key->conf.key, key_data, key_len);
INIT_LIST_HEAD(&key->list);
return key;
}
staticvoid ieee80211_key_free_common(struct ieee80211_key *key)
{ switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256:
ieee80211_aes_key_free(key->u.ccmp.tfm); break; case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_CMAC_256:
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256:
ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm); break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
ieee80211_aes_gcm_key_free(key->u.gcmp.tfm); break;
}
kfree_sensitive(key);
}
/* The rekey code assumes that the old and new key are using *thesamecipher.Enforcetheassumptionforpairwisekeys.
*/ if ((alt_key && alt_key->conf.cipher != key->conf.cipher) ||
(old_key && old_key->conf.cipher != key->conf.cipher)) {
ret = -EOPNOTSUPP; goto out;
}
} elseif (sta) { struct link_sta_info *link_sta = &sta->deflink; int link_id = key->conf.link_id;
if (link_id >= 0) {
link_sta = rcu_dereference_protected(sta->link[link_id],
lockdep_is_held(&sta->local->hw.wiphy->mtx)); if (!link_sta) {
ret = -ENOLINK; goto out;
}
}
/* Non-pairwise keys must also not switch the cipher on rekey */ if (!pairwise) { if (old_key && old_key->conf.cipher != key->conf.cipher) {
ret = -EOPNOTSUPP; goto out;
}
}
/* *Silentlyacceptkeyre-installationwithoutreallyinstallingthe *newversionofthekeytoavoidnoncereuseorreplayissues.
*/ if (ieee80211_key_identical(sdata, old_key, key)) {
ret = -EALREADY; goto out;
}
if (WARN_ON(!local->wowlan)) return ERR_PTR(-EINVAL);
if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) return ERR_PTR(-EINVAL);
if (WARN_ON(idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS +
NUM_DEFAULT_BEACON_KEYS)) return ERR_PTR(-EINVAL);
prev_key = wiphy_dereference(local->hw.wiphy,
link_data->gtk[idx]); if (!prev_key) { if (idx < NUM_DEFAULT_KEYS) { for (int i = 0; i < NUM_DEFAULT_KEYS; i++) { if (i == idx) continue;
prev_key = wiphy_dereference(local->hw.wiphy,
link_data->gtk[i]); if (prev_key) break;
}
} else { /* For IGTK we have 4 and 5 and for BIGTK - 6 and 7 */
prev_key = wiphy_dereference(local->hw.wiphy,
link_data->gtk[idx ^ 1]);
}
}
if (WARN_ON(!prev_key)) return ERR_PTR(-EINVAL);
if (WARN_ON(key_len < prev_key->conf.keylen)) return ERR_PTR(-EINVAL);
switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_CMAC_256:
key->u.aes_cmac.icverrors++; break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256:
key->u.aes_gmac.icverrors++; break; default: /* ignore the others for now, we don't keep counters now */ break;
}
}
EXPORT_SYMBOL_GPL(ieee80211_key_mic_failure);
switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_CCMP_256:
key->u.ccmp.replays++; break; case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_CMAC_256:
key->u.aes_cmac.replays++; break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256:
key->u.aes_gmac.replays++; break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
key->u.gcmp.replays++; break;
}
}
EXPORT_SYMBOL_GPL(ieee80211_key_replay);
int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata, unsignedlong del_links_mask, unsignedlong add_links_mask)
{ struct ieee80211_key *key; int ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.