]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/khash.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <linux/if_alg.h>
5 #include <sys/socket.h>
7 #include "alloc-util.h"
13 #include "string-util.h"
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
23 uint8_t digest
[LONGEST_DIGEST
+1];
28 int khash_supported(void) {
31 struct sockaddr_alg alg
;
33 .alg
.salg_family
= AF_ALG
,
34 .alg
.salg_type
= "hash",
35 .alg
.salg_name
= "sha256", /* a very common algorithm */
38 static int cached
= -1;
41 _cleanup_close_
int fd1
= -1, fd2
= -1;
42 uint8_t buf
[LONGEST_DIGEST
+1];
44 fd1
= socket(AF_ALG
, SOCK_SEQPACKET
|SOCK_CLOEXEC
, 0);
46 /* The kernel returns EAFNOSUPPORT if AF_ALG is not supported at all */
47 if (IN_SET(errno
, EAFNOSUPPORT
, EOPNOTSUPP
))
48 return (cached
= false);
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. */
59 if (IN_SET(errno
, ENOENT
, EOPNOTSUPP
))
60 return (cached
= false);
65 fd2
= accept4(fd1
, NULL
, 0, SOCK_CLOEXEC
);
67 if (errno
== EOPNOTSUPP
)
68 return (cached
= false);
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 */
78 if (IN_SET(errno
, ENOKEY
, EOPNOTSUPP
))
79 return (cached
= false);
88 int khash_new_with_key(khash
**ret
, const char *algorithm
, const void *key
, size_t key_size
) {
91 struct sockaddr_alg alg
;
93 .alg
.salg_family
= AF_ALG
,
94 .alg
.salg_type
= "hash",
97 _cleanup_(khash_unrefp
) khash
*h
= NULL
;
98 _cleanup_close_
int fd
= -1;
103 assert(key
|| key_size
== 0);
105 /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
106 if (isempty(algorithm
))
109 /* Overly long hash algorithm names we definitely do not support */
110 if (strlen(algorithm
) >= sizeof(sa
.alg
.salg_name
))
113 supported
= khash_supported();
119 fd
= socket(AF_ALG
, SOCK_SEQPACKET
|SOCK_CLOEXEC
, 0);
123 strcpy((char*) sa
.alg
.salg_name
, algorithm
);
124 if (bind(fd
, &sa
.sa
, sizeof(sa
)) < 0) {
131 if (setsockopt(fd
, SOL_ALG
, ALG_SET_KEY
, key
, key_size
) < 0)
139 h
->fd
= accept4(fd
, NULL
, 0, SOCK_CLOEXEC
);
143 h
->algorithm
= strdup(algorithm
);
147 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
148 (void) send(h
->fd
, NULL
, 0, 0);
150 /* Figure out the digest size */
151 n
= recv(h
->fd
, h
->digest
, sizeof(h
->digest
), 0);
154 if (n
>= LONGEST_DIGEST
) /* longer than what we expected? If so, we don't support this */
157 h
->digest_size
= (size_t) n
;
158 h
->digest_valid
= true;
160 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
161 (void) send(h
->fd
, NULL
, 0, 0);
169 int khash_new(khash
**ret
, const char *algorithm
) {
170 return khash_new_with_key(ret
, algorithm
, NULL
, 0);
173 khash
* khash_unref(khash
*h
) {
182 int khash_dup(khash
*h
, khash
**ret
) {
183 _cleanup_(khash_unrefp
) khash
*copy
= NULL
;
188 copy
= newdup(khash
, h
, 1);
193 copy
->algorithm
= strdup(h
->algorithm
);
194 if (!copy
->algorithm
)
197 copy
->fd
= accept4(h
->fd
, NULL
, 0, SOCK_CLOEXEC
);
201 *ret
= TAKE_PTR(copy
);
206 const char *khash_get_algorithm(khash
*h
) {
212 size_t khash_get_size(khash
*h
) {
215 return h
->digest_size
;
218 int khash_reset(khash
*h
) {
223 n
= send(h
->fd
, NULL
, 0, 0);
227 h
->digest_valid
= false;
232 int khash_put(khash
*h
, const void *buffer
, size_t size
) {
236 assert(buffer
|| size
== 0);
241 n
= send(h
->fd
, buffer
, size
, MSG_MORE
);
245 h
->digest_valid
= false;
250 int khash_put_iovec(khash
*h
, const struct iovec
*iovec
, size_t n
) {
252 mh
.msg_iov
= (struct iovec
*) iovec
,
258 assert(iovec
|| n
== 0);
263 k
= sendmsg(h
->fd
, &mh
, MSG_MORE
);
267 h
->digest_valid
= false;
272 static int retrieve_digest(khash
*h
) {
280 n
= recv(h
->fd
, h
->digest
, h
->digest_size
, 0);
283 if ((size_t) n
!= h
->digest_size
) /* digest size changed? */
286 h
->digest_valid
= true;
291 int khash_digest_data(khash
*h
, const void **ret
) {
297 r
= retrieve_digest(h
);
305 int khash_digest_string(khash
*h
, char **ret
) {
312 r
= retrieve_digest(h
);
316 p
= hexmem(h
->digest
, h
->digest_size
);