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