* Re: [PATCH v3 02/10] dpp-util: add URI parsing
@ 2022-02-14 22:52 Denis Kenzior
0 siblings, 0 replies; 4+ messages in thread
From: Denis Kenzior @ 2022-02-14 22:52 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 6473 bytes --]
Hi James,
On 2/14/22 15:48, James Prestwood wrote:
> 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.
> ---
> src/dpp-util.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++
> src/dpp-util.h | 13 +++
> 2 files changed, 230 insertions(+)
>
> diff --git a/src/dpp-util.c b/src/dpp-util.c
> index 5199d0ae..ce2e1740 100644
> --- a/src/dpp-util.c
> +++ b/src/dpp-util.c
> @@ -866,3 +866,220 @@ 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)
> +{
> + 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_NEXT(pos, ',')) {
> + uint8_t channel;
> + uint8_t oper_class = strtol(pos, &end, 10);
strtoul? Also, might want to check for the minus sign and make sure that strtol
isn't returning something larger > 255.
> +
> + if (!end || end == pos || (end && *end != '/'))
> + goto free_set;
> +
> + pos = end + 1;
> + channel = strtol(pos, &end, 10);
> +
> + /* Either another pair (,) or end of this token (;) */
> + if (!end || end == pos || (*end != ',' && *end != ';'))
> + goto free_set;
> +
> + scan_freq_set_add(freqs, oci_to_frequency(oper_class, channel));
> + }
> +
> + 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, uint8_t *mac_out)
> +{
> + uint8_t mac[6];
> + unsigned int i;
> +
> + 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, uint8_t *version_out)
> +{
> + char *end;
> + uint8_t version;
> + size_t len = TOKEN_LEN(str, ';');
> +
> + if (len != 1)
> + return -EINVAL;
> +
> + version = strtol(str, &end, 10);
> +
> + if (version != 1 && version != 2)
> + return -EINVAL;
> +
> + *version_out = version;
> +
> + return 0;
> +}
> +
> +static struct l_ecc_point *dpp_parse_key(const char *str)
> +{
> + _auto_(l_free) uint8_t *decoded = NULL;
> + size_t decoded_len;
> + size_t len = TOKEN_LEN(str, ';');
> +
> + if (!len)
> + return NULL;
> +
> + 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 (strncmp(pos, "DPP:", 4))
> + return NULL;
strncmp? If the string has already been validated to be a valid string, then
strcmp is probably just fine.
> +
> + 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, ';')) {
You don't seem to be validating that the token is terminated by ';'?
> + switch (*pos) {
> + case 'C':
> + info->freqs = dpp_parse_class_and_channel(pos + 2);
> + if (!info->freqs)
> + goto free_info;
> + break;
> + case 'M':
> + ret = dpp_parse_mac(pos + 2, info->mac);
> + if (ret < 0)
> + goto free_info;
This doesn't seem to be checking that parse_mac consumes the entire token?
> + break;
> + case 'V':
> + ret = dpp_parse_version(pos + 2, &info->version);
> + if (ret < 0)
> + goto free_info;
This does...
> + break;
> + case 'K':
> + info->boot_public = dpp_parse_key(pos + 2);
> + if (!info->boot_public)
> + goto free_info;
Unclear here?
> + 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;
> +}
> +
Regards,
-Denis
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3 02/10] dpp-util: add URI parsing
@ 2022-02-16 22:20 Denis Kenzior
0 siblings, 0 replies; 4+ messages in thread
From: Denis Kenzior @ 2022-02-16 22:20 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 1093 bytes --]
Hi James,
On 2/16/22 14:39, James Prestwood wrote:
> Hi Denis,
>
> <snip>
>>> + */
>>> +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 (strncmp(pos, "DPP:", 4))
>>> + return NULL;
>>
>> strncmp? If the string has already been validated to be a valid
>> string, then
>> strcmp is probably just fine.
>
> Since we're only checking a substring of 'pos' (that the first 4
> characters are DPP:) strncmp is needed.
Ah yes, ignore that comment.
Do note that l_str_has_prefix() exists, so that might be even nicer to use here.
>>
>> Unclear here?
>
> This one is tricky because l_base64_decode doesn't give back the number
> of characters consumed... Maybe l_base64_decode should fail if the
> bytes consumed != length? I don't see any such checks in there.
>
Yes, we probably should.
Regards,
-Denis
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v3 02/10] dpp-util: add URI parsing
@ 2022-02-16 20:39 James Prestwood
0 siblings, 0 replies; 4+ messages in thread
From: James Prestwood @ 2022-02-16 20:39 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 3771 bytes --]
Hi Denis,
<snip>
> > + */
> > +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 (strncmp(pos, "DPP:", 4))
> > + return NULL;
>
> strncmp? If the string has already been validated to be a valid
> string, then
> strcmp is probably just fine.
Since we're only checking a substring of 'pos' (that the first 4
characters are DPP:) strncmp is needed.
>
> > +
> > + 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, ';')) {
>
> You don't seem to be validating that the token is terminated by ';'?
Yeah, I'll get the length here and pass that to each parse function to
verify the entire token was consumed.
>
> > + switch (*pos) {
> > + case 'C':
> > + info->freqs =
> > dpp_parse_class_and_channel(pos + 2);
> > + if (!info->freqs)
> > + goto free_info;
> > + break;
> > + case 'M':
> > + ret = dpp_parse_mac(pos + 2, info->mac);
> > + if (ret < 0)
> > + goto free_info;
>
> This doesn't seem to be checking that parse_mac consumes the entire
> token?
>
> > + break;
> > + case 'V':
> > + ret = dpp_parse_version(pos + 2, &info-
> > >version);
> > + if (ret < 0)
> > + goto free_info;
>
> This does...
>
> > + break;
> > + case 'K':
> > + info->boot_public = dpp_parse_key(pos + 2);
> > + if (!info->boot_public)
> > + goto free_info;
>
> Unclear here?
This one is tricky because l_base64_decode doesn't give back the number
of characters consumed... Maybe l_base64_decode should fail if the
bytes consumed != length? I don't see any such checks in there.
>
> > + 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;
> > +}
> > +
>
> Regards,
> -Denis
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 02/10] dpp-util: add URI parsing
@ 2022-02-14 21:48 James Prestwood
0 siblings, 0 replies; 4+ messages in thread
From: James Prestwood @ 2022-02-14 21:48 UTC (permalink / raw)
To: iwd
[-- Attachment #1: Type: text/plain, Size: 6687 bytes --]
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.
---
src/dpp-util.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 13 +++
2 files changed, 230 insertions(+)
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 5199d0ae..ce2e1740 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -866,3 +866,220 @@ 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)
+{
+ 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_NEXT(pos, ',')) {
+ uint8_t channel;
+ uint8_t oper_class = strtol(pos, &end, 10);
+
+ if (!end || end == pos || (end && *end != '/'))
+ goto free_set;
+
+ pos = end + 1;
+ channel = strtol(pos, &end, 10);
+
+ /* Either another pair (,) or end of this token (;) */
+ if (!end || end == pos || (*end != ',' && *end != ';'))
+ goto free_set;
+
+ scan_freq_set_add(freqs, oci_to_frequency(oper_class, channel));
+ }
+
+ 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, uint8_t *mac_out)
+{
+ uint8_t mac[6];
+ unsigned int i;
+
+ 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, uint8_t *version_out)
+{
+ char *end;
+ uint8_t version;
+ size_t len = TOKEN_LEN(str, ';');
+
+ if (len != 1)
+ return -EINVAL;
+
+ version = strtol(str, &end, 10);
+
+ if (version != 1 && version != 2)
+ return -EINVAL;
+
+ *version_out = version;
+
+ return 0;
+}
+
+static struct l_ecc_point *dpp_parse_key(const char *str)
+{
+ _auto_(l_free) uint8_t *decoded = NULL;
+ size_t decoded_len;
+ size_t len = TOKEN_LEN(str, ';');
+
+ if (!len)
+ return NULL;
+
+ 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 (strncmp(pos, "DPP:", 4))
+ 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, ';')) {
+ switch (*pos) {
+ case 'C':
+ info->freqs = dpp_parse_class_and_channel(pos + 2);
+ if (!info->freqs)
+ goto free_info;
+ break;
+ case 'M':
+ ret = dpp_parse_mac(pos + 2, info->mac);
+ if (ret < 0)
+ goto free_info;
+ break;
+ case 'V':
+ ret = dpp_parse_version(pos + 2, &info->version);
+ if (ret < 0)
+ goto free_info;
+ break;
+ case 'K':
+ info->boot_public = dpp_parse_key(pos + 2);
+ 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);
+}
diff --git a/src/dpp-util.h b/src/dpp-util.h
index 82535ff8..a3ddd452 100644
--- a/src/dpp-util.h
+++ b/src/dpp-util.h
@@ -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);
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2022-02-16 22:20 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-14 22:52 [PATCH v3 02/10] dpp-util: add URI parsing Denis Kenzior
-- strict thread matches above, loose matches on Subject: below --
2022-02-16 22:20 Denis Kenzior
2022-02-16 20:39 James Prestwood
2022-02-14 21:48 James Prestwood
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.