2019-07-15 21:27:02 +02:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* 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 <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <ell/ell.h>
|
|
|
|
|
2019-11-07 18:29:11 +01:00
|
|
|
#include "src/anqputil.h"
|
|
|
|
#include "src/ie.h"
|
|
|
|
#include "src/util.h"
|
2019-07-15 21:27:02 +02:00
|
|
|
|
|
|
|
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
|
2020-01-21 07:21:38 +01:00
|
|
|
* we don't know how many bytes parse_eap advanced (it does
|
2019-07-15 21:27:02 +02:00
|
|
|
* internal length checking so it should not overflow). We
|
2020-01-21 07:21:38 +01:00
|
|
|
* can't incrementally advance anqp/len, hence the hardcoded
|
2019-07-15 21:27:02 +02:00
|
|
|
* 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;
|
|
|
|
}
|