]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-id128/id128-util.c
id128: add id128_digest() helper
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / id128-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
910fd145 2
dccca82b 3#include <errno.h>
910fd145 4#include <fcntl.h>
15b1248a 5#include <unistd.h>
910fd145
LP
6
7#include "fd-util.h"
0f44b766 8#include "fs-util.h"
910fd145
LP
9#include "hexdecoct.h"
10#include "id128-util.h"
11#include "io-util.h"
0408d802 12#include "sha256.h"
910fd145 13#include "stdio-util.h"
80851148 14#include "string-util.h"
bf819d3a 15#include "sync-util.h"
910fd145 16
7c9de5d8
ZJS
17int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
18 sd_id128_t t;
19 int r;
20
21 assert(ret);
22
23 r = sd_id128_from_string(ASSERT_PTR(s), &t);
24 if (r < 0)
25 return r;
26
27 if (sd_id128_is_null(t))
28 return -ENXIO;
29
30 *ret = t;
31 return 0;
32}
33
910fd145 34bool id128_is_valid(const char *s) {
28bf2de2 35 size_t l;
910fd145
LP
36
37 assert(s);
38
39 l = strlen(s);
910fd145 40
28bf2de2 41 if (l == SD_ID128_STRING_MAX - 1)
da890466 42 /* Plain formatted 128-bit hex string */
28bf2de2 43 return in_charset(s, HEXDIGITS);
910fd145 44
28bf2de2 45 if (l == SD_ID128_UUID_STRING_MAX - 1) {
910fd145 46 /* Formatted UUID */
28bf2de2 47 for (size_t i = 0; i < l; i++) {
910fd145
LP
48 char c = s[i];
49
945c2931 50 if (IN_SET(i, 8, 13, 18, 23)) {
910fd145
LP
51 if (c != '-')
52 return false;
28bf2de2
YW
53 } else if (!ascii_ishex(c))
54 return false;
910fd145 55 }
28bf2de2
YW
56 return true;
57 }
910fd145 58
28bf2de2 59 return false;
910fd145
LP
60}
61
762b78de 62int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
28bf2de2 63 char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
52673ef8 64 sd_id128_t id;
910fd145 65 ssize_t l;
e8a66254 66 int r;
910fd145
LP
67
68 assert(fd >= 0);
910fd145 69
da890466 70 /* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
65548c58
LP
71 * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
72 * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
057bf780
YW
73 * accept".
74 *
75 * This returns the following:
76 * -ENOMEDIUM: an empty string,
77 * -ENOPKG: "uninitialized" or "uninitialized\n",
e8a66254 78 * -EUCLEAN: other invalid strings. */
910fd145 79
65548c58 80 l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */
910fd145
LP
81 if (l < 0)
82 return (int) l;
83 if (l == 0) /* empty? */
84 return -ENOMEDIUM;
85
65548c58 86 switch (l) {
910fd145 87
057bf780
YW
88 case STRLEN("uninitialized"):
89 case STRLEN("uninitialized\n"):
90 return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL;
80851148 91
28bf2de2
YW
92 case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */
93 if (buffer[SD_ID128_STRING_MAX-1] != '\n')
e8a66254 94 return -EUCLEAN;
910fd145 95
4831981d 96 _fallthrough_;
28bf2de2 97 case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */
057bf780 98 if (!FLAGS_SET(f, ID128_FORMAT_PLAIN))
e8a66254 99 return -EUCLEAN;
65548c58 100
28bf2de2 101 buffer[SD_ID128_STRING_MAX-1] = 0;
65548c58 102 break;
910fd145 103
28bf2de2
YW
104 case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */
105 if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n')
e8a66254 106 return -EUCLEAN;
910fd145 107
4831981d 108 _fallthrough_;
28bf2de2 109 case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */
057bf780 110 if (!FLAGS_SET(f, ID128_FORMAT_UUID))
e8a66254 111 return -EUCLEAN;
910fd145 112
28bf2de2 113 buffer[SD_ID128_UUID_STRING_MAX-1] = 0;
65548c58
LP
114 break;
115
116 default:
e8a66254 117 return -EUCLEAN;
65548c58 118 }
910fd145 119
52673ef8
YW
120 r = sd_id128_from_string(buffer, &id);
121 if (r == -EINVAL)
122 return -EUCLEAN;
123 if (r < 0)
124 return r;
125
126 if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id))
127 return -ENOMEDIUM;
128
129 if (ret)
130 *ret = id;
131 return 0;
910fd145
LP
132}
133
0f44b766 134int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) {
254d1313 135 _cleanup_close_ int fd = -EBADF;
910fd145 136
0f44b766 137 assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
169d91b7
YW
138 assert(path);
139
420d2e31 140 fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0);
910fd145 141 if (fd < 0)
0f44b766 142 return fd;
910fd145
LP
143
144 return id128_read_fd(fd, f, ret);
145}
146
762b78de 147int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) {
28bf2de2 148 char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */
910fd145 149 size_t sz;
15b1248a 150 int r;
910fd145
LP
151
152 assert(fd >= 0);
057bf780 153 assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID));
910fd145 154
52673ef8
YW
155 if (FLAGS_SET(f, ID128_REFUSE_NULL) && sd_id128_is_null(id))
156 return -ENOMEDIUM;
157
057bf780 158 if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) {
b7416360 159 assert_se(sd_id128_to_string(id, buffer));
b7416360 160 sz = SD_ID128_STRING_MAX;
910fd145 161 } else {
b7416360 162 assert_se(sd_id128_to_uuid_string(id, buffer));
b7416360 163 sz = SD_ID128_UUID_STRING_MAX;
910fd145
LP
164 }
165
28bf2de2 166 buffer[sz - 1] = '\n';
e22c60a9 167 r = loop_write(fd, buffer, sz);
15b1248a
LP
168 if (r < 0)
169 return r;
170
b40c8ebd 171 if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) {
bf819d3a 172 r = fsync_full(fd);
8ac2f74f
LP
173 if (r < 0)
174 return r;
15b1248a
LP
175 }
176
8ac2f74f 177 return 0;
910fd145
LP
178}
179
c227c46b 180int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
254d1313 181 _cleanup_close_ int fd = -EBADF;
910fd145 182
c227c46b 183 assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
169d91b7
YW
184 assert(path);
185
420d2e31 186 fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444);
910fd145 187 if (fd < 0)
c227c46b 188 return fd;
910fd145 189
b40c8ebd 190 return id128_write_fd(fd, f, id);
910fd145 191}
4b58153d 192
7a08d314
YW
193void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
194 siphash24_compress(p, sizeof(sd_id128_t), state);
4b58153d
LP
195}
196
7a08d314 197int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
4b58153d
LP
198 return memcmp(a, b, 16);
199}
200
1293a168
LP
201sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
202 /* Stolen from generate_random_uuid() of drivers/char/random.c
203 * in the kernel sources */
204
205 /* Set UUID version to 4 --- truly random generation */
206 id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
207
208 /* Set the UUID variant to DCE */
209 id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
210
211 return id;
212}
213
7a08d314 214DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func);
3e61656f 215DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free);
b4be4ff8
LP
216
217int id128_get_product(sd_id128_t *ret) {
218 sd_id128_t uuid;
219 int r;
220
221 assert(ret);
222
223 /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
224 * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
225
17f9d6d8 226 r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
b4be4ff8 227 if (r == -ENOENT)
17f9d6d8 228 r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
b4be4ff8
LP
229 if (r < 0)
230 return r;
231
232 if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
233 return -EADDRNOTAVAIL; /* Recognizable error */
234
235 *ret = uuid;
236 return 0;
237}
0408d802
LP
238
239sd_id128_t id128_digest(const void *data, size_t size) {
240 assert(data || size == 0);
241
242 /* Hashes a UUID from some arbitrary data */
243
244 if (size == SIZE_MAX)
245 size = strlen(data);
246
247 uint8_t h[SHA256_DIGEST_SIZE];
248 sd_id128_t id;
249
250 /* Take the first half of the SHA256 result */
251 assert_cc(sizeof(h) >= sizeof(id.bytes));
252 memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes));
253
254 return id128_make_v4_uuid(id);
255}