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