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