Author: axeld Date: 2010-12-09 23:33:34 +0100 (Thu, 09 Dec 2010) New Revision: 39792 Changeset: http://dev.haiku-os.org/changeset/39792 Modified: haiku/trunk/headers/os/net/NetworkDevice.h haiku/trunk/src/kits/network/libnetapi/NetworkDevice.cpp haiku/trunk/src/tests/kits/net/wlan_test.cpp Log: * Parse the additional information elements the station sends in order to retrieve the WLAN cipher/key configuration. Might not work perfectly yet; so far I've only seen WPA2, and WPA networks. * Have wlan_test show this extra info. Modified: haiku/trunk/headers/os/net/NetworkDevice.h =================================================================== --- haiku/trunk/headers/os/net/NetworkDevice.h 2010-12-09 16:47:49 UTC (rev 39791) +++ haiku/trunk/headers/os/net/NetworkDevice.h 2010-12-09 22:33:34 UTC (rev 39792) @@ -22,6 +22,7 @@ uint32 flags; uint32 authentication_mode; uint32 cipher; + uint32 group_cipher; uint32 key_mode; }; @@ -41,15 +42,18 @@ B_NETWORK_CIPHER_NONE = 0x01, B_NETWORK_CIPHER_WEP_40 = 0x02, B_NETWORK_CIPHER_WEP_104 = 0x04, - B_NETWORK_CIPHER_WEP_TKIP = 0x08, - B_NETWORK_CIPHER_WEP_CCMP = 0x10, - B_NETWORK_CIPHER_WEP_AES_128_CMAC = 0x20 + B_NETWORK_CIPHER_TKIP = 0x08, + B_NETWORK_CIPHER_CCMP = 0x10, + B_NETWORK_CIPHER_AES_128_CMAC = 0x20 }; // key modes enum { B_KEY_MODE_IEEE802_1X = 0x0001, B_KEY_MODE_PSK = 0x0002, + B_KEY_MODE_NONE = 0x0004, + B_KEY_MODE_FT_IEEE802_1X = 0x0020, + B_KEY_MODE_FT_PSK = 0x0040, B_KEY_MODE_IEEE802_1X_SHA256 = 0x0080, B_KEY_MODE_PSK_SHA256 = 0x0100, B_KEY_MODE_WPS = 0x0200 Modified: haiku/trunk/src/kits/network/libnetapi/NetworkDevice.cpp =================================================================== --- haiku/trunk/src/kits/network/libnetapi/NetworkDevice.cpp 2010-12-09 16:47:49 UTC (rev 39791) +++ haiku/trunk/src/kits/network/libnetapi/NetworkDevice.cpp 2010-12-09 22:33:34 UTC (rev 39792) @@ -22,6 +22,14 @@ } +//#define TRACE_DEVICE +#ifdef TRACE_DEVICE +# define TRACE(x, ...) printf(x, __VA_ARGS__); +#else +# define TRACE(x, ...) ; +#endif + + namespace { @@ -111,27 +119,217 @@ } +//! Read a 16 bit little endian value +static uint16 +read_le16(uint8*& data, int32& length) +{ + uint16 value = B_LENDIAN_TO_HOST_INT16(*(uint16*)data); + data += 2; + length -= 2; + return value; +} + + +//! Read a 32 bit little endian value +static uint32 +read_le32(uint8*& data, int32& length) +{ + uint32 value = B_LENDIAN_TO_HOST_INT32(*(uint32*)data); + data += 4; + length -= 4; + return value; +} + + +static uint32 +from_rsn_cipher(uint32 cipher) +{ + if ((cipher & 0xffffff) != RSN_OUI) + return B_NETWORK_CIPHER_CCMP; + + switch (cipher >> 24) { + case RSN_CSE_NULL: + return B_NETWORK_CIPHER_NONE; + case RSN_CSE_WEP40: + return B_NETWORK_CIPHER_WEP_40; + case RSN_CSE_WEP104: + return B_NETWORK_CIPHER_WEP_104; + case RSN_CSE_TKIP: + return B_NETWORK_CIPHER_TKIP; + default: + case RSN_CSE_CCMP: + return B_NETWORK_CIPHER_CCMP; + case RSN_CSE_WRAP: + return B_NETWORK_CIPHER_AES_128_CMAC; + } +} + + +static uint32 +from_rsn_key_mode(uint32 mode) +{ + if ((mode & 0xffffff) != RSN_OUI) + return B_KEY_MODE_IEEE802_1X; + + switch (mode >> 24) { + default: + case RSN_ASE_8021X_UNSPEC: + return B_KEY_MODE_IEEE802_1X; + case RSN_ASE_8021X_PSK: + return B_KEY_MODE_PSK; + // the following are currently not defined in net80211 + case 3: + return B_KEY_MODE_FT_IEEE802_1X; + case 4: + return B_KEY_MODE_FT_PSK; + case 5: + return B_KEY_MODE_IEEE802_1X_SHA256; + case 6: + return B_KEY_MODE_PSK_SHA256; + } +} + + +//! Parse RSN/WPA information elements common data +static void +parse_ie_rsn_wpa(wireless_network& network, uint8*& data, int32& length) +{ + if (length >= 4) { + // parse group cipher + network.group_cipher = from_rsn_cipher(read_le32(data, length)); + } else if (length > 0) + return; + + if (length >= 2) { + // parse unicast cipher + uint16 count = read_le16(data, length); + network.cipher = 0; + + for (uint16 i = 0; i < count; i++) { + if (length < 4) + return; + network.cipher |= from_rsn_cipher(read_le32(data, length)); + } + } else if (length > 0) + return; + + if (length >= 2) { + // parse key management mode + uint16 count = read_le16(data, length); + network.key_mode = 0; + + for (uint16 i = 0; i < count; i++) { + if (length < 4) + return; + network.key_mode |= from_rsn_key_mode(read_le32(data, length)); + } + } else if (length > 0) + return; + + // TODO: capabilities, and PMKID following in case of RSN +} + + +//! Parse RSN (Robust Security Network) information element. +static void +parse_ie_rsn(wireless_network& network, ie_data* ie) +{ + network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2; + network.cipher = B_NETWORK_CIPHER_CCMP; + network.group_cipher = B_NETWORK_CIPHER_CCMP; + network.key_mode = B_KEY_MODE_IEEE802_1X; + + int32 length = ie->length; + if (length < 2) + return; + + uint8* data = ie->data; + + uint16 version = read_le16(data, length); + if (version != RSN_VERSION) + return; + + parse_ie_rsn_wpa(network, data, length); +} + + +//! Parse WPA information element. +static bool +parse_ie_wpa(wireless_network& network, ie_data* ie) +{ + int32 length = ie->length; + if (length < 6) + return false; + + uint8* data = ie->data; + + uint32 oui = read_le32(data, length); + TRACE(" oui: %" B_PRIx32 "\n", oui); + if (oui != ((WPA_OUI_TYPE << 24) | WPA_OUI)) + return false; + + uint16 version = read_le16(data, length); + if (version != WPA_VERSION) + return false; + + network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA; + network.cipher = B_NETWORK_CIPHER_TKIP; + network.group_cipher = B_NETWORK_CIPHER_TKIP; + network.key_mode = B_KEY_MODE_IEEE802_1X; + + parse_ie_rsn_wpa(network, data, length); + return true; +} + + //! Parse information elements. static void parse_ie(wireless_network& network, uint8* _ie, int32 ieLength) { struct ie_data* ie = (ie_data*)_ie; + bool hadRSN = false; + bool hadWPA = false; while (ieLength > 1) { + TRACE("ie type %u\n", ie->type); switch (ie->type) { case IEEE80211_ELEMID_SSID: strlcpy(network.name, (char*)ie->data, min_c(ie->length + 1, (int)sizeof(network.name))); break; case IEEE80211_ELEMID_RSN: - // TODO: we might need to parse those in order to find out - // the authentication mode (WPA info in vendor?) + parse_ie_rsn(network, ie); + hadRSN = true; break; + case IEEE80211_ELEMID_VENDOR: + if (!hadRSN && parse_ie_wpa(network, ie)) + hadWPA = true; + break; } ieLength -= 2 + ie->length; ie = (ie_data*)((uint8*)ie + 2 + ie->length); } + + if (hadRSN || hadWPA) { + // Determine authentication mode + + if ((network.key_mode & (B_KEY_MODE_IEEE802_1X_SHA256 + | B_KEY_MODE_PSK_SHA256)) != 0) { + network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2; + } else if ((network.key_mode & (B_KEY_MODE_IEEE802_1X + | B_KEY_MODE_PSK | B_KEY_MODE_FT_IEEE802_1X + | B_KEY_MODE_FT_PSK)) != 0) { + if (!hadRSN) + network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA; + } else if ((network.key_mode & B_KEY_MODE_NONE) != 0) { + if ((network.cipher & (B_NETWORK_CIPHER_WEP_40 + | B_NETWORK_CIPHER_WEP_104)) != 0) + network.authentication_mode = B_NETWORK_AUTHENTICATION_WEP; + else + network.authentication_mode = B_NETWORK_AUTHENTICATION_NONE; + } + } } @@ -188,8 +386,11 @@ network.noise_level = info.isi_noise; network.flags |= (info.isi_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0 ? B_NETWORK_IS_ENCRYPTED : 0; + network.authentication_mode = 0; - // TODO: build from IE if possible + network.cipher = 0; + network.group_cipher = 0; + network.key_mode = 0; parse_ie(network, info); } @@ -206,8 +407,11 @@ network.noise_level = result.isr_noise; network.flags = (result.isr_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0 ? B_NETWORK_IS_ENCRYPTED : 0; + network.authentication_mode = 0; - // TODO: build from IE if possible + network.cipher = 0; + network.group_cipher = 0; + network.key_mode = 0; parse_ie(network, result); } Modified: haiku/trunk/src/tests/kits/net/wlan_test.cpp =================================================================== --- haiku/trunk/src/tests/kits/net/wlan_test.cpp 2010-12-09 16:47:49 UTC (rev 39791) +++ haiku/trunk/src/tests/kits/net/wlan_test.cpp 2010-12-09 22:33:34 UTC (rev 39792) @@ -21,11 +21,30 @@ } +const char* +get_authentication_mode(uint32 mode) +{ + switch (mode) { + default: + case B_NETWORK_AUTHENTICATION_NONE: + return "-"; + case B_NETWORK_AUTHENTICATION_WEP: + return "WEP"; + case B_NETWORK_AUTHENTICATION_WPA: + return "WPA"; + case B_NETWORK_AUTHENTICATION_WPA2: + return "WPA2"; + } +} + + void show(const wireless_network& network) { - printf("%-32s %s %3g dB%s\n", network.name, - network.address.ToString().String(), network.signal_strength / 2.0, + printf("%-32s %s %4g dB %s C%02" B_PRIx32 " K%02" B_PRIx32 + " %s\n", network.name, network.address.ToString().String(), + network.signal_strength / 2.0, get_authentication_mode( + network.authentication_mode), network.cipher, network.key_mode, (network.flags & B_NETWORK_IS_ENCRYPTED) != 0 ? " (encrypted)" : ""); }