[PATCH 1/7] scan: add scan_bss_get_security
by James Prestwood
This was already implemented in station but with no dependency on
that module at all. AP will need this for a scanning API so its
being moved into scan.c.
---
src/scan.c | 17 +++++++++++++++++
src/scan.h | 2 ++
2 files changed, 19 insertions(+)
diff --git a/src/scan.c b/src/scan.c
index 1ab6420b..7c510df8 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -1680,6 +1680,23 @@ int scan_bss_get_rsn_info(const struct scan_bss *bss, struct ie_rsn_info *info)
return 0;
}
+int scan_bss_get_security(const struct scan_bss *bss, enum security *security)
+{
+ int ret;
+ struct ie_rsn_info info;
+
+ ret = scan_bss_get_rsn_info(bss, &info);
+ if (ret < 0) {
+ if (ret != -ENOENT)
+ return ret;
+
+ *security = security_determine(bss->capability, NULL);
+ } else
+ *security = security_determine(bss->capability, &info);
+
+ return 0;
+}
+
int scan_bss_rank_compare(const void *a, const void *b, void *user_data)
{
const struct scan_bss *new_bss = a, *bss = b;
diff --git a/src/scan.h b/src/scan.h
index 81cb9e46..2c401df6 100644
--- a/src/scan.h
+++ b/src/scan.h
@@ -28,6 +28,7 @@ struct p2p_probe_req;
struct p2p_beacon;
struct mmpdu_header;
struct wiphy;
+enum security;
enum scan_state {
SCAN_STATE_NOT_RUNNING,
@@ -161,6 +162,7 @@ void scan_bss_free(struct scan_bss *bss);
int scan_bss_rank_compare(const void *a, const void *b, void *user);
int scan_bss_get_rsn_info(const struct scan_bss *bss, struct ie_rsn_info *info);
+int scan_bss_get_security(const struct scan_bss *bss, enum security *security);
struct scan_bss *scan_bss_new_from_probe_req(const struct mmpdu_header *mpdu,
const uint8_t *body,
--
2.34.1
5 months, 3 weeks
[PATCH v2] netdev: fail connection if the link goes down
by James Prestwood
In certain rare cases IWD gets a link down event before nl80211 ever sends
a disconnect event. Netdev notifies station of the link down which causes
station to be freed, but netdev remains in the same state. Then later the
disconnect event arrives and netdev still thinks its connected, calls into
(the now freed) station object and causes a crash.
To fix this netdev_connect_free() is now called on any link down events
which will reset the netdev object to a proper state.
src/netdev.c:netdev_link_notify() event 16 on ifindex 16
src/netdev.c:netdev_mlme_notify() MLME notification Del Station(20)
src/netdev.c:netdev_link_notify() event 16 on ifindex 16
src/netdev.c:netdev_mlme_notify() MLME notification Deauthenticate(39)
src/netdev.c:netdev_deauthenticate_event()
src/netdev.c:netdev_link_notify() event 16 on ifindex 16
src/station.c:station_free()
src/netconfig.c:netconfig_destroy()
src/resolve.c:resolve_systemd_revert() ifindex: 16
src/station.c:station_roam_state_clear() 16
src/netdev.c:netdev_mlme_notify() MLME notification Disconnect(48)
src/netdev.c:netdev_disconnect_event()
Received Deauthentication event, reason: 3, from_ap: false
0 0x472fa4 in station_disconnect_event src/station.c:2916
1 0x472fa4 in station_netdev_event src/station.c:2954
2 0x43a262 in netdev_disconnect_event src/netdev.c:1213
3 0x43a262 in netdev_mlme_notify src/netdev.c:5471
4 0x6706eb in process_multicast ell/genl.c:1029
5 0x6706eb in received_data ell/genl.c:1096
6 0x65e630 in io_callback ell/io.c:120
7 0x65a94e in l_main_iterate ell/main.c:478
8 0x65b0b3 in l_main_run ell/main.c:525
9 0x65b0b3 in l_main_run ell/main.c:507
10 0x65b5cc in l_main_run_with_signal ell/main.c:647
11 0x4124d7 in main src/main.c:532
---
src/netdev.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/netdev.c b/src/netdev.c
index bac6860c..36d183e7 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -6199,6 +6199,9 @@ static void netdev_newlink_notify(const struct ifinfomsg *ifi, int bytes)
new_up = netdev_get_is_up(netdev);
+ if (!new_up)
+ netdev_connect_free(netdev);
+
/*
* If mac_change_cmd_id is set we are in the process of changing the
* MAC address and this event is a result of powering down/up. In this
--
2.31.1
5 months, 3 weeks
[PATCH v7 1/9] dpp-util: add URI parsing
by James Prestwood
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 | 250 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 13 +++
2 files changed, 263 insertions(+)
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 5199d0ae..cd627e2a 100644
--- a/src/dpp-util.c
+++ b/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);
+}
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
5 months, 3 weeks
[PATCH v6 1/9] dpp-util: add URI parsing
by James Prestwood
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 | 250 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 13 +++
2 files changed, 263 insertions(+)
v6
* Set errno to 0 prior to calling strtoul
* check for version 1 or 2 as ascii rather than using strtoul
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 5199d0ae..cd627e2a 100644
--- a/src/dpp-util.c
+++ b/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);
+}
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
5 months, 3 weeks
[PATCH v5 1/9] dpp-util: add URI parsing
by James Prestwood
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 | 253 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 13 +++
2 files changed, 266 insertions(+)
v5:
* Added several checks to the strtoul calls to handle hopefully all
invalid inputs that strtoul still accepts.
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 5199d0ae..01122e6b 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -866,3 +866,256 @@ 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 */
+ 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;
+
+ 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)
+{
+ char *end;
+ uint8_t version;
+
+ 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, 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);
+}
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
5 months, 3 weeks
[PATCH 1/3] hwsim: check if radio name was already set
by James Prestwood
This was caught by static analysis and shouldn't ever happen.
---
tools/hwsim.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tools/hwsim.c b/tools/hwsim.c
index 3ff3faab..8c076f51 100644
--- a/tools/hwsim.c
+++ b/tools/hwsim.c
@@ -639,6 +639,9 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
break;
case HWSIM_ATTR_RADIO_NAME:
+ if (name)
+ return;
+
name = l_strndup(data, len);
break;
}
--
2.34.1
5 months, 4 weeks
[PATCH v4 1/9] dpp-util: add URI parsing
by James Prestwood
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 | 251 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 13 +++
2 files changed, 264 insertions(+)
v4:
* Added extra checks for parsing channel info
* Added lenth verification for each type of token
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 5199d0ae..8a9842e8 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -866,3 +866,254 @@ 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 negative values for whatever reason */
+ if (*pos == '-')
+ goto free_set;
+
+ /* to check uint8_t overflow */
+ r = oper_class = strtoul(pos, &end, 10);
+
+ /*
+ * Did strtoul fail, not advance pointer, not reach the next
+ * token, or overflow?
+ */
+ if (!end || end == pos || (end && *end != '/') ||
+ r != oper_class)
+ goto free_set;
+
+ pos = end + 1;
+
+ if (*pos == '-')
+ goto free_set;
+
+ r = channel = strtoul(pos, &end, 10);
+
+ /*
+ * Same verification as above, but also checks either for
+ * another pair (,) or end of this token (;)
+ */
+ if (!end || 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)
+{
+ char *end;
+ uint8_t version;
+
+ 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, 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);
+}
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
5 months, 4 weeks
[PATCH v3 01/10] dpp-util: fix dpp_point_from_asn1
by James Prestwood
This got merged without a few additional fixes, in particular an
over 80 character line and incorrect length check.
---
src/dpp-util.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 4a5bd046..5199d0ae 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -828,7 +828,8 @@ struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len)
return NULL;
/* SEQUENCE */
- inner_seq = asn1_der_find_elem(outer_seq, outer_len, 0, &tag, &inner_len);
+ inner_seq = asn1_der_find_elem(outer_seq, outer_len, 0, &tag,
+ &inner_len);
if (!inner_seq || tag != ASN1_ID_SEQUENCE)
return NULL;
@@ -859,7 +860,7 @@ struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len)
/* BITSTRING */
key_data = asn1_der_find_elem(outer_seq, outer_len, 1, &tag, &elen);
- if (!key_data || tag != ASN1_ID_BIT_STRING || elen > 2)
+ if (!key_data || tag != ASN1_ID_BIT_STRING || elen < 2)
return NULL;
return l_ecc_point_from_data(curve, key_data[1],
--
2.34.1
5 months, 4 weeks
[PATCH v3 1/2] hwsim: allow concurrent radio creations
by James Prestwood
Currently CreateRadio only allows a single outstanding DBus message
until the radio is fully created. 99% of the time this is just fine
but in order to test dual phy cards there needs to be support for
phy's appearing at the same time.
This required storing the pending DBus message inside the radio object
rather than a single static variable.
The code was refactored to handle the internal radio info objects better
for the various cases:
- Creation from CreateRadio()
- Radio already existed before hwsim started, or created externally
- Existing radio changed name, address, etc.
First, Name is now a required option to CreateRadio(). This allows
the radio info to be pushed to the queue immediately (also allowing the
pending DBus message to be tracked). Then, when the NEW_RADIO event
fires the pending radio can be looked up (by name) and filled with the
remaining info.
If the radio was not found by name but a matching ID was found this is
the 'changed' case and the radio is re-initialized with the changed
values.
If neither name or ID matches the radio was created externally, or
prior to hwsim starting. A radio info object is created at this time
and initialized.
The ID was changed to a signed integer in order to initialize it to an
invalid number -1. Doing this was required since a pending uninitalized
radio ID (0) could match an existing radio ID. This required some
bounds checks in case the kernels counter reaches an extremely high value.
This isn't likely to ever happen in practice.
---
tools/hwsim.c | 187 +++++++++++++++++++++++++++-----------------------
1 file changed, 101 insertions(+), 86 deletions(-)
diff --git a/tools/hwsim.c b/tools/hwsim.c
index f74dd82e..3ff3faab 100644
--- a/tools/hwsim.c
+++ b/tools/hwsim.c
@@ -35,6 +35,7 @@
#include <net/if_arp.h>
#include <ell/ell.h>
+#include "ell/useful.h"
#include "linux/nl80211.h"
@@ -405,7 +406,7 @@ static void list_callback(struct l_genl_msg *msg, void *user_data)
}
struct radio_info_rec {
- uint32_t id;
+ int32_t id;
uint32_t wiphy_id;
char alpha2[2];
bool p2p;
@@ -415,6 +416,8 @@ struct radio_info_rec {
uint8_t addrs[2][ETH_ALEN];
char *name;
bool ap_only;
+ struct l_dbus_message *pending;
+ uint32_t cmd_id;
};
struct interface_info_rec {
@@ -428,13 +431,16 @@ struct interface_info_rec {
static struct l_queue *radio_info;
static struct l_queue *interface_info;
-static struct l_dbus_message *pending_create_msg;
-static uint32_t pending_create_radio_id;
-
static void radio_free(void *user_data)
{
struct radio_info_rec *rec = user_data;
+ if (rec->cmd_id)
+ l_genl_family_cancel(nl80211, rec->cmd_id);
+
+ if (rec->pending)
+ l_dbus_message_unref(rec->pending);
+
l_free(rec->name);
l_free(rec);
}
@@ -458,7 +464,7 @@ static void hwsim_radio_cache_cleanup(void)
static bool radio_info_match_id(const void *a, const void *b)
{
const struct radio_info_rec *rec = a;
- uint32_t id = L_PTR_TO_UINT(b);
+ int32_t id = L_PTR_TO_INT(b);
return rec->id == id;
}
@@ -512,12 +518,6 @@ static const char *interface_get_path(const struct interface_info_rec *rec)
return path;
}
-static struct l_dbus_message *dbus_error_busy(struct l_dbus_message *msg)
-{
- return l_dbus_message_new_error(msg, HWSIM_SERVICE ".InProgress",
- "Operation already in progress");
-}
-
static struct l_dbus_message *dbus_error_failed(struct l_dbus_message *msg)
{
return l_dbus_message_new_error(msg, HWSIM_SERVICE ".Failed",
@@ -603,17 +603,19 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
- const char *name = NULL;
- const uint32_t *id = NULL;
- size_t name_len = 0;
- struct radio_info_rec *rec;
+ _auto_(l_free) char *name = NULL;
+ const int32_t *id = NULL;
+ struct radio_info_rec *rec = NULL;
uint8_t file_buffer[128];
int bytes, consumed;
unsigned int uintval;
- bool old;
+ bool changed = false;
+ bool new = false;
struct radio_info_rec prev_rec;
bool name_change = false;
const char *path;
+ struct l_dbus_message *reply;
+ const struct l_queue_entry *entry;
if (!l_genl_attr_init(&attr, msg))
return;
@@ -625,11 +627,19 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
break;
id = data;
+
+ /*
+ * ID of -1 denotes a pending creation, so if somehow
+ * the kernel ID counter reaches an extremely high
+ * number of radios we just bail.
+ */
+ if (L_WARN_ON(*id < 0))
+ return;
+
break;
case HWSIM_ATTR_RADIO_NAME:
- name = data;
- name_len = len;
+ name = l_strndup(data, len);
break;
}
}
@@ -637,24 +647,37 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
if (!id || !name)
return;
- rec = l_queue_find(radio_info, radio_info_match_id, L_UINT_TO_PTR(*id));
- if (rec) {
- old = true;
- memcpy(&prev_rec, rec, sizeof(prev_rec));
+ for (entry = l_queue_get_entries(radio_info); entry;
+ entry = entry->next) {
+ struct radio_info_rec *r = entry->data;
- if (strlen(rec->name) != name_len ||
- memcmp(rec->name, name, name_len))
- name_change = true;
+ if (*id == r->id) {
+ changed = true;
+ memcpy(&prev_rec, r, sizeof(prev_rec));
- l_free(rec->name);
- } else {
- old = false;
+ if (strcmp(r->name, name))
+ name_change = true;
+
+ l_free(r->name);
+ r->name = l_steal_ptr(name);
+
+ rec = r;
+ break;
+ } else if (!strcmp(r->name, name)) {
+ rec = r;
+ rec->id = *id;
+
+ break;
+ }
+ }
+
+ if (!rec) {
+ new = true;
rec = l_new(struct radio_info_rec, 1);
rec->id = *id;
+ rec->name = l_steal_ptr(name);
}
- rec->name = l_strndup(name, name_len);
-
l_genl_attr_init(&attr, msg);
while (l_genl_attr_next(&attr, &type, &len, &data)) {
@@ -726,12 +749,12 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
if (!radio_info)
radio_info = l_queue_new();
- if (!old)
+ if (new)
l_queue_push_tail(radio_info, rec);
path = radio_get_path(rec);
- if (!old) {
+ if (!changed) {
/* Create Dbus object */
if (!l_dbus_object_add_interface(dbus, path,
@@ -758,23 +781,24 @@ static void get_radio_callback(struct l_genl_msg *msg, void *user_data)
}
/* Send pending CreateRadio reply */
- if (pending_create_msg && pending_create_radio_id == rec->id) {
- struct l_dbus_message *reply =
- l_dbus_message_new_method_return(pending_create_msg);
+ if (rec->pending) {
+ reply = l_dbus_message_new_method_return(rec->pending);
l_dbus_message_set_arguments(reply, "o", path);
- dbus_pending_reply(&pending_create_msg, reply);
+ dbus_pending_reply(&rec->pending, reply);
}
return;
err_free_radio:
- if (!old)
- radio_free(rec);
+ if (rec->pending)
+ dbus_pending_reply(&rec->pending,
+ dbus_error_failed(rec->pending));
- if (pending_create_msg && pending_create_radio_id == *id)
- dbus_pending_reply(&pending_create_msg,
- dbus_error_failed(pending_create_msg));
+ if (!new)
+ l_queue_remove(radio_info, rec);
+
+ radio_free(rec);
}
static bool radio_ap_only(struct radio_info_rec *rec)
@@ -934,7 +958,7 @@ static void del_radio_event(struct l_genl_msg *msg)
struct l_genl_attr attr;
uint16_t type, len;
const void *data;
- const uint32_t *id = NULL;
+ const int32_t *id = NULL;
if (!l_genl_attr_init(&attr, msg))
return;
@@ -947,6 +971,9 @@ static void del_radio_event(struct l_genl_msg *msg)
id = data;
+ if (L_WARN_ON(*id < 0))
+ return;
+
break;
}
}
@@ -955,7 +982,7 @@ static void del_radio_event(struct l_genl_msg *msg)
return;
radio = l_queue_find(radio_info, radio_info_match_id,
- L_UINT_TO_PTR(*id));
+ L_INT_TO_PTR(*id));
if (!radio)
return;
@@ -1642,44 +1669,27 @@ static void unicast_handler(struct l_genl_msg *msg, void *user_data)
static void radio_manager_create_callback(struct l_genl_msg *msg,
void *user_data)
{
+ struct radio_info_rec *radio = user_data;
struct l_dbus_message *reply;
- struct l_genl_attr attr;
- struct radio_info_rec *radio;
- int err;
- /*
- * Note that the radio id is returned in the error field of
- * the returned message.
- */
- if (l_genl_attr_init(&attr, msg))
- goto error;
-
- err = l_genl_msg_get_error(msg);
- if (err < 0)
- goto error;
+ radio->cmd_id = 0;
- pending_create_radio_id = err;
+ if (l_genl_msg_get_error(msg) >= 0)
+ return;
/*
- * If the NEW_RADIO event has been received we'll have added the
- * radio to radio_info already but we can send the method return
- * only now that we know the ID returned by our command.
+ * In theory pending should always be set. This is to handle the
+ * NEW_RADIO event coming prior to this callback and this callback
+ * also having an error. It doesn't seem possible for this to happen,
+ * but who knows.
*/
- radio = l_queue_find(radio_info, radio_info_match_id,
- L_UINT_TO_PTR(pending_create_radio_id));
- if (radio && pending_create_msg) {
- const char *path = radio_get_path(radio);
-
- reply = l_dbus_message_new_method_return(pending_create_msg);
- l_dbus_message_set_arguments(reply, "o", path);
- dbus_pending_reply(&pending_create_msg, reply);
+ if (radio->pending) {
+ reply = dbus_error_failed(radio->pending);
+ dbus_pending_reply(&radio->pending, reply);
}
- return;
-
-error:
- reply = dbus_error_failed(pending_create_msg);
- dbus_pending_reply(&pending_create_msg, reply);
+ l_queue_remove(radio_info, radio);
+ radio_free(radio);
}
static struct l_dbus_message *radio_manager_create(struct l_dbus *dbus,
@@ -1694,9 +1704,7 @@ static struct l_dbus_message *radio_manager_create(struct l_dbus *dbus,
bool p2p = false;
const char *disabled_iftypes = NULL;
const char *disabled_ciphers = NULL;
-
- if (pending_create_msg)
- return dbus_error_busy(message);
+ struct radio_info_rec *radio;
if (!l_dbus_message_get_arguments(message, "a{sv}", &dict))
goto invalid;
@@ -1720,13 +1728,15 @@ static struct l_dbus_message *radio_manager_create(struct l_dbus *dbus,
goto invalid;
}
+ if (!name)
+ goto invalid;
+
new_msg = l_genl_msg_new(HWSIM_CMD_NEW_RADIO);
l_genl_msg_append_attr(new_msg, HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
0, NULL);
- if (name)
- l_genl_msg_append_attr(new_msg, HWSIM_ATTR_RADIO_NAME,
- strlen(name) + 1, name);
+ l_genl_msg_append_attr(new_msg, HWSIM_ATTR_RADIO_NAME,
+ strlen(name) + 1, name);
if (p2p)
l_genl_msg_append_attr(new_msg, HWSIM_ATTR_SUPPORT_P2P_DEVICE,
@@ -1752,11 +1762,19 @@ static struct l_dbus_message *radio_manager_create(struct l_dbus *dbus,
}
- l_genl_family_send(hwsim, new_msg, radio_manager_create_callback,
- pending_create_msg, NULL);
+ radio = l_new(struct radio_info_rec, 1);
+ radio->pending = l_dbus_message_ref(message);
+ radio->name = l_strdup(name);
+ radio->id = -1;
+
+ if (!radio_info)
+ radio_info = l_queue_new();
- pending_create_msg = l_dbus_message_ref(message);
- pending_create_radio_id = 0;
+ l_queue_push_tail(radio_info, radio);
+
+ radio->cmd_id = l_genl_family_send(hwsim, new_msg,
+ radio_manager_create_callback,
+ radio, NULL);
return NULL;
@@ -3088,9 +3106,6 @@ int main(int argc, char *argv[])
l_genl_family_free(nl80211);
l_genl_unref(genl);
- if (pending_create_msg)
- l_dbus_message_unref(pending_create_msg);
-
l_dbus_destroy(dbus);
hwsim_radio_cache_cleanup();
l_queue_destroy(rules, l_free);
--
2.34.1
6 months
[PATCH 1/2] tools: add decrypt-profile tool
by James Prestwood
This tool will decrypt an IWD network profile which was previously
encrypted using a systemd provided key. Either a text passphrase
can be provided (--pass) or a file containing the secret (--file).
This can be useful for debugging, or recovering an encrypted
profile after enabling SystemdEncrypt.
---
Makefile.am | 8 +-
tools/iwd-decrypt-profile.c | 213 ++++++++++++++++++++++++++++++++++++
2 files changed, 220 insertions(+), 1 deletion(-)
create mode 100644 tools/iwd-decrypt-profile.c
Following up after the core changes were merged. The --ssid option was
changed to --name to be consitent with the internal usage where either
the SSID or hotspot network name can be passed.
diff --git a/Makefile.am b/Makefile.am
index 35938d22..a0e54594 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -362,7 +362,7 @@ man_MANS += wired/ead.8
endif
endif
-noinst_PROGRAMS += tools/probe-req
+noinst_PROGRAMS += tools/probe-req tools/iwd-decrypt-profile
tools_probe_req_SOURCES = tools/probe-req.c src/mpdu.h src/mpdu.c \
src/ie.h src/ie.c \
@@ -372,6 +372,12 @@ tools_probe_req_SOURCES = tools/probe-req.c src/mpdu.h src/mpdu.c \
src/band.h src/band.c
tools_probe_req_LDADD = $(ell_ldadd)
+tools_iwd_decrypt_profile_SOURCES = tools/iwd-decrypt-profile.c \
+ src/common.h src/common.c \
+ src/crypto.h src/crypto.c \
+ src/storage.h src/storage.c
+tools_iwd_decrypt_profile_LDADD = ${ell_ldadd}
+
if HWSIM
bin_PROGRAMS += tools/hwsim
diff --git a/tools/iwd-decrypt-profile.c b/tools/iwd-decrypt-profile.c
new file mode 100644
index 00000000..fa957183
--- /dev/null
+++ b/tools/iwd-decrypt-profile.c
@@ -0,0 +1,213 @@
+/*
+ *
+ * Wireless daemon for Linux
+ *
+ * Copyright (C) 2022 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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <getopt.h>
+
+#include <ell/ell.h>
+
+#include "ell/useful.h"
+
+#include "src/storage.h"
+#include "src/common.h"
+
+static void usage(void)
+{
+ printf("decrypt-profile - Decrypt a network profile\n"
+ "Usage:\n");
+ printf("\tdecrypt-profile [--pass | --file] [OPTIONS]\n");
+ printf("\n\tEither --pass or --file must be provided. The profile\n");
+ printf("\tshould be supplied using --infile.\n");
+ printf("\tThe --name argument must be used if the name cannot be\n");
+ printf("\tinferred from the input file\n\n");
+ printf("Options:\n"
+ "\t-p, --pass Password/key used to encrypt\n"
+ "\t-f, --file File containing key\n"
+ "\t-s, --name Name for associated profile (will\n"
+ "\t be inferred from --infile if not\n"
+ "\t provided). For non hotspot networks\n"
+ "\t this will be the SSID.\n"
+ "\t-i, --infile Input profile\n"
+ "\t-o, --outfile Output file for decrypted profile\n"
+ "\t-h, --help Show help options\n");
+ printf("\n");
+}
+
+static const struct option main_options[] = {
+ { "pass", required_argument, NULL, 'p' },
+ { "file", required_argument, NULL, 'f' },
+ { "infile", required_argument, NULL, 'i' },
+ { "outfile", required_argument, NULL, 'o' },
+ { "name", required_argument, NULL, 'n' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+static bool secret_from_file(const char *file)
+{
+ int fd;
+ struct stat st;
+ void *data = NULL;
+ bool r;
+
+ fd = open(file, O_RDONLY, 0);
+ if (fd < 0) {
+ printf("Cant open %s (%d)\n", file, fd);
+ return false;
+ }
+
+ if (fstat(fd, &st) < 0 || st.st_size == 0) {
+ close(fd);
+ return false;
+ }
+
+ data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (data == MAP_FAILED)
+ return false;
+
+ r = storage_init(data, st.st_size);
+
+ munmap(data, st.st_size);
+
+ return r;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *pass = NULL;
+ const char *file = NULL;
+ const char *infile = NULL;
+ const char *outfile = NULL;
+ const char *name = NULL;
+ _auto_(l_free) char *decrypted = NULL;
+ _auto_(l_settings_free) struct l_settings *settings = NULL;
+ enum security sec;
+ int ret = EXIT_FAILURE;
+ ssize_t len = 0;
+ int r;
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "pfhion", main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'p':
+ pass = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'i':
+ infile = optarg;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'n':
+ name = optarg;
+ break;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ goto failed;
+ }
+ }
+
+ if (!file && !pass) {
+ printf("--file or --pass must be supplied\n\n");
+ goto usage;
+ }
+
+ if (!infile) {
+ printf("--infile must be supplied\n\n");
+ goto usage;
+ }
+
+ if (!name) {
+ name = storage_network_ssid_from_path(infile, &sec);
+ if (!name) {
+ printf("Can't get name from --infile, use --name\n\n");
+ goto usage;
+ }
+ }
+
+ settings = l_settings_new();
+
+ if (!l_settings_load_from_file(settings, infile)) {
+ printf("Profile is not formatted correctly\n");
+ goto failed;
+ }
+
+ if (pass) {
+ if (!storage_init((const uint8_t *)pass, strlen(pass)))
+ goto failed;
+ } else if (!secret_from_file(file))
+ goto failed;
+
+ r = __storage_decrypt(settings, name, NULL);
+ if (r < 0) {
+ printf("Unable to decrypt profile (%d)\n", r);
+ goto failed;
+ }
+
+ decrypted = l_settings_to_data(settings, (size_t *)&len);
+
+ if (!outfile)
+ fwrite(decrypted, 1, len, stdout);
+ else {
+ int fd = open(outfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+
+ if (fd < 0) {
+ printf("Unable to open %s (%d)\n", outfile, fd);
+ close(fd);
+ goto failed;
+ }
+
+ len = write(fd, decrypted, len);
+
+ close(fd);
+
+ if (len < 0) {
+ printf("Unable to write to %s (%ld)\n", outfile, len);
+ goto failed;
+ }
+ }
+
+ ret = EXIT_SUCCESS;
+
+usage:
+ if (ret != EXIT_SUCCESS)
+ usage();
+
+failed:
+ return ret;
+}
--
2.34.1
6 months