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