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/cred_impl.h>
37#include <sys/errno.h>
38#include <sys/proc.h>
39#include <sys/debug.h>
40#include <sys/policy.h>
41
42
43int
44setgid(gid_t gid)
45{
46	proc_t *p;
47	int error;
48	int do_nocd = 0;
49	cred_t	*cr, *newcr;
50	ksid_t ksid, *ksp;
51	zone_t	*zone = crgetzone(CRED());
52
53
54	if (!VALID_GID(gid, zone))
55		return (set_errno(EINVAL));
56
57	if (gid > MAXUID) {
58		if (ksid_lookupbygid(zone, gid, &ksid) != 0)
59			return (set_errno(EINVAL));
60		ksp = &ksid;
61	} else {
62		ksp = NULL;
63	}
64
65	/*
66	 * Need to pre-allocate the new cred structure before grabbing
67	 * the p_crlock mutex.  We cannot hold the mutex across the
68	 * secpolicy functions.
69	 */
70	newcr = cralloc_ksid();
71	p = ttoproc(curthread);
72	mutex_enter(&p->p_crlock);
73retry:
74	cr = p->p_cred;
75	crhold(cr);
76	mutex_exit(&p->p_crlock);
77
78
79	if ((gid == cr->cr_rgid || gid == cr->cr_sgid) &&
80	    secpolicy_allow_setid(cr, -1, B_TRUE) != 0) {
81		mutex_enter(&p->p_crlock);
82		crfree(cr);
83		if (cr != p->p_cred)
84			goto retry;
85		error = 0;
86		crcopy_to(cr, newcr);
87		p->p_cred = newcr;
88		newcr->cr_gid = gid;
89		crsetsid(newcr, ksp, KSID_GROUP);
90		mutex_exit(&p->p_crlock);
91	} else if ((error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) {
92		mutex_enter(&p->p_crlock);
93		crfree(cr);
94		if (cr != p->p_cred)
95			goto retry;
96		/*
97		 * A privileged process that makes itself look like a
98		 * set-gid process must be marked to produce no core dump.
99		 */
100		if (cr->cr_gid != gid ||
101		    cr->cr_rgid != gid ||
102		    cr->cr_sgid != gid)
103			do_nocd = 1;
104		crcopy_to(cr, newcr);
105		p->p_cred = newcr;
106		newcr->cr_gid = gid;
107		newcr->cr_rgid = gid;
108		newcr->cr_sgid = gid;
109		crsetsid(newcr, ksp, KSID_GROUP);
110		mutex_exit(&p->p_crlock);
111	} else {
112		crfree(newcr);
113		crfree(cr);
114		if (ksp != NULL)
115			ksid_rele(ksp);
116
117	}
118
119	if (error == 0) {
120		if (do_nocd) {
121			mutex_enter(&p->p_lock);
122			p->p_flag |= SNOCD;
123			mutex_exit(&p->p_lock);
124		}
125		crset(p, newcr);	/* broadcast to process threads */
126		return (0);
127	}
128	return (set_errno(error));
129}
130
131int64_t
132getgid(void)
133{
134	rval_t	r;
135	cred_t	*cr;
136
137	cr = curthread->t_cred;
138	r.r_val1 = cr->cr_rgid;
139	r.r_val2 = cr->cr_gid;
140	return (r.r_vals);
141}
142
143int
144setegid(gid_t gid)
145{
146	proc_t *p;
147	cred_t	*cr, *newcr;
148	int error = EPERM;
149	int do_nocd = 0;
150	ksid_t ksid, *ksp;
151	zone_t	*zone = crgetzone(CRED());
152
153	if (!VALID_GID(gid, zone))
154		return (set_errno(EINVAL));
155
156	if (gid > MAXUID) {
157		if (ksid_lookupbygid(zone, gid, &ksid) != 0)
158			return (set_errno(EINVAL));
159		ksp = &ksid;
160	} else {
161		ksp = NULL;
162	}
163	/*
164	 * Need to pre-allocate the new cred structure before grabbing
165	 * the p_crlock mutex.
166	 */
167	newcr = cralloc_ksid();
168	p = ttoproc(curthread);
169	mutex_enter(&p->p_crlock);
170retry:
171	crhold(cr = p->p_cred);
172	mutex_exit(&p->p_crlock);
173
174	if (gid == cr->cr_rgid || gid == cr->cr_gid || gid == cr->cr_sgid ||
175	    (error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) {
176		mutex_enter(&p->p_crlock);
177		crfree(cr);
178		if (cr != p->p_cred)
179			goto retry;
180		/*
181		 * A privileged process that makes itself look like a
182		 * set-gid process must be marked to produce no core dump.
183		 */
184		if (cr->cr_gid != gid && error == 0)
185			do_nocd = 1;
186		error = 0;
187		crcopy_to(cr, newcr);
188		p->p_cred = newcr;
189		newcr->cr_gid = gid;
190		crsetsid(newcr, ksp, KSID_GROUP);
191		mutex_exit(&p->p_crlock);
192	} else {
193		crfree(newcr);
194		crfree(cr);
195		if (ksp != NULL)
196			ksid_rele(ksp);
197	}
198
199	if (error == 0) {
200		if (do_nocd) {
201			mutex_enter(&p->p_lock);
202			p->p_flag |= SNOCD;
203			mutex_exit(&p->p_lock);
204		}
205		crset(p, newcr);	/* broadcast to process threads */
206		return (0);
207	}
208	return (set_errno(error));
209}
210
211/*
212 * Buy-back from SunOS 4.x
213 *
214 * Like setgid() and setegid() combined -except- that non-root users
215 * can change cr_rgid to cr_gid, and the semantics of cr_sgid are
216 * subtly different.
217 */
218int
219setregid(gid_t rgid, gid_t egid)
220{
221	proc_t *p;
222	int error = EPERM;
223	int do_nocd = 0;
224	cred_t *cr, *newcr;
225	ksid_t ksid, *ksp;
226	zone_t	*zone = crgetzone(CRED());
227
228	if ((rgid != -1 && !VALID_GID(rgid, zone)) ||
229	    (egid != -1 && !VALID_GID(egid, zone)))
230		return (set_errno(EINVAL));
231
232	if (egid != -1 && egid > MAXUID) {
233		if (ksid_lookupbygid(zone, egid, &ksid) != 0)
234			return (set_errno(EINVAL));
235		ksp = &ksid;
236	} else {
237		ksp = NULL;
238	}
239	/*
240	 * Need to pre-allocate the new cred structure before grabbing
241	 * the p_crlock mutex.
242	 */
243	newcr = cralloc_ksid();
244
245	p = ttoproc(curthread);
246	mutex_enter(&p->p_crlock);
247	cr = p->p_cred;
248
249	if ((rgid == -1 ||
250	    rgid == cr->cr_rgid || rgid == cr->cr_gid || rgid == cr->cr_sgid) &&
251	    (egid == -1 || egid == cr->cr_rgid || egid == cr->cr_gid ||
252	    egid == cr->cr_sgid) ||
253	    (error = secpolicy_allow_setid(cr, -1, B_FALSE)) == 0) {
254		crhold(cr);
255		crcopy_to(cr, newcr);
256		p->p_cred = newcr;
257
258		if (egid != -1) {
259			newcr->cr_gid = egid;
260			crsetsid(newcr, ksp, KSID_GROUP);
261		}
262		if (rgid != -1)
263			newcr->cr_rgid = rgid;
264		/*
265		 * "If the real gid is being changed, or the effective gid is
266		 * being changed to a value not equal to the real gid, the
267		 * saved gid is set to the new effective gid."
268		 */
269		if (rgid != -1 ||
270		    (egid != -1 && newcr->cr_gid != newcr->cr_rgid))
271			newcr->cr_sgid = newcr->cr_gid;
272		/*
273		 * A privileged process that makes itself look like a
274		 * set-gid process must be marked to produce no core dump.
275		 */
276		if ((cr->cr_gid != newcr->cr_gid ||
277		    cr->cr_rgid != newcr->cr_rgid ||
278		    cr->cr_sgid != newcr->cr_sgid) && error == 0)
279			do_nocd = 1;
280		error = 0;
281		crfree(cr);
282	}
283	mutex_exit(&p->p_crlock);
284
285	if (error == 0) {
286		if (do_nocd) {
287			mutex_enter(&p->p_lock);
288			p->p_flag |= SNOCD;
289			mutex_exit(&p->p_lock);
290		}
291		crset(p, newcr);	/* broadcast to process threads */
292		return (0);
293	}
294	crfree(newcr);
295	if (ksp != NULL)
296		ksid_rele(ksp);
297	return (set_errno(error));
298}
299