uid.c revision 4321:a8930ec16e52
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
46int
47setuid(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_lookup(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
76retry:
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
156int64_t
157getuid(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
168int
169seteuid(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_lookup(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 */
238int
239setreuid(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_lookup(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
270retry:
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