mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-12-22 13:02:44 +01:00
dpp-util: add URI parsing
Parses K (key), M (mac), C (class/channels), and V (version) tokens into a new structure dpp_uri_info. H/I are not parsed since there currently isn't any use for them.
This commit is contained in:
parent
c819903a7c
commit
f7f602e1b1
250
src/dpp-util.c
250
src/dpp-util.c
@ -866,3 +866,253 @@ struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len)
|
||||
return l_ecc_point_from_data(curve, key_data[1],
|
||||
key_data + 2, elen - 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Advances 'p' to the next character 'sep' plus one. strchr can be trusted to
|
||||
* find the next character, but we do need to check that the next character + 1
|
||||
* isn't the NULL terminator, i.e. that data actually exists past this point.
|
||||
*/
|
||||
#define TOKEN_NEXT(p, sep) \
|
||||
({ \
|
||||
const char *_next = strchr((p), (sep)); \
|
||||
if (_next) { \
|
||||
if (*(_next + 1) == '\0') \
|
||||
_next = NULL; \
|
||||
else \
|
||||
_next++; \
|
||||
} \
|
||||
_next; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Finds the length of the current token (characters until next 'sep'). If no
|
||||
* 'sep' is found zero is returned.
|
||||
*/
|
||||
#define TOKEN_LEN(p, sep) \
|
||||
({ \
|
||||
const char *_next = strchr((p), (sep)); \
|
||||
if (!_next) \
|
||||
_next = (p); \
|
||||
(_next - (p)); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Ensures 'p' points to something resembling a single character followed by
|
||||
* ':' followed by at least one non-null byte of data. This allows the parse
|
||||
* loop to safely advance the pointer to each tokens data (pos + 2)
|
||||
*/
|
||||
#define TOKEN_OK(p) \
|
||||
((p) && (p)[0] != '\0' && (p)[1] == ':' && (p)[2] != '\0') \
|
||||
|
||||
static struct scan_freq_set *dpp_parse_class_and_channel(const char *token,
|
||||
unsigned int len)
|
||||
{
|
||||
const char *pos = token;
|
||||
char *end;
|
||||
struct scan_freq_set *freqs = scan_freq_set_new();
|
||||
|
||||
/* Checking for <operclass>/<channel>,<operclass>/<channel>,... */
|
||||
for (; pos && pos < token + len; pos = TOKEN_NEXT(pos, ',')) {
|
||||
unsigned long r;
|
||||
uint8_t channel;
|
||||
uint8_t oper_class;
|
||||
uint32_t freq;
|
||||
|
||||
/* strtoul accepts minus and plus signs before value */
|
||||
if (*pos == '-' || *pos == '+')
|
||||
goto free_set;
|
||||
|
||||
/* to check uint8_t overflow */
|
||||
errno = 0;
|
||||
r = oper_class = strtoul(pos, &end, 10);
|
||||
|
||||
if (errno == ERANGE || errno == EINVAL)
|
||||
goto free_set;
|
||||
/*
|
||||
* Did strtoul not advance pointer, not reach the next
|
||||
* token, or overflow?
|
||||
*/
|
||||
if (end == pos || *end != '/' || r != oper_class)
|
||||
goto free_set;
|
||||
|
||||
pos = end + 1;
|
||||
|
||||
if (*pos == '-' || *pos == '+')
|
||||
goto free_set;
|
||||
|
||||
errno = 0;
|
||||
r = channel = strtoul(pos, &end, 10);
|
||||
|
||||
if (errno == ERANGE || errno == EINVAL)
|
||||
goto free_set;
|
||||
/*
|
||||
* Same verification as above, but also checks either for
|
||||
* another pair (,) or end of this token (;)
|
||||
*/
|
||||
if (end == pos || (*end != ',' && *end != ';') || r != channel)
|
||||
goto free_set;
|
||||
|
||||
freq = oci_to_frequency(oper_class, channel);
|
||||
if (!freq)
|
||||
goto free_set;
|
||||
|
||||
scan_freq_set_add(freqs, freq);
|
||||
}
|
||||
|
||||
if (token + len != end)
|
||||
goto free_set;
|
||||
|
||||
if (scan_freq_set_isempty(freqs)) {
|
||||
free_set:
|
||||
scan_freq_set_free(freqs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return freqs;
|
||||
}
|
||||
|
||||
static int dpp_parse_mac(const char *str, unsigned int len, uint8_t *mac_out)
|
||||
{
|
||||
uint8_t mac[6];
|
||||
unsigned int i;
|
||||
|
||||
if (len != 12)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < 12; i += 2) {
|
||||
if (!l_ascii_isxdigit(str[i]))
|
||||
return -EINVAL;
|
||||
|
||||
if (!l_ascii_isxdigit(str[i + 1]))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sscanf(str, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
|
||||
&mac[0], &mac[1], &mac[2],
|
||||
&mac[3], &mac[4], &mac[5]) != 6)
|
||||
return -EINVAL;
|
||||
|
||||
if (!util_is_valid_sta_address(mac))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(mac_out, mac, 6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dpp_parse_version(const char *str, unsigned int len,
|
||||
uint8_t *version_out)
|
||||
{
|
||||
if (len != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (str[0] != '1' && str[0] != '2')
|
||||
return -EINVAL;
|
||||
|
||||
*version_out = str[0] - '0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct l_ecc_point *dpp_parse_key(const char *str, unsigned int len)
|
||||
{
|
||||
_auto_(l_free) uint8_t *decoded = NULL;
|
||||
size_t decoded_len;
|
||||
|
||||
decoded = l_base64_decode(str, len, &decoded_len);
|
||||
if (!decoded)
|
||||
return NULL;
|
||||
|
||||
return dpp_point_from_asn1(decoded, decoded_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a bootstrapping URI. This parses the tokens defined in the Easy Connect
|
||||
* spec, and verifies they are the correct syntax. Some values have extra
|
||||
* verification:
|
||||
* - The bootstrapping key is base64 decoded and converted to an l_ecc_point
|
||||
* - The operating class and channels are checked against the OCI table.
|
||||
* - The version is checked to be either 1 or 2, as defined by the spec.
|
||||
* - The MAC is verified to be a valid station address.
|
||||
*/
|
||||
struct dpp_uri_info *dpp_parse_uri(const char *uri)
|
||||
{
|
||||
struct dpp_uri_info *info;
|
||||
const char *pos = uri;
|
||||
const char *end = uri + strlen(uri) - 1;
|
||||
int ret = 0;
|
||||
|
||||
if (!l_str_has_prefix(pos, "DPP:"))
|
||||
return NULL;
|
||||
|
||||
info = l_new(struct dpp_uri_info, 1);
|
||||
|
||||
pos += 4;
|
||||
|
||||
/* EasyConnect 5.2.1 - Bootstrapping information format */
|
||||
for (; TOKEN_OK(pos); pos = TOKEN_NEXT(pos, ';')) {
|
||||
unsigned int len = TOKEN_LEN(pos + 2, ';');
|
||||
|
||||
if (!len)
|
||||
goto free_info;
|
||||
|
||||
switch (*pos) {
|
||||
case 'C':
|
||||
info->freqs = dpp_parse_class_and_channel(pos + 2, len);
|
||||
if (!info->freqs)
|
||||
goto free_info;
|
||||
break;
|
||||
case 'M':
|
||||
ret = dpp_parse_mac(pos + 2, len, info->mac);
|
||||
if (ret < 0)
|
||||
goto free_info;
|
||||
break;
|
||||
case 'V':
|
||||
ret = dpp_parse_version(pos + 2, len, &info->version);
|
||||
if (ret < 0)
|
||||
goto free_info;
|
||||
break;
|
||||
case 'K':
|
||||
info->boot_public = dpp_parse_key(pos + 2, len);
|
||||
if (!info->boot_public)
|
||||
goto free_info;
|
||||
break;
|
||||
case 'H':
|
||||
case 'I':
|
||||
break;
|
||||
default:
|
||||
goto free_info;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra data found after last token */
|
||||
if (pos != end)
|
||||
goto free_info;
|
||||
|
||||
/* The public bootstrapping key is the only required token */
|
||||
if (!info->boot_public)
|
||||
goto free_info;
|
||||
|
||||
return info;
|
||||
|
||||
free_info:
|
||||
dpp_free_uri_info(info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dpp_free_uri_info(struct dpp_uri_info *info)
|
||||
{
|
||||
if (info->freqs)
|
||||
scan_freq_set_free(info->freqs);
|
||||
|
||||
if (info->boot_public)
|
||||
l_ecc_point_free(info->boot_public);
|
||||
|
||||
if (info->information)
|
||||
l_free(info->information);
|
||||
|
||||
if (info->host)
|
||||
l_free(info->host);
|
||||
|
||||
l_free(info);
|
||||
}
|
||||
|
@ -22,6 +22,16 @@
|
||||
struct l_ecc_point;
|
||||
struct l_ecc_scalar;
|
||||
enum ie_rsn_akm_suite;
|
||||
struct scan_freq_set;
|
||||
|
||||
struct dpp_uri_info {
|
||||
struct scan_freq_set *freqs;
|
||||
struct l_ecc_point *boot_public;
|
||||
uint8_t mac[6];
|
||||
char *information;
|
||||
uint8_t version;
|
||||
char *host;
|
||||
};
|
||||
|
||||
enum dpp_frame_type {
|
||||
DPP_FRAME_AUTHENTICATION_REQUEST = 0,
|
||||
@ -168,3 +178,6 @@ bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce,
|
||||
|
||||
uint8_t *dpp_point_to_asn1(const struct l_ecc_point *p, size_t *len_out);
|
||||
struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len);
|
||||
|
||||
struct dpp_uri_info *dpp_parse_uri(const char *uri);
|
||||
void dpp_free_uri_info(struct dpp_uri_info *info);
|
||||
|
Loading…
Reference in New Issue
Block a user