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