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