chiark / gitweb /
honor SELinux labels, when creating and writing config files
[elogind.git] / src / core / machine-id-setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 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 <unistd.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <sys/mount.h>
29
30 #include <systemd/sd-id128.h>
31
32 #include "machine-id-setup.h"
33 #include "macro.h"
34 #include "util.h"
35 #include "mkdir.h"
36 #include "log.h"
37 #include "virt.h"
38 #include "fileio.h"
39
40 static int shorten_uuid(char destination[36], const char *source) {
41         unsigned i, j;
42
43         for (i = 0, j = 0; i < 36 && j < 32; i++) {
44                 int t;
45
46                 t = unhexchar(source[i]);
47                 if (t < 0)
48                         continue;
49
50                 destination[j++] = hexchar(t);
51         }
52
53         if (i == 36 && j == 32) {
54                 destination[32] = '\n';
55                 destination[33] = 0;
56                 return 0;
57         }
58
59         return -EINVAL;
60 }
61
62 static int generate(char id[34]) {
63         int fd, r;
64         unsigned char *p;
65         sd_id128_t buf;
66         char *q;
67         ssize_t k;
68         const char *vm_id;
69
70         assert(id);
71
72         /* First, try reading the D-Bus machine id, unless it is a symlink */
73         fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
74         if (fd >= 0) {
75
76                 k = loop_read(fd, id, 32, false);
77                 close_nointr_nofail(fd);
78
79                 if (k >= 32) {
80                         id[32] = '\n';
81                         id[33] = 0;
82
83                         log_info("Initializing machine ID from D-Bus machine ID.");
84                         return 0;
85                 }
86         }
87
88         /* If that didn't work, see if we are running in qemu/kvm and a
89          * machine ID was passed in via -uuid on the qemu/kvm command
90          * line */
91
92         r = detect_vm(&vm_id);
93         if (r > 0 && streq(vm_id, "kvm")) {
94                 char uuid[37];
95
96                 fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
97                 if (fd >= 0) {
98                         k = loop_read(fd, uuid, 36, false);
99                         close_nointr_nofail(fd);
100
101                         if (k >= 36) {
102                                 r = shorten_uuid(id, uuid);
103                                 if (r >= 0) {
104                                         log_info("Initializing machine ID from KVM UUID.");
105                                         return 0;
106                                 }
107                         }
108                 }
109         }
110
111         /* If that didn't work either, see if we are running in a
112          * container, and a machine ID was passed in via
113          * $container_uuid the way libvirt/LXC does it */
114         r = detect_container(NULL);
115         if (r > 0) {
116                 char *e;
117
118                 r = getenv_for_pid(1, "container_uuid", &e);
119                 if (r > 0) {
120                         if (strlen(e) >= 36) {
121                                 r = shorten_uuid(id, e);
122                                 if (r >= 0) {
123                                         log_info("Initializing machine ID from container UUID.");
124                                         free(e);
125                                         return 0;
126                                 }
127                         }
128
129                         free(e);
130                 }
131         }
132
133         /* If that didn't work, generate a random machine id */
134         r = sd_id128_randomize(&buf);
135         if (r < 0) {
136                 log_error("Failed to open /dev/urandom: %s", strerror(-r));
137                 return r;
138         }
139
140         for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
141                 q[0] = hexchar(*p >> 4);
142                 q[1] = hexchar(*p & 15);
143         }
144
145         id[32] = '\n';
146         id[33] = 0;
147
148         log_info("Initializing machine ID from random generator.");
149
150         return 0;
151 }
152
153 int machine_id_setup(void) {
154         int fd, r;
155         bool writable;
156         struct stat st;
157         char id[34]; /* 32 + \n + \0 */
158         mode_t m;
159
160         m = umask(0000);
161
162         /* We create this 0444, to indicate that this isn't really
163          * something you should ever modify. Of course, since the file
164          * will be owned by root it doesn't matter much, but maybe
165          * people look. */
166
167         fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
168         if (fd >= 0)
169                 writable = true;
170         else {
171                 fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
172                 if (fd < 0) {
173                         umask(m);
174                         log_error("Cannot open /etc/machine-id: %m");
175                         return -errno;
176                 }
177
178                 writable = false;
179         }
180
181         umask(m);
182
183         if (fstat(fd, &st) < 0) {
184                 log_error("fstat() failed: %m");
185                 r = -errno;
186                 goto finish;
187         }
188
189         if (S_ISREG(st.st_mode)) {
190                 if (loop_read(fd, id, 32, false) >= 32) {
191                         r = 0;
192                         goto finish;
193                 }
194         }
195
196         /* Hmm, so, the id currently stored is not useful, then let's
197          * generate one */
198
199         r = generate(id);
200         if (r < 0)
201                 goto finish;
202
203         if (S_ISREG(st.st_mode) && writable) {
204                 lseek(fd, 0, SEEK_SET);
205
206                 if (loop_write(fd, id, 33, false) == 33) {
207                         r = 0;
208                         goto finish;
209                 }
210         }
211
212         close_nointr_nofail(fd);
213         fd = -1;
214
215         /* Hmm, we couldn't write it? So let's write it to
216          * /run/machine-id as a replacement */
217
218         m = umask(0022);
219         r = write_one_line_file("/run/machine-id", id);
220         umask(m);
221
222         if (r < 0) {
223                 log_error("Cannot write /run/machine-id: %s", strerror(-r));
224
225                 unlink("/run/machine-id");
226                 goto finish;
227         }
228
229         /* And now, let's mount it over */
230         r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0;
231         if (r < 0) {
232                 unlink("/run/machine-id");
233                 log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
234         } else {
235                 log_info("Installed transient /etc/machine-id file.");
236
237                 /* Mark the mount read-only */
238                 mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
239         }
240
241 finish:
242
243         if (fd >= 0)
244                 close_nointr_nofail(fd);
245
246         return r;
247 }