]> git.proxmox.com Git - systemd.git/blame - src/basic/khash.c
New upstream version 236
[systemd.git] / src / basic / khash.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
2897b343
MP
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);
52ad194e 130 return mfree(h);
2897b343
MP
131}
132
133int khash_dup(khash *h, khash **ret) {
134 _cleanup_(khash_unrefp) khash *copy = NULL;
135
136 assert(h);
137 assert(ret);
138
139 copy = newdup(khash, h, 1);
140 if (!copy)
141 return -ENOMEM;
142
143 copy->fd = -1;
144 copy->algorithm = strdup(h->algorithm);
145 if (!copy->algorithm)
146 return -ENOMEM;
147
148 copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
149 if (copy->fd < 0)
150 return -errno;
151
152 *ret = copy;
153 copy = NULL;
154
155 return 0;
156}
157
158const char *khash_get_algorithm(khash *h) {
159 assert(h);
160
161 return h->algorithm;
162}
163
164size_t khash_get_size(khash *h) {
165 assert(h);
166
167 return h->digest_size;
168}
169
170int khash_reset(khash *h) {
171 ssize_t n;
172
173 assert(h);
174
175 n = send(h->fd, NULL, 0, 0);
176 if (n < 0)
177 return -errno;
178
179 h->digest_valid = false;
180
181 return 0;
182}
183
184int khash_put(khash *h, const void *buffer, size_t size) {
185 ssize_t n;
186
187 assert(h);
188 assert(buffer || size == 0);
189
190 if (size <= 0)
191 return 0;
192
193 n = send(h->fd, buffer, size, MSG_MORE);
194 if (n < 0)
195 return -errno;
196
197 h->digest_valid = false;
198
199 return 0;
200}
201
202int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
203 struct msghdr mh = {
204 mh.msg_iov = (struct iovec*) iovec,
205 mh.msg_iovlen = n,
206 };
207 ssize_t k;
208
209 assert(h);
210 assert(iovec || n == 0);
211
212 if (n <= 0)
213 return 0;
214
215 k = sendmsg(h->fd, &mh, MSG_MORE);
216 if (k < 0)
217 return -errno;
218
219 h->digest_valid = false;
220
221 return 0;
222}
223
224static int retrieve_digest(khash *h) {
225 ssize_t n;
226
227 assert(h);
228
229 if (h->digest_valid)
230 return 0;
231
232 n = recv(h->fd, h->digest, h->digest_size, 0);
233 if (n < 0)
234 return n;
235 if ((size_t) n != h->digest_size) /* digest size changed? */
236 return -EIO;
237
238 h->digest_valid = true;
239
240 return 0;
241}
242
243int khash_digest_data(khash *h, const void **ret) {
244 int r;
245
246 assert(h);
247 assert(ret);
248
249 r = retrieve_digest(h);
250 if (r < 0)
251 return r;
252
253 *ret = h->digest;
254 return 0;
255}
256
257int khash_digest_string(khash *h, char **ret) {
258 int r;
259 char *p;
260
261 assert(h);
262 assert(ret);
263
264 r = retrieve_digest(h);
265 if (r < 0)
266 return r;
267
268 p = hexmem(h->digest, h->digest_size);
269 if (!p)
270 return -ENOMEM;
271
272 *ret = p;
273 return 0;
274}