1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "alloc-util.h"
26 #include "dirent-util.h"
29 #include "format-util.h"
30 #include "logind-acl.h"
32 #include "string-util.h"
33 #include "udev-util.h"
36 static int flush_acl(acl_t acl
) {
43 for (found
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &i
);
45 found
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &i
)) {
49 if (acl_get_tag_type(i
, &tag
) < 0)
55 if (acl_delete_entry(acl
, i
) < 0)
67 int devnode_acl(const char *path
,
69 bool del
, uid_t old_uid
,
70 bool add
, uid_t new_uid
) {
78 acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
90 } else if (del
&& old_uid
> 0) {
93 r
= acl_find_uid(acl
, old_uid
, &entry
);
98 if (acl_delete_entry(acl
, entry
) < 0) {
107 if (add
&& new_uid
> 0) {
109 acl_permset_t permset
;
112 r
= acl_find_uid(acl
, new_uid
, &entry
);
117 if (acl_create_entry(&acl
, &entry
) < 0) {
122 if (acl_set_tag_type(entry
, ACL_USER
) < 0 ||
123 acl_set_qualifier(entry
, &new_uid
) < 0) {
129 if (acl_get_permset(entry
, &permset
) < 0) {
134 rd
= acl_get_perm(permset
, ACL_READ
);
140 wt
= acl_get_perm(permset
, ACL_WRITE
);
148 if (acl_add_perm(permset
, ACL_READ
|ACL_WRITE
) < 0) {
160 if (acl_calc_mask(&acl
) < 0) {
165 if (acl_set_file(path
, ACL_TYPE_ACCESS
, acl
) < 0) {
178 int devnode_acl_all(struct udev
*udev
,
181 bool del
, uid_t old_uid
,
182 bool add
, uid_t new_uid
) {
184 _cleanup_udev_enumerate_unref_
struct udev_enumerate
*e
= NULL
;
185 struct udev_list_entry
*item
= NULL
, *first
= NULL
;
186 _cleanup_set_free_free_ Set
*nodes
= NULL
;
187 _cleanup_closedir_
DIR *dir
= NULL
;
195 nodes
= set_new(&string_hash_ops
);
199 e
= udev_enumerate_new(udev
);
206 /* We can only match by one tag in libudev. We choose
207 * "uaccess" for that. If we could match for two tags here we
208 * could add the seat name as second match tag, but this would
209 * be hardly optimizable in libudev, and hence checking the
210 * second tag manually in our loop is a good solution. */
211 r
= udev_enumerate_add_match_tag(e
, "uaccess");
215 r
= udev_enumerate_add_match_is_initialized(e
);
219 r
= udev_enumerate_scan_devices(e
);
223 first
= udev_enumerate_get_list_entry(e
);
224 udev_list_entry_foreach(item
, first
) {
225 _cleanup_udev_device_unref_
struct udev_device
*d
= NULL
;
226 const char *node
, *sn
;
228 d
= udev_device_new_from_syspath(udev
, udev_list_entry_get_name(item
));
232 sn
= udev_device_get_property_value(d
, "ID_SEAT");
236 if (!streq(seat
, sn
))
239 node
= udev_device_get_devnode(d
);
240 /* In case people mistag devices with nodes, we need to ignore this */
248 log_debug("Found udev node %s for seat %s", n
, seat
);
249 r
= set_consume(nodes
, n
);
254 /* udev exports "dead" device nodes to allow module on-demand loading,
255 * these devices are not known to the kernel at this moment */
256 dir
= opendir("/run/udev/static_node-tags/uaccess");
258 FOREACH_DIRENT(dent
, dir
, return -errno
) {
259 _cleanup_free_
char *unescaped_devname
= NULL
;
261 if (cunescape(dent
->d_name
, UNESCAPE_RELAX
, &unescaped_devname
) < 0)
264 n
= strappend("/dev/", unescaped_devname
);
268 log_debug("Found static node %s for seat %s", n
, seat
);
269 r
= set_consume(nodes
, n
);
278 SET_FOREACH(n
, nodes
, i
) {
281 log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT
"→"UID_FMT
"%s%s)",
282 n
, seat
, old_uid
, new_uid
,
283 del
? " del" : "", add
? " add" : "");
285 k
= devnode_acl(n
, flush
, del
, old_uid
, add
, new_uid
);
287 log_debug("Device %s disappeared while setting ACLs", n
);
288 else if (k
< 0 && r
== 0)