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