chiark / gitweb /
time-util: introduce usec_sub()
[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 "acl-util.h"
26 #include "alloc-util.h"
27 #include "dirent-util.h"
28 #include "escape.h"
29 #include "fd-util.h"
30 #include "formats-util.h"
31 #include "logind-acl.h"
32 #include "set.h"
33 #include "string-util.h"
34 #include "udev-util.h"
35 #include "util.h"
36
37 static 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
68 int 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;
74         int r = 0;
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
94                 r = acl_find_uid(acl, old_uid, &entry);
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
113                 r = acl_find_uid(acl, new_uid, &entry);
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
173 finish:
174         acl_free(acl);
175
176         return r;
177 }
178
179 int 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
185         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
186         struct udev_list_entry *item = NULL, *first = NULL;
187         _cleanup_set_free_free_ Set *nodes = NULL;
188         _cleanup_closedir_ DIR *dir = NULL;
189         struct dirent *dent;
190         Iterator i;
191         char *n;
192         int r;
193
194         assert(udev);
195
196         nodes = set_new(&string_hash_ops);
197         if (!nodes)
198                 return -ENOMEM;
199
200         e = udev_enumerate_new(udev);
201         if (!e)
202                 return -ENOMEM;
203
204         if (isempty(seat))
205                 seat = "seat0";
206
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. */
212         r = udev_enumerate_add_match_tag(e, "uaccess");
213         if (r < 0)
214                 return r;
215
216         r = udev_enumerate_add_match_is_initialized(e);
217         if (r < 0)
218                 return r;
219
220         r = udev_enumerate_scan_devices(e);
221         if (r < 0)
222                 return r;
223
224         first = udev_enumerate_get_list_entry(e);
225         udev_list_entry_foreach(item, first) {
226                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
227                 const char *node, *sn;
228
229                 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
230                 if (!d)
231                         return -ENOMEM;
232
233                 sn = udev_device_get_property_value(d, "ID_SEAT");
234                 if (isempty(sn))
235                         sn = "seat0";
236
237                 if (!streq(seat, sn))
238                         continue;
239
240                 node = udev_device_get_devnode(d);
241                 /* In case people mistag devices with nodes, we need to ignore this */
242                 if (!node)
243                         continue;
244
245                 n = strdup(node);
246                 if (!n)
247                         return -ENOMEM;
248
249                 log_debug("Found udev node %s for seat %s", n, seat);
250                 r = set_consume(nodes, n);
251                 if (r < 0)
252                         return r;
253         }
254
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) {
259                 FOREACH_DIRENT(dent, dir, return -errno) {
260                         _cleanup_free_ char *unescaped_devname = NULL;
261
262                         if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
263                                 return -ENOMEM;
264
265                         n = strappend("/dev/", unescaped_devname);
266                         if (!n)
267                                 return -ENOMEM;
268
269                         log_debug("Found static node %s for seat %s", n, seat);
270                         r = set_consume(nodes, n);
271                         if (r == -EEXIST)
272                                 continue;
273                         if (r < 0)
274                                 return r;
275                 }
276         }
277
278         r = 0;
279         SET_FOREACH(n, nodes, i) {
280                 int k;
281
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
286                 k = devnode_acl(n, flush, del, old_uid, add, new_uid);
287                 if (k == -ENOENT)
288                         log_debug("Device %s disappeared while setting ACLs", n);
289                 else if (k < 0 && r == 0)
290                         r = k;
291         }
292
293         return r;
294 }