xref: /illumos-gate/usr/src/uts/common/syscall/gid.c (revision bde3d612a7c090234c60e6e4578821237a5db135)
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 
43 int
44 setgid(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);
73 retry:
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 
131 int64_t
132 getgid(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 
143 int
144 setegid(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);
170 retry:
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  */
218 int
219 setregid(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