chiark / gitweb /
Merge nss-myhostname
[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 <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 }