mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 10:39:23 +01:00
ap: Set up the GTK and pass to handshake
Set a default GTK cipher type same as our current PTK type, generate a random GTK when the first STA connects and set it up in the kernel, then pass the values that EAPoL is going to need to the handshake_state.
This commit is contained in:
parent
01edef9001
commit
36c441c945
257
src/ap.c
257
src/ap.c
@ -50,17 +50,21 @@ struct ap_state {
|
||||
char *ssid;
|
||||
int channel;
|
||||
unsigned int ciphers;
|
||||
enum ie_rsn_cipher_suite group_cipher;
|
||||
uint32_t beacon_interval;
|
||||
struct l_uintset *rates;
|
||||
uint8_t pmk[32];
|
||||
struct l_queue *frame_watch_ids;
|
||||
uint32_t start_stop_cmd_id;
|
||||
uint8_t gtk[CRYPTO_MAX_GTK_LEN];
|
||||
uint8_t gtk_index;
|
||||
|
||||
uint16_t last_aid;
|
||||
struct l_queue *sta_states;
|
||||
|
||||
struct l_dbus_message *pending;
|
||||
bool started : 1;
|
||||
bool gtk_set : 1;
|
||||
};
|
||||
|
||||
struct sta_state {
|
||||
@ -76,6 +80,7 @@ struct sta_state {
|
||||
uint8_t *assoc_rsne;
|
||||
struct eapol_sm *sm;
|
||||
struct handshake_state *hs;
|
||||
uint32_t gtk_query_cmd_id;
|
||||
};
|
||||
|
||||
static struct l_genl_family *nl80211 = NULL;
|
||||
@ -91,6 +96,9 @@ static void ap_sta_free(void *data)
|
||||
if (sta->assoc_resp_cmd_id)
|
||||
l_genl_family_cancel(nl80211, sta->assoc_resp_cmd_id);
|
||||
|
||||
if (sta->gtk_query_cmd_id)
|
||||
l_genl_family_cancel(nl80211, sta->gtk_query_cmd_id);
|
||||
|
||||
if (sta->sm)
|
||||
eapol_sm_free(sta->sm);
|
||||
|
||||
@ -153,6 +161,11 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
|
||||
sta->associated = false;
|
||||
sta->rsna = false;
|
||||
|
||||
if (sta->gtk_query_cmd_id) {
|
||||
l_genl_family_cancel(nl80211, sta->gtk_query_cmd_id);
|
||||
sta->gtk_query_cmd_id = 0;
|
||||
}
|
||||
|
||||
if (sta->sm)
|
||||
eapol_sm_free(sta->sm);
|
||||
|
||||
@ -252,7 +265,7 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
|
||||
memset(rsn, 0, sizeof(*rsn));
|
||||
rsn->akm_suites = IE_RSN_AKM_SUITE_PSK;
|
||||
rsn->pairwise_ciphers = ap->ciphers;
|
||||
rsn->group_cipher = IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC;
|
||||
rsn->group_cipher = ap->group_cipher;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -390,9 +403,8 @@ static void ap_handshake_event(struct handshake_state *hs,
|
||||
}
|
||||
}
|
||||
|
||||
static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
|
||||
static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
|
||||
{
|
||||
struct sta_state *sta = user_data;
|
||||
struct ap_state *ap = sta->ap;
|
||||
struct netdev *netdev = sta->ap->netdev;
|
||||
const uint8_t *own_addr = netdev_get_address(netdev);
|
||||
@ -400,12 +412,6 @@ static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
|
||||
struct ie_rsn_info rsn;
|
||||
uint8_t bss_rsne[24];
|
||||
|
||||
if (l_genl_msg_get_error(msg) < 0) {
|
||||
l_error("NEW_STATION/SET_STATION failed: %i",
|
||||
l_genl_msg_get_error(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&bss, 0, sizeof(bss));
|
||||
|
||||
ap_set_rsn_info(ap, &rsn);
|
||||
@ -427,6 +433,10 @@ static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
|
||||
handshake_state_set_authenticator_address(sta->hs, own_addr);
|
||||
handshake_state_set_supplicant_address(sta->hs, sta->addr);
|
||||
|
||||
if (gtk_rsc)
|
||||
handshake_state_set_gtk(sta->hs, ap->gtk, ap->gtk_index,
|
||||
gtk_rsc);
|
||||
|
||||
sta->sm = eapol_sm_new(sta->hs);
|
||||
if (!sta->sm) {
|
||||
handshake_state_free(sta->hs);
|
||||
@ -446,6 +456,221 @@ error:
|
||||
ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
|
||||
}
|
||||
|
||||
static void ap_gtk_query_cb(struct l_genl_msg *msg, void *user_data)
|
||||
{
|
||||
struct sta_state *sta = user_data;
|
||||
struct l_genl_attr attr, nested;
|
||||
uint16_t type, len;
|
||||
const void *data;
|
||||
|
||||
sta->gtk_query_cmd_id = 0;
|
||||
|
||||
if (l_genl_msg_get_error(msg) < 0 || !l_genl_attr_init(&attr, msg)) {
|
||||
l_error("GET_KEY failed for the GTK: %i",
|
||||
l_genl_msg_get_error(msg));
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
||||
if (type != NL80211_ATTR_KEY)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != NL80211_ATTR_KEY || !l_genl_attr_recurse(&attr, &nested)) {
|
||||
l_error("Can't recurse into ATTR_KEY in GET_KEY reply");
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (l_genl_attr_next(&nested, &type, &len, &data)) {
|
||||
if (type != NL80211_KEY_SEQ)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != NL80211_KEY_SEQ) {
|
||||
l_error("KEY_SEQ not returned in GET_KEY reply");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (len != 6) {
|
||||
l_error("KEY_SEQ length != 6 in GET_KEY reply");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ap_start_rsna(sta, data);
|
||||
return;
|
||||
|
||||
error:
|
||||
ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
|
||||
}
|
||||
|
||||
static struct l_genl_msg *ap_build_cmd_new_key(struct ap_state *ap,
|
||||
uint32_t cipher, size_t key_len)
|
||||
{
|
||||
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
msg = l_genl_msg_new_sized(NL80211_CMD_NEW_KEY, 128);
|
||||
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||
l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &ap->gtk_index);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_DATA, key_len, ap->gtk);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_CIPHER, 4, &cipher);
|
||||
l_genl_msg_leave_nested(msg);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct l_genl_msg *ap_build_cmd_set_key(struct ap_state *ap)
|
||||
{
|
||||
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
msg = l_genl_msg_new_sized(NL80211_CMD_SET_KEY, 128);
|
||||
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||
l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &ap->gtk_index);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT, 0, NULL);
|
||||
l_genl_msg_enter_nested(msg, NL80211_KEY_DEFAULT_TYPES);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST,
|
||||
0, NULL);
|
||||
l_genl_msg_leave_nested(msg);
|
||||
l_genl_msg_leave_nested(msg);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap)
|
||||
{
|
||||
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
msg = l_genl_msg_new_sized(NL80211_CMD_DEL_KEY, 128);
|
||||
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||
l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY);
|
||||
l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &ap->gtk_index);
|
||||
l_genl_msg_leave_nested(msg);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct l_genl_msg *ap_build_cmd_get_key(struct ap_state *ap)
|
||||
{
|
||||
uint32_t ifindex = netdev_get_ifindex(ap->netdev);
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
msg = l_genl_msg_new_sized(NL80211_CMD_GET_KEY, 128);
|
||||
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_IDX, 1, &ap->gtk_index);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void ap_gtk_op_cb(struct l_genl_msg *msg, void *user_data)
|
||||
{
|
||||
if (l_genl_msg_get_error(msg) < 0) {
|
||||
uint8_t cmd = l_genl_msg_get_command(msg);
|
||||
const char *cmd_name =
|
||||
cmd == NL80211_CMD_NEW_KEY ? "NEW_KEY" :
|
||||
cmd == NL80211_CMD_SET_KEY ? "SET_KEY" :
|
||||
"DEL_KEY";
|
||||
|
||||
l_error("%s failed for the GTK: %i",
|
||||
cmd_name, l_genl_msg_get_error(msg));
|
||||
}
|
||||
}
|
||||
|
||||
static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
|
||||
{
|
||||
struct sta_state *sta = user_data;
|
||||
struct ap_state *ap = sta->ap;
|
||||
|
||||
if (l_genl_msg_get_error(msg) < 0) {
|
||||
l_error("NEW_STATION/SET_STATION failed: %i",
|
||||
l_genl_msg_get_error(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the group key. If this is our first STA then we have
|
||||
* to add the new GTK to the kernel. In theory we should be
|
||||
* able to supply our own RSC (e.g. generated randomly) and use it
|
||||
* immediately for our 4-Way Handshake without querying the kernel.
|
||||
* However NL80211_CMD_NEW_KEY only lets us set the receive RSC --
|
||||
* the Rx PN for CCMP and the Rx IV for TKIP -- and the
|
||||
* transmit RSC always starts as all zeros. There's effectively
|
||||
* no way to set the Tx RSC or query the Rx RSC through nl80211.
|
||||
* So we query the Tx RSC in both scenarios just in case some
|
||||
* driver/hardware uses a different initial Tx RSC.
|
||||
*
|
||||
* Optimally we would get called back by the EAPoL state machine
|
||||
* only when building the step 3 of 4 message to query the RSC as
|
||||
* late as possible but that would complicate EAPoL.
|
||||
*/
|
||||
if (ap->group_cipher != IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC &&
|
||||
!ap->gtk_set) {
|
||||
enum crypto_cipher group_cipher =
|
||||
ie_rsn_cipher_suite_to_cipher(ap->group_cipher);
|
||||
int gtk_len = crypto_cipher_key_len(group_cipher);
|
||||
|
||||
/*
|
||||
* Generate our GTK. Not following the example derivation
|
||||
* method in 802.11-2016 section 12.7.1.4 because a simple
|
||||
* l_getrandom is just as good.
|
||||
*/
|
||||
l_getrandom(ap->gtk, gtk_len);
|
||||
ap->gtk_index = 1;
|
||||
|
||||
msg = ap_build_cmd_new_key(ap, group_cipher, gtk_len);
|
||||
if (!l_genl_family_send(nl80211, msg, ap_gtk_op_cb, NULL,
|
||||
NULL)) {
|
||||
l_genl_msg_unref(msg);
|
||||
l_error("Issuing NEW_KEY failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
msg = ap_build_cmd_set_key(ap);
|
||||
if (!l_genl_family_send(nl80211, msg, ap_gtk_op_cb, NULL,
|
||||
NULL)) {
|
||||
l_genl_msg_unref(msg);
|
||||
l_error("Issuing SET_KEY failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the flag now because any new associating STA will
|
||||
* just use NL80211_CMD_GET_KEY from now.
|
||||
*/
|
||||
ap->gtk_set = true;
|
||||
}
|
||||
|
||||
if (ap->group_cipher == IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC)
|
||||
ap_start_rsna(sta, NULL);
|
||||
else {
|
||||
msg = ap_build_cmd_get_key(ap);
|
||||
sta->gtk_query_cmd_id = l_genl_family_send(nl80211, msg,
|
||||
ap_gtk_query_cb,
|
||||
sta, NULL);
|
||||
if (!sta->gtk_query_cmd_id) {
|
||||
l_genl_msg_unref(msg);
|
||||
l_error("Issuing GET_KEY failed");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
|
||||
}
|
||||
|
||||
static void ap_associate_sta(struct ap_state *ap, struct sta_state *sta)
|
||||
{
|
||||
struct l_genl_msg *msg;
|
||||
@ -1235,6 +1460,7 @@ static int ap_start(struct ap_state *ap, const char *ssid, const char *psk,
|
||||
ap->channel = 6;
|
||||
/* TODO: Add all ciphers supported by wiphy */
|
||||
ap->ciphers = wiphy_select_cipher(wiphy, 0xffff);
|
||||
ap->group_cipher = wiphy_select_cipher(wiphy, 0xffff);
|
||||
ap->beacon_interval = 100;
|
||||
/* TODO: Use actual supported rates */
|
||||
ap->rates = l_uintset_new(200);
|
||||
@ -1356,6 +1582,19 @@ static int ap_stop(struct ap_state *ap, struct l_dbus_message *message)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (ap->gtk_set) {
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
ap->gtk_set = false;
|
||||
|
||||
msg = ap_build_cmd_del_key(ap);
|
||||
if (!l_genl_family_send(nl80211, msg, ap_gtk_op_cb, NULL,
|
||||
NULL)) {
|
||||
l_genl_msg_unref(msg);
|
||||
l_error("Issuing DEL_KEY failed");
|
||||
}
|
||||
}
|
||||
|
||||
ap->pending = l_dbus_message_ref(message);
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user