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