1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <linux/if_alg.h>
23 #include <sys/socket.h>
25 #include "alloc-util.h"
27 #include "hexdecoct.h"
31 #include "string-util.h"
34 /* On current kernels the maximum digest (according to "grep digestsize /proc/crypto | sort -u") is actually 32, but
35 * let's add some extra room, the few wasted bytes don't really matter... */
36 #define LONGEST_DIGEST 128
41 uint8_t digest[LONGEST_DIGEST+1];
46 int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
49 struct sockaddr_alg alg;
51 .alg.salg_family = AF_ALG,
52 .alg.salg_type = "hash",
55 _cleanup_(khash_unrefp) khash *h = NULL;
56 _cleanup_close_ int fd = -1;
60 assert(key || key_size == 0);
62 /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
63 if (isempty(algorithm))
66 /* Overly long hash algorithm names we definitely do not support */
67 if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
70 fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
74 strcpy((char*) sa.alg.salg_name, algorithm);
75 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
82 if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0)
90 h->fd = accept4(fd, NULL, 0, SOCK_CLOEXEC);
94 h->algorithm = strdup(algorithm);
98 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
99 (void) send(h->fd, NULL, 0, 0);
101 /* Figure out the digest size */
102 n = recv(h->fd, h->digest, sizeof(h->digest), 0);
105 if (n >= LONGEST_DIGEST) /* longer than what we expected? If so, we don't support this */
108 h->digest_size = (size_t) n;
109 h->digest_valid = true;
111 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
112 (void) send(h->fd, NULL, 0, 0);
120 int khash_new(khash **ret, const char *algorithm) {
121 return khash_new_with_key(ret, algorithm, NULL, 0);
124 khash* khash_unref(khash *h) {
133 int khash_dup(khash *h, khash **ret) {
134 _cleanup_(khash_unrefp) khash *copy = NULL;
139 copy = newdup(khash, h, 1);
144 copy->algorithm = strdup(h->algorithm);
145 if (!copy->algorithm)
148 copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
158 const char *khash_get_algorithm(khash *h) {
164 size_t khash_get_size(khash *h) {
167 return h->digest_size;
170 int khash_reset(khash *h) {
175 n = send(h->fd, NULL, 0, 0);
179 h->digest_valid = false;
184 int khash_put(khash *h, const void *buffer, size_t size) {
188 assert(buffer || size == 0);
193 n = send(h->fd, buffer, size, MSG_MORE);
197 h->digest_valid = false;
202 int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
204 mh.msg_iov = (struct iovec*) iovec,
210 assert(iovec || n == 0);
215 k = sendmsg(h->fd, &mh, MSG_MORE);
219 h->digest_valid = false;
224 static int retrieve_digest(khash *h) {
232 n = recv(h->fd, h->digest, h->digest_size, 0);
235 if ((size_t) n != h->digest_size) /* digest size changed? */
238 h->digest_valid = true;
243 int khash_digest_data(khash *h, const void **ret) {
249 r = retrieve_digest(h);
257 int khash_digest_string(khash *h, char **ret) {
264 r = retrieve_digest(h);
268 p = hexmem(h->digest, h->digest_size);