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 | } |