xref: /illumos-gate/usr/src/lib/libsec/common/aclsort.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright (c) 1993-1997 by Sun Microsystems, Inc.
25  * All rights reserved
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 /*LINTLIBRARY*/
30 
31 /*
32  * aclsort():
33  *	Sort an ACL by entry type according to the following order:
34  *	USER_OBJ, USER, GROUP_OBJ, GROUP, CLASS_OBJ, OTHER_OBJ
35  *	DEF_USER_OBJ, DEF_USER, DEF_GROUP_OBJ, DEF_GROUP, DEF_CLASS_OBJ,
36  *	DEF_OTHER_OBJ.
37  *	For USER, GROUP, DEF_USER, and DEF_GROUP entries, the entries
38  *	are further sorted by ids.
39  */
40 
41 #include <stdlib.h>
42 #include <sys/acl.h>
43 
44 #define	TOTAL_ENTRY_TYPES	12
45 
46 /*
47  * This maps the entry defined value to a value for sorting.
48  * These values may not be the same. It is easier to add an
49  * entry type with this map.
50  *
51  * Because the defines and sorting order are not the same,
52  * the following map_to_sort table is needed.
53  */
54 struct map {
55 	int	sort_order;
56 	int	entry_type;
57 };
58 
59 static struct map map_to_sort[] = {
60 		{0, 0}, /* UNUSED */
61 		{1,	USER_OBJ},
62 		{2,	USER},
63 		{3, 	GROUP_OBJ},
64 		{4,	GROUP},
65 		{5,	CLASS_OBJ},
66 		{6, 	OTHER_OBJ},
67 		{7, 	DEF_USER_OBJ},
68 		{8, 	DEF_USER},
69 		{9, 	DEF_GROUP_OBJ},
70 		{10,	DEF_GROUP},
71 		{11,	DEF_CLASS_OBJ},
72 		{12,	DEF_OTHER_OBJ},
73 };
74 
75 static int entrycmp(const aclent_t *, const aclent_t *);
76 static int idcmp(const aclent_t *, const aclent_t *);
77 static void sortid(aclent_t *, int, int);
78 
79 int
80 aclsort(int nentries, int calcmask, aclent_t *aclbufp)
81 {
82 	aclent_t		*tp;
83 	unsigned int		newmask = 0;
84 	int			which;
85 	int			i;
86 	int			k;
87 
88 	/* check validity first before sorting */
89 	if (aclcheck(aclbufp, nentries, &which) != 0)
90 		return (-1);
91 
92 	/*
93 	 * Performance enhancement:
94 	 * We change entry type to sort order in the ACL, do the sorting.
95 	 * We then change sort order back to entry type.
96 	 * This makes entrycmp() very "light" and thus improves performance.
97 	 * Contrast to original implementation that had to find out
98 	 * the sorting order each time it is called.
99 	 */
100 	for (tp = aclbufp, i = 0; i < nentries; tp++, i++) {
101 		for (k = 1; k <= TOTAL_ENTRY_TYPES; k++) {
102 			if (tp->a_type == map_to_sort[k].entry_type) {
103 				tp->a_type = map_to_sort[k].sort_order;
104 				break;
105 			}
106 		}
107 	}
108 
109 	/* typecast to remove incompatible type warning */
110 	qsort(aclbufp, nentries, sizeof (aclent_t),
111 	    (int (*)(const void *, const void *))entrycmp);
112 
113 	for (tp = aclbufp, i = 0; i < nentries; tp++, i++) {
114 		for (k = 1; k <= TOTAL_ENTRY_TYPES; k++) {
115 			if (tp->a_type == map_to_sort[k].sort_order) {
116 				tp->a_type = map_to_sort[k].entry_type;
117 				break;
118 			}
119 		}
120 	}
121 
122 	/*
123 	 * Start sorting id within USER and GROUP
124 	 * sortid() could return a pointer and entries left
125 	 * so that we dont have to search from the beginning
126 	 *  every time it calls
127 	 */
128 	sortid(aclbufp, nentries, USER);
129 	sortid(aclbufp, nentries, GROUP);
130 	sortid(aclbufp, nentries, DEF_USER);
131 	sortid(aclbufp, nentries, DEF_GROUP);
132 
133 	/*
134 	 * Recalculate mask entry
135 	 */
136 	if (calcmask != 0) {
137 		/*
138 		 * At this point, ACL is valid and sorted. We may find a
139 		 * CLASS_OBJ entry and stop. Because of the case of minimum ACL,
140 		 * we still have to check till OTHER_OBJ entry is shown.
141 		 */
142 		for (tp = aclbufp; tp->a_type != OTHER_OBJ; tp++) {
143 			if (tp->a_type == USER || tp->a_type == GROUP ||
144 			    tp->a_type == GROUP_OBJ)
145 				newmask |= tp->a_perm;
146 			if (tp->a_type == CLASS_OBJ)
147 				break;
148 		}
149 		if (tp->a_type == CLASS_OBJ)
150 			tp->a_perm = (unsigned char)newmask;
151 	}
152 	return (0);
153 }
154 
155 /*
156  * sortid() sorts the ids with the same entry type in increasing order
157  */
158 static void
159 sortid(aclent_t *ap, int cnt, int type)
160 {
161 	aclent_t	*tp;
162 	aclent_t	*startp; /* start of the desired entry type */
163 	int		howmany;
164 
165 	for (tp = ap; cnt-- > 0; tp++) {
166 		if (tp->a_type != type)
167 			continue;
168 		startp = tp;
169 		howmany = 1;
170 		for (tp++, cnt--; cnt > 0 && tp->a_type == type; tp++, cnt--)
171 			howmany++;
172 		/* typecast to remove incompatible type warning */
173 		qsort(startp, howmany, sizeof (aclent_t),
174 		    (int (*)(const void*, const void*))idcmp);
175 	}
176 }
177 
178 /*
179  * compare the field a_type
180  */
181 static int
182 entrycmp(const aclent_t *i, const aclent_t *j)
183 {
184 	return ((int)(i->a_type) - (int)(j->a_type));
185 }
186 
187 /*
188  * compare the field a_id
189  */
190 static int
191 idcmp(const aclent_t *i, const aclent_t *j)
192 {
193 	return ((int)(i->a_id) - (int)(j->a_id));
194 }
195