LCOV - code coverage report
Current view: top level - basic - khash.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 106 159 66.7 %
Date: 2019-08-22 15:41:25 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <linux/if_alg.h>
       4             : #include <stdbool.h>
       5             : #include <sys/socket.h>
       6             : 
       7             : #include "alloc-util.h"
       8             : #include "fd-util.h"
       9             : #include "hexdecoct.h"
      10             : #include "khash.h"
      11             : #include "macro.h"
      12             : #include "missing.h"
      13             : #include "string-util.h"
      14             : #include "util.h"
      15             : 
      16             : /* On current kernels the maximum digest (according to "grep digestsize /proc/crypto | sort -u") is actually 32, but
      17             :  * let's add some extra room, the few wasted bytes don't really matter... */
      18             : #define LONGEST_DIGEST 128
      19             : 
      20             : struct khash {
      21             :         int fd;
      22             :         char *algorithm;
      23             :         uint8_t digest[LONGEST_DIGEST+1];
      24             :         size_t digest_size;
      25             :         bool digest_valid;
      26             : };
      27             : 
      28           7 : int khash_supported(void) {
      29             :         static const union {
      30             :                 struct sockaddr sa;
      31             :                 struct sockaddr_alg alg;
      32             :         } sa = {
      33             :                 .alg.salg_family = AF_ALG,
      34             :                 .alg.salg_type = "hash",
      35             :                 .alg.salg_name = "sha256", /* a very common algorithm */
      36             :         };
      37             : 
      38             :         static int cached = -1;
      39             : 
      40           7 :         if (cached < 0) {
      41           2 :                 _cleanup_close_ int fd1 = -1, fd2 = -1;
      42             :                 uint8_t buf[LONGEST_DIGEST+1];
      43             : 
      44           2 :                 fd1 = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
      45           2 :                 if (fd1 < 0) {
      46             :                         /* The kernel returns EAFNOSUPPORT if AF_ALG is not supported at all */
      47           0 :                         if (IN_SET(errno, EAFNOSUPPORT, EOPNOTSUPP))
      48           0 :                                 return (cached = false);
      49             : 
      50           0 :                         return -errno;
      51             :                 }
      52             : 
      53           2 :                 if (bind(fd1, &sa.sa, sizeof(sa)) < 0) {
      54             :                         /* The kernel returns ENOENT if the selected algorithm is not supported at all. We use a check
      55             :                          * for SHA256 as a proxy for whether the whole API is supported at all. After all it's one of
      56             :                          * the most common hash functions, and if it isn't supported, that's ample indication that
      57             :                          * something is really off. */
      58             : 
      59           0 :                         if (IN_SET(errno, ENOENT, EOPNOTSUPP))
      60           0 :                                 return (cached = false);
      61             : 
      62           0 :                         return -errno;
      63             :                 }
      64             : 
      65           2 :                 fd2 = accept4(fd1, NULL, 0, SOCK_CLOEXEC);
      66           2 :                 if (fd2 < 0) {
      67           0 :                         if (errno == EOPNOTSUPP)
      68           0 :                                 return (cached = false);
      69             : 
      70           0 :                         return -errno;
      71             :                 }
      72             : 
      73           2 :                 if (recv(fd2, buf, sizeof(buf), 0) < 0) {
      74             :                         /* On some kernels we get ENOKEY for non-keyed hash functions (such as sha256), let's refuse
      75             :                          * using the API in those cases, since the kernel is
      76             :                          * broken. https://github.com/systemd/systemd/issues/8278 */
      77             : 
      78           0 :                         if (IN_SET(errno, ENOKEY, EOPNOTSUPP))
      79           0 :                                 return (cached = false);
      80             :                 }
      81             : 
      82           2 :                 cached = true;
      83             :         }
      84             : 
      85           7 :         return cached;
      86             : }
      87             : 
      88           8 : int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
      89             :         union {
      90             :                 struct sockaddr sa;
      91             :                 struct sockaddr_alg alg;
      92           8 :         } sa = {
      93             :                 .alg.salg_family = AF_ALG,
      94             :                 .alg.salg_type = "hash",
      95             :         };
      96             : 
      97           8 :         _cleanup_(khash_unrefp) khash *h = NULL;
      98           8 :         _cleanup_close_ int fd = -1;
      99             :         int supported;
     100             :         ssize_t n;
     101             : 
     102           8 :         assert(ret);
     103           8 :         assert(key || key_size == 0);
     104             : 
     105             :         /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
     106           8 :         if (isempty(algorithm))
     107           2 :                 return -EINVAL;
     108             : 
     109             :         /* Overly long hash algorithm names we definitely do not support */
     110           6 :         if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
     111           0 :                 return -EOPNOTSUPP;
     112             : 
     113           6 :         supported = khash_supported();
     114           6 :         if (supported < 0)
     115           0 :                 return supported;
     116           6 :         if (supported == 0)
     117           0 :                 return -EOPNOTSUPP;
     118             : 
     119           6 :         fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
     120           6 :         if (fd < 0)
     121           0 :                 return -errno;
     122             : 
     123           6 :         strcpy((char*) sa.alg.salg_name, algorithm);
     124           6 :         if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
     125           1 :                 if (errno == ENOENT)
     126           1 :                         return -EOPNOTSUPP;
     127           0 :                 return -errno;
     128             :         }
     129             : 
     130           5 :         if (key) {
     131           4 :                 if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0)
     132           0 :                         return -errno;
     133             :         }
     134             : 
     135           5 :         h = new0(khash, 1);
     136           5 :         if (!h)
     137           0 :                 return -ENOMEM;
     138             : 
     139           5 :         h->fd = accept4(fd, NULL, 0, SOCK_CLOEXEC);
     140           5 :         if (h->fd < 0)
     141           0 :                 return -errno;
     142             : 
     143           5 :         h->algorithm = strdup(algorithm);
     144           5 :         if (!h->algorithm)
     145           0 :                 return -ENOMEM;
     146             : 
     147             :         /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
     148           5 :         (void) send(h->fd, NULL, 0, 0);
     149             : 
     150             :         /* Figure out the digest size */
     151           5 :         n = recv(h->fd, h->digest, sizeof(h->digest), 0);
     152           5 :         if (n < 0)
     153           0 :                 return -errno;
     154           5 :         if (n >= LONGEST_DIGEST) /* longer than what we expected? If so, we don't support this */
     155           0 :                 return -EOPNOTSUPP;
     156             : 
     157           5 :         h->digest_size = (size_t) n;
     158           5 :         h->digest_valid = true;
     159             : 
     160             :         /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
     161           5 :         (void) send(h->fd, NULL, 0, 0);
     162             : 
     163           5 :         *ret = h;
     164           5 :         h = NULL;
     165             : 
     166           5 :         return 0;
     167             : }
     168             : 
     169           4 : int khash_new(khash **ret, const char *algorithm) {
     170           4 :         return khash_new_with_key(ret, algorithm, NULL, 0);
     171             : }
     172             : 
     173           6 : khash* khash_unref(khash *h) {
     174           6 :         if (!h)
     175           0 :                 return NULL;
     176             : 
     177           6 :         safe_close(h->fd);
     178           6 :         free(h->algorithm);
     179           6 :         return mfree(h);
     180             : }
     181             : 
     182           1 : int khash_dup(khash *h, khash **ret) {
     183           1 :         _cleanup_(khash_unrefp) khash *copy = NULL;
     184             : 
     185           1 :         assert(h);
     186           1 :         assert(ret);
     187             : 
     188           1 :         copy = newdup(khash, h, 1);
     189           1 :         if (!copy)
     190           0 :                 return -ENOMEM;
     191             : 
     192           1 :         copy->fd = -1;
     193           1 :         copy->algorithm = strdup(h->algorithm);
     194           1 :         if (!copy->algorithm)
     195           0 :                 return -ENOMEM;
     196             : 
     197           1 :         copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
     198           1 :         if (copy->fd < 0)
     199           0 :                 return -errno;
     200             : 
     201           1 :         *ret = TAKE_PTR(copy);
     202             : 
     203           1 :         return 0;
     204             : }
     205             : 
     206           2 : const char *khash_get_algorithm(khash *h) {
     207           2 :         assert(h);
     208             : 
     209           2 :         return h->algorithm;
     210             : }
     211             : 
     212           5 : size_t khash_get_size(khash *h) {
     213           5 :         assert(h);
     214             : 
     215           5 :         return h->digest_size;
     216             : }
     217             : 
     218           0 : int khash_reset(khash *h) {
     219             :         ssize_t n;
     220             : 
     221           0 :         assert(h);
     222             : 
     223           0 :         n = send(h->fd, NULL, 0, 0);
     224           0 :         if (n < 0)
     225           0 :                 return -errno;
     226             : 
     227           0 :         h->digest_valid = false;
     228             : 
     229           0 :         return 0;
     230             : }
     231             : 
     232           9 : int khash_put(khash *h, const void *buffer, size_t size) {
     233             :         ssize_t n;
     234             : 
     235           9 :         assert(h);
     236           9 :         assert(buffer || size == 0);
     237             : 
     238           9 :         if (size <= 0)
     239           0 :                 return 0;
     240             : 
     241           9 :         n = send(h->fd, buffer, size, MSG_MORE);
     242           9 :         if (n < 0)
     243           0 :                 return -errno;
     244             : 
     245           9 :         h->digest_valid = false;
     246             : 
     247           9 :         return 0;
     248             : }
     249             : 
     250           0 : int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
     251           0 :         struct msghdr mh = {
     252           0 :                 mh.msg_iov = (struct iovec*) iovec,
     253           0 :                 mh.msg_iovlen = n,
     254             :         };
     255             :         ssize_t k;
     256             : 
     257           0 :         assert(h);
     258           0 :         assert(iovec || n == 0);
     259             : 
     260           0 :         if (n <= 0)
     261           0 :                 return 0;
     262             : 
     263           0 :         k = sendmsg(h->fd, &mh, MSG_MORE);
     264           0 :         if (k < 0)
     265           0 :                 return -errno;
     266             : 
     267           0 :         h->digest_valid = false;
     268             : 
     269           0 :         return 0;
     270             : }
     271             : 
     272          10 : static int retrieve_digest(khash *h) {
     273             :         ssize_t n;
     274             : 
     275          10 :         assert(h);
     276             : 
     277          10 :         if (h->digest_valid)
     278           2 :                 return 0;
     279             : 
     280           8 :         n = recv(h->fd, h->digest, h->digest_size, 0);
     281           8 :         if (n < 0)
     282           0 :                 return n;
     283           8 :         if ((size_t) n != h->digest_size) /* digest size changed? */
     284           0 :                 return -EIO;
     285             : 
     286           8 :         h->digest_valid = true;
     287             : 
     288           8 :         return 0;
     289             : }
     290             : 
     291           3 : int khash_digest_data(khash *h, const void **ret) {
     292             :         int r;
     293             : 
     294           3 :         assert(h);
     295           3 :         assert(ret);
     296             : 
     297           3 :         r = retrieve_digest(h);
     298           3 :         if (r < 0)
     299           0 :                 return r;
     300             : 
     301           3 :         *ret = h->digest;
     302           3 :         return 0;
     303             : }
     304             : 
     305           7 : int khash_digest_string(khash *h, char **ret) {
     306             :         int r;
     307             :         char *p;
     308             : 
     309           7 :         assert(h);
     310           7 :         assert(ret);
     311             : 
     312           7 :         r = retrieve_digest(h);
     313           7 :         if (r < 0)
     314           0 :                 return r;
     315             : 
     316           7 :         p = hexmem(h->digest, h->digest_size);
     317           7 :         if (!p)
     318           0 :                 return -ENOMEM;
     319             : 
     320           7 :         *ret = p;
     321           7 :         return 0;
     322             : }

Generated by: LCOV version 1.14