nl80211: Check support for rekey offload on first use
[mech_eap.git] / src / drivers / driver_nl80211.c
index 05521fb..efcfd36 100644 (file)
@@ -281,6 +281,22 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
 }
 
 
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+       /*
+        * Clear nlmsg data, e.g., to make sure key material is not left in
+        * heap memory for unnecessarily long time.
+        */
+       if (msg) {
+               struct nlmsghdr *hdr = nlmsg_hdr(msg);
+               void *data = nlmsg_data(hdr);
+               int len = nlmsg_datalen(hdr);
+
+               os_memset(data, 0, len);
+       }
+}
+
+
 static int send_and_recv(struct nl80211_global *global,
                         struct nl_handle *nl_handle, struct nl_msg *msg,
                         int (*valid_handler)(struct nl_msg *, void *),
@@ -320,6 +336,8 @@ static int send_and_recv(struct nl80211_global *global,
        }
  out:
        nl_cb_put(cb);
+       if (!valid_handler && valid_data == (void *) -1)
+               nl80211_nlmsg_clear(msg);
        nlmsg_free(msg);
        return err;
 }
@@ -1559,6 +1577,14 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
        drv->ctx = ctx;
        drv->hostapd = !!hostapd;
        drv->eapol_sock = -1;
+
+       /*
+        * There is no driver capability flag for this, so assume it is
+        * supported and disable this on first attempt to use if the driver
+        * rejects the command due to missing support.
+        */
+       drv->set_rekey_offload = 1;
+
        drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
        drv->if_indices = drv->default_if_indices;
 
@@ -1812,11 +1838,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 
        /* WMM-AC ADDTS Response */
        if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
-               return -1;
+               ret = -1;
 
        /* WMM-AC DELTS */
        if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
-               return -1;
+               ret = -1;
 
        /* Radio Measurement - Neighbor Report Response */
        if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
@@ -2331,10 +2357,11 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
            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)) {
+               nl80211_nlmsg_clear(msg);
                nlmsg_free(msg);
                return -1;
        }
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
        if (ret) {
                wpa_printf(MSG_DEBUG,
                           "nl80211: Key management set key failed: ret=%d (%s)",
@@ -2426,7 +2453,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
        if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
                goto fail;
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
        if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
                ret = 0;
        if (ret)
@@ -2477,6 +2504,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
        return ret;
 
 fail:
+       nl80211_nlmsg_clear(msg);
        nlmsg_free(msg);
        return -ENOBUFS;
 }
@@ -3354,7 +3382,7 @@ fail:
 
 
 static int nl80211_put_freq_params(struct nl_msg *msg,
-                                  struct hostapd_freq_params *freq)
+                                  const struct hostapd_freq_params *freq)
 {
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
                return -ENOBUFS;
@@ -6710,20 +6738,31 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nlattr *replay_nested;
        struct nl_msg *msg;
+       int ret;
+
+       if (!drv->set_rekey_offload)
+               return;
 
+       wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
        if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
            !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
            nla_put(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek) ||
            nla_put(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck) ||
            nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
                    replay_ctr)) {
+               nl80211_nlmsg_clear(msg);
                nlmsg_free(msg);
                return;
        }
 
        nla_nest_end(msg, replay_nested);
 
-       send_and_recv_msgs(drv, msg, NULL, NULL);
+       ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+       if (ret == -EOPNOTSUPP) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Driver does not support rekey offload");
+               drv->set_rekey_offload = 0;
+       }
 }
 
 
@@ -6949,6 +6988,62 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
        return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
+
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+                                  const struct hostapd_freq_params *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+                  " oper_class=%u freq=%u",
+                  MAC2STR(addr), oper_class, params->freq);
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+       if (!msg ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+           nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+           (ret = nl80211_put_freq_params(msg, params))) {
+               nlmsg_free(msg);
+               wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+               return ret;
+       }
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+                  MAC2STR(addr));
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+       if (!msg ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+               nlmsg_free(msg);
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Could not build TDLS cancel chan switch");
+               return -ENOBUFS;
+       }
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
 #endif /* CONFIG TDLS */
 
 
@@ -7233,7 +7328,9 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
                                  "capa.max_stations=%u\n"
                                  "capa.probe_resp_offloads=0x%x\n"
                                  "capa.max_acl_mac_addrs=%u\n"
-                                 "capa.num_multichan_concurrent=%u\n",
+                                 "capa.num_multichan_concurrent=%u\n"
+                                 "capa.mac_addr_rand_sched_scan_supported=%d\n"
+                                 "capa.mac_addr_rand_scan_supported=%d\n",
                                  drv->capa.key_mgmt,
                                  drv->capa.enc,
                                  drv->capa.auth,
@@ -7247,7 +7344,9 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
                                  drv->capa.max_stations,
                                  drv->capa.probe_resp_offloads,
                                  drv->capa.max_acl_mac_addrs,
-                                 drv->capa.num_multichan_concurrent);
+                                 drv->capa.num_multichan_concurrent,
+                                 drv->capa.mac_addr_rand_sched_scan_supported,
+                                 drv->capa.mac_addr_rand_scan_supported);
                if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
@@ -8236,6 +8335,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef CONFIG_TDLS
        .send_tdls_mgmt = nl80211_send_tdls_mgmt,
        .tdls_oper = nl80211_tdls_oper,
+       .tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+       .tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
 #endif /* CONFIG_TDLS */
        .update_ft_ies = wpa_driver_nl80211_update_ft_ies,
        .get_mac_addr = wpa_driver_nl80211_get_macaddr,