]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-acl.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / login / logind-acl.c
CommitLineData
5eda94dd
LP
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
5430f7f2
LP
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
5eda94dd
LP
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
5430f7f2 16 Lesser General Public License for more details.
5eda94dd 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
5eda94dd
LP
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"
79c07722 30#include "acl-util.h"
5eda94dd
LP
31
32static 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
63int 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;
501c92c4 69 int r = 0;
5eda94dd
LP
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
f4b47811 89 r = acl_find_uid(acl, old_uid, &entry);
5eda94dd
LP
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
f4b47811 108 r = acl_find_uid(acl, new_uid, &entry);
5eda94dd
LP
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
168finish:
169 acl_free(acl);
170
171 return r;
172}
173
174int 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
53907215 186 if (isempty(seat))
5eda94dd
LP
187 seat = "seat0";
188
189 e = udev_enumerate_new(udev);
190 if (!e)
191 return -ENOMEM;
192
7b3afbac
LP
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
5eda94dd
LP
199 r = udev_enumerate_add_match_tag(e, "uaccess");
200 if (r < 0)
201 goto finish;
202
5eda94dd
LP
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
53907215
LP
218 sn = udev_device_get_property_value(d, "ID_SEAT");
219 if (isempty(sn))
5eda94dd
LP
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);
5eda94dd 228 if (!node) {
d2d4b038 229 /* In case people mistag devices with nodes, we need to ignore this */
ce0f7c97 230 udev_device_unref(d);
d2d4b038 231 continue;
5eda94dd
LP
232 }
233
53907215
LP
234 log_debug("Fixing up %s for seat %s...", node, sn);
235
5eda94dd 236 r = devnode_acl(node, flush, del, old_uid, add, new_uid);
ce0f7c97
LP
237 udev_device_unref(d);
238
5eda94dd
LP
239 if (r < 0)
240 goto finish;
241 }
242
243finish:
244 if (e)
245 udev_enumerate_unref(e);
246
247 return r;
248}