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