xref: /illumos-gate/usr/src/uts/common/os/sid.c (revision 15deec582ef80846f1c88f9f61d26d8dbb992894)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Sid manipulation (stubs).
31  */
32 
33 #include <sys/atomic.h>
34 #include <sys/avl.h>
35 #include <sys/cmn_err.h>
36 #include <sys/kmem.h>
37 #include <sys/mutex.h>
38 #include <sys/sid.h>
39 #include <sys/sysmacros.h>
40 #include <sys/systm.h>
41 #include <sys/kidmap.h>
42 #include <sys/idmap.h>
43 
44 #define	KSIDLIST_MEM(n)	(sizeof (ksidlist_t) + ((n) - 1) * sizeof (ksid_t))
45 
46 static kmutex_t sid_lock;
47 static avl_tree_t sid_tree;
48 static boolean_t sid_inited = B_FALSE;
49 
50 static ksiddomain_t
51 *ksid_enterdomain(const char *dom)
52 {
53 	size_t len = strlen(dom) + 1;
54 	ksiddomain_t *res;
55 
56 	ASSERT(MUTEX_HELD(&sid_lock));
57 	res = kmem_alloc(sizeof (ksiddomain_t), KM_SLEEP);
58 	res->kd_len = (uint_t)len;
59 	res->kd_name = kmem_alloc(len, KM_SLEEP);
60 	bcopy(dom, res->kd_name, len);
61 
62 	res->kd_ref = 1;
63 
64 	avl_add(&sid_tree, res);
65 
66 	return (res);
67 }
68 
69 void
70 ksid_hold(ksid_t *ks)
71 {
72 	if (ks->ks_domain != NULL)
73 		ksiddomain_hold(ks->ks_domain);
74 }
75 
76 void
77 ksid_rele(ksid_t *ks)
78 {
79 	if (ks->ks_domain != NULL)
80 		ksiddomain_rele(ks->ks_domain);
81 }
82 
83 void
84 ksiddomain_hold(ksiddomain_t *kd)
85 {
86 	atomic_add_32(&kd->kd_ref, 1);
87 }
88 
89 void
90 ksiddomain_rele(ksiddomain_t *kd)
91 {
92 	if (atomic_add_32_nv(&kd->kd_ref, -1) == 0) {
93 		/*
94 		 * The kd reference can only be incremented from 0 when
95 		 * the sid_lock is held; so we lock and then check need to
96 		 * check for 0 again.
97 		 */
98 		mutex_enter(&sid_lock);
99 		if (kd->kd_ref == 0) {
100 			avl_remove(&sid_tree, kd);
101 			kmem_free(kd->kd_name, kd->kd_len);
102 			kmem_free(kd, sizeof (*kd));
103 		}
104 		mutex_exit(&sid_lock);
105 	}
106 }
107 
108 void
109 ksidlist_hold(ksidlist_t *ksl)
110 {
111 	atomic_add_32(&ksl->ksl_ref, 1);
112 }
113 
114 void
115 ksidlist_rele(ksidlist_t *ksl)
116 {
117 	if (atomic_add_32_nv(&ksl->ksl_ref, -1) == 0) {
118 		int i;
119 
120 		for (i = 0; i < ksl->ksl_nsid; i++)
121 			ksid_rele(&ksl->ksl_sids[i]);
122 
123 		kmem_free(ksl, KSIDLIST_MEM(ksl->ksl_nsid));
124 	}
125 }
126 
127 static int
128 ksid_cmp(const void *a, const void *b)
129 {
130 	const ksiddomain_t *ap = a;
131 	const ksiddomain_t *bp = b;
132 	int res;
133 
134 	res = strcmp(ap->kd_name, bp->kd_name);
135 	if (res > 0)
136 		return (1);
137 	if (res != 0)
138 		return (-1);
139 	return (0);
140 }
141 
142 /*
143  * Lookup the named domain in the AVL tree.
144  * If no entry is found, add the domain to the AVL tree.
145  * The domain is returned held and needs to be released
146  * when done.
147  */
148 ksiddomain_t
149 *ksid_lookupdomain(const char *dom)
150 {
151 	ksiddomain_t *res;
152 	ksiddomain_t tmpl;
153 
154 	mutex_enter(&sid_lock);
155 
156 	if (!sid_inited) {
157 		avl_create(&sid_tree, ksid_cmp, sizeof (ksiddomain_t),
158 		    offsetof(ksiddomain_t, kd_link));
159 
160 		res = ksid_enterdomain(dom);
161 		sid_inited = B_TRUE;
162 		mutex_exit(&sid_lock);
163 		return (res);
164 	}
165 
166 	tmpl.kd_name = (char *)dom;
167 
168 	res = avl_find(&sid_tree, &tmpl, NULL);
169 	if (res == NULL) {
170 		res = ksid_enterdomain(dom);
171 	} else {
172 		ksiddomain_hold(res);
173 	}
174 
175 	mutex_exit(&sid_lock);
176 	return (res);
177 }
178 
179 const char *
180 ksid_getdomain(ksid_t *ks)
181 {
182 	return (ks->ks_domain->kd_name);
183 }
184 
185 uint_t
186 ksid_getrid(ksid_t *ks)
187 {
188 	return (ks->ks_rid);
189 }
190 
191 int
192 ksid_lookupbyuid(uid_t id, ksid_t *res)
193 {
194 	const char *sid_prefix;
195 
196 	if (kidmap_getsidbyuid(id, &sid_prefix, &res->ks_rid) != IDMAP_SUCCESS)
197 		return (-1);
198 
199 	res->ks_domain = ksid_lookupdomain(sid_prefix);
200 
201 	res->ks_id = id;
202 
203 	return (0);
204 }
205 
206 int
207 ksid_lookupbygid(gid_t id, ksid_t *res)
208 {
209 	const char *sid_prefix;
210 
211 	if (kidmap_getsidbygid(id, &sid_prefix, &res->ks_rid) != IDMAP_SUCCESS)
212 		return (-1);
213 
214 	res->ks_domain = ksid_lookupdomain(sid_prefix);
215 
216 	res->ks_id = id;
217 
218 	return (0);
219 }
220 
221 credsid_t *
222 kcrsid_alloc(void)
223 {
224 	credsid_t *kcr = kmem_zalloc(sizeof (*kcr), KM_SLEEP);
225 	kcr->kr_ref = 1;
226 	return (kcr);
227 }
228 
229 /*
230  * Returns a credsid_t with a refcount of 1.
231  */
232 static credsid_t *
233 kcrsid_dup(credsid_t *org)
234 {
235 	credsid_t *new;
236 	ksid_index_t ki;
237 
238 	if (org == NULL)
239 		return (kcrsid_alloc());
240 	if (org->kr_ref == 1)
241 		return (org);
242 	new = kcrsid_alloc();
243 
244 	/* Copy, then update reference counts */
245 	*new = *org;
246 	new->kr_ref = 1;
247 	for (ki = 0; ki < KSID_COUNT; ki++)
248 		ksid_hold(&new->kr_sidx[ki]);
249 
250 	if (new->kr_sidlist != NULL)
251 		ksidlist_hold(new->kr_sidlist);
252 
253 	kcrsid_rele(org);
254 	return (new);
255 }
256 
257 void
258 kcrsid_hold(credsid_t *kcr)
259 {
260 	atomic_add_32(&kcr->kr_ref, 1);
261 }
262 
263 void
264 kcrsid_rele(credsid_t *kcr)
265 {
266 	if (atomic_add_32_nv(&kcr->kr_ref, -1) == 0) {
267 		ksid_index_t i;
268 
269 		for (i = 0; i < KSID_COUNT; i++)
270 			ksid_rele(&kcr->kr_sidx[i]);
271 
272 		if (kcr->kr_sidlist != NULL)
273 			ksidlist_rele(kcr->kr_sidlist);
274 
275 		kmem_free(kcr, sizeof (*kcr));
276 	}
277 }
278 
279 /*
280  * Copy the SID credential into a previously allocated piece of memory.
281  */
282 void
283 kcrsidcopy_to(const credsid_t *okcr, credsid_t *nkcr)
284 {
285 	int i;
286 
287 	ASSERT(nkcr->kr_ref == 1);
288 
289 	if (okcr == NULL)
290 		return;
291 	*nkcr = *okcr;
292 	for (i = 0; i < KSID_COUNT; i++)
293 		ksid_hold(&nkcr->kr_sidx[i]);
294 	if (nkcr->kr_sidlist != NULL)
295 		ksidlist_hold(nkcr->kr_sidlist);
296 	nkcr->kr_ref = 1;
297 }
298 
299 static int
300 kcrsid_sidcount(const credsid_t *kcr)
301 {
302 	int cnt = 0;
303 	int i;
304 
305 	if (kcr == NULL)
306 		return (0);
307 
308 	for (i = 0; i < KSID_COUNT; i++)
309 		if (kcr->kr_sidx[i].ks_domain != NULL)
310 			cnt++;
311 
312 	if (kcr->kr_sidlist != NULL)
313 		cnt += kcr->kr_sidlist->ksl_nsid;
314 	return (cnt);
315 }
316 
317 /*
318  * Argument needs to be a ksid_t with a properly held ks_domain reference.
319  */
320 credsid_t *
321 kcrsid_setsid(credsid_t *okcr, ksid_t *ksp, ksid_index_t i)
322 {
323 	int ocnt = kcrsid_sidcount(okcr);
324 	credsid_t *nkcr;
325 
326 	/*
327 	 * Unset the particular ksid; if there are no other SIDs or if this
328 	 * is the last SID, remove the auxilary data structure.
329 	 */
330 	if (ksp == NULL) {
331 		if (ocnt == 0 ||
332 		    (ocnt == 1 && okcr->kr_sidx[i].ks_domain != NULL)) {
333 			if (okcr != NULL)
334 				kcrsid_rele(okcr);
335 			return (NULL);
336 		}
337 	}
338 	nkcr = kcrsid_dup(okcr);
339 	ksid_rele(&nkcr->kr_sidx[i]);
340 	if (ksp == NULL)
341 		bzero(&nkcr->kr_sidx[i], sizeof (ksid_t));
342 	else
343 		nkcr->kr_sidx[i] = *ksp;
344 
345 	return (nkcr);
346 }
347 
348 /*
349  * Argument needs to be a ksidlist_t with properly held ks_domain references
350  * and a reference count taking the new reference into account.
351  */
352 credsid_t *
353 kcrsid_setsidlist(credsid_t *okcr, ksidlist_t *ksl)
354 {
355 	int ocnt = kcrsid_sidcount(okcr);
356 	credsid_t *nkcr;
357 
358 	/*
359 	 * Unset the sidlist; if there are no further SIDs, remove the
360 	 * auxilary data structure.
361 	 */
362 	if (ksl == NULL) {
363 		if (ocnt == 0 || (okcr->kr_sidlist != NULL &&
364 		    ocnt == okcr->kr_sidlist->ksl_nsid)) {
365 			if (okcr != NULL)
366 				kcrsid_rele(okcr);
367 			return (NULL);
368 		}
369 	}
370 	nkcr = kcrsid_dup(okcr);
371 	if (nkcr->kr_sidlist != NULL)
372 		ksidlist_rele(nkcr->kr_sidlist);
373 
374 	nkcr->kr_sidlist = ksl;
375 	return (nkcr);
376 }
377 
378 ksidlist_t *
379 kcrsid_gidstosids(int ngrp, gid_t *grp)
380 {
381 	int i;
382 	ksidlist_t *list;
383 	int cnt;
384 
385 	if (ngrp == 0)
386 		return (NULL);
387 
388 	cnt = 0;
389 	list = kmem_zalloc(KSIDLIST_MEM(ngrp), KM_SLEEP);
390 
391 	list->ksl_nsid = ngrp;
392 	list->ksl_ref = 1;
393 
394 	for (i = 0; i < ngrp; i++) {
395 		if (grp[i] > MAXUID) {
396 			list->ksl_neid++;
397 			if (ksid_lookupbygid(grp[i], &list->ksl_sids[i]) != 0) {
398 				while (--i >= 0)
399 					ksid_rele(&list->ksl_sids[i]);
400 				cnt = 0;
401 				break;
402 			}
403 			cnt++;
404 		} else {
405 			list->ksl_sids[i].ks_id = grp[i];
406 		}
407 	}
408 	if (cnt == 0) {
409 		kmem_free(list, KSIDLIST_MEM(ngrp));
410 		return (NULL);
411 	}
412 	return (list);
413 }
414