Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0+ */
2 :
3 : #include <errno.h>
4 : #include <fcntl.h>
5 : #include <stdbool.h>
6 : #include <stddef.h>
7 : #include <stdio.h>
8 : #include <string.h>
9 : #include <sys/stat.h>
10 : #include <unistd.h>
11 :
12 : #include "alloc-util.h"
13 : #include "device-nodes.h"
14 : #include "device-private.h"
15 : #include "device-util.h"
16 : #include "dirent-util.h"
17 : #include "fd-util.h"
18 : #include "format-util.h"
19 : #include "fs-util.h"
20 : #include "libudev-util.h"
21 : #include "mkdir.h"
22 : #include "path-util.h"
23 : #include "selinux-util.h"
24 : #include "smack-util.h"
25 : #include "stdio-util.h"
26 : #include "string-util.h"
27 : #include "strxcpyx.h"
28 : #include "udev-node.h"
29 : #include "user-util.h"
30 :
31 0 : static int node_symlink(sd_device *dev, const char *node, const char *slink) {
32 0 : _cleanup_free_ char *slink_dirname = NULL, *target = NULL;
33 : const char *id_filename, *slink_tmp;
34 : struct stat stats;
35 : int r;
36 :
37 0 : assert(dev);
38 0 : assert(node);
39 0 : assert(slink);
40 :
41 0 : slink_dirname = dirname_malloc(slink);
42 0 : if (!slink_dirname)
43 0 : return log_oom();
44 :
45 : /* use relative link */
46 0 : r = path_make_relative(slink_dirname, node, &target);
47 0 : if (r < 0)
48 0 : return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
49 :
50 : /* preserve link with correct target, do not replace node of other device */
51 0 : if (lstat(slink, &stats) == 0) {
52 0 : if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
53 0 : log_device_error(dev, "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
54 0 : return -EOPNOTSUPP;
55 0 : } else if (S_ISLNK(stats.st_mode)) {
56 0 : _cleanup_free_ char *buf = NULL;
57 :
58 0 : if (readlink_malloc(slink, &buf) >= 0 &&
59 0 : streq(target, buf)) {
60 0 : log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
61 0 : (void) label_fix(slink, LABEL_IGNORE_ENOENT);
62 0 : (void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
63 0 : return 0;
64 : }
65 : }
66 : } else {
67 0 : log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
68 : do {
69 0 : r = mkdir_parents_label(slink, 0755);
70 0 : if (!IN_SET(r, 0, -ENOENT))
71 0 : break;
72 0 : mac_selinux_create_file_prepare(slink, S_IFLNK);
73 0 : if (symlink(target, slink) < 0)
74 0 : r = -errno;
75 0 : mac_selinux_create_file_clear();
76 0 : } while (r == -ENOENT);
77 0 : if (r == 0)
78 0 : return 0;
79 0 : if (r < 0)
80 0 : log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink);
81 : }
82 :
83 0 : log_device_debug(dev, "Atomically replace '%s'", slink);
84 0 : r = device_get_id_filename(dev, &id_filename);
85 0 : if (r < 0)
86 0 : return log_device_error_errno(dev, r, "Failed to get id_filename: %m");
87 0 : slink_tmp = strjoina(slink, ".tmp-", id_filename);
88 0 : (void) unlink(slink_tmp);
89 : do {
90 0 : r = mkdir_parents_label(slink_tmp, 0755);
91 0 : if (!IN_SET(r, 0, -ENOENT))
92 0 : break;
93 0 : mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
94 0 : if (symlink(target, slink_tmp) < 0)
95 0 : r = -errno;
96 0 : mac_selinux_create_file_clear();
97 0 : } while (r == -ENOENT);
98 0 : if (r < 0)
99 0 : return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
100 :
101 0 : if (rename(slink_tmp, slink) < 0) {
102 0 : r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
103 0 : (void) unlink(slink_tmp);
104 : }
105 :
106 0 : return r;
107 : }
108 :
109 : /* find device node of device with highest priority */
110 0 : static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
111 0 : _cleanup_closedir_ DIR *dir = NULL;
112 0 : _cleanup_free_ char *target = NULL;
113 : struct dirent *dent;
114 0 : int r, priority = 0;
115 :
116 0 : assert(!add || dev);
117 0 : assert(stackdir);
118 0 : assert(ret);
119 :
120 0 : if (add) {
121 : const char *devnode;
122 :
123 0 : r = device_get_devlink_priority(dev, &priority);
124 0 : if (r < 0)
125 0 : return r;
126 :
127 0 : r = sd_device_get_devname(dev, &devnode);
128 0 : if (r < 0)
129 0 : return r;
130 :
131 0 : target = strdup(devnode);
132 0 : if (!target)
133 0 : return -ENOMEM;
134 : }
135 :
136 0 : dir = opendir(stackdir);
137 0 : if (!dir) {
138 0 : if (target) {
139 0 : *ret = TAKE_PTR(target);
140 0 : return 0;
141 : }
142 :
143 0 : return -errno;
144 : }
145 :
146 0 : FOREACH_DIRENT_ALL(dent, dir, break) {
147 0 : _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
148 : const char *devnode, *id_filename;
149 0 : int db_prio = 0;
150 :
151 0 : if (dent->d_name[0] == '\0')
152 0 : break;
153 0 : if (dent->d_name[0] == '.')
154 0 : continue;
155 :
156 0 : log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
157 :
158 0 : if (device_get_id_filename(dev, &id_filename) < 0)
159 0 : continue;
160 :
161 : /* did we find ourself? */
162 0 : if (streq(dent->d_name, id_filename))
163 0 : continue;
164 :
165 0 : if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0)
166 0 : continue;
167 :
168 0 : if (sd_device_get_devname(dev_db, &devnode) < 0)
169 0 : continue;
170 :
171 0 : if (device_get_devlink_priority(dev_db, &db_prio) < 0)
172 0 : continue;
173 :
174 0 : if (target && db_prio <= priority)
175 0 : continue;
176 :
177 0 : log_device_debug(dev_db, "Device claims priority %i for '%s'", db_prio, stackdir);
178 :
179 0 : r = free_and_strdup(&target, devnode);
180 0 : if (r < 0)
181 0 : return r;
182 0 : priority = db_prio;
183 : }
184 :
185 0 : if (!target)
186 0 : return -ENOENT;
187 :
188 0 : *ret = TAKE_PTR(target);
189 0 : return 0;
190 : }
191 :
192 : /* manage "stack of names" with possibly specified device priorities */
193 0 : static int link_update(sd_device *dev, const char *slink, bool add) {
194 0 : _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL;
195 : char name_enc[PATH_MAX];
196 : const char *id_filename;
197 : int r;
198 :
199 0 : assert(dev);
200 0 : assert(slink);
201 :
202 0 : r = device_get_id_filename(dev, &id_filename);
203 0 : if (r < 0)
204 0 : return log_device_debug_errno(dev, r, "Failed to get id_filename: %m");
205 :
206 0 : util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc));
207 0 : dirname = path_join("/run/udev/links/", name_enc);
208 0 : if (!dirname)
209 0 : return log_oom();
210 0 : filename = path_join(dirname, id_filename);
211 0 : if (!filename)
212 0 : return log_oom();
213 :
214 0 : if (!add && unlink(filename) == 0)
215 0 : (void) rmdir(dirname);
216 :
217 0 : r = link_find_prioritized(dev, add, dirname, &target);
218 0 : if (r < 0) {
219 0 : log_device_debug(dev, "No reference left, removing '%s'", slink);
220 0 : if (unlink(slink) == 0)
221 0 : (void) rmdir_parents(slink, "/");
222 : } else
223 0 : (void) node_symlink(dev, target, slink);
224 :
225 0 : if (add)
226 : do {
227 0 : _cleanup_close_ int fd = -1;
228 :
229 0 : r = mkdir_parents(filename, 0755);
230 0 : if (!IN_SET(r, 0, -ENOENT))
231 0 : break;
232 0 : fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
233 0 : if (fd < 0)
234 0 : r = -errno;
235 0 : } while (r == -ENOENT);
236 :
237 0 : return r;
238 : }
239 :
240 0 : int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
241 : const char *name, *devpath;
242 : int r;
243 :
244 0 : assert(dev);
245 0 : assert(dev_old);
246 :
247 0 : r = sd_device_get_devpath(dev, &devpath);
248 0 : if (r < 0)
249 0 : return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
250 :
251 : /* update possible left-over symlinks */
252 0 : FOREACH_DEVICE_DEVLINK(dev_old, name) {
253 : const char *name_current;
254 0 : bool found = false;
255 :
256 : /* check if old link name still belongs to this device */
257 0 : FOREACH_DEVICE_DEVLINK(dev, name_current)
258 0 : if (streq(name, name_current)) {
259 0 : found = true;
260 0 : break;
261 : }
262 :
263 0 : if (found)
264 0 : continue;
265 :
266 0 : log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
267 : name, devpath);
268 0 : link_update(dev, name, false);
269 : }
270 :
271 0 : return 0;
272 : }
273 :
274 0 : static int node_permissions_apply(sd_device *dev, bool apply_mac,
275 : mode_t mode, uid_t uid, gid_t gid,
276 : OrderedHashmap *seclabel_list) {
277 0 : const char *devnode, *subsystem, *id_filename = NULL;
278 : struct stat stats;
279 : dev_t devnum;
280 : bool apply_mode, apply_uid, apply_gid;
281 : int r;
282 :
283 0 : assert(dev);
284 :
285 0 : r = sd_device_get_devname(dev, &devnode);
286 0 : if (r < 0)
287 0 : return log_device_debug_errno(dev, r, "Failed to get devname: %m");
288 0 : r = sd_device_get_subsystem(dev, &subsystem);
289 0 : if (r < 0)
290 0 : return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
291 0 : r = sd_device_get_devnum(dev, &devnum);
292 0 : if (r < 0)
293 0 : return log_device_debug_errno(dev, r, "Failed to get devnum: %m");
294 0 : (void) device_get_id_filename(dev, &id_filename);
295 :
296 0 : if (streq(subsystem, "block"))
297 0 : mode |= S_IFBLK;
298 : else
299 0 : mode |= S_IFCHR;
300 :
301 0 : if (lstat(devnode, &stats) < 0)
302 0 : return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
303 :
304 0 : if ((mode != MODE_INVALID && (stats.st_mode & S_IFMT) != (mode & S_IFMT)) || stats.st_rdev != devnum)
305 0 : return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
306 : "Found node '%s' with non-matching devnum %s, skip handling",
307 : devnode, id_filename);
308 :
309 0 : apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
310 0 : apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
311 0 : apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
312 :
313 0 : if (apply_mode || apply_uid || apply_gid || apply_mac) {
314 0 : bool selinux = false, smack = false;
315 : const char *name, *label;
316 : Iterator i;
317 :
318 0 : if (apply_mode || apply_uid || apply_gid) {
319 0 : log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
320 : devnode,
321 : uid_is_valid(uid) ? uid : stats.st_uid,
322 : gid_is_valid(gid) ? gid : stats.st_gid,
323 : mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
324 :
325 0 : r = chmod_and_chown(devnode, mode, uid, gid);
326 0 : if (r < 0)
327 0 : log_device_warning_errno(dev, r, "Failed to set owner/mode of %s to uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o: %m",
328 : devnode,
329 : uid_is_valid(uid) ? uid : stats.st_uid,
330 : gid_is_valid(gid) ? gid : stats.st_gid,
331 : mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
332 : } else
333 0 : log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
334 : devnode,
335 : uid_is_valid(uid) ? uid : stats.st_uid,
336 : gid_is_valid(gid) ? gid : stats.st_gid,
337 : mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
338 :
339 : /* apply SECLABEL{$module}=$label */
340 0 : ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) {
341 : int q;
342 :
343 0 : if (streq(name, "selinux")) {
344 0 : selinux = true;
345 :
346 0 : q = mac_selinux_apply(devnode, label);
347 0 : if (q < 0)
348 0 : log_device_error_errno(dev, q, "SECLABEL: failed to set SELinux label '%s': %m", label);
349 : else
350 0 : log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
351 :
352 0 : } else if (streq(name, "smack")) {
353 0 : smack = true;
354 :
355 0 : q = mac_smack_apply(devnode, SMACK_ATTR_ACCESS, label);
356 0 : if (q < 0)
357 0 : log_device_error_errno(dev, q, "SECLABEL: failed to set SMACK label '%s': %m", label);
358 : else
359 0 : log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
360 :
361 : } else
362 0 : log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
363 : }
364 :
365 : /* set the defaults */
366 0 : if (!selinux)
367 0 : (void) mac_selinux_fix(devnode, LABEL_IGNORE_ENOENT);
368 0 : if (!smack)
369 0 : (void) mac_smack_apply(devnode, SMACK_ATTR_ACCESS, NULL);
370 : }
371 :
372 : /* always update timestamp when we re-use the node, like on media change events */
373 0 : (void) utimensat(AT_FDCWD, devnode, NULL, 0);
374 :
375 0 : return r;
376 : }
377 :
378 0 : static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
379 : char filename[DEV_NUM_PATH_MAX], *s;
380 : const char *subsystem;
381 : dev_t devnum;
382 : int r;
383 :
384 0 : assert(ret);
385 :
386 0 : r = sd_device_get_subsystem(dev, &subsystem);
387 0 : if (r < 0)
388 0 : return r;
389 :
390 0 : r = sd_device_get_devnum(dev, &devnum);
391 0 : if (r < 0)
392 0 : return r;
393 :
394 0 : xsprintf_dev_num_path(filename,
395 : streq(subsystem, "block") ? "block" : "char",
396 : devnum);
397 :
398 0 : s = strdup(filename);
399 0 : if (!s)
400 0 : return -ENOMEM;
401 :
402 0 : *ret = s;
403 0 : return 0;
404 : }
405 :
406 0 : int udev_node_add(sd_device *dev, bool apply,
407 : mode_t mode, uid_t uid, gid_t gid,
408 : OrderedHashmap *seclabel_list) {
409 : const char *devnode, *devlink;
410 0 : _cleanup_free_ char *filename = NULL;
411 : int r;
412 :
413 0 : assert(dev);
414 :
415 0 : r = sd_device_get_devname(dev, &devnode);
416 0 : if (r < 0)
417 0 : return log_device_debug_errno(dev, r, "Failed to get devnode: %m");
418 :
419 0 : if (DEBUG_LOGGING) {
420 0 : const char *id_filename = NULL;
421 :
422 0 : (void) device_get_id_filename(dev, &id_filename);
423 0 : log_device_debug(dev, "Handling device node '%s', devnum=%s", devnode, strnull(id_filename));
424 : }
425 :
426 0 : r = node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list);
427 0 : if (r < 0)
428 0 : return r;
429 :
430 0 : r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
431 0 : if (r < 0)
432 0 : return log_device_debug_errno(dev, r, "Failed to get device path: %m");
433 :
434 : /* always add /dev/{block,char}/$major:$minor */
435 0 : (void) node_symlink(dev, devnode, filename);
436 :
437 : /* create/update symlinks, add symlinks to name index */
438 0 : FOREACH_DEVICE_DEVLINK(dev, devlink)
439 0 : (void) link_update(dev, devlink, true);
440 :
441 0 : return 0;
442 : }
443 :
444 0 : int udev_node_remove(sd_device *dev) {
445 0 : _cleanup_free_ char *filename = NULL;
446 : const char *devlink;
447 : int r;
448 :
449 0 : assert(dev);
450 :
451 : /* remove/update symlinks, remove symlinks from name index */
452 0 : FOREACH_DEVICE_DEVLINK(dev, devlink)
453 0 : (void) link_update(dev, devlink, false);
454 :
455 0 : r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
456 0 : if (r < 0)
457 0 : return log_device_debug_errno(dev, r, "Failed to get device path: %m");
458 :
459 : /* remove /dev/{block,char}/$major:$minor */
460 0 : (void) unlink(filename);
461 :
462 0 : return 0;
463 : }
|