xref: /illumos-gate/usr/src/uts/common/syscall/sidsys.c (revision 34b3058f17535674a5b5c68e924617f6076dd640)
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 system call.
31  */
32 
33 #include <sys/sid.h>
34 #include <sys/cred.h>
35 #include <sys/errno.h>
36 #include <sys/systm.h>
37 #include <sys/policy.h>
38 #include <sys/door.h>
39 
40 static kmutex_t idmap_mutex;
41 
42 typedef struct idmap_reg {
43 	door_handle_t 	idmap_door;
44 	int		idmap_flags;
45 	int		idmap_ref;
46 } idmap_reg_t;
47 
48 static idmap_reg_t *idmap_ptr;
49 
50 static int idmap_unreg_dh(door_handle_t);
51 
52 static void
53 idmap_freeone(idmap_reg_t *p)
54 {
55 	ASSERT(p->idmap_ref == 0);
56 	ASSERT(MUTEX_HELD(&idmap_mutex));
57 
58 	door_ki_rele(p->idmap_door);
59 	if (idmap_ptr == p)
60 		idmap_ptr = NULL;
61 
62 	kmem_free(p, sizeof (*p));
63 }
64 
65 static int
66 idmap_do_call(sidmap_call_t *callp, size_t callsz, void **resp, size_t *respsz)
67 {
68 	door_arg_t da;
69 	idmap_reg_t *p;
70 	int ret;
71 	int dres;
72 
73 	mutex_enter(&idmap_mutex);
74 	p = idmap_ptr;
75 	if (p != NULL) {
76 		p->idmap_ref++;
77 	} else {
78 		mutex_exit(&idmap_mutex);
79 		return (-1);
80 	}
81 	mutex_exit(&idmap_mutex);
82 
83 	da.data_ptr = (char *)callp;
84 	da.data_size = callsz;
85 	da.desc_ptr = NULL;
86 	da.desc_num = 0;
87 	da.rbuf = *resp;
88 	da.rsize = *respsz;
89 
90 	while ((dres = door_ki_upcall(p->idmap_door, &da)) != 0) {
91 		switch (dres) {
92 		case EINTR:
93 		case EAGAIN:
94 			delay(1);
95 			continue;
96 		case EINVAL:
97 		case EBADF:
98 			(void) idmap_unreg_dh(p->idmap_door);
99 			/* FALLTHROUGH */
100 		default:
101 			ret = -1;
102 			goto out;
103 		}
104 	}
105 	*resp = da.rbuf;
106 	*respsz = da.rsize;
107 	ret = 0;
108 out:
109 	mutex_enter(&idmap_mutex);
110 	if (--p->idmap_ref == 0)
111 		idmap_freeone(p);
112 	mutex_exit(&idmap_mutex);
113 	return (ret);
114 }
115 
116 /*
117  * Current code only attempts to map ids to sids.
118  */
119 int
120 idmap_call_byid(uid_t id, ksid_t *ksid)
121 {
122 	sidmap_call_t call;
123 	domsid_t res, *resp = &res;
124 	size_t respsz = sizeof (res);
125 
126 	call.sc_type = SIDSYS_ID2SID;
127 	call.sc_val.sc_id = id;
128 
129 	if (idmap_do_call(&call, sizeof (call), (void **)&resp, &respsz) != 0)
130 		return (-1);
131 
132 	ksid->ks_domain = ksid_lookupdomain(resp->ds_dom);
133 	ksid->ks_rid = resp->ds_rid;
134 
135 	/* Larger SID return value; this usually happens */
136 	if (resp != &res)
137 		kmem_free(resp, respsz);
138 
139 	return (0);
140 }
141 
142 uid_t
143 idmap_call_bysid(ksid_t *ksid)
144 {
145 	ksiddomain_t *domp = ksid->ks_domain;
146 	sidmap_call_t *callp;
147 	uid_t res = (uid_t)-1;
148 	uid_t *resp = &res;
149 	size_t callsz;
150 	size_t respsz = sizeof (res);
151 
152 	callsz = sizeof (sidmap_call_t) + domp->kd_len;
153 
154 	callp = kmem_alloc(callsz, KM_SLEEP);
155 	callp->sc_type = SIDSYS_SID2ID;
156 	bcopy(domp->kd_name, callp->sc_val.sc_sid.ds_dom, domp->kd_len);
157 	callp->sc_val.sc_sid.ds_rid = ksid->ks_rid;
158 
159 	if (idmap_do_call(callp, callsz, (void **)&resp, &respsz) != 0)
160 		goto out;
161 
162 	/* Should never happen; the original buffer should be large enough */
163 	if (resp != &res) {
164 		kmem_free(resp, respsz);
165 		goto out;
166 	}
167 
168 	if (respsz != sizeof (uid_t))
169 		res = (uid_t)-1;
170 
171 out:
172 	kmem_free(callp, callsz);
173 	return (res);
174 }
175 
176 static int
177 idmap_reg(int did)
178 {
179 	door_handle_t dh;
180 	idmap_reg_t *idmp;
181 	int err;
182 
183 	if ((err = secpolicy_idmap(CRED())) != 0)
184 		return (set_errno(err));
185 
186 	dh = door_ki_lookup(did);
187 
188 	if (dh == NULL)
189 		return (set_errno(EBADF));
190 
191 	idmp = kmem_alloc(sizeof (*idmp), KM_SLEEP);
192 
193 	idmp->idmap_door = dh;
194 	mutex_enter(&idmap_mutex);
195 	if (idmap_ptr != NULL) {
196 		if (--idmap_ptr->idmap_ref == 0)
197 			idmap_freeone(idmap_ptr);
198 	}
199 	idmp->idmap_flags = 0;
200 	idmp->idmap_ref = 1;
201 	idmap_ptr = idmp;
202 	mutex_exit(&idmap_mutex);
203 	return (0);
204 }
205 
206 static int
207 idmap_unreg_dh(door_handle_t dh)
208 {
209 	mutex_enter(&idmap_mutex);
210 	if (idmap_ptr == NULL || idmap_ptr->idmap_door != dh) {
211 		mutex_exit(&idmap_mutex);
212 		return (EINVAL);
213 	}
214 
215 	if (idmap_ptr->idmap_flags != 0) {
216 		mutex_exit(&idmap_mutex);
217 		return (EAGAIN);
218 	}
219 	idmap_ptr->idmap_flags = 1;
220 	if (--idmap_ptr->idmap_ref == 0)
221 		idmap_freeone(idmap_ptr);
222 	mutex_exit(&idmap_mutex);
223 	return (0);
224 }
225 
226 static int
227 idmap_unreg(int did)
228 {
229 	door_handle_t dh = door_ki_lookup(did);
230 	int res;
231 
232 	if (dh == NULL)
233 		return (set_errno(EINVAL));
234 
235 	res = idmap_unreg_dh(dh);
236 	door_ki_rele(dh);
237 
238 	if (res != 0)
239 		return (set_errno(res));
240 	return (0);
241 }
242 
243 static boolean_t
244 its_my_door(void)
245 {
246 	mutex_enter(&idmap_mutex);
247 	if (idmap_ptr != NULL) {
248 		struct door_info info;
249 		int err = door_ki_info(idmap_ptr->idmap_door, &info);
250 		if (err == 0 && info.di_target == curproc->p_pid) {
251 			mutex_exit(&idmap_mutex);
252 			return (B_TRUE);
253 		}
254 	}
255 	mutex_exit(&idmap_mutex);
256 	return (B_FALSE);
257 }
258 
259 static uint64_t
260 allocids(int flag, int nuids, int ngids)
261 {
262 	rval_t r;
263 	uid_t su = 0;
264 	gid_t sg = 0;
265 	int err;
266 
267 	if (!its_my_door())
268 		return (set_errno(EPERM));
269 
270 	if (nuids < 0 || ngids < 0)
271 		return (set_errno(EINVAL));
272 
273 	if (flag != 0 || nuids > 0)
274 		err = eph_uid_alloc(flag, &su, nuids);
275 	if (err == 0 && (flag != 0 || ngids > 0))
276 		err = eph_gid_alloc(flag, &sg, ngids);
277 
278 	if (err != 0)
279 		return (set_errno(EOVERFLOW));
280 
281 	r.r_val1 = su;
282 	r.r_val2 = sg;
283 	return (r.r_vals);
284 }
285 
286 uint64_t
287 sidsys(int op, int flag, int nuids, int ngids)
288 {
289 	switch (op) {
290 	case SIDSYS_ALLOC_IDS:
291 		return (allocids(flag, nuids, ngids));
292 	case SIDSYS_IDMAP_REG:
293 		return (idmap_reg(flag));
294 	case SIDSYS_IDMAP_UNREG:
295 		return (idmap_unreg(flag));
296 	default:
297 		return (set_errno(EINVAL));
298 	}
299 }
300