]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-acl.c
logind-acl: use macros
[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 <assert.h>
23 #include <sys/acl.h>
24 #include <acl/libacl.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #include "logind-acl.h"
29 #include "util.h"
30 #include "acl-util.h"
31 #include "set.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 struct udev_list_entry *item = NULL, *first = NULL;
182 struct udev_enumerate *e;
183 Set *nodes;
184 Iterator i;
185 char *n;
186 _cleanup_closedir_ DIR *dir = NULL;
187 struct dirent *dent;
188 int r;
189
190 assert(udev);
191
192 nodes = set_new(string_hash_func, string_compare_func);
193 if (!nodes) {
194 return -ENOMEM;
195 }
196
197 e = udev_enumerate_new(udev);
198 if (!e) {
199 r = -ENOMEM;
200 goto finish;
201 }
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 goto finish;
214
215 r = udev_enumerate_scan_devices(e);
216 if (r < 0)
217 goto finish;
218
219 first = udev_enumerate_get_list_entry(e);
220 udev_list_entry_foreach(item, first) {
221 struct udev_device *d;
222 const char *node, *sn;
223
224 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
225 if (!d) {
226 r = -ENOMEM;
227 goto finish;
228 }
229
230 sn = udev_device_get_property_value(d, "ID_SEAT");
231 if (isempty(sn))
232 sn = "seat0";
233
234 if (!streq(seat, sn)) {
235 udev_device_unref(d);
236 continue;
237 }
238
239 node = udev_device_get_devnode(d);
240 if (!node) {
241 /* In case people mistag devices with nodes, we need to ignore this */
242 udev_device_unref(d);
243 continue;
244 }
245
246 n = strdup(node);
247 udev_device_unref(d);
248 if (!n)
249 goto finish;
250
251 log_debug("Found udev node %s for seat %s", n, seat);
252 r = set_put(nodes, n);
253 if (r < 0)
254 goto finish;
255 }
256
257 /* udev exports "dead" device nodes to allow module on-demand loading,
258 * these devices are not known to the kernel at this moment */
259 dir = opendir("/run/udev/static_node-tags/uaccess");
260 if (dir) {
261 FOREACH_DIRENT(dent, dir, r = -errno; goto finish) {
262 _cleanup_free_ char *unescaped_devname = NULL;
263
264 unescaped_devname = cunescape(dent->d_name);
265 if (unescaped_devname == NULL) {
266 r = -ENOMEM;
267 goto finish;
268 }
269
270 n = strappend("/dev/", unescaped_devname);
271 if (!n) {
272 r = -ENOMEM;
273 goto finish;
274 }
275
276 log_debug("Found static node %s for seat %s", n, seat);
277 r = set_put(nodes, n);
278 if (0 && r < 0 && r != -EEXIST) {
279 goto finish;
280 } else
281 r = 0;
282 }
283 }
284
285 SET_FOREACH(n, nodes, i) {
286 log_debug("Fixing up ACLs at %s for seat %s", n, seat);
287 r = devnode_acl(n, flush, del, old_uid, add, new_uid);
288 }
289
290 finish:
291 udev_enumerate_unref(e);
292 set_free_free(nodes);
293 return r;
294 }