* @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
* NAN Request/Response and NAN Indication messages. These messages are
* interpreted between the framework and the firmware component.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ * used to configure PMK to the driver even when not connected. This can
+ * be used to request offloading of key management operations.
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
+ * NL80211_CMD_ROAM event with optional attributes including information
+ * from offloaded key management operation. Uses
+ * enum qca_wlan_vendor_attr_roam_auth attributes.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
/* 14..49 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
};
QCA_ROAMING_ALLOWED_WITHIN_ESS,
};
+enum qca_wlan_vendor_attr_roam_auth {
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+};
+
#endif /* QCA_VENDOR_H */
const struct ieee80211_vht_capabilities *vhtcaps;
const struct ieee80211_vht_capabilities *vhtcaps_mask;
#endif /* CONFIG_VHT_OVERRIDES */
+
+ /**
+ * req_key_mgmt_offload - Request key management offload for connection
+ *
+ * Request key management offload for this connection if the device
+ * supports it.
+ */
+ int req_key_mgmt_offload;
};
enum hide_ssid {
* addr - Station address (for AP mode)
*/
const u8 *addr;
+
+ /**
+ * The following is the key management offload information
+ * @authorized
+ * @key_replay_ctr
+ * @key_replay_ctr_len
+ * @ptk_kck
+ * @ptk_kek_len
+ * @ptk_kek
+ * @ptk_kek_len
+ */
+
+ /**
+ * authorized - Status of key management offload,
+ * 1 = successful
+ */
+ int authorized;
+
+ /**
+ * key_replay_ctr - Key replay counter value last used
+ * in a valid EAPOL-Key frame
+ */
+ const u8 *key_replay_ctr;
+
+ /**
+ * key_replay_ctr_len - The length of key_replay_ctr
+ */
+ size_t key_replay_ctr_len;
+
+ /**
+ * ptk_kck - The derived PTK KCK
+ */
+ const u8 *ptk_kck;
+
+ /**
+ * ptk_kek_len - The length of ptk_kck
+ */
+ size_t ptk_kck_len;
+
+ /**
+ * ptk_kek - The derived PTK KEK
+ */
+ const u8 *ptk_kek;
+
+ /**
+ * ptk_kek_len - The length of ptk_kek
+ */
+ size_t ptk_kek_len;
} assoc_info;
/**
unsigned int have_low_prio_scan:1;
unsigned int force_connect_cmd:1;
unsigned int addr_changed:1;
+ unsigned int key_mgmt_set_key_vendor_cmd_avail:1;
+ unsigned int roam_auth_vendor_event_avail:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
enum nl80211_commands cmd, struct nlattr *status,
struct nlattr *addr, struct nlattr *req_ie,
- struct nlattr *resp_ie)
+ struct nlattr *resp_ie,
+ struct nlattr *authorized,
+ struct nlattr *key_replay_ctr,
+ struct nlattr *ptk_kck,
+ struct nlattr *ptk_kek)
{
union wpa_event_data event;
event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+ if (authorized && nla_get_u8(authorized)) {
+ event.assoc_info.authorized = 1;
+ wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+ }
+ if (key_replay_ctr) {
+ event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+ event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+ }
+ if (ptk_kck) {
+ event.assoc_info.ptk_kck = nla_data(ptk_kck);
+ event.assoc_info.ptk_kck_len = nla_len(ptk_kek);
+ }
+ if (ptk_kek) {
+ event.assoc_info.ptk_kek = nla_data(ptk_kek);
+ event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+ }
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
}
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+ const u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+ u8 *bssid;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Key management roam+auth vendor event received");
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+ (struct nlattr *) data, len, NULL))
+ return;
+ if (!tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+ nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+ return;
+
+ bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+ wpa_printf(MSG_DEBUG, " * roam BSSID " MACSTR, MAC2STR(bssid));
+
+ mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+}
+
+
static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
u32 subcmd, u8 *data, size_t len)
{
case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
qca_nl80211_avoid_freq(drv, data, len);
break;
+ case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+ qca_nl80211_key_mgmt_auth(drv, data, len);
+ break;
default:
wpa_printf(MSG_DEBUG,
"nl80211: Ignore unsupported QCA vendor event %u",
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname);
+ if (cmd == NL80211_CMD_ROAM && drv->roam_auth_vendor_event_avail) {
+ /*
+ * Device will use roam+auth vendor event to indicate
+ * roaming, so ignore the regular roam event.
+ */
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+ cmd);
+ return;
+ }
+
if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
(cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
cmd == NL80211_CMD_SCAN_ABORTED)) {
tb[NL80211_ATTR_STATUS_CODE],
tb[NL80211_ATTR_MAC],
tb[NL80211_ATTR_REQ_IE],
- tb[NL80211_ATTR_RESP_IE]);
+ tb[NL80211_ATTR_RESP_IE],
+ NULL, NULL, NULL, NULL);
break;
case NL80211_CMD_CH_SWITCH_NOTIFY:
mlme_event_ch_switch(drv,
case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
drv->dfs_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
+ drv->key_mgmt_set_key_vendor_cmd_avail = 1;
+ break;
}
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
continue;
}
vinfo = nla_data(nl);
+ if (vinfo->subcmd ==
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH)
+ drv->roam_auth_vendor_event_avail = 1;
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
vinfo->vendor_id, vinfo->subcmd);
}
}
+static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+ const u8 *key, size_t key_len)
+{
+ struct nl_msg *msg;
+ int ret = 0;
+
+ if (!drv->key_mgmt_set_key_vendor_cmd_avail)
+ return 0;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return -1;
+
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+ NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
+ NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY);
+ NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, key_len, key);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Key management set key failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
enum wpa_alg alg, const u8 *addr,
int key_idx, int set_tx,
}
#endif /* CONFIG_TDLS */
+ if (alg == WPA_ALG_PMK && drv->key_mgmt_set_key_vendor_cmd_avail) {
+ wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+ __func__);
+ ret = issue_key_mgmt_set_key(drv, key, key_len);
+ return ret;
+ }
+
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
int ret;
int algs;
+ if (params->req_key_mgmt_offload && params->psk &&
+ (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+ ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+ if (ret)
+ return ret;
+ }
+
msg = nlmsg_alloc();
if (!msg)
return -1;
}
+static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
+{
+#ifdef CONFIG_IEEE80211R
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+ if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cannot set low order 256 bits of MSK for key management offload");
+ } else {
+#endif /* CONFIG_IEEE80211R */
+ if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cannot set PMK for key management offload");
+#ifdef CONFIG_IEEE80211R
+ }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
const unsigned char *src_addr,
const u8 *pmkid)
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
"machines", sm->pmk, pmk_len);
sm->pmk_len = pmk_len;
+ wpa_supplicant_key_mgmt_set_pmk(sm);
if (sm->proto == WPA_PROTO_RSN &&
!wpa_key_mgmt_ft(sm->key_mgmt)) {
sa = pmksa_cache_add(sm->pmksa,
}
#endif /* CONFIG_P2P */
+
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
+{
+ if (rx_replay_counter == NULL)
+ return;
+
+ os_memcpy(sm->rx_replay_counter, rx_replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 1;
+ wpa_printf(MSG_DEBUG, "Updated key replay counter");
+}
+
+
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+ const u8 *ptk_kek)
+{
+ if (ptk_kck) {
+ os_memcpy(sm->ptk.kck, ptk_kck, 16);
+ wpa_printf(MSG_DEBUG, "Updated PTK KCK");
+ }
+ if (ptk_kek) {
+ os_memcpy(sm->ptk.kek, ptk_kek, 16);
+ wpa_printf(MSG_DEBUG, "Updated PTK KEK");
+ }
+ sm->ptk_set = 1;
+}
#endif /* CONFIG_TDLS */
void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
const u8 *replay_ctr);
+ int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
};
int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+ const u8 *ptk_kek);
+
#else /* CONFIG_NO_WPA */
static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
{
}
+static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
+ const u8 *rx_replay_counter)
+{
+}
+
+static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+ const u8 *ptk_kek)
+{
+}
+
#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_PEERKEY
}
#endif /* CONFIG_TDLS */
+static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
+ const u8 *pmk, size_t pmk_len)
+{
+ if (!sm->proactive_key_caching)
+ return 0;
+ if (!sm->ctx->key_mgmt_set_pmk)
+ return -1;
+ return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
+}
+
void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
config->wmm_ac_params[3] = ac_vo;
config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+ config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
{ INT(mac_addr), 0 },
{ INT(rand_addr_lifetime), 0 },
{ INT(preassoc_mac_addr), 0 },
+ { INT(key_mgmt_offload), 0},
};
#undef FUNC
#define DEFAULT_SCAN_CUR_FREQ 0
#define DEFAULT_P2P_SEARCH_DELAY 500
#define DEFAULT_RAND_ADDR_LIFETIME 60
+#define DEFAULT_KEY_MGMT_OFFLOAD 1
#include "config_ssid.h"
#include "wps/wps.h"
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
int preassoc_mac_addr;
+
+ /**
+ * key_mgmt_offload - Use key management offload
+ *
+ * Key management offload should be used if the device supports it.
+ * Key management offload is the capability of a device operating as
+ * a station to do the exchange necessary to establish temporal keys
+ * during initial RSN connection, after roaming, or during a PTK
+ * rekeying operation.
+ */
+ int key_mgmt_offload;
};
if (config->preassoc_mac_addr)
fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
+
+ if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
+ fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
}
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Connection authorized by device, previous state %d",
+ wpa_s->wpa_state);
+ if (wpa_s->wpa_state == WPA_ASSOCIATED) {
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ }
+ wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
+ wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
+ data->assoc_info.ptk_kek);
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
break;
case EVENT_ASSOC:
wpa_supplicant_event_assoc(wpa_s, data);
+ if (data && data->assoc_info.authorized)
+ wpa_supplicant_event_assoc_auth(wpa_s, data);
break;
case EVENT_DISASSOC:
wpas_event_disassoc(wpa_s,
params.psk = ssid->psk;
}
+ if (wpa_s->conf->key_mgmt_offload) {
+ if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256)
+ params.req_key_mgmt_offload =
+ ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc : ssid->proactive_key_caching;
+ else
+ params.req_key_mgmt_offload = 1;
+
+ if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+ params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+ ssid->psk_set)
+ params.psk = ssid->psk;
+ }
+
params.drop_unencrypted = use_crypt;
#ifdef CONFIG_IEEE80211W
#endif /* CONFIG_NO_WPA */
+static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
+ size_t pmk_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->conf->key_mgmt_offload)
+ return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
+ NULL, 0, pmk, pmk_len);
+ else
+ return 0;
+}
+
+
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
#endif /* CONFIG_TDLS */
ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
+ ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {