1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999-2006 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed by Robert Watson for the TrustedBSD Project.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30 /*
31 * Developed by the TrustedBSD Project.
32 *
33 * ACL support routines specific to POSIX.1e access control lists. These are
34 * utility routines for code common across file systems implementing POSIX.1e
35 * ACLs.
36 */
37
38 #include <sys/cdefs.h>
39 #if 0
40 __FBSDID("$FreeBSD: head/sys/kern/subr_acl_posix1e.c 341827 2018-12-11 19:32:16Z mjg $");
41 #endif
42 __KERNEL_RCSID(0, "$NetBSD: subr_acl_posix1e.c,v 1.1 2020/05/16 18:31:50 christos Exp $");
43
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46 #include <sys/module.h>
47 #include <sys/systm.h>
48 #include <sys/mount.h>
49 #include <sys/vnode.h>
50 #include <sys/kauth.h>
51 #include <sys/errno.h>
52 #include <sys/stat.h>
53 #include <sys/acl.h>
54
55 /*
56 * For the purposes of filesystems maintaining the _OBJ entries in an inode
57 * with a mode_t field, this routine converts a mode_t entry to an
58 * acl_perm_t.
59 */
60 acl_perm_t
61 acl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode)
62 {
63 acl_perm_t perm = 0;
64
65 switch(tag) {
66 case ACL_USER_OBJ:
67 if (mode & S_IXUSR)
68 perm |= ACL_EXECUTE;
69 if (mode & S_IRUSR)
70 perm |= ACL_READ;
71 if (mode & S_IWUSR)
72 perm |= ACL_WRITE;
73 return (perm);
74
75 case ACL_GROUP_OBJ:
76 if (mode & S_IXGRP)
77 perm |= ACL_EXECUTE;
78 if (mode & S_IRGRP)
79 perm |= ACL_READ;
80 if (mode & S_IWGRP)
81 perm |= ACL_WRITE;
82 return (perm);
83
84 case ACL_OTHER:
85 if (mode & S_IXOTH)
86 perm |= ACL_EXECUTE;
87 if (mode & S_IROTH)
88 perm |= ACL_READ;
89 if (mode & S_IWOTH)
90 perm |= ACL_WRITE;
91 return (perm);
92
93 default:
94 printf("%s: invalid tag (%u)\n", __func__, tag);
95 return (0);
96 }
97 }
98
99 /*
100 * Given inode information (uid, gid, mode), return an acl entry of the
101 * appropriate type.
102 */
103 struct acl_entry
104 acl_posix1e_mode_to_entry(acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode)
105 {
106 struct acl_entry acl_entry;
107
108 acl_entry.ae_tag = tag;
109 acl_entry.ae_perm = acl_posix1e_mode_to_perm(tag, mode);
110 acl_entry.ae_entry_type = 0;
111 acl_entry.ae_flags = 0;
112 switch(tag) {
113 case ACL_USER_OBJ:
114 acl_entry.ae_id = uid;
115 break;
116
117 case ACL_GROUP_OBJ:
118 acl_entry.ae_id = gid;
119 break;
120
121 case ACL_OTHER:
122 acl_entry.ae_id = ACL_UNDEFINED_ID;
123 break;
124
125 default:
126 acl_entry.ae_id = ACL_UNDEFINED_ID;
127 printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag);
128 }
129
130 return (acl_entry);
131 }
132
133 /*
134 * Utility function to generate a file mode given appropriate ACL entries.
135 */
136 mode_t
137 acl_posix1e_perms_to_mode(struct acl_entry *acl_user_obj_entry,
138 struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry)
139 {
140 mode_t mode;
141
142 mode = 0;
143 if (acl_user_obj_entry->ae_perm & ACL_EXECUTE)
144 mode |= S_IXUSR;
145 if (acl_user_obj_entry->ae_perm & ACL_READ)
146 mode |= S_IRUSR;
147 if (acl_user_obj_entry->ae_perm & ACL_WRITE)
148 mode |= S_IWUSR;
149 if (acl_group_obj_entry->ae_perm & ACL_EXECUTE)
150 mode |= S_IXGRP;
151 if (acl_group_obj_entry->ae_perm & ACL_READ)
152 mode |= S_IRGRP;
153 if (acl_group_obj_entry->ae_perm & ACL_WRITE)
154 mode |= S_IWGRP;
155 if (acl_other_entry->ae_perm & ACL_EXECUTE)
156 mode |= S_IXOTH;
157 if (acl_other_entry->ae_perm & ACL_READ)
158 mode |= S_IROTH;
159 if (acl_other_entry->ae_perm & ACL_WRITE)
160 mode |= S_IWOTH;
161
162 return (mode);
163 }
164
165 /*
166 * Utility function to generate a file mode given a complete POSIX.1e access
167 * ACL. Note that if the ACL is improperly formed, this may result in a
168 * panic.
169 */
170 mode_t
171 acl_posix1e_acl_to_mode(struct acl *acl)
172 {
173 struct acl_entry *acl_mask, *acl_user_obj, *acl_group_obj, *acl_other;
174 int i;
175
176 /*
177 * Find the ACL entries relevant to a POSIX permission mode.
178 */
179 acl_user_obj = acl_group_obj = acl_other = acl_mask = NULL;
180 for (i = 0; i < acl->acl_cnt; i++) {
181 switch (acl->acl_entry[i].ae_tag) {
182 case ACL_USER_OBJ:
183 acl_user_obj = &acl->acl_entry[i];
184 break;
185
186 case ACL_GROUP_OBJ:
187 acl_group_obj = &acl->acl_entry[i];
188 break;
189
190 case ACL_OTHER:
191 acl_other = &acl->acl_entry[i];
192 break;
193
194 case ACL_MASK:
195 acl_mask = &acl->acl_entry[i];
196 break;
197
198 case ACL_USER:
199 case ACL_GROUP:
200 break;
201
202 default:
203 panic("acl_posix1e_acl_to_mode: bad ae_tag");
204 }
205 }
206
207 if (acl_user_obj == NULL || acl_group_obj == NULL || acl_other == NULL)
208 panic("acl_posix1e_acl_to_mode: missing base ae_tags");
209
210 /*
211 * POSIX.1e specifies that if there is an ACL_MASK entry, we replace
212 * the mode "group" bits with its permissions. If there isn't, we
213 * use the ACL_GROUP_OBJ permissions.
214 */
215 if (acl_mask != NULL)
216 return (acl_posix1e_perms_to_mode(acl_user_obj, acl_mask,
217 acl_other));
218 else
219 return (acl_posix1e_perms_to_mode(acl_user_obj, acl_group_obj,
220 acl_other));
221 }
222
223 /*
224 * Perform a syntactic check of the ACL, sufficient to allow an implementing
225 * filesystem to determine if it should accept this and rely on the POSIX.1e
226 * ACL properties.
227 */
228 int
229 acl_posix1e_check(struct acl *acl)
230 {
231 int num_acl_user_obj, num_acl_user, num_acl_group_obj, num_acl_group;
232 int num_acl_mask, num_acl_other, i;
233
234 /*
235 * Verify that the number of entries does not exceed the maximum
236 * defined for acl_t.
237 *
238 * Verify that the correct number of various sorts of ae_tags are
239 * present:
240 * Exactly one ACL_USER_OBJ
241 * Exactly one ACL_GROUP_OBJ
242 * Exactly one ACL_OTHER
243 * If any ACL_USER or ACL_GROUP entries appear, then exactly one
244 * ACL_MASK entry must also appear.
245 *
246 * Verify that all ae_perm entries are in ACL_PERM_BITS.
247 *
248 * Verify all ae_tag entries are understood by this implementation.
249 *
250 * Note: Does not check for uniqueness of qualifier (ae_id) field.
251 */
252 num_acl_user_obj = num_acl_user = num_acl_group_obj = num_acl_group =
253 num_acl_mask = num_acl_other = 0;
254 if (acl->acl_cnt > ACL_MAX_ENTRIES)
255 return (EINVAL);
256 for (i = 0; i < acl->acl_cnt; i++) {
257 struct acl_entry *ae = &acl->acl_entry[i];
258 /*
259 * Check for a valid tag.
260 */
261 switch(ae->ae_tag) {
262 case ACL_USER_OBJ:
263 ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
264 if (ae->ae_id != ACL_UNDEFINED_ID)
265 return (EINVAL);
266 num_acl_user_obj++;
267 break;
268 case ACL_GROUP_OBJ:
269 ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
270 if (ae->ae_id != ACL_UNDEFINED_ID)
271 return (EINVAL);
272 num_acl_group_obj++;
273 break;
274 case ACL_USER:
275 if (ae->ae_id == ACL_UNDEFINED_ID)
276 return (EINVAL);
277 num_acl_user++;
278 break;
279 case ACL_GROUP:
280 if (ae->ae_id == ACL_UNDEFINED_ID)
281 return (EINVAL);
282 num_acl_group++;
283 break;
284 case ACL_OTHER:
285 ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
286 if (ae->ae_id != ACL_UNDEFINED_ID)
287 return (EINVAL);
288 num_acl_other++;
289 break;
290 case ACL_MASK:
291 ae->ae_id = ACL_UNDEFINED_ID; /* XXX */
292 if (ae->ae_id != ACL_UNDEFINED_ID)
293 return (EINVAL);
294 num_acl_mask++;
295 break;
296 default:
297 return (EINVAL);
298 }
299 /*
300 * Check for valid perm entries.
301 */
302 if ((acl->acl_entry[i].ae_perm | ACL_PERM_BITS) !=
303 ACL_PERM_BITS)
304 return (EINVAL);
305 }
306 if ((num_acl_user_obj != 1) || (num_acl_group_obj != 1) ||
307 (num_acl_other != 1) || (num_acl_mask != 0 && num_acl_mask != 1))
308 return (EINVAL);
309 if (((num_acl_group != 0) || (num_acl_user != 0)) &&
310 (num_acl_mask != 1))
311 return (EINVAL);
312 return (0);
313 }
314
315 /*
316 * Given a requested mode for a new object, and a default ACL, combine the
317 * two to produce a new mode. Be careful not to clear any bits that aren't
318 * intended to be affected by the POSIX.1e ACL. Eventually, this might also
319 * take the cmask as an argument, if we push that down into
320 * per-filesystem-code.
321 */
322 mode_t
323 acl_posix1e_newfilemode(mode_t cmode, struct acl *dacl)
324 {
325 mode_t mode;
326
327 mode = cmode;
328 /*
329 * The current composition policy is that a permission bit must be
330 * set in *both* the ACL and the requested creation mode for it to
331 * appear in the resulting mode/ACL. First clear any possibly
332 * effected bits, then reconstruct.
333 */
334 mode &= ACL_PRESERVE_MASK;
335 mode |= (ACL_OVERRIDE_MASK & cmode & acl_posix1e_acl_to_mode(dacl));
336
337 return (mode);
338 }
Cache object: 0f0f8c4a55ff7cb94d881e7c851d91d0
|