| /* |
| * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: Public APIs for crypto service |
| */ |
| |
| #include <qdf_types.h> |
| #include <wlan_cmn.h> |
| #include <wlan_objmgr_cmn.h> |
| #include <wlan_objmgr_global_obj.h> |
| #include <wlan_objmgr_psoc_obj.h> |
| #include <wlan_objmgr_pdev_obj.h> |
| #include <wlan_objmgr_vdev_obj.h> |
| #include <wlan_objmgr_peer_obj.h> |
| #include <wlan_utility.h> |
| |
| #include "wlan_crypto_global_def.h" |
| #include "wlan_crypto_global_api.h" |
| #include "wlan_crypto_def_i.h" |
| #include "wlan_crypto_param_handling_i.h" |
| #include "wlan_crypto_obj_mgr_i.h" |
| #include <qdf_module.h> |
| |
| const struct wlan_crypto_cipher *wlan_crypto_cipher_ops[WLAN_CRYPTO_CIPHER_MAX]; |
| |
| #define WPA_ADD_CIPHER_TO_SUITE(frm, cipher) \ |
| WLAN_CRYPTO_ADDSELECTOR(frm,\ |
| wlan_crypto_wpa_cipher_to_suite(cipher)) |
| |
| #define RSN_ADD_CIPHER_TO_SUITE(frm, cipher) \ |
| WLAN_CRYPTO_ADDSELECTOR(frm,\ |
| wlan_crypto_rsn_cipher_to_suite(cipher)) |
| |
| #define WPA_ADD_KEYMGMT_TO_SUITE(frm, keymgmt)\ |
| WLAN_CRYPTO_ADDSELECTOR(frm,\ |
| wlan_crypto_wpa_keymgmt_to_suite(keymgmt)) |
| |
| #define RSN_ADD_KEYMGMT_TO_SUITE(frm, keymgmt)\ |
| WLAN_CRYPTO_ADDSELECTOR(frm,\ |
| wlan_crypto_rsn_keymgmt_to_suite(keymgmt)) |
| |
| /** |
| * wlan_crypto_vdev_get_crypto_params - called by mlme to get crypto params |
| * @vdev:vdev |
| * |
| * This function gets called by mlme to get crypto params |
| * |
| * Return: wlan_crypto_params or NULL in case of failure |
| */ |
| static struct wlan_crypto_params *wlan_crypto_vdev_get_comp_params( |
| struct wlan_objmgr_vdev *vdev, |
| struct wlan_crypto_comp_priv **crypto_priv){ |
| *crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| if (!(*crypto_priv)) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| |
| return &((*crypto_priv)->crypto_params); |
| } |
| |
| /** |
| * wlan_crypto_peer_get_crypto_params - called by mlme to get crypto params |
| * @peer:peer |
| * |
| * This function gets called by mlme to get crypto params |
| * |
| * Return: wlan_crypto_params or NULL in case of failure |
| */ |
| static struct wlan_crypto_params *wlan_crypto_peer_get_comp_params( |
| struct wlan_objmgr_peer *peer, |
| struct wlan_crypto_comp_priv **crypto_priv){ |
| |
| *crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_peer_crypto_obj(peer); |
| if (!*crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| |
| return &((*crypto_priv)->crypto_params); |
| } |
| |
| static QDF_STATUS wlan_crypto_set_igtk_key(struct wlan_crypto_key *key) |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_set_param - called by ucfg to set crypto param |
| * @crypto_params: crypto_params |
| * @param: param to be set. |
| * @value: value |
| * |
| * This function gets called from ucfg to set param |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| static QDF_STATUS wlan_crypto_set_param(struct wlan_crypto_params *crypto_params, |
| wlan_crypto_param_type param, |
| uint32_t value){ |
| QDF_STATUS status = QDF_STATUS_E_INVAL; |
| |
| crypto_debug("param %d, value %d", param, value); |
| switch (param) { |
| case WLAN_CRYPTO_PARAM_AUTH_MODE: |
| status = wlan_crypto_set_authmode(crypto_params, value); |
| break; |
| case WLAN_CRYPTO_PARAM_UCAST_CIPHER: |
| status = wlan_crypto_set_ucastciphers(crypto_params, value); |
| break; |
| case WLAN_CRYPTO_PARAM_MCAST_CIPHER: |
| status = wlan_crypto_set_mcastcipher(crypto_params, value); |
| break; |
| case WLAN_CRYPTO_PARAM_MGMT_CIPHER: |
| status = wlan_crypto_set_mgmtcipher(crypto_params, value); |
| break; |
| case WLAN_CRYPTO_PARAM_CIPHER_CAP: |
| status = wlan_crypto_set_cipher_cap(crypto_params, value); |
| break; |
| case WLAN_CRYPTO_PARAM_RSN_CAP: |
| status = wlan_crypto_set_rsn_cap(crypto_params, value); |
| break; |
| case WLAN_CRYPTO_PARAM_KEY_MGMT: |
| status = wlan_crypto_set_key_mgmt(crypto_params, value); |
| break; |
| default: |
| status = QDF_STATUS_E_INVAL; |
| } |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_set_vdev_param - called by ucfg to set crypto param |
| * @vdev: vdev |
| * @param: param to be set. |
| * @value: value |
| * |
| * This function gets called from ucfg to set param |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_set_vdev_param(struct wlan_objmgr_vdev *vdev, |
| wlan_crypto_param_type param, |
| uint32_t value){ |
| QDF_STATUS status = QDF_STATUS_E_INVAL; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = &(crypto_priv->crypto_params); |
| |
| status = wlan_crypto_set_param(crypto_params, param, value); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_set_param - called by ucfg to set crypto param |
| * |
| * @peer: peer |
| * @param: param to be set. |
| * @value: value |
| * |
| * This function gets called from ucfg to set param |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_set_peer_param(struct wlan_objmgr_peer *peer, |
| wlan_crypto_param_type param, |
| uint32_t value){ |
| QDF_STATUS status = QDF_STATUS_E_INVAL; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = &(crypto_priv->crypto_params); |
| |
| status = wlan_crypto_set_param(crypto_params, param, value); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_get_param_value - called by crypto APIs to get value for param |
| * @param: Crypto param type |
| * @crypto_params: Crypto params struct |
| * |
| * This function gets called from in-within crypto layer |
| * |
| * Return: value or -1 for failure |
| */ |
| static int32_t wlan_crypto_get_param_value(wlan_crypto_param_type param, |
| struct wlan_crypto_params *crypto_params) |
| { |
| int32_t value = -1; |
| |
| switch (param) { |
| case WLAN_CRYPTO_PARAM_AUTH_MODE: |
| value = wlan_crypto_get_authmode(crypto_params); |
| break; |
| case WLAN_CRYPTO_PARAM_UCAST_CIPHER: |
| value = wlan_crypto_get_ucastciphers(crypto_params); |
| break; |
| case WLAN_CRYPTO_PARAM_MCAST_CIPHER: |
| value = wlan_crypto_get_mcastcipher(crypto_params); |
| break; |
| case WLAN_CRYPTO_PARAM_MGMT_CIPHER: |
| value = wlan_crypto_get_mgmtciphers(crypto_params); |
| break; |
| case WLAN_CRYPTO_PARAM_CIPHER_CAP: |
| value = wlan_crypto_get_cipher_cap(crypto_params); |
| break; |
| case WLAN_CRYPTO_PARAM_RSN_CAP: |
| value = wlan_crypto_get_rsn_cap(crypto_params); |
| break; |
| case WLAN_CRYPTO_PARAM_KEY_MGMT: |
| value = wlan_crypto_get_key_mgmt(crypto_params); |
| break; |
| default: |
| value = QDF_STATUS_E_INVAL; |
| } |
| |
| return value; |
| } |
| |
| /** |
| * wlan_crypto_get_param - called to get value for param from vdev |
| * @vdev: vdev |
| * @param: Crypto param type |
| * |
| * This function gets called to get value for param from vdev |
| * |
| * Return: value or -1 for failure |
| */ |
| int32_t wlan_crypto_get_param(struct wlan_objmgr_vdev *vdev, |
| wlan_crypto_param_type param) |
| { |
| int32_t value = -1; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = &(crypto_priv->crypto_params); |
| value = wlan_crypto_get_param_value(param, crypto_params); |
| |
| return value; |
| } |
| /** |
| * wlan_crypto_get_peer_param - called to get value for param from peer |
| * @peer: peer |
| * @param: Crypto param type |
| * |
| * This function gets called to get value for param from peer |
| * |
| * Return: value or -1 for failure |
| */ |
| int32_t wlan_crypto_get_peer_param(struct wlan_objmgr_peer *peer, |
| wlan_crypto_param_type param) |
| { |
| int32_t value = -1; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| |
| if (!crypto_params) { |
| crypto_err("crypto_params NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| value = wlan_crypto_get_param_value(param, crypto_params); |
| |
| return value; |
| } |
| qdf_export_symbol(wlan_crypto_get_peer_param); |
| |
| static |
| QDF_STATUS wlan_crypto_set_pmksa(struct wlan_crypto_params *crypto_params, |
| struct wlan_crypto_pmksa *pmksa) |
| { |
| uint8_t i, first_available_slot = 0; |
| bool slot_found = false; |
| |
| /* find the empty slot or slot with same bssid */ |
| for (i = 0; i < WLAN_CRYPTO_MAX_PMKID; i++) { |
| if (!crypto_params->pmksa[i]) { |
| if (!slot_found) { |
| slot_found = true; |
| first_available_slot = i; |
| } |
| continue; |
| } |
| if (qdf_is_macaddr_equal(&pmksa->bssid, |
| &crypto_params->pmksa[i]->bssid)) { |
| /* free the current pmksa and use this slot */ |
| qdf_mem_zero(crypto_params->pmksa[i], |
| sizeof(struct wlan_crypto_pmksa)); |
| qdf_mem_free(crypto_params->pmksa[i]); |
| crypto_params->pmksa[i] = pmksa; |
| return QDF_STATUS_SUCCESS; |
| } |
| } |
| |
| if (i == WLAN_CRYPTO_MAX_PMKID && !slot_found) { |
| crypto_err("no entry available for pmksa"); |
| return QDF_STATUS_E_INVAL; |
| } |
| crypto_params->pmksa[first_available_slot] = pmksa; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static |
| QDF_STATUS wlan_crypto_del_pmksa(struct wlan_crypto_params *crypto_params, |
| struct wlan_crypto_pmksa *pmksa) |
| { |
| uint8_t i; |
| |
| /* find slot with same bssid */ |
| for (i = 0; i < WLAN_CRYPTO_MAX_PMKID; i++) { |
| if (!crypto_params->pmksa[i]) |
| continue; |
| if (qdf_is_macaddr_equal(&pmksa->bssid, |
| &crypto_params->pmksa[i]->bssid)) { |
| qdf_mem_zero(crypto_params->pmksa[i], |
| sizeof(struct wlan_crypto_pmksa)); |
| qdf_mem_free(crypto_params->pmksa[i]); |
| crypto_params->pmksa[i] = NULL; |
| return QDF_STATUS_SUCCESS; |
| } |
| } |
| |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| QDF_STATUS wlan_crypto_pmksa_flush(struct wlan_crypto_params *crypto_params) |
| { |
| uint8_t i; |
| |
| for (i = 0; i < WLAN_CRYPTO_MAX_PMKID; i++) { |
| if (!crypto_params->pmksa[i]) |
| continue; |
| qdf_mem_zero(crypto_params->pmksa[i], |
| sizeof(struct wlan_crypto_pmksa)); |
| qdf_mem_free(crypto_params->pmksa[i]); |
| crypto_params->pmksa[i] = NULL; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wlan_crypto_set_del_pmksa(struct wlan_objmgr_vdev *vdev, |
| struct wlan_crypto_pmksa *pmksa, |
| bool set) |
| { |
| QDF_STATUS status = QDF_STATUS_E_INVAL; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| enum QDF_OPMODE op_mode; |
| |
| op_mode = wlan_vdev_mlme_get_opmode(vdev); |
| |
| if (op_mode != QDF_STA_MODE && op_mode != QDF_SAP_MODE) |
| return QDF_STATUS_E_NOSUPPORT; |
| |
| if (!pmksa && set) { |
| crypto_err("pmksa is NULL for set operation"); |
| return QDF_STATUS_E_INVAL; |
| } |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = &crypto_priv->crypto_params; |
| if (set) { |
| status = wlan_crypto_set_pmksa(crypto_params, pmksa); |
| /* Set pmksa */ |
| } else { |
| /* del pmksa */ |
| if (!pmksa) |
| status = wlan_crypto_pmksa_flush(crypto_params); |
| else |
| status = wlan_crypto_del_pmksa(crypto_params, pmksa); |
| } |
| |
| return status; |
| } |
| |
| struct wlan_crypto_pmksa * |
| wlan_crypto_get_pmksa(struct wlan_objmgr_vdev *vdev, struct qdf_mac_addr *bssid) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| uint8_t i; |
| |
| if (!bssid) { |
| crypto_err("bssid is NULL"); |
| return NULL; |
| } |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| |
| crypto_params = &crypto_priv->crypto_params; |
| |
| for (i = 0; i < WLAN_CRYPTO_MAX_PMKID; i++) { |
| if (!crypto_params->pmksa[i]) |
| continue; |
| if (qdf_is_macaddr_equal(bssid, |
| &crypto_params->pmksa[i]->bssid)) { |
| return crypto_params->pmksa[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| /** |
| * wlan_crypto_is_htallowed - called to check is HT allowed for cipher |
| * @vdev: vdev |
| * @peer: peer |
| * |
| * This function gets called to check is HT allowed for cipher. |
| * HT is not allowed for wep and tkip. |
| * |
| * Return: 0 - not allowed or 1 - allowed |
| */ |
| uint8_t wlan_crypto_is_htallowed(struct wlan_objmgr_vdev *vdev, |
| struct wlan_objmgr_peer *peer) |
| { |
| int32_t ucast_cipher; |
| |
| if (!(vdev || peer)) { |
| crypto_err("Invalid params"); |
| return 0; |
| } |
| |
| if (vdev) |
| ucast_cipher = wlan_crypto_get_param(vdev, |
| WLAN_CRYPTO_PARAM_UCAST_CIPHER); |
| else |
| ucast_cipher = wlan_crypto_get_peer_param(peer, |
| WLAN_CRYPTO_PARAM_UCAST_CIPHER); |
| |
| return (ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_WEP)) || |
| ((ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_TKIP)) && |
| !(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_CCM)) && |
| !(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_GCM)) && |
| !(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_GCM_256)) && |
| !(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_CCM_256))); |
| } |
| qdf_export_symbol(wlan_crypto_is_htallowed); |
| |
| /** |
| * wlan_crypto_setkey - called by ucfg to setkey |
| * @vdev: vdev |
| * @req_key: req_key with cipher type, key macaddress |
| * |
| * This function gets called from ucfg to sey key |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_setkey(struct wlan_objmgr_vdev *vdev, |
| struct wlan_crypto_req_key *req_key){ |
| |
| QDF_STATUS status = QDF_STATUS_E_INVAL; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_objmgr_psoc *psoc; |
| struct wlan_objmgr_peer *peer; |
| struct wlan_crypto_key *key = NULL; |
| const struct wlan_crypto_cipher *cipher; |
| uint8_t macaddr[QDF_MAC_ADDR_SIZE] = |
| {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| bool isbcast; |
| enum QDF_OPMODE vdev_mode; |
| uint8_t igtk_idx = 0; |
| |
| if (!vdev || !req_key || req_key->keylen > (sizeof(req_key->keydata))) { |
| crypto_err("Invalid params vdev%pK, req_key%pK", vdev, req_key); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| isbcast = qdf_is_macaddr_group( |
| (struct qdf_mac_addr *)req_key->macaddr); |
| if ((req_key->keylen == 0) && !IS_FILS_CIPHER(req_key->type)) { |
| /* zero length keys, only set default key id if flags are set*/ |
| if ((req_key->flags & WLAN_CRYPTO_KEY_DEFAULT) |
| && (req_key->keyix != WLAN_CRYPTO_KEYIX_NONE) |
| && (!IS_MGMT_CIPHER(req_key->type))) { |
| wlan_crypto_default_key(vdev, |
| req_key->macaddr, |
| req_key->keyix, |
| !isbcast); |
| return QDF_STATUS_SUCCESS; |
| } |
| crypto_err("req_key len zero"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| cipher = wlan_crypto_cipher_ops[req_key->type]; |
| |
| if (!cipher && !IS_MGMT_CIPHER(req_key->type)) { |
| crypto_err("cipher invalid"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (cipher && (!IS_FILS_CIPHER(req_key->type)) && |
| (!IS_MGMT_CIPHER(req_key->type)) && |
| ((req_key->keylen != (cipher->keylen / CRYPTO_NBBY)) && |
| (req_key->type != WLAN_CRYPTO_CIPHER_WEP))) { |
| crypto_err("cipher invalid"); |
| return QDF_STATUS_E_INVAL; |
| } else if ((req_key->type == WLAN_CRYPTO_CIPHER_WEP) && |
| !((req_key->keylen == WLAN_CRYPTO_KEY_WEP40_LEN) |
| || (req_key->keylen == WLAN_CRYPTO_KEY_WEP104_LEN) |
| || (req_key->keylen == WLAN_CRYPTO_KEY_WEP128_LEN))) { |
| crypto_err("wep key len invalid. keylen: %d", req_key->keylen); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (req_key->keyix == WLAN_CRYPTO_KEYIX_NONE) { |
| if (req_key->flags != (WLAN_CRYPTO_KEY_XMIT |
| | WLAN_CRYPTO_KEY_RECV)) { |
| req_key->flags |= (WLAN_CRYPTO_KEY_XMIT |
| | WLAN_CRYPTO_KEY_RECV); |
| } |
| } else { |
| if ((req_key->keyix >= WLAN_CRYPTO_MAX_VLANKEYIX) |
| && (!IS_MGMT_CIPHER(req_key->type))) { |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| req_key->flags |= (WLAN_CRYPTO_KEY_XMIT |
| | WLAN_CRYPTO_KEY_RECV); |
| if (isbcast) |
| req_key->flags |= WLAN_CRYPTO_KEY_GROUP; |
| } |
| |
| vdev_mode = wlan_vdev_mlme_get_opmode(vdev); |
| |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(macaddr, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (req_key->type == WLAN_CRYPTO_CIPHER_WEP) { |
| if (wlan_crypto_vdev_has_auth_mode(vdev, |
| (1 << WLAN_CRYPTO_AUTH_8021X))) { |
| req_key->flags |= WLAN_CRYPTO_KEY_DEFAULT; |
| } |
| } |
| |
| if (isbcast) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (IS_MGMT_CIPHER(req_key->type)) { |
| igtk_idx = req_key->keyix - WLAN_CRYPTO_MAXKEYIDX; |
| if (igtk_idx >= WLAN_CRYPTO_MAXIGTKKEYIDX) { |
| crypto_err("igtk key invalid keyid %d", |
| igtk_idx); |
| return QDF_STATUS_E_INVAL; |
| } |
| key = qdf_mem_malloc(sizeof(struct wlan_crypto_key)); |
| if (!key) |
| return QDF_STATUS_E_NOMEM; |
| |
| if (crypto_priv->igtk_key[igtk_idx]) |
| qdf_mem_free(crypto_priv->igtk_key[igtk_idx]); |
| |
| crypto_priv->igtk_key[igtk_idx] = key; |
| crypto_priv->igtk_key_type = req_key->type; |
| crypto_priv->def_igtk_tx_keyid = igtk_idx; |
| } else { |
| if (IS_FILS_CIPHER(req_key->type)) { |
| crypto_err("FILS key is not for BroadCast pkt"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (!HAS_MCAST_CIPHER(crypto_params, req_key->type) |
| && (req_key->type != WLAN_CRYPTO_CIPHER_WEP)) { |
| return QDF_STATUS_E_INVAL; |
| } |
| if (!crypto_priv->key[req_key->keyix]) { |
| crypto_priv->key[req_key->keyix] |
| = qdf_mem_malloc( |
| sizeof(struct wlan_crypto_key)); |
| if (!crypto_priv->key[req_key->keyix]) |
| return QDF_STATUS_E_NOMEM; |
| } |
| key = crypto_priv->key[req_key->keyix]; |
| } |
| if (vdev_mode == QDF_STA_MODE) { |
| peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, |
| WLAN_CRYPTO_ID); |
| if (!peer) { |
| crypto_err("peer NULL"); |
| if (IS_MGMT_CIPHER(req_key->type)) { |
| crypto_priv->igtk_key[igtk_idx] = NULL; |
| crypto_priv->igtk_key_type |
| = WLAN_CRYPTO_CIPHER_NONE; |
| } else |
| crypto_priv->key[req_key->keyix] = NULL; |
| if (key) |
| qdf_mem_free(key); |
| return QDF_STATUS_E_INVAL; |
| } |
| qdf_mem_copy(macaddr, wlan_peer_get_macaddr(peer), |
| QDF_MAC_ADDR_SIZE); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| } |
| } else { |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, |
| pdev_id, |
| macaddr, |
| req_key->macaddr, |
| WLAN_CRYPTO_ID); |
| |
| if (!peer) { |
| crypto_err("peer NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| qdf_mem_copy(macaddr, req_key->macaddr, QDF_MAC_ADDR_SIZE); |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (IS_MGMT_CIPHER(req_key->type)) { |
| igtk_idx = req_key->keyix - WLAN_CRYPTO_MAXKEYIDX; |
| if (igtk_idx >= WLAN_CRYPTO_MAXIGTKKEYIDX) { |
| crypto_err("igtk key invalid keyid %d", |
| igtk_idx); |
| return QDF_STATUS_E_INVAL; |
| } |
| key = qdf_mem_malloc(sizeof(struct wlan_crypto_key)); |
| if (!key) |
| return QDF_STATUS_E_NOMEM; |
| |
| if (crypto_priv->igtk_key[igtk_idx]) |
| qdf_mem_free(crypto_priv->igtk_key[igtk_idx]); |
| |
| crypto_priv->igtk_key[igtk_idx] = key; |
| crypto_priv->igtk_key_type = req_key->type; |
| crypto_priv->def_igtk_tx_keyid = igtk_idx; |
| } else { |
| uint16_t kid = req_key->keyix; |
| if (kid == WLAN_CRYPTO_KEYIX_NONE) |
| kid = 0; |
| if (kid >= WLAN_CRYPTO_MAX_VLANKEYIX) { |
| crypto_err("invalid keyid %d", kid); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (!crypto_priv->key[kid]) { |
| crypto_priv->key[kid] |
| = qdf_mem_malloc( |
| sizeof(struct wlan_crypto_key)); |
| if (!crypto_priv->key[kid]) |
| return QDF_STATUS_E_NOMEM; |
| } |
| key = crypto_priv->key[kid]; |
| } |
| } |
| |
| /* alloc key might not required as it is already there */ |
| key->cipher_table = (void *)cipher; |
| key->keylen = req_key->keylen; |
| key->flags = req_key->flags; |
| |
| if (req_key->keyix == WLAN_CRYPTO_KEYIX_NONE) |
| key->keyix = 0; |
| else |
| key->keyix = req_key->keyix; |
| |
| if (req_key->flags & WLAN_CRYPTO_KEY_DEFAULT |
| && (!IS_MGMT_CIPHER(req_key->type))) { |
| crypto_priv->def_tx_keyid = key->keyix; |
| key->flags |= WLAN_CRYPTO_KEY_DEFAULT; |
| } |
| if ((req_key->type == WLAN_CRYPTO_CIPHER_WAPI_SMS4) |
| || (req_key->type == WLAN_CRYPTO_CIPHER_WAPI_GCM4)) { |
| uint8_t iv_AP[16] = { 0x5c, 0x36, 0x5c, 0x36, |
| 0x5c, 0x36, 0x5c, 0x36, |
| 0x5c, 0x36, 0x5c, 0x36, |
| 0x5c, 0x36, 0x5c, 0x37}; |
| uint8_t iv_STA[16] = { 0x5c, 0x36, 0x5c, 0x36, |
| 0x5c, 0x36, 0x5c, 0x36, |
| 0x5c, 0x36, 0x5c, 0x36, |
| 0x5c, 0x36, 0x5c, 0x36}; |
| |
| /* During Tx PN should be increment and |
| * send but as per our implementation we increment only after |
| * Tx complete. So First packet PN check will be failed. |
| * To compensate increment the PN here by 2 |
| */ |
| if (vdev_mode == QDF_SAP_MODE) { |
| iv_AP[15] += 2; |
| qdf_mem_copy(key->recviv, iv_STA, |
| WLAN_CRYPTO_WAPI_IV_SIZE); |
| qdf_mem_copy(key->txiv, iv_AP, |
| WLAN_CRYPTO_WAPI_IV_SIZE); |
| } else { |
| iv_STA[15] += 2; |
| qdf_mem_copy(key->recviv, iv_AP, |
| WLAN_CRYPTO_WAPI_IV_SIZE); |
| qdf_mem_copy(key->txiv, iv_STA, |
| WLAN_CRYPTO_WAPI_IV_SIZE); |
| } |
| } else { |
| uint8_t i = 0; |
| qdf_mem_copy((uint8_t *)(&key->keytsc), |
| (uint8_t *)(&req_key->keytsc), sizeof(key->keytsc)); |
| for (i = 0; i < WLAN_CRYPTO_TID_SIZE; i++) { |
| qdf_mem_copy((uint8_t *)(&key->keyrsc[i]), |
| (uint8_t *)(&req_key->keyrsc), |
| sizeof(key->keyrsc[0])); |
| } |
| } |
| |
| qdf_mem_copy(key->keyval, req_key->keydata, sizeof(key->keyval)); |
| key->valid = 1; |
| if ((IS_MGMT_CIPHER(req_key->type))) { |
| if (HAS_CIPHER_CAP(crypto_params, |
| WLAN_CRYPTO_CAP_PMF_OFFLOAD)) { |
| if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(vdev, |
| key, macaddr, req_key->type); |
| } |
| } |
| wlan_crypto_set_mgmtcipher(crypto_params, req_key->type); |
| status = wlan_crypto_set_igtk_key(key); |
| return status; |
| } else if (IS_FILS_CIPHER(req_key->type)) { |
| /* Take request key object to FILS setkey */ |
| key->private = req_key; |
| } else { |
| if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(vdev, key, |
| macaddr, req_key->type); |
| } |
| } |
| status = cipher->setkey(key); |
| |
| if ((req_key->flags & WLAN_CRYPTO_KEY_DEFAULT) && |
| (req_key->keyix != WLAN_CRYPTO_KEYIX_NONE) && |
| (!IS_MGMT_CIPHER(req_key->type))) { |
| /* default xmit key */ |
| wlan_crypto_default_key(vdev, |
| req_key->macaddr, |
| req_key->keyix, |
| !isbcast); |
| } |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_get_keytype - get keytype |
| * @key: key |
| * |
| * This function gets keytype from key |
| * |
| * Return: keytype |
| */ |
| wlan_crypto_cipher_type wlan_crypto_get_key_type( |
| struct wlan_crypto_key *key){ |
| if (key && key->cipher_table) { |
| return ((struct wlan_crypto_cipher *) |
| (key->cipher_table))->cipher; |
| } |
| return WLAN_CRYPTO_CIPHER_NONE; |
| } |
| qdf_export_symbol(wlan_crypto_get_key_type); |
| /** |
| * wlan_crypto_vdev_getkey - get key from vdev |
| * @vdev: vdev |
| * @keyix: keyix |
| * |
| * This function gets key from vdev |
| * |
| * Return: key or NULL |
| */ |
| struct wlan_crypto_key *wlan_crypto_vdev_getkey(struct wlan_objmgr_vdev *vdev, |
| uint16_t keyix){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key = NULL; |
| |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| /* for keyix 4,5 we return the igtk keys for keyix more than 5 |
| * we return the default key, for all other keyix we return the |
| * key accordingly. |
| */ |
| if (keyix == WLAN_CRYPTO_KEYIX_NONE || |
| keyix >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX)) |
| key = crypto_priv->key[crypto_priv->def_tx_keyid]; |
| else if (keyix >= WLAN_CRYPTO_MAXKEYIDX) |
| key = crypto_priv->igtk_key[keyix - WLAN_CRYPTO_MAXKEYIDX]; |
| else |
| key = crypto_priv->key[keyix]; |
| |
| if (key && key->valid) |
| return key; |
| |
| return NULL; |
| } |
| qdf_export_symbol(wlan_crypto_vdev_getkey); |
| |
| /** |
| * wlan_crypto_peer_getkey - get key from peer |
| * @peer: peer |
| * @keyix: keyix |
| * |
| * This function gets key from peer |
| * |
| * Return: key or NULL |
| */ |
| struct wlan_crypto_key *wlan_crypto_peer_getkey(struct wlan_objmgr_peer *peer, |
| uint16_t keyix){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key = NULL; |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, &crypto_priv); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| |
| /* for keyix 4,5 we return the igtk keys for keyix more than 5 |
| * we return the default key, for all other keyix we return the |
| * key accordingly. |
| */ |
| if (keyix == WLAN_CRYPTO_KEYIX_NONE || |
| keyix >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX)) |
| key = crypto_priv->key[crypto_priv->def_tx_keyid]; |
| else if (keyix >= WLAN_CRYPTO_MAXKEYIDX) |
| key = crypto_priv->igtk_key[keyix - WLAN_CRYPTO_MAXKEYIDX]; |
| else |
| key = crypto_priv->key[keyix]; |
| |
| if (key && key->valid) |
| return key; |
| |
| return NULL; |
| } |
| qdf_export_symbol(wlan_crypto_peer_getkey); |
| |
| /** |
| * wlan_crypto_getkey - called by ucfg to get key |
| * @vdev: vdev |
| * @req_key: key value will be copied in this req_key |
| * @mac_address: mac address of the peer for unicast key |
| * or broadcast address if group key is requested. |
| * |
| * This function gets called from ucfg to get key |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_getkey(struct wlan_objmgr_vdev *vdev, |
| struct wlan_crypto_req_key *req_key, |
| uint8_t *mac_addr){ |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_crypto_key *key; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t macaddr[QDF_MAC_ADDR_SIZE] = |
| {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(macaddr, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) { |
| key = wlan_crypto_vdev_getkey(vdev, req_key->keyix); |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, |
| pdev_id, |
| macaddr, |
| mac_addr, |
| WLAN_CRYPTO_ID); |
| if (!peer) { |
| crypto_err("peer NULL"); |
| return QDF_STATUS_E_NOENT; |
| } |
| key = wlan_crypto_peer_getkey(peer, req_key->keyix); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (key->valid) { |
| qdf_mem_copy(req_key->keydata, |
| key->keyval, key->keylen); |
| qdf_mem_copy((uint8_t *)(&req_key->keytsc), |
| (uint8_t *)(&key->keytsc), |
| sizeof(req_key->keytsc)); |
| qdf_mem_copy((uint8_t *)(&req_key->keyrsc), |
| (uint8_t *)(&key->keyrsc[0]), |
| sizeof(req_key->keyrsc)); |
| req_key->keylen = key->keylen; |
| req_key->flags = key->flags; |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| |
| if (!cipher_table) |
| return QDF_STATUS_SUCCESS; |
| |
| req_key->type = cipher_table->cipher; |
| if (req_key->type == WLAN_CRYPTO_CIPHER_WAPI_SMS4) { |
| qdf_mem_copy((uint8_t *)(&req_key->txiv), |
| (uint8_t *)(key->txiv), |
| sizeof(req_key->txiv)); |
| qdf_mem_copy((uint8_t *)(&req_key->recviv), |
| (uint8_t *)(key->recviv), |
| sizeof(req_key->recviv)); |
| } |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_delkey - called by ucfg to delete key |
| * @vdev: vdev |
| * @mac_address: mac address of the peer for unicast key |
| * or broadcast address if group key is deleted. |
| * @key_idx: key index to be deleted |
| * |
| * This function gets called from ucfg to delete key |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_delkey(struct wlan_objmgr_vdev *vdev, |
| uint8_t *macaddr, |
| uint8_t key_idx){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t bssid_mac[QDF_MAC_ADDR_SIZE]; |
| |
| if (!vdev || !macaddr || |
| (key_idx >= |
| (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX))) { |
| crypto_err("Invalid param vdev %pK macaddr %pK keyidx %d", |
| vdev, macaddr, key_idx); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)macaddr)) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, pdev_id, |
| bssid_mac, |
| macaddr, |
| WLAN_CRYPTO_ID); |
| if (!peer) { |
| return QDF_STATUS_E_INVAL; |
| } |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| } |
| |
| if (key_idx >= WLAN_CRYPTO_MAXKEYIDX) { |
| uint8_t igtk_idx = key_idx - WLAN_CRYPTO_MAXKEYIDX; |
| if (igtk_idx >= WLAN_CRYPTO_MAXIGTKKEYIDX) { |
| crypto_err("Igtk key invalid keyid %d", igtk_idx); |
| return QDF_STATUS_E_INVAL; |
| } |
| key = crypto_priv->igtk_key[igtk_idx]; |
| crypto_priv->igtk_key[igtk_idx] = NULL; |
| if (key) |
| key->valid = 0; |
| } else { |
| key = crypto_priv->key[key_idx]; |
| crypto_priv->key[key_idx] = NULL; |
| } |
| |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| |
| if (key->valid) { |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| qdf_mem_zero(key->keyval, sizeof(key->keyval)); |
| |
| if (WLAN_CRYPTO_TX_OPS_DELKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_DELKEY(psoc)(vdev, key, |
| macaddr, cipher_table->cipher); |
| } |
| } |
| qdf_mem_free(key); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| #ifdef CRYPTO_SET_KEY_CONVERGED |
| static QDF_STATUS wlan_crypto_set_default_key(struct wlan_objmgr_vdev *vdev, |
| uint8_t key_idx, uint8_t *macaddr) |
| { |
| return QDF_STATUS_SUCCESS; |
| } |
| #else |
| static QDF_STATUS wlan_crypto_set_default_key(struct wlan_objmgr_vdev *vdev, |
| uint8_t key_idx, uint8_t *macaddr) |
| { |
| struct wlan_objmgr_psoc *psoc; |
| |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| crypto_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (WLAN_CRYPTO_TX_OPS_DEFAULTKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_DEFAULTKEY(psoc)(vdev, key_idx, |
| macaddr); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif |
| |
| /** |
| * wlan_crypto_default_key - called by ucfg to set default tx key |
| * @vdev: vdev |
| * @mac_address: mac address of the peer for unicast key |
| * or broadcast address if group key need to made default. |
| * @key_idx: key index to be made as default key |
| * @unicast: is key was unicast or group key. |
| * |
| * This function gets called from ucfg to set default key |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_default_key(struct wlan_objmgr_vdev *vdev, |
| uint8_t *macaddr, |
| uint8_t key_idx, |
| bool unicast){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t bssid_mac[QDF_MAC_ADDR_SIZE]; |
| |
| if (!vdev || !macaddr || (key_idx >= WLAN_CRYPTO_MAXKEYIDX)) { |
| crypto_err("Invalid param vdev %pK macaddr %pK keyidx %d", |
| vdev, macaddr, key_idx); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)macaddr)) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[key_idx]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, pdev_id, |
| bssid_mac, |
| macaddr, |
| WLAN_CRYPTO_ID); |
| |
| if (!peer) { |
| crypto_err("peer NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[key_idx]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } |
| if (!key->valid) |
| return QDF_STATUS_E_INVAL; |
| |
| if (wlan_crypto_set_default_key(vdev, key_idx, macaddr) != |
| QDF_STATUS_SUCCESS) |
| return QDF_STATUS_E_INVAL; |
| crypto_priv->def_tx_keyid = key_idx; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_encap - called by mgmt for encap the frame based on cipher |
| * @vdev: vdev |
| * @wbuf: wbuf |
| * @macaddr: macaddr |
| * @encapdone: is encapdone already or not. |
| * |
| * This function gets called from mgmt txrx to encap frame. |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_encap(struct wlan_objmgr_vdev *vdev, |
| qdf_nbuf_t wbuf, |
| uint8_t *mac_addr, |
| uint8_t encapdone){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| QDF_STATUS status; |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_objmgr_psoc *psoc; |
| struct wlan_objmgr_peer *peer; |
| uint8_t bssid_mac[QDF_MAC_ADDR_SIZE]; |
| uint8_t pdev_id; |
| uint8_t hdrlen; |
| enum QDF_OPMODE opmode; |
| |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); |
| /* FILS Encap required only for (Re-)Assoc response */ |
| peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_CRYPTO_ID); |
| |
| if (!wlan_crypto_is_data_protected((uint8_t *)qdf_nbuf_data(wbuf)) && |
| peer && !wlan_crypto_get_peer_fils_aead(peer)) { |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (peer) |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (qdf_is_macaddr_group((struct qdf_mac_addr *)mac_addr)) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[crypto_priv->def_tx_keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev(psoc, pdev_id, |
| bssid_mac, mac_addr, |
| WLAN_CRYPTO_ID); |
| |
| if (!peer) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[crypto_priv->def_tx_keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } |
| if (opmode == QDF_MONITOR_MODE) |
| hdrlen = ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)); |
| else |
| hdrlen = ieee80211_hdrspace(wlan_vdev_get_pdev(vdev), |
| (uint8_t *)qdf_nbuf_data(wbuf)); |
| |
| /* if tkip, is counter measures enabled, then drop the frame */ |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| status = cipher_table->encap(key, wbuf, encapdone, |
| hdrlen); |
| |
| return status; |
| } |
| qdf_export_symbol(wlan_crypto_encap); |
| |
| /** |
| * wlan_crypto_decap - called by mgmt for decap the frame based on cipher |
| * @vdev: vdev |
| * @wbuf: wbuf |
| * @macaddr: macaddr |
| * @tid: tid of the frame |
| * |
| * This function gets called from mgmt txrx to decap frame. |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_decap(struct wlan_objmgr_vdev *vdev, |
| qdf_nbuf_t wbuf, |
| uint8_t *mac_addr, |
| uint8_t tid){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| QDF_STATUS status; |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_objmgr_psoc *psoc; |
| struct wlan_objmgr_peer *peer; |
| uint8_t bssid_mac[QDF_MAC_ADDR_SIZE]; |
| uint8_t keyid; |
| uint8_t pdev_id; |
| uint8_t hdrlen; |
| enum QDF_OPMODE opmode; |
| |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (opmode == QDF_MONITOR_MODE) |
| hdrlen = ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)); |
| else |
| hdrlen = ieee80211_hdrspace(wlan_vdev_get_pdev(vdev), |
| (uint8_t *)qdf_nbuf_data(wbuf)); |
| |
| keyid = wlan_crypto_get_keyid((uint8_t *)qdf_nbuf_data(wbuf), hdrlen); |
| |
| if (keyid >= WLAN_CRYPTO_MAXKEYIDX) |
| return QDF_STATUS_E_INVAL; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); |
| /* FILS Decap required only for (Re-)Assoc request */ |
| peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_CRYPTO_ID); |
| |
| if (!wlan_crypto_is_data_protected((uint8_t *)qdf_nbuf_data(wbuf)) && |
| peer && !wlan_crypto_get_peer_fils_aead(peer)) { |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (peer) |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (qdf_is_macaddr_group((struct qdf_mac_addr *)mac_addr)) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, pdev_id, bssid_mac, |
| mac_addr, WLAN_CRYPTO_ID); |
| if (!peer) { |
| crypto_err("peer NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } |
| /* if tkip, is counter measures enabled, then drop the frame */ |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| status = cipher_table->decap(key, wbuf, tid, hdrlen); |
| |
| return status; |
| } |
| qdf_export_symbol(wlan_crypto_decap); |
| /** |
| * wlan_crypto_enmic - called by mgmt for adding mic in frame based on cipher |
| * @vdev: vdev |
| * @wbuf: wbuf |
| * @macaddr: macaddr |
| * @encapdone: is encapdone already or not. |
| * |
| * This function gets called from mgmt txrx to adding mic to the frame. |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_enmic(struct wlan_objmgr_vdev *vdev, |
| qdf_nbuf_t wbuf, |
| uint8_t *mac_addr, |
| uint8_t encapdone){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| QDF_STATUS status; |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t bssid_mac[QDF_MAC_ADDR_SIZE]; |
| uint8_t hdrlen; |
| enum QDF_OPMODE opmode; |
| |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| |
| |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[crypto_priv->def_tx_keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, pdev_id, bssid_mac, |
| mac_addr, WLAN_CRYPTO_ID); |
| if (!peer) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[crypto_priv->def_tx_keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } |
| if (opmode == QDF_MONITOR_MODE) |
| hdrlen = ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)); |
| else |
| hdrlen = ieee80211_hdrspace(wlan_vdev_get_pdev(vdev), |
| (uint8_t *)qdf_nbuf_data(wbuf)); |
| |
| /* if tkip, is counter measures enabled, then drop the frame */ |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| status = cipher_table->enmic(key, wbuf, encapdone, hdrlen); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_demic - called by mgmt for remove and check mic for |
| * the frame based on cipher |
| * @vdev: vdev |
| * @wbuf: wbuf |
| * @macaddr: macaddr |
| * @tid: tid of the frame |
| * @keyid: keyid in the received frame |
| * This function gets called from mgmt txrx to decap frame. |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_demic(struct wlan_objmgr_vdev *vdev, |
| qdf_nbuf_t wbuf, |
| uint8_t *mac_addr, |
| uint8_t tid, |
| uint8_t keyid){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| QDF_STATUS status; |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t bssid_mac[QDF_MAC_ADDR_SIZE]; |
| uint8_t hdrlen; |
| enum QDF_OPMODE opmode; |
| |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| |
| if (opmode == QDF_MONITOR_MODE) |
| hdrlen = ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)); |
| else |
| hdrlen = ieee80211_hdrspace(wlan_vdev_get_pdev(vdev), |
| (uint8_t *)qdf_nbuf_data(wbuf)); |
| |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), |
| QDF_MAC_ADDR_SIZE); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!psoc) { |
| wlan_vdev_obj_unlock(vdev); |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| wlan_vdev_obj_unlock(vdev); |
| |
| if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) { |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| |
| } else { |
| struct wlan_objmgr_peer *peer; |
| uint8_t pdev_id; |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id( |
| wlan_vdev_get_pdev(vdev)); |
| peer = wlan_objmgr_get_peer_by_mac_n_vdev( |
| psoc, pdev_id, bssid_mac, |
| mac_addr, WLAN_CRYPTO_ID); |
| if (!peer) { |
| crypto_err("peer NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| key = crypto_priv->key[keyid]; |
| if (!key) |
| return QDF_STATUS_E_INVAL; |
| } |
| /* if tkip, is counter measures enabled, then drop the frame */ |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| status = cipher_table->demic(key, wbuf, tid, hdrlen); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_vdev_is_pmf_enabled - called to check is pmf enabled in vdev |
| * @vdev: vdev |
| * |
| * This function gets called to check is pmf enabled or not in vdev. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_vdev_is_pmf_enabled(struct wlan_objmgr_vdev *vdev) |
| { |
| |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *vdev_crypto_params; |
| |
| if (!vdev) |
| return false; |
| vdev_crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if ((vdev_crypto_params->rsn_caps & |
| WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) |
| || (vdev_crypto_params->rsn_caps & |
| WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * wlan_crypto_vdev_is_pmf_required - called to check is pmf required in vdev |
| * @vdev: vdev |
| * |
| * This function gets called to check is pmf required or not in vdev. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_vdev_is_pmf_required(struct wlan_objmgr_vdev *vdev) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *vdev_crypto_params; |
| |
| if (!vdev) |
| return false; |
| |
| vdev_crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (vdev_crypto_params->rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * wlan_crypto_is_pmf_enabled - called by mgmt txrx to check is pmf enabled |
| * @vdev: vdev |
| * @peer: peer |
| * |
| * This function gets called by mgmt txrx to check is pmf enabled or not. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_is_pmf_enabled(struct wlan_objmgr_vdev *vdev, |
| struct wlan_objmgr_peer *peer){ |
| |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *vdev_crypto_params; |
| struct wlan_crypto_params *peer_crypto_params; |
| |
| if (!vdev || !peer) |
| return false; |
| vdev_crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| peer_crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (((vdev_crypto_params->rsn_caps & |
| WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) && |
| (peer_crypto_params->rsn_caps & |
| WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)) |
| || (vdev_crypto_params->rsn_caps & |
| WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * wlan_crypto_is_key_valid - called by mgmt txrx to check if key is valid |
| * @vdev: vdev |
| * @peer: peer |
| * @keyidx : key index |
| * |
| * This function gets called by mgmt txrx to check if key is valid |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_is_key_valid(struct wlan_objmgr_vdev *vdev, |
| struct wlan_objmgr_peer *peer, |
| uint16_t keyidx) |
| { |
| struct wlan_crypto_key *key = NULL; |
| |
| if (!vdev && !peer) |
| return false; |
| |
| if (peer) |
| key = wlan_crypto_peer_getkey(peer, keyidx); |
| else if (vdev) |
| key = wlan_crypto_vdev_getkey(vdev, keyidx); |
| |
| if ((key) && key->valid) |
| return true; |
| |
| return false; |
| } |
| |
| static void wlan_crypto_gmac_pn_swap(uint8_t *a, uint8_t *b) |
| { |
| a[0] = b[5]; |
| a[1] = b[4]; |
| a[2] = b[3]; |
| a[3] = b[2]; |
| a[4] = b[1]; |
| a[5] = b[0]; |
| } |
| |
| /** |
| * wlan_crypto_add_mmie - called by mgmt txrx to add mmie in frame |
| * @vdev: vdev |
| * @bfrm: frame starting pointer |
| * @len: length of the frame |
| * |
| * This function gets called by mgmt txrx to add mmie in frame |
| * |
| * Return: end of frame or NULL in case failure |
| */ |
| uint8_t *wlan_crypto_add_mmie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *bfrm, |
| uint32_t len) { |
| struct wlan_crypto_key *key; |
| struct wlan_crypto_mmie *mmie; |
| uint8_t *pn, *aad, *buf, *efrm, nounce[12]; |
| struct wlan_frame_hdr *hdr; |
| uint32_t i, hdrlen, mic_len, aad_len; |
| uint8_t mic[16]; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| int32_t ret = -1; |
| |
| if (!bfrm) { |
| crypto_err("frame is NULL"); |
| return NULL; |
| } |
| |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| |
| if (crypto_priv->def_igtk_tx_keyid >= WLAN_CRYPTO_MAXIGTKKEYIDX) { |
| crypto_err("igtk key invalid keyid %d", |
| crypto_priv->def_igtk_tx_keyid); |
| return NULL; |
| } |
| |
| key = crypto_priv->igtk_key[crypto_priv->def_igtk_tx_keyid]; |
| if (!key) { |
| crypto_err("No igtk key present"); |
| return NULL; |
| } |
| mic_len = (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_CMAC) ? 8 : 16; |
| |
| efrm = bfrm + len; |
| aad_len = 20; |
| hdrlen = sizeof(struct wlan_frame_hdr); |
| len += sizeof(struct wlan_crypto_mmie); |
| |
| mmie = (struct wlan_crypto_mmie *) efrm; |
| qdf_mem_zero((unsigned char *)mmie, sizeof(*mmie)); |
| mmie->element_id = WLAN_ELEMID_MMIE; |
| mmie->length = sizeof(*mmie) - 2; |
| mmie->key_id = qdf_cpu_to_le16(key->keyix); |
| |
| mic_len = (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_CMAC) ? 8 : 16; |
| if (mic_len == 8) { |
| mmie->length -= 8; |
| len -= 8; |
| } |
| /* PN = PN + 1 */ |
| pn = (uint8_t *)&key->keytsc; |
| |
| for (i = 0; i <= 5; i++) { |
| pn[i]++; |
| if (pn[i]) |
| break; |
| } |
| |
| /* Copy IPN */ |
| qdf_mem_copy(mmie->sequence_number, pn, 6); |
| |
| hdr = (struct wlan_frame_hdr *) bfrm; |
| |
| buf = qdf_mem_malloc(len - hdrlen + 20); |
| if (!buf) |
| return NULL; |
| |
| qdf_mem_zero(buf, len - hdrlen + 20); |
| aad = buf; |
| /* generate BIP AAD: FC(masked) || A1 || A2 || A3 */ |
| |
| /* FC type/subtype */ |
| aad[0] = hdr->i_fc[0]; |
| /* Mask FC Retry, PwrMgt, MoreData flags to zero */ |
| aad[1] = (hdr->i_fc[1] & ~(WLAN_FC1_RETRY | WLAN_FC1_PWRMGT |
| | WLAN_FC1_MOREDATA)); |
| /* A1 || A2 || A3 */ |
| qdf_mem_copy(aad + 2, hdr->i_addr1, QDF_MAC_ADDR_SIZE); |
| qdf_mem_copy(aad + 8, hdr->i_addr2, QDF_MAC_ADDR_SIZE); |
| qdf_mem_copy(aad + 14, hdr->i_addr3, QDF_MAC_ADDR_SIZE); |
| qdf_mem_zero(mic, 16); |
| |
| /* |
| * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) |
| */ |
| |
| qdf_mem_copy(buf + aad_len, bfrm + hdrlen, len - hdrlen); |
| if (crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_CMAC) { |
| |
| ret = omac1_aes_128(key->keyval, buf, |
| len + aad_len - hdrlen, mic); |
| qdf_mem_copy(mmie->mic, mic, 8); |
| |
| } else if (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_CMAC_256) { |
| |
| ret = omac1_aes_256(key->keyval, buf, |
| len + aad_len - hdrlen, mmie->mic); |
| } else if ((crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_GMAC) |
| || (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_GMAC_256)) { |
| |
| qdf_mem_copy(nounce, hdr->i_addr2, QDF_MAC_ADDR_SIZE); |
| wlan_crypto_gmac_pn_swap(nounce + 6, pn); |
| ret = wlan_crypto_aes_gmac(key->keyval, key->keylen, nounce, |
| sizeof(nounce), buf, |
| len + aad_len - hdrlen, mmie->mic); |
| } |
| qdf_mem_free(buf); |
| if (ret < 0) { |
| crypto_err("add mmie failed"); |
| return NULL; |
| } |
| |
| return bfrm + len; |
| } |
| |
| /** |
| * wlan_crypto_is_mmie_valid - called by mgmt txrx to check mmie of the frame |
| * @vdev: vdev |
| * @frm: frame starting pointer |
| * @efrm: end of frame pointer |
| * |
| * This function gets called by mgmt txrx to check mmie of the frame |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_is_mmie_valid(struct wlan_objmgr_vdev *vdev, |
| uint8_t *frm, |
| uint8_t *efrm){ |
| struct wlan_crypto_mmie *mmie = NULL; |
| uint8_t *ipn, *aad, *buf, mic[16], nounce[12]; |
| struct wlan_crypto_key *key; |
| struct wlan_frame_hdr *hdr; |
| uint16_t mic_len, hdrlen, len; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| uint8_t aad_len = 20; |
| int32_t ret = -1; |
| |
| /* check if frame is illegal length */ |
| if (!frm || !efrm || (efrm < frm) |
| || ((efrm - frm) < sizeof(struct wlan_frame_hdr))) { |
| crypto_err("Invalid params"); |
| return false; |
| } |
| len = efrm - frm; |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return false; |
| } |
| |
| crypto_params = &(crypto_priv->crypto_params); |
| |
| |
| mic_len = (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_CMAC) ? 8 : 16; |
| hdrlen = sizeof(struct wlan_frame_hdr); |
| |
| if (mic_len == 8) |
| mmie = (struct wlan_crypto_mmie *)(efrm - sizeof(*mmie) + 8); |
| else |
| mmie = (struct wlan_crypto_mmie *)(efrm - sizeof(*mmie)); |
| |
| |
| /* check Elem ID*/ |
| if ((!mmie) || (mmie->element_id != WLAN_ELEMID_MMIE)) { |
| crypto_err("IE is not MMIE"); |
| return false; |
| } |
| |
| if (mmie->key_id >= (WLAN_CRYPTO_MAXKEYIDX + |
| WLAN_CRYPTO_MAXIGTKKEYIDX) || |
| (mmie->key_id < WLAN_CRYPTO_MAXKEYIDX)) { |
| crypto_err("keyid not valid"); |
| return false; |
| } |
| |
| key = crypto_priv->igtk_key[mmie->key_id - WLAN_CRYPTO_MAXKEYIDX]; |
| if (!key) { |
| crypto_err("No igtk key present"); |
| return false; |
| } |
| |
| /* validate ipn */ |
| ipn = mmie->sequence_number; |
| if (qdf_mem_cmp(ipn, key->keyrsc, 6) <= 0) { |
| uint8_t *su = (uint8_t *)key->keyrsc; |
| uint8_t *end = ipn + 6; |
| |
| crypto_err("replay error :"); |
| while (ipn < end) { |
| crypto_err("expected pn = %x received pn = %x", |
| *ipn++, *su++); |
| } |
| return false; |
| } |
| |
| buf = qdf_mem_malloc(len - hdrlen + 20); |
| if (!buf) |
| return false; |
| |
| aad = buf; |
| |
| /* construct AAD */ |
| hdr = (struct wlan_frame_hdr *)frm; |
| /* generate BIP AAD: FC(masked) || A1 || A2 || A3 */ |
| |
| /* FC type/subtype */ |
| aad[0] = hdr->i_fc[0]; |
| /* Mask FC Retry, PwrMgt, MoreData flags to zero */ |
| aad[1] = (hdr->i_fc[1] & ~(WLAN_FC1_RETRY | WLAN_FC1_PWRMGT |
| | WLAN_FC1_MOREDATA)); |
| /* A1 || A2 || A3 */ |
| qdf_mem_copy(aad + 2, hdr->i_addr1, 3 * QDF_MAC_ADDR_SIZE); |
| |
| /* |
| * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) |
| */ |
| qdf_mem_copy(buf + 20, frm + hdrlen, len - hdrlen); |
| qdf_mem_zero(buf + (len - hdrlen + 20 - mic_len), mic_len); |
| qdf_mem_zero(mic, 16); |
| if (crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_CMAC) { |
| ret = omac1_aes_128(key->keyval, buf, |
| len - hdrlen + aad_len, mic); |
| } else if (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_CMAC_256) { |
| ret = omac1_aes_256(key->keyval, buf, |
| len + aad_len - hdrlen, mic); |
| } else if ((crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_GMAC) |
| || (crypto_priv->igtk_key_type |
| == WLAN_CRYPTO_CIPHER_AES_GMAC_256)) { |
| qdf_mem_copy(nounce, hdr->i_addr2, QDF_MAC_ADDR_SIZE); |
| wlan_crypto_gmac_pn_swap(nounce + 6, ipn); |
| ret = wlan_crypto_aes_gmac(key->keyval, key->keylen, nounce, |
| sizeof(nounce), buf, |
| len + aad_len - hdrlen, mic); |
| } |
| |
| qdf_mem_free(buf); |
| |
| if (ret < 0) { |
| crypto_err("genarate mmie failed"); |
| return false; |
| } |
| |
| if (qdf_mem_cmp(mic, mmie->mic, mic_len) != 0) { |
| crypto_err("mmie mismatch"); |
| /* MMIE MIC mismatch */ |
| return false; |
| } |
| |
| /* Update the receive sequence number */ |
| qdf_mem_copy(key->keyrsc, ipn, 6); |
| crypto_debug("mmie matched"); |
| |
| return true; |
| } |
| |
| |
| static int32_t wlan_crypto_wpa_cipher_to_suite(uint32_t cipher) |
| { |
| int32_t status = -1; |
| |
| switch (cipher) { |
| case WLAN_CRYPTO_CIPHER_TKIP: |
| return WPA_CIPHER_SUITE_TKIP; |
| case WLAN_CRYPTO_CIPHER_AES_CCM: |
| return WPA_CIPHER_SUITE_CCMP; |
| case WLAN_CRYPTO_CIPHER_NONE: |
| return WPA_CIPHER_SUITE_NONE; |
| } |
| |
| return status; |
| } |
| |
| static int32_t wlan_crypto_rsn_cipher_to_suite(uint32_t cipher) |
| { |
| int32_t status = -1; |
| |
| switch (cipher) { |
| case WLAN_CRYPTO_CIPHER_TKIP: |
| return RSN_CIPHER_SUITE_TKIP; |
| case WLAN_CRYPTO_CIPHER_AES_CCM: |
| return RSN_CIPHER_SUITE_CCMP; |
| case WLAN_CRYPTO_CIPHER_AES_CCM_256: |
| return RSN_CIPHER_SUITE_CCMP_256; |
| case WLAN_CRYPTO_CIPHER_AES_GCM: |
| return RSN_CIPHER_SUITE_GCMP; |
| case WLAN_CRYPTO_CIPHER_AES_GCM_256: |
| return RSN_CIPHER_SUITE_GCMP_256; |
| case WLAN_CRYPTO_CIPHER_AES_CMAC: |
| return RSN_CIPHER_SUITE_AES_CMAC; |
| case WLAN_CRYPTO_CIPHER_AES_CMAC_256: |
| return RSN_CIPHER_SUITE_BIP_CMAC_256; |
| case WLAN_CRYPTO_CIPHER_AES_GMAC: |
| return RSN_CIPHER_SUITE_BIP_GMAC_128; |
| case WLAN_CRYPTO_CIPHER_AES_GMAC_256: |
| return RSN_CIPHER_SUITE_BIP_GMAC_256; |
| case WLAN_CRYPTO_CIPHER_NONE: |
| return RSN_CIPHER_SUITE_NONE; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * Convert an RSN key management/authentication algorithm |
| * to an internal code. |
| */ |
| static int32_t |
| wlan_crypto_rsn_keymgmt_to_suite(uint32_t keymgmt) |
| { |
| int32_t status = -1; |
| |
| switch (keymgmt) { |
| case WLAN_CRYPTO_KEY_MGMT_NONE: |
| return RSN_AUTH_KEY_MGMT_NONE; |
| case WLAN_CRYPTO_KEY_MGMT_IEEE8021X: |
| return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; |
| case WLAN_CRYPTO_KEY_MGMT_PSK: |
| return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; |
| case WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X: |
| return RSN_AUTH_KEY_MGMT_FT_802_1X; |
| case WLAN_CRYPTO_KEY_MGMT_FT_PSK: |
| return RSN_AUTH_KEY_MGMT_FT_PSK; |
| case WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256: |
| return RSN_AUTH_KEY_MGMT_802_1X_SHA256; |
| case WLAN_CRYPTO_KEY_MGMT_PSK_SHA256: |
| return RSN_AUTH_KEY_MGMT_PSK_SHA256; |
| case WLAN_CRYPTO_KEY_MGMT_SAE: |
| return RSN_AUTH_KEY_MGMT_SAE; |
| case WLAN_CRYPTO_KEY_MGMT_FT_SAE: |
| return RSN_AUTH_KEY_MGMT_FT_SAE; |
| case WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B: |
| return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; |
| case WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192: |
| return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; |
| case WLAN_CRYPTO_KEY_MGMT_CCKM: |
| return RSN_AUTH_KEY_MGMT_CCKM; |
| case WLAN_CRYPTO_KEY_MGMT_OSEN: |
| return RSN_AUTH_KEY_MGMT_OSEN; |
| case WLAN_CRYPTO_KEY_MGMT_FILS_SHA256: |
| return RSN_AUTH_KEY_MGMT_FILS_SHA256; |
| case WLAN_CRYPTO_KEY_MGMT_FILS_SHA384: |
| return RSN_AUTH_KEY_MGMT_FILS_SHA384; |
| case WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA256: |
| return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; |
| case WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA384: |
| return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; |
| case WLAN_CRYPTO_KEY_MGMT_OWE: |
| return RSN_AUTH_KEY_MGMT_OWE; |
| case WLAN_CRYPTO_KEY_MGMT_DPP: |
| return RSN_AUTH_KEY_MGMT_DPP; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * Convert an RSN key management/authentication algorithm |
| * to an internal code. |
| */ |
| static int32_t |
| wlan_crypto_wpa_keymgmt_to_suite(uint32_t keymgmt) |
| { |
| int32_t status = -1; |
| |
| switch (keymgmt) { |
| case WLAN_CRYPTO_KEY_MGMT_NONE: |
| return WPA_AUTH_KEY_MGMT_NONE; |
| case WLAN_CRYPTO_KEY_MGMT_IEEE8021X: |
| return WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; |
| case WLAN_CRYPTO_KEY_MGMT_PSK: |
| return WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; |
| case WLAN_CRYPTO_KEY_MGMT_CCKM: |
| return WPA_AUTH_KEY_MGMT_CCKM; |
| } |
| |
| return status; |
| } |
| /** |
| * Convert a WPA cipher selector OUI to an internal |
| * cipher algorithm. Where appropriate we also |
| * record any key length. |
| */ |
| static int32_t wlan_crypto_wpa_suite_to_cipher(uint8_t *sel) |
| { |
| uint32_t w = LE_READ_4(sel); |
| int32_t status = -1; |
| |
| switch (w) { |
| case WPA_CIPHER_SUITE_TKIP: |
| return WLAN_CRYPTO_CIPHER_TKIP; |
| case WPA_CIPHER_SUITE_CCMP: |
| return WLAN_CRYPTO_CIPHER_AES_CCM; |
| case WPA_CIPHER_SUITE_NONE: |
| return WLAN_CRYPTO_CIPHER_NONE; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * Convert a WPA key management/authentication algorithm |
| * to an internal code. |
| */ |
| static int32_t wlan_crypto_wpa_suite_to_keymgmt(uint8_t *sel) |
| { |
| uint32_t w = LE_READ_4(sel); |
| int32_t status = -1; |
| |
| switch (w) { |
| case WPA_AUTH_KEY_MGMT_UNSPEC_802_1X: |
| return WLAN_CRYPTO_KEY_MGMT_IEEE8021X; |
| case WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X: |
| return WLAN_CRYPTO_KEY_MGMT_PSK; |
| case WPA_AUTH_KEY_MGMT_CCKM: |
| return WLAN_CRYPTO_KEY_MGMT_CCKM; |
| case WPA_AUTH_KEY_MGMT_NONE: |
| return WLAN_CRYPTO_KEY_MGMT_NONE; |
| } |
| return status; |
| } |
| |
| /* |
| * Convert a RSN cipher selector OUI to an internal |
| * cipher algorithm. Where appropriate we also |
| * record any key length. |
| */ |
| static int32_t wlan_crypto_rsn_suite_to_cipher(uint8_t *sel) |
| { |
| uint32_t w = LE_READ_4(sel); |
| int32_t status = -1; |
| |
| switch (w) { |
| case RSN_CIPHER_SUITE_TKIP: |
| return WLAN_CRYPTO_CIPHER_TKIP; |
| case RSN_CIPHER_SUITE_CCMP: |
| return WLAN_CRYPTO_CIPHER_AES_CCM; |
| case RSN_CIPHER_SUITE_CCMP_256: |
| return WLAN_CRYPTO_CIPHER_AES_CCM_256; |
| case RSN_CIPHER_SUITE_GCMP: |
| return WLAN_CRYPTO_CIPHER_AES_GCM; |
| case RSN_CIPHER_SUITE_GCMP_256: |
| return WLAN_CRYPTO_CIPHER_AES_GCM_256; |
| case RSN_CIPHER_SUITE_AES_CMAC: |
| return WLAN_CRYPTO_CIPHER_AES_CMAC; |
| case RSN_CIPHER_SUITE_BIP_CMAC_256: |
| return WLAN_CRYPTO_CIPHER_AES_CMAC_256; |
| case RSN_CIPHER_SUITE_BIP_GMAC_128: |
| return WLAN_CRYPTO_CIPHER_AES_GMAC; |
| case RSN_CIPHER_SUITE_BIP_GMAC_256: |
| return WLAN_CRYPTO_CIPHER_AES_GMAC_256; |
| case RSN_CIPHER_SUITE_NONE: |
| return WLAN_CRYPTO_CIPHER_NONE; |
| } |
| |
| return status; |
| } |
| /* |
| * Convert an RSN key management/authentication algorithm |
| * to an internal code. |
| */ |
| static int32_t wlan_crypto_rsn_suite_to_keymgmt(uint8_t *sel) |
| { |
| uint32_t w = LE_READ_4(sel); |
| int32_t status = -1; |
| |
| switch (w) { |
| case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X: |
| return WLAN_CRYPTO_KEY_MGMT_IEEE8021X; |
| case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X: |
| return WLAN_CRYPTO_KEY_MGMT_PSK; |
| case RSN_AUTH_KEY_MGMT_FT_802_1X: |
| return WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X; |
| case RSN_AUTH_KEY_MGMT_FT_PSK: |
| return WLAN_CRYPTO_KEY_MGMT_FT_PSK; |
| case RSN_AUTH_KEY_MGMT_802_1X_SHA256: |
| return WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256; |
| case RSN_AUTH_KEY_MGMT_PSK_SHA256: |
| return WLAN_CRYPTO_KEY_MGMT_PSK_SHA256; |
| case RSN_AUTH_KEY_MGMT_SAE: |
| return WLAN_CRYPTO_KEY_MGMT_SAE; |
| case RSN_AUTH_KEY_MGMT_FT_SAE: |
| return WLAN_CRYPTO_KEY_MGMT_FT_SAE; |
| case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B: |
| return WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B; |
| case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192: |
| return WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192; |
| case RSN_AUTH_KEY_MGMT_CCKM: |
| return WLAN_CRYPTO_KEY_MGMT_CCKM; |
| case RSN_AUTH_KEY_MGMT_OSEN: |
| return WLAN_CRYPTO_KEY_MGMT_OSEN; |
| case RSN_AUTH_KEY_MGMT_FILS_SHA256: |
| return WLAN_CRYPTO_KEY_MGMT_FILS_SHA256; |
| case RSN_AUTH_KEY_MGMT_FILS_SHA384: |
| return WLAN_CRYPTO_KEY_MGMT_FILS_SHA384; |
| case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256: |
| return WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA256; |
| case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384: |
| return WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA384; |
| case RSN_AUTH_KEY_MGMT_OWE: |
| return WLAN_CRYPTO_KEY_MGMT_OWE; |
| case RSN_AUTH_KEY_MGMT_DPP: |
| return WLAN_CRYPTO_KEY_MGMT_DPP; |
| case RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_384: |
| return WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X_SHA384; |
| } |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_wpaie_check - called by mlme to check the wpaie |
| * @crypto params: crypto params |
| * @iebuf: ie buffer |
| * |
| * This function gets called by mlme to check the contents of wpa is |
| * matching with given crypto params |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_wpaie_check(struct wlan_crypto_params *crypto_params, |
| uint8_t *frm){ |
| uint8_t len = frm[1]; |
| int32_t w; |
| int n; |
| |
| /* |
| * Check the length once for fixed parts: OUI, type, |
| * version, mcast cipher, and 2 selector counts. |
| * Other, variable-length data, must be checked separately. |
| */ |
| RESET_AUTHMODE(crypto_params); |
| SET_AUTHMODE(crypto_params, WLAN_CRYPTO_AUTH_WPA); |
| |
| if (len < 14) |
| return QDF_STATUS_E_INVAL; |
| |
| frm += 6, len -= 4; |
| |
| w = LE_READ_2(frm); |
| if (w != WPA_VERSION) |
| return QDF_STATUS_E_INVAL; |
| |
| frm += 2, len -= 2; |
| |
| /* multicast/group cipher */ |
| RESET_MCAST_CIPHERS(crypto_params); |
| w = wlan_crypto_wpa_suite_to_cipher(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| SET_MCAST_CIPHER(crypto_params, w); |
| frm += 4, len -= 4; |
| |
| /* unicast ciphers */ |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (len < n*4+2) |
| return QDF_STATUS_E_INVAL; |
| |
| RESET_UCAST_CIPHERS(crypto_params); |
| for (; n > 0; n--) { |
| w = wlan_crypto_wpa_suite_to_cipher(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| SET_UCAST_CIPHER(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| |
| if (!crypto_params->ucastcipherset) |
| return QDF_STATUS_E_INVAL; |
| |
| /* key management algorithms */ |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (len < n*4) |
| return QDF_STATUS_E_INVAL; |
| |
| w = 0; |
| RESET_KEY_MGMT(crypto_params); |
| for (; n > 0; n--) { |
| w = wlan_crypto_wpa_suite_to_keymgmt(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| SET_KEY_MGMT(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| |
| /* optional capabilities */ |
| if (len >= 2) { |
| crypto_params->rsn_caps = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_crypto_rsnie_check - called by mlme to check the rsnie |
| * @crypto params: crypto params |
| * @iebuf: ie buffer |
| * |
| * This function gets called by mlme to check the contents of wpa is |
| * matching with given crypto params |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_rsnie_check(struct wlan_crypto_params *crypto_params, |
| uint8_t *frm){ |
| uint8_t len = frm[1]; |
| int32_t w; |
| int n; |
| |
| /* Check the length once for fixed parts: OUI, type & version */ |
| if (len < 2) |
| return QDF_STATUS_E_INVAL; |
| |
| /* initialize crypto params */ |
| qdf_mem_zero(crypto_params, sizeof(struct wlan_crypto_params)); |
| |
| SET_AUTHMODE(crypto_params, WLAN_CRYPTO_AUTH_RSNA); |
| |
| frm += 2; |
| /* NB: iswapoui already validated the OUI and type */ |
| w = LE_READ_2(frm); |
| if (w != RSN_VERSION) |
| return QDF_STATUS_E_INVAL; |
| |
| frm += 2, len -= 2; |
| |
| if (!len) { |
| /* set defaults */ |
| /* default group cipher CCMP-128 */ |
| SET_MCAST_CIPHER(crypto_params, WLAN_CRYPTO_CIPHER_AES_CCM); |
| /* default ucast cipher CCMP-128 */ |
| SET_UCAST_CIPHER(crypto_params, WLAN_CRYPTO_CIPHER_AES_CCM); |
| /* default key mgmt 8021x */ |
| SET_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X); |
| return QDF_STATUS_SUCCESS; |
| } else if (len < 4) { |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| /* multicast/group cipher */ |
| w = wlan_crypto_rsn_suite_to_cipher(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| else { |
| SET_MCAST_CIPHER(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| |
| if (crypto_params->mcastcipherset == 0) |
| return QDF_STATUS_E_INVAL; |
| |
| if (!len) { |
| /* default ucast cipher CCMP-128 */ |
| SET_UCAST_CIPHER(crypto_params, WLAN_CRYPTO_CIPHER_AES_CCM); |
| /* default key mgmt 8021x */ |
| SET_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X); |
| return QDF_STATUS_SUCCESS; |
| } else if (len < 2) { |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| /* unicast ciphers */ |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (n) { |
| if (len < n * 4) |
| return QDF_STATUS_E_INVAL; |
| |
| for (; n > 0; n--) { |
| w = wlan_crypto_rsn_suite_to_cipher(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| SET_UCAST_CIPHER(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| } else { |
| /* default ucast cipher CCMP-128 */ |
| SET_UCAST_CIPHER(crypto_params, WLAN_CRYPTO_CIPHER_AES_CCM); |
| } |
| |
| if (crypto_params->ucastcipherset == 0) |
| return QDF_STATUS_E_INVAL; |
| |
| if (!len) { |
| /* default key mgmt 8021x */ |
| SET_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X); |
| return QDF_STATUS_SUCCESS; |
| } else if (len < 2) { |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| /* key management algorithms */ |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| |
| if (n) { |
| if (len < n * 4) |
| return QDF_STATUS_E_INVAL; |
| |
| for (; n > 0; n--) { |
| w = wlan_crypto_rsn_suite_to_keymgmt(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| SET_KEY_MGMT(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| } else { |
| /* default key mgmt 8021x */ |
| SET_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X); |
| } |
| |
| if (crypto_params->key_mgmt == 0) |
| return QDF_STATUS_E_INVAL; |
| |
| /* optional capabilities */ |
| if (len >= 2) { |
| crypto_params->rsn_caps = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| } else if (len && len < 2) { |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| |
| /* PMKID */ |
| if (len >= 2) { |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (n && len) { |
| if (len >= n * PMKID_LEN) |
| frm += (n * PMKID_LEN), len -= (n * PMKID_LEN); |
| else |
| return QDF_STATUS_E_INVAL; |
| } else if (n && !len) { |
| return QDF_STATUS_E_INVAL; |
| } |
| /*TODO: Save pmkid in params for further reference */ |
| } |
| |
| /* BIP */ |
| if (!len && |
| (crypto_params->rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)) { |
| /* when no BIP mentioned and MFP capable use CMAC as default*/ |
| SET_MGMT_CIPHER(crypto_params, WLAN_CRYPTO_CIPHER_AES_CMAC); |
| return QDF_STATUS_SUCCESS; |
| } else if (len >= 4) { |
| w = wlan_crypto_rsn_suite_to_cipher(frm); |
| frm += 4, len -= 4; |
| SET_MGMT_CIPHER(crypto_params, w); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_build_wpaie - called by mlme to build wpaie |
| * @vdev: vdev |
| * @iebuf: ie buffer |
| * |
| * This function gets called by mlme to build wpaie from given vdev |
| * |
| * Return: end of buffer |
| */ |
| uint8_t *wlan_crypto_build_wpaie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *iebuf){ |
| uint8_t *frm = iebuf; |
| uint8_t *selcnt; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| |
| if (!frm) |
| return NULL; |
| |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv); |
| |
| if (!crypto_params) |
| return NULL; |
| |
| *frm++ = WLAN_ELEMID_VENDOR; |
| *frm++ = 0; |
| WLAN_CRYPTO_ADDSELECTOR(frm, WPA_TYPE_OUI); |
| WLAN_CRYPTO_ADDSHORT(frm, WPA_VERSION); |
| |
| |
| /* multicast cipher */ |
| if (MCIPHER_IS_TKIP(crypto_params)) |
| WPA_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_TKIP); |
| else if (MCIPHER_IS_CCMP128(crypto_params)) |
| WPA_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_CCM); |
| |
| /* unicast cipher list */ |
| selcnt = frm; |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| /* do not use CCMP unicast cipher in WPA mode */ |
| if (UCIPHER_IS_CCMP128(crypto_params)) { |
| selcnt[0]++; |
| WPA_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_CCM); |
| } |
| if (UCIPHER_IS_TKIP(crypto_params)) { |
| selcnt[0]++; |
| WPA_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_TKIP); |
| } |
| |
| /* authenticator selector list */ |
| selcnt = frm; |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X)) { |
| selcnt[0]++; |
| WPA_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_IEEE8021X); |
| } else if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_PSK)) { |
| selcnt[0]++; |
| WPA_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_PSK); |
| } else if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_CCKM)) { |
| selcnt[0]++; |
| WPA_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_CCKM); |
| } else { |
| selcnt[0]++; |
| WPA_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_NONE); |
| } |
| |
| /* optional capabilities */ |
| if (crypto_params->rsn_caps != 0 && |
| crypto_params->rsn_caps != WLAN_CRYPTO_RSN_CAP_PREAUTH) { |
| WLAN_CRYPTO_ADDSHORT(frm, crypto_params->rsn_caps); |
| } |
| |
| /* calculate element length */ |
| iebuf[1] = frm - iebuf - 2; |
| |
| return frm; |
| } |
| |
| /** |
| * wlan_crypto_build_rsnie - called by mlme to build rsnie |
| * @vdev: vdev |
| * @iebuf: ie buffer |
| * @bssid: bssid mac address to add pmkid in rsnie |
| * |
| * This function gets called by mlme to build rsnie from given vdev |
| * |
| * Return: end of buffer |
| */ |
| uint8_t *wlan_crypto_build_rsnie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *iebuf, |
| struct qdf_mac_addr *bssid) |
| { |
| uint8_t *frm = iebuf; |
| uint8_t *selcnt; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| |
| if (!frm) { |
| return NULL; |
| } |
| |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv); |
| |
| if (!crypto_params) { |
| return NULL; |
| } |
| |
| *frm++ = WLAN_ELEMID_RSN; |
| *frm++ = 0; |
| WLAN_CRYPTO_ADDSHORT(frm, RSN_VERSION); |
| |
| |
| /* multicast cipher */ |
| if (MCIPHER_IS_TKIP(crypto_params)) |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_TKIP); |
| else if (MCIPHER_IS_CCMP128(crypto_params)) |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_CCM); |
| else if (MCIPHER_IS_CCMP256(crypto_params)) |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_CCM_256); |
| else if (MCIPHER_IS_GCMP128(crypto_params)) |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_GCM); |
| else if (MCIPHER_IS_GCMP256(crypto_params)) |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_GCM_256); |
| |
| /* unicast cipher list */ |
| selcnt = frm; |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| |
| if (UCIPHER_IS_CCMP256(crypto_params)) { |
| selcnt[0]++; |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_CCM_256); |
| } |
| if (UCIPHER_IS_GCMP256(crypto_params)) { |
| selcnt[0]++; |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_GCM_256); |
| } |
| if (UCIPHER_IS_CCMP128(crypto_params)) { |
| selcnt[0]++; |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_CCM); |
| } |
| if (UCIPHER_IS_GCMP128(crypto_params)) { |
| selcnt[0]++; |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_AES_GCM); |
| } |
| if (UCIPHER_IS_TKIP(crypto_params)) { |
| selcnt[0]++; |
| RSN_ADD_CIPHER_TO_SUITE(frm, WLAN_CRYPTO_CIPHER_TKIP); |
| } |
| |
| /* authenticator selector list */ |
| selcnt = frm; |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_CCKM)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_CCKM); |
| /* Other key mgmt should not be added after CCKM */ |
| goto add_rsn_caps; |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_IEEE8021X); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_PSK)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_PSK); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, |
| WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FT_PSK)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_FT_PSK); |
| } |
| if (HAS_KEY_MGMT(crypto_params, |
| WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, |
| WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_PSK_SHA256)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_PSK_SHA256); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_SAE)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_SAE); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FT_SAE)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_FT_SAE); |
| } |
| if (HAS_KEY_MGMT(crypto_params, |
| WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B)) { |
| uint32_t kmgmt = WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B; |
| |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, kmgmt); |
| } |
| if (HAS_KEY_MGMT(crypto_params, |
| WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192)) { |
| uint32_t kmgmt = WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192; |
| |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, kmgmt); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FILS_SHA256)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_FILS_SHA256); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FILS_SHA384)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_FILS_SHA384); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA256)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, |
| WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA256); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA384)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, |
| WLAN_CRYPTO_KEY_MGMT_FT_FILS_SHA384); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_OWE)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_OWE); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_DPP)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_DPP); |
| } |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_OSEN)) { |
| selcnt[0]++; |
| RSN_ADD_KEYMGMT_TO_SUITE(frm, WLAN_CRYPTO_KEY_MGMT_OSEN); |
| } |
| add_rsn_caps: |
| WLAN_CRYPTO_ADDSHORT(frm, crypto_params->rsn_caps); |
| /* optional capabilities */ |
| if (crypto_params->rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) { |
| /* PMK list */ |
| if (bssid) { |
| struct wlan_crypto_pmksa *pmksa; |
| |
| pmksa = wlan_crypto_get_pmksa(vdev, bssid); |
| |
| if (pmksa) { |
| WLAN_CRYPTO_ADDSHORT(frm, 1); |
| qdf_mem_copy(frm, pmksa->pmkid, PMKID_LEN); |
| frm += PMKID_LEN; |
| } else { |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| } |
| } else |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| |
| if (HAS_MGMT_CIPHER(crypto_params, |
| WLAN_CRYPTO_CIPHER_AES_CMAC)) { |
| RSN_ADD_CIPHER_TO_SUITE(frm, |
| WLAN_CRYPTO_CIPHER_AES_CMAC); |
| } |
| if (HAS_MGMT_CIPHER(crypto_params, |
| WLAN_CRYPTO_CIPHER_AES_GMAC)) { |
| RSN_ADD_CIPHER_TO_SUITE(frm, |
| WLAN_CRYPTO_CIPHER_AES_GMAC); |
| } |
| if (HAS_MGMT_CIPHER(crypto_params, |
| WLAN_CRYPTO_CIPHER_AES_CMAC_256)) { |
| RSN_ADD_CIPHER_TO_SUITE(frm, |
| WLAN_CRYPTO_CIPHER_AES_CMAC_256 |
| ); |
| } |
| |
| if (HAS_MGMT_CIPHER(crypto_params, |
| WLAN_CRYPTO_CIPHER_AES_GMAC_256)) { |
| RSN_ADD_CIPHER_TO_SUITE(frm, |
| WLAN_CRYPTO_CIPHER_AES_GMAC_256 |
| ); |
| } |
| } else { |
| /* PMK list */ |
| if (bssid) { |
| struct wlan_crypto_pmksa *pmksa; |
| |
| pmksa = wlan_crypto_get_pmksa(vdev, bssid); |
| if (pmksa) { |
| WLAN_CRYPTO_ADDSHORT(frm, 1); |
| qdf_mem_copy(frm, pmksa->pmkid, PMKID_LEN); |
| frm += PMKID_LEN; |
| } else { |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| } |
| } |
| } |
| |
| /* calculate element length */ |
| iebuf[1] = frm - iebuf - 2; |
| |
| return frm; |
| } |
| |
| bool wlan_crypto_rsn_info(struct wlan_objmgr_vdev *vdev, |
| struct wlan_crypto_params *crypto_params){ |
| struct wlan_crypto_params *my_crypto_params; |
| my_crypto_params = wlan_crypto_vdev_get_crypto_params(vdev); |
| |
| if (!my_crypto_params) { |
| crypto_debug("vdev crypto params is NULL"); |
| return false; |
| } |
| /* |
| * Check peer's pairwise ciphers. |
| * At least one must match with our unicast cipher |
| */ |
| if (!UCAST_CIPHER_MATCH(crypto_params, my_crypto_params)) { |
| crypto_debug("Unicast cipher match failed"); |
| return false; |
| } |
| /* |
| * Check peer's group cipher is our enabled multicast cipher. |
| */ |
| if (!MCAST_CIPHER_MATCH(crypto_params, my_crypto_params)) { |
| crypto_debug("Multicast cipher match failed"); |
| return false; |
| } |
| /* |
| * Check peer's key management class set (PSK or UNSPEC) |
| */ |
| if (!KEY_MGMTSET_MATCH(crypto_params, my_crypto_params)) { |
| crypto_debug("Key mgmt match failed"); |
| return false; |
| } |
| if (wlan_crypto_vdev_is_pmf_required(vdev) && |
| !(crypto_params->rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)) { |
| crypto_debug("Peer is not PMF capable"); |
| return false; |
| } |
| if (!wlan_crypto_vdev_is_pmf_enabled(vdev) && |
| (crypto_params->rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)) { |
| crypto_debug("Peer needs PMF, but vdev is not capable"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Convert an WAPI CIPHER suite to to an internal code. |
| */ |
| static int32_t wlan_crypto_wapi_suite_to_cipher(uint8_t *sel) |
| { |
| uint32_t w = LE_READ_4(sel); |
| int32_t status = -1; |
| |
| switch (w) { |
| case (WLAN_WAPI_SEL(WLAN_CRYPTO_WAPI_SMS4_CIPHER)): |
| return WLAN_CRYPTO_CIPHER_WAPI_SMS4; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * Convert an WAPI key management/authentication algorithm |
| * to an internal code. |
| */ |
| static int32_t wlan_crypto_wapi_keymgmt(u_int8_t *sel) |
| { |
| uint32_t w = LE_READ_4(sel); |
| int32_t status = -1; |
| |
| switch (w) { |
| case (WLAN_WAPI_SEL(WLAN_WAI_PSK)): |
| return WLAN_CRYPTO_KEY_MGMT_WAPI_PSK; |
| case (WLAN_WAPI_SEL(WLAN_WAI_CERT_OR_SMS4)): |
| return WLAN_CRYPTO_KEY_MGMT_WAPI_CERT; |
| } |
| |
| return status; |
| } |
| /** |
| * wlan_crypto_wapiie_check - called by mlme to check the wapiie |
| * @crypto params: crypto params |
| * @iebuf: ie buffer |
| * |
| * This function gets called by mlme to check the contents of wapi is |
| * matching with given crypto params |
| * |
| * Return: QDF_STATUS_SUCCESS - in case of success |
| */ |
| QDF_STATUS wlan_crypto_wapiie_check(struct wlan_crypto_params *crypto_params, |
| uint8_t *frm) |
| { |
| uint8_t len = frm[1]; |
| int32_t w; |
| int n; |
| |
| /* |
| * Check the length once for fixed parts: OUI, type, |
| * version, mcast cipher, and 2 selector counts. |
| * Other, variable-length data, must be checked separately. |
| */ |
| RESET_AUTHMODE(crypto_params); |
| SET_AUTHMODE(crypto_params, WLAN_CRYPTO_AUTH_WAPI); |
| |
| if (len < WLAN_CRYPTO_WAPI_IE_LEN) |
| return QDF_STATUS_E_INVAL; |
| |
| |
| frm += 2; |
| |
| w = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (w != WAPI_VERSION) |
| return QDF_STATUS_E_INVAL; |
| |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (len < n*4+2) |
| return QDF_STATUS_E_INVAL; |
| |
| RESET_KEY_MGMT(crypto_params); |
| for (; n > 0; n--) { |
| w = wlan_crypto_wapi_keymgmt(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| |
| SET_KEY_MGMT(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| |
| /* unicast ciphers */ |
| n = LE_READ_2(frm); |
| frm += 2, len -= 2; |
| if (len < n*4+2) |
| return QDF_STATUS_E_INVAL; |
| |
| RESET_UCAST_CIPHERS(crypto_params); |
| for (; n > 0; n--) { |
| w = wlan_crypto_wapi_suite_to_cipher(frm); |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| SET_UCAST_CIPHER(crypto_params, w); |
| frm += 4, len -= 4; |
| } |
| |
| if (!crypto_params->ucastcipherset) |
| return QDF_STATUS_E_INVAL; |
| |
| /* multicast/group cipher */ |
| RESET_MCAST_CIPHERS(crypto_params); |
| w = wlan_crypto_wapi_suite_to_cipher(frm); |
| |
| if (w < 0) |
| return QDF_STATUS_E_INVAL; |
| |
| SET_MCAST_CIPHER(crypto_params, w); |
| frm += 4, len -= 4; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_build_wapiie - called by mlme to build wapi ie |
| * @vdev: vdev |
| * @iebuf: ie buffer |
| * |
| * This function gets called by mlme to build wapi ie from given vdev |
| * |
| * Return: end of buffer |
| */ |
| uint8_t *wlan_crypto_build_wapiie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *iebuf) |
| { |
| uint8_t *frm; |
| uint8_t *selcnt; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| |
| frm = iebuf; |
| if (!frm) { |
| crypto_err("ie buffer NULL"); |
| return NULL; |
| } |
| |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv); |
| |
| if (!crypto_params) { |
| crypto_err("crypto_params NULL"); |
| return NULL; |
| } |
| |
| *frm++ = WLAN_ELEMID_WAPI; |
| *frm++ = 0; |
| |
| WLAN_CRYPTO_ADDSHORT(frm, WAPI_VERSION); |
| |
| /* authenticator selector list */ |
| selcnt = frm; |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_WAPI_PSK)) { |
| selcnt[0]++; |
| WLAN_CRYPTO_ADDSELECTOR(frm, |
| WLAN_WAPI_SEL(WLAN_WAI_PSK)); |
| } |
| |
| if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_WAPI_CERT)) { |
| selcnt[0]++; |
| WLAN_CRYPTO_ADDSELECTOR(frm, |
| WLAN_WAPI_SEL(WLAN_WAI_CERT_OR_SMS4)); |
| } |
| |
| /* unicast cipher list */ |
| selcnt = frm; |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| |
| if (UCIPHER_IS_SMS4(crypto_params)) { |
| selcnt[0]++; |
| WLAN_CRYPTO_ADDSELECTOR(frm, |
| WLAN_WAPI_SEL(WLAN_CRYPTO_WAPI_SMS4_CIPHER)); |
| } |
| |
| WLAN_CRYPTO_ADDSELECTOR(frm, |
| WLAN_WAPI_SEL(WLAN_CRYPTO_WAPI_SMS4_CIPHER)); |
| |
| /* optional capabilities */ |
| WLAN_CRYPTO_ADDSHORT(frm, crypto_params->rsn_caps); |
| |
| /* bkid count */ |
| if (vdev->vdev_mlme.vdev_opmode == QDF_STA_MODE || |
| vdev->vdev_mlme.vdev_opmode == QDF_P2P_CLIENT_MODE) |
| WLAN_CRYPTO_ADDSHORT(frm, 0); |
| |
| /* calculate element length */ |
| iebuf[1] = frm - iebuf - 2; |
| |
| return frm; |
| |
| } |
| |
| /** |
| * wlan_crypto_pn_check - called by data patch for PN check |
| * @vdev: vdev |
| * @wbuf: wbuf |
| * |
| * This function gets called by data patch for PN check |
| * |
| * Return: QDF_STATUS |
| */ |
| QDF_STATUS wlan_crypto_pn_check(struct wlan_objmgr_vdev *vdev, |
| qdf_nbuf_t wbuf){ |
| /* Need to check is there real requirement for this function |
| * as PN check is already handled in decap function. |
| */ |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_vdev_get_crypto_params - called by mlme to get crypto params |
| * @vdev:vdev |
| * |
| * This function gets called by mlme to get crypto params |
| * |
| * Return: wlan_crypto_params or NULL in case of failure |
| */ |
| struct wlan_crypto_params *wlan_crypto_vdev_get_crypto_params( |
| struct wlan_objmgr_vdev *vdev){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| |
| return wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv); |
| } |
| |
| /** |
| * wlan_crypto_peer_get_crypto_params - called by mlme to get crypto params |
| * @peer:peer |
| * |
| * This function gets called by mlme to get crypto params |
| * |
| * Return: wlan_crypto_params or NULL in case of failure |
| */ |
| struct wlan_crypto_params *wlan_crypto_peer_get_crypto_params( |
| struct wlan_objmgr_peer *peer){ |
| struct wlan_crypto_comp_priv *crypto_priv; |
| |
| return wlan_crypto_peer_get_comp_params(peer, &crypto_priv); |
| } |
| |
| |
| QDF_STATUS wlan_crypto_set_peer_wep_keys(struct wlan_objmgr_vdev *vdev, |
| struct wlan_objmgr_peer *peer) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_comp_priv *sta_crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| struct wlan_crypto_key *sta_key; |
| struct wlan_crypto_cipher *cipher_table; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t *mac_addr; |
| int i; |
| enum QDF_OPMODE opmode; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!vdev) |
| return QDF_STATUS_E_NULL_VALUE; |
| |
| if (!peer) { |
| crypto_debug("peer NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| psoc = wlan_vdev_get_psoc(vdev); |
| |
| if (!psoc) { |
| crypto_err("psoc NULL"); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| wlan_peer_obj_lock(peer); |
| mac_addr = wlan_peer_get_macaddr(peer); |
| wlan_peer_obj_unlock(peer); |
| |
| crypto_params = wlan_crypto_vdev_get_comp_params(vdev, |
| &crypto_priv); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| /* push only valid static WEP keys from vap */ |
| if (AUTH_IS_8021X(crypto_params)) |
| return QDF_STATUS_E_INVAL; |
| |
| if (opmode == QDF_STA_MODE) { |
| peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_CRYPTO_ID); |
| if (!peer) { |
| crypto_err("peer NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| } |
| |
| wlan_crypto_peer_get_comp_params(peer, &sta_crypto_priv); |
| if (!sta_crypto_priv) { |
| crypto_err("sta priv is null"); |
| status = QDF_STATUS_E_INVAL; |
| goto exit; |
| } |
| |
| for (i = 0; i < WLAN_CRYPTO_MAXKEYIDX; i++) { |
| if (crypto_priv->key[i]) { |
| key = crypto_priv->key[i]; |
| if (!key || !key->valid) |
| continue; |
| |
| cipher_table = (struct wlan_crypto_cipher *) |
| key->cipher_table; |
| |
| if (cipher_table->cipher == WLAN_CRYPTO_CIPHER_WEP) { |
| sta_key = qdf_mem_malloc( |
| sizeof(struct wlan_crypto_key)); |
| if (!sta_key) { |
| status = QDF_STATUS_E_NOMEM; |
| goto exit; |
| } |
| |
| sta_crypto_priv->key[i] = sta_key; |
| qdf_mem_copy(sta_key, key, |
| sizeof(struct wlan_crypto_key)); |
| |
| sta_key->flags &= ~WLAN_CRYPTO_KEY_DEFAULT; |
| |
| if (crypto_priv->def_tx_keyid == i) { |
| sta_key->flags |
| |= WLAN_CRYPTO_KEY_DEFAULT; |
| sta_crypto_priv->def_tx_keyid = |
| crypto_priv->def_tx_keyid; |
| } |
| /* setting the broadcast/multicast key for sta*/ |
| if (opmode == QDF_STA_MODE || |
| opmode == QDF_IBSS_MODE){ |
| if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_SETKEY(psoc)( |
| vdev, sta_key, mac_addr, |
| cipher_table->cipher); |
| } |
| } |
| |
| /* setting unicast key */ |
| sta_key->flags &= ~WLAN_CRYPTO_KEY_GROUP; |
| if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(vdev, |
| sta_key, mac_addr, |
| cipher_table->cipher); |
| } |
| } |
| } |
| } |
| |
| exit: |
| if (opmode == QDF_STA_MODE) |
| wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_crypto_register_crypto_rx_ops - set crypto_rx_ops |
| * @crypto_rx_ops: crypto_rx_ops |
| * |
| * This function gets called by object manger to register crypto rx ops. |
| * |
| * Return: QDF_STATUS |
| */ |
| QDF_STATUS wlan_crypto_register_crypto_rx_ops( |
| struct wlan_lmac_if_crypto_rx_ops *crypto_rx_ops){ |
| crypto_rx_ops->crypto_encap = wlan_crypto_encap; |
| crypto_rx_ops->crypto_decap = wlan_crypto_decap; |
| crypto_rx_ops->crypto_enmic = wlan_crypto_enmic; |
| crypto_rx_ops->crypto_demic = wlan_crypto_demic; |
| crypto_rx_ops->set_peer_wep_keys = wlan_crypto_set_peer_wep_keys; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * wlan_crypto_get_crypto_rx_ops - get crypto_rx_ops from psoc |
| * @psoc: psoc |
| * |
| * This function gets called by umac to get the crypto_rx_ops |
| * |
| * Return: crypto_rx_ops |
| */ |
| struct wlan_lmac_if_crypto_rx_ops *wlan_crypto_get_crypto_rx_ops( |
| struct wlan_objmgr_psoc *psoc) |
| { |
| |
| return &(psoc->soc_cb.rx_ops.crypto_rx_ops); |
| } |
| qdf_export_symbol(wlan_crypto_get_crypto_rx_ops); |
| |
| /** |
| * wlan_crypto_vdev_has_auth_mode - check authmode for vdev |
| * @vdev: vdev |
| * @authvalue: authvalue to be checked |
| * |
| * This function check is authvalue passed is set in vdev or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_vdev_has_auth_mode(struct wlan_objmgr_vdev *vdev, |
| wlan_crypto_auth_mode authvalue) |
| { |
| return wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE) |
| & authvalue; |
| } |
| qdf_export_symbol(wlan_crypto_vdev_has_auth_mode); |
| |
| /** |
| * wlan_crypto_peer_has_auth_mode - check authmode for peer |
| * @peer: peer |
| * @authvalue: authvalue to be checked |
| * |
| * This function check is authvalue passed is set in peer or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_peer_has_auth_mode(struct wlan_objmgr_peer *peer, |
| wlan_crypto_auth_mode authvalue) |
| { |
| return wlan_crypto_get_peer_param(peer, WLAN_CRYPTO_PARAM_AUTH_MODE) |
| & authvalue; |
| } |
| qdf_export_symbol(wlan_crypto_peer_has_auth_mode); |
| |
| /** |
| * wlan_crypto_vdev_has_ucastcipher - check ucastcipher for vdev |
| * @vdev: vdev |
| * @ucastcipher: ucastcipher to be checked |
| * |
| * This function check is ucastcipher passed is set in vdev or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_vdev_has_ucastcipher(struct wlan_objmgr_vdev *vdev, |
| wlan_crypto_cipher_type ucastcipher) |
| { |
| return wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_UCAST_CIPHER) |
| & ucastcipher; |
| } |
| qdf_export_symbol(wlan_crypto_vdev_has_ucastcipher); |
| |
| /** |
| * wlan_crypto_peer_has_ucastcipher - check ucastcipher for peer |
| * @peer: peer |
| * @ucastcipher: ucastcipher to be checked |
| * |
| * This function check is ucastcipher passed is set in peer or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_peer_has_ucastcipher(struct wlan_objmgr_peer *peer, |
| wlan_crypto_cipher_type ucastcipher) |
| { |
| return wlan_crypto_get_peer_param(peer, WLAN_CRYPTO_PARAM_UCAST_CIPHER) |
| & ucastcipher; |
| } |
| qdf_export_symbol(wlan_crypto_peer_has_ucastcipher); |
| |
| /** |
| * wlan_crypto_vdev_has_mcastcipher - check mcastcipher for vdev |
| * @vdev: vdev |
| * @mcastcipher: mcastcipher to be checked |
| * |
| * This function check is mcastcipher passed is set in vdev or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_vdev_has_mcastcipher(struct wlan_objmgr_vdev *vdev, |
| wlan_crypto_cipher_type mcastcipher) |
| { |
| return wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MCAST_CIPHER) |
| & mcastcipher; |
| } |
| qdf_export_symbol(wlan_crypto_vdev_has_mcastcipher); |
| |
| /** |
| * wlan_crypto_peer_has_mcastcipher - check mcastcipher for peer |
| * @peer: peer |
| * @mcastcipher: mcastcipher to be checked |
| * |
| * This function check is mcastcipher passed is set in peer or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_peer_has_mcastcipher(struct wlan_objmgr_peer *peer, |
| wlan_crypto_cipher_type mcastcipher) |
| { |
| return wlan_crypto_get_peer_param(peer, WLAN_CRYPTO_PARAM_MCAST_CIPHER) |
| & mcastcipher; |
| } |
| qdf_export_symbol(wlan_crypto_peer_has_mcastcipher); |
| |
| /** |
| * wlan_crypto_vdev_has_mgmtcipher - check mgmtcipher for vdev |
| * @vdev: vdev |
| * @mgmtcipher: mgmtcipher to be checked |
| * |
| * This function checks any one of mgmtciphers are supported by vdev or not. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_vdev_has_mgmtcipher(struct wlan_objmgr_vdev *vdev, |
| uint32_t mgmtcipher) |
| { |
| return (wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER) |
| & mgmtcipher) != 0; |
| } |
| |
| qdf_export_symbol(wlan_crypto_vdev_has_mgmtcipher); |
| |
| /** |
| * wlan_crypto_peer_has_mgmtcipher - check mgmtcipher for peer |
| * @peer: peer |
| * @mgmtcipher: mgmtcipher to be checked |
| * |
| * This function checks any one of mgmtciphers are supported by peer or not |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_peer_has_mgmtcipher(struct wlan_objmgr_peer *peer, |
| uint32_t mgmtcipher) |
| { |
| return (wlan_crypto_get_peer_param(peer, WLAN_CRYPTO_PARAM_MGMT_CIPHER) |
| & mgmtcipher) != 0; |
| } |
| |
| qdf_export_symbol(wlan_crypto_peer_has_mgmtcipher); |
| |
| uint8_t wlan_crypto_get_peer_fils_aead(struct wlan_objmgr_peer *peer) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv = NULL; |
| |
| if (!peer) { |
| crypto_err("Invalid Input"); |
| return 0; |
| } |
| |
| crypto_priv = wlan_get_peer_crypto_obj(peer); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return 0; |
| } |
| |
| return crypto_priv->fils_aead_set; |
| } |
| |
| void |
| wlan_crypto_set_peer_fils_aead(struct wlan_objmgr_peer *peer, uint8_t value) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv = NULL; |
| |
| if (!peer) { |
| crypto_err("Invalid Input"); |
| return; |
| } |
| |
| crypto_priv = wlan_get_peer_crypto_obj(peer); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return; |
| } |
| |
| crypto_priv->fils_aead_set = value; |
| } |
| |
| /** |
| * wlan_crypto_get_key_header - get header length |
| * @key: key |
| * |
| * This function gets header length based on keytype |
| * |
| * Return: header length |
| */ |
| uint8_t wlan_crypto_get_key_header(struct wlan_crypto_key *key) |
| { |
| struct wlan_crypto_cipher *cipher_table; |
| |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| if (cipher_table) |
| return cipher_table->header; |
| else |
| return 0; |
| } |
| |
| qdf_export_symbol(wlan_crypto_get_key_header); |
| |
| /** |
| * wlan_crypto_get_key_trailer - get cipher trailer length |
| * @key: key |
| * |
| * This function gets cipher trailer length based on keytype |
| * |
| * Return: cipher trailer length |
| */ |
| uint8_t wlan_crypto_get_key_trailer(struct wlan_crypto_key *key) |
| { |
| struct wlan_crypto_cipher *cipher_table; |
| |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| if (cipher_table) |
| return cipher_table->trailer; |
| else |
| return 0; |
| } |
| |
| qdf_export_symbol(wlan_crypto_get_key_trailer); |
| |
| /** |
| * wlan_crypto_get_key_miclen - get cipher miclen length |
| * @key: key |
| * |
| * This function gets cipher miclen length based on keytype |
| * |
| * Return: cipher miclen length |
| */ |
| uint8_t wlan_crypto_get_key_miclen(struct wlan_crypto_key *key) |
| { |
| struct wlan_crypto_cipher *cipher_table; |
| |
| cipher_table = (struct wlan_crypto_cipher *)key->cipher_table; |
| if (cipher_table) |
| return cipher_table->miclen; |
| else |
| return 0; |
| } |
| |
| qdf_export_symbol(wlan_crypto_get_key_miclen); |
| |
| /** |
| * wlan_crypto_get_keyid - get keyid from frame |
| * @data: frame |
| * |
| * This function parse frame and returns keyid |
| * |
| * Return: keyid |
| */ |
| uint16_t wlan_crypto_get_keyid(uint8_t *data, int hdrlen) |
| { |
| struct wlan_frame_hdr *hdr = (struct wlan_frame_hdr *)data; |
| uint8_t *iv; |
| uint8_t stype = WLAN_FC0_GET_STYPE(hdr->i_fc[0]); |
| |
| /* |
| * In FILS SK (Re)Association request/response frame has |
| * to be decrypted |
| */ |
| if ((stype == WLAN_FC0_STYPE_ASSOC_REQ) || |
| (stype == WLAN_FC0_STYPE_REASSOC_REQ) || |
| (stype == WLAN_FC0_STYPE_ASSOC_RESP) || |
| (stype == WLAN_FC0_STYPE_REASSOC_RESP)) { |
| return 0; |
| } |
| |
| if (hdr->i_fc[1] & WLAN_FC1_ISWEP) { |
| iv = data + hdrlen; |
| /* |
| * iv[3] is the Key ID octet in the CCMP/TKIP/WEP headers |
| * Bits 6–7 of the Key ID octet are for the Key ID subfield |
| */ |
| return ((iv[3] >> 6) & 0x3); |
| } else { |
| return WLAN_CRYPTO_KEYIX_NONE; |
| } |
| } |
| |
| qdf_export_symbol(wlan_crypto_get_keyid); |
| |
| /** |
| * crypto_plumb_peer_keys - called during radio reset |
| * @vdev: vdev |
| * @object: peer |
| * @arg: psoc |
| * |
| * Restore unicast and persta hardware keys |
| * |
| * Return: void |
| */ |
| static void crypto_plumb_peer_keys(struct wlan_objmgr_vdev *vdev, |
| void *object, void *arg) { |
| struct wlan_objmgr_peer *peer = (struct wlan_objmgr_peer *)object; |
| struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)arg; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key = NULL; |
| int i; |
| |
| if ((!peer) || (!vdev) || (!psoc)) { |
| crypto_err("Peer or vdev or psoc objects are null!"); |
| return; |
| } |
| |
| crypto_params = wlan_crypto_peer_get_comp_params(peer, |
| &crypto_priv); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return; |
| } |
| |
| for (i = 0; i < WLAN_CRYPTO_MAXKEYIDX; i++) { |
| key = crypto_priv->key[i]; |
| if (key && key->valid) { |
| if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_SETKEY(psoc) |
| ( |
| vdev, |
| key, |
| wlan_peer_get_macaddr(peer), |
| wlan_crypto_get_key_type(key) |
| ); |
| } |
| } |
| } |
| } |
| |
| /** |
| * wlan_crypto_restore_keys - called during radio reset |
| * @vdev: vdev |
| * |
| * Clear and restore keycache, needed for some DA chipsets which put |
| * random values in keycache when phy reset is triggered |
| * |
| * Return: void |
| */ |
| void wlan_crypto_restore_keys(struct wlan_objmgr_vdev *vdev) |
| { |
| int i; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_crypto_key *key; |
| uint8_t macaddr[QDF_MAC_ADDR_SIZE] = |
| {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
| struct wlan_objmgr_pdev *pdev = NULL; |
| struct wlan_objmgr_psoc *psoc = NULL; |
| |
| pdev = wlan_vdev_get_pdev(vdev); |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (!pdev) { |
| crypto_err("pdev is NULL"); |
| return; |
| } |
| if (!psoc) { |
| crypto_err("psoc is NULL"); |
| return; |
| } |
| |
| /* TBD: QWRAP key restore*/ |
| /* crypto is on */ |
| if (wlan_vdev_mlme_feat_cap_get(vdev, WLAN_VDEV_F_PRIVACY)) { |
| /* restore static shared keys */ |
| for (i = 0; i < WLAN_CRYPTO_MAXKEYIDX; i++) { |
| crypto_params = wlan_crypto_vdev_get_comp_params |
| ( |
| vdev, |
| &crypto_priv |
| ); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv is NULL"); |
| return; |
| } |
| key = crypto_priv->key[i]; |
| if (key && key->valid) { |
| if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) { |
| WLAN_CRYPTO_TX_OPS_SETKEY(psoc) |
| ( |
| vdev, |
| key, |
| macaddr, |
| wlan_crypto_get_key_type(key) |
| ); |
| } |
| } |
| } |
| |
| wlan_objmgr_iterate_peerobj_list(vdev, |
| crypto_plumb_peer_keys, |
| psoc, |
| WLAN_CRYPTO_ID); |
| } |
| } |
| |
| /** |
| * wlan_crypto_check_open_none - called by ucfg to check for open security |
| * @psoc: psoc pointer |
| * @vdev_id: vdev id |
| * |
| * This function gets called from ucfg to check open security. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_check_open_none(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_objmgr_vdev *vdev; |
| bool match = true; |
| |
| if (!psoc) { |
| crypto_err("PSOC is NULL"); |
| return false; |
| } |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, |
| WLAN_CRYPTO_ID); |
| if (!vdev) { |
| crypto_err("vdev is NULL"); |
| return false; |
| } |
| |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| match = false; |
| goto send_res; |
| } |
| |
| crypto_params = &crypto_priv->crypto_params; |
| |
| if (crypto_params->mcastcipherset != WLAN_CRYPTO_CIPHER_NONE) { |
| match = false; |
| goto send_res; |
| } |
| |
| if ((crypto_params->authmodeset != WLAN_CRYPTO_AUTH_AUTO) && |
| (crypto_params->authmodeset != WLAN_CRYPTO_AUTH_NONE)) |
| match = false; |
| |
| send_res: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_CRYPTO_ID); |
| |
| return match; |
| } |
| |
| /** |
| * wlan_crypto_check_wep - called by ucfg to check for WEP security |
| * @psoc: psoc pointer |
| * @vdev_id: vdev id |
| * |
| * This function gets called from ucfg to check WEP security. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_check_wep(struct wlan_objmgr_psoc *psoc, uint8_t vdev_id) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| struct wlan_crypto_params *crypto_params; |
| struct wlan_objmgr_vdev *vdev; |
| bool match = true; |
| |
| if (!psoc) { |
| crypto_err("PSOC is NULL"); |
| return false; |
| } |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, |
| WLAN_CRYPTO_ID); |
| if (!vdev) { |
| crypto_err("vdev is NULL"); |
| return false; |
| } |
| |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| match = false; |
| goto send_res; |
| } |
| |
| crypto_params = &crypto_priv->crypto_params; |
| |
| if ((crypto_params->ucastcipherset != WLAN_CRYPTO_CIPHER_WEP) && |
| (crypto_params->ucastcipherset != WLAN_CRYPTO_CIPHER_WEP_40) && |
| (crypto_params->ucastcipherset != WLAN_CRYPTO_CIPHER_WEP_104)) { |
| match = false; |
| goto send_res; |
| } |
| if ((crypto_params->mcastcipherset != WLAN_CRYPTO_CIPHER_WEP) && |
| (crypto_params->mcastcipherset != WLAN_CRYPTO_CIPHER_WEP_40) && |
| (crypto_params->mcastcipherset != WLAN_CRYPTO_CIPHER_WEP_104)) { |
| match = false; |
| goto send_res; |
| } |
| if (crypto_params->ucastcipherset != crypto_params->mcastcipherset) { |
| match = false; |
| goto send_res; |
| } |
| if ((crypto_params->authmodeset != WLAN_CRYPTO_AUTH_AUTO) && |
| (crypto_params->authmodeset != WLAN_CRYPTO_AUTH_OPEN) && |
| (crypto_params->authmodeset != WLAN_CRYPTO_AUTH_SHARED)) { |
| match = false; |
| } |
| send_res: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_CRYPTO_ID); |
| |
| return match; |
| } |
| |
| static QDF_STATUS |
| wlan_get_crypto_params_from_rsn_ie(struct wlan_crypto_params *crypto_params, |
| uint8_t *ie_ptr, uint16_t ie_len) |
| { |
| const uint8_t *rsn_ie = NULL; |
| QDF_STATUS status; |
| |
| qdf_mem_zero(crypto_params, sizeof(struct wlan_crypto_params)); |
| rsn_ie = wlan_get_ie_ptr_from_eid(WLAN_ELEMID_RSN, ie_ptr, ie_len); |
| if (!rsn_ie) { |
| crypto_err("RSN IE NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| status = wlan_crypto_rsnie_check(crypto_params, (uint8_t *)rsn_ie); |
| if (QDF_STATUS_SUCCESS != status) { |
| crypto_err("RSN IE check failed"); |
| return status; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static QDF_STATUS |
| wlan_get_crypto_params_from_wpa_ie(struct wlan_crypto_params *crypto_params, |
| uint8_t *ie_ptr, uint16_t ie_len) |
| { |
| const uint8_t *wpa_ie = NULL; |
| uint32_t wpa_oui; |
| QDF_STATUS status; |
| |
| qdf_mem_zero(crypto_params, sizeof(struct wlan_crypto_params)); |
| |
| wpa_oui = WLAN_WPA_SEL(WLAN_WPA_OUI_TYPE); |
| wpa_ie = wlan_get_vendor_ie_ptr_from_oui((uint8_t *)&wpa_oui, |
| WLAN_OUI_SIZE, ie_ptr, ie_len); |
| if (!wpa_ie) { |
| crypto_err("WPA IE NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| status = wlan_crypto_wpaie_check(crypto_params, (uint8_t *)wpa_ie); |
| if (QDF_STATUS_SUCCESS != status) { |
| crypto_err("WPA IE check failed"); |
| return status; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| /** |
| * wlan_crypto_check_rsn_match - called by ucfg to check for RSN match |
| * @psoc: psoc pointer |
| * @vdev_id: vdev id |
| * @ie_ptr: pointer to IEs |
| * @ie_len: IE length |
| * |
| * This function gets called from ucfg to check RSN match. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_check_rsn_match(struct wlan_objmgr_psoc *psoc, |
| uint8_t vdev_id, uint8_t *ie_ptr, |
| uint16_t ie_len) |
| { |
| struct wlan_crypto_params peer_crypto_params; |
| struct wlan_objmgr_vdev *vdev; |
| bool match = true; |
| QDF_STATUS status; |
| |
| if (!psoc) { |
| crypto_err("PSOC is NULL"); |
| return false; |
| } |
| status = wlan_get_crypto_params_from_rsn_ie(&peer_crypto_params, |
| ie_ptr, ie_len); |
| if (QDF_STATUS_SUCCESS != status) { |
| crypto_err("get crypto prarams from RSN IE failed"); |
| return false; |
| } |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, |
| WLAN_CRYPTO_ID); |
| if (!vdev) { |
| crypto_err("vdev is NULL"); |
| return false; |
| } |
| |
| match = wlan_crypto_rsn_info(vdev, &peer_crypto_params); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_CRYPTO_ID); |
| |
| return match; |
| } |
| |
| /** |
| * wlan_crypto_check_wpa_match - called by ucfg to check for WPA match |
| * @psoc: psoc pointer |
| * @vdev_id: vdev id |
| * @ie_ptr: pointer to IEs |
| * @ie_len: IE length |
| * |
| * This function gets called from ucfg to check WPA match. |
| * |
| * Return: true or false |
| */ |
| bool wlan_crypto_check_wpa_match(struct wlan_objmgr_psoc *psoc, |
| uint8_t vdev_id, uint8_t *ie_ptr, |
| uint16_t ie_len) |
| { |
| struct wlan_crypto_params peer_crypto_params; |
| struct wlan_objmgr_vdev *vdev; |
| bool match = true; |
| QDF_STATUS status; |
| |
| if (!psoc) { |
| crypto_err("PSOC is NULL"); |
| return false; |
| } |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id, |
| WLAN_CRYPTO_ID); |
| if (!vdev) { |
| crypto_err("vdev is NULL"); |
| return false; |
| } |
| |
| status = wlan_get_crypto_params_from_wpa_ie(&peer_crypto_params, |
| ie_ptr, ie_len); |
| if (QDF_STATUS_SUCCESS != status) { |
| crypto_err("get crypto prarams from WPA IE failed"); |
| match = false; |
| goto send_res; |
| } |
| match = wlan_crypto_rsn_info(vdev, &peer_crypto_params); |
| send_res: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_CRYPTO_ID); |
| |
| return match; |
| } |
| |
| |
| static void |
| wlan_crypto_merge_prarams(struct wlan_crypto_params *dst_params, |
| struct wlan_crypto_params *src_params) |
| { |
| dst_params->authmodeset |= src_params->authmodeset; |
| dst_params->ucastcipherset |= src_params->ucastcipherset; |
| dst_params->mcastcipherset |= src_params->mcastcipherset; |
| dst_params->mgmtcipherset |= src_params->mgmtcipherset; |
| dst_params->cipher_caps |= src_params->cipher_caps; |
| dst_params->key_mgmt |= src_params->key_mgmt; |
| dst_params->rsn_caps |= src_params->rsn_caps; |
| } |
| |
| static void |
| wlan_crypto_reset_prarams(struct wlan_crypto_params *params) |
| { |
| params->authmodeset = 0; |
| params->ucastcipherset = 0; |
| params->mcastcipherset = 0; |
| params->mgmtcipherset = 0; |
| params->cipher_caps = 0; |
| params->key_mgmt = 0; |
| params->rsn_caps = 0; |
| } |
| |
| QDF_STATUS wlan_set_vdev_crypto_prarams_from_ie(struct wlan_objmgr_vdev *vdev, |
| uint8_t *ie_ptr, |
| uint16_t ie_len) |
| { |
| struct wlan_crypto_params crypto_params; |
| QDF_STATUS status; |
| struct wlan_crypto_params *vdev_crypto_params; |
| struct wlan_crypto_comp_priv *crypto_priv; |
| bool send_fail = false; |
| |
| if (!vdev) { |
| crypto_err("VDEV is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (!ie_ptr) { |
| crypto_err("IE ptr is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| crypto_priv = (struct wlan_crypto_comp_priv *) |
| wlan_get_vdev_crypto_obj(vdev); |
| |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| vdev_crypto_params = &crypto_priv->crypto_params; |
| |
| wlan_crypto_reset_prarams(vdev_crypto_params); |
| status = wlan_get_crypto_params_from_rsn_ie(&crypto_params, |
| ie_ptr, ie_len); |
| if (QDF_STATUS_SUCCESS == status) { |
| wlan_crypto_merge_prarams(vdev_crypto_params, &crypto_params); |
| } else { |
| crypto_err("get crypto prarams from RSN IE failed"); |
| send_fail = true; |
| } |
| |
| status = wlan_get_crypto_params_from_wpa_ie(&crypto_params, |
| ie_ptr, ie_len); |
| if (QDF_STATUS_SUCCESS == status) { |
| wlan_crypto_merge_prarams(vdev_crypto_params, &crypto_params); |
| send_fail = false; |
| } else { |
| crypto_debug("get crypto prarams from WPA IE failed"); |
| } |
| |
| return send_fail ? QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS; |
| } |
| |
| int8_t wlan_crypto_get_default_key_idx(struct wlan_objmgr_vdev *vdev, bool igtk) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| |
| crypto_priv = wlan_get_vdev_crypto_obj(vdev); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (igtk) |
| return crypto_priv->def_igtk_tx_keyid; |
| else |
| return crypto_priv->def_tx_keyid; |
| } |
| |
| enum wlan_crypto_cipher_type |
| wlan_crypto_get_cipher(struct wlan_objmgr_vdev *vdev, |
| bool pairwise, uint8_t key_index) |
| { |
| struct wlan_crypto_key *crypto_key; |
| |
| crypto_key = wlan_crypto_get_key(vdev, key_index); |
| |
| if (crypto_key) |
| return crypto_key->cipher_type; |
| else |
| return WLAN_CRYPTO_CIPHER_INVALID; |
| } |
| |
| #ifdef CRYPTO_SET_KEY_CONVERGED |
| QDF_STATUS wlan_crypto_validate_key_params(enum wlan_crypto_cipher_type cipher, |
| uint8_t key_index, uint8_t key_len, |
| uint8_t seq_len) |
| { |
| if (key_index >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX)) { |
| crypto_err("Invalid Key index %d", key_index); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (cipher == WLAN_CRYPTO_CIPHER_INVALID) { |
| crypto_err("Invalid Cipher %d", cipher); |
| return QDF_STATUS_E_INVAL; |
| } |
| if ((!(cipher == WLAN_CRYPTO_CIPHER_AES_CMAC || |
| cipher == WLAN_CRYPTO_CIPHER_AES_CMAC_256 || |
| cipher == WLAN_CRYPTO_CIPHER_AES_GMAC || |
| cipher == WLAN_CRYPTO_CIPHER_AES_GMAC_256)) && |
| (key_index >= WLAN_CRYPTO_MAXKEYIDX)) { |
| crypto_err("Invalid key index %d for cipher %d", |
| key_index, cipher); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (key_len > (WLAN_CRYPTO_KEYBUF_SIZE + WLAN_CRYPTO_MICBUF_SIZE)) { |
| crypto_err("Invalid key length %d", key_len); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (seq_len > WLAN_CRYPTO_RSC_SIZE) { |
| crypto_err("Invalid seq length %d", seq_len); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| crypto_debug("key: idx:%d, len:%d, seq len:%d", |
| key_index, key_len, seq_len); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS wlan_crypto_save_key(struct wlan_objmgr_vdev *vdev, |
| uint8_t key_index, |
| struct wlan_crypto_key *crypto_key) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| |
| crypto_priv = wlan_get_vdev_crypto_obj(vdev); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| if (key_index >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX)) { |
| crypto_err("Invalid Key index %d", key_index); |
| return QDF_STATUS_E_FAILURE; |
| } |
| if (key_index < WLAN_CRYPTO_MAXKEYIDX) |
| crypto_priv->key[key_index] = crypto_key; |
| else |
| crypto_priv->igtk_key[key_index - WLAN_CRYPTO_MAXKEYIDX] = |
| crypto_key; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| struct wlan_crypto_key *wlan_crypto_get_key(struct wlan_objmgr_vdev *vdev, |
| uint8_t key_index) |
| { |
| struct wlan_crypto_comp_priv *crypto_priv; |
| |
| crypto_priv = wlan_get_vdev_crypto_obj(vdev); |
| if (!crypto_priv) { |
| crypto_err("crypto_priv NULL"); |
| return NULL; |
| } |
| if (key_index >= (WLAN_CRYPTO_MAXKEYIDX + WLAN_CRYPTO_MAXIGTKKEYIDX)) { |
| crypto_err("Invalid Key index %d", key_index); |
| return NULL; |
| } |
| if (key_index < WLAN_CRYPTO_MAXKEYIDX) |
| return crypto_priv->key[key_index]; |
| |
| return crypto_priv->igtk_key[key_index - WLAN_CRYPTO_MAXKEYIDX]; |
| } |
| |
| QDF_STATUS wlan_crypto_set_key_req(struct wlan_objmgr_vdev *vdev, |
| struct wlan_crypto_key *req, |
| enum wlan_crypto_key_type key_type) |
| { |
| struct wlan_objmgr_psoc *psoc; |
| |
| psoc = wlan_vdev_get_psoc(vdev); |
| if (psoc && WLAN_CRYPTO_TX_OPS_SET_KEY(psoc)) |
| WLAN_CRYPTO_TX_OPS_SET_KEY(psoc)(vdev, req, key_type); |
| else |
| return QDF_STATUS_E_FAILURE; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| void wlan_crypto_update_set_key_peer(struct wlan_objmgr_vdev *vdev, |
| bool pairwise, uint8_t key_index, |
| struct qdf_mac_addr *peer_mac) |
| { |
| struct wlan_crypto_key *crypto_key; |
| |
| crypto_key = wlan_crypto_get_key(vdev, key_index); |
| if (!crypto_key) { |
| crypto_err("crypto_key not present for key_idx %d", key_index); |
| return; |
| } |
| |
| qdf_mem_copy(crypto_key->macaddr, peer_mac, QDF_MAC_ADDR_SIZE); |
| } |
| #endif |