]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/khash.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / basic / khash.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
0fe5f3c5
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
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.
11
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.
16
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/>.
19***/
20
21#include <linux/if_alg.h>
22#include <stdbool.h>
23#include <sys/socket.h>
24
25#include "alloc-util.h"
26#include "fd-util.h"
27#include "hexdecoct.h"
28#include "khash.h"
29#include "macro.h"
30#include "missing.h"
31#include "string-util.h"
32#include "util.h"
33
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
37
38struct khash {
39 int fd;
40 char *algorithm;
41 uint8_t digest[LONGEST_DIGEST+1];
42 size_t digest_size;
43 bool digest_valid;
44};
45
46int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
47 union {
48 struct sockaddr sa;
49 struct sockaddr_alg alg;
50 } sa = {
51 .alg.salg_family = AF_ALG,
52 .alg.salg_type = "hash",
53 };
54
55 _cleanup_(khash_unrefp) khash *h = NULL;
56 _cleanup_close_ int fd = -1;
57 ssize_t n;
58
59 assert(ret);
60 assert(key || key_size == 0);
61
62 /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
63 if (isempty(algorithm))
64 return -EINVAL;
65
66 /* Overly long hash algorithm names we definitely do not support */
67 if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
68 return -EOPNOTSUPP;
69
70 fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
71 if (fd < 0)
72 return -errno;
73
74 strcpy((char*) sa.alg.salg_name, algorithm);
75 if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
76 if (errno == ENOENT)
77 return -EOPNOTSUPP;
78 return -errno;
79 }
80
81 if (key) {
82 if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0)
83 return -errno;
84 }
85
86 h = new0(khash, 1);
87 if (!h)
88 return -ENOMEM;
89
90 h->fd = accept4(fd, NULL, 0, SOCK_CLOEXEC);
91 if (h->fd < 0)
92 return -errno;
93
94 h->algorithm = strdup(algorithm);
95 if (!h->algorithm)
96 return -ENOMEM;
97
98 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
99 (void) send(h->fd, NULL, 0, 0);
100
101 /* Figure out the digest size */
102 n = recv(h->fd, h->digest, sizeof(h->digest), 0);
103 if (n < 0)
104 return -errno;
105 if (n >= LONGEST_DIGEST) /* longer than what we expected? If so, we don't support this */
106 return -EOPNOTSUPP;
107
108 h->digest_size = (size_t) n;
109 h->digest_valid = true;
110
111 /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
112 (void) send(h->fd, NULL, 0, 0);
113
114 *ret = h;
115 h = NULL;
116
117 return 0;
118}
119
120int khash_new(khash **ret, const char *algorithm) {
121 return khash_new_with_key(ret, algorithm, NULL, 0);
122}
123
124khash* khash_unref(khash *h) {
125 if (!h)
126 return NULL;
127
128 safe_close(h->fd);
129 free(h->algorithm);
130 free(h);
131
132 return NULL;
133}
134
135int khash_dup(khash *h, khash **ret) {
136 _cleanup_(khash_unrefp) khash *copy = NULL;
137
138 assert(h);
139 assert(ret);
140
141 copy = newdup(khash, h, 1);
142 if (!copy)
143 return -ENOMEM;
144
145 copy->fd = -1;
146 copy->algorithm = strdup(h->algorithm);
ef1fd941 147 if (!copy->algorithm)
0fe5f3c5
LP
148 return -ENOMEM;
149
150 copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
151 if (copy->fd < 0)
152 return -errno;
153
154 *ret = copy;
155 copy = NULL;
156
157 return 0;
158}
159
160const char *khash_get_algorithm(khash *h) {
161 assert(h);
162
163 return h->algorithm;
164}
165
166size_t khash_get_size(khash *h) {
167 assert(h);
168
169 return h->digest_size;
170}
171
172int khash_reset(khash *h) {
173 ssize_t n;
174
175 assert(h);
176
177 n = send(h->fd, NULL, 0, 0);
178 if (n < 0)
179 return -errno;
180
181 h->digest_valid = false;
182
183 return 0;
184}
185
186int khash_put(khash *h, const void *buffer, size_t size) {
187 ssize_t n;
188
189 assert(h);
190 assert(buffer || size == 0);
191
192 if (size <= 0)
193 return 0;
194
195 n = send(h->fd, buffer, size, MSG_MORE);
196 if (n < 0)
197 return -errno;
198
199 h->digest_valid = false;
200
201 return 0;
202}
203
204int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
205 struct msghdr mh = {
206 mh.msg_iov = (struct iovec*) iovec,
207 mh.msg_iovlen = n,
208 };
209 ssize_t k;
210
211 assert(h);
212 assert(iovec || n == 0);
213
214 if (n <= 0)
215 return 0;
216
217 k = sendmsg(h->fd, &mh, MSG_MORE);
218 if (k < 0)
219 return -errno;
220
221 h->digest_valid = false;
222
223 return 0;
224}
225
226static int retrieve_digest(khash *h) {
227 ssize_t n;
228
229 assert(h);
230
231 if (h->digest_valid)
232 return 0;
233
234 n = recv(h->fd, h->digest, h->digest_size, 0);
235 if (n < 0)
236 return n;
237 if ((size_t) n != h->digest_size) /* digest size changed? */
238 return -EIO;
239
240 h->digest_valid = true;
241
242 return 0;
243}
244
245int khash_digest_data(khash *h, const void **ret) {
246 int r;
247
248 assert(h);
249 assert(ret);
250
251 r = retrieve_digest(h);
252 if (r < 0)
253 return r;
254
255 *ret = h->digest;
256 return 0;
257}
258
259int khash_digest_string(khash *h, char **ret) {
260 int r;
261 char *p;
262
263 assert(h);
264 assert(ret);
265
266 r = retrieve_digest(h);
267 if (r < 0)
268 return r;
269
270 p = hexmem(h->digest, h->digest_size);
271 if (!p)
272 return -ENOMEM;
273
274 *ret = p;
275 return 0;
276}