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