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