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