diff --git a/src/device.c b/src/device.c index cc2c9ab6..9d431318 100644 --- a/src/device.c +++ b/src/device.c @@ -28,7 +28,12 @@ #include +#include "src/iwd.h" #include "src/common.h" +#include "src/ie.h" +#include "src/eapol.h" +#include "src/wiphy.h" +#include "src/scan.h" #include "src/netdev.h" #include "src/dbus.h" #include "src/network.h" @@ -217,11 +222,65 @@ void device_connect_network(struct device *device, struct network *network, struct scan_bss *bss, struct l_dbus_message *message) { + enum security security = network_get_security(network); + struct wiphy *wiphy = device->wiphy; struct l_dbus *dbus = dbus_get_bus(); + struct eapol_sm *sm = NULL; + + if (security == SECURITY_PSK || security == SECURITY_8021X) { + uint16_t pairwise_ciphers, group_ciphers; + uint8_t rsne_buf[256]; + struct ie_rsn_info info; + + sm = eapol_sm_new(); + + eapol_sm_set_authenticator_address(sm, bss->addr); + eapol_sm_set_supplicant_address(sm, + netdev_get_address(device->netdev)); + + memset(&info, 0, sizeof(info)); + + if (security == SECURITY_PSK) + info.akm_suites = + bss->sha256 ? IE_RSN_AKM_SUITE_PSK_SHA256 : + IE_RSN_AKM_SUITE_PSK; + else + info.akm_suites = + bss->sha256 ? IE_RSN_AKM_SUITE_8021X_SHA256 : + IE_RSN_AKM_SUITE_8021X; + + bss_get_supported_ciphers(bss, + &pairwise_ciphers, &group_ciphers); + + info.pairwise_ciphers = wiphy_select_cipher(wiphy, + pairwise_ciphers); + info.group_cipher = wiphy_select_cipher(wiphy, group_ciphers); + + /* RSN takes priority */ + if (bss->rsne) { + ie_build_rsne(&info, rsne_buf); + eapol_sm_set_ap_rsn(sm, bss->rsne, bss->rsne[1] + 2); + eapol_sm_set_own_rsn(sm, rsne_buf, rsne_buf[1] + 2); + } else { + ie_build_wpa(&info, rsne_buf); + eapol_sm_set_ap_wpa(sm, bss->wpa, bss->wpa[1] + 2); + eapol_sm_set_own_wpa(sm, rsne_buf, rsne_buf[1] + 2); + } + + if (security == SECURITY_PSK) + eapol_sm_set_pmk(sm, network_get_psk(network)); + else + eapol_sm_set_8021x_config(sm, + network_get_settings(network)); + + eapol_sm_set_user_data(sm, device); + eapol_sm_set_tx_user_data(sm, + L_INT_TO_PTR(l_io_get_fd(device->eapol_io))); + } device->connect_pending = l_dbus_message_ref(message); - if (netdev_connect(device->netdev, bss, NULL, + if (netdev_connect(device->netdev, bss, sm, device_netdev_event, device_connect_cb, device) < 0) { dbus_pending_reply(&device->connect_pending, diff --git a/src/netdev.c b/src/netdev.c index b77b20c1..81b773db 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -34,7 +34,12 @@ #include #include "linux/nl80211.h" + +#include "src/iwd.h" #include "src/wiphy.h" +#include "src/ie.h" +#include "src/eapol.h" +#include "src/crypto.h" #include "src/device.h" #include "src/scan.h" #include "src/netdev.h" @@ -48,6 +53,8 @@ struct netdev { netdev_event_func_t event_filter; netdev_connect_cb_t connect_cb; void *user_data; + struct l_genl_msg *associate_msg; + struct eapol_sm *sm; }; static struct l_netlink *rtnl = NULL; @@ -151,6 +158,17 @@ static void netdev_free(void *data) struct netdev *netdev = data; l_debug("Freeing netdev %s[%d]", netdev->name, netdev->index); + + if (netdev->sm) { + eapol_sm_free(netdev->sm); + netdev->sm = NULL; + } + + if (netdev->associate_msg) { + l_genl_msg_unref(netdev->associate_msg); + netdev->associate_msg = NULL; + } + l_free(netdev); } @@ -167,6 +185,126 @@ struct netdev *netdev_find(int ifindex) return l_queue_find(netdev_list, netdev_match, L_UINT_TO_PTR(ifindex)); } +static void netdev_cmd_associate_cb(struct l_genl_msg *msg, void *user_data) +{ + struct netdev *netdev = user_data; + + /* Wait for associate event */ + if (l_genl_msg_get_error(msg) >= 0) { + if (netdev->event_filter) + netdev->event_filter(netdev, + NETDEV_EVENT_ASSOCIATING, + netdev->user_data); + + if (netdev->sm) { + eapol_start(netdev->index, netdev->sm); + netdev->sm = NULL; + } + + return; + } + + if (netdev->connect_cb) + netdev->connect_cb(netdev, NETDEV_RESULT_ASSOCIATION_FAILED, + netdev->user_data); +} + +static struct l_genl_msg *netdev_build_cmd_associate(struct netdev *netdev, + struct scan_bss *bss, + struct eapol_sm *sm) +{ + struct l_genl_msg *msg; + + msg = l_genl_msg_new_sized(NL80211_CMD_ASSOCIATE, 512); + l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); + l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, + 4, &bss->frequency); + l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, bss->addr); + l_genl_msg_append_attr(msg, NL80211_ATTR_SSID, + bss->ssid_len, bss->ssid); + + if (sm) { + uint32_t cipher; + uint32_t nl_cipher; + size_t ie_len; + const uint8_t *ie; + + cipher = eapol_sm_get_pairwise_cipher(sm); + if (cipher == IE_RSN_CIPHER_SUITE_CCMP) + nl_cipher = CRYPTO_CIPHER_CCMP; + else + nl_cipher = CRYPTO_CIPHER_TKIP; + + l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + 4, &nl_cipher); + + cipher = eapol_sm_get_group_cipher(sm); + if (cipher == IE_RSN_CIPHER_SUITE_CCMP) + nl_cipher = CRYPTO_CIPHER_CCMP; + else + nl_cipher = CRYPTO_CIPHER_TKIP; + + l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + 4, &nl_cipher); + + l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL); + + ie = eapol_sm_get_own_ie(sm, &ie_len); + if (ie) + l_genl_msg_append_attr(msg, NL80211_ATTR_IE, + ie_len, ie); + } + + return msg; +} + +static void netdev_authenticate_event(struct l_genl_msg *msg, + struct netdev *netdev) +{ + struct l_genl_attr attr; + uint16_t type, len; + const void *data; + int err; + + l_debug(""); + + err = l_genl_msg_get_error(msg); + if (err < 0) { + l_error("authentication failed %s (%d)", strerror(-err), err); + goto error; + } + + if (!l_genl_attr_init(&attr, msg)) { + l_debug("attr init failed"); + goto error; + } + + while (l_genl_attr_next(&attr, &type, &len, &data)) { + switch (type) { + case NL80211_ATTR_TIMED_OUT: + l_warn("authentication timed out"); + goto error; + } + } + + if (!netdev->associate_msg) + return; + + if (l_genl_family_send(nl80211, netdev->associate_msg, + netdev_cmd_associate_cb, netdev, NULL) > 0) { + netdev->associate_msg = NULL; + return; + } + + l_genl_msg_unref(netdev->associate_msg); + netdev->associate_msg = NULL; + +error: + if (netdev->connect_cb) + netdev->connect_cb(netdev, NETDEV_RESULT_AUTHENTICATION_FAILED, + netdev->user_data); +} + static void netdev_cmd_authenticate_cb(struct l_genl_msg *msg, void *user_data) { struct netdev *netdev = user_data; @@ -209,18 +347,30 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss, netdev_connect_cb_t cb, void *user_data) { struct l_genl_msg *authenticate; + struct l_genl_msg *associate; authenticate = netdev_build_cmd_authenticate(netdev, bss); if (!authenticate) return -EINVAL; + associate = netdev_build_cmd_associate(netdev, bss, sm); + if (!associate) { + l_genl_msg_unref(authenticate); + return -EINVAL; + } + if (!l_genl_family_send(nl80211, authenticate, - netdev_cmd_authenticate_cb, netdev, NULL)) + netdev_cmd_authenticate_cb, netdev, NULL)) { + l_genl_msg_unref(associate); + l_genl_msg_unref(authenticate); return -EIO; + } netdev->event_filter = event_filter; netdev->connect_cb = cb; netdev->user_data = user_data; + netdev->sm = sm; + netdev->associate_msg = associate; return 0; } @@ -231,6 +381,46 @@ int netdev_disconnect(struct netdev *netdev, return 0; } +static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data) +{ + struct netdev *netdev = NULL; + struct l_genl_attr attr; + uint16_t type, len; + const void *data; + uint8_t cmd; + + cmd = l_genl_msg_get_command(msg); + + l_debug("MLME notification %u", cmd); + + if (!l_genl_attr_init(&attr, msg)) + return; + + while (l_genl_attr_next(&attr, &type, &len, &data)) { + switch (type) { + case NL80211_ATTR_IFINDEX: + if (len != sizeof(uint32_t)) { + l_warn("Invalid interface index attribute"); + return; + } + + netdev = netdev_find(*((uint32_t *) data)); + break; + } + } + + if (!netdev) { + l_warn("MLME notification is missing ifindex attribute"); + return; + } + + switch (cmd) { + case NL80211_CMD_AUTHENTICATE: + netdev_authenticate_event(msg, netdev); + break; + } +} + static void netdev_get_interface_callback(struct l_genl_msg *msg, void *user_data) { @@ -411,6 +601,10 @@ bool netdev_init(struct l_genl_family *in) NULL, NULL)) l_error("Getting all interface information failed"); + if (!l_genl_family_register(nl80211, "mlme", netdev_mlme_notify, + NULL, NULL)) + l_error("Registering for MLME notification failed"); + return true; } diff --git a/src/wiphy.c b/src/wiphy.c index 1262cf6d..b098216d 100644 --- a/src/wiphy.c +++ b/src/wiphy.c @@ -836,146 +836,6 @@ static void mlme_associate_event(struct l_genl_msg *msg, struct device *device) operstate_cb, device); } -static void genl_associate_cb(struct l_genl_msg *msg, void *user_data) -{ - struct device *device = user_data; - - if (l_genl_msg_get_error(msg) < 0 && device->connect_pending) - dbus_pending_reply(&device->connect_pending, - dbus_error_failed(device->connect_pending)); -} - -static void mlme_associate_cmd(struct device *device) -{ - struct l_genl_msg *msg; - struct scan_bss *bss = device->connected_bss; - struct network *network = device->connected_network; - struct wiphy *wiphy = device->wiphy; - const char *ssid = network_get_ssid(network); - enum security security = network_get_security(network); - - l_debug(""); - - msg = l_genl_msg_new_sized(NL80211_CMD_ASSOCIATE, 512); - msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &device->index); - msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &bss->frequency); - msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, bss->addr); - msg_append_attr(msg, NL80211_ATTR_SSID, strlen(ssid), ssid); - - if (security == SECURITY_PSK || security == SECURITY_8021X) { - uint16_t pairwise_ciphers, group_ciphers; - uint32_t pairwise_cipher_attr; - uint32_t group_cipher_attr; - uint8_t rsne_buf[256]; - struct ie_rsn_info info; - struct eapol_sm *sm = eapol_sm_new(); - - memset(&info, 0, sizeof(info)); - - if (security == SECURITY_PSK) - info.akm_suites = - bss->sha256 ? IE_RSN_AKM_SUITE_PSK_SHA256 : - IE_RSN_AKM_SUITE_PSK; - else - info.akm_suites = - bss->sha256 ? IE_RSN_AKM_SUITE_8021X_SHA256 : - IE_RSN_AKM_SUITE_8021X; - - bss_get_supported_ciphers(bss, &pairwise_ciphers, - &group_ciphers); - - info.pairwise_ciphers = wiphy_select_cipher(wiphy, - pairwise_ciphers); - if (info.pairwise_ciphers == IE_RSN_CIPHER_SUITE_CCMP) - pairwise_cipher_attr = CRYPTO_CIPHER_CCMP; - else - pairwise_cipher_attr = CRYPTO_CIPHER_TKIP; - - info.group_cipher = wiphy_select_cipher(wiphy, group_ciphers); - if (info.group_cipher == IE_RSN_CIPHER_SUITE_CCMP) - group_cipher_attr = CRYPTO_CIPHER_CCMP; - else - group_cipher_attr = CRYPTO_CIPHER_TKIP; - - /* RSN takes priority */ - if (bss->rsne) { - ie_build_rsne(&info, rsne_buf); - eapol_sm_set_ap_rsn(sm, bss->rsne, bss->rsne[1] + 2); - eapol_sm_set_own_rsn(sm, rsne_buf, rsne_buf[1] + 2); - } else { - ie_build_wpa(&info, rsne_buf); - eapol_sm_set_ap_wpa(sm, bss->wpa, bss->wpa[1] + 2); - eapol_sm_set_own_wpa(sm, rsne_buf, rsne_buf[1] + 2); - } - - if (security == SECURITY_PSK) - eapol_sm_set_pmk(sm, network_get_psk(network)); - else - eapol_sm_set_8021x_config(sm, - network_get_settings(network)); - - eapol_sm_set_authenticator_address(sm, bss->addr); - eapol_sm_set_supplicant_address(sm, - netdev_get_address(device->netdev)); - eapol_sm_set_user_data(sm, device); - eapol_sm_set_tx_user_data(sm, - L_INT_TO_PTR(l_io_get_fd(device->eapol_io))); - eapol_start(device->index, sm); - - msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, - 4, &pairwise_cipher_attr); - msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - 4, &group_cipher_attr); - - msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL); - msg_append_attr(msg, NL80211_ATTR_IE, - rsne_buf[1] + 2, rsne_buf); - } - - l_genl_family_send(nl80211, msg, genl_associate_cb, device, NULL); -} - -static void mlme_authenticate_event(struct l_genl_msg *msg, - struct device *device) -{ - struct l_genl_attr attr; - uint16_t type, len; - const void *data; - int err; - - l_debug(""); - - err = l_genl_msg_get_error(msg); - if (err < 0) { - l_error("authentication failed %s (%d)", strerror(-err), err); - goto error; - } - - if (!l_genl_attr_init(&attr, msg)) { - l_debug("attr init failed"); - goto error; - } - - while (l_genl_attr_next(&attr, &type, &len, &data)) { - switch (type) { - case NL80211_ATTR_TIMED_OUT: - l_warn("authentication timed out"); - goto error; - } - } - - l_info("Authentication completed"); - mlme_associate_cmd(device); - return; - -error: - if (device->connect_pending) - dbus_pending_reply(&device->connect_pending, - dbus_error_failed(device->connect_pending)); - - device_disassociated(device); -} - static void mlme_deauthenticate_event(struct l_genl_msg *msg, struct device *device) { @@ -1670,9 +1530,6 @@ static void wiphy_mlme_notify(struct l_genl_msg *msg, void *user_data) switch (cmd) { - case NL80211_CMD_AUTHENTICATE: - mlme_authenticate_event(msg, device); - break; case NL80211_CMD_ASSOCIATE: mlme_associate_event(msg, device); break;