summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-02-26 13:46:58 +0100
committerLennart Poettering <lennart@poettering.net>2018-02-26 15:52:16 +0100
commit09b9348e82379d506f279a6a6b2c1032188f140a (patch)
tree1bd37220b6aab3b60a257cf7789ff094b0c39b40
parentf2e3f3695052ce59d119758d02bec9b1021731bd (diff)
downloadsystemd-09b9348e82379d506f279a6a6b2c1032188f140a.tar.gz
systemd-09b9348e82379d506f279a6a6b2c1032188f140a.tar.bz2
systemd-09b9348e82379d506f279a6a6b2c1032188f140a.zip
khash: try to detect broken AF_ALG support in centos kernels
Fixes: #8278
-rw-r--r--src/basic/khash.c67
-rw-r--r--src/basic/khash.h2
-rw-r--r--src/test/test-hash.c9
-rw-r--r--src/test/test-id128.c4
4 files changed, 77 insertions, 5 deletions
diff --git a/src/basic/khash.c b/src/basic/khash.c
index 694210512c..6463faf3e1 100644
--- a/src/basic/khash.c
+++ b/src/basic/khash.c
@@ -43,6 +43,66 @@ struct khash {
bool digest_valid;
};
+int khash_supported(void) {
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_alg alg;
+ } sa = {
+ .alg.salg_family = AF_ALG,
+ .alg.salg_type = "hash",
+ .alg.salg_name = "sha256", /* a very common algorithm */
+ };
+
+ static int cached = -1;
+
+ if (cached < 0) {
+ _cleanup_close_ int fd1 = -1, fd2 = -1;
+ uint8_t buf[LONGEST_DIGEST+1];
+
+ fd1 = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
+ if (fd1 < 0) {
+ /* The kernel returns EAFNOSUPPORT if AF_ALG is not supported at all */
+ if (IN_SET(errno, EAFNOSUPPORT, EOPNOTSUPP))
+ return (cached = false);
+
+ return -errno;
+ }
+
+ if (bind(fd1, &sa.sa, sizeof(sa)) < 0) {
+ /* The kernel returns ENOENT if the selected algorithm is not supported at all. We use a check
+ * for SHA256 as a proxy for whether the whole API is supported at all. After all it's one of
+ * the most common hash functions, and if it isn't supported, that's ample indication that
+ * something is really off. */
+
+ if (IN_SET(errno, ENOENT, EOPNOTSUPP))
+ return (cached = false);
+
+ return -errno;
+ }
+
+ fd2 = accept4(fd1, NULL, 0, SOCK_CLOEXEC);
+ if (fd2 < 0) {
+ if (errno == EOPNOTSUPP)
+ return (cached = false);
+
+ return -errno;
+ }
+
+ if (recv(fd2, buf, sizeof(buf), 0) < 0) {
+ /* On some kernels we get ENOKEY for non-keyed hash functions (such as sha256), let's refuse
+ * using the API in those cases, since the kernel is
+ * broken. https://github.com/systemd/systemd/issues/8278 */
+
+ if (IN_SET(errno, ENOKEY, EOPNOTSUPP))
+ return (cached = false);
+ }
+
+ cached = true;
+ }
+
+ return cached;
+}
+
int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
union {
struct sockaddr sa;
@@ -54,6 +114,7 @@ int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size
_cleanup_(khash_unrefp) khash *h = NULL;
_cleanup_close_ int fd = -1;
+ int supported;
ssize_t n;
assert(ret);
@@ -67,6 +128,12 @@ int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size
if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
return -EOPNOTSUPP;
+ supported = khash_supported();
+ if (supported < 0)
+ return supported;
+ if (supported == 0)
+ return -EOPNOTSUPP;
+
fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
diff --git a/src/basic/khash.h b/src/basic/khash.h
index 7041d39993..e9c41a3f20 100644
--- a/src/basic/khash.h
+++ b/src/basic/khash.h
@@ -28,6 +28,8 @@
typedef struct khash khash;
+int khash_supported(void);
+
/* For plain hash functions. Hash functions commonly supported on today's kernels are: crc32c, crct10dif, crc32,
* sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more. */
int khash_new(khash **ret, const char *algorithm);
diff --git a/src/test/test-hash.c b/src/test/test-hash.c
index 0366727476..d84a6a8859 100644
--- a/src/test/test-hash.c
+++ b/src/test/test-hash.c
@@ -35,12 +35,15 @@ int main(int argc, char *argv[]) {
assert_se(khash_new(&h, NULL) == -EINVAL);
assert_se(khash_new(&h, "") == -EINVAL);
- r = khash_new(&h, "foobar");
- if (r == -EAFNOSUPPORT) {
+
+ r = khash_supported();
+ assert_se(r >= 0);
+ if (r == 0) {
puts("khash not supported on this kernel, skipping");
return EXIT_TEST_SKIP;
}
- assert_se(r == -EOPNOTSUPP);
+
+ assert_se(khash_new(&h, "foobar") == -EOPNOTSUPP); /* undefined hash function */
assert_se(khash_new(&h, "sha256") >= 0);
assert_se(khash_get_size(h) == 32);
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index b7fca1540c..dba7283769 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -156,9 +156,9 @@ int main(int argc, char *argv[]) {
assert_se(sd_id128_equal(id, id2));
r = sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id);
- if (r == -EAFNOSUPPORT) {
+ if (r == -EOPNOTSUPP)
log_info("khash not supported on this kernel, skipping sd_id128_get_machine_app_specific() checks");
- } else {
+ else {
assert_se(r >= 0);
assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
assert_se(sd_id128_equal(id, id2));