]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-id128/sd-id128.c
sd-id128: add new sd_id128_get_machine_app_specific() API
[thirdparty/systemd.git] / src / libsystemd / sd-id128 / sd-id128.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2011 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #include "sd-id128.h"
25
26 #include "fd-util.h"
27 #include "hexdecoct.h"
28 #include "id128-util.h"
29 #include "io-util.h"
30 #include "khash.h"
31 #include "macro.h"
32 #include "random-util.h"
33 #include "util.h"
34
35 _public_ char *sd_id128_to_string(sd_id128_t id, char s[SD_ID128_STRING_MAX]) {
36 unsigned n;
37
38 assert_return(s, NULL);
39
40 for (n = 0; n < 16; n++) {
41 s[n*2] = hexchar(id.bytes[n] >> 4);
42 s[n*2+1] = hexchar(id.bytes[n] & 0xF);
43 }
44
45 s[32] = 0;
46
47 return s;
48 }
49
50 _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) {
51 unsigned n, i;
52 sd_id128_t t;
53 bool is_guid = false;
54
55 assert_return(s, -EINVAL);
56
57 for (n = 0, i = 0; n < 16;) {
58 int a, b;
59
60 if (s[i] == '-') {
61 /* Is this a GUID? Then be nice, and skip over
62 * the dashes */
63
64 if (i == 8)
65 is_guid = true;
66 else if (i == 13 || i == 18 || i == 23) {
67 if (!is_guid)
68 return -EINVAL;
69 } else
70 return -EINVAL;
71
72 i++;
73 continue;
74 }
75
76 a = unhexchar(s[i++]);
77 if (a < 0)
78 return -EINVAL;
79
80 b = unhexchar(s[i++]);
81 if (b < 0)
82 return -EINVAL;
83
84 t.bytes[n++] = (a << 4) | b;
85 }
86
87 if (i != (is_guid ? 36 : 32))
88 return -EINVAL;
89
90 if (s[i] != 0)
91 return -EINVAL;
92
93 if (ret)
94 *ret = t;
95 return 0;
96 }
97
98 _public_ int sd_id128_get_machine(sd_id128_t *ret) {
99 static thread_local sd_id128_t saved_machine_id = {};
100 int r;
101
102 assert_return(ret, -EINVAL);
103
104 if (sd_id128_is_null(saved_machine_id)) {
105 r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id);
106 if (r < 0)
107 return r;
108
109 if (sd_id128_is_null(saved_machine_id))
110 return -EINVAL;
111 }
112
113 *ret = saved_machine_id;
114 return 0;
115 }
116
117 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
118 static thread_local sd_id128_t saved_boot_id = {};
119 int r;
120
121 assert_return(ret, -EINVAL);
122
123 if (sd_id128_is_null(saved_boot_id)) {
124 r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id);
125 if (r < 0)
126 return r;
127 }
128
129 *ret = saved_boot_id;
130 return 0;
131 }
132
133 _public_ int sd_id128_get_invocation(sd_id128_t *ret) {
134 static thread_local sd_id128_t saved_invocation_id = {};
135 int r;
136
137 assert_return(ret, -EINVAL);
138
139 if (sd_id128_is_null(saved_invocation_id)) {
140 const char *e;
141
142 e = secure_getenv("INVOCATION_ID");
143 if (!e)
144 return -ENXIO;
145
146 r = sd_id128_from_string(e, &saved_invocation_id);
147 if (r < 0)
148 return r;
149 }
150
151 *ret = saved_invocation_id;
152 return 0;
153 }
154
155 static sd_id128_t make_v4_uuid(sd_id128_t id) {
156 /* Stolen from generate_random_uuid() of drivers/char/random.c
157 * in the kernel sources */
158
159 /* Set UUID version to 4 --- truly random generation */
160 id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40;
161
162 /* Set the UUID variant to DCE */
163 id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80;
164
165 return id;
166 }
167
168 _public_ int sd_id128_randomize(sd_id128_t *ret) {
169 sd_id128_t t;
170 int r;
171
172 assert_return(ret, -EINVAL);
173
174 r = dev_urandom(&t, sizeof(t));
175 if (r < 0)
176 return r;
177
178 /* Turn this into a valid v4 UUID, to be nice. Note that we
179 * only guarantee this for newly generated UUIDs, not for
180 * pre-existing ones. */
181
182 *ret = make_v4_uuid(t);
183 return 0;
184 }
185
186 _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
187 _cleanup_(khash_unrefp) khash *h = NULL;
188 sd_id128_t m, result;
189 const void *p;
190 int r;
191
192 assert_return(ret, -EINVAL);
193
194 r = sd_id128_get_machine(&m);
195 if (r < 0)
196 return r;
197
198 r = khash_new_with_key(&h, "hmac(sha256)", &m, sizeof(m));
199 if (r < 0)
200 return r;
201
202 r = khash_put(h, &app_id, sizeof(app_id));
203 if (r < 0)
204 return r;
205
206 r = khash_digest_data(h, &p);
207 if (r < 0)
208 return r;
209
210 /* We chop off the trailing 16 bytes */
211 memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
212
213 *ret = make_v4_uuid(result);
214 return 0;
215 }