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