3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-26 02:19:26 +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:
Andrew Zaborowski 2018-09-25 17:43:48 +02:00 committed by Denis Kenzior
parent 01edef9001
commit 36c441c945

257
src/ap.c
View File

@ -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;