]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-acl.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / login / logind-acl.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
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.
11
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.
16
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/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23
24 #include "acl-util.h"
25 #include "alloc-util.h"
26 #include "dirent-util.h"
27 #include "escape.h"
28 #include "fd-util.h"
29 #include "format-util.h"
30 #include "logind-acl.h"
31 #include "set.h"
32 #include "string-util.h"
33 #include "udev-util.h"
34 #include "util.h"
35
36 static int flush_acl(acl_t acl) {
37 acl_entry_t i;
38 int found;
39 bool changed = false;
40
41 assert(acl);
42
43 for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
44 found > 0;
45 found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
46
47 acl_tag_t tag;
48
49 if (acl_get_tag_type(i, &tag) < 0)
50 return -errno;
51
52 if (tag != ACL_USER)
53 continue;
54
55 if (acl_delete_entry(acl, i) < 0)
56 return -errno;
57
58 changed = true;
59 }
60
61 if (found < 0)
62 return -errno;
63
64 return changed;
65 }
66
67 int devnode_acl(const char *path,
68 bool flush,
69 bool del, uid_t old_uid,
70 bool add, uid_t new_uid) {
71
72 acl_t acl;
73 int r = 0;
74 bool changed = false;
75
76 assert(path);
77
78 acl = acl_get_file(path, ACL_TYPE_ACCESS);
79 if (!acl)
80 return -errno;
81
82 if (flush) {
83
84 r = flush_acl(acl);
85 if (r < 0)
86 goto finish;
87 if (r > 0)
88 changed = true;
89
90 } else if (del && old_uid > 0) {
91 acl_entry_t entry;
92
93 r = acl_find_uid(acl, old_uid, &entry);
94 if (r < 0)
95 goto finish;
96
97 if (r > 0) {
98 if (acl_delete_entry(acl, entry) < 0) {
99 r = -errno;
100 goto finish;
101 }
102
103 changed = true;
104 }
105 }
106
107 if (add && new_uid > 0) {
108 acl_entry_t entry;
109 acl_permset_t permset;
110 int rd, wt;
111
112 r = acl_find_uid(acl, new_uid, &entry);
113 if (r < 0)
114 goto finish;
115
116 if (r == 0) {
117 if (acl_create_entry(&acl, &entry) < 0) {
118 r = -errno;
119 goto finish;
120 }
121
122 if (acl_set_tag_type(entry, ACL_USER) < 0 ||
123 acl_set_qualifier(entry, &new_uid) < 0) {
124 r = -errno;
125 goto finish;
126 }
127 }
128
129 if (acl_get_permset(entry, &permset) < 0) {
130 r = -errno;
131 goto finish;
132 }
133
134 rd = acl_get_perm(permset, ACL_READ);
135 if (rd < 0) {
136 r = -errno;
137 goto finish;
138 }
139
140 wt = acl_get_perm(permset, ACL_WRITE);
141 if (wt < 0) {
142 r = -errno;
143 goto finish;
144 }
145
146 if (!rd || !wt) {
147
148 if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) {
149 r = -errno;
150 goto finish;
151 }
152
153 changed = true;
154 }
155 }
156
157 if (!changed)
158 goto finish;
159
160 if (acl_calc_mask(&acl) < 0) {
161 r = -errno;
162 goto finish;
163 }
164
165 if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
166 r = -errno;
167 goto finish;
168 }
169
170 r = 0;
171
172 finish:
173 acl_free(acl);
174
175 return r;
176 }
177
178 int devnode_acl_all(struct udev *udev,
179 const char *seat,
180 bool flush,
181 bool del, uid_t old_uid,
182 bool add, uid_t new_uid) {
183
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;
188 struct dirent *dent;
189 Iterator i;
190 char *n;
191 int r;
192
193 assert(udev);
194
195 nodes = set_new(&string_hash_ops);
196 if (!nodes)
197 return -ENOMEM;
198
199 e = udev_enumerate_new(udev);
200 if (!e)
201 return -ENOMEM;
202
203 if (isempty(seat))
204 seat = "seat0";
205
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");
212 if (r < 0)
213 return r;
214
215 r = udev_enumerate_add_match_is_initialized(e);
216 if (r < 0)
217 return r;
218
219 r = udev_enumerate_scan_devices(e);
220 if (r < 0)
221 return r;
222
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;
227
228 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
229 if (!d)
230 return -ENOMEM;
231
232 sn = udev_device_get_property_value(d, "ID_SEAT");
233 if (isempty(sn))
234 sn = "seat0";
235
236 if (!streq(seat, sn))
237 continue;
238
239 node = udev_device_get_devnode(d);
240 /* In case people mistag devices with nodes, we need to ignore this */
241 if (!node)
242 continue;
243
244 n = strdup(node);
245 if (!n)
246 return -ENOMEM;
247
248 log_debug("Found udev node %s for seat %s", n, seat);
249 r = set_consume(nodes, n);
250 if (r < 0)
251 return r;
252 }
253
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");
257 if (dir) {
258 FOREACH_DIRENT(dent, dir, return -errno) {
259 _cleanup_free_ char *unescaped_devname = NULL;
260
261 if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
262 return -ENOMEM;
263
264 n = strappend("/dev/", unescaped_devname);
265 if (!n)
266 return -ENOMEM;
267
268 log_debug("Found static node %s for seat %s", n, seat);
269 r = set_consume(nodes, n);
270 if (r == -EEXIST)
271 continue;
272 if (r < 0)
273 return r;
274 }
275 }
276
277 r = 0;
278 SET_FOREACH(n, nodes, i) {
279 int k;
280
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" : "");
284
285 k = devnode_acl(n, flush, del, old_uid, add, new_uid);
286 if (k == -ENOENT)
287 log_debug("Device %s disappeared while setting ACLs", n);
288 else if (k < 0 && r == 0)
289 r = k;
290 }
291
292 return r;
293 }