diff --git a/src/adhoc.c b/src/adhoc.c index 19efeaee..4edf0dee 100644 --- a/src/adhoc.c +++ b/src/adhoc.c @@ -26,6 +26,8 @@ #include +#include "linux/nl80211.h" + #include "src/iwd.h" #include "src/device.h" #include "src/netdev.h" @@ -38,6 +40,7 @@ #include "src/mpdu.h" #include "src/adhoc.h" #include "src/dbus.h" +#include "src/nl80211_util.h" struct adhoc_state { struct netdev *netdev; @@ -47,8 +50,13 @@ struct adhoc_state { uint32_t sta_watch_id; uint32_t netdev_watch_id; struct l_dbus_message *pending; + uint32_t ciphers; + uint32_t group_cipher; + uint8_t gtk[CRYPTO_MAX_GTK_LEN]; + uint8_t gtk_index; bool started : 1; bool open : 1; + bool gtk_set : 1; }; struct sta_state { @@ -58,10 +66,12 @@ struct sta_state { struct handshake_state *hs_sta; struct eapol_sm *sm_a; struct handshake_state *hs_auth; + uint32_t gtk_query_cmd_id; bool authenticated : 1; }; +static struct l_genl_family *nl80211 = NULL; static uint32_t netdev_watch; static void adhoc_sta_free(void *data) @@ -71,6 +81,9 @@ static void adhoc_sta_free(void *data) if (sta->adhoc->open) goto end; + if (sta->gtk_query_cmd_id) + l_genl_family_cancel(nl80211, sta->gtk_query_cmd_id); + if (sta->sm) eapol_sm_free(sta->sm); @@ -94,6 +107,11 @@ static void adhoc_remove_sta(struct sta_state *sta) return; } + if (sta->gtk_query_cmd_id) { + l_genl_family_cancel(nl80211, sta->gtk_query_cmd_id); + sta->gtk_query_cmd_id = 0; + } + /* signal station has been removed */ if (sta->authenticated) { l_dbus_property_changed(dbus_get_bus(), @@ -125,12 +143,10 @@ static void adhoc_reset(struct adhoc_state *adhoc) static void adhoc_set_rsn_info(struct adhoc_state *adhoc, struct ie_rsn_info *rsn) { - struct wiphy *wiphy = netdev_get_wiphy(adhoc->netdev); - memset(rsn, 0, sizeof(*rsn)); rsn->akm_suites = IE_RSN_AKM_SUITE_PSK; - rsn->pairwise_ciphers = wiphy_select_cipher(wiphy, 0xffff); - rsn->group_cipher = IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC; + rsn->pairwise_ciphers = adhoc->ciphers; + rsn->group_cipher = adhoc->group_cipher; } static bool ap_sta_match_addr(const void *a, const void *b) @@ -180,7 +196,8 @@ static void adhoc_handshake_event(struct handshake_state *hs, } } -static struct eapol_sm *adhoc_new_sm(struct sta_state *sta, bool authenticator) +static struct eapol_sm *adhoc_new_sm(struct sta_state *sta, bool authenticator, + const uint8_t *gtk_rsc) { struct adhoc_state *adhoc = sta->adhoc; struct netdev *netdev = adhoc->netdev; @@ -216,6 +233,10 @@ static struct eapol_sm *adhoc_new_sm(struct sta_state *sta, bool authenticator) handshake_state_set_supplicant_address(hs, own_addr); } + if (gtk_rsc) + handshake_state_set_gtk(hs, adhoc->gtk, adhoc->gtk_index, + gtk_rsc); + sm = eapol_sm_new(hs); if (!sm) { l_error("could not create sm object"); @@ -239,9 +260,65 @@ static void adhoc_free(struct adhoc_state *adhoc) l_free(adhoc); } +static void adhoc_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc) +{ + sta->sm_a = adhoc_new_sm(sta, true, gtk_rsc); + if (!sta->sm_a) { + l_error("could not create authenticator state machine"); + goto failed; + } + + sta->sm = adhoc_new_sm(sta, false, NULL); + if (!sta->sm) { + l_error("could not create station state machine"); + goto failed; + } + + eapol_register(sta->sm); + eapol_register(sta->sm_a); + + eapol_start(sta->sm); + + return; + +failed: + adhoc_remove_sta(sta); +} + +static void adhoc_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" : "SET_KEY"; + + l_error("%s failed for the GTK: %i", + cmd_name, l_genl_msg_get_error(msg)); + } +} + +static void adhoc_gtk_query_cb(struct l_genl_msg *msg, void *user_data) +{ + struct sta_state *sta = user_data; + const void *gtk_rsc; + + sta->gtk_query_cmd_id = 0; + + gtk_rsc = nl80211_parse_get_key_seq(msg); + if (!gtk_rsc) + goto error; + + adhoc_start_rsna(sta, gtk_rsc); + return; + +error: + adhoc_remove_sta(sta); +} + static void adhoc_new_station(struct adhoc_state *adhoc, const uint8_t *mac) { struct sta_state *sta; + struct l_genl_msg *msg; sta = l_queue_find(adhoc->sta_states, ap_sta_match_addr, mac); if (sta) { @@ -249,6 +326,54 @@ static void adhoc_new_station(struct adhoc_state *adhoc, const uint8_t *mac) return; } + /* + * Follows same logic as AP. If this is the first station we create and + * set a group key. Any subsequent connections will use GET_KEY for this + * tx GTK. + */ + if (adhoc->group_cipher != IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC && + !adhoc->gtk_set && !adhoc->open) { + enum crypto_cipher group_cipher = + ie_rsn_cipher_suite_to_cipher(adhoc->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(adhoc->gtk, gtk_len); + adhoc->gtk_index = 1; + + msg = nl80211_build_new_key_group( + netdev_get_ifindex(adhoc->netdev), + group_cipher, adhoc->gtk_index, + adhoc->gtk, gtk_len, NULL, + 0, NULL); + + if (!l_genl_family_send(nl80211, msg, adhoc_gtk_op_cb, NULL, + NULL)) { + l_genl_msg_unref(msg); + l_error("Issuing NEW_KEY failed"); + return; + } + + msg = nl80211_build_set_key(netdev_get_ifindex(adhoc->netdev), + adhoc->gtk_index); + if (!l_genl_family_send(nl80211, msg, adhoc_gtk_op_cb, NULL, + NULL)) { + l_genl_msg_unref(msg); + l_error("Issuing SET_KEY failed"); + return; + } + + /* + * Set the flag now because any new associating STA will + * just use NL80211_CMD_GET_KEY from now. + */ + adhoc->gtk_set = true; + } + sta = l_new(struct sta_state, 1); memset(sta, 0, sizeof(struct sta_state)); @@ -269,27 +394,22 @@ static void adhoc_new_station(struct adhoc_state *adhoc, const uint8_t *mac) return; } - sta->sm_a = adhoc_new_sm(sta, true); - if (!sta->sm_a) { - l_error("could not create authenticator state machine"); - goto failed; + if (adhoc->group_cipher == IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC) + adhoc_start_rsna(sta, NULL); + else { + msg = nl80211_build_get_key(netdev_get_ifindex(adhoc->netdev), + adhoc->gtk_index); + sta->gtk_query_cmd_id = l_genl_family_send(nl80211, msg, + adhoc_gtk_query_cb, + sta, NULL); + if (!sta->gtk_query_cmd_id) { + l_genl_msg_unref(msg); + l_error("Issuing GET_KEY failed"); + + adhoc_remove_sta(sta); + return; + } } - - sta->sm = adhoc_new_sm(sta, false); - if (!sta->sm) { - l_error("could not create station state machine"); - goto failed; - } - - eapol_register(sta->sm); - eapol_register(sta->sm_a); - - eapol_start(sta->sm); - - return; - -failed: - adhoc_remove_sta(sta); } static void adhoc_del_station(struct adhoc_state *adhoc, const uint8_t *mac) @@ -363,11 +483,10 @@ static struct l_dbus_message *adhoc_dbus_start(struct l_dbus *dbus, adhoc->ssid = l_strdup(ssid); adhoc->pending = l_dbus_message_ref(message); adhoc->sta_states = l_queue_new(); + adhoc->ciphers = wiphy_select_cipher(wiphy, 0xffff); + adhoc->group_cipher = wiphy_select_cipher(wiphy, 0xffff); - memset(&rsn, 0, sizeof(rsn)); - rsn.akm_suites = IE_RSN_AKM_SUITE_PSK; - rsn.pairwise_ciphers = wiphy_select_cipher(wiphy, 0xffff); - rsn.group_cipher = IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC; + adhoc_set_rsn_info(adhoc, &rsn); ie_build_rsne(&rsn, ie_elems); rsn_ie.iov_base = ie_elems; @@ -556,11 +675,14 @@ static void adhoc_netdev_watch(struct netdev *netdev, } } -bool adhoc_init(void) +bool adhoc_init(struct l_genl_family *nl) { netdev_watch = netdev_watch_add(adhoc_netdev_watch, NULL, NULL); l_dbus_register_interface(dbus_get_bus(), IWD_ADHOC_INTERFACE, adhoc_setup_interface, adhoc_destroy_interface, false); + + nl80211 = nl; + return true; } diff --git a/src/adhoc.h b/src/adhoc.h index 3130276d..b7835bdf 100644 --- a/src/adhoc.h +++ b/src/adhoc.h @@ -22,5 +22,5 @@ struct device; -bool adhoc_init(void); +bool adhoc_init(struct l_genl_family *nl); void adhoc_exit(void); diff --git a/src/main.c b/src/main.c index d3333fa5..4fccfdfe 100644 --- a/src/main.c +++ b/src/main.c @@ -159,6 +159,7 @@ static void nl80211_appeared(void *user_data) l_error("Unable to init scan functionality"); ap_init(nl80211); + adhoc_init(nl80211); } static void nl80211_vanished(void *user_data) @@ -166,6 +167,7 @@ static void nl80211_vanished(void *user_data) l_debug("Lost nl80211 interface"); ap_exit(); + adhoc_exit(); scan_exit(); wiphy_exit(); } @@ -477,7 +479,6 @@ int main(int argc, char *argv[]) if (!device_init()) goto fail_device; - adhoc_init(); station_init(); wsc_init(); network_init(); @@ -494,7 +495,6 @@ int main(int argc, char *argv[]) network_exit(); wsc_exit(); station_exit(); - adhoc_exit(); device_exit(); fail_device: netdev_exit();