/* * * 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 #include "src/anqp.h" #include "src/util.h" #include "src/eap-private.h" static const uint8_t wifi_alliance_oui[3] = { 0x50, 0x6f, 0x9a }; 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; }