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