chiark / gitweb /
sd-login: teach sd_pid_get_unit() proper handling of instantiated services
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 }