xref: /freebsd/sys/compat/linux/linux_uid16.c (revision 9768746b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2001  The FreeBSD Project
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/fcntl.h>
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/malloc.h>
37 #include <sys/mutex.h>
38 #include <sys/priv.h>
39 #include <sys/proc.h>
40 #include <sys/sdt.h>
41 #include <sys/syscallsubr.h>
42 #include <sys/sysproto.h>
43 #include <sys/systm.h>
44 
45 #ifdef COMPAT_LINUX32
46 #include <machine/../linux32/linux.h>
47 #include <machine/../linux32/linux32_proto.h>
48 #else
49 #include <machine/../linux/linux.h>
50 #include <machine/../linux/linux_proto.h>
51 #endif
52 
53 #include <compat/linux/linux_dtrace.h>
54 #include <compat/linux/linux_util.h>
55 
56 /* DTrace init */
57 LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE);
58 
59 /**
60  * DTrace probes in this module.
61  */
62 LIN_SDT_PROBE_DEFINE1(uid16, linux_chown16, conv_path, "char *");
63 LIN_SDT_PROBE_DEFINE1(uid16, linux_lchown16, conv_path, "char *");
64 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgroups16, copyin_error, "int");
65 LIN_SDT_PROBE_DEFINE1(uid16, linux_setgroups16, priv_check_cred_error, "int");
66 LIN_SDT_PROBE_DEFINE1(uid16, linux_getgroups16, copyout_error, "int");
67 
68 DUMMY(setfsuid16);
69 DUMMY(setfsgid16);
70 DUMMY(getresuid16);
71 DUMMY(getresgid16);
72 
73 #define	CAST_NOCHG(x)	((x == 0xFFFF) ? -1 : x)
74 
75 int
76 linux_chown16(struct thread *td, struct linux_chown16_args *args)
77 {
78 	char *path;
79 	int error;
80 
81 	if (!LUSECONVPATH(td) && !SDT_PROBES_ENABLED()) {
82 		error = kern_fchownat(td, AT_FDCWD, args->path, UIO_USERSPACE,
83 		    CAST_NOCHG(args->uid), CAST_NOCHG(args->gid), 0);
84 	} else {
85 		LCONVPATHEXIST(args->path, &path);
86 		/*
87 		 * The DTrace probes have to be after the LCONVPATHEXIST, as
88 		 * LCONVPATHEXIST may return on its own and we do not want to
89 		 * have a stray entry without the corresponding return.
90 		 */
91 		LIN_SDT_PROBE1(uid16, linux_chown16, conv_path, path);
92 
93 		error = kern_fchownat(td, AT_FDCWD, path, UIO_SYSSPACE,
94 		    CAST_NOCHG(args->uid), CAST_NOCHG(args->gid), 0);
95 		LFREEPATH(path);
96 	}
97 	return (error);
98 }
99 
100 int
101 linux_lchown16(struct thread *td, struct linux_lchown16_args *args)
102 {
103 	char *path;
104 	int error;
105 
106 	if (!LUSECONVPATH(td) && !SDT_PROBES_ENABLED()) {
107 		error = kern_fchownat(td, AT_FDCWD, args->path, UIO_USERSPACE,
108 		    CAST_NOCHG(args->uid), CAST_NOCHG(args->gid), AT_SYMLINK_NOFOLLOW);
109 	} else {
110 		LCONVPATHEXIST(args->path, &path);
111 
112 		/*
113 		 * The DTrace probes have to be after the LCONVPATHEXIST, as
114 		 * LCONVPATHEXIST may return on its own and we do not want to
115 		 * have a stray entry without the corresponding return.
116 		 */
117 		LIN_SDT_PROBE1(uid16, linux_lchown16, conv_path, path);
118 
119 		error = kern_fchownat(td, AT_FDCWD, path, UIO_SYSSPACE,
120 		    CAST_NOCHG(args->uid), CAST_NOCHG(args->gid), AT_SYMLINK_NOFOLLOW);
121 		LFREEPATH(path);
122 	}
123 	return (error);
124 }
125 
126 int
127 linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
128 {
129 	struct ucred *newcred, *oldcred;
130 	l_gid16_t *linux_gidset;
131 	gid_t *bsd_gidset;
132 	int ngrp, error;
133 	struct proc *p;
134 
135 	ngrp = args->gidsetsize;
136 	if (ngrp < 0 || ngrp >= ngroups_max + 1)
137 		return (EINVAL);
138 	linux_gidset = malloc(ngrp * sizeof(*linux_gidset), M_LINUX, M_WAITOK);
139 	error = copyin(args->gidset, linux_gidset, ngrp * sizeof(l_gid16_t));
140 	if (error) {
141 		LIN_SDT_PROBE1(uid16, linux_setgroups16, copyin_error, error);
142 		free(linux_gidset, M_LINUX);
143 		return (error);
144 	}
145 	newcred = crget();
146 	p = td->td_proc;
147 	PROC_LOCK(p);
148 	oldcred = crcopysafe(p, newcred);
149 
150 	/*
151 	 * cr_groups[0] holds egid. Setting the whole set from
152 	 * the supplied set will cause egid to be changed too.
153 	 * Keep cr_groups[0] unchanged to prevent that.
154 	 */
155 
156 	if ((error = priv_check_cred(oldcred, PRIV_CRED_SETGROUPS)) != 0) {
157 		PROC_UNLOCK(p);
158 		crfree(newcred);
159 
160 		LIN_SDT_PROBE1(uid16, linux_setgroups16, priv_check_cred_error,
161 		    error);
162 		goto out;
163 	}
164 
165 	if (ngrp > 0) {
166 		newcred->cr_ngroups = ngrp + 1;
167 
168 		bsd_gidset = newcred->cr_groups;
169 		ngrp--;
170 		while (ngrp >= 0) {
171 			bsd_gidset[ngrp + 1] = linux_gidset[ngrp];
172 			ngrp--;
173 		}
174 	}
175 	else
176 		newcred->cr_ngroups = 1;
177 
178 	setsugid(td->td_proc);
179 	proc_set_cred(p, newcred);
180 	PROC_UNLOCK(p);
181 	crfree(oldcred);
182 	error = 0;
183 out:
184 	free(linux_gidset, M_LINUX);
185 
186 	return (error);
187 }
188 
189 int
190 linux_getgroups16(struct thread *td, struct linux_getgroups16_args *args)
191 {
192 	struct ucred *cred;
193 	l_gid16_t *linux_gidset;
194 	gid_t *bsd_gidset;
195 	int bsd_gidsetsz, ngrp, error;
196 
197 	cred = td->td_ucred;
198 	bsd_gidset = cred->cr_groups;
199 	bsd_gidsetsz = cred->cr_ngroups - 1;
200 
201 	/*
202 	 * cr_groups[0] holds egid. Returning the whole set
203 	 * here will cause a duplicate. Exclude cr_groups[0]
204 	 * to prevent that.
205 	 */
206 
207 	if ((ngrp = args->gidsetsize) == 0) {
208 		td->td_retval[0] = bsd_gidsetsz;
209 		return (0);
210 	}
211 
212 	if (ngrp < bsd_gidsetsz)
213 		return (EINVAL);
214 
215 	ngrp = 0;
216 	linux_gidset = malloc(bsd_gidsetsz * sizeof(*linux_gidset),
217 	    M_LINUX, M_WAITOK);
218 	while (ngrp < bsd_gidsetsz) {
219 		linux_gidset[ngrp] = bsd_gidset[ngrp + 1];
220 		ngrp++;
221 	}
222 
223 	error = copyout(linux_gidset, args->gidset, ngrp * sizeof(l_gid16_t));
224 	free(linux_gidset, M_LINUX);
225 	if (error) {
226 		LIN_SDT_PROBE1(uid16, linux_getgroups16, copyout_error, error);
227 		return (error);
228 	}
229 
230 	td->td_retval[0] = ngrp;
231 
232 	return (0);
233 }
234 
235 int
236 linux_getgid16(struct thread *td, struct linux_getgid16_args *args)
237 {
238 
239 	td->td_retval[0] = td->td_ucred->cr_rgid;
240 
241 	return (0);
242 }
243 
244 int
245 linux_getuid16(struct thread *td, struct linux_getuid16_args *args)
246 {
247 
248 	td->td_retval[0] = td->td_ucred->cr_ruid;
249 
250 	return (0);
251 }
252 
253 int
254 linux_getegid16(struct thread *td, struct linux_getegid16_args *args)
255 {
256 	struct getegid_args bsd;
257 	int error;
258 
259 	error = sys_getegid(td, &bsd);
260 
261 	return (error);
262 }
263 
264 int
265 linux_geteuid16(struct thread *td, struct linux_geteuid16_args *args)
266 {
267 	struct geteuid_args bsd;
268 	int error;
269 
270 	error = sys_geteuid(td, &bsd);
271 
272 	return (error);
273 }
274 
275 int
276 linux_setgid16(struct thread *td, struct linux_setgid16_args *args)
277 {
278 	struct setgid_args bsd;
279 	int error;
280 
281 	bsd.gid = args->gid;
282 	error = sys_setgid(td, &bsd);
283 
284 	return (error);
285 }
286 
287 int
288 linux_setuid16(struct thread *td, struct linux_setuid16_args *args)
289 {
290 	struct setuid_args bsd;
291 	int error;
292 
293 	bsd.uid = args->uid;
294 	error = sys_setuid(td, &bsd);
295 
296 	return (error);
297 }
298 
299 int
300 linux_setregid16(struct thread *td, struct linux_setregid16_args *args)
301 {
302 	struct setregid_args bsd;
303 	int error;
304 
305 	bsd.rgid = CAST_NOCHG(args->rgid);
306 	bsd.egid = CAST_NOCHG(args->egid);
307 	error = sys_setregid(td, &bsd);
308 
309 	return (error);
310 }
311 
312 int
313 linux_setreuid16(struct thread *td, struct linux_setreuid16_args *args)
314 {
315 	struct setreuid_args bsd;
316 	int error;
317 
318 	bsd.ruid = CAST_NOCHG(args->ruid);
319 	bsd.euid = CAST_NOCHG(args->euid);
320 	error = sys_setreuid(td, &bsd);
321 
322 	return (error);
323 }
324 
325 int
326 linux_setresgid16(struct thread *td, struct linux_setresgid16_args *args)
327 {
328 	struct setresgid_args bsd;
329 	int error;
330 
331 	bsd.rgid = CAST_NOCHG(args->rgid);
332 	bsd.egid = CAST_NOCHG(args->egid);
333 	bsd.sgid = CAST_NOCHG(args->sgid);
334 	error = sys_setresgid(td, &bsd);
335 
336 	return (error);
337 }
338 
339 int
340 linux_setresuid16(struct thread *td, struct linux_setresuid16_args *args)
341 {
342 	struct setresuid_args bsd;
343 	int error;
344 
345 	bsd.ruid = CAST_NOCHG(args->ruid);
346 	bsd.euid = CAST_NOCHG(args->euid);
347 	bsd.suid = CAST_NOCHG(args->suid);
348 	error = sys_setresuid(td, &bsd);
349 
350 	return (error);
351 }
352