2657 lines · 62.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  linux/kernel/sys.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/export.h>#include <linux/mm.h>#include <linux/utsname.h>#include <linux/mman.h>#include <linux/reboot.h>#include <linux/prctl.h>#include <linux/highuid.h>#include <linux/fs.h>#include <linux/kmod.h>#include <linux/perf_event.h>#include <linux/resource.h>#include <linux/kernel.h>#include <linux/workqueue.h>#include <linux/capability.h>#include <linux/device.h>#include <linux/key.h>#include <linux/times.h>#include <linux/posix-timers.h>#include <linux/security.h>#include <linux/dcookies.h>#include <linux/suspend.h>#include <linux/tty.h>#include <linux/signal.h>#include <linux/cn_proc.h>#include <linux/getcpu.h>#include <linux/task_io_accounting_ops.h>#include <linux/seccomp.h>#include <linux/cpu.h>#include <linux/personality.h>#include <linux/ptrace.h>#include <linux/fs_struct.h>#include <linux/file.h>#include <linux/mount.h>#include <linux/gfp.h>#include <linux/syscore_ops.h>#include <linux/version.h>#include <linux/ctype.h> #include <linux/compat.h>#include <linux/syscalls.h>#include <linux/kprobes.h>#include <linux/user_namespace.h>#include <linux/binfmts.h> #include <linux/sched.h>#include <linux/sched/autogroup.h>#include <linux/sched/loadavg.h>#include <linux/sched/stat.h>#include <linux/sched/mm.h>#include <linux/sched/coredump.h>#include <linux/sched/task.h>#include <linux/sched/cputime.h>#include <linux/rcupdate.h>#include <linux/uidgid.h>#include <linux/cred.h> #include <linux/nospec.h> #include <linux/kmsg_dump.h>/* Move somewhere else to avoid recompiling? */#include <generated/utsrelease.h> #include <linux/uaccess.h>#include <asm/io.h>#include <asm/unistd.h> #include "uid16.h" #ifndef SET_UNALIGN_CTL# define SET_UNALIGN_CTL(a, b)	(-EINVAL)#endif#ifndef GET_UNALIGN_CTL# define GET_UNALIGN_CTL(a, b)	(-EINVAL)#endif#ifndef SET_FPEMU_CTL# define SET_FPEMU_CTL(a, b)	(-EINVAL)#endif#ifndef GET_FPEMU_CTL# define GET_FPEMU_CTL(a, b)	(-EINVAL)#endif#ifndef SET_FPEXC_CTL# define SET_FPEXC_CTL(a, b)	(-EINVAL)#endif#ifndef GET_FPEXC_CTL# define GET_FPEXC_CTL(a, b)	(-EINVAL)#endif#ifndef GET_ENDIAN# define GET_ENDIAN(a, b)	(-EINVAL)#endif#ifndef SET_ENDIAN# define SET_ENDIAN(a, b)	(-EINVAL)#endif#ifndef GET_TSC_CTL# define GET_TSC_CTL(a)		(-EINVAL)#endif#ifndef SET_TSC_CTL# define SET_TSC_CTL(a)		(-EINVAL)#endif#ifndef MPX_ENABLE_MANAGEMENT# define MPX_ENABLE_MANAGEMENT()	(-EINVAL)#endif#ifndef MPX_DISABLE_MANAGEMENT# define MPX_DISABLE_MANAGEMENT()	(-EINVAL)#endif#ifndef GET_FP_MODE# define GET_FP_MODE(a)		(-EINVAL)#endif#ifndef SET_FP_MODE# define SET_FP_MODE(a,b)	(-EINVAL)#endif#ifndef SVE_SET_VL# define SVE_SET_VL(a)		(-EINVAL)#endif#ifndef SVE_GET_VL# define SVE_GET_VL()		(-EINVAL)#endif#ifndef PAC_RESET_KEYS# define PAC_RESET_KEYS(a, b)	(-EINVAL)#endif /* * this is where the system-wide overflow UID and GID are defined, for * architectures that now have 32-bit UID/GID but didn't in the past */ int overflowuid = DEFAULT_OVERFLOWUID;int overflowgid = DEFAULT_OVERFLOWGID; EXPORT_SYMBOL(overflowuid);EXPORT_SYMBOL(overflowgid); /* * the same as above, but for filesystems which can only store a 16-bit * UID and GID. as such, this is needed on all architectures */ int fs_overflowuid = DEFAULT_FS_OVERFLOWUID;int fs_overflowgid = DEFAULT_FS_OVERFLOWGID; EXPORT_SYMBOL(fs_overflowuid);EXPORT_SYMBOL(fs_overflowgid); /* * Returns true if current's euid is same as p's uid or euid, * or has CAP_SYS_NICE to p's user_ns. * * Called with rcu_read_lock, creds are safe */static bool set_one_prio_perm(struct task_struct *p){	const struct cred *cred = current_cred(), *pcred = __task_cred(p); 	if (uid_eq(pcred->uid,  cred->euid) ||	    uid_eq(pcred->euid, cred->euid))		return true;	if (ns_capable(pcred->user_ns, CAP_SYS_NICE))		return true;	return false;} /* * set the priority of a task * - the caller must hold the RCU read lock */static int set_one_prio(struct task_struct *p, int niceval, int error){	int no_nice; 	if (!set_one_prio_perm(p)) {		error = -EPERM;		goto out;	}	if (niceval < task_nice(p) && !can_nice(p, niceval)) {		error = -EACCES;		goto out;	}	no_nice = security_task_setnice(p, niceval);	if (no_nice) {		error = no_nice;		goto out;	}	if (error == -ESRCH)		error = 0;	set_user_nice(p, niceval);out:	return error;} SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval){	struct task_struct *g, *p;	struct user_struct *user;	const struct cred *cred = current_cred();	int error = -EINVAL;	struct pid *pgrp;	kuid_t uid; 	if (which > PRIO_USER || which < PRIO_PROCESS)		goto out; 	/* normalize: avoid signed division (rounding problems) */	error = -ESRCH;	if (niceval < MIN_NICE)		niceval = MIN_NICE;	if (niceval > MAX_NICE)		niceval = MAX_NICE; 	rcu_read_lock();	read_lock(&tasklist_lock);	switch (which) {	case PRIO_PROCESS:		if (who)			p = find_task_by_vpid(who);		else			p = current;		if (p)			error = set_one_prio(p, niceval, error);		break;	case PRIO_PGRP:		if (who)			pgrp = find_vpid(who);		else			pgrp = task_pgrp(current);		do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {			error = set_one_prio(p, niceval, error);		} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);		break;	case PRIO_USER:		uid = make_kuid(cred->user_ns, who);		user = cred->user;		if (!who)			uid = cred->uid;		else if (!uid_eq(uid, cred->uid)) {			user = find_user(uid);			if (!user)				goto out_unlock;	/* No processes for this user */		}		do_each_thread(g, p) {			if (uid_eq(task_uid(p), uid) && task_pid_vnr(p))				error = set_one_prio(p, niceval, error);		} while_each_thread(g, p);		if (!uid_eq(uid, cred->uid))			free_uid(user);		/* For find_user() */		break;	}out_unlock:	read_unlock(&tasklist_lock);	rcu_read_unlock();out:	return error;} /* * Ugh. To avoid negative return values, "getpriority()" will * not return the normal nice-value, but a negated value that * has been offset by 20 (ie it returns 40..1 instead of -20..19) * to stay compatible. */SYSCALL_DEFINE2(getpriority, int, which, int, who){	struct task_struct *g, *p;	struct user_struct *user;	const struct cred *cred = current_cred();	long niceval, retval = -ESRCH;	struct pid *pgrp;	kuid_t uid; 	if (which > PRIO_USER || which < PRIO_PROCESS)		return -EINVAL; 	rcu_read_lock();	read_lock(&tasklist_lock);	switch (which) {	case PRIO_PROCESS:		if (who)			p = find_task_by_vpid(who);		else			p = current;		if (p) {			niceval = nice_to_rlimit(task_nice(p));			if (niceval > retval)				retval = niceval;		}		break;	case PRIO_PGRP:		if (who)			pgrp = find_vpid(who);		else			pgrp = task_pgrp(current);		do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {			niceval = nice_to_rlimit(task_nice(p));			if (niceval > retval)				retval = niceval;		} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);		break;	case PRIO_USER:		uid = make_kuid(cred->user_ns, who);		user = cred->user;		if (!who)			uid = cred->uid;		else if (!uid_eq(uid, cred->uid)) {			user = find_user(uid);			if (!user)				goto out_unlock;	/* No processes for this user */		}		do_each_thread(g, p) {			if (uid_eq(task_uid(p), uid) && task_pid_vnr(p)) {				niceval = nice_to_rlimit(task_nice(p));				if (niceval > retval)					retval = niceval;			}		} while_each_thread(g, p);		if (!uid_eq(uid, cred->uid))			free_uid(user);		/* for find_user() */		break;	}out_unlock:	read_unlock(&tasklist_lock);	rcu_read_unlock(); 	return retval;} /* * Unprivileged users may change the real gid to the effective gid * or vice versa.  (BSD-style) * * If you set the real gid at all, or set the effective gid to a value not * equal to the real gid, then the saved gid is set to the new effective gid. * * This makes it possible for a setgid program to completely drop its * privileges, which is often a useful assertion to make when you are doing * a security audit over a program. * * The general idea is that a program which uses just setregid() will be * 100% compatible with BSD.  A program which uses just setgid() will be * 100% compatible with POSIX with saved IDs. * * SMP: There are not races, the GIDs are checked only by filesystem *      operations (as far as semantic preservation is concerned). */#ifdef CONFIG_MULTIUSERlong __sys_setregid(gid_t rgid, gid_t egid){	struct user_namespace *ns = current_user_ns();	const struct cred *old;	struct cred *new;	int retval;	kgid_t krgid, kegid; 	krgid = make_kgid(ns, rgid);	kegid = make_kgid(ns, egid); 	if ((rgid != (gid_t) -1) && !gid_valid(krgid))		return -EINVAL;	if ((egid != (gid_t) -1) && !gid_valid(kegid))		return -EINVAL; 	new = prepare_creds();	if (!new)		return -ENOMEM;	old = current_cred(); 	retval = -EPERM;	if (rgid != (gid_t) -1) {		if (gid_eq(old->gid, krgid) ||		    gid_eq(old->egid, krgid) ||		    ns_capable(old->user_ns, CAP_SETGID))			new->gid = krgid;		else			goto error;	}	if (egid != (gid_t) -1) {		if (gid_eq(old->gid, kegid) ||		    gid_eq(old->egid, kegid) ||		    gid_eq(old->sgid, kegid) ||		    ns_capable(old->user_ns, CAP_SETGID))			new->egid = kegid;		else			goto error;	} 	if (rgid != (gid_t) -1 ||	    (egid != (gid_t) -1 && !gid_eq(kegid, old->gid)))		new->sgid = new->egid;	new->fsgid = new->egid; 	return commit_creds(new); error:	abort_creds(new);	return retval;} SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid){	return __sys_setregid(rgid, egid);} /* * setgid() is implemented like SysV w/ SAVED_IDS * * SMP: Same implicit races as above. */long __sys_setgid(gid_t gid){	struct user_namespace *ns = current_user_ns();	const struct cred *old;	struct cred *new;	int retval;	kgid_t kgid; 	kgid = make_kgid(ns, gid);	if (!gid_valid(kgid))		return -EINVAL; 	new = prepare_creds();	if (!new)		return -ENOMEM;	old = current_cred(); 	retval = -EPERM;	if (ns_capable(old->user_ns, CAP_SETGID))		new->gid = new->egid = new->sgid = new->fsgid = kgid;	else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid))		new->egid = new->fsgid = kgid;	else		goto error; 	return commit_creds(new); error:	abort_creds(new);	return retval;} SYSCALL_DEFINE1(setgid, gid_t, gid){	return __sys_setgid(gid);} /* * change the user struct in a credentials set to match the new UID */static int set_user(struct cred *new){	struct user_struct *new_user; 	new_user = alloc_uid(new->uid);	if (!new_user)		return -EAGAIN; 	/*	 * We don't fail in case of NPROC limit excess here because too many	 * poorly written programs don't check set*uid() return code, assuming	 * it never fails if called by root.  We may still enforce NPROC limit	 * for programs doing set*uid()+execve() by harmlessly deferring the	 * failure to the execve() stage.	 */	if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) &&			new_user != INIT_USER)		current->flags |= PF_NPROC_EXCEEDED;	else		current->flags &= ~PF_NPROC_EXCEEDED; 	free_uid(new->user);	new->user = new_user;	return 0;} /* * Unprivileged users may change the real uid to the effective uid * or vice versa.  (BSD-style) * * If you set the real uid at all, or set the effective uid to a value not * equal to the real uid, then the saved uid is set to the new effective uid. * * This makes it possible for a setuid program to completely drop its * privileges, which is often a useful assertion to make when you are doing * a security audit over a program. * * The general idea is that a program which uses just setreuid() will be * 100% compatible with BSD.  A program which uses just setuid() will be * 100% compatible with POSIX with saved IDs. */long __sys_setreuid(uid_t ruid, uid_t euid){	struct user_namespace *ns = current_user_ns();	const struct cred *old;	struct cred *new;	int retval;	kuid_t kruid, keuid; 

Lines 1–500 of 2657.