uid.c revision 9799:641e52717cb5
10Sstevel@tonic-gate/*
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
44int
45setuid(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
77retry:
78	mutex_enter(&p->p_crlock);
79retry_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
172int64_t
173getuid(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
184int
185seteuid(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);
212retry:
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 */
259int
260setreuid(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
292retry:
293	mutex_enter(&p->p_crlock);
294retry_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