sid.c revision 4520:7dbeadedd7fe
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
46static kmutex_t sid_lock;
47static avl_tree_t sid_tree;
48static boolean_t sid_inited = B_FALSE;
49
50static 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
69void
70ksid_hold(ksid_t *ks)
71{
72	if (ks->ks_domain != NULL)
73		ksiddomain_hold(ks->ks_domain);
74}
75
76void
77ksid_rele(ksid_t *ks)
78{
79	if (ks->ks_domain != NULL)
80		ksiddomain_rele(ks->ks_domain);
81}
82
83void
84ksiddomain_hold(ksiddomain_t *kd)
85{
86	atomic_add_32(&kd->kd_ref, 1);
87}
88
89void
90ksiddomain_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
108void
109ksidlist_hold(ksidlist_t *ksl)
110{
111	atomic_add_32(&ksl->ksl_ref, 1);
112}
113
114void
115ksidlist_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
127static int
128ksid_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 */
148ksiddomain_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
179const char *
180ksid_getdomain(ksid_t *ks)
181{
182	return (ks->ks_domain->kd_name);
183}
184
185uint_t
186ksid_getrid(ksid_t *ks)
187{
188	return (ks->ks_rid);
189}
190
191int
192ksid_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
206int
207ksid_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
221credsid_t *
222kcrsid_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 */
232static credsid_t *
233kcrsid_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
257void
258kcrsid_hold(credsid_t *kcr)
259{
260	atomic_add_32(&kcr->kr_ref, 1);
261}
262
263void
264kcrsid_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 */
282void
283kcrsidcopy_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
299static int
300kcrsid_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 */
320credsid_t *
321kcrsid_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 */
352credsid_t *
353kcrsid_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
378ksidlist_t *
379kcrsid_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