1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51462Smarks  * Common Development and Distribution License (the "License").
61462Smarks  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
227057Smarks  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens 
27789Sahrens #include <stdlib.h>
28789Sahrens #include <string.h>
29789Sahrens #include <unistd.h>
30789Sahrens #include <limits.h>
31789Sahrens #include <grp.h>
32789Sahrens #include <pwd.h>
331462Smarks #include <strings.h>
34789Sahrens #include <sys/types.h>
35789Sahrens #include <errno.h>
36789Sahrens #include <sys/stat.h>
371420Smarks #include <sys/varargs.h>
38789Sahrens #include <locale.h>
39789Sahrens #include <aclutils.h>
405331Samw #include <sys/avl.h>
41789Sahrens #include <acl_common.h>
427057Smarks #include <idmap.h>
43789Sahrens 
44789Sahrens #define	ACL_PATH	0
45789Sahrens #define	ACL_FD		1
46789Sahrens 
471231Smarks 
48789Sahrens typedef union {
49789Sahrens 	const char *file;
50789Sahrens 	int  fd;
51789Sahrens } acl_inp;
52789Sahrens 
53789Sahrens 
54789Sahrens /*
55789Sahrens  * Determine whether a file has a trivial ACL
56789Sahrens  * returns: 	0 = trivial
57789Sahrens  *		1 = nontrivial
58789Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
59789Sahrens  */
60789Sahrens int
61789Sahrens acl_trivial(const char *filename)
62789Sahrens {
63789Sahrens 	int acl_flavor;
64789Sahrens 	int aclcnt;
65789Sahrens 	int cntcmd;
66789Sahrens 	int val = 0;
67789Sahrens 	ace_t *acep;
68789Sahrens 
69789Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
70789Sahrens 
71789Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
72789Sahrens 		cntcmd = ACE_GETACLCNT;
73789Sahrens 	else
74789Sahrens 		cntcmd = GETACLCNT;
75789Sahrens 
76789Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
77789Sahrens 	if (aclcnt > 0) {
78789Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
791231Smarks 			acep = malloc(sizeof (ace_t) * aclcnt);
801231Smarks 			if (acep == NULL)
811231Smarks 				return (-1);
821231Smarks 			if (acl(filename, ACE_GETACL,
831231Smarks 			    aclcnt, acep) < 0) {
841231Smarks 				free(acep);
851231Smarks 				return (-1);
861231Smarks 			}
87789Sahrens 
881231Smarks 			val = ace_trivial(acep, aclcnt);
891231Smarks 			free(acep);
901231Smarks 
91789Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
92789Sahrens 			val = 1;
93789Sahrens 	}
94789Sahrens 	return (val);
95789Sahrens }
96789Sahrens 
971462Smarks 
981477Smarks static int
99789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
100789Sahrens {
101789Sahrens 	const char *fname;
102789Sahrens 	int fd;
103789Sahrens 	int ace_acl = 0;
104789Sahrens 	int error;
105789Sahrens 	int getcmd, cntcmd;
106789Sahrens 	acl_t *acl_info;
107789Sahrens 	int	save_errno;
108789Sahrens 	int	stat_error;
109789Sahrens 	struct stat64 statbuf;
110789Sahrens 
111789Sahrens 	*aclp = NULL;
112789Sahrens 	if (type == ACL_PATH) {
113789Sahrens 		fname = inp.file;
114789Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
115789Sahrens 	} else {
116789Sahrens 		fd = inp.fd;
117789Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
118789Sahrens 	}
119789Sahrens 
120789Sahrens 	/*
121789Sahrens 	 * if acl's aren't supported then
122789Sahrens 	 * send it through the old GETACL interface
123789Sahrens 	 */
1241666Smarks 	if (ace_acl == 0 || ace_acl == -1) {
125789Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
126789Sahrens 	}
127789Sahrens 
128789Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
129789Sahrens 		cntcmd = ACE_GETACLCNT;
130789Sahrens 		getcmd = ACE_GETACL;
131789Sahrens 		acl_info = acl_alloc(ACE_T);
132789Sahrens 	} else {
133789Sahrens 		cntcmd = GETACLCNT;
134789Sahrens 		getcmd = GETACL;
135789Sahrens 		acl_info = acl_alloc(ACLENT_T);
136789Sahrens 	}
137789Sahrens 
138789Sahrens 	if (acl_info == NULL)
139789Sahrens 		return (-1);
140789Sahrens 
141789Sahrens 	if (type == ACL_PATH) {
142789Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
143789Sahrens 	} else {
144789Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
145789Sahrens 	}
146789Sahrens 
147789Sahrens 	save_errno = errno;
148789Sahrens 	if (acl_info->acl_cnt < 0) {
149789Sahrens 		acl_free(acl_info);
150789Sahrens 		errno = save_errno;
151789Sahrens 		return (-1);
152789Sahrens 	}
153789Sahrens 
154789Sahrens 	if (acl_info->acl_cnt == 0) {
155789Sahrens 		acl_free(acl_info);
156789Sahrens 		errno = save_errno;
157789Sahrens 		return (0);
158789Sahrens 	}
159789Sahrens 
160789Sahrens 	acl_info->acl_aclp =
161789Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
162789Sahrens 	save_errno = errno;
163789Sahrens 
164789Sahrens 	if (acl_info->acl_aclp == NULL) {
165789Sahrens 		acl_free(acl_info);
166789Sahrens 		errno = save_errno;
167789Sahrens 		return (-1);
168789Sahrens 	}
169789Sahrens 
170789Sahrens 	if (type == ACL_PATH) {
171789Sahrens 		stat_error = stat64(fname, &statbuf);
172789Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
173789Sahrens 		    acl_info->acl_aclp);
174789Sahrens 	} else {
175789Sahrens 		stat_error = fstat64(fd, &statbuf);
176789Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
177789Sahrens 		    acl_info->acl_aclp);
178789Sahrens 	}
179789Sahrens 
180789Sahrens 	save_errno = errno;
181789Sahrens 	if (error == -1) {
182789Sahrens 		acl_free(acl_info);
183789Sahrens 		errno = save_errno;
184789Sahrens 		return (-1);
185789Sahrens 	}
186789Sahrens 
187789Sahrens 
188789Sahrens 	if (stat_error == 0) {
189789Sahrens 		acl_info->acl_flags =
190789Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
191789Sahrens 	} else
192789Sahrens 		acl_info->acl_flags = 0;
193789Sahrens 
194789Sahrens 	switch (acl_info->acl_type) {
195789Sahrens 	case ACLENT_T:
196789Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
197789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
198789Sahrens 		break;
199789Sahrens 	case ACE_T:
200789Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
201789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
202789Sahrens 		break;
203789Sahrens 	default:
204789Sahrens 		errno = EINVAL;
205789Sahrens 		acl_free(acl_info);
206789Sahrens 		return (-1);
207789Sahrens 	}
208789Sahrens 
209789Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
210789Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
211789Sahrens 		acl_free(acl_info);
212789Sahrens 		errno = 0;
213789Sahrens 		return (0);
214789Sahrens 	}
215789Sahrens 
216789Sahrens 	*aclp = acl_info;
217789Sahrens 	return (0);
218789Sahrens }
219789Sahrens 
220789Sahrens /*
221789Sahrens  * return -1 on failure, otherwise the number of acl
222789Sahrens  * entries is returned
223789Sahrens  */
224789Sahrens int
225789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
226789Sahrens {
227789Sahrens 	acl_inp acl_inp;
228789Sahrens 	acl_inp.file = path;
229789Sahrens 
230789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
231789Sahrens }
232789Sahrens 
233789Sahrens int
234789Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
235789Sahrens {
236789Sahrens 
237789Sahrens 	acl_inp acl_inp;
238789Sahrens 	acl_inp.fd = fd;
239789Sahrens 
240789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
241789Sahrens }
242789Sahrens 
243789Sahrens /*
244789Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
245789Sahrens  */
246789Sahrens static int
247789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
248789Sahrens {
249789Sahrens 	int error = 0;
250789Sahrens 	int acl_flavor_target;
251789Sahrens 	struct stat64 statbuf;
252789Sahrens 	int stat_error;
253789Sahrens 	int isdir;
254789Sahrens 
255789Sahrens 
256789Sahrens 	if (type == ACL_PATH) {
257789Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
258789Sahrens 		if (stat_error)
259789Sahrens 			return (-1);
260789Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
261789Sahrens 	} else {
262789Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
263789Sahrens 		if (stat_error)
264789Sahrens 			return (-1);
265789Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
266789Sahrens 	}
267789Sahrens 
2681666Smarks 	/*
2691666Smarks 	 * If target returns an error or 0 from pathconf call then
2701666Smarks 	 * fall back to UFS/POSIX Draft interface.
2711666Smarks 	 * In the case of 0 we will then fail in either acl(2) or
2721666Smarks 	 * acl_translate().  We could erroneously get 0 back from
2731666Smarks 	 * a file system that is using fs_pathconf() and not answering
2741666Smarks 	 * the _PC_ACL_ENABLED question itself.
2751666Smarks 	 */
2761666Smarks 	if (acl_flavor_target == 0 || acl_flavor_target == -1)
2771666Smarks 		acl_flavor_target = _ACL_ACLENT_ENABLED;
2781666Smarks 
279789Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
280789Sahrens 
2811462Smarks 	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
2821462Smarks 	    statbuf.st_uid, statbuf.st_gid)) != 0) {
2831462Smarks 		return (error);
284789Sahrens 	}
285789Sahrens 
286789Sahrens 	if (type == ACL_PATH) {
287789Sahrens 		error = acl(acl_inp->file,
288789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
289789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
290789Sahrens 	} else {
291789Sahrens 		error = facl(acl_inp->fd,
292789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
293789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
294789Sahrens 	}
295789Sahrens 
296789Sahrens 	return (error);
297789Sahrens }
298789Sahrens 
299789Sahrens int
300789Sahrens acl_set(const char *path, acl_t *aclp)
301789Sahrens {
302789Sahrens 	acl_inp acl_inp;
303789Sahrens 
304789Sahrens 	acl_inp.file = path;
305789Sahrens 
306789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
307789Sahrens }
308789Sahrens 
309789Sahrens int
310789Sahrens facl_set(int fd, acl_t *aclp)
311789Sahrens {
312789Sahrens 	acl_inp acl_inp;
313789Sahrens 
314789Sahrens 	acl_inp.fd = fd;
315789Sahrens 
316789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
317789Sahrens }
318789Sahrens 
319789Sahrens int
320789Sahrens acl_cnt(acl_t *aclp)
321789Sahrens {
322789Sahrens 	return (aclp->acl_cnt);
323789Sahrens }
324789Sahrens 
325789Sahrens int
326789Sahrens acl_type(acl_t *aclp)
327789Sahrens {
328789Sahrens 	return (aclp->acl_type);
329789Sahrens }
330789Sahrens 
331789Sahrens acl_t *
332789Sahrens acl_dup(acl_t *aclp)
333789Sahrens {
334789Sahrens 	acl_t *newaclp;
335789Sahrens 
336789Sahrens 	newaclp = acl_alloc(aclp->acl_type);
337789Sahrens 	if (newaclp == NULL)
338789Sahrens 		return (NULL);
339789Sahrens 
340789Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
341789Sahrens 	if (newaclp->acl_aclp == NULL) {
342789Sahrens 		acl_free(newaclp);
343789Sahrens 		return (NULL);
344789Sahrens 	}
345789Sahrens 
346789Sahrens 	(void) memcpy(newaclp->acl_aclp,
347789Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
348789Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
349789Sahrens 
350789Sahrens 	return (newaclp);
351789Sahrens }
352789Sahrens 
353789Sahrens int
354789Sahrens acl_flags(acl_t *aclp)
355789Sahrens {
356789Sahrens 	return (aclp->acl_flags);
357789Sahrens }
358789Sahrens 
359789Sahrens void *
360789Sahrens acl_data(acl_t *aclp)
361789Sahrens {
362789Sahrens 	return (aclp->acl_aclp);
363789Sahrens }
364789Sahrens 
365789Sahrens /*
3661953Smarks  * Take an acl array and build an acl_t.
3671953Smarks  */
3681953Smarks acl_t *
3691953Smarks acl_to_aclp(enum acl_type type, void *acl, int count)
3701953Smarks {
3711953Smarks 	acl_t *aclp;
3721953Smarks 
3731953Smarks 
3741953Smarks 	aclp = acl_alloc(type);
3751953Smarks 	if (aclp == NULL)
3761953Smarks 		return (aclp);
3771953Smarks 
3781953Smarks 	aclp->acl_aclp = acl;
3791953Smarks 	aclp->acl_cnt = count;
3801953Smarks 
3811953Smarks 	return (aclp);
3821953Smarks }
3831953Smarks 
3841953Smarks /*
385789Sahrens  * Remove an ACL from a file and create a trivial ACL based
386789Sahrens  * off of the mode argument.  After acl has been set owner/group
387789Sahrens  * are updated to match owner,group arguments
388789Sahrens  */
389789Sahrens int
390789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
391789Sahrens {
392789Sahrens 	int	error = 0;
393789Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
394789Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
395789Sahrens 	int	acl_flavor;
396789Sahrens 	int	aclcnt;
397789Sahrens 
398789Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
399789Sahrens 
400789Sahrens 	/*
401789Sahrens 	 * force it through aclent flavor when file system doesn't
402789Sahrens 	 * understand question
403789Sahrens 	 */
4041666Smarks 	if (acl_flavor == 0 || acl_flavor == -1)
405789Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
406789Sahrens 
407789Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
408789Sahrens 		min_acl[0].a_type = USER_OBJ;
409789Sahrens 		min_acl[0].a_id   = owner;
410789Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
411789Sahrens 		min_acl[1].a_type = GROUP_OBJ;
412789Sahrens 		min_acl[1].a_id   = group;
413789Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
414789Sahrens 		min_acl[2].a_type = CLASS_OBJ;
415789Sahrens 		min_acl[2].a_id   = (uid_t)-1;
416789Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
417789Sahrens 		min_acl[3].a_type = OTHER_OBJ;
418789Sahrens 		min_acl[3].a_id   = (uid_t)-1;
419789Sahrens 		min_acl[3].a_perm = (mode & 0007);
420789Sahrens 		aclcnt = 4;
421789Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
422789Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
423789Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
424789Sahrens 
425789Sahrens 		/*
426789Sahrens 		 * Make aces match request mode
427789Sahrens 		 */
428789Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
429789Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
430789Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
431789Sahrens 
432789Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
433789Sahrens 	} else {
434789Sahrens 		errno = EINVAL;
435789Sahrens 		error = 1;
436789Sahrens 	}
437789Sahrens 
438789Sahrens 	if (error == 0)
439789Sahrens 		error = chown(file, owner, group);
440789Sahrens 	return (error);
441789Sahrens }
442789Sahrens 
443789Sahrens static int
444789Sahrens ace_match(void *entry1, void *entry2)
445789Sahrens {
446789Sahrens 	ace_t *p1 = (ace_t *)entry1;
447789Sahrens 	ace_t *p2 = (ace_t *)entry2;
448789Sahrens 	ace_t ace1, ace2;
449789Sahrens 
450789Sahrens 	ace1 = *p1;
451789Sahrens 	ace2 = *p2;
452789Sahrens 
453789Sahrens 	/*
454789Sahrens 	 * Need to fixup who field for abstrations for
455789Sahrens 	 * accurate comparison, since field is undefined.
456789Sahrens 	 */
457789Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
4584321Scasper 		ace1.a_who = (uid_t)-1;
459789Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
4604321Scasper 		ace2.a_who = (uid_t)-1;
461789Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
462789Sahrens }
463789Sahrens 
464789Sahrens static int
465789Sahrens aclent_match(void *entry1, void *entry2)
466789Sahrens {
467789Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
468789Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
469789Sahrens 
470789Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
471789Sahrens }
472789Sahrens 
473789Sahrens /*
474789Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
475789Sahrens  * is started from slot.  The flag argument indicates whether to
476789Sahrens  * remove all matches or just the first match.
477789Sahrens  */
478789Sahrens int
479789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
480789Sahrens {
481789Sahrens 	int i, j;
482789Sahrens 	int match;
483789Sahrens 	int (*acl_match)(void *acl1, void *acl2);
484789Sahrens 	void *acl_entry, *remove_entry;
485789Sahrens 	void *start;
486789Sahrens 	int found = 0;
487789Sahrens 
488789Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
489789Sahrens 		flag = ACL_REMOVE_FIRST;
490789Sahrens 
491789Sahrens 	if (acl == NULL || removeacl == NULL)
492789Sahrens 		return (EACL_NO_ACL_ENTRY);
493789Sahrens 
494789Sahrens 	if (acl->acl_type != removeacl->acl_type)
495789Sahrens 		return (EACL_DIFF_TYPE);
496789Sahrens 
497789Sahrens 	if (acl->acl_type == ACLENT_T)
498789Sahrens 		acl_match = aclent_match;
499789Sahrens 	else
500789Sahrens 		acl_match = ace_match;
501789Sahrens 
502789Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
503789Sahrens 	    i != removeacl->acl_cnt; i++) {
504789Sahrens 
505789Sahrens 		j = 0;
506789Sahrens 		acl_entry = (char *)acl->acl_aclp +
507789Sahrens 		    (acl->acl_entry_size * start_slot);
508789Sahrens 		for (;;) {
509789Sahrens 			match = acl_match(acl_entry, remove_entry);
510789Sahrens 			if (match == 0)  {
511789Sahrens 				found++;
512789Sahrens 				start = (char *)acl_entry +
513789Sahrens 				    acl->acl_entry_size;
514789Sahrens 				(void) memmove(acl_entry, start,
515789Sahrens 				    acl->acl_entry_size *
516789Sahrens 				    acl->acl_cnt-- - (j + 1));
517789Sahrens 
518789Sahrens 				if (flag == ACL_REMOVE_FIRST)
519789Sahrens 					break;
520789Sahrens 				/*
5217057Smarks 				 * List has changed, just continue so this
5227057Smarks 				 * slot gets checked with it's new contents.
523789Sahrens 				 */
524789Sahrens 				continue;
525789Sahrens 			}
526789Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
527789Sahrens 			if (++j >= acl->acl_cnt) {
528789Sahrens 				break;
529789Sahrens 			}
530789Sahrens 		}
5317057Smarks 		remove_entry = (char *)remove_entry + removeacl->acl_entry_size;
532789Sahrens 	}
533789Sahrens 
534789Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
535789Sahrens }
536789Sahrens 
537789Sahrens /*
538789Sahrens  * Replace entires entries in acl1 with the corresponding entries
539789Sahrens  * in newentries.  The where argument specifies where to begin
540789Sahrens  * the replacement.  If the where argument is 1 greater than the
541789Sahrens  * number of acl entries in acl1 then they are appended.  If the
542789Sahrens  * where argument is 2+ greater than the number of acl entries then
543789Sahrens  * EACL_INVALID_SLOT is returned.
544789Sahrens  */
545789Sahrens int
546789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
547789Sahrens {
548789Sahrens 
549789Sahrens 	int slot;
550789Sahrens 	int slots_needed;
551789Sahrens 	int slots_left;
552789Sahrens 	int newsize;
553789Sahrens 
554789Sahrens 	if (acl1 == NULL || newentries == NULL)
555789Sahrens 		return (EACL_NO_ACL_ENTRY);
556789Sahrens 
557789Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
558789Sahrens 		return (EACL_INVALID_SLOT);
559789Sahrens 
560789Sahrens 	if (acl1->acl_type != newentries->acl_type)
561789Sahrens 		return (EACL_DIFF_TYPE);
562789Sahrens 
563789Sahrens 	slot = where;
564789Sahrens 
565789Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
566789Sahrens 	if (slots_left < newentries->acl_cnt) {
567789Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
568789Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
569789Sahrens 		    (acl1->acl_entry_size * slots_needed);
570789Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
571789Sahrens 		if (acl1->acl_aclp == NULL)
572789Sahrens 			return (-1);
573789Sahrens 	}
574789Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
575789Sahrens 	    newentries->acl_aclp,
576789Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
577789Sahrens 
578789Sahrens 	/*
579789Sahrens 	 * Did ACL grow?
580789Sahrens 	 */
581789Sahrens 
582789Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
583789Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
584789Sahrens 	}
585789Sahrens 
586789Sahrens 	return (0);
587789Sahrens }
588789Sahrens 
589789Sahrens /*
590789Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
591789Sahrens  * to add the entries.
592789Sahrens  */
593789Sahrens int
594789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
595789Sahrens {
596789Sahrens 
597789Sahrens 	int newsize;
598789Sahrens 	int len;
599789Sahrens 	void *start;
600789Sahrens 	void *to;
601789Sahrens 
602789Sahrens 	if (acl1 == NULL || acl2 == NULL)
603789Sahrens 		return (EACL_NO_ACL_ENTRY);
604789Sahrens 
605789Sahrens 	if (acl1->acl_type != acl2->acl_type)
606789Sahrens 		return (EACL_DIFF_TYPE);
607789Sahrens 
608789Sahrens 	/*
609789Sahrens 	 * allow where to specify 1 past last slot for an append operation
610789Sahrens 	 * but anything greater is an error.
611789Sahrens 	 */
612789Sahrens 	if (where < 0 || where > acl1->acl_cnt)
613789Sahrens 		return (EACL_INVALID_SLOT);
614789Sahrens 
615789Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
616789Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
617789Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
618789Sahrens 	if (acl1->acl_aclp == NULL)
619789Sahrens 		return (-1);
620789Sahrens 
621789Sahrens 	/*
622789Sahrens 	 * first push down entries where new ones will be inserted
623789Sahrens 	 */
624789Sahrens 
625789Sahrens 	to = (void *)((char *)acl1->acl_aclp +
626789Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
627789Sahrens 
628789Sahrens 	start = (void *)((char *)acl1->acl_aclp +
629789Sahrens 	    where * acl1->acl_entry_size);
630789Sahrens 
631789Sahrens 	if (where < acl1->acl_cnt) {
632789Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
633789Sahrens 		(void) memmove(to, start, len);
634789Sahrens 	}
635789Sahrens 
636789Sahrens 	/*
637789Sahrens 	 * now stick in new entries.
638789Sahrens 	 */
639789Sahrens 
640789Sahrens 	(void) memmove(start, acl2->acl_aclp,
641789Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
642789Sahrens 
643789Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
644789Sahrens 	return (0);
645789Sahrens }
646789Sahrens 
647789Sahrens /*
648789Sahrens  * return text for an ACL error.
649789Sahrens  */
650789Sahrens char *
651789Sahrens acl_strerror(int errnum)
652789Sahrens {
653789Sahrens 	switch (errnum) {
654789Sahrens 	case EACL_GRP_ERROR:
655789Sahrens 		return (dgettext(TEXT_DOMAIN,
6561420Smarks 		    "There is more than one group or default group entry"));
657789Sahrens 	case EACL_USER_ERROR:
658789Sahrens 		return (dgettext(TEXT_DOMAIN,
6591420Smarks 		    "There is more than one user or default user entry"));
660789Sahrens 	case EACL_OTHER_ERROR:
661789Sahrens 		return (dgettext(TEXT_DOMAIN,
662789Sahrens 		    "There is more than one other entry"));
663789Sahrens 	case EACL_CLASS_ERROR:
664789Sahrens 		return (dgettext(TEXT_DOMAIN,
665789Sahrens 		    "There is more than one mask entry"));
666789Sahrens 	case EACL_DUPLICATE_ERROR:
667789Sahrens 		return (dgettext(TEXT_DOMAIN,
668789Sahrens 		    "Duplicate user or group entries"));
669789Sahrens 	case EACL_MISS_ERROR:
670789Sahrens 		return (dgettext(TEXT_DOMAIN,
671789Sahrens 		    "Missing user/group owner, other, mask entry"));
672789Sahrens 	case EACL_MEM_ERROR:
673789Sahrens 		return (dgettext(TEXT_DOMAIN,
674789Sahrens 		    "Memory error"));
675789Sahrens 	case EACL_ENTRY_ERROR:
676789Sahrens 		return (dgettext(TEXT_DOMAIN,
677789Sahrens 		    "Unrecognized entry type"));
678789Sahrens 	case EACL_INHERIT_ERROR:
679789Sahrens 		return (dgettext(TEXT_DOMAIN,
680789Sahrens 		    "Invalid inheritance flags"));
681789Sahrens 	case EACL_FLAGS_ERROR:
682789Sahrens 		return (dgettext(TEXT_DOMAIN,
683789Sahrens 		    "Unrecognized entry flags"));
684789Sahrens 	case EACL_PERM_MASK_ERROR:
685789Sahrens 		return (dgettext(TEXT_DOMAIN,
686789Sahrens 		    "Invalid ACL permissions"));
687789Sahrens 	case EACL_COUNT_ERROR:
688789Sahrens 		return (dgettext(TEXT_DOMAIN,
689789Sahrens 		    "Invalid ACL count"));
690789Sahrens 	case EACL_INVALID_SLOT:
691789Sahrens 		return (dgettext(TEXT_DOMAIN,
692789Sahrens 		    "Invalid ACL entry number specified"));
693789Sahrens 	case EACL_NO_ACL_ENTRY:
694789Sahrens 		return (dgettext(TEXT_DOMAIN,
695789Sahrens 		    "ACL entry doesn't exist"));
696789Sahrens 	case EACL_DIFF_TYPE:
697789Sahrens 		return (dgettext(TEXT_DOMAIN,
698789Sahrens 		    "ACL type's are different"));
699789Sahrens 	case EACL_INVALID_USER_GROUP:
700789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
701789Sahrens 	case EACL_INVALID_STR:
702789Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
703789Sahrens 	case EACL_FIELD_NOT_BLANK:
704789Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
705789Sahrens 	case EACL_INVALID_ACCESS_TYPE:
706789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
707789Sahrens 	case EACL_UNKNOWN_DATA:
708789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
709789Sahrens 	case EACL_MISSING_FIELDS:
710789Sahrens 		return (dgettext(TEXT_DOMAIN,
711789Sahrens 		    "ACL specification missing required fields"));
712789Sahrens 	case EACL_INHERIT_NOTDIR:
713789Sahrens 		return (dgettext(TEXT_DOMAIN,
714789Sahrens 		    "Inheritance flags are only allowed on directories"));
715789Sahrens 	case -1:
716789Sahrens 		return (strerror(errno));
717789Sahrens 	default:
718789Sahrens 		errno = EINVAL;
719789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
720789Sahrens 	}
721789Sahrens }
7221420Smarks 
7231420Smarks extern int yyinteractive;
7241420Smarks 
7251420Smarks /* PRINTFLIKE1 */
7261420Smarks void
7271420Smarks acl_error(const char *fmt, ...)
7281420Smarks {
7291420Smarks 	va_list va;
7301420Smarks 
7311420Smarks 	if (yyinteractive == 0)
7321420Smarks 		return;
7331420Smarks 
7341420Smarks 	va_start(va, fmt);
7351420Smarks 	(void) vfprintf(stderr, fmt, va);
7361420Smarks 	va_end(va);
7371420Smarks }
7387057Smarks 
7397057Smarks int
7407057Smarks sid_to_id(char *sid, boolean_t user, uid_t *id)
7417057Smarks {
7427057Smarks 	idmap_handle_t *idmap_hdl = NULL;
7437057Smarks 	idmap_get_handle_t *get_hdl = NULL;
7447057Smarks 	char *rid_start = NULL;
7457057Smarks 	idmap_stat status;
7467057Smarks 	char *end;
747*7680SMark.Shellenbaum@Sun.COM 	int error = 1;
7487057Smarks 	char *domain_start;
7497057Smarks 
7507057Smarks 	if ((domain_start = strchr(sid, '@')) == NULL) {
7517057Smarks 		idmap_rid_t rid;
7527057Smarks 
7537057Smarks 		if ((rid_start = strrchr(sid, '-')) == NULL)
754*7680SMark.Shellenbaum@Sun.COM 			return (1);
7557057Smarks 		*rid_start++ = '\0';
7567057Smarks 		errno = 0;
7577057Smarks 		rid = strtoul(rid_start--, &end, 10);
7587057Smarks 		if (errno == 0 && *end == '\0') {
759*7680SMark.Shellenbaum@Sun.COM 			if (idmap_init(&idmap_hdl) == IDMAP_SUCCESS &&
760*7680SMark.Shellenbaum@Sun.COM 			    idmap_get_create(idmap_hdl, &get_hdl) ==
761*7680SMark.Shellenbaum@Sun.COM 			    IDMAP_SUCCESS) {
7627057Smarks 				if (user)
7637057Smarks 					error = idmap_get_uidbysid(get_hdl,
7647369SJulian.Pullen@Sun.COM 					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
7657369SJulian.Pullen@Sun.COM 					    id, &status);
7667057Smarks 				else
7677057Smarks 					error = idmap_get_gidbysid(get_hdl,
7687369SJulian.Pullen@Sun.COM 					    sid, rid, IDMAP_REQ_FLG_USE_CACHE,
7697369SJulian.Pullen@Sun.COM 					    id, &status);
770*7680SMark.Shellenbaum@Sun.COM 				if (error == IDMAP_SUCCESS) {
771*7680SMark.Shellenbaum@Sun.COM 					error = idmap_get_mappings(get_hdl);
772*7680SMark.Shellenbaum@Sun.COM 					if (error == IDMAP_SUCCESS &&
773*7680SMark.Shellenbaum@Sun.COM 					    status != IDMAP_SUCCESS)
774*7680SMark.Shellenbaum@Sun.COM 						error = 1;
775*7680SMark.Shellenbaum@Sun.COM 					else
776*7680SMark.Shellenbaum@Sun.COM 						error = 0;
777*7680SMark.Shellenbaum@Sun.COM 				}
778*7680SMark.Shellenbaum@Sun.COM 			} else {
779*7680SMark.Shellenbaum@Sun.COM 				error = 1;
7807057Smarks 			}
781*7680SMark.Shellenbaum@Sun.COM 			if (get_hdl)
782*7680SMark.Shellenbaum@Sun.COM 				idmap_get_destroy(get_hdl);
783*7680SMark.Shellenbaum@Sun.COM 			if (idmap_hdl)
784*7680SMark.Shellenbaum@Sun.COM 				(void) idmap_fini(idmap_hdl);
7857057Smarks 		} else {
786*7680SMark.Shellenbaum@Sun.COM 			error = 1;
7877057Smarks 		}
7887057Smarks 		*rid_start = '-'; /* putback character removed earlier */
7897057Smarks 	} else {
7907057Smarks 		char *name = sid;
7917057Smarks 		*domain_start++ = '\0';
7927057Smarks 
7937057Smarks 		if (user)
7947369SJulian.Pullen@Sun.COM 			error = idmap_getuidbywinname(name, domain_start,
7957369SJulian.Pullen@Sun.COM 			    IDMAP_REQ_FLG_USE_CACHE, id);
7967057Smarks 		else
7977369SJulian.Pullen@Sun.COM 			error = idmap_getgidbywinname(name, domain_start,
7987369SJulian.Pullen@Sun.COM 			    IDMAP_REQ_FLG_USE_CACHE, id);
7997057Smarks 		*--domain_start = '@';
800*7680SMark.Shellenbaum@Sun.COM 		error = (error == IDMAP_SUCCESS) ? 0 : 1;
8017057Smarks 	}
8027057Smarks 
8037057Smarks 	return (error);
8047057Smarks }
805