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