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