[PATCHv2] gpio: add simple get/set helpers for GPIO lines
by Martin Hundebøll
The Linux kernel GPIO api operates with chips, lines, handles, and
events.
The chip and line structures represent info about gpio chips, and
gpio lines, respectively. They are used to e.g. lookup a line with a
certain name, and/or line flags.
The handle structure is used to "obtain" a handle to one or more gpio
lines on a chip. Until the file descriptor in this handle is closed, the
gpio lines cannot be used by others. The same file descriptor is used
when setting or getting the line values (one can also set the initial
value when obtaining handles for output lines).
The event structure is used to get a file descriptor that can be used
with select/poll to wait for changes in line levels.
This commit add simple support for setting and getting the value for a
single gpio line. It does so by obtaining a line handle to get/set the
value, and then release the handle immediately again.
Functionality that could be implemented, but is postponed until the need
arises includes:
* looking up a gpio line by its name
* setting/getting multiple gpio lines with a single function
* waiting for events
* holding on to handles
Some of the above probably require adding structures to represent gpio
lines and events, while handles should be private to the class.
---
Changes since v1:
* added gpiochip info and corresponding getters
* changed "line" to "line_num" a few places
Changes since RFC:
* added gpio.h to ell.h
* changed copyright to Geanix
* open gpiochip in l_gpio_chip_new()
* open gpiochip read-only
* added input checks
* clear ioctl structs with memset instead of = {0}
* reorder error-paths
Makefile.am | 6 +-
ell/ell.h | 1 +
ell/ell.sym | 7 +++
ell/gpio.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/gpio.h | 49 +++++++++++++++
5 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 ell/gpio.c
create mode 100644 ell/gpio.h
diff --git a/Makefile.am b/Makefile.am
index 8401972..0ecb9a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,7 +51,8 @@ pkginclude_HEADERS = ell/ell.h \
ell/dhcp.h \
ell/cert.h \
ell/ecc.h \
- ell/ecdh.h
+ ell/ecdh.h \
+ ell/gpio.h
lib_LTLIBRARIES = ell/libell.la
@@ -119,7 +120,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/ecc.h \
ell/ecc-external.c \
ell/ecc.c \
- ell/ecdh.c
+ ell/ecdh.c \
+ ell/gpio.c
ell_libell_la_LDFLAGS = -no-undefined \
-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
diff --git a/ell/ell.h b/ell/ell.h
index aab6417..fb1dd79 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -59,3 +59,4 @@
#include <ell/cert.h>
#include <ell/ecc.h>
#include <ell/ecdh.h>
+#include <ell/gpio.h>
diff --git a/ell/ell.sym b/ell/ell.sym
index 841bc49..793e4c3 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -463,6 +463,13 @@ global:
/* ecdh */
l_ecdh_generate_key_pair;
l_ecdh_generate_shared_secret;
+ /* gpio */
+ l_gpio_chip_new;
+ l_gpio_chip_free;
+ l_gpio_chip_get_label;
+ l_gpio_chip_get_num_lines;
+ l_gpio_chip_get_line_value;
+ l_gpio_chip_set_line_value;
local:
*;
};
diff --git a/ell/gpio.c b/ell/gpio.c
new file mode 100644
index 0000000..83032f9
--- /dev/null
+++ b/ell/gpio.c
@@ -0,0 +1,175 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2018 Geanix. 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 <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+
+#include "util.h"
+#include "gpio.h"
+#include "private.h"
+
+struct l_gpio_chip {
+ int fd;
+ char *label;
+ uint32_t num_lines;
+};
+
+LIB_EXPORT struct l_gpio_chip *l_gpio_chip_new(const char *chip_name)
+{
+ struct l_gpio_chip *chip;
+ struct gpiochip_info info;
+ char *path;
+ int ret;
+
+ if (!chip_name)
+ return NULL;
+
+ chip = l_new(struct l_gpio_chip, 1);
+
+ path = l_strdup_printf("/dev/%s", chip_name);
+ chip->fd = open(path, O_RDONLY | O_CLOEXEC);
+ l_free(path);
+
+ if (chip->fd < 0) {
+ l_free(chip);
+ return NULL;
+ }
+
+ memset(&info, 0, sizeof(info));
+
+ ret = ioctl(chip->fd, GPIO_GET_CHIPINFO_IOCTL, &info);
+ if (ret < 0) {
+ l_free(chip);
+ return NULL;
+ }
+
+ if (info.label)
+ chip->label = l_strndup(info.label, sizeof(info.label));
+
+ chip->num_lines = info.lines;
+
+ return chip;
+}
+
+LIB_EXPORT void l_gpio_chip_free(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return;
+
+ if (chip->fd >= 0)
+ close(chip->fd);
+
+ l_free(chip->label);
+ l_free(chip);
+}
+
+LIB_EXPORT const char *l_gpio_chip_get_label(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return NULL;
+
+ return chip->label;
+}
+
+LIB_EXPORT uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return 0;
+
+ return chip->num_lines;
+}
+
+LIB_EXPORT bool l_gpio_chip_get_line_value(struct l_gpio_chip *chip,
+ uint32_t line_num, bool *value)
+{
+ struct gpiohandle_request req;
+ struct gpiohandle_data data;
+ int ret;
+
+ if (!chip)
+ return false;
+
+ if (line_num >= chip->num_lines)
+ return false;
+
+ if (chip->fd < 0)
+ return false;
+
+ memset(&req, 0, sizeof(req));
+ req.lineoffsets[0] = line_num;
+ req.lines = 1;
+ req.flags = GPIOHANDLE_REQUEST_INPUT;
+
+ ret = ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+ if (ret < 0 || req.fd <= 0)
+ return false;
+
+ memset(&data, 0, sizeof(data));
+
+ ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+
+ close(req.fd);
+
+ if (ret < 0)
+ return false;
+
+ if (value)
+ *value = !!data.values[0];
+
+ return true;
+}
+
+LIB_EXPORT bool l_gpio_chip_set_line_value(struct l_gpio_chip *chip,
+ uint32_t line_num, bool value)
+{
+ struct gpiohandle_request req;
+ int ret;
+
+ if (!chip)
+ return false;
+
+ if (line_num >= chip->num_lines)
+ return false;
+
+ if (chip->fd < 0)
+ return false;
+
+ memset(&req, 0, sizeof(req));
+ req.lineoffsets[0] = line_num;
+ req.lines = 1;
+ req.flags = GPIOHANDLE_REQUEST_OUTPUT;
+ req.default_values[0] = value;
+
+ ret = ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+ if (ret < 0 || req.fd <= 0)
+ return false;
+
+ close(req.fd);
+
+ return true;
+}
diff --git a/ell/gpio.h b/ell/gpio.h
new file mode 100644
index 0000000..9f4ae6a
--- /dev/null
+++ b/ell/gpio.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2011-2018 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
+ *
+ */
+
+#ifndef __ELL_GPIO_H
+#define __ELL_GPIO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct l_gpio_chip;
+
+struct l_gpio_chip *l_gpio_chip_new(const char *chip_name);
+void l_gpio_chip_free(struct l_gpio_chip *chip);
+
+const char *l_gpio_chip_get_label(struct l_gpio_chip *chip);
+uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip);
+bool l_gpio_chip_get_line_value(struct l_gpio_chip *chip, uint32_t line_num,
+ bool *value);
+bool l_gpio_chip_set_line_value(struct l_gpio_chip *chip, uint32_t line_num,
+ bool value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_GPIO_H */
--
2.20.0
3 years
[PATCH] unit: Remove loop initial declaration.
by Ossama Othman
Move the variable declaration in test_string.c:test_strv_append() out
of the for-loop initial declaration to support older C standards, as
well as to conform to the ELL coding style.
---
unit/test-string.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/unit/test-string.c b/unit/test-string.c
index 710617b..d8aebcb 100644
--- a/unit/test-string.c
+++ b/unit/test-string.c
@@ -257,15 +257,15 @@ static void test_strv_append(const void *test_data)
{
const char *src[] = { "Foo", "Bar" };
char **dst = NULL;
-
size_t len = L_ARRAY_SIZE(src);
+ size_t i;
- for (size_t i = 0; i < len; i++)
+ for (i = 0; i < len; i++)
dst = l_strv_append(dst, src[i]);
assert(l_strv_length(dst) == len);
- for (size_t i = 0; i < len; i++)
+ for (i = 0; i < len; i++)
assert(strcmp(src[i], dst[i]) == 0);
l_strv_free(dst);
--
2.17.1
3 years, 4 months
[PATCH v3 1/2] settings: fix segfault if when settings has no group
by James Prestwood
A settings file containing a key/value but no group caused
l_settings_load_from_data to segfault. This was due to not checking
that a group was actually found before parsing a key/value pair.
A new flag was added, 'has_group', which gets set after a group has
been parsed successfully. This flag also must be true for the load
to be successful.
Also, the return of parse_key was being checked against false, when
it actually is returning a unsigned int. This was changed to just
check !parse_key.
---
ell/settings.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/ell/settings.c b/ell/settings.c
index e3e8303..0c06fe7 100644
--- a/ell/settings.c
+++ b/ell/settings.c
@@ -347,7 +347,7 @@ static bool parse_keyvalue(struct l_settings *settings, const char *data,
return false;
}
- if (parse_key(settings, data, equal - data, line) == false)
+ if (!parse_key(settings, data, equal - data, line))
return false;
equal += 1;
@@ -362,6 +362,7 @@ LIB_EXPORT bool l_settings_load_from_data(struct l_settings *settings,
{
size_t pos = 0;
bool r = true;
+ bool has_group = false;
const char *eol;
size_t line = 1;
size_t line_len;
@@ -387,11 +388,17 @@ LIB_EXPORT bool l_settings_load_from_data(struct l_settings *settings,
line_len = eol - data - pos;
- if (data[pos] == '[')
+ if (data[pos] == '[') {
r = parse_group(settings, data + pos, line_len, line);
- else if (data[pos] != '#')
+ if (r)
+ has_group = true;
+ } else if (data[pos] != '#') {
+ if (!has_group)
+ return false;
+
r = parse_keyvalue(settings, data + pos, line_len,
line);
+ }
pos += line_len;
}
--
2.17.1
3 years, 4 months
[PATCH v2] settings: fix segfault if when settings has no group
by James Prestwood
A settings file containing a key/value but no group caused
l_settings_load_from_data to segfault. This was due to not checking
that a group was actually found before parsing a key/value pair.
A new flag was added, 'has_group', which gets set after a group has
been parsed successfully. This flag also must be true for the load
to be successful.
Also, the return of parse_key was being checked against false, when
it actually is returning a unsigned int. This was changed to just
check !parse_key.
---
ell/settings.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/ell/settings.c b/ell/settings.c
index e3e8303..a5856bd 100644
--- a/ell/settings.c
+++ b/ell/settings.c
@@ -347,7 +347,7 @@ static bool parse_keyvalue(struct l_settings *settings, const char *data,
return false;
}
- if (parse_key(settings, data, equal - data, line) == false)
+ if (!parse_key(settings, data, equal - data, line))
return false;
equal += 1;
@@ -362,6 +362,7 @@ LIB_EXPORT bool l_settings_load_from_data(struct l_settings *settings,
{
size_t pos = 0;
bool r = true;
+ bool has_group = false;
const char *eol;
size_t line = 1;
size_t line_len;
@@ -387,16 +388,18 @@ LIB_EXPORT bool l_settings_load_from_data(struct l_settings *settings,
line_len = eol - data - pos;
- if (data[pos] == '[')
+ if (data[pos] == '[') {
r = parse_group(settings, data + pos, line_len, line);
- else if (data[pos] != '#')
+ if (r)
+ has_group = true;
+ } else if (data[pos] != '#' && has_group)
r = parse_keyvalue(settings, data + pos, line_len,
line);
pos += line_len;
}
- return r;
+ return r && has_group;
}
LIB_EXPORT char *l_settings_to_data(const struct l_settings *settings,
--
2.17.1
3 years, 4 months
[PATCH 1/2] settings: fix segfault if when settings has no group
by James Prestwood
A settings file containing a key/value but no group caused
l_settings_load_from_data to segfault. This was due to both parse_key
and parse_value not checking that the group was non-NULL.
Also, the return of parse_key was being checked against false, when
it actually is returning a unsigned int. This was changed to just
check !parse_key.
---
ell/settings.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/ell/settings.c b/ell/settings.c
index e3e8303..2153218 100644
--- a/ell/settings.c
+++ b/ell/settings.c
@@ -297,6 +297,9 @@ static unsigned int parse_key(struct l_settings *settings, const char *data,
}
group = l_queue_peek_tail(settings->groups);
+ if (!group)
+ return 0;
+
pair = l_new(struct setting_data, 1);
pair->key = l_strndup(data, end);
l_queue_push_head(group->settings, pair);
@@ -312,6 +315,9 @@ static bool parse_value(struct l_settings *settings, const char *data,
struct setting_data *pair;
group = l_queue_peek_tail(settings->groups);
+ if (!group)
+ return false;
+
pair = l_queue_pop_head(group->settings);
if (!l_utf8_validate(data, len, NULL)) {
@@ -347,7 +353,7 @@ static bool parse_keyvalue(struct l_settings *settings, const char *data,
return false;
}
- if (parse_key(settings, data, equal - data, line) == false)
+ if (!parse_key(settings, data, equal - data, line))
return false;
equal += 1;
--
2.17.1
3 years, 4 months
[PATCH] unit: Add runtime check for TLS suite cipher support
by Mat Martineau
Skip TLS suite tests where the currently running kernel does not have
support for the necessary cipher. Checksums types already have runtime
validation.
---
unit/test-tls.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/unit/test-tls.c b/unit/test-tls.c
index 01496d2..03c5370 100644
--- a/unit/test-tls.c
+++ b/unit/test-tls.c
@@ -709,9 +709,24 @@ int main(int argc, char *argv[])
l_test_add("TLS connection version mismatch",
test_tls_version_mismatch_test, NULL);
- for (i = 0; tls_cipher_suite_pref[i]; i++)
- l_test_add(tls_cipher_suite_pref[i]->name, test_tls_suite_test,
- tls_cipher_suite_pref[i]->name);
+ for (i = 0; tls_cipher_suite_pref[i]; i++) {
+ struct tls_cipher_suite *suite = tls_cipher_suite_pref[i];
+ struct tls_bulk_encryption_algorithm *alg = suite->encryption;
+ bool supported;
+
+ if (alg->cipher_type == TLS_CIPHER_AEAD)
+ supported = l_aead_cipher_is_supported(alg->l_aead_id);
+ else
+ supported = l_cipher_is_supported(alg->l_id);
+
+ if (supported) {
+ l_test_add(suite->name, test_tls_suite_test,
+ suite->name);
+ } else {
+ printf("Skipping %s due to missing cipher support\n",
+ suite->name);
+ }
+ }
done:
return l_test_run();
--
2.20.1
3 years, 4 months
[PATCH 0/2] l_strv_append() test and minor patch
by Ossama Othman
The l_strv_append() function needlessly post-incremented an array
index value prior to returning. Drop the post-increment, and a
l_strv_append() unit test.
Ossama Othman (2):
unit: Add test for l_strv_append().
strv: Remove unnecessary post-increment.
ell/strv.c | 2 +-
unit/test-string.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)
--
2.17.1
3 years, 4 months
[PATCH 1/4] tls: Implement the Signature Algorithms extension
by Andrew Zaborowski
---
ell/tls-extensions.c | 279 +++++++++++++++++++++++++++++++++++++++++++
ell/tls-private.h | 4 +
ell/tls.c | 10 +-
3 files changed, 284 insertions(+), 9 deletions(-)
diff --git a/ell/tls-extensions.c b/ell/tls-extensions.c
index d179e67..7b20e17 100644
--- a/ell/tls-extensions.c
+++ b/ell/tls-extensions.c
@@ -568,6 +568,278 @@ static ssize_t tls_ec_point_formats_server_write(struct l_tls *tls,
return 2;
}
+/*
+ * This is used to append the list of signature algorithm and hash type
+ * combinations we support to the Signature Algorithms client hello
+ * extension (on the client) and the Certificate Request message (on the
+ * server). In both cases we need to list the algorithms we support for
+ * two use cases: certificate chain verification and signing/verifying
+ * Server Key Exchange params (server->client) or Certificate Verify
+ * data (client->server).
+ *
+ * For the server side RFC 5462, Section 7.4.1.4.1 says:
+ * "If the client [...] is willing to use them for verifying
+ * messages sent by the server, i.e., server certificates and
+ * server key exchange [...] it MUST send the
+ * signature_algorithms extension, listing the algorithms it
+ * is willing to accept."
+ *
+ * As for the certificate chains we mostly rely on the kernel to do
+ * this so when we receive the list we do not currently verify the
+ * that the whole chain uses only algorithms from the list on either
+ * side (TODO). But we know that the chain verification in the kernel
+ * can use a superset of the hash algorithms l_checksum supports.
+ * For the Server Key Exchange and Certificate Verify signatures we
+ * use l_checksum but we need to map the TLS-specific hash IDs to
+ * enum l_checksum_type using the tls_handshake_hash_data list in
+ * signature->sign() and signature->verify(), so we use
+ * tls_handshake_hash_data as the definitive list of allowed hash
+ * algorithms.
+ *
+ * Our supported signature algorithms can work with any hash type so we
+ * basically have to send all possible combinations of the signature
+ * algorithm IDs from the supported cipher suites (except anonymous)
+ * with the hash algorithms we can use for signature verification,
+ * i.e. those in the tls_handshake_hash_data table.
+ */
+static ssize_t tls_write_signature_algorithms(struct l_tls *tls,
+ uint8_t *buf, size_t len)
+{
+ uint8_t *ptr = buf;
+ unsigned int i, j;
+ struct tls_cipher_suite **suite;
+ uint8_t sig_alg_ids[16];
+ uint8_t hash_ids[16];
+ unsigned int sig_alg_cnt = 0;
+ unsigned int hash_cnt = 0;
+
+ for (suite = tls->cipher_suite_pref_list; *suite; suite++) {
+ uint8_t id;
+
+ if (!(*suite)->signature)
+ continue;
+
+ id = (*suite)->signature->id;
+
+ if (memchr(sig_alg_ids, id, sig_alg_cnt))
+ continue;
+
+ if (!tls_cipher_suite_is_compatible(tls, *suite, NULL))
+ continue;
+
+ if (sig_alg_cnt >= sizeof(sig_alg_ids))
+ return -ENOMEM;
+
+ sig_alg_ids[sig_alg_cnt++] = id;
+ }
+
+ for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) {
+ const struct tls_hash_algorithm *hash =
+ &tls_handshake_hash_data[i];
+ bool supported;
+
+ /*
+ * The hash types in the Signature Algorithms extension are
+ * all supported hashes but the ones in the Certificate
+ * Request (server->client) must be in the set for which we
+ * maintain handshake message hashes because that is going
+ * to be used in Certificate Verify.
+ */
+ if (tls->server)
+ supported = !!tls->handshake_hash[i];
+ else
+ supported = l_checksum_is_supported(hash->l_id, false);
+
+ if (supported)
+ hash_ids[hash_cnt++] = hash->tls_id;
+ }
+
+ if (len < 2 + sig_alg_cnt * hash_cnt * 2)
+ return -ENOMEM;
+
+ l_put_be16(sig_alg_cnt * hash_cnt * 2, ptr);
+ ptr += 2;
+
+ for (i = 0; i < sig_alg_cnt; i++)
+ for (j = 0; j < hash_cnt; j++) {
+ *ptr++ = hash_ids[j];
+ *ptr++ = sig_alg_ids[i];
+ }
+
+ return ptr - buf;
+}
+
+static ssize_t tls_parse_signature_algorithms(struct l_tls *tls,
+ const uint8_t *buf, size_t len)
+{
+ const uint8_t *ptr = buf;
+ enum handshake_hash_type first_supported, hash;
+ const struct tls_hash_algorithm *preferred;
+ struct tls_cipher_suite **suite;
+ uint8_t sig_alg_ids[16];
+ unsigned int sig_alg_cnt = 0;
+
+ /*
+ * This only makes sense as a variable-length field, assume
+ * there's a typo in RFC5246 7.4.4 here.
+ */
+ if (len < 4)
+ return -EINVAL;
+
+ if (l_get_be16(ptr) > len - 2)
+ return -EINVAL;
+
+ len = l_get_be16(ptr);
+ ptr += 2;
+
+ if (len & 1)
+ return -EINVAL;
+
+ for (suite = tls->cipher_suite_pref_list; *suite; suite++) {
+ uint8_t id;
+
+ if (!(*suite)->signature)
+ continue;
+
+ id = (*suite)->signature->id;
+
+ if (memchr(sig_alg_ids, id, sig_alg_cnt))
+ continue;
+
+ if (!tls_cipher_suite_is_compatible(tls, *suite, NULL))
+ continue;
+
+ if (sig_alg_cnt >= sizeof(sig_alg_ids))
+ return -ENOMEM;
+
+ sig_alg_ids[sig_alg_cnt++] = id;
+ }
+
+ /*
+ * In 1.2 we force our preference for SHA256/SHA384 (depending on
+ * cipher suite's PRF hmac) if it is supported by the peer because
+ * that must be supported anyway for the PRF and the Finished hash
+ * meaning that we only need to keep one hash instead of two.
+ * If not available fall back to the first common hash algorithm.
+ */
+ first_supported = -1;
+
+ if (tls->prf_hmac)
+ preferred = tls->prf_hmac;
+ else
+ preferred = &tls_handshake_hash_data[HANDSHAKE_HASH_SHA256];
+
+ while (len) {
+ uint8_t hash_id = *ptr++;
+ uint8_t sig_alg_id = *ptr++;
+ bool supported;
+
+ len -= 2;
+
+ /* Ignore hash types for signatures other than ours */
+ if (tls->pending.cipher_suite &&
+ (!tls->pending.cipher_suite->signature ||
+ tls->pending.cipher_suite->signature->id !=
+ sig_alg_id))
+ continue;
+ else if (!tls->pending.cipher_suite &&
+ !memchr(sig_alg_ids, sig_alg_id, sig_alg_cnt))
+ continue;
+
+ if (hash_id == preferred->tls_id) {
+ for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++)
+ if (&tls_handshake_hash_data[hash] == preferred)
+ break;
+ break;
+ }
+
+ if ((int) first_supported != -1)
+ continue;
+
+ for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++)
+ if (hash_id == tls_handshake_hash_data[hash].tls_id)
+ break;
+
+ if (hash == __HANDSHAKE_HASH_COUNT)
+ continue;
+
+ if (tls->server)
+ supported = l_checksum_is_supported(
+ tls_handshake_hash_data[hash].l_id,
+ false);
+ else
+ supported = !!tls->handshake_hash[hash];
+
+ if (supported)
+ first_supported = hash;
+ }
+
+ if (len)
+ tls->signature_hash = hash;
+ else if ((int) first_supported != -1)
+ tls->signature_hash = first_supported;
+ else
+ return -ENOTSUP;
+
+ return ptr + len - buf;
+}
+
+/* RFC 5462, Section 7.4.1.4.1 */
+static ssize_t tls_signature_algorithms_client_write(struct l_tls *tls,
+ uint8_t *buf, size_t len)
+{
+ /*
+ * "Note: this extension is not meaningful for TLS versions
+ * prior to 1.2. Clients MUST NOT offer it if they are offering
+ * prior versions."
+ */
+ if (tls->max_version < L_TLS_V12)
+ return -ENOMSG;
+
+ return tls_write_signature_algorithms(tls, buf, len);
+}
+
+static bool tls_signature_algorithms_client_handle(struct l_tls *tls,
+ const uint8_t *buf, size_t len)
+{
+ ssize_t ret;
+
+ /*
+ * "However, even if clients do offer it, the rules specified in
+ * [TLSEXT] require servers to ignore extensions they do not
+ * understand."
+ */
+ if (tls->max_version < L_TLS_V12)
+ return true;
+
+ ret = tls_parse_signature_algorithms(tls, buf, len);
+
+ if (ret == -ENOTSUP)
+ TLS_DEBUG("No common signature algorithms");
+
+ /*
+ * TODO: also check our certificate chain against the parsed
+ * signature algorithms.
+ */
+
+ return ret == (ssize_t) len;
+}
+
+static bool tls_signature_algorithms_client_absent(struct l_tls *tls)
+{
+ /*
+ * "If the client does not send the signature_algorithms extension,
+ * the server MUST do the following:
+ * - [...] behave as if client had sent the value {sha1,rsa}.
+ * - [...] behave as if client had sent the value {sha1,dsa}.
+ * - [...] behave as if client had sent the value {sha1,ecdsa}.
+ */
+ if (tls->max_version >= L_TLS_V12)
+ tls->signature_hash = HANDSHAKE_HASH_SHA1;
+
+ return true;
+}
+
const struct tls_hello_extension tls_extensions[] = {
{
"Supported Groups", "elliptic_curves", 10,
@@ -584,6 +856,13 @@ const struct tls_hello_extension tls_extensions[] = {
tls_ec_point_formats_server_write,
NULL, NULL,
},
+ {
+ "Signature Algorithms", "signature_algoritms", 13,
+ tls_signature_algorithms_client_write,
+ tls_signature_algorithms_client_handle,
+ tls_signature_algorithms_client_absent,
+ NULL, NULL, NULL,
+ },
{}
};
diff --git a/ell/tls-private.h b/ell/tls-private.h
index 03ad07c..2d1424e 100644
--- a/ell/tls-private.h
+++ b/ell/tls-private.h
@@ -308,6 +308,10 @@ bool tls_handle_message(struct l_tls *tls, const uint8_t *message,
void tls_tx_handshake(struct l_tls *tls, int type, uint8_t *buf, size_t length);
+bool tls_cipher_suite_is_compatible(struct l_tls *tls,
+ const struct tls_cipher_suite *suite,
+ const char **error);
+
/* Optionally limit allowed cipher suites to a custom set */
bool tls_set_cipher_suites(struct l_tls *tls, const char **suite_list);
diff --git a/ell/tls.c b/ell/tls.c
index 02e49f9..fd898f6 100644
--- a/ell/tls.c
+++ b/ell/tls.c
@@ -379,7 +379,7 @@ static void tls_reset_cipher_spec(struct l_tls *tls, bool txrx)
tls_change_cipher_spec(tls, txrx, NULL);
}
-static bool tls_cipher_suite_is_compatible(struct l_tls *tls,
+bool tls_cipher_suite_is_compatible(struct l_tls *tls,
const struct tls_cipher_suite *suite,
const char **error)
{
@@ -1486,13 +1486,6 @@ static void tls_handle_client_hello(struct l_tls *tls,
* trying to connect somewhere else. We might want to throw an error.
*/
- /*
- * TODO: Obligatory in 1.2: check for signature_algorithms extension,
- * store the list of algorithms for later checking in
- * tls_send_certificate on both server and client sides. If not
- * present assume only SHA1+RSA (7.4.1.4.1).
- */
-
/* Save client_version for Premaster Secret verification */
tls->client_version = l_get_be16(buf);
@@ -2407,7 +2400,6 @@ LIB_EXPORT struct l_tls *l_tls_new(bool server,
tls->disconnected = disconnect_handler;
tls->user_data = user_data;
tls->cipher_suite_pref_list = tls_cipher_suite_pref;
- tls->signature_hash = HANDSHAKE_HASH_SHA256;
tls->min_version = TLS_MIN_VERSION;
tls->max_version = TLS_MAX_VERSION;
--
2.19.1
3 years, 4 months
[PATCH v2] uuid: Added l_uuid_from_string
by Michał 'Khorne' Lowas-Rzechonek
From: Michał Lowas-Rzechonek <michal.lowas-rzechonek(a)silvair.com>
This commit adds API complementary to l_uuid_to_string, allowing parsing
canonical string representation into a byte array.
---
ell/ell.sym | 1 +
ell/uuid.c | 34 +++++++++++++++++++++++++
ell/uuid.h | 1 +
unit/test-uuid.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 100 insertions(+)
diff --git a/ell/ell.sym b/ell/ell.sym
index e41e1c3..4fc547e 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -445,6 +445,7 @@ global:
l_uuid_is_valid;
l_uuid_get_version;
l_uuid_to_string;
+ l_uuid_from_string;
/* cert */
l_cert_new_from_der;
l_cert_free;
diff --git a/ell/uuid.c b/ell/uuid.c
index a4d72fc..d590487 100644
--- a/ell/uuid.c
+++ b/ell/uuid.c
@@ -224,3 +224,37 @@ LIB_EXPORT bool l_uuid_to_string(const uint8_t uuid[16],
return true;
}
+
+LIB_EXPORT bool l_uuid_from_string(const char *src, uint8_t uuid[16])
+{
+ uint8_t buf[16];
+ int n;
+
+ /*
+ * textual representation: 32 hex digits + 4 group separators
+ */
+ if (strlen(src) < 16 * 2 + 4)
+ return false;
+
+ n = sscanf(src,
+ "%02hhx%02hhx%02hhx%02hhx-"
+ "%02hhx%02hhx-"
+ "%02hhx%02hhx-"
+ "%02hhx%02hhx-"
+ "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+ &buf[0], &buf[1], &buf[2], &buf[3],
+ &buf[4], &buf[5],
+ &buf[6], &buf[7],
+ &buf[8], &buf[9],
+ &buf[10], &buf[11], &buf[12],
+ &buf[13], &buf[14], &buf[15]);
+
+ if (n != 16)
+ return false;
+
+ if (!l_uuid_is_valid(buf))
+ return false;
+
+ memcpy(uuid, buf, sizeof(buf));
+ return true;
+}
diff --git a/ell/uuid.h b/ell/uuid.h
index 89be520..cb59b78 100644
--- a/ell/uuid.h
+++ b/ell/uuid.h
@@ -53,6 +53,7 @@ bool l_uuid_is_valid(const uint8_t uuid[16]);
enum l_uuid_version l_uuid_get_version(const uint8_t uuid[16]);
bool l_uuid_to_string(const uint8_t uuid[16], char *dest, size_t dest_size);
+bool l_uuid_from_string(const char *src, uint8_t uuid[16]);
#ifdef __cplusplus
}
diff --git a/unit/test-uuid.c b/unit/test-uuid.c
index 088d45c..7a6010d 100644
--- a/unit/test-uuid.c
+++ b/unit/test-uuid.c
@@ -158,6 +158,65 @@ static void test_to_string(const void *data)
assert(!strcmp(buf, expected_uuid));
}
+static void test_from_string_too_short(const void *data)
+{
+ uint8_t uuid[16];
+ const char *string_uuid = "65fcc697-0776-5bf9-8573-72a51080c7d";
+ bool r;
+
+ r = l_uuid_from_string(string_uuid, uuid);
+ assert(!r);
+}
+
+static void test_from_string_too_long(const void *data)
+{
+ uint8_t uuid[16];
+ const char *string_uuid = "65fcc697-0776-5bf9-8573-72a51080c7detoolong";
+ uint8_t expected_uuid[16] = { 0x65, 0xfc, 0xc6, 0x97, 0x07, 0x76, 0x5b, 0xf9,
+ 0x85, 0x73, 0x72, 0xa5, 0x10, 0x80, 0xc7, 0xde };
+
+ bool r;
+
+ r = l_uuid_from_string(string_uuid, uuid);
+ assert(r);
+
+ assert(!memcmp(uuid, expected_uuid, sizeof(uuid)));
+}
+
+static void test_from_string_invalid_variant(const void *data)
+{
+ uint8_t uuid[16];
+ const char *string_uuid = "65fcc697-0776-5bf9-c573-72a51080c7de";
+ bool r;
+
+ r = l_uuid_from_string(string_uuid, uuid);
+ assert(!r);
+}
+
+static void test_from_string_invalid_hex(const void *data)
+{
+ uint8_t uuid[16];
+ const char *string_uuid = "65fcc697-this-isno-tava-lidhexstring";
+ bool r;
+
+ r = l_uuid_from_string(string_uuid, uuid);
+ assert(!r);
+}
+
+static void test_from_string(const void *data)
+{
+ uint8_t uuid[16];
+ bool r;
+ const char *string_uuid = "65fcc697-0776-5bf9-8573-72a51080c7de";
+ uint8_t expected_uuid[16] = { 0x65, 0xfc, 0xc6, 0x97, 0x07, 0x76, 0x5b, 0xf9,
+ 0x85, 0x73, 0x72, 0xa5, 0x10, 0x80, 0xc7, 0xde };
+
+ r = l_uuid_from_string(string_uuid, uuid);
+ assert(r);
+
+ assert(!memcmp(uuid, expected_uuid, sizeof(uuid)));
+}
+
int main(int argc, char *argv[])
{
l_test_init(&argc, &argv);
@@ -169,6 +228,11 @@ int main(int argc, char *argv[])
l_test_add("/uuid/v5", test_v5, NULL);
l_test_add("/uuid/to string", test_to_string, NULL);
+ l_test_add("/uuid/from string", test_from_string, NULL);
+ l_test_add("/uuid/from string/too short", test_from_string_too_short, NULL);
+ l_test_add("/uuid/from string/too long", test_from_string_too_long, NULL);
+ l_test_add("/uuid/from string/invalid variant", test_from_string_invalid_variant, NULL);
+ l_test_add("/uuid/from string/invalid hex", test_from_string_invalid_hex, NULL);
return l_test_run();
--
2.19.1
3 years, 4 months
[PATCH] ecc: fix potential buffer overrun
by James Prestwood
l_ecc_curve_get_[tls|ike]_group were iterating over curves and assuming
a NULL terminated list which was not the case. This never got caught
because IWD played nice and only got groups it already new existed.
This was changed to iterate over L_ARRAY_SIZE(curves).
---
ell/ecc.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/ell/ecc.c b/ell/ecc.c
index eeb12dd..88c8373 100644
--- a/ell/ecc.c
+++ b/ell/ecc.c
@@ -134,9 +134,9 @@ LIB_EXPORT size_t l_ecc_curve_get_scalar_bytes(const struct l_ecc_curve *curve)
LIB_EXPORT const struct l_ecc_curve *l_ecc_curve_get_ike_group(
unsigned int group)
{
- int i;
+ unsigned int i;
- for (i = 0; curves[i]; i++) {
+ for (i = 0; i < L_ARRAY_SIZE(curves); i++) {
if (curves[i]->ike_group == group)
return curves[i];
}
@@ -147,9 +147,9 @@ LIB_EXPORT const struct l_ecc_curve *l_ecc_curve_get_ike_group(
LIB_EXPORT const struct l_ecc_curve *l_ecc_curve_get_tls_group(
unsigned int group)
{
- int i;
+ unsigned int i;
- for (i = 0; curves[i]; i++) {
+ for (i = 0; i < L_ARRAY_SIZE(curves); i++) {
if (curves[i]->tls_group == group)
return curves[i];
}
--
2.17.1
3 years, 4 months