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