]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-acl.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / login / logind-acl.c
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>
23 #include <sys/acl.h>
24 #include <acl/libacl.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #include "logind-acl.h"
29 #include "util.h"
30 #include "acl-util.h"
31
32 static 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
63 int 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;
69 int r = 0;
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
89 r = acl_find_uid(acl, old_uid, &entry);
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
108 r = acl_find_uid(acl, new_uid, &entry);
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
168 finish:
169 acl_free(acl);
170
171 return r;
172 }
173
174 int 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
180 struct udev_list_entry *item = NULL, *first = NULL;
181 struct udev_enumerate *e;
182 int r;
183
184 assert(udev);
185
186 if (isempty(seat))
187 seat = "seat0";
188
189 e = udev_enumerate_new(udev);
190 if (!e)
191 return -ENOMEM;
192
193 /* We can only match by one tag in libudev. We choose
194 * "uaccess" for that. If we could match for two tags here we
195 * could add the seat name as second match tag, but this would
196 * be hardly optimizable in libudev, and hence checking the
197 * second tag manually in our loop is a good solution. */
198
199 r = udev_enumerate_add_match_tag(e, "uaccess");
200 if (r < 0)
201 goto finish;
202
203 r = udev_enumerate_scan_devices(e);
204 if (r < 0)
205 goto finish;
206
207 first = udev_enumerate_get_list_entry(e);
208 udev_list_entry_foreach(item, first) {
209 struct udev_device *d;
210 const char *node, *sn;
211
212 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
213 if (!d) {
214 r = -ENOMEM;
215 goto finish;
216 }
217
218 sn = udev_device_get_property_value(d, "ID_SEAT");
219 if (isempty(sn))
220 sn = "seat0";
221
222 if (!streq(seat, sn)) {
223 udev_device_unref(d);
224 continue;
225 }
226
227 node = udev_device_get_devnode(d);
228 if (!node) {
229 /* In case people mistag devices with nodes, we need to ignore this */
230 udev_device_unref(d);
231 continue;
232 }
233
234 log_debug("Fixing up %s for seat %s...", node, sn);
235
236 r = devnode_acl(node, flush, del, old_uid, add, new_uid);
237 udev_device_unref(d);
238
239 if (r < 0)
240 goto finish;
241 }
242
243 finish:
244 if (e)
245 udev_enumerate_unref(e);
246
247 return r;
248 }