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