From 2c19085ccdb12ebb3c1f1a9352ddbe4599b9c14c Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 15 Jul 2019 12:27:02 -0700 Subject: [PATCH] anqp: move ANQP parsers into anqputil This allows monitor to use the ANQP parser utils without depending on netdev. --- Makefile.am | 1 + src/anqp.c | 171 ----------------------------------------- src/anqp.h | 83 -------------------- src/anqputil.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ src/anqputil.h | 103 +++++++++++++++++++++++++ src/station.c | 1 + 6 files changed, 307 insertions(+), 254 deletions(-) create mode 100644 src/anqputil.c create mode 100644 src/anqputil.h diff --git a/Makefile.am b/Makefile.am index bd2efb1d..f30e02bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -207,6 +207,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \ src/rtnlutil.h src/rtnlutil.c \ src/auth-proto.h \ src/anqp.h src/anqp.c \ + src/anqputil.h src/anqputil.c \ src/netconfig.h src/netconfig.c\ src/resolve.h src/resolve.c\ src/hotspot.h src/hotspot.c \ diff --git a/src/anqp.c b/src/anqp.c index 25fbb9bc..b3584b3e 100644 --- a/src/anqp.c +++ b/src/anqp.c @@ -59,177 +59,6 @@ static uint8_t anqp_token = 0; static uint32_t netdev_watch; static uint32_t unicast_watch; -void anqp_iter_init(struct anqp_iter *iter, const unsigned char *anqp, - unsigned int len) -{ - iter->anqp = anqp; - iter->max = len; - iter->pos = 0; -} - -bool anqp_iter_next(struct anqp_iter *iter, uint16_t *id, uint16_t *len, - const void **data) -{ - const unsigned char *anqp = iter->anqp + iter->pos; - const unsigned char *end = iter->anqp + iter->max; - - if (iter->pos + 4 >= iter->max) - return false; - - if (anqp + l_get_le16(anqp + 2) > end) - return false; - - *id = l_get_le16(anqp); - anqp += 2; - - *len = l_get_le16(anqp); - anqp += 2; - - *data = anqp; - - iter->id = *id; - iter->len = *len; - iter->data = *data; - - iter->pos = anqp + *len - iter->anqp; - - return true; -} - -bool anqp_hs20_parse_osu_provider_nai(const unsigned char *anqp, - unsigned int len, const char **nai_out) -{ - uint8_t nai_len; - static char nai[256] = { 0 }; - - if (len < 1) - return false; - - nai_len = *anqp++; - len--; - - if (len < nai_len) - return false; - - memcpy(nai, anqp, nai_len); - - *nai_out = nai; - - return true; -} - -bool anqp_iter_is_hs20(const struct anqp_iter *iter, uint8_t *stype, - unsigned int *len, const unsigned char **data) -{ - const unsigned char *anqp = iter->data; - unsigned int anqp_len = iter->len; - uint8_t type; - - if (iter->len < 6) - return false; - - if (memcmp(anqp, wifi_alliance_oui, 3)) - return false; - - anqp += 3; - anqp_len -= 3; - - type = *anqp++; - anqp_len--; - - if (type != 0x11) - return false; - - *stype = *anqp++; - anqp_len--; - - /* reserved byte */ - anqp++; - anqp_len--; - - *data = anqp; - *len = anqp_len; - - return true; -} - -char **anqp_parse_nai_realms(const unsigned char *anqp, unsigned int len) -{ - char **realms = NULL; - uint16_t count; - - if (len < 2) - return false; - - count = l_get_le16(anqp); - - anqp += 2; - len -= 2; - - l_debug(""); - - while (count--) { - uint16_t realm_len; - uint8_t encoding; - uint8_t nai_len; - char nai_realm[256] = { 0 }; - - /* - * The method list is a variable field, so the only way to - * reliably increment anqp is by realm_len at the very end since - * we dont know how many bytes parse_eap advanced (it does - * internal length checking so it should not overflow). We - * cant incrementally advance anqp/len, hence the hardcoded - * length and pointer adjustments. - */ - - if (len < 4) - goto failed; - - realm_len = l_get_le16(anqp); - anqp += 2; - len -= 2; - - encoding = anqp[0]; - - nai_len = anqp[1]; - - if (len - 2 < nai_len) - goto failed; - - memcpy(nai_realm, anqp + 2, nai_len); - - /* - * TODO: Verify NAI encoding in accordance with RFC 4282 ? - * - * The encoding in RFC 4282 seems to only limit which characters - * can be used in an NAI. Since these come in from public - * action frames it could have been spoofed, but ultimately if - * its bogus the AP won't allow us to connect. - */ - if (!util_is_bit_set(encoding, 0)) - l_warn("Not verifying NAI encoding"); - else if (!l_utf8_validate(nai_realm, nai_len, NULL)) { - l_warn("NAI is not UTF-8"); - goto failed; - } - - realms = l_strv_append(realms, nai_realm); - - if (len < realm_len) - goto failed; - - anqp += realm_len; - len -= realm_len; - } - - return realms; - -failed: - l_strv_free(realms); - return NULL; -} - static void anqp_destroy(void *user_data) { struct anqp_request *request = user_data; diff --git a/src/anqp.h b/src/anqp.h index f25dd45f..62d097d1 100644 --- a/src/anqp.h +++ b/src/anqp.h @@ -20,10 +20,6 @@ * */ -#include -#include -#include - struct scan_bss; enum anqp_result { @@ -38,85 +34,6 @@ typedef void (*anqp_response_func_t)(enum anqp_result result, const void *anqp, size_t len, void *user_data); -/* IEEE 802.11-2016 Section 9.4.5 ANQP elements */ -enum anqp_element { - /* 0-255 reserved */ - ANQP_QUERY_LIST = 256, - ANQP_CAPABILITY_LIST = 257, - ANQP_VENUE_NAME = 258, - ANQP_EMERGENCY_CALL_NUMBER = 259, - ANQP_NETWORK_AUTH_TYPE = 260, - ANQP_ROAMING_CONSORTIUM = 261, - ANQP_IP_ADDRESS_TYPE_AVAILABILITY = 262, - ANQP_NAI_REALM = 263, - ANQP_3GPP_CELLULAR_NETWORK = 264, - ANQP_AP_GEOSPATIAL_LOCATION = 265, - ANQP_AP_CIVIC_LOCATION = 266, - ANQP_AP_LOCATION_PUBLIC_ID = 267, - ANQP_DOMAIN_NAME = 268, - ANQP_EMERGENCY_ALERT_ID_URI = 269, - ANQP_TDLS_CAPABILITY = 270, - ANQP_EMERGENCY_NAI = 271, - ANQP_NEIGHBOR_REPORT = 272, - /* 273-276 reserved */ - ANQP_VENUE_URI = 277, - ANQP_ADVICE_OF_CHARGE = 278, - ANQP_LOCAL_CONTENT = 279, - ANQP_NETWORK_AUTH_TYPE_WITH_TIMESTAMP = 280, - /* 281-56796 reserved */ - ANQP_VENDOR_SPECIFIC = 56797, - /* 56798-65535 reserved */ -}; - -/* WiFi Alliance Hotspot 2.0 Spec - Section 4 Hotspot 2.0 ANQP-elements */ -enum anqp_hs20_element { - ANQP_HS20_QUERY_LIST = 1, - ANQP_HS20_CAPABILITY_LIST = 2, - ANQP_HS20_OPERATOR_FRIENDLY_NAME = 3, - ANQP_HS20_WLAN_METRICS = 4, - ANQP_HS20_CONNECTION_CAPABILITY = 5, - ANQP_HS20_NAI_HOME_REALM_QUERY = 6, - ANQP_HS20_OPERATING_CLASS_INDICATION = 7, - ANQP_HS20_OSU_PROVIDERS_LIST = 8, - /* 9 reserved */ - ANQP_HS20_ICON_REQUST = 10, - ANQP_HS20_ICON_BINARY_FILE = 11, - ANQP_HS20_OPERATOR_ICON_METADATA = 12, - ANQP_HS20_OSU_PROVIDERS_NAI_LIST = 13, - /* 14 - 255 reserved */ -}; - -/* IEEE 802.11-2016 Table 9-275 Authentication Parameter types */ -enum anqp_auth_parameter_type { - ANQP_AP_EXPANDED_EAP_METHOD = 1, - ANQP_AP_NON_INNER_AUTH_EAP = 2, - ANQP_AP_INNER_AUTH_EAP = 3, - ANQP_AP_EXPANDED_INNER_EAP_METHOD = 4, - ANQP_AP_CREDENTIAL = 5, - ANQP_AP_TUNNELED_EAP_CREDENTIAL = 6, - ANQP_AP_VENDOR_SPECIFIC = 221, -}; - -struct anqp_iter { - unsigned int max; - unsigned int pos; - const unsigned char *anqp; - - unsigned int id; - unsigned int len; - const unsigned char *data; -}; - -void anqp_iter_init(struct anqp_iter *iter, const unsigned char *anqp, - unsigned int len); -bool anqp_iter_next(struct anqp_iter *iter, uint16_t *id, uint16_t *len, - const void **data); -bool anqp_iter_is_hs20(const struct anqp_iter *iter, uint8_t *stype, - unsigned int *len, const unsigned char **data); -bool anqp_hs20_parse_osu_provider_nai(const unsigned char *anqp, - unsigned int len, const char **nai_out); -char **anqp_parse_nai_realms(const unsigned char *anqp, unsigned int len); - uint32_t anqp_request(uint32_t ifindex, const uint8_t *addr, struct scan_bss *bss, const uint8_t *anqp, size_t len, anqp_response_func_t cb, void *user_data, diff --git a/src/anqputil.c b/src/anqputil.c new file mode 100644 index 00000000..f04ac05d --- /dev/null +++ b/src/anqputil.c @@ -0,0 +1,202 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "anqputil.h" +#include "ie.h" +#include "util.h" + +void anqp_iter_init(struct anqp_iter *iter, const unsigned char *anqp, + unsigned int len) +{ + iter->anqp = anqp; + iter->max = len; + iter->pos = 0; +} + +bool anqp_iter_next(struct anqp_iter *iter, uint16_t *id, uint16_t *len, + const void **data) +{ + const unsigned char *anqp = iter->anqp + iter->pos; + const unsigned char *end = iter->anqp + iter->max; + + if (iter->pos + 4 >= iter->max) + return false; + + if (anqp + l_get_le16(anqp + 2) > end) + return false; + + *id = l_get_le16(anqp); + anqp += 2; + + *len = l_get_le16(anqp); + anqp += 2; + + *data = anqp; + + iter->id = *id; + iter->len = *len; + iter->data = *data; + + iter->pos = anqp + *len - iter->anqp; + + return true; +} + +bool anqp_hs20_parse_osu_provider_nai(const unsigned char *anqp, + unsigned int len, const char **nai_out) +{ + uint8_t nai_len; + static char nai[256] = { 0 }; + + if (len < 1) + return false; + + nai_len = *anqp++; + len--; + + if (len < nai_len) + return false; + + memcpy(nai, anqp, nai_len); + + *nai_out = nai; + + return true; +} + +bool anqp_iter_is_hs20(const struct anqp_iter *iter, uint8_t *stype, + unsigned int *len, const unsigned char **data) +{ + const unsigned char *anqp = iter->data; + unsigned int anqp_len = iter->len; + uint8_t type; + + if (iter->len < 6) + return false; + + if (memcmp(anqp, wifi_alliance_oui, 3)) + return false; + + anqp += 3; + anqp_len -= 3; + + type = *anqp++; + anqp_len--; + + if (type != 0x11) + return false; + + *stype = *anqp++; + anqp_len--; + + /* reserved byte */ + anqp++; + anqp_len--; + + *data = anqp; + *len = anqp_len; + + return true; +} + +char **anqp_parse_nai_realms(const unsigned char *anqp, unsigned int len) +{ + char **realms = NULL; + uint16_t count; + + if (len < 2) + return false; + + count = l_get_le16(anqp); + + anqp += 2; + len -= 2; + + l_debug(""); + + while (count--) { + uint16_t realm_len; + uint8_t encoding; + uint8_t nai_len; + char nai_realm[256] = { 0 }; + + /* + * The method list is a variable field, so the only way to + * reliably increment anqp is by realm_len at the very end since + * we dont know how many bytes parse_eap advanced (it does + * internal length checking so it should not overflow). We + * cant incrementally advance anqp/len, hence the hardcoded + * length and pointer adjustments. + */ + + if (len < 4) + goto failed; + + realm_len = l_get_le16(anqp); + anqp += 2; + len -= 2; + + encoding = anqp[0]; + + nai_len = anqp[1]; + + if (len - 2 < nai_len) + goto failed; + + memcpy(nai_realm, anqp + 2, nai_len); + + /* + * TODO: Verify NAI encoding in accordance with RFC 4282 ? + * + * The encoding in RFC 4282 seems to only limit which characters + * can be used in an NAI. Since these come in from public + * action frames it could have been spoofed, but ultimately if + * its bogus the AP won't allow us to connect. + */ + if (!util_is_bit_set(encoding, 0)) + l_warn("Not verifying NAI encoding"); + else if (!l_utf8_validate(nai_realm, nai_len, NULL)) { + l_warn("NAI is not UTF-8"); + goto failed; + } + + realms = l_strv_append(realms, nai_realm); + + if (len < realm_len) + goto failed; + + anqp += realm_len; + len -= realm_len; + } + + return realms; + +failed: + l_strv_free(realms); + return NULL; +} diff --git a/src/anqputil.h b/src/anqputil.h new file mode 100644 index 00000000..dba687e3 --- /dev/null +++ b/src/anqputil.h @@ -0,0 +1,103 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +/* IEEE 802.11-2016 Section 9.4.5 ANQP elements */ +enum anqp_element { + /* 0-255 reserved */ + ANQP_QUERY_LIST = 256, + ANQP_CAPABILITY_LIST = 257, + ANQP_VENUE_NAME = 258, + ANQP_EMERGENCY_CALL_NUMBER = 259, + ANQP_NETWORK_AUTH_TYPE = 260, + ANQP_ROAMING_CONSORTIUM = 261, + ANQP_IP_ADDRESS_TYPE_AVAILABILITY = 262, + ANQP_NAI_REALM = 263, + ANQP_3GPP_CELLULAR_NETWORK = 264, + ANQP_AP_GEOSPATIAL_LOCATION = 265, + ANQP_AP_CIVIC_LOCATION = 266, + ANQP_AP_LOCATION_PUBLIC_ID = 267, + ANQP_DOMAIN_NAME = 268, + ANQP_EMERGENCY_ALERT_ID_URI = 269, + ANQP_TDLS_CAPABILITY = 270, + ANQP_EMERGENCY_NAI = 271, + ANQP_NEIGHBOR_REPORT = 272, + /* 273-276 reserved */ + ANQP_VENUE_URI = 277, + ANQP_ADVICE_OF_CHARGE = 278, + ANQP_LOCAL_CONTENT = 279, + ANQP_NETWORK_AUTH_TYPE_WITH_TIMESTAMP = 280, + /* 281-56796 reserved */ + ANQP_VENDOR_SPECIFIC = 56797, + /* 56798-65535 reserved */ +}; + +/* WiFi Alliance Hotspot 2.0 Spec - Section 4 Hotspot 2.0 ANQP-elements */ +enum anqp_hs20_element { + ANQP_HS20_QUERY_LIST = 1, + ANQP_HS20_CAPABILITY_LIST = 2, + ANQP_HS20_OPERATOR_FRIENDLY_NAME = 3, + ANQP_HS20_WLAN_METRICS = 4, + ANQP_HS20_CONNECTION_CAPABILITY = 5, + ANQP_HS20_NAI_HOME_REALM_QUERY = 6, + ANQP_HS20_OPERATING_CLASS_INDICATION = 7, + ANQP_HS20_OSU_PROVIDERS_LIST = 8, + /* 9 reserved */ + ANQP_HS20_ICON_REQUST = 10, + ANQP_HS20_ICON_BINARY_FILE = 11, + ANQP_HS20_OPERATOR_ICON_METADATA = 12, + ANQP_HS20_OSU_PROVIDERS_NAI_LIST = 13, + /* 14 - 255 reserved */ +}; + +/* IEEE 802.11-2016 Table 9-275 Authentication Parameter types */ +enum anqp_auth_parameter_type { + ANQP_AP_EXPANDED_EAP_METHOD = 1, + ANQP_AP_NON_INNER_AUTH_EAP = 2, + ANQP_AP_INNER_AUTH_EAP = 3, + ANQP_AP_EXPANDED_INNER_EAP_METHOD = 4, + ANQP_AP_CREDENTIAL = 5, + ANQP_AP_TUNNELED_EAP_CREDENTIAL = 6, + ANQP_AP_VENDOR_SPECIFIC = 221, +}; + +struct anqp_iter { + unsigned int max; + unsigned int pos; + const unsigned char *anqp; + + unsigned int id; + unsigned int len; + const unsigned char *data; +}; + +void anqp_iter_init(struct anqp_iter *iter, const unsigned char *anqp, + unsigned int len); +bool anqp_iter_next(struct anqp_iter *iter, uint16_t *id, uint16_t *len, + const void **data); +bool anqp_iter_is_hs20(const struct anqp_iter *iter, uint8_t *stype, + unsigned int *len, const unsigned char **data); +bool anqp_hs20_parse_osu_provider_nai(const unsigned char *anqp, + unsigned int len, const char **nai_out); +char **anqp_parse_nai_realms(const unsigned char *anqp, unsigned int len); diff --git a/src/station.c b/src/station.c index 4cd3a064..712e9501 100644 --- a/src/station.c +++ b/src/station.c @@ -52,6 +52,7 @@ #include "src/erp.h" #include "src/netconfig.h" #include "src/anqp.h" +#include "src/anqputil.h" #include "src/hotspot.h" static struct l_queue *station_list;