sidsys.c revision 4321:a8930ec16e52
1131087Smarcel/*
2131087Smarcel * CDDL HEADER START
3131087Smarcel *
4131087Smarcel * The contents of this file are subject to the terms of the
5131087Smarcel * Common Development and Distribution License (the "License").
6131087Smarcel * You may not use this file except in compliance with the License.
7131087Smarcel *
8131087Smarcel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9131087Smarcel * or http://www.opensolaris.org/os/licensing.
10131087Smarcel * See the License for the specific language governing permissions
11131087Smarcel * and limitations under the License.
12131087Smarcel *
13131087Smarcel * When distributing Covered Code, include this CDDL HEADER in each
14131087Smarcel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15131087Smarcel * If applicable, add the following below this CDDL HEADER, with the
16131087Smarcel * fields enclosed by brackets "[]" replaced with your own identifying
17131087Smarcel * information: Portions Copyright [yyyy] [name of copyright owner]
18131087Smarcel *
19131087Smarcel * CDDL HEADER END
20131087Smarcel */
21131087Smarcel
22131087Smarcel/*
23131087Smarcel * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24131087Smarcel * Use is subject to license terms.
25131087Smarcel */
26131087Smarcel
27131087Smarcel#pragma ident	"%Z%%M%	%I%	%E% SMI"
28131087Smarcel
29131087Smarcel/*
30131087Smarcel * SID system call.
31131087Smarcel */
32131087Smarcel
33131087Smarcel#include <sys/sid.h>
34131087Smarcel#include <sys/cred.h>
35131087Smarcel#include <sys/errno.h>
36131087Smarcel#include <sys/systm.h>
37131087Smarcel#include <sys/policy.h>
38131087Smarcel#include <sys/door.h>
39131087Smarcel
40131087Smarcelstatic kmutex_t idmap_mutex;
41131087Smarcel
42131087Smarceltypedef struct idmap_reg {
43131087Smarcel	door_handle_t 	idmap_door;
44131087Smarcel	int		idmap_flags;
45131087Smarcel	int		idmap_ref;
46131087Smarcel} idmap_reg_t;
47131087Smarcel
48131087Smarcelstatic idmap_reg_t *idmap_ptr;
49131087Smarcel
50131087Smarcelstatic int idmap_unreg_dh(door_handle_t);
51131087Smarcel
52131087Smarcelstatic void
53131087Smarcelidmap_freeone(idmap_reg_t *p)
54131087Smarcel{
55131087Smarcel	ASSERT(p->idmap_ref == 0);
56131087Smarcel	ASSERT(MUTEX_HELD(&idmap_mutex));
57131087Smarcel
58131087Smarcel	door_ki_rele(p->idmap_door);
59131087Smarcel	if (idmap_ptr == p)
60131087Smarcel		idmap_ptr = NULL;
61131087Smarcel
62131087Smarcel	kmem_free(p, sizeof (*p));
63131087Smarcel}
64131087Smarcel
65131087Smarcelstatic int
66131087Smarcelidmap_do_call(sidmap_call_t *callp, size_t callsz, void **resp, size_t *respsz)
67131087Smarcel{
68131087Smarcel	door_arg_t da;
69131087Smarcel	idmap_reg_t *p;
70131087Smarcel	int ret;
71131087Smarcel	int dres;
72131087Smarcel
73131087Smarcel	mutex_enter(&idmap_mutex);
74131087Smarcel	p = idmap_ptr;
75131087Smarcel	if (p != NULL) {
76131087Smarcel		p->idmap_ref++;
77131087Smarcel	} else {
78131087Smarcel		mutex_exit(&idmap_mutex);
79131087Smarcel		return (-1);
80131087Smarcel	}
81131087Smarcel	mutex_exit(&idmap_mutex);
82131087Smarcel
83131087Smarcel	da.data_ptr = (char *)callp;
84131087Smarcel	da.data_size = callsz;
85131087Smarcel	da.desc_ptr = NULL;
86131087Smarcel	da.desc_num = 0;
87131087Smarcel	da.rbuf = *resp;
88131087Smarcel	da.rsize = *respsz;
89131087Smarcel
90131087Smarcel	while ((dres = door_ki_upcall(p->idmap_door, &da)) != 0) {
91131087Smarcel		switch (dres) {
92131087Smarcel		case EINTR:
93131087Smarcel		case EAGAIN:
94131087Smarcel			delay(1);
95131087Smarcel			continue;
96131087Smarcel		case EINVAL:
97131087Smarcel		case EBADF:
98131087Smarcel			(void) idmap_unreg_dh(p->idmap_door);
99131087Smarcel			/* FALLTHROUGH */
100131087Smarcel		default:
101131087Smarcel			ret = -1;
102131087Smarcel			goto out;
103131087Smarcel		}
104131087Smarcel	}
105131087Smarcel	*resp = da.rbuf;
106131087Smarcel	*respsz = da.rsize;
107131087Smarcel	ret = 0;
108131087Smarcelout:
109131087Smarcel	mutex_enter(&idmap_mutex);
110131087Smarcel	if (--p->idmap_ref == 0)
111131087Smarcel		idmap_freeone(p);
112131087Smarcel	mutex_exit(&idmap_mutex);
113131087Smarcel	return (ret);
114131087Smarcel}
115131087Smarcel
116131087Smarcel/*
117131087Smarcel * Current code only attempts to map ids to sids.
118131087Smarcel */
119131087Smarcelint
120131087Smarcelidmap_call_byid(uid_t id, ksid_t *ksid)
121131087Smarcel{
122131087Smarcel	sidmap_call_t call;
123131087Smarcel	domsid_t res, *resp = &res;
124131087Smarcel	size_t respsz = sizeof (res);
125131087Smarcel
126131087Smarcel	call.sc_type = SIDSYS_ID2SID;
127131087Smarcel	call.sc_val.sc_id = id;
128131087Smarcel
129131087Smarcel	if (idmap_do_call(&call, sizeof (call), (void **)&resp, &respsz) != 0)
130131087Smarcel		return (-1);
131131087Smarcel
132131087Smarcel	ksid->ks_domain = ksid_lookupdomain(resp->ds_dom);
133131087Smarcel	ksid->ks_rid = resp->ds_rid;
134131087Smarcel
135131087Smarcel	/* Larger SID return value; this usually happens */
136131087Smarcel	if (resp != &res)
137131087Smarcel		kmem_free(resp, respsz);
138131087Smarcel
139131087Smarcel	return (0);
140131087Smarcel}
141131087Smarcel
142131087Smarceluid_t
143131087Smarcelidmap_call_bysid(ksid_t *ksid)
144131087Smarcel{
145131087Smarcel	ksiddomain_t *domp = ksid->ks_domain;
146131087Smarcel	sidmap_call_t *callp;
147131087Smarcel	uid_t res = (uid_t)-1;
148131087Smarcel	uid_t *resp = &res;
149131087Smarcel	size_t callsz;
150131087Smarcel	size_t respsz = sizeof (res);
151131087Smarcel
152131087Smarcel	callsz = sizeof (sidmap_call_t) + domp->kd_len;
153131087Smarcel
154131087Smarcel	callp = kmem_alloc(callsz, KM_SLEEP);
155131087Smarcel	callp->sc_type = SIDSYS_SID2ID;
156131087Smarcel	bcopy(domp->kd_name, callp->sc_val.sc_sid.ds_dom, domp->kd_len);
157131087Smarcel	callp->sc_val.sc_sid.ds_rid = ksid->ks_rid;
158131087Smarcel
159131087Smarcel	if (idmap_do_call(callp, callsz, (void **)&resp, &respsz) != 0)
160131087Smarcel		goto out;
161131087Smarcel
162131087Smarcel	/* Should never happen; the original buffer should be large enough */
163131087Smarcel	if (resp != &res) {
164131087Smarcel		kmem_free(resp, respsz);
165146818Sdfr		goto out;
166131087Smarcel	}
167131087Smarcel
168131087Smarcel	if (respsz != sizeof (uid_t))
169131087Smarcel		res = (uid_t)-1;
170131087Smarcel
171131087Smarcelout:
172131087Smarcel	kmem_free(callp, callsz);
173131087Smarcel	return (res);
174131087Smarcel}
175131087Smarcel
176131087Smarcelstatic int
177138383Smarcelidmap_reg(int did)
178131087Smarcel{
179138383Smarcel	door_handle_t dh;
180131087Smarcel	idmap_reg_t *idmp;
181131087Smarcel	int err;
182131087Smarcel
183131087Smarcel	if ((err = secpolicy_idmap(CRED())) != 0)
184131087Smarcel		return (set_errno(err));
185131087Smarcel
186131087Smarcel	dh = door_ki_lookup(did);
187131087Smarcel
188131087Smarcel	if (dh == NULL)
189131087Smarcel		return (set_errno(EBADF));
190131087Smarcel
191131087Smarcel	idmp = kmem_alloc(sizeof (*idmp), KM_SLEEP);
192131087Smarcel
193131087Smarcel	idmp->idmap_door = dh;
194131087Smarcel	mutex_enter(&idmap_mutex);
195131087Smarcel	if (idmap_ptr != NULL) {
196131087Smarcel		if (--idmap_ptr->idmap_ref == 0)
197131087Smarcel			idmap_freeone(idmap_ptr);
198131087Smarcel	}
199131087Smarcel	idmp->idmap_flags = 0;
200131087Smarcel	idmp->idmap_ref = 1;
201131087Smarcel	idmap_ptr = idmp;
202131087Smarcel	mutex_exit(&idmap_mutex);
203131087Smarcel	return (0);
204131087Smarcel}
205131087Smarcel
206131087Smarcelstatic int
207131087Smarcelidmap_unreg_dh(door_handle_t dh)
208131087Smarcel{
209131087Smarcel	mutex_enter(&idmap_mutex);
210131087Smarcel	if (idmap_ptr == NULL || idmap_ptr->idmap_door != dh) {
211131087Smarcel		mutex_exit(&idmap_mutex);
212131087Smarcel		return (EINVAL);
213131087Smarcel	}
214131087Smarcel
215131087Smarcel	if (idmap_ptr->idmap_flags != 0) {
216131087Smarcel		mutex_exit(&idmap_mutex);
217131087Smarcel		return (EAGAIN);
218131087Smarcel	}
219131087Smarcel	idmap_ptr->idmap_flags = 1;
220131087Smarcel	if (--idmap_ptr->idmap_ref == 0)
221131087Smarcel		idmap_freeone(idmap_ptr);
222131087Smarcel	mutex_exit(&idmap_mutex);
223131087Smarcel	return (0);
224131087Smarcel}
225131087Smarcel
226131087Smarcelstatic int
227131087Smarcelidmap_unreg(int did)
228131087Smarcel{
229131087Smarcel	door_handle_t dh = door_ki_lookup(did);
230131087Smarcel	int res;
231131087Smarcel
232131087Smarcel	if (dh == NULL)
233131087Smarcel		return (set_errno(EINVAL));
234131087Smarcel
235131087Smarcel	res = idmap_unreg_dh(dh);
236131087Smarcel	door_ki_rele(dh);
237131087Smarcel
238131087Smarcel	if (res != 0)
239131087Smarcel		return (set_errno(res));
240131087Smarcel	return (0);
241131087Smarcel}
242131087Smarcel
243131087Smarcelstatic boolean_t
244131087Smarcelits_my_door(void)
245131087Smarcel{
246131087Smarcel	mutex_enter(&idmap_mutex);
247131087Smarcel	if (idmap_ptr != NULL) {
248131087Smarcel		struct door_info info;
249131087Smarcel		int err = door_ki_info(idmap_ptr->idmap_door, &info);
250131087Smarcel		if (err == 0 && info.di_target == curproc->p_pid) {
251131087Smarcel			mutex_exit(&idmap_mutex);
252131087Smarcel			return (B_TRUE);
253131087Smarcel		}
254131087Smarcel	}
255131087Smarcel	mutex_exit(&idmap_mutex);
256131087Smarcel	return (B_FALSE);
257131087Smarcel}
258131087Smarcel
259131087Smarcelstatic uint64_t
260131087Smarcelallocids(int flag, int nuids, int ngids)
261131087Smarcel{
262131087Smarcel	rval_t r;
263131087Smarcel	uid_t su = 0;
264131087Smarcel	gid_t sg = 0;
265131087Smarcel	int err;
266131087Smarcel
267131087Smarcel	if (!its_my_door())
268131087Smarcel		return (set_errno(EPERM));
269131087Smarcel
270131087Smarcel	if (nuids < 0 || ngids < 0)
271131087Smarcel		return (set_errno(EINVAL));
272131087Smarcel
273131087Smarcel	if (flag != 0 || nuids > 0)
274131087Smarcel		err = eph_uid_alloc(flag, &su, nuids);
275131087Smarcel	if (err == 0 && (flag != 0 || ngids > 0))
276131087Smarcel		err = eph_gid_alloc(flag, &sg, ngids);
277131087Smarcel
278131087Smarcel	if (err != 0)
279131087Smarcel		return (set_errno(EOVERFLOW));
280131087Smarcel
281131087Smarcel	r.r_val1 = su;
282131087Smarcel	r.r_val2 = sg;
283131087Smarcel	return (r.r_vals);
284131087Smarcel}
285131087Smarcel
286131087Smarceluint64_t
287131087Smarcelsidsys(int op, int flag, int nuids, int ngids)
288131087Smarcel{
289131087Smarcel	switch (op) {
290131087Smarcel	case SIDSYS_ALLOC_IDS:
291131087Smarcel		return (allocids(flag, nuids, ngids));
292131087Smarcel	case SIDSYS_IDMAP_REG:
293131087Smarcel		return (idmap_reg(flag));
294131087Smarcel	case SIDSYS_IDMAP_UNREG:
295131087Smarcel		return (idmap_unreg(flag));
296131087Smarcel	default:
297131087Smarcel		return (set_errno(EINVAL));
298131087Smarcel	}
299131087Smarcel}
300131087Smarcel