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