xref: /illumos-gate/usr/src/uts/common/syscall/uid.c (revision ddf7fe95)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * 	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/sysmacros.h>
35 #include <sys/systm.h>
36 #include <sys/tuneable.h>
37 #include <sys/cred_impl.h>
38 #include <sys/errno.h>
39 #include <sys/proc.h>
40 #include <sys/signal.h>
41 #include <sys/debug.h>
42 #include <sys/policy.h>
43 #include <sys/zone.h>
44 #include <sys/sid.h>
45 
46 int
47 setuid(uid_t uid)
48 {
49 	proc_t *p;
50 	int error;
51 	int do_nocd = 0;
52 	int uidchge = 0;
53 	cred_t	*cr, *newcr;
54 	uid_t oldruid = uid;
55 	zoneid_t zoneid = getzoneid();
56 	ksid_t ksid, *ksp;
57 	zone_t	*zone = crgetzone(CRED());
58 
59 	if (!VALID_UID(uid, zone))
60 		return (set_errno(EINVAL));
61 
62 	if (uid > MAXUID) {
63 		if (ksid_lookupbyuid(zone, uid, &ksid) != 0)
64 			return (set_errno(EINVAL));
65 		ksp = &ksid;
66 	} else {
67 		ksp = NULL;
68 	}
69 	/*
70 	 * Need to pre-allocate the new cred structure before grabbing
71 	 * the p_crlock mutex.  We can't hold on to the p_crlock for most
72 	 * if this though, now that we allow kernel upcalls from the
73 	 * policy routines.
74 	 */
75 	newcr = cralloc_ksid();
76 
77 	p = ttoproc(curthread);
78 
79 retry:
80 	mutex_enter(&p->p_crlock);
81 retry_locked:
82 	cr = p->p_cred;
83 	crhold(cr);
84 	mutex_exit(&p->p_crlock);
85 
86 	if ((uid == cr->cr_ruid || uid == cr->cr_suid) &&
87 	    secpolicy_allow_setid(cr, uid, B_TRUE) != 0) {
88 		mutex_enter(&p->p_crlock);
89 		crfree(cr);
90 		if (cr != p->p_cred)
91 			goto retry_locked;
92 		error = 0;
93 		crcopy_to(cr, newcr);
94 		p->p_cred = newcr;
95 		newcr->cr_uid = uid;
96 		crsetsid(newcr, ksp, KSID_USER);
97 		mutex_exit(&p->p_crlock);
98 	} else if ((error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) {
99 		mutex_enter(&p->p_crlock);
100 		crfree(cr);
101 		if (cr != p->p_cred)
102 			goto retry_locked;
103 		if (!uidchge && uid != cr->cr_ruid) {
104 			/*
105 			 * The ruid of the process is going to change. In order
106 			 * to avoid a race condition involving the
107 			 * process-count associated with the newly given ruid,
108 			 * we increment the count before assigning the
109 			 * credential to the process.
110 			 * To do that, we'll have to take pidlock, so we first
111 			 * release p_crlock.
112 			 */
113 			mutex_exit(&p->p_crlock);
114 			uidchge = 1;
115 			mutex_enter(&pidlock);
116 			upcount_inc(uid, zoneid);
117 			mutex_exit(&pidlock);
118 			/*
119 			 * As we released p_crlock we can't rely on the cr
120 			 * we read. So retry the whole thing.
121 			 */
122 			goto retry;
123 		}
124 		/*
125 		 * A privileged process that gives up its privilege
126 		 * must be marked to produce no core dump.
127 		 */
128 		if (cr->cr_uid != uid ||
129 		    cr->cr_ruid != uid ||
130 		    cr->cr_suid != uid)
131 			do_nocd = 1;
132 		oldruid = cr->cr_ruid;
133 		crcopy_to(cr, newcr);
134 		p->p_cred = newcr;
135 		newcr->cr_ruid = uid;
136 		newcr->cr_suid = uid;
137 		newcr->cr_uid = uid;
138 		crsetsid(newcr, ksp, KSID_USER);
139 		ASSERT(uid != oldruid ? uidchge : 1);
140 		mutex_exit(&p->p_crlock);
141 	} else {
142 		crfree(newcr);
143 		crfree(cr);
144 		if (ksp != NULL)
145 			ksid_rele(ksp);
146 	}
147 
148 	/*
149 	 * We decrement the number of processes associated with the oldruid
150 	 * to match the increment above, even if the ruid of the process
151 	 * did not change or an error occurred (oldruid == uid).
152 	 */
153 	if (uidchge) {
154 		mutex_enter(&pidlock);
155 		upcount_dec(oldruid, zoneid);
156 		mutex_exit(&pidlock);
157 	}
158 
159 	if (error == 0) {
160 		if (do_nocd) {
161 			mutex_enter(&p->p_lock);
162 			p->p_flag |= SNOCD;
163 			mutex_exit(&p->p_lock);
164 		}
165 		crset(p, newcr);	/* broadcast to process threads */
166 		return (0);
167 	}
168 	return (set_errno(error));
169 }
170 
171 int64_t
172 getuid(void)
173 {
174 	rval_t	r;
175 	cred_t *cr;
176 
177 	cr = curthread->t_cred;
178 	r.r_val1 = cr->cr_ruid;
179 	r.r_val2 = cr->cr_uid;
180 	return (r.r_vals);
181 }
182 
183 int
184 seteuid(uid_t uid)
185 {
186 	proc_t *p;
187 	int error = EPERM;
188 	int do_nocd = 0;
189 	cred_t	*cr, *newcr;
190 	ksid_t ksid, *ksp;
191 	zone_t	*zone = crgetzone(CRED());
192 
193 	if (!VALID_UID(uid, zone))
194 		return (set_errno(EINVAL));
195 
196 	if (uid > MAXUID) {
197 		if (ksid_lookupbyuid(zone, uid, &ksid) != 0)
198 			return (set_errno(EINVAL));
199 		ksp = &ksid;
200 	} else {
201 		ksp = NULL;
202 	}
203 
204 	/*
205 	 * Need to pre-allocate the new cred structure before grabbing
206 	 * the p_crlock mutex.
207 	 */
208 	newcr = cralloc_ksid();
209 	p = ttoproc(curthread);
210 	mutex_enter(&p->p_crlock);
211 retry:
212 	crhold(cr = p->p_cred);
213 	mutex_exit(&p->p_crlock);
214 
215 	if (uid == cr->cr_ruid || uid == cr->cr_uid || uid == cr->cr_suid ||
216 	    (error = secpolicy_allow_setid(cr, uid, B_FALSE)) == 0) {
217 		/*
218 		 * A privileged process that makes itself look like a
219 		 * set-uid process must be marked to produce no core dump,
220 		 * if the effective uid did changed.
221 		 */
222 		mutex_enter(&p->p_crlock);
223 		crfree(cr);
224 		if (cr != p->p_cred)
225 			goto retry;
226 		if (cr->cr_uid != uid && error == 0)
227 			do_nocd = 1;
228 		error = 0;
229 		crcopy_to(cr, newcr);
230 		p->p_cred = newcr;
231 		newcr->cr_uid = uid;
232 		crsetsid(newcr, ksp, KSID_USER);
233 		mutex_exit(&p->p_crlock);
234 		if (do_nocd) {
235 			mutex_enter(&p->p_lock);
236 			p->p_flag |= SNOCD;
237 			mutex_exit(&p->p_lock);
238 		}
239 		crset(p, newcr);	/* broadcast to process threads */
240 		return (0);
241 	}
242 
243 	crfree(newcr);
244 	crfree(cr);
245 	if (ksp != NULL)
246 		ksid_rele(ksp);
247 	return (set_errno(error));
248 }
249 
250 /*
251  * Buy-back from SunOS 4.x
252  *
253  * Like setuid() and seteuid() combined -except- that non-root users
254  * can change cr_ruid to cr_uid, and the semantics of cr_suid are
255  * subtly different.
256  */
257 int
258 setreuid(uid_t ruid, uid_t euid)
259 {
260 	proc_t *p;
261 	int error = 0;
262 	int do_nocd = 0;
263 	int uidchge = 0;
264 	uid_t oldruid = ruid;
265 	cred_t *cr, *newcr;
266 	zoneid_t zoneid = getzoneid();
267 	ksid_t ksid, *ksp;
268 	zone_t	*zone = crgetzone(CRED());
269 
270 	if ((ruid != -1 && !VALID_UID(ruid, zone)) ||
271 	    (euid != -1 && !VALID_UID(euid, zone)))
272 		return (set_errno(EINVAL));
273 
274 	if (euid != -1 && euid > MAXUID) {
275 		if (ksid_lookupbyuid(zone, euid, &ksid) != 0)
276 			return (set_errno(EINVAL));
277 		ksp = &ksid;
278 	} else {
279 		ksp = NULL;
280 	}
281 
282 	/*
283 	 * Need to pre-allocate the new cred structure before grabbing
284 	 * the p_crlock mutex.
285 	 */
286 	newcr = cralloc_ksid();
287 
288 	p = ttoproc(curthread);
289 
290 retry:
291 	mutex_enter(&p->p_crlock);
292 retry_locked:
293 	crhold(cr = p->p_cred);
294 	mutex_exit(&p->p_crlock);
295 
296 	if (ruid != -1 && ruid != cr->cr_ruid && ruid != cr->cr_uid &&
297 	    secpolicy_allow_setid(cr, ruid, B_FALSE) != 0) {
298 		mutex_enter(&p->p_crlock);
299 		crfree(cr);
300 		if (cr != p->p_cred)
301 			goto retry_locked;
302 		error = EPERM;
303 	} else if (euid != -1 &&
304 	    euid != cr->cr_ruid && euid != cr->cr_uid &&
305 	    euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) {
306 		mutex_enter(&p->p_crlock);
307 		crfree(cr);
308 		if (cr != p->p_cred)
309 			goto retry_locked;
310 		error = EPERM;
311 	} else {
312 		mutex_enter(&p->p_crlock);
313 		crfree(cr);
314 		if (cr != p->p_cred)
315 			goto retry_locked;
316 		if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) {
317 			/*
318 			 * The ruid of the process is going to change. In order
319 			 * to avoid a race condition involving the
320 			 * process-count associated with the newly given ruid,
321 			 * we increment the count before assigning the
322 			 * credential to the process.
323 			 * To do that, we'll have to take pidlock, so we first
324 			 * release p_crlock.
325 			 */
326 			mutex_exit(&p->p_crlock);
327 			uidchge = 1;
328 			mutex_enter(&pidlock);
329 			upcount_inc(ruid, zoneid);
330 			mutex_exit(&pidlock);
331 			/*
332 			 * As we released p_crlock we can't rely on the cr
333 			 * we read. So retry the whole thing.
334 			 */
335 			goto retry;
336 		}
337 		crhold(cr);
338 		crcopy_to(cr, newcr);
339 		p->p_cred = newcr;
340 
341 		if (euid != -1) {
342 			newcr->cr_uid = euid;
343 			crsetsid(newcr, ksp, KSID_USER);
344 		}
345 		if (ruid != -1) {
346 			oldruid = newcr->cr_ruid;
347 			newcr->cr_ruid = ruid;
348 			ASSERT(ruid != oldruid ? uidchge : 1);
349 		}
350 		/*
351 		 * "If the real uid is being changed, or the effective uid is
352 		 * being changed to a value not equal to the real uid, the
353 		 * saved uid is set to the new effective uid."
354 		 */
355 		if (ruid != -1 ||
356 		    (euid != -1 && newcr->cr_uid != newcr->cr_ruid))
357 			newcr->cr_suid = newcr->cr_uid;
358 		/*
359 		 * A process that gives up its privilege
360 		 * must be marked to produce no core dump.
361 		 */
362 		if ((cr->cr_uid != newcr->cr_uid ||
363 		    cr->cr_ruid != newcr->cr_ruid ||
364 		    cr->cr_suid != newcr->cr_suid))
365 			do_nocd = 1;
366 
367 		crfree(cr);
368 	}
369 	mutex_exit(&p->p_crlock);
370 
371 	/*
372 	 * We decrement the number of processes associated with the oldruid
373 	 * to match the increment above, even if the ruid of the process
374 	 * did not change or an error occurred (oldruid == uid).
375 	 */
376 	if (uidchge) {
377 		ASSERT(oldruid != -1 && ruid != -1);
378 		mutex_enter(&pidlock);
379 		upcount_dec(oldruid, zoneid);
380 		mutex_exit(&pidlock);
381 	}
382 
383 	if (error == 0) {
384 		if (do_nocd) {
385 			mutex_enter(&p->p_lock);
386 			p->p_flag |= SNOCD;
387 			mutex_exit(&p->p_lock);
388 		}
389 		crset(p, newcr);	/* broadcast to process threads */
390 		return (0);
391 	}
392 	crfree(newcr);
393 	if (ksp != NULL)
394 		ksid_rele(ksp);
395 	return (set_errno(error));
396 }
397