]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-acl.c
util: split out escaping code into escape.[ch]
[thirdparty/systemd.git] / src / login / logind-acl.c
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 <string.h>
24
25 #include "acl-util.h"
26 #include "escape.h"
27 #include "formats-util.h"
28 #include "set.h"
29 #include "udev-util.h"
30 #include "util.h"
31 #include "logind-acl.h"
32
33 static int flush_acl(acl_t acl) {
34 acl_entry_t i;
35 int found;
36 bool changed = false;
37
38 assert(acl);
39
40 for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
41 found > 0;
42 found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
43
44 acl_tag_t tag;
45
46 if (acl_get_tag_type(i, &tag) < 0)
47 return -errno;
48
49 if (tag != ACL_USER)
50 continue;
51
52 if (acl_delete_entry(acl, i) < 0)
53 return -errno;
54
55 changed = true;
56 }
57
58 if (found < 0)
59 return -errno;
60
61 return changed;
62 }
63
64 int devnode_acl(const char *path,
65 bool flush,
66 bool del, uid_t old_uid,
67 bool add, uid_t new_uid) {
68
69 acl_t acl;
70 int r = 0;
71 bool changed = false;
72
73 assert(path);
74
75 acl = acl_get_file(path, ACL_TYPE_ACCESS);
76 if (!acl)
77 return -errno;
78
79 if (flush) {
80
81 r = flush_acl(acl);
82 if (r < 0)
83 goto finish;
84 if (r > 0)
85 changed = true;
86
87 } else if (del && old_uid > 0) {
88 acl_entry_t entry;
89
90 r = acl_find_uid(acl, old_uid, &entry);
91 if (r < 0)
92 goto finish;
93
94 if (r > 0) {
95 if (acl_delete_entry(acl, entry) < 0) {
96 r = -errno;
97 goto finish;
98 }
99
100 changed = true;
101 }
102 }
103
104 if (add && new_uid > 0) {
105 acl_entry_t entry;
106 acl_permset_t permset;
107 int rd, wt;
108
109 r = acl_find_uid(acl, new_uid, &entry);
110 if (r < 0)
111 goto finish;
112
113 if (r == 0) {
114 if (acl_create_entry(&acl, &entry) < 0) {
115 r = -errno;
116 goto finish;
117 }
118
119 if (acl_set_tag_type(entry, ACL_USER) < 0 ||
120 acl_set_qualifier(entry, &new_uid) < 0) {
121 r = -errno;
122 goto finish;
123 }
124 }
125
126 if (acl_get_permset(entry, &permset) < 0) {
127 r = -errno;
128 goto finish;
129 }
130
131 rd = acl_get_perm(permset, ACL_READ);
132 if (rd < 0) {
133 r = -errno;
134 goto finish;
135 }
136
137 wt = acl_get_perm(permset, ACL_WRITE);
138 if (wt < 0) {
139 r = -errno;
140 goto finish;
141 }
142
143 if (!rd || !wt) {
144
145 if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) {
146 r = -errno;
147 goto finish;
148 }
149
150 changed = true;
151 }
152 }
153
154 if (!changed)
155 goto finish;
156
157 if (acl_calc_mask(&acl) < 0) {
158 r = -errno;
159 goto finish;
160 }
161
162 if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
163 r = -errno;
164 goto finish;
165 }
166
167 r = 0;
168
169 finish:
170 acl_free(acl);
171
172 return r;
173 }
174
175 int devnode_acl_all(struct udev *udev,
176 const char *seat,
177 bool flush,
178 bool del, uid_t old_uid,
179 bool add, uid_t new_uid) {
180
181 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
182 struct udev_list_entry *item = NULL, *first = NULL;
183 _cleanup_set_free_free_ Set *nodes = NULL;
184 _cleanup_closedir_ DIR *dir = NULL;
185 struct dirent *dent;
186 Iterator i;
187 char *n;
188 int r;
189
190 assert(udev);
191
192 nodes = set_new(&string_hash_ops);
193 if (!nodes)
194 return -ENOMEM;
195
196 e = udev_enumerate_new(udev);
197 if (!e)
198 return -ENOMEM;
199
200 if (isempty(seat))
201 seat = "seat0";
202
203 /* We can only match by one tag in libudev. We choose
204 * "uaccess" for that. If we could match for two tags here we
205 * could add the seat name as second match tag, but this would
206 * be hardly optimizable in libudev, and hence checking the
207 * second tag manually in our loop is a good solution. */
208 r = udev_enumerate_add_match_tag(e, "uaccess");
209 if (r < 0)
210 return r;
211
212 r = udev_enumerate_add_match_is_initialized(e);
213 if (r < 0)
214 return r;
215
216 r = udev_enumerate_scan_devices(e);
217 if (r < 0)
218 return r;
219
220 first = udev_enumerate_get_list_entry(e);
221 udev_list_entry_foreach(item, first) {
222 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
223 const char *node, *sn;
224
225 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
226 if (!d)
227 return -ENOMEM;
228
229 sn = udev_device_get_property_value(d, "ID_SEAT");
230 if (isempty(sn))
231 sn = "seat0";
232
233 if (!streq(seat, sn))
234 continue;
235
236 node = udev_device_get_devnode(d);
237 /* In case people mistag devices with nodes, we need to ignore this */
238 if (!node)
239 continue;
240
241 n = strdup(node);
242 if (!n)
243 return -ENOMEM;
244
245 log_debug("Found udev node %s for seat %s", n, seat);
246 r = set_consume(nodes, n);
247 if (r < 0)
248 return r;
249 }
250
251 /* udev exports "dead" device nodes to allow module on-demand loading,
252 * these devices are not known to the kernel at this moment */
253 dir = opendir("/run/udev/static_node-tags/uaccess");
254 if (dir) {
255 FOREACH_DIRENT(dent, dir, return -errno) {
256 _cleanup_free_ char *unescaped_devname = NULL;
257
258 if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
259 return -ENOMEM;
260
261 n = strappend("/dev/", unescaped_devname);
262 if (!n)
263 return -ENOMEM;
264
265 log_debug("Found static node %s for seat %s", n, seat);
266 r = set_consume(nodes, n);
267 if (r == -EEXIST)
268 continue;
269 if (r < 0)
270 return r;
271 }
272 }
273
274 r = 0;
275 SET_FOREACH(n, nodes, i) {
276 int k;
277
278 log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)",
279 n, seat, old_uid, new_uid,
280 del ? " del" : "", add ? " add" : "");
281
282 k = devnode_acl(n, flush, del, old_uid, add, new_uid);
283 if (k == -ENOENT)
284 log_debug("Device %s disappeared while setting ACLs", n);
285 else if (k < 0 && r == 0)
286 r = k;
287 }
288
289 return r;
290 }