| File: | build-scan/../src/basic/khash.c |
| Warning: | line 141, column 25 Potential leak of memory pointed to by 'h' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <linux1/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_DIGEST128 128 | |||
| 19 | ||||
| 20 | struct khash { | |||
| 21 | int fd; | |||
| 22 | char *algorithm; | |||
| 23 | uint8_t digest[LONGEST_DIGEST128+1]; | |||
| 24 | size_t digest_size; | |||
| 25 | bool_Bool digest_valid; | |||
| 26 | }; | |||
| 27 | ||||
| 28 | int khash_supported(void) { | |||
| 29 | static const union { | |||
| 30 | struct sockaddr sa; | |||
| 31 | struct sockaddr_alg alg; | |||
| 32 | } sa = { | |||
| 33 | .alg.salg_family = AF_ALG38, | |||
| 34 | .alg.salg_type = "hash", | |||
| 35 | .alg.salg_name = "sha256", /* a very common algorithm */ | |||
| 36 | }; | |||
| 37 | ||||
| 38 | static int cached = -1; | |||
| 39 | ||||
| 40 | if (cached < 0) { | |||
| 41 | _cleanup_close___attribute__((cleanup(closep))) int fd1 = -1, fd2 = -1; | |||
| 42 | uint8_t buf[LONGEST_DIGEST128+1]; | |||
| 43 | ||||
| 44 | fd1 = socket(AF_ALG38, SOCK_SEQPACKETSOCK_SEQPACKET|SOCK_CLOEXECSOCK_CLOEXEC, 0); | |||
| 45 | if (fd1 < 0) { | |||
| 46 | /* The kernel returns EAFNOSUPPORT if AF_ALG is not supported at all */ | |||
| 47 | if (IN_SET(errno, EAFNOSUPPORT, EOPNOTSUPP)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){97, 95})/sizeof(int)]; switch((*__errno_location ())) { case 97: case 95: _found = 1; break; default: break; } _found; })) | |||
| 48 | return (cached = false0); | |||
| 49 | ||||
| 50 | return -errno(*__errno_location ()); | |||
| 51 | } | |||
| 52 | ||||
| 53 | 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 | if (IN_SET(errno, ENOENT, EOPNOTSUPP)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){2, 95})/sizeof(int)]; switch((*__errno_location ())) { case 2: case 95: _found = 1; break; default: break; } _found; })) | |||
| 60 | return (cached = false0); | |||
| 61 | ||||
| 62 | return -errno(*__errno_location ()); | |||
| 63 | } | |||
| 64 | ||||
| 65 | fd2 = accept4(fd1, NULL((void*)0), 0, SOCK_CLOEXECSOCK_CLOEXEC); | |||
| 66 | if (fd2 < 0) { | |||
| 67 | if (errno(*__errno_location ()) == EOPNOTSUPP95) | |||
| 68 | return (cached = false0); | |||
| 69 | ||||
| 70 | return -errno(*__errno_location ()); | |||
| 71 | } | |||
| 72 | ||||
| 73 | 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 | if (IN_SET(errno, ENOKEY, EOPNOTSUPP)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){126, 95})/sizeof(int)]; switch((*__errno_location ())) { case 126: case 95: _found = 1; break; default: break; } _found; })) | |||
| 79 | return (cached = false0); | |||
| 80 | } | |||
| 81 | ||||
| 82 | cached = true1; | |||
| 83 | } | |||
| 84 | ||||
| 85 | return cached; | |||
| 86 | } | |||
| 87 | ||||
| 88 | 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 | } sa = { | |||
| 93 | .alg.salg_family = AF_ALG38, | |||
| 94 | .alg.salg_type = "hash", | |||
| 95 | }; | |||
| 96 | ||||
| 97 | _cleanup_(khash_unrefp)__attribute__((cleanup(khash_unrefp))) khash *h = NULL((void*)0); | |||
| 98 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | |||
| 99 | int supported; | |||
| 100 | ssize_t n; | |||
| 101 | ||||
| 102 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 102, __PRETTY_FUNCTION__ ); } while (0); | |||
| 103 | assert(key || key_size == 0)do { if ((__builtin_expect(!!(!(key || key_size == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("key || key_size == 0"), "../src/basic/khash.c" , 103, __PRETTY_FUNCTION__); } while (0); | |||
| 104 | ||||
| 105 | /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */ | |||
| 106 | if (isempty(algorithm)) | |||
| 107 | return -EINVAL22; | |||
| 108 | ||||
| 109 | /* Overly long hash algorithm names we definitely do not support */ | |||
| 110 | if (strlen(algorithm) >= sizeof(sa.alg.salg_name)) | |||
| 111 | return -EOPNOTSUPP95; | |||
| 112 | ||||
| 113 | supported = khash_supported(); | |||
| 114 | if (supported < 0) | |||
| 115 | return supported; | |||
| 116 | if (supported == 0) | |||
| 117 | return -EOPNOTSUPP95; | |||
| 118 | ||||
| 119 | fd = socket(AF_ALG38, SOCK_SEQPACKETSOCK_SEQPACKET|SOCK_CLOEXECSOCK_CLOEXEC, 0); | |||
| 120 | if (fd < 0) | |||
| 121 | return -errno(*__errno_location ()); | |||
| 122 | ||||
| 123 | strcpy((char*) sa.alg.salg_name, algorithm); | |||
| 124 | if (bind(fd, &sa.sa, sizeof(sa)) < 0) { | |||
| 125 | if (errno(*__errno_location ()) == ENOENT2) | |||
| 126 | return -EOPNOTSUPP95; | |||
| 127 | return -errno(*__errno_location ()); | |||
| 128 | } | |||
| 129 | ||||
| 130 | if (key
| |||
| 131 | if (setsockopt(fd, SOL_ALG279, ALG_SET_KEY1, key, key_size) < 0) | |||
| 132 | return -errno(*__errno_location ()); | |||
| 133 | } | |||
| 134 | ||||
| 135 | h = new0(khash, 1)((khash*) calloc((1), sizeof(khash))); | |||
| 136 | if (!h) | |||
| 137 | return -ENOMEM12; | |||
| 138 | ||||
| 139 | h->fd = accept4(fd, NULL((void*)0), 0, SOCK_CLOEXECSOCK_CLOEXEC); | |||
| 140 | if (h->fd < 0) | |||
| 141 | return -errno(*__errno_location ()); | |||
| ||||
| 142 | ||||
| 143 | h->algorithm = strdup(algorithm); | |||
| 144 | if (!h->algorithm) | |||
| 145 | return -ENOMEM12; | |||
| 146 | ||||
| 147 | /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */ | |||
| 148 | (void) send(h->fd, NULL((void*)0), 0, 0); | |||
| 149 | ||||
| 150 | /* Figure out the digest size */ | |||
| 151 | n = recv(h->fd, h->digest, sizeof(h->digest), 0); | |||
| 152 | if (n < 0) | |||
| 153 | return -errno(*__errno_location ()); | |||
| 154 | if (n >= LONGEST_DIGEST128) /* longer than what we expected? If so, we don't support this */ | |||
| 155 | return -EOPNOTSUPP95; | |||
| 156 | ||||
| 157 | h->digest_size = (size_t) n; | |||
| 158 | h->digest_valid = true1; | |||
| 159 | ||||
| 160 | /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */ | |||
| 161 | (void) send(h->fd, NULL((void*)0), 0, 0); | |||
| 162 | ||||
| 163 | *ret = h; | |||
| 164 | h = NULL((void*)0); | |||
| 165 | ||||
| 166 | return 0; | |||
| 167 | } | |||
| 168 | ||||
| 169 | int khash_new(khash **ret, const char *algorithm) { | |||
| 170 | return khash_new_with_key(ret, algorithm, NULL((void*)0), 0); | |||
| ||||
| 171 | } | |||
| 172 | ||||
| 173 | khash* khash_unref(khash *h) { | |||
| 174 | if (!h) | |||
| 175 | return NULL((void*)0); | |||
| 176 | ||||
| 177 | safe_close(h->fd); | |||
| 178 | free(h->algorithm); | |||
| 179 | return mfree(h); | |||
| 180 | } | |||
| 181 | ||||
| 182 | int khash_dup(khash *h, khash **ret) { | |||
| 183 | _cleanup_(khash_unrefp)__attribute__((cleanup(khash_unrefp))) khash *copy = NULL((void*)0); | |||
| 184 | ||||
| 185 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 185, __PRETTY_FUNCTION__ ); } while (0); | |||
| 186 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 186, __PRETTY_FUNCTION__ ); } while (0); | |||
| 187 | ||||
| 188 | copy = newdup(khash, h, 1)((khash*) memdup_multiply(h, sizeof(khash), (1))); | |||
| 189 | if (!copy) | |||
| 190 | return -ENOMEM12; | |||
| 191 | ||||
| 192 | copy->fd = -1; | |||
| 193 | copy->algorithm = strdup(h->algorithm); | |||
| 194 | if (!copy->algorithm) | |||
| 195 | return -ENOMEM12; | |||
| 196 | ||||
| 197 | copy->fd = accept4(h->fd, NULL((void*)0), 0, SOCK_CLOEXECSOCK_CLOEXEC); | |||
| 198 | if (copy->fd < 0) | |||
| 199 | return -errno(*__errno_location ()); | |||
| 200 | ||||
| 201 | *ret = TAKE_PTR(copy)({ typeof(copy) _ptr_ = (copy); (copy) = ((void*)0); _ptr_; } ); | |||
| 202 | ||||
| 203 | return 0; | |||
| 204 | } | |||
| 205 | ||||
| 206 | const char *khash_get_algorithm(khash *h) { | |||
| 207 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 207, __PRETTY_FUNCTION__ ); } while (0); | |||
| 208 | ||||
| 209 | return h->algorithm; | |||
| 210 | } | |||
| 211 | ||||
| 212 | size_t khash_get_size(khash *h) { | |||
| 213 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 213, __PRETTY_FUNCTION__ ); } while (0); | |||
| 214 | ||||
| 215 | return h->digest_size; | |||
| 216 | } | |||
| 217 | ||||
| 218 | int khash_reset(khash *h) { | |||
| 219 | ssize_t n; | |||
| 220 | ||||
| 221 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 221, __PRETTY_FUNCTION__ ); } while (0); | |||
| 222 | ||||
| 223 | n = send(h->fd, NULL((void*)0), 0, 0); | |||
| 224 | if (n < 0) | |||
| 225 | return -errno(*__errno_location ()); | |||
| 226 | ||||
| 227 | h->digest_valid = false0; | |||
| 228 | ||||
| 229 | return 0; | |||
| 230 | } | |||
| 231 | ||||
| 232 | int khash_put(khash *h, const void *buffer, size_t size) { | |||
| 233 | ssize_t n; | |||
| 234 | ||||
| 235 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 235, __PRETTY_FUNCTION__ ); } while (0); | |||
| 236 | assert(buffer || size == 0)do { if ((__builtin_expect(!!(!(buffer || size == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buffer || size == 0"), "../src/basic/khash.c" , 236, __PRETTY_FUNCTION__); } while (0); | |||
| 237 | ||||
| 238 | if (size <= 0) | |||
| 239 | return 0; | |||
| 240 | ||||
| 241 | n = send(h->fd, buffer, size, MSG_MOREMSG_MORE); | |||
| 242 | if (n < 0) | |||
| 243 | return -errno(*__errno_location ()); | |||
| 244 | ||||
| 245 | h->digest_valid = false0; | |||
| 246 | ||||
| 247 | return 0; | |||
| 248 | } | |||
| 249 | ||||
| 250 | int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) { | |||
| 251 | struct msghdr mh = { | |||
| 252 | mh.msg_iov = (struct iovec*) iovec, | |||
| 253 | mh.msg_iovlen = n, | |||
| 254 | }; | |||
| 255 | ssize_t k; | |||
| 256 | ||||
| 257 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 257, __PRETTY_FUNCTION__ ); } while (0); | |||
| 258 | assert(iovec || n == 0)do { if ((__builtin_expect(!!(!(iovec || n == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("iovec || n == 0"), "../src/basic/khash.c" , 258, __PRETTY_FUNCTION__); } while (0); | |||
| 259 | ||||
| 260 | if (n <= 0) | |||
| 261 | return 0; | |||
| 262 | ||||
| 263 | k = sendmsg(h->fd, &mh, MSG_MOREMSG_MORE); | |||
| 264 | if (k < 0) | |||
| 265 | return -errno(*__errno_location ()); | |||
| 266 | ||||
| 267 | h->digest_valid = false0; | |||
| 268 | ||||
| 269 | return 0; | |||
| 270 | } | |||
| 271 | ||||
| 272 | static int retrieve_digest(khash *h) { | |||
| 273 | ssize_t n; | |||
| 274 | ||||
| 275 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 275, __PRETTY_FUNCTION__ ); } while (0); | |||
| 276 | ||||
| 277 | if (h->digest_valid) | |||
| 278 | return 0; | |||
| 279 | ||||
| 280 | n = recv(h->fd, h->digest, h->digest_size, 0); | |||
| 281 | if (n < 0) | |||
| 282 | return n; | |||
| 283 | if ((size_t) n != h->digest_size) /* digest size changed? */ | |||
| 284 | return -EIO5; | |||
| 285 | ||||
| 286 | h->digest_valid = true1; | |||
| 287 | ||||
| 288 | return 0; | |||
| 289 | } | |||
| 290 | ||||
| 291 | int khash_digest_data(khash *h, const void **ret) { | |||
| 292 | int r; | |||
| 293 | ||||
| 294 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 294, __PRETTY_FUNCTION__ ); } while (0); | |||
| 295 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 295, __PRETTY_FUNCTION__ ); } while (0); | |||
| 296 | ||||
| 297 | r = retrieve_digest(h); | |||
| 298 | if (r < 0) | |||
| 299 | return r; | |||
| 300 | ||||
| 301 | *ret = h->digest; | |||
| 302 | return 0; | |||
| 303 | } | |||
| 304 | ||||
| 305 | int khash_digest_string(khash *h, char **ret) { | |||
| 306 | int r; | |||
| 307 | char *p; | |||
| 308 | ||||
| 309 | assert(h)do { if ((__builtin_expect(!!(!(h)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("h"), "../src/basic/khash.c", 309, __PRETTY_FUNCTION__ ); } while (0); | |||
| 310 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/khash.c", 310, __PRETTY_FUNCTION__ ); } while (0); | |||
| 311 | ||||
| 312 | r = retrieve_digest(h); | |||
| 313 | if (r < 0) | |||
| 314 | return r; | |||
| 315 | ||||
| 316 | p = hexmem(h->digest, h->digest_size); | |||
| 317 | if (!p) | |||
| 318 | return -ENOMEM12; | |||
| 319 | ||||
| 320 | *ret = p; | |||
| 321 | return 0; | |||
| 322 | } |