wifiembedded-linuxnetlinkwpa-supplicant

How to get WiFi security key(WPA, WPA2, ESS) of scanned networks using NL80211_BSS_CAPABILITY?


I'm using netlink library nl80211.h to scan wifi networks and successfully getting ssid, mac address, status, frequency and signal. I want to add security type of every network using same library. I'm using NL80211_BSS_CAPABILITY as one of the enums that gives be an integer which I'm hardcoding to my code for security. This seems to be tedious and a lot of data(integer values) has to be fed with this approach. Getting WPA/WPA2 is fine but the code fails when open networks are around. Following are the values i've used so far. A better logic that could get me open network(ESS or WEP) would be appreciated preferably analogous to wpa_supplicant and netlink Library.

int keynum;
char *keytype;
keynum = nla_get_u32(bss[NL80211_BSS_CAPABILITY]);

if(keynum==5153 || keynum == 34)
    keytype="NONE";

else if(keynum==5169 || keynum == 1297 || keynum==1073|| keynum == 5393)
    keytype="WPA2";

else if (keynum == 1041)
    keytype="WPA WPA2";

else
    keytype="WPA WPA2";

Solution

  • First, let's take a look at what kind of data is in NL80211_BSS_CAPABILITY
    To understand this, I will first draw your attention to the property NL80211_BSS_BEACON_IES
    The description of this field is as follows:

    /**
    * @NL80211_BSS_BEACON_IES: binary attribute containing the raw
    * information
    */
    

    source
    But I consider such a description too scanty, for a more detailed description, we turn here.
    To begin with, we are interested in:

    1. Capability Information (2 byte) This field contains number of subfields that are used to indicate requested or advertised optional capabilities.

    Take, for example, one of the packets captured with wireshark and its Сapabilities Information field (A good article on how to do this is here):

    Сapabilities Information: 0x0431
                .... .... .... ...1 = ESS capabilities: Transmitter is an AP
                .... .... .... ..0. = IBSS status: Transmitter belongs to a BSS
                .... ..0. .... 00.. = CFP participation capabilities: No point coordinator at AP (0x00)
                .... .... ...1 .... = Privacy: AP/STA can support WEP
                .... .... ..1. .... = Short Preamble: Allowed
                .... .... .0.. .... = PBCC: Not Allowed
                .... .... 0... .... = Channel Agility: Not in use
                .... ...0 .... .... = Spectrum Management: Not Implemented
                .... .1.. .... .... = Short Slot Time: In use
                .... 0... .... .... = Automatic Power Save Delivery: Not Implemented
                ...0 .... .... .... = Radio Measurement: Not Implemented
                ..0. .... .... .... = DSSS-OFDM: Not Allowed
                .0.. .... .... .... = Delayed Block Ack: Not Implemented
                0... .... .... .... = Immediate Block Ack: Not Implemented
    

    As we can observe, one of the flags (under the general name security, or Privacy: AP/STA can support WEP as in this case). This flag, as written, tells us that this network supports wep encryption. But, other flags of this tag do not carry information about other coding algorithms used by the network. Therefore, subsequently, this analysis of this field will turn into:

    if (bss[NL80211_BSS_CAPABILITY] != NULL) {
        wlan_info.bss_capability = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
      }
      if (!(wlan_info.bss_wpa1_wpa2 | wlan_info.bss_wpa2 | wlan_info.bss_wpa1)) {
        wlan_info.bss_wep = wlan_info.bss_capability & 16;
      }
    

    But it’s not over yet. We still need to find information about other encryption algorithms.
    To do this, we go back to NL80211_BSS_BEACON_IES.
    1.We check if it is empty.

    if (NULL != bss[NL80211_BSS_BEACON_IES] && NULL != nla_data(bss[NL80211_BSS_BEACON_IES]))
    

    2.We go through each tag, read the first two bytes, and, as you can see from the article above, we thus find out the type of the tag and the length of the data that it contains.

    for (int i = 0;;) {
          bool first = i;
          uint8_t header_type =
              *(uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) + i++);
          uint8_t header_len =
              *(uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) + i++);
          i += header_len;
    

    3.Among all tags we are looking for 2 specific ones: # 48 (0x30) or # 221 (0xDD).They might look like this:

    First:

    Tag: RSN Information
                Tag Number: RSN Information (48)
                Tag length: 20
                RSN Version: 1
                Group Cipher Suite: 00:0f:ac (Ieee 802.11) AES (CCM)
                    Group Cipher Suite OUI: 00:0f:ac (Ieee 802.11)
                    Group Cipher Suite type: AES (CCM) (4)
                Pairwise Cipher Suite Count: 1
                Pairwise Cipher Suite List 00:0f:ac (Ieee 802.11) AES (CCM)
                    Pairwise Cipher Suite: 00:0f:ac (Ieee 802.11) AES (CCM)
                        Pairwise Cipher Suite OUI: 00:0f:ac (Ieee 802.11)
                        Pairwise Cipher Suite type: AES (CCM) (4)
                Auth Key Management (AKM) Suite Count: 1
                Auth Key Management (AKM) List 00:0f:ac (Ieee 802.11) PSK
                    Auth Key Management (AKM) Suite: 00:0f:ac (Ieee 802.11) PSK
                        Auth Key Management (AKM) OUI: 00:0f:ac (Ieee 802.11)
                        Auth Key Management (AKM) type: PSK (2)
                RSN Capabilities: 0x0000
    

    in the RSN Information Tag we are interested in 2 fields (future 4 bytes): Auth Key Management (AKM) OUI: 00: 0f: ac (Ieee 802.11) and Auth Key Management (AKM) type: PSK (2).

    depending on (AKM) type: PSK (2) or (AKM) type: WPA (1) these will be either WPA2 or WPA1 respectively

    Second:

    Tag: Vendor Specific: Microsoft Corp.: WPA Information Element
                Tag Number: Vendor Specific (221)
                Tag length: 22
                OUI: 00:50:f2 (Microsoft Corp.)
                Vendor Specific OUI Type: 1
                Type: WPA Information Element (0x01)
                WPA Version: 1
                Multicast Cipher Suite: 00:50:f2 (Microsoft Corp.) AES (CCM)
                    Multicast Cipher Suite OUI: 00:50:f2 (Microsoft Corp.)
                    Multicast Cipher Suite type: AES (CCM) (4)
                Unicast Cipher Suite Count: 1
                Unicast Cipher Suite List 00:50:f2 (Microsoft Corp.) AES (CCM)
                    Unicast Cipher Suite: 00:50:f2 (Microsoft Corp.) AES (CCM)
                        Unicast Cipher Suite OUI: 00:50:f2 (Microsoft Corp.)
                        Unicast Cipher Suite type: AES (CCM) (4)
                Auth Key Management (AKM) Suite Count: 1
                Auth Key Management (AKM) List 00:50:f2 (Microsoft Corp.) PSK
                    Auth Key Management (AKM) Suite: 00:50:f2 (Microsoft Corp.) PSK
                        Auth Key Management (AKM) OUI: 00:50:f2 (Microsoft Corp.)
                        Auth Key Management (AKM) type: PSK (2)
    

    This one points us to WPA1 WPA2. Here is a snippet how can we deal with it.

    if (header_type == VENDOR_TAG_ID) {
            long long int oui = 0;
            memcpy(&oui,
                   (uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) -
                               header_len + i),
                   6);
            if (oui == WPA1_WPA2_BEACON) wlan_info.bss_wpa1_wpa2 = 1;
          } else if (header_type == RSN_TAG_ID) {
            int32_t oui = 0;
            memcpy(&oui,
                   (uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) - 6 + i), 4);
            if ((oui == WPA2_BEACON_1) || (oui == WPA2_BEACON_2))
              wlan_info.bss_wpa2 = 1;
            else if (oui == WPA1_BEACON)
              wlan_info.bss_wpa1 = 1;
          }
    

    And used here defines is:

    #define RSN_TAG_ID 0x30
    #define VENDOR_TAG_ID 0xdd
    #define BEACON_DATA_LEN 0x100
    #define BSS_SSID_LEN BEACON_DATA_LEN
    #define WPA1_BEACON 0x1AC0F00
    #define WPA2_BEACON_1 0x2AC0F00
    #define WPA2_BEACON_2 0x4AC0F00
    #define WPA1_WPA2_BEACON 0x101F25000
    

    Another interesting point is iw kernel impl
    Look here This is a ready-made function, but I haven't studied how it works.

    void print_ies(unsigned char *ie, int ielen, bool unknown,
               enum print_ie_type ptype)
    {
        struct print_ies_data ie_buffer = {
            .ie = ie,
            .ielen = ielen };
    
        if (ie == NULL || ielen < 0)
            return;
    
        while (ielen >= 2 && ielen - 2 >= ie[1]) {
            if (ie[0] < ARRAY_SIZE(ieprinters) &&
                ieprinters[ie[0]].name &&
                ieprinters[ie[0]].flags & BIT(ptype)) {
                print_ie(&ieprinters[ie[0]],
                     ie[0], ie[1], ie + 2, &ie_buffer);
            } else if (ie[0] == 221 /* vendor */) {
                print_vendor(ie[1], ie + 2, unknown, ptype);
            } else if (ie[0] == 255 /* extension */) {
                print_extension(ie[1], ie + 2, unknown, ptype);
            } else if (unknown) {
                int i;
    
                printf("\tUnknown IE (%d):", ie[0]);
                for (i=0; i<ie[1]; i++)
                    printf(" %.2x", ie[2+i]);
                printf("\n");
            }
            ielen -= ie[1] + 2;
            ie += ie[1] + 2;
        }
    }
    

    And the last one point is the attributes of the "group" "attr". Unlike the "group" attributes "bss" they apply to wiphy structures
    I haven't studied this enough to understand how the network manager extracts the necessary information about networks from it, but ou can try to look on linbl and netlink sources and there probably will be information about that. Also here is one example how to use them:
    repo
    source file
    Enum values for security keys:

    /**
     * NMDeviceWifiCapabilities:
     * @NM_WIFI_DEVICE_CAP_NONE: device has no encryption/authentication capabilities
     * @NM_WIFI_DEVICE_CAP_CIPHER_WEP40: device supports 40/64-bit WEP encryption
     * @NM_WIFI_DEVICE_CAP_CIPHER_WEP104: device supports 104/128-bit WEP encryption
     * @NM_WIFI_DEVICE_CAP_CIPHER_TKIP: device supports TKIP encryption
     * @NM_WIFI_DEVICE_CAP_CIPHER_CCMP: device supports AES/CCMP encryption
     * @NM_WIFI_DEVICE_CAP_WPA: device supports WPA1 authentication
     * @NM_WIFI_DEVICE_CAP_RSN: device supports WPA2/RSN authentication
     * @NM_WIFI_DEVICE_CAP_AP: device supports Access Point mode
     *
     * 802.11 specific device encryption and authentication capabilities.
     **/
    typedef enum {
        NM_WIFI_DEVICE_CAP_NONE          = 0x00000000,
        NM_WIFI_DEVICE_CAP_CIPHER_WEP40  = 0x00000001,
        NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 0x00000002,
        NM_WIFI_DEVICE_CAP_CIPHER_TKIP   = 0x00000004,
        NM_WIFI_DEVICE_CAP_CIPHER_CCMP   = 0x00000008,
        NM_WIFI_DEVICE_CAP_WPA           = 0x00000010,
        NM_WIFI_DEVICE_CAP_RSN           = 0x00000020,
        NM_WIFI_DEVICE_CAP_AP            = 0x00000040
    } NMDeviceWifiCapabilities;
    

    And ways to use it:
    source file

    if (tb[NL80211_ATTR_CIPHER_SUITES]) {
        int num;
        int i;
        __u32 *ciphers = nla_data (tb[NL80211_ATTR_CIPHER_SUITES]);
    
        num = nla_len (tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32);
        for (i = 0; i < num; i++) {
            switch (ciphers[i]) {
            case 0x000fac01:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40;
                break;
            case 0x000fac05:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
                break;
            case 0x000fac02:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP |
                          NM_WIFI_DEVICE_CAP_WPA;
                break;
            case 0x000fac04:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP |
                          NM_WIFI_DEVICE_CAP_RSN;
                break;
            default:
                nm_log_err (LOGD_HW | LOGD_WIFI, "Don't know the meaning of NL80211_ATTR_CIPHER_SUITES %#8.8x.", ciphers[i]);
                break;
            }
        }
    }
    

    and calls and callbacks "chain" used there:

         wifi_utils_init (const char *iface, int ifindex, gboolean check_scan)
                                      ^
                                      ||
                 wifi_nl80211_init (const char *iface, int ifindex)
                                       ^
                                      ||
        static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
    

    WPA 3 EDIT:
    I can't say anything yet. My searches started from this page and continued through the pages to which there is a link.
    nmcli Probably does not support WPA 3 since the last commit, judging by the repository, was 9 years ago (And WPA 3 was introduced by from the 802.11ax standard which came out if I'm not mistaken just two years ago).
    If you have an access point with WPA 3 encryption, this can be easily checked with the command nmcli dev wifi. Unlike nmcli, the last commit in the iw repo was relatively recent and, accordingly, it may already be handling our case.
    Also, I can not say anything about the content of packets from the wireshark with WPA 3 encryption, since I do not have the necessary access point and, accordingly, I have not tested it.