diff options
author | Lennart Poettering <lennart@poettering.net> | 2018-02-26 13:46:58 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2018-02-26 15:52:16 +0100 |
commit | 09b9348e82379d506f279a6a6b2c1032188f140a (patch) | |
tree | 1bd37220b6aab3b60a257cf7789ff094b0c39b40 | |
parent | f2e3f3695052ce59d119758d02bec9b1021731bd (diff) | |
download | systemd-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.c | 67 | ||||
-rw-r--r-- | src/basic/khash.h | 2 | ||||
-rw-r--r-- | src/test/test-hash.c | 9 | ||||
-rw-r--r-- | src/test/test-id128.c | 4 |
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)); |