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