xref: /titanic_44/usr/src/lib/libsec/common/aclutils.c (revision 27dd1e87cd3d939264769dd4af7e6a529cde001f)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
53eb3c573Smarks  * Common Development and Distribution License (the "License").
63eb3c573Smarks  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
22*27dd1e87SMark Shellenbaum  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23fa9e4066Sahrens  */
24fa9e4066Sahrens 
25fa9e4066Sahrens 
26fa9e4066Sahrens #include <stdlib.h>
27fa9e4066Sahrens #include <string.h>
28fa9e4066Sahrens #include <unistd.h>
29fa9e4066Sahrens #include <limits.h>
30fa9e4066Sahrens #include <grp.h>
31fa9e4066Sahrens #include <pwd.h>
323eb3c573Smarks #include <strings.h>
33fa9e4066Sahrens #include <sys/types.h>
34fa9e4066Sahrens #include <errno.h>
35fa9e4066Sahrens #include <sys/stat.h>
365a5eeccaSmarks #include <sys/varargs.h>
37fa9e4066Sahrens #include <locale.h>
38fa9e4066Sahrens #include <aclutils.h>
393eb3c573Smarks #include <sys/avl.h>
40da6c28aaSamw #include <acl_common.h>
41b249c65cSmarks #include <idmap.h>
42fa9e4066Sahrens 
43fa9e4066Sahrens #define	ACL_PATH	0
44fa9e4066Sahrens #define	ACL_FD		1
45fa9e4066Sahrens 
46d2443e76Smarks 
47fa9e4066Sahrens typedef union {
48fa9e4066Sahrens 	const char *file;
49fa9e4066Sahrens 	int  fd;
50fa9e4066Sahrens } acl_inp;
51fa9e4066Sahrens 
52fa9e4066Sahrens 
53fa9e4066Sahrens /*
54fa9e4066Sahrens  * Determine whether a file has a trivial ACL
55fa9e4066Sahrens  * returns: 	0 = trivial
56fa9e4066Sahrens  *		1 = nontrivial
57fa9e4066Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
58fa9e4066Sahrens  */
59fa9e4066Sahrens int
60fa9e4066Sahrens acl_trivial(const char *filename)
61fa9e4066Sahrens {
62fa9e4066Sahrens 	int acl_flavor;
63fa9e4066Sahrens 	int aclcnt;
64fa9e4066Sahrens 	int cntcmd;
65fa9e4066Sahrens 	int val = 0;
66fa9e4066Sahrens 	ace_t *acep;
67fa9e4066Sahrens 
68fa9e4066Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
69fa9e4066Sahrens 
70fa9e4066Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
71fa9e4066Sahrens 		cntcmd = ACE_GETACLCNT;
72fa9e4066Sahrens 	else
73fa9e4066Sahrens 		cntcmd = GETACLCNT;
74fa9e4066Sahrens 
75fa9e4066Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
76fa9e4066Sahrens 	if (aclcnt > 0) {
77fa9e4066Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
78fa9e4066Sahrens 			acep = malloc(sizeof (ace_t) * aclcnt);
79fa9e4066Sahrens 			if (acep == NULL)
80fa9e4066Sahrens 				return (-1);
81fa9e4066Sahrens 			if (acl(filename, ACE_GETACL,
82fa9e4066Sahrens 			    aclcnt, acep) < 0) {
83fa9e4066Sahrens 				free(acep);
84fa9e4066Sahrens 				return (-1);
85fa9e4066Sahrens 			}
86fa9e4066Sahrens 
87fa9e4066Sahrens 			val = ace_trivial(acep, aclcnt);
88fa9e4066Sahrens 			free(acep);
89d2443e76Smarks 
90fa9e4066Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
91fa9e4066Sahrens 			val = 1;
92fa9e4066Sahrens 	}
93fa9e4066Sahrens 	return (val);
94fa9e4066Sahrens }
95fa9e4066Sahrens 
963eb3c573Smarks 
970157963dSmarks static int
98fa9e4066Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
99fa9e4066Sahrens {
100fa9e4066Sahrens 	const char *fname;
101fa9e4066Sahrens 	int fd;
102fa9e4066Sahrens 	int ace_acl = 0;
103fa9e4066Sahrens 	int error;
104fa9e4066Sahrens 	int getcmd, cntcmd;
105fa9e4066Sahrens 	acl_t *acl_info;
106fa9e4066Sahrens 	int	save_errno;
107fa9e4066Sahrens 	int	stat_error;
108fa9e4066Sahrens 	struct stat64 statbuf;
109fa9e4066Sahrens 
110fa9e4066Sahrens 	*aclp = NULL;
111fa9e4066Sahrens 	if (type == ACL_PATH) {
112fa9e4066Sahrens 		fname = inp.file;
113fa9e4066Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
114fa9e4066Sahrens 	} else {
115fa9e4066Sahrens 		fd = inp.fd;
116fa9e4066Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
117fa9e4066Sahrens 	}
118fa9e4066Sahrens 
119fa9e4066Sahrens 	/*
120fa9e4066Sahrens 	 * if acl's aren't supported then
121fa9e4066Sahrens 	 * send it through the old GETACL interface
122fa9e4066Sahrens 	 */
123a222db82Smarks 	if (ace_acl == 0 || ace_acl == -1) {
124fa9e4066Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
125fa9e4066Sahrens 	}
126fa9e4066Sahrens 
127fa9e4066Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
128fa9e4066Sahrens 		cntcmd = ACE_GETACLCNT;
129fa9e4066Sahrens 		getcmd = ACE_GETACL;
130fa9e4066Sahrens 		acl_info = acl_alloc(ACE_T);
131fa9e4066Sahrens 	} else {
132fa9e4066Sahrens 		cntcmd = GETACLCNT;
133fa9e4066Sahrens 		getcmd = GETACL;
134fa9e4066Sahrens 		acl_info = acl_alloc(ACLENT_T);
135fa9e4066Sahrens 	}
136fa9e4066Sahrens 
137fa9e4066Sahrens 	if (acl_info == NULL)
138fa9e4066Sahrens 		return (-1);
139fa9e4066Sahrens 
140fa9e4066Sahrens 	if (type == ACL_PATH) {
141fa9e4066Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
142fa9e4066Sahrens 	} else {
143fa9e4066Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
144fa9e4066Sahrens 	}
145fa9e4066Sahrens 
146fa9e4066Sahrens 	save_errno = errno;
147fa9e4066Sahrens 	if (acl_info->acl_cnt < 0) {
148fa9e4066Sahrens 		acl_free(acl_info);
149fa9e4066Sahrens 		errno = save_errno;
150fa9e4066Sahrens 		return (-1);
151fa9e4066Sahrens 	}
152fa9e4066Sahrens 
153fa9e4066Sahrens 	if (acl_info->acl_cnt == 0) {
154fa9e4066Sahrens 		acl_free(acl_info);
155fa9e4066Sahrens 		errno = save_errno;
156fa9e4066Sahrens 		return (0);
157fa9e4066Sahrens 	}
158fa9e4066Sahrens 
159fa9e4066Sahrens 	acl_info->acl_aclp =
160fa9e4066Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
161fa9e4066Sahrens 	save_errno = errno;
162fa9e4066Sahrens 
163fa9e4066Sahrens 	if (acl_info->acl_aclp == NULL) {
164fa9e4066Sahrens 		acl_free(acl_info);
165fa9e4066Sahrens 		errno = save_errno;
166fa9e4066Sahrens 		return (-1);
167fa9e4066Sahrens 	}
168fa9e4066Sahrens 
169fa9e4066Sahrens 	if (type == ACL_PATH) {
170fa9e4066Sahrens 		stat_error = stat64(fname, &statbuf);
171fa9e4066Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
172fa9e4066Sahrens 		    acl_info->acl_aclp);
173fa9e4066Sahrens 	} else {
174fa9e4066Sahrens 		stat_error = fstat64(fd, &statbuf);
175fa9e4066Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
176fa9e4066Sahrens 		    acl_info->acl_aclp);
177fa9e4066Sahrens 	}
178fa9e4066Sahrens 
179fa9e4066Sahrens 	save_errno = errno;
180fa9e4066Sahrens 	if (error == -1) {
181fa9e4066Sahrens 		acl_free(acl_info);
182fa9e4066Sahrens 		errno = save_errno;
183fa9e4066Sahrens 		return (-1);
184fa9e4066Sahrens 	}
185fa9e4066Sahrens 
186fa9e4066Sahrens 
187fa9e4066Sahrens 	if (stat_error == 0) {
188fa9e4066Sahrens 		acl_info->acl_flags =
189fa9e4066Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
190fa9e4066Sahrens 	} else
191fa9e4066Sahrens 		acl_info->acl_flags = 0;
192fa9e4066Sahrens 
193fa9e4066Sahrens 	switch (acl_info->acl_type) {
194fa9e4066Sahrens 	case ACLENT_T:
195fa9e4066Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
196fa9e4066Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
197fa9e4066Sahrens 		break;
198fa9e4066Sahrens 	case ACE_T:
199fa9e4066Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
200fa9e4066Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
201fa9e4066Sahrens 		break;
202fa9e4066Sahrens 	default:
203fa9e4066Sahrens 		errno = EINVAL;
204fa9e4066Sahrens 		acl_free(acl_info);
205fa9e4066Sahrens 		return (-1);
206fa9e4066Sahrens 	}
207fa9e4066Sahrens 
208fa9e4066Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
209fa9e4066Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
210fa9e4066Sahrens 		acl_free(acl_info);
211fa9e4066Sahrens 		errno = 0;
212fa9e4066Sahrens 		return (0);
213fa9e4066Sahrens 	}
214fa9e4066Sahrens 
215fa9e4066Sahrens 	*aclp = acl_info;
216fa9e4066Sahrens 	return (0);
217fa9e4066Sahrens }
218fa9e4066Sahrens 
219fa9e4066Sahrens /*
220fa9e4066Sahrens  * return -1 on failure, otherwise the number of acl
221fa9e4066Sahrens  * entries is returned
222fa9e4066Sahrens  */
223fa9e4066Sahrens int
224fa9e4066Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
225fa9e4066Sahrens {
226fa9e4066Sahrens 	acl_inp acl_inp;
227fa9e4066Sahrens 	acl_inp.file = path;
228fa9e4066Sahrens 
229fa9e4066Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
230fa9e4066Sahrens }
231fa9e4066Sahrens 
232fa9e4066Sahrens int
233fa9e4066Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
234fa9e4066Sahrens {
235fa9e4066Sahrens 
236fa9e4066Sahrens 	acl_inp acl_inp;
237fa9e4066Sahrens 	acl_inp.fd = fd;
238fa9e4066Sahrens 
239fa9e4066Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
240fa9e4066Sahrens }
241fa9e4066Sahrens 
242fa9e4066Sahrens /*
243fa9e4066Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
244fa9e4066Sahrens  */
245fa9e4066Sahrens static int
246fa9e4066Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
247fa9e4066Sahrens {
248fa9e4066Sahrens 	int error = 0;
249fa9e4066Sahrens 	int acl_flavor_target;
250fa9e4066Sahrens 	struct stat64 statbuf;
251fa9e4066Sahrens 	int stat_error;
252fa9e4066Sahrens 	int isdir;
253fa9e4066Sahrens 
254fa9e4066Sahrens 
255fa9e4066Sahrens 	if (type == ACL_PATH) {
256fa9e4066Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
257fa9e4066Sahrens 		if (stat_error)
258fa9e4066Sahrens 			return (-1);
259fa9e4066Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
260fa9e4066Sahrens 	} else {
261fa9e4066Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
262fa9e4066Sahrens 		if (stat_error)
263fa9e4066Sahrens 			return (-1);
264fa9e4066Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
265fa9e4066Sahrens 	}
266fa9e4066Sahrens 
267a222db82Smarks 	/*
268a222db82Smarks 	 * If target returns an error or 0 from pathconf call then
269a222db82Smarks 	 * fall back to UFS/POSIX Draft interface.
270a222db82Smarks 	 * In the case of 0 we will then fail in either acl(2) or
271a222db82Smarks 	 * acl_translate().  We could erroneously get 0 back from
272a222db82Smarks 	 * a file system that is using fs_pathconf() and not answering
273a222db82Smarks 	 * the _PC_ACL_ENABLED question itself.
274a222db82Smarks 	 */
275a222db82Smarks 	if (acl_flavor_target == 0 || acl_flavor_target == -1)
276a222db82Smarks 		acl_flavor_target = _ACL_ACLENT_ENABLED;
277a222db82Smarks 
278fa9e4066Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
279fa9e4066Sahrens 
2803eb3c573Smarks 	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
2813eb3c573Smarks 	    statbuf.st_uid, statbuf.st_gid)) != 0) {
2823eb3c573Smarks 		return (error);
283fa9e4066Sahrens 	}
284fa9e4066Sahrens 
285fa9e4066Sahrens 	if (type == ACL_PATH) {
286fa9e4066Sahrens 		error = acl(acl_inp->file,
287fa9e4066Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
288fa9e4066Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
289fa9e4066Sahrens 	} else {
290fa9e4066Sahrens 		error = facl(acl_inp->fd,
291fa9e4066Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
292fa9e4066Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
293fa9e4066Sahrens 	}
294fa9e4066Sahrens 
295fa9e4066Sahrens 	return (error);
296fa9e4066Sahrens }
297fa9e4066Sahrens 
298fa9e4066Sahrens int
299fa9e4066Sahrens acl_set(const char *path, acl_t *aclp)
300fa9e4066Sahrens {
301fa9e4066Sahrens 	acl_inp acl_inp;
302fa9e4066Sahrens 
303fa9e4066Sahrens 	acl_inp.file = path;
304fa9e4066Sahrens 
305fa9e4066Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
306fa9e4066Sahrens }
307fa9e4066Sahrens 
308fa9e4066Sahrens int
309fa9e4066Sahrens facl_set(int fd, acl_t *aclp)
310fa9e4066Sahrens {
311fa9e4066Sahrens 	acl_inp acl_inp;
312fa9e4066Sahrens 
313fa9e4066Sahrens 	acl_inp.fd = fd;
314fa9e4066Sahrens 
315fa9e4066Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
316fa9e4066Sahrens }
317fa9e4066Sahrens 
318fa9e4066Sahrens int
319fa9e4066Sahrens acl_cnt(acl_t *aclp)
320fa9e4066Sahrens {
321fa9e4066Sahrens 	return (aclp->acl_cnt);
322fa9e4066Sahrens }
323fa9e4066Sahrens 
324fa9e4066Sahrens int
325fa9e4066Sahrens acl_type(acl_t *aclp)
326fa9e4066Sahrens {
327fa9e4066Sahrens 	return (aclp->acl_type);
328fa9e4066Sahrens }
329fa9e4066Sahrens 
330fa9e4066Sahrens acl_t *
331fa9e4066Sahrens acl_dup(acl_t *aclp)
332fa9e4066Sahrens {
333fa9e4066Sahrens 	acl_t *newaclp;
334fa9e4066Sahrens 
335fa9e4066Sahrens 	newaclp = acl_alloc(aclp->acl_type);
336fa9e4066Sahrens 	if (newaclp == NULL)
337fa9e4066Sahrens 		return (NULL);
338fa9e4066Sahrens 
339fa9e4066Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
340fa9e4066Sahrens 	if (newaclp->acl_aclp == NULL) {
341fa9e4066Sahrens 		acl_free(newaclp);
342fa9e4066Sahrens 		return (NULL);
343fa9e4066Sahrens 	}
344fa9e4066Sahrens 
345fa9e4066Sahrens 	(void) memcpy(newaclp->acl_aclp,
346fa9e4066Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
347fa9e4066Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
348fa9e4066Sahrens 
349fa9e4066Sahrens 	return (newaclp);
350fa9e4066Sahrens }
351fa9e4066Sahrens 
352fa9e4066Sahrens int
353fa9e4066Sahrens acl_flags(acl_t *aclp)
354fa9e4066Sahrens {
355fa9e4066Sahrens 	return (aclp->acl_flags);
356fa9e4066Sahrens }
357fa9e4066Sahrens 
358fa9e4066Sahrens void *
359fa9e4066Sahrens acl_data(acl_t *aclp)
360fa9e4066Sahrens {
361fa9e4066Sahrens 	return (aclp->acl_aclp);
362fa9e4066Sahrens }
363fa9e4066Sahrens 
364fa9e4066Sahrens /*
36549f0e518Smarks  * Take an acl array and build an acl_t.
36649f0e518Smarks  */
36749f0e518Smarks acl_t *
36849f0e518Smarks acl_to_aclp(enum acl_type type, void *acl, int count)
36949f0e518Smarks {
37049f0e518Smarks 	acl_t *aclp;
37149f0e518Smarks 
37249f0e518Smarks 
37349f0e518Smarks 	aclp = acl_alloc(type);
37449f0e518Smarks 	if (aclp == NULL)
37549f0e518Smarks 		return (aclp);
37649f0e518Smarks 
37749f0e518Smarks 	aclp->acl_aclp = acl;
37849f0e518Smarks 	aclp->acl_cnt = count;
37949f0e518Smarks 
38049f0e518Smarks 	return (aclp);
38149f0e518Smarks }
38249f0e518Smarks 
38349f0e518Smarks /*
384fa9e4066Sahrens  * Remove an ACL from a file and create a trivial ACL based
385fa9e4066Sahrens  * off of the mode argument.  After acl has been set owner/group
386fa9e4066Sahrens  * are updated to match owner,group arguments
387fa9e4066Sahrens  */
388fa9e4066Sahrens int
389fa9e4066Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
390fa9e4066Sahrens {
391fa9e4066Sahrens 	int	error = 0;
392fa9e4066Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
393*27dd1e87SMark Shellenbaum 	ace_t	*min_ace_acl;
394fa9e4066Sahrens 	int	acl_flavor;
395fa9e4066Sahrens 	int	aclcnt;
396fa9e4066Sahrens 
397fa9e4066Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
398fa9e4066Sahrens 
399fa9e4066Sahrens 	/*
400fa9e4066Sahrens 	 * force it through aclent flavor when file system doesn't
401fa9e4066Sahrens 	 * understand question
402fa9e4066Sahrens 	 */
403a222db82Smarks 	if (acl_flavor == 0 || acl_flavor == -1)
404fa9e4066Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
405fa9e4066Sahrens 
406fa9e4066Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
407fa9e4066Sahrens 		min_acl[0].a_type = USER_OBJ;
408fa9e4066Sahrens 		min_acl[0].a_id   = owner;
409fa9e4066Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
410fa9e4066Sahrens 		min_acl[1].a_type = GROUP_OBJ;
411fa9e4066Sahrens 		min_acl[1].a_id   = group;
412fa9e4066Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
413fa9e4066Sahrens 		min_acl[2].a_type = CLASS_OBJ;
414fa9e4066Sahrens 		min_acl[2].a_id   = (uid_t)-1;
415fa9e4066Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
416fa9e4066Sahrens 		min_acl[3].a_type = OTHER_OBJ;
417fa9e4066Sahrens 		min_acl[3].a_id   = (uid_t)-1;
418fa9e4066Sahrens 		min_acl[3].a_perm = (mode & 0007);
419fa9e4066Sahrens 		aclcnt = 4;
420fa9e4066Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
421fa9e4066Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
422*27dd1e87SMark Shellenbaum 		if ((error = acl_trivial_create(mode, &min_ace_acl,
423*27dd1e87SMark Shellenbaum 		    &aclcnt)) != 0)
424*27dd1e87SMark Shellenbaum 			return (error);
425*27dd1e87SMark Shellenbaum 		error = acl(file, ACE_SETACL, aclcnt, min_ace_acl);
426*27dd1e87SMark Shellenbaum 		free(min_ace_acl);
427fa9e4066Sahrens 	} else {
428fa9e4066Sahrens 		errno = EINVAL;
429fa9e4066Sahrens 		error = 1;
430fa9e4066Sahrens 	}
431fa9e4066Sahrens 
432fa9e4066Sahrens 	if (error == 0)
433fa9e4066Sahrens 		error = chown(file, owner, group);
434fa9e4066Sahrens 	return (error);
435fa9e4066Sahrens }
436fa9e4066Sahrens 
437fa9e4066Sahrens static int
438fa9e4066Sahrens ace_match(void *entry1, void *entry2)
439fa9e4066Sahrens {
440fa9e4066Sahrens 	ace_t *p1 = (ace_t *)entry1;
441fa9e4066Sahrens 	ace_t *p2 = (ace_t *)entry2;
442fa9e4066Sahrens 	ace_t ace1, ace2;
443fa9e4066Sahrens 
444fa9e4066Sahrens 	ace1 = *p1;
445fa9e4066Sahrens 	ace2 = *p2;
446fa9e4066Sahrens 
447fa9e4066Sahrens 	/*
448fa9e4066Sahrens 	 * Need to fixup who field for abstrations for
449fa9e4066Sahrens 	 * accurate comparison, since field is undefined.
450fa9e4066Sahrens 	 */
451fa9e4066Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
452f48205beScasper 		ace1.a_who = (uid_t)-1;
453fa9e4066Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
454f48205beScasper 		ace2.a_who = (uid_t)-1;
455fa9e4066Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
456fa9e4066Sahrens }
457fa9e4066Sahrens 
458fa9e4066Sahrens static int
459fa9e4066Sahrens aclent_match(void *entry1, void *entry2)
460fa9e4066Sahrens {
461fa9e4066Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
462fa9e4066Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
463fa9e4066Sahrens 
464fa9e4066Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
465fa9e4066Sahrens }
466fa9e4066Sahrens 
467fa9e4066Sahrens /*
468fa9e4066Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
469fa9e4066Sahrens  * is started from slot.  The flag argument indicates whether to
470fa9e4066Sahrens  * remove all matches or just the first match.
471fa9e4066Sahrens  */
472fa9e4066Sahrens int
473fa9e4066Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
474fa9e4066Sahrens {
475fa9e4066Sahrens 	int i, j;
476fa9e4066Sahrens 	int match;
477fa9e4066Sahrens 	int (*acl_match)(void *acl1, void *acl2);
478fa9e4066Sahrens 	void *acl_entry, *remove_entry;
479fa9e4066Sahrens 	void *start;
480fa9e4066Sahrens 	int found = 0;
481fa9e4066Sahrens 
482fa9e4066Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
483fa9e4066Sahrens 		flag = ACL_REMOVE_FIRST;
484fa9e4066Sahrens 
485fa9e4066Sahrens 	if (acl == NULL || removeacl == NULL)
486fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
487fa9e4066Sahrens 
488fa9e4066Sahrens 	if (acl->acl_type != removeacl->acl_type)
489fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
490fa9e4066Sahrens 
491fa9e4066Sahrens 	if (acl->acl_type == ACLENT_T)
492fa9e4066Sahrens 		acl_match = aclent_match;
493fa9e4066Sahrens 	else
494fa9e4066Sahrens 		acl_match = ace_match;
495fa9e4066Sahrens 
496fa9e4066Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
497fa9e4066Sahrens 	    i != removeacl->acl_cnt; i++) {
498fa9e4066Sahrens 
499fa9e4066Sahrens 		j = 0;
500fa9e4066Sahrens 		acl_entry = (char *)acl->acl_aclp +
501fa9e4066Sahrens 		    (acl->acl_entry_size * start_slot);
502fa9e4066Sahrens 		for (;;) {
503fa9e4066Sahrens 			match = acl_match(acl_entry, remove_entry);
504fa9e4066Sahrens 			if (match == 0)  {
505fa9e4066Sahrens 				found++;
50657841ad7SRenaud Manus 
50757841ad7SRenaud Manus 				/* avoid memmove if last entry */
50857841ad7SRenaud Manus 				if (acl->acl_cnt == (j + 1)) {
50957841ad7SRenaud Manus 					acl->acl_cnt--;
51057841ad7SRenaud Manus 					break;
51157841ad7SRenaud Manus 				}
51257841ad7SRenaud Manus 
513fa9e4066Sahrens 				start = (char *)acl_entry +
514fa9e4066Sahrens 				    acl->acl_entry_size;
515fa9e4066Sahrens 				(void) memmove(acl_entry, start,
516fa9e4066Sahrens 				    acl->acl_entry_size *
51757841ad7SRenaud Manus 				    (acl->acl_cnt-- - (j + 1)));
518fa9e4066Sahrens 
519fa9e4066Sahrens 				if (flag == ACL_REMOVE_FIRST)
520fa9e4066Sahrens 					break;
521fa9e4066Sahrens 				/*
522b249c65cSmarks 				 * List has changed, just continue so this
523b249c65cSmarks 				 * slot gets checked with it's new contents.
524fa9e4066Sahrens 				 */
525fa9e4066Sahrens 				continue;
526fa9e4066Sahrens 			}
527fa9e4066Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
528fa9e4066Sahrens 			if (++j >= acl->acl_cnt) {
529fa9e4066Sahrens 				break;
530fa9e4066Sahrens 			}
531fa9e4066Sahrens 		}
532b249c65cSmarks 		remove_entry = (char *)remove_entry + removeacl->acl_entry_size;
533fa9e4066Sahrens 	}
534fa9e4066Sahrens 
535fa9e4066Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
536fa9e4066Sahrens }
537fa9e4066Sahrens 
538fa9e4066Sahrens /*
539fa9e4066Sahrens  * Replace entires entries in acl1 with the corresponding entries
540fa9e4066Sahrens  * in newentries.  The where argument specifies where to begin
541fa9e4066Sahrens  * the replacement.  If the where argument is 1 greater than the
542fa9e4066Sahrens  * number of acl entries in acl1 then they are appended.  If the
543fa9e4066Sahrens  * where argument is 2+ greater than the number of acl entries then
544fa9e4066Sahrens  * EACL_INVALID_SLOT is returned.
545fa9e4066Sahrens  */
546fa9e4066Sahrens int
547fa9e4066Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
548fa9e4066Sahrens {
549fa9e4066Sahrens 
550fa9e4066Sahrens 	int slot;
551fa9e4066Sahrens 	int slots_needed;
552fa9e4066Sahrens 	int slots_left;
553fa9e4066Sahrens 	int newsize;
554fa9e4066Sahrens 
555fa9e4066Sahrens 	if (acl1 == NULL || newentries == NULL)
556fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
557fa9e4066Sahrens 
558fa9e4066Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
559fa9e4066Sahrens 		return (EACL_INVALID_SLOT);
560fa9e4066Sahrens 
561fa9e4066Sahrens 	if (acl1->acl_type != newentries->acl_type)
562fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
563fa9e4066Sahrens 
564fa9e4066Sahrens 	slot = where;
565fa9e4066Sahrens 
566fa9e4066Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
567fa9e4066Sahrens 	if (slots_left < newentries->acl_cnt) {
568fa9e4066Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
569fa9e4066Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
570fa9e4066Sahrens 		    (acl1->acl_entry_size * slots_needed);
571fa9e4066Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
572fa9e4066Sahrens 		if (acl1->acl_aclp == NULL)
573fa9e4066Sahrens 			return (-1);
574fa9e4066Sahrens 	}
575fa9e4066Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
576fa9e4066Sahrens 	    newentries->acl_aclp,
577fa9e4066Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
578fa9e4066Sahrens 
579fa9e4066Sahrens 	/*
580fa9e4066Sahrens 	 * Did ACL grow?
581fa9e4066Sahrens 	 */
582fa9e4066Sahrens 
583fa9e4066Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
584fa9e4066Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
585fa9e4066Sahrens 	}
586fa9e4066Sahrens 
587fa9e4066Sahrens 	return (0);
588fa9e4066Sahrens }
589fa9e4066Sahrens 
590fa9e4066Sahrens /*
591fa9e4066Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
592fa9e4066Sahrens  * to add the entries.
593fa9e4066Sahrens  */
594fa9e4066Sahrens int
595fa9e4066Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
596fa9e4066Sahrens {
597fa9e4066Sahrens 
598fa9e4066Sahrens 	int newsize;
599fa9e4066Sahrens 	int len;
600fa9e4066Sahrens 	void *start;
601fa9e4066Sahrens 	void *to;
602fa9e4066Sahrens 
603fa9e4066Sahrens 	if (acl1 == NULL || acl2 == NULL)
604fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
605fa9e4066Sahrens 
606fa9e4066Sahrens 	if (acl1->acl_type != acl2->acl_type)
607fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
608fa9e4066Sahrens 
609fa9e4066Sahrens 	/*
610fa9e4066Sahrens 	 * allow where to specify 1 past last slot for an append operation
611fa9e4066Sahrens 	 * but anything greater is an error.
612fa9e4066Sahrens 	 */
613fa9e4066Sahrens 	if (where < 0 || where > acl1->acl_cnt)
614fa9e4066Sahrens 		return (EACL_INVALID_SLOT);
615fa9e4066Sahrens 
616fa9e4066Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
617fa9e4066Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
618fa9e4066Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
619fa9e4066Sahrens 	if (acl1->acl_aclp == NULL)
620fa9e4066Sahrens 		return (-1);
621fa9e4066Sahrens 
622fa9e4066Sahrens 	/*
623fa9e4066Sahrens 	 * first push down entries where new ones will be inserted
624fa9e4066Sahrens 	 */
625fa9e4066Sahrens 
626fa9e4066Sahrens 	to = (void *)((char *)acl1->acl_aclp +
627fa9e4066Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
628fa9e4066Sahrens 
629fa9e4066Sahrens 	start = (void *)((char *)acl1->acl_aclp +
630fa9e4066Sahrens 	    where * acl1->acl_entry_size);
631fa9e4066Sahrens 
632fa9e4066Sahrens 	if (where < acl1->acl_cnt) {
633fa9e4066Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
634fa9e4066Sahrens 		(void) memmove(to, start, len);
635fa9e4066Sahrens 	}
636fa9e4066Sahrens 
637fa9e4066Sahrens 	/*
638fa9e4066Sahrens 	 * now stick in new entries.
639fa9e4066Sahrens 	 */
640fa9e4066Sahrens 
641fa9e4066Sahrens 	(void) memmove(start, acl2->acl_aclp,
642fa9e4066Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
643fa9e4066Sahrens 
644fa9e4066Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
645fa9e4066Sahrens 	return (0);
646fa9e4066Sahrens }
647fa9e4066Sahrens 
648fa9e4066Sahrens /*
649fa9e4066Sahrens  * return text for an ACL error.
650fa9e4066Sahrens  */
651fa9e4066Sahrens char *
652fa9e4066Sahrens acl_strerror(int errnum)
653fa9e4066Sahrens {
654fa9e4066Sahrens 	switch (errnum) {
655fa9e4066Sahrens 	case EACL_GRP_ERROR:
656fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
6575a5eeccaSmarks 		    "There is more than one group or default group entry"));
658fa9e4066Sahrens 	case EACL_USER_ERROR:
659fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
6605a5eeccaSmarks 		    "There is more than one user or default user entry"));
661fa9e4066Sahrens 	case EACL_OTHER_ERROR:
662fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
663fa9e4066Sahrens 		    "There is more than one other entry"));
664fa9e4066Sahrens 	case EACL_CLASS_ERROR:
665fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
666fa9e4066Sahrens 		    "There is more than one mask entry"));
667fa9e4066Sahrens 	case EACL_DUPLICATE_ERROR:
668fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
669fa9e4066Sahrens 		    "Duplicate user or group entries"));
670fa9e4066Sahrens 	case EACL_MISS_ERROR:
671fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
672fa9e4066Sahrens 		    "Missing user/group owner, other, mask entry"));
673fa9e4066Sahrens 	case EACL_MEM_ERROR:
674fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
675fa9e4066Sahrens 		    "Memory error"));
676fa9e4066Sahrens 	case EACL_ENTRY_ERROR:
677fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
678fa9e4066Sahrens 		    "Unrecognized entry type"));
679fa9e4066Sahrens 	case EACL_INHERIT_ERROR:
680fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
681fa9e4066Sahrens 		    "Invalid inheritance flags"));
682fa9e4066Sahrens 	case EACL_FLAGS_ERROR:
683fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
684fa9e4066Sahrens 		    "Unrecognized entry flags"));
685fa9e4066Sahrens 	case EACL_PERM_MASK_ERROR:
686fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
687fa9e4066Sahrens 		    "Invalid ACL permissions"));
688fa9e4066Sahrens 	case EACL_COUNT_ERROR:
689fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
690fa9e4066Sahrens 		    "Invalid ACL count"));
691fa9e4066Sahrens 	case EACL_INVALID_SLOT:
692fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
693fa9e4066Sahrens 		    "Invalid ACL entry number specified"));
694fa9e4066Sahrens 	case EACL_NO_ACL_ENTRY:
695fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
696fa9e4066Sahrens 		    "ACL entry doesn't exist"));
697fa9e4066Sahrens 	case EACL_DIFF_TYPE:
698fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
6992269545aSstephanie scheffler 		    "Different file system ACL types cannot be merged"));
700fa9e4066Sahrens 	case EACL_INVALID_USER_GROUP:
701fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
702fa9e4066Sahrens 	case EACL_INVALID_STR:
703fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
704fa9e4066Sahrens 	case EACL_FIELD_NOT_BLANK:
705fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
706fa9e4066Sahrens 	case EACL_INVALID_ACCESS_TYPE:
707fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
708fa9e4066Sahrens 	case EACL_UNKNOWN_DATA:
709fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
710fa9e4066Sahrens 	case EACL_MISSING_FIELDS:
711fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
712fa9e4066Sahrens 		    "ACL specification missing required fields"));
713fa9e4066Sahrens 	case EACL_INHERIT_NOTDIR:
714fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
715fa9e4066Sahrens 		    "Inheritance flags are only allowed on directories"));
716fa9e4066Sahrens 	case -1:
717fa9e4066Sahrens 		return (strerror(errno));
718fa9e4066Sahrens 	default:
719fa9e4066Sahrens 		errno = EINVAL;
720fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
721fa9e4066Sahrens 	}
722fa9e4066Sahrens }
7235a5eeccaSmarks 
7245a5eeccaSmarks extern int yyinteractive;
7255a5eeccaSmarks 
7265a5eeccaSmarks /* PRINTFLIKE1 */
7275a5eeccaSmarks void
7285a5eeccaSmarks acl_error(const char *fmt, ...)
7295a5eeccaSmarks {
7305a5eeccaSmarks 	va_list va;
7315a5eeccaSmarks 
7325a5eeccaSmarks 	if (yyinteractive == 0)
7335a5eeccaSmarks 		return;
7345a5eeccaSmarks 
7355a5eeccaSmarks 	va_start(va, fmt);
7365a5eeccaSmarks 	(void) vfprintf(stderr, fmt, va);
7375a5eeccaSmarks 	va_end(va);
7385a5eeccaSmarks }
739b249c65cSmarks 
740b249c65cSmarks int
741b249c65cSmarks sid_to_id(char *sid, boolean_t user, uid_t *id)
742b249c65cSmarks {
743b249c65cSmarks 	idmap_handle_t *idmap_hdl = NULL;
744b249c65cSmarks 	idmap_get_handle_t *get_hdl = NULL;
745b249c65cSmarks 	char *rid_start = NULL;
746b249c65cSmarks 	idmap_stat status;
747b249c65cSmarks 	char *end;
748909c9a9fSMark Shellenbaum 	int error = 1;
749b249c65cSmarks 	char *domain_start;
750b249c65cSmarks 
751b249c65cSmarks 	if ((domain_start = strchr(sid, '@')) == NULL) {
752b249c65cSmarks 		idmap_rid_t rid;
753b249c65cSmarks 
754b249c65cSmarks 		if ((rid_start = strrchr(sid, '-')) == NULL)
755909c9a9fSMark Shellenbaum 			return (1);
756b249c65cSmarks 		*rid_start++ = '\0';
757b249c65cSmarks 		errno = 0;
758b249c65cSmarks 		rid = strtoul(rid_start--, &end, 10);
759b249c65cSmarks 		if (errno == 0 && *end == '\0') {
760909c9a9fSMark Shellenbaum 			if (idmap_init(&idmap_hdl) == IDMAP_SUCCESS &&
761909c9a9fSMark Shellenbaum 			    idmap_get_create(idmap_hdl, &get_hdl) ==
762909c9a9fSMark Shellenbaum 			    IDMAP_SUCCESS) {
763b249c65cSmarks 				if (user)
764b249c65cSmarks 					error = idmap_get_uidbysid(get_hdl,
7653ee87bcaSJulian Pullen 					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
7663ee87bcaSJulian Pullen 					    id, &status);
767b249c65cSmarks 				else
768b249c65cSmarks 					error = idmap_get_gidbysid(get_hdl,
7693ee87bcaSJulian Pullen 					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
7703ee87bcaSJulian Pullen 					    id, &status);
771909c9a9fSMark Shellenbaum 				if (error == IDMAP_SUCCESS) {
772909c9a9fSMark Shellenbaum 					error = idmap_get_mappings(get_hdl);
773909c9a9fSMark Shellenbaum 					if (error == IDMAP_SUCCESS &&
774909c9a9fSMark Shellenbaum 					    status != IDMAP_SUCCESS)
775909c9a9fSMark Shellenbaum 						error = 1;
776909c9a9fSMark Shellenbaum 					else
777909c9a9fSMark Shellenbaum 						error = 0;
778b249c65cSmarks 				}
779b249c65cSmarks 			} else {
780909c9a9fSMark Shellenbaum 				error = 1;
781b249c65cSmarks 			}
782b249c65cSmarks 			if (get_hdl)
783b249c65cSmarks 				idmap_get_destroy(get_hdl);
784b249c65cSmarks 			if (idmap_hdl)
785b249c65cSmarks 				(void) idmap_fini(idmap_hdl);
786909c9a9fSMark Shellenbaum 		} else {
787909c9a9fSMark Shellenbaum 			error = 1;
788909c9a9fSMark Shellenbaum 		}
789b249c65cSmarks 		*rid_start = '-'; /* putback character removed earlier */
790b249c65cSmarks 	} else {
791b249c65cSmarks 		char *name = sid;
792b249c65cSmarks 		*domain_start++ = '\0';
793b249c65cSmarks 
794b249c65cSmarks 		if (user)
7953ee87bcaSJulian Pullen 			error = idmap_getuidbywinname(name, domain_start,
7963ee87bcaSJulian Pullen 			    IDMAP_REQ_FLG_USE_CACHE, id);
797b249c65cSmarks 		else
7983ee87bcaSJulian Pullen 			error = idmap_getgidbywinname(name, domain_start,
7993ee87bcaSJulian Pullen 			    IDMAP_REQ_FLG_USE_CACHE, id);
800b249c65cSmarks 		*--domain_start = '@';
801909c9a9fSMark Shellenbaum 		error = (error == IDMAP_SUCCESS) ? 0 : 1;
802b249c65cSmarks 	}
803b249c65cSmarks 
804b249c65cSmarks 	return (error);
805b249c65cSmarks }
806