This implements the new handshake callback for setting a TK with an extended key ID. The procedure is different from legacy zero index TKs. First the new TK is set as RX only. Then message 4 should be sent out (so it uses the existing TK). This poses a slight issue with PAE sockets since message order is not guaranteed. In this case the 4th message is stored and sent after the new TK is installed. Then the new TK is modified using SET_KEY to both send and receive. In the case of control port over NL80211 the above can be avoided and we can simply install the new key, send message 4, and modify the TK as TX + RX all in sequence, without waiting for any callbacks. --- src/netdev.c | 401 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 297 insertions(+), 104 deletions(-) diff --git a/src/netdev.c b/src/netdev.c index de155da1..0bfc4c10 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -87,6 +87,7 @@ struct netdev_handshake_state { uint32_t group_management_new_key_cmd_id; uint32_t set_station_cmd_id; uint32_t set_pmk_cmd_id; + uint32_t pairwise_set_key_tx_cmd_id; bool ptk_installed; bool gtk_installed; bool igtk_installed; @@ -102,6 +103,12 @@ struct netdev_ft_over_ds_info { bool parsed : 1; }; +struct netdev_ext_key_info { + uint16_t proto; + bool noencrypt; + struct eapol_frame frame[0]; +}; + struct netdev { uint32_t index; uint64_t wdev_id; @@ -170,6 +177,8 @@ struct netdev { struct l_queue *ft_ds_list; + struct netdev_ext_key_info *ext_key_info; + bool connected : 1; bool associated : 1; bool operational : 1; @@ -336,6 +345,11 @@ static void netdev_handshake_state_cancel_all( l_genl_family_cancel(nl80211, nhs->set_pmk_cmd_id); nhs->set_pmk_cmd_id = 0; } + + if (nhs->pairwise_set_key_tx_cmd_id) { + l_genl_family_cancel(nl80211, nhs->pairwise_set_key_tx_cmd_id); + nhs->pairwise_set_key_tx_cmd_id = 0; + } } static void netdev_handshake_state_free(struct handshake_state *hs) @@ -766,6 +780,11 @@ static void netdev_connect_free(struct netdev *netdev) netdev->handshake = NULL; } + if (netdev->ext_key_info) { + l_free(netdev->ext_key_info); + netdev->ext_key_info = NULL; + } + if (netdev->neighbor_report_cb) { netdev->neighbor_report_cb(netdev, -ENOTCONN, NULL, 0, netdev->user_data); @@ -1693,6 +1712,24 @@ static void netdev_set_igtk(struct handshake_state *hs, uint16_t key_index, netdev_setting_keys_failed(nhs, -EIO); } +static struct l_genl_msg *netdev_build_cmd_set_key_tx(struct netdev *netdev) +{ + uint8_t key_mode = NL80211_KEY_SET_TX; + struct l_genl_msg *msg = l_genl_msg_new_sized(NL80211_CMD_SET_KEY, 512); + + l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, + netdev->handshake->aa); + l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); + + l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY); + l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, + &netdev->handshake->active_tk_index); + l_genl_msg_append_attr(msg, NL80211_KEY_MODE, 1, &key_mode); + l_genl_msg_leave_nested(msg); + + return msg; +} + static void netdev_new_pairwise_key_cb(struct l_genl_msg *msg, void *data) { struct netdev_handshake_state *nhs = data; @@ -1730,14 +1767,171 @@ error: netdev_setting_keys_failed(nhs, err); } +static struct l_genl_msg *netdev_build_control_port_frame(struct netdev *netdev, + const uint8_t *to, + uint16_t proto, + bool unencrypted, + const void *body, + size_t body_len) +{ + struct l_genl_msg *msg; + + msg = l_genl_msg_new_sized(NL80211_CMD_CONTROL_PORT_FRAME, + 128 + body_len); + + l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); + l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME, body_len, body); + l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, 2, + &proto); + l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, to); + + if (unencrypted) + l_genl_msg_append_attr(msg, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, 0, NULL); + + return msg; +} + +static void netdev_control_port_frame_cb(struct l_genl_msg *msg, + void *user_data) +{ + int err = l_genl_msg_get_error(msg); + const char *ext_error; + + if (err >= 0) + return; + + ext_error = l_genl_msg_get_extended_error(msg); + l_error("CMD_CONTROL_PORT failed: %s", + ext_error ? ext_error : strerror(-err)); +} + +static int netdev_control_port_write_pae(struct netdev *netdev, + const uint8_t *dest, + uint16_t proto, + const struct eapol_frame *ef, + bool noencrypt) +{ + int fd = l_io_get_fd(netdev->pae_io); + struct sockaddr_ll sll; + size_t frame_size = sizeof(struct eapol_header) + + L_BE16_TO_CPU(ef->header.packet_len); + ssize_t r; + + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = netdev->index; + sll.sll_protocol = htons(proto); + sll.sll_halen = ETH_ALEN; + memcpy(sll.sll_addr, dest, ETH_ALEN); + + r = sendto(fd, ef, frame_size, 0, + (struct sockaddr *) &sll, sizeof(sll)); + if (r < 0) + l_error("EAPoL write socket: %s", strerror(errno)); + + return r; +} + +static int netdev_control_port_frame(uint32_t ifindex, + const uint8_t *dest, uint16_t proto, + const struct eapol_frame *ef, + bool noencrypt, + void *user_data) +{ + struct l_genl_msg *msg; + struct netdev *netdev; + size_t frame_size; + + netdev = netdev_find(ifindex); + if (!netdev) + return -ENOENT; + + frame_size = sizeof(struct eapol_header) + + L_BE16_TO_CPU(ef->header.packet_len); + + if (!netdev->pae_over_nl80211) + return netdev_control_port_write_pae(netdev, dest, proto, + ef, noencrypt); + + msg = netdev_build_control_port_frame(netdev, dest, proto, noencrypt, + ef, frame_size); + if (!msg) + return -ENOMEM; + + if (!l_genl_family_send(nl80211, msg, netdev_control_port_frame_cb, + netdev, NULL)) { + l_genl_msg_unref(msg); + return -EINVAL; + } + + return 0; +} + +static int netdev_set_key_tx(struct netdev *netdev) +{ + struct netdev_handshake_state *nhs = l_container_of(netdev->handshake, + struct netdev_handshake_state, super); + struct l_genl_msg *msg = netdev_build_cmd_set_key_tx(netdev); + + nhs->pairwise_set_key_tx_cmd_id = l_genl_family_send(nl80211, msg, + netdev_new_pairwise_key_cb, + nhs, NULL); + if (nhs->pairwise_set_key_tx_cmd_id > 0) + return 0; + + l_genl_msg_unref(msg); + + return -EIO; +} + +static void netdev_new_rx_pairwise_key_cb(struct l_genl_msg *msg, void *data) +{ + struct netdev_handshake_state *nhs = data; + struct netdev *netdev = nhs->netdev; + struct netdev_ext_key_info *info = netdev->ext_key_info; + int err = l_genl_msg_get_error(msg); + + nhs->pairwise_new_key_cmd_id = 0; + + if (err < 0) { + const char *ext_error = l_genl_msg_get_extended_error(msg); + + l_error("New Key for RX Pairwise Key failed for ifindex: %d:%s", + netdev->index, + ext_error ? ext_error : strerror(-err)); + goto error; + } + + if (!info) + return; + + err = netdev_control_port_write_pae(netdev, nhs->super.aa, info->proto, + info->frame, info->noencrypt); + l_free(netdev->ext_key_info); + netdev->ext_key_info = NULL; + + if (err < 0) + goto error; + + err = netdev_set_key_tx(netdev); + if (err < 0) + goto error; + + return; + +error: + netdev_setting_keys_failed(nhs, err); +} + static struct l_genl_msg *netdev_build_cmd_new_key_pairwise( struct netdev *netdev, uint32_t cipher, const uint8_t *aa, const uint8_t *tk, - size_t tk_len) + size_t tk_len, + uint8_t key_id) { - uint8_t key_id = 0; struct l_genl_msg *msg; msg = l_genl_msg_new_sized(NL80211_CMD_NEW_KEY, 512); @@ -1751,6 +1945,36 @@ static struct l_genl_msg *netdev_build_cmd_new_key_pairwise( return msg; } +static struct l_genl_msg *netdev_build_cmd_new_rx_key_pairwise( + struct netdev *netdev, + uint32_t cipher, + const uint8_t *aa, + const uint8_t *tk, + size_t tk_len, + uint8_t key_id) +{ + uint8_t key_mode = NL80211_KEY_NO_TX; + uint32_t key_type = NL80211_KEYTYPE_PAIRWISE; + struct l_genl_msg *msg; + + msg = l_genl_msg_new_sized(NL80211_CMD_NEW_KEY, 512); + + l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, aa); + l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); + + l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY); + + l_genl_msg_append_attr(msg, NL80211_KEY_DATA, tk_len, tk); + l_genl_msg_append_attr(msg, NL80211_KEY_CIPHER, 4, &cipher); + l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &key_id); + l_genl_msg_append_attr(msg, NL80211_KEY_MODE, 1, &key_mode); + l_genl_msg_append_attr(msg, NL80211_KEY_TYPE, 4, &key_type); + + l_genl_msg_leave_nested(msg); + + return msg; +} + static void netdev_group_timeout_cb(struct l_timeout *timeout, void *user_data) { struct netdev_handshake_state *nhs = user_data; @@ -1823,7 +2047,8 @@ static void netdev_set_tk(struct handshake_state *hs, goto invalid_key; msg = netdev_build_cmd_new_key_pairwise(netdev, cipher, addr, tk_buf, - crypto_cipher_key_len(cipher)); + crypto_cipher_key_len(cipher), + hs->active_tk_index); nhs->pairwise_new_key_cmd_id = l_genl_family_send(nl80211, msg, netdev_new_pairwise_key_cb, nhs, NULL); @@ -1836,6 +2061,74 @@ invalid_key: netdev_setting_keys_failed(nhs, err); } +static void netdev_set_ext_tk(struct handshake_state *hs, uint8_t key_idx, + const uint8_t *tk, uint32_t cipher, + const struct eapol_frame *step4, uint16_t proto, + bool noencrypt) +{ + struct netdev_handshake_state *nhs = + l_container_of(hs, struct netdev_handshake_state, super); + uint8_t tk_buf[32]; + struct netdev *netdev = nhs->netdev; + struct l_genl_msg *msg; + const uint8_t *addr = netdev_choose_key_address(nhs); + int err; + size_t frame_size = sizeof(struct eapol_header) + + L_BE16_TO_CPU(step4->header.packet_len); + + err = -ENOENT; + if (!netdev_copy_tk(tk_buf, tk, cipher, false)) + goto error; + + msg = netdev_build_cmd_new_rx_key_pairwise(netdev, cipher, addr, tk_buf, + crypto_cipher_key_len(cipher), + hs->active_tk_index); + nhs->pairwise_new_key_cmd_id = + l_genl_family_send(nl80211, msg, netdev_new_rx_pairwise_key_cb, + nhs, NULL); + + if (!nhs->pairwise_new_key_cmd_id) + goto io_error; + + /* + * Without control port we cannot guarantee the order that messages go + * out and must wait for NEW_KEY to call back before sending message 4 + */ + if (!wiphy_control_port_enabled(netdev->wiphy)) { + netdev->ext_key_info = l_malloc( + sizeof(struct netdev_ext_key_info) + + frame_size); + memcpy(netdev->ext_key_info->frame, step4, frame_size); + netdev->ext_key_info->proto = proto; + netdev->ext_key_info->noencrypt = noencrypt; + return; + } + + /* + * Otherwise, order of messages will be guaranteed. Therefore we can + * send send message 4, and set the TK to TX (below) without waiting for + * any callbacks + */ + err = netdev_control_port_frame(netdev->index, netdev->handshake->aa, + proto, step4, noencrypt, NULL); + if (err < 0) + goto error; + + /* Then toggle to RX + TX */ + err = netdev_set_key_tx(netdev); + if (err < 0) + goto error; + + return; + +io_error: + err = -EIO; + l_genl_msg_unref(msg); + +error: + netdev_setting_keys_failed(nhs, err); +} + static void netdev_set_pmk_cb(struct l_genl_msg *msg, void *user_data) { struct netdev_handshake_state *nhs = user_data; @@ -5122,107 +5415,6 @@ static void netdev_control_port_frame_event(struct l_genl_msg *msg, frame, frame_len, unencrypted); } -static struct l_genl_msg *netdev_build_control_port_frame(struct netdev *netdev, - const uint8_t *to, - uint16_t proto, - bool unencrypted, - const void *body, - size_t body_len) -{ - struct l_genl_msg *msg; - - msg = l_genl_msg_new_sized(NL80211_CMD_CONTROL_PORT_FRAME, - 128 + body_len); - - l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); - l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME, body_len, body); - l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, 2, - &proto); - l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, to); - - if (unencrypted) - l_genl_msg_append_attr(msg, - NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, 0, NULL); - - return msg; -} - -static void netdev_control_port_frame_cb(struct l_genl_msg *msg, - void *user_data) -{ - int err = l_genl_msg_get_error(msg); - const char *ext_error; - - if (err >= 0) - return; - - ext_error = l_genl_msg_get_extended_error(msg); - l_error("CMD_CONTROL_PORT failed: %s", - ext_error ? ext_error : strerror(-err)); -} - -static int netdev_control_port_write_pae(struct netdev *netdev, - const uint8_t *dest, - uint16_t proto, - const struct eapol_frame *ef, - bool noencrypt) -{ - int fd = l_io_get_fd(netdev->pae_io); - struct sockaddr_ll sll; - size_t frame_size = sizeof(struct eapol_header) + - L_BE16_TO_CPU(ef->header.packet_len); - ssize_t r; - - memset(&sll, 0, sizeof(sll)); - sll.sll_family = AF_PACKET; - sll.sll_ifindex = netdev->index; - sll.sll_protocol = htons(proto); - sll.sll_halen = ETH_ALEN; - memcpy(sll.sll_addr, dest, ETH_ALEN); - - r = sendto(fd, ef, frame_size, 0, - (struct sockaddr *) &sll, sizeof(sll)); - if (r < 0) - l_error("EAPoL write socket: %s", strerror(errno)); - - return r; -} - -static int netdev_control_port_frame(uint32_t ifindex, - const uint8_t *dest, uint16_t proto, - const struct eapol_frame *ef, - bool noencrypt, - void *user_data) -{ - struct l_genl_msg *msg; - struct netdev *netdev; - size_t frame_size; - - netdev = netdev_find(ifindex); - if (!netdev) - return -ENOENT; - - frame_size = sizeof(struct eapol_header) + - L_BE16_TO_CPU(ef->header.packet_len); - - if (!netdev->pae_over_nl80211) - return netdev_control_port_write_pae(netdev, dest, proto, - ef, noencrypt); - - msg = netdev_build_control_port_frame(netdev, dest, proto, noencrypt, - ef, frame_size); - if (!msg) - return -ENOMEM; - - if (!l_genl_family_send(nl80211, msg, netdev_control_port_frame_cb, - netdev, NULL)) { - l_genl_msg_unref(msg); - return -EINVAL; - } - - return 0; -} - static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data) { struct netdev *netdev = NULL; @@ -6243,6 +6435,7 @@ static int netdev_init(void) __handshake_set_install_tk_func(netdev_set_tk); __handshake_set_install_gtk_func(netdev_set_gtk); __handshake_set_install_igtk_func(netdev_set_igtk); + __handshake_set_install_ext_tk_func(netdev_set_ext_tk); __eapol_set_rekey_offload_func(netdev_set_rekey_offload); __eapol_set_tx_packet_func(netdev_control_port_frame); -- 2.31.1