chiark / gitweb /
khash: try to detect broken AF_ALG support in centos kernels
authorLennart Poettering <lennart@poettering.net>
Mon, 26 Feb 2018 12:46:58 +0000 (13:46 +0100)
committerSven Eden <yamakuzure@gmx.net>
Wed, 30 May 2018 05:59:07 +0000 (07:59 +0200)
Fixes: #8278
src/basic/khash.c
src/basic/khash.h
src/test/test-hash.c
src/test/test-id128.c

index 694210512cf1bf29e04f91ad99a0635950408ae7..6463faf3e12ac72a5b9e3edb59765b2fdb1a2fce 100644 (file)
@@ -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;
index 7041d39993e5e036f238ec23fca1eec1bbda96a2..e9c41a3f2044f0d208cf25818170eb726bbdf5e6 100644 (file)
@@ -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);
index a08771abb70845742665207161281e87b0efc545..0d627a4257642c2774c11e3ee9a414c4c3516403 100644 (file)
@@ -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);
index b7fca1540cefbe702e2c95fcc1459daee8394109..dba7283769b81c7fff8c1374c0dee6b80de6a567 100644 (file)
@@ -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));