Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <string.h>
5 :
6 : #include "sd-device.h"
7 :
8 : #include "acl-util.h"
9 : #include "alloc-util.h"
10 : #include "device-util.h"
11 : #include "dirent-util.h"
12 : #include "escape.h"
13 : #include "fd-util.h"
14 : #include "format-util.h"
15 : #include "logind-acl.h"
16 : #include "set.h"
17 : #include "string-util.h"
18 : #include "util.h"
19 :
20 0 : static int flush_acl(acl_t acl) {
21 : acl_entry_t i;
22 : int found;
23 0 : bool changed = false;
24 :
25 0 : assert(acl);
26 :
27 0 : for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
28 : found > 0;
29 0 : found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
30 :
31 : acl_tag_t tag;
32 :
33 0 : if (acl_get_tag_type(i, &tag) < 0)
34 0 : return -errno;
35 :
36 0 : if (tag != ACL_USER)
37 0 : continue;
38 :
39 0 : if (acl_delete_entry(acl, i) < 0)
40 0 : return -errno;
41 :
42 0 : changed = true;
43 : }
44 :
45 0 : if (found < 0)
46 0 : return -errno;
47 :
48 0 : return changed;
49 : }
50 :
51 0 : int devnode_acl(const char *path,
52 : bool flush,
53 : bool del, uid_t old_uid,
54 : bool add, uid_t new_uid) {
55 :
56 : acl_t acl;
57 0 : int r = 0;
58 0 : bool changed = false;
59 :
60 0 : assert(path);
61 :
62 0 : acl = acl_get_file(path, ACL_TYPE_ACCESS);
63 0 : if (!acl)
64 0 : return -errno;
65 :
66 0 : if (flush) {
67 :
68 0 : r = flush_acl(acl);
69 0 : if (r < 0)
70 0 : goto finish;
71 0 : if (r > 0)
72 0 : changed = true;
73 :
74 0 : } else if (del && old_uid > 0) {
75 : acl_entry_t entry;
76 :
77 0 : r = acl_find_uid(acl, old_uid, &entry);
78 0 : if (r < 0)
79 0 : goto finish;
80 :
81 0 : if (r > 0) {
82 0 : if (acl_delete_entry(acl, entry) < 0) {
83 0 : r = -errno;
84 0 : goto finish;
85 : }
86 :
87 0 : changed = true;
88 : }
89 : }
90 :
91 0 : if (add && new_uid > 0) {
92 : acl_entry_t entry;
93 : acl_permset_t permset;
94 : int rd, wt;
95 :
96 0 : r = acl_find_uid(acl, new_uid, &entry);
97 0 : if (r < 0)
98 0 : goto finish;
99 :
100 0 : if (r == 0) {
101 0 : if (acl_create_entry(&acl, &entry) < 0) {
102 0 : r = -errno;
103 0 : goto finish;
104 : }
105 :
106 0 : if (acl_set_tag_type(entry, ACL_USER) < 0 ||
107 0 : acl_set_qualifier(entry, &new_uid) < 0) {
108 0 : r = -errno;
109 0 : goto finish;
110 : }
111 : }
112 :
113 0 : if (acl_get_permset(entry, &permset) < 0) {
114 0 : r = -errno;
115 0 : goto finish;
116 : }
117 :
118 0 : rd = acl_get_perm(permset, ACL_READ);
119 0 : if (rd < 0) {
120 0 : r = -errno;
121 0 : goto finish;
122 : }
123 :
124 0 : wt = acl_get_perm(permset, ACL_WRITE);
125 0 : if (wt < 0) {
126 0 : r = -errno;
127 0 : goto finish;
128 : }
129 :
130 0 : if (!rd || !wt) {
131 :
132 0 : if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) {
133 0 : r = -errno;
134 0 : goto finish;
135 : }
136 :
137 0 : changed = true;
138 : }
139 : }
140 :
141 0 : if (!changed)
142 0 : goto finish;
143 :
144 0 : if (acl_calc_mask(&acl) < 0) {
145 0 : r = -errno;
146 0 : goto finish;
147 : }
148 :
149 0 : if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) {
150 0 : r = -errno;
151 0 : goto finish;
152 : }
153 :
154 0 : r = 0;
155 :
156 0 : finish:
157 0 : acl_free(acl);
158 :
159 0 : return r;
160 : }
161 :
162 0 : int devnode_acl_all(const char *seat,
163 : bool flush,
164 : bool del, uid_t old_uid,
165 : bool add, uid_t new_uid) {
166 :
167 0 : _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
168 0 : _cleanup_set_free_free_ Set *nodes = NULL;
169 0 : _cleanup_closedir_ DIR *dir = NULL;
170 : struct dirent *dent;
171 : sd_device *d;
172 : Iterator i;
173 : char *n;
174 : int r;
175 :
176 0 : nodes = set_new(&path_hash_ops);
177 0 : if (!nodes)
178 0 : return -ENOMEM;
179 :
180 0 : r = sd_device_enumerator_new(&e);
181 0 : if (r < 0)
182 0 : return r;
183 :
184 0 : if (isempty(seat))
185 0 : seat = "seat0";
186 :
187 : /* We can only match by one tag in libudev. We choose
188 : * "uaccess" for that. If we could match for two tags here we
189 : * could add the seat name as second match tag, but this would
190 : * be hardly optimizable in libudev, and hence checking the
191 : * second tag manually in our loop is a good solution. */
192 0 : r = sd_device_enumerator_add_match_tag(e, "uaccess");
193 0 : if (r < 0)
194 0 : return r;
195 :
196 0 : FOREACH_DEVICE(e, d) {
197 : const char *node, *sn;
198 :
199 0 : if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
200 0 : sn = "seat0";
201 :
202 0 : if (!streq(seat, sn))
203 0 : continue;
204 :
205 : /* In case people mistag devices with nodes, we need to ignore this */
206 0 : if (sd_device_get_devname(d, &node) < 0)
207 0 : continue;
208 :
209 0 : log_device_debug(d, "Found udev node %s for seat %s", node, seat);
210 0 : r = set_put_strdup(nodes, node);
211 0 : if (r < 0)
212 0 : return r;
213 : }
214 :
215 : /* udev exports "dead" device nodes to allow module on-demand loading,
216 : * these devices are not known to the kernel at this moment */
217 0 : dir = opendir("/run/udev/static_node-tags/uaccess");
218 0 : if (dir) {
219 0 : FOREACH_DIRENT(dent, dir, return -errno) {
220 0 : _cleanup_free_ char *unescaped_devname = NULL;
221 :
222 0 : if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
223 0 : return -ENOMEM;
224 :
225 0 : n = path_join("/dev", unescaped_devname);
226 0 : if (!n)
227 0 : return -ENOMEM;
228 :
229 0 : log_debug("Found static node %s for seat %s", n, seat);
230 0 : r = set_consume(nodes, n);
231 0 : if (r == -EEXIST)
232 0 : continue;
233 0 : if (r < 0)
234 0 : return r;
235 : }
236 : }
237 :
238 0 : r = 0;
239 0 : SET_FOREACH(n, nodes, i) {
240 : int k;
241 :
242 0 : log_debug("Changing ACLs at %s for seat %s (uid "UID_FMT"→"UID_FMT"%s%s)",
243 : n, seat, old_uid, new_uid,
244 : del ? " del" : "", add ? " add" : "");
245 :
246 0 : k = devnode_acl(n, flush, del, old_uid, add, new_uid);
247 0 : if (k == -ENOENT)
248 0 : log_debug("Device %s disappeared while setting ACLs", n);
249 0 : else if (k < 0 && r == 0)
250 0 : r = k;
251 : }
252 :
253 0 : return r;
254 : }
|