chiark / gitweb /
resolve: update sd-resolve to match the other APIs in style and functionality
[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 <assert.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/acl.h>
26 #include <acl/libacl.h>
27
28 #include "util.h"
29 #include "acl-util.h"
30 #include "set.h"
31 #include "logind-acl.h"
32 #include "udev-util.h"
33
34 static 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
65 int 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
170 finish:
171         acl_free(acl);
172
173         return r;
174 }
175
176 int 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
182         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
183         struct udev_list_entry *item = NULL, *first = NULL;
184         _cleanup_set_free_free_ Set *nodes = NULL;
185         _cleanup_closedir_ DIR *dir = NULL;
186         struct dirent *dent;
187         Iterator i;
188         char *n;
189         int r;
190
191         assert(udev);
192
193         nodes = set_new(string_hash_func, string_compare_func);
194         if (!nodes)
195                 return -ENOMEM;
196
197         e = udev_enumerate_new(udev);
198         if (!e)
199                 return -ENOMEM;
200
201         if (isempty(seat))
202                 seat = "seat0";
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. */
209         r = udev_enumerate_add_match_tag(e, "uaccess");
210         if (r < 0)
211                 return r;
212
213         r = udev_enumerate_add_match_is_initialized(e);
214         if (r < 0)
215                 return r;
216
217         r = udev_enumerate_scan_devices(e);
218         if (r < 0)
219                 return r;
220
221         first = udev_enumerate_get_list_entry(e);
222         udev_list_entry_foreach(item, first) {
223                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
224                 const char *node, *sn;
225
226                 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
227                 if (!d)
228                         return -ENOMEM;
229
230                 sn = udev_device_get_property_value(d, "ID_SEAT");
231                 if (isempty(sn))
232                         sn = "seat0";
233
234                 if (!streq(seat, sn))
235                         continue;
236
237                 node = udev_device_get_devnode(d);
238                 /* In case people mistag devices with nodes, we need to ignore this */
239                 if (!node)
240                         continue;
241
242                 n = strdup(node);
243                 if (!n)
244                         return -ENOMEM;
245
246                 log_debug("Found udev node %s for seat %s", n, seat);
247                 r = set_consume(nodes, n);
248                 if (r < 0)
249                         return r;
250         }
251
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) {
256                 FOREACH_DIRENT(dent, dir, return -errno) {
257                         _cleanup_free_ char *unescaped_devname = NULL;
258
259                         unescaped_devname = cunescape(dent->d_name);
260                         if (!unescaped_devname)
261                                 return -ENOMEM;
262
263                         n = strappend("/dev/", unescaped_devname);
264                         if (!n)
265                                 return -ENOMEM;
266
267                         log_debug("Found static node %s for seat %s", n, seat);
268                         r = set_consume(nodes, n);
269                         if (r == -EEXIST)
270                                 continue;
271                         if (r < 0)
272                                 return r;
273                 }
274         }
275
276         r = 0;
277         SET_FOREACH(n, nodes, i) {
278                 int k;
279
280                 log_debug("Fixing up ACLs at %s for seat %s", n, seat);
281                 k = devnode_acl(n, flush, del, old_uid, add, new_uid);
282                 if (k < 0)
283                         r = k;
284         }
285
286         return r;
287 }