162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Common capabilities, needed by capability.o.
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/capability.h>
662306a36Sopenharmony_ci#include <linux/audit.h>
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/lsm_hooks.h>
1062306a36Sopenharmony_ci#include <linux/file.h>
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/mman.h>
1362306a36Sopenharmony_ci#include <linux/pagemap.h>
1462306a36Sopenharmony_ci#include <linux/swap.h>
1562306a36Sopenharmony_ci#include <linux/skbuff.h>
1662306a36Sopenharmony_ci#include <linux/netlink.h>
1762306a36Sopenharmony_ci#include <linux/ptrace.h>
1862306a36Sopenharmony_ci#include <linux/xattr.h>
1962306a36Sopenharmony_ci#include <linux/hugetlb.h>
2062306a36Sopenharmony_ci#include <linux/mount.h>
2162306a36Sopenharmony_ci#include <linux/sched.h>
2262306a36Sopenharmony_ci#include <linux/prctl.h>
2362306a36Sopenharmony_ci#include <linux/securebits.h>
2462306a36Sopenharmony_ci#include <linux/user_namespace.h>
2562306a36Sopenharmony_ci#include <linux/binfmts.h>
2662306a36Sopenharmony_ci#include <linux/personality.h>
2762306a36Sopenharmony_ci#include <linux/mnt_idmapping.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * If a non-root user executes a setuid-root binary in
3162306a36Sopenharmony_ci * !secure(SECURE_NOROOT) mode, then we raise capabilities.
3262306a36Sopenharmony_ci * However if fE is also set, then the intent is for only
3362306a36Sopenharmony_ci * the file capabilities to be applied, and the setuid-root
3462306a36Sopenharmony_ci * bit is left on either to change the uid (plausible) or
3562306a36Sopenharmony_ci * to get full privilege on a kernel without file capabilities
3662306a36Sopenharmony_ci * support.  So in that case we do not raise capabilities.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * Warn if that happens, once per boot.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_cistatic void warn_setuid_and_fcaps_mixed(const char *fname)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	static int warned;
4362306a36Sopenharmony_ci	if (!warned) {
4462306a36Sopenharmony_ci		printk(KERN_INFO "warning: `%s' has both setuid-root and"
4562306a36Sopenharmony_ci			" effective capabilities. Therefore not raising all"
4662306a36Sopenharmony_ci			" capabilities.\n", fname);
4762306a36Sopenharmony_ci		warned = 1;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/**
5262306a36Sopenharmony_ci * cap_capable - Determine whether a task has a particular effective capability
5362306a36Sopenharmony_ci * @cred: The credentials to use
5462306a36Sopenharmony_ci * @targ_ns:  The user namespace in which we need the capability
5562306a36Sopenharmony_ci * @cap: The capability to check for
5662306a36Sopenharmony_ci * @opts: Bitmask of options defined in include/linux/security.h
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Determine whether the nominated task has the specified capability amongst
5962306a36Sopenharmony_ci * its effective set, returning 0 if it does, -ve if it does not.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable()
6262306a36Sopenharmony_ci * and has_capability() functions.  That is, it has the reverse semantics:
6362306a36Sopenharmony_ci * cap_has_capability() returns 0 when a task has a capability, but the
6462306a36Sopenharmony_ci * kernel's capable() and has_capability() returns 1 for this case.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_ciint cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
6762306a36Sopenharmony_ci		int cap, unsigned int opts)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct user_namespace *ns = targ_ns;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* See if cred has the capability in the target user namespace
7262306a36Sopenharmony_ci	 * by examining the target user namespace and all of the target
7362306a36Sopenharmony_ci	 * user namespace's parents.
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	for (;;) {
7662306a36Sopenharmony_ci		/* Do we have the necessary capabilities? */
7762306a36Sopenharmony_ci		if (ns == cred->user_ns)
7862306a36Sopenharmony_ci			return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		/*
8162306a36Sopenharmony_ci		 * If we're already at a lower level than we're looking for,
8262306a36Sopenharmony_ci		 * we're done searching.
8362306a36Sopenharmony_ci		 */
8462306a36Sopenharmony_ci		if (ns->level <= cred->user_ns->level)
8562306a36Sopenharmony_ci			return -EPERM;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		/*
8862306a36Sopenharmony_ci		 * The owner of the user namespace in the parent of the
8962306a36Sopenharmony_ci		 * user namespace has all caps.
9062306a36Sopenharmony_ci		 */
9162306a36Sopenharmony_ci		if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
9262306a36Sopenharmony_ci			return 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		/*
9562306a36Sopenharmony_ci		 * If you have a capability in a parent user ns, then you have
9662306a36Sopenharmony_ci		 * it over all children user namespaces as well.
9762306a36Sopenharmony_ci		 */
9862306a36Sopenharmony_ci		ns = ns->parent;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* We never get here */
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/**
10562306a36Sopenharmony_ci * cap_settime - Determine whether the current process may set the system clock
10662306a36Sopenharmony_ci * @ts: The time to set
10762306a36Sopenharmony_ci * @tz: The timezone to set
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Determine whether the current process may set the system clock and timezone
11062306a36Sopenharmony_ci * information, returning 0 if permission granted, -ve if denied.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ciint cap_settime(const struct timespec64 *ts, const struct timezone *tz)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (!capable(CAP_SYS_TIME))
11562306a36Sopenharmony_ci		return -EPERM;
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * cap_ptrace_access_check - Determine whether the current process may access
12162306a36Sopenharmony_ci *			   another
12262306a36Sopenharmony_ci * @child: The process to be accessed
12362306a36Sopenharmony_ci * @mode: The mode of attachment.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * If we are in the same or an ancestor user_ns and have all the target
12662306a36Sopenharmony_ci * task's capabilities, then ptrace access is allowed.
12762306a36Sopenharmony_ci * If we have the ptrace capability to the target user_ns, then ptrace
12862306a36Sopenharmony_ci * access is allowed.
12962306a36Sopenharmony_ci * Else denied.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * Determine whether a process may access another, returning 0 if permission
13262306a36Sopenharmony_ci * granted, -ve if denied.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_ciint cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	int ret = 0;
13762306a36Sopenharmony_ci	const struct cred *cred, *child_cred;
13862306a36Sopenharmony_ci	const kernel_cap_t *caller_caps;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	rcu_read_lock();
14162306a36Sopenharmony_ci	cred = current_cred();
14262306a36Sopenharmony_ci	child_cred = __task_cred(child);
14362306a36Sopenharmony_ci	if (mode & PTRACE_MODE_FSCREDS)
14462306a36Sopenharmony_ci		caller_caps = &cred->cap_effective;
14562306a36Sopenharmony_ci	else
14662306a36Sopenharmony_ci		caller_caps = &cred->cap_permitted;
14762306a36Sopenharmony_ci	if (cred->user_ns == child_cred->user_ns &&
14862306a36Sopenharmony_ci	    cap_issubset(child_cred->cap_permitted, *caller_caps))
14962306a36Sopenharmony_ci		goto out;
15062306a36Sopenharmony_ci	if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE))
15162306a36Sopenharmony_ci		goto out;
15262306a36Sopenharmony_ci	ret = -EPERM;
15362306a36Sopenharmony_ciout:
15462306a36Sopenharmony_ci	rcu_read_unlock();
15562306a36Sopenharmony_ci	return ret;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/**
15962306a36Sopenharmony_ci * cap_ptrace_traceme - Determine whether another process may trace the current
16062306a36Sopenharmony_ci * @parent: The task proposed to be the tracer
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * If parent is in the same or an ancestor user_ns and has all current's
16362306a36Sopenharmony_ci * capabilities, then ptrace access is allowed.
16462306a36Sopenharmony_ci * If parent has the ptrace capability to current's user_ns, then ptrace
16562306a36Sopenharmony_ci * access is allowed.
16662306a36Sopenharmony_ci * Else denied.
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci * Determine whether the nominated task is permitted to trace the current
16962306a36Sopenharmony_ci * process, returning 0 if permission is granted, -ve if denied.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_ciint cap_ptrace_traceme(struct task_struct *parent)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	int ret = 0;
17462306a36Sopenharmony_ci	const struct cred *cred, *child_cred;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	rcu_read_lock();
17762306a36Sopenharmony_ci	cred = __task_cred(parent);
17862306a36Sopenharmony_ci	child_cred = current_cred();
17962306a36Sopenharmony_ci	if (cred->user_ns == child_cred->user_ns &&
18062306a36Sopenharmony_ci	    cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
18162306a36Sopenharmony_ci		goto out;
18262306a36Sopenharmony_ci	if (has_ns_capability(parent, child_cred->user_ns, CAP_SYS_PTRACE))
18362306a36Sopenharmony_ci		goto out;
18462306a36Sopenharmony_ci	ret = -EPERM;
18562306a36Sopenharmony_ciout:
18662306a36Sopenharmony_ci	rcu_read_unlock();
18762306a36Sopenharmony_ci	return ret;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/**
19162306a36Sopenharmony_ci * cap_capget - Retrieve a task's capability sets
19262306a36Sopenharmony_ci * @target: The task from which to retrieve the capability sets
19362306a36Sopenharmony_ci * @effective: The place to record the effective set
19462306a36Sopenharmony_ci * @inheritable: The place to record the inheritable set
19562306a36Sopenharmony_ci * @permitted: The place to record the permitted set
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * This function retrieves the capabilities of the nominated task and returns
19862306a36Sopenharmony_ci * them to the caller.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_ciint cap_capget(const struct task_struct *target, kernel_cap_t *effective,
20162306a36Sopenharmony_ci	       kernel_cap_t *inheritable, kernel_cap_t *permitted)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	const struct cred *cred;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Derived from kernel/capability.c:sys_capget. */
20662306a36Sopenharmony_ci	rcu_read_lock();
20762306a36Sopenharmony_ci	cred = __task_cred(target);
20862306a36Sopenharmony_ci	*effective   = cred->cap_effective;
20962306a36Sopenharmony_ci	*inheritable = cred->cap_inheritable;
21062306a36Sopenharmony_ci	*permitted   = cred->cap_permitted;
21162306a36Sopenharmony_ci	rcu_read_unlock();
21262306a36Sopenharmony_ci	return 0;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*
21662306a36Sopenharmony_ci * Determine whether the inheritable capabilities are limited to the old
21762306a36Sopenharmony_ci * permitted set.  Returns 1 if they are limited, 0 if they are not.
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic inline int cap_inh_is_capped(void)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	/* they are so limited unless the current task has the CAP_SETPCAP
22262306a36Sopenharmony_ci	 * capability
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	if (cap_capable(current_cred(), current_cred()->user_ns,
22562306a36Sopenharmony_ci			CAP_SETPCAP, CAP_OPT_NONE) == 0)
22662306a36Sopenharmony_ci		return 0;
22762306a36Sopenharmony_ci	return 1;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * cap_capset - Validate and apply proposed changes to current's capabilities
23262306a36Sopenharmony_ci * @new: The proposed new credentials; alterations should be made here
23362306a36Sopenharmony_ci * @old: The current task's current credentials
23462306a36Sopenharmony_ci * @effective: A pointer to the proposed new effective capabilities set
23562306a36Sopenharmony_ci * @inheritable: A pointer to the proposed new inheritable capabilities set
23662306a36Sopenharmony_ci * @permitted: A pointer to the proposed new permitted capabilities set
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * This function validates and applies a proposed mass change to the current
23962306a36Sopenharmony_ci * process's capability sets.  The changes are made to the proposed new
24062306a36Sopenharmony_ci * credentials, and assuming no error, will be committed by the caller of LSM.
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_ciint cap_capset(struct cred *new,
24362306a36Sopenharmony_ci	       const struct cred *old,
24462306a36Sopenharmony_ci	       const kernel_cap_t *effective,
24562306a36Sopenharmony_ci	       const kernel_cap_t *inheritable,
24662306a36Sopenharmony_ci	       const kernel_cap_t *permitted)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	if (cap_inh_is_capped() &&
24962306a36Sopenharmony_ci	    !cap_issubset(*inheritable,
25062306a36Sopenharmony_ci			  cap_combine(old->cap_inheritable,
25162306a36Sopenharmony_ci				      old->cap_permitted)))
25262306a36Sopenharmony_ci		/* incapable of using this inheritable set */
25362306a36Sopenharmony_ci		return -EPERM;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (!cap_issubset(*inheritable,
25662306a36Sopenharmony_ci			  cap_combine(old->cap_inheritable,
25762306a36Sopenharmony_ci				      old->cap_bset)))
25862306a36Sopenharmony_ci		/* no new pI capabilities outside bounding set */
25962306a36Sopenharmony_ci		return -EPERM;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* verify restrictions on target's new Permitted set */
26262306a36Sopenharmony_ci	if (!cap_issubset(*permitted, old->cap_permitted))
26362306a36Sopenharmony_ci		return -EPERM;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* verify the _new_Effective_ is a subset of the _new_Permitted_ */
26662306a36Sopenharmony_ci	if (!cap_issubset(*effective, *permitted))
26762306a36Sopenharmony_ci		return -EPERM;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	new->cap_effective   = *effective;
27062306a36Sopenharmony_ci	new->cap_inheritable = *inheritable;
27162306a36Sopenharmony_ci	new->cap_permitted   = *permitted;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * Mask off ambient bits that are no longer both permitted and
27562306a36Sopenharmony_ci	 * inheritable.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	new->cap_ambient = cap_intersect(new->cap_ambient,
27862306a36Sopenharmony_ci					 cap_intersect(*permitted,
27962306a36Sopenharmony_ci						       *inheritable));
28062306a36Sopenharmony_ci	if (WARN_ON(!cap_ambient_invariant_ok(new)))
28162306a36Sopenharmony_ci		return -EINVAL;
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/**
28662306a36Sopenharmony_ci * cap_inode_need_killpriv - Determine if inode change affects privileges
28762306a36Sopenharmony_ci * @dentry: The inode/dentry in being changed with change marked ATTR_KILL_PRIV
28862306a36Sopenharmony_ci *
28962306a36Sopenharmony_ci * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV
29062306a36Sopenharmony_ci * affects the security markings on that inode, and if it is, should
29162306a36Sopenharmony_ci * inode_killpriv() be invoked or the change rejected.
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci * Return: 1 if security.capability has a value, meaning inode_killpriv()
29462306a36Sopenharmony_ci * is required, 0 otherwise, meaning inode_killpriv() is not required.
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ciint cap_inode_need_killpriv(struct dentry *dentry)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct inode *inode = d_backing_inode(dentry);
29962306a36Sopenharmony_ci	int error;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	error = __vfs_getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0);
30262306a36Sopenharmony_ci	return error > 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/**
30662306a36Sopenharmony_ci * cap_inode_killpriv - Erase the security markings on an inode
30762306a36Sopenharmony_ci *
30862306a36Sopenharmony_ci * @idmap:	idmap of the mount the inode was found from
30962306a36Sopenharmony_ci * @dentry:	The inode/dentry to alter
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * Erase the privilege-enhancing security markings on an inode.
31262306a36Sopenharmony_ci *
31362306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of
31462306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then
31562306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking
31662306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be
31762306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap.
31862306a36Sopenharmony_ci *
31962306a36Sopenharmony_ci * Return: 0 if successful, -ve on error.
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_ciint cap_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int error;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	error = __vfs_removexattr(idmap, dentry, XATTR_NAME_CAPS);
32662306a36Sopenharmony_ci	if (error == -EOPNOTSUPP)
32762306a36Sopenharmony_ci		error = 0;
32862306a36Sopenharmony_ci	return error;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic bool rootid_owns_currentns(vfsuid_t rootvfsuid)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct user_namespace *ns;
33462306a36Sopenharmony_ci	kuid_t kroot;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (!vfsuid_valid(rootvfsuid))
33762306a36Sopenharmony_ci		return false;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	kroot = vfsuid_into_kuid(rootvfsuid);
34062306a36Sopenharmony_ci	for (ns = current_user_ns();; ns = ns->parent) {
34162306a36Sopenharmony_ci		if (from_kuid(ns, kroot) == 0)
34262306a36Sopenharmony_ci			return true;
34362306a36Sopenharmony_ci		if (ns == &init_user_ns)
34462306a36Sopenharmony_ci			break;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return false;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic __u32 sansflags(__u32 m)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	return m & ~VFS_CAP_FLAGS_EFFECTIVE;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic bool is_v2header(int size, const struct vfs_cap_data *cap)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	if (size != XATTR_CAPS_SZ_2)
35862306a36Sopenharmony_ci		return false;
35962306a36Sopenharmony_ci	return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic bool is_v3header(int size, const struct vfs_cap_data *cap)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	if (size != XATTR_CAPS_SZ_3)
36562306a36Sopenharmony_ci		return false;
36662306a36Sopenharmony_ci	return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_3;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*
37062306a36Sopenharmony_ci * getsecurity: We are called for security.* before any attempt to read the
37162306a36Sopenharmony_ci * xattr from the inode itself.
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * This gives us a chance to read the on-disk value and convert it.  If we
37462306a36Sopenharmony_ci * return -EOPNOTSUPP, then vfs_getxattr() will call the i_op handler.
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * Note we are not called by vfs_getxattr_alloc(), but that is only called
37762306a36Sopenharmony_ci * by the integrity subsystem, which really wants the unconverted values -
37862306a36Sopenharmony_ci * so that's good.
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_ciint cap_inode_getsecurity(struct mnt_idmap *idmap,
38162306a36Sopenharmony_ci			  struct inode *inode, const char *name, void **buffer,
38262306a36Sopenharmony_ci			  bool alloc)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int size;
38562306a36Sopenharmony_ci	kuid_t kroot;
38662306a36Sopenharmony_ci	vfsuid_t vfsroot;
38762306a36Sopenharmony_ci	u32 nsmagic, magic;
38862306a36Sopenharmony_ci	uid_t root, mappedroot;
38962306a36Sopenharmony_ci	char *tmpbuf = NULL;
39062306a36Sopenharmony_ci	struct vfs_cap_data *cap;
39162306a36Sopenharmony_ci	struct vfs_ns_cap_data *nscap = NULL;
39262306a36Sopenharmony_ci	struct dentry *dentry;
39362306a36Sopenharmony_ci	struct user_namespace *fs_ns;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (strcmp(name, "capability") != 0)
39662306a36Sopenharmony_ci		return -EOPNOTSUPP;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	dentry = d_find_any_alias(inode);
39962306a36Sopenharmony_ci	if (!dentry)
40062306a36Sopenharmony_ci		return -EINVAL;
40162306a36Sopenharmony_ci	size = vfs_getxattr_alloc(idmap, dentry, XATTR_NAME_CAPS, &tmpbuf,
40262306a36Sopenharmony_ci				  sizeof(struct vfs_ns_cap_data), GFP_NOFS);
40362306a36Sopenharmony_ci	dput(dentry);
40462306a36Sopenharmony_ci	/* gcc11 complains if we don't check for !tmpbuf */
40562306a36Sopenharmony_ci	if (size < 0 || !tmpbuf)
40662306a36Sopenharmony_ci		goto out_free;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	fs_ns = inode->i_sb->s_user_ns;
40962306a36Sopenharmony_ci	cap = (struct vfs_cap_data *) tmpbuf;
41062306a36Sopenharmony_ci	if (is_v2header(size, cap)) {
41162306a36Sopenharmony_ci		root = 0;
41262306a36Sopenharmony_ci	} else if (is_v3header(size, cap)) {
41362306a36Sopenharmony_ci		nscap = (struct vfs_ns_cap_data *) tmpbuf;
41462306a36Sopenharmony_ci		root = le32_to_cpu(nscap->rootid);
41562306a36Sopenharmony_ci	} else {
41662306a36Sopenharmony_ci		size = -EINVAL;
41762306a36Sopenharmony_ci		goto out_free;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	kroot = make_kuid(fs_ns, root);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* If this is an idmapped mount shift the kuid. */
42362306a36Sopenharmony_ci	vfsroot = make_vfsuid(idmap, fs_ns, kroot);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* If the root kuid maps to a valid uid in current ns, then return
42662306a36Sopenharmony_ci	 * this as a nscap. */
42762306a36Sopenharmony_ci	mappedroot = from_kuid(current_user_ns(), vfsuid_into_kuid(vfsroot));
42862306a36Sopenharmony_ci	if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) {
42962306a36Sopenharmony_ci		size = sizeof(struct vfs_ns_cap_data);
43062306a36Sopenharmony_ci		if (alloc) {
43162306a36Sopenharmony_ci			if (!nscap) {
43262306a36Sopenharmony_ci				/* v2 -> v3 conversion */
43362306a36Sopenharmony_ci				nscap = kzalloc(size, GFP_ATOMIC);
43462306a36Sopenharmony_ci				if (!nscap) {
43562306a36Sopenharmony_ci					size = -ENOMEM;
43662306a36Sopenharmony_ci					goto out_free;
43762306a36Sopenharmony_ci				}
43862306a36Sopenharmony_ci				nsmagic = VFS_CAP_REVISION_3;
43962306a36Sopenharmony_ci				magic = le32_to_cpu(cap->magic_etc);
44062306a36Sopenharmony_ci				if (magic & VFS_CAP_FLAGS_EFFECTIVE)
44162306a36Sopenharmony_ci					nsmagic |= VFS_CAP_FLAGS_EFFECTIVE;
44262306a36Sopenharmony_ci				memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
44362306a36Sopenharmony_ci				nscap->magic_etc = cpu_to_le32(nsmagic);
44462306a36Sopenharmony_ci			} else {
44562306a36Sopenharmony_ci				/* use allocated v3 buffer */
44662306a36Sopenharmony_ci				tmpbuf = NULL;
44762306a36Sopenharmony_ci			}
44862306a36Sopenharmony_ci			nscap->rootid = cpu_to_le32(mappedroot);
44962306a36Sopenharmony_ci			*buffer = nscap;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci		goto out_free;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (!rootid_owns_currentns(vfsroot)) {
45562306a36Sopenharmony_ci		size = -EOVERFLOW;
45662306a36Sopenharmony_ci		goto out_free;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* This comes from a parent namespace.  Return as a v2 capability */
46062306a36Sopenharmony_ci	size = sizeof(struct vfs_cap_data);
46162306a36Sopenharmony_ci	if (alloc) {
46262306a36Sopenharmony_ci		if (nscap) {
46362306a36Sopenharmony_ci			/* v3 -> v2 conversion */
46462306a36Sopenharmony_ci			cap = kzalloc(size, GFP_ATOMIC);
46562306a36Sopenharmony_ci			if (!cap) {
46662306a36Sopenharmony_ci				size = -ENOMEM;
46762306a36Sopenharmony_ci				goto out_free;
46862306a36Sopenharmony_ci			}
46962306a36Sopenharmony_ci			magic = VFS_CAP_REVISION_2;
47062306a36Sopenharmony_ci			nsmagic = le32_to_cpu(nscap->magic_etc);
47162306a36Sopenharmony_ci			if (nsmagic & VFS_CAP_FLAGS_EFFECTIVE)
47262306a36Sopenharmony_ci				magic |= VFS_CAP_FLAGS_EFFECTIVE;
47362306a36Sopenharmony_ci			memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
47462306a36Sopenharmony_ci			cap->magic_etc = cpu_to_le32(magic);
47562306a36Sopenharmony_ci		} else {
47662306a36Sopenharmony_ci			/* use unconverted v2 */
47762306a36Sopenharmony_ci			tmpbuf = NULL;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci		*buffer = cap;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ciout_free:
48262306a36Sopenharmony_ci	kfree(tmpbuf);
48362306a36Sopenharmony_ci	return size;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/**
48762306a36Sopenharmony_ci * rootid_from_xattr - translate root uid of vfs caps
48862306a36Sopenharmony_ci *
48962306a36Sopenharmony_ci * @value:	vfs caps value which may be modified by this function
49062306a36Sopenharmony_ci * @size:	size of @ivalue
49162306a36Sopenharmony_ci * @task_ns:	user namespace of the caller
49262306a36Sopenharmony_ci */
49362306a36Sopenharmony_cistatic vfsuid_t rootid_from_xattr(const void *value, size_t size,
49462306a36Sopenharmony_ci				  struct user_namespace *task_ns)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	const struct vfs_ns_cap_data *nscap = value;
49762306a36Sopenharmony_ci	uid_t rootid = 0;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (size == XATTR_CAPS_SZ_3)
50062306a36Sopenharmony_ci		rootid = le32_to_cpu(nscap->rootid);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return VFSUIDT_INIT(make_kuid(task_ns, rootid));
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic bool validheader(size_t size, const struct vfs_cap_data *cap)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	return is_v2header(size, cap) || is_v3header(size, cap);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/**
51162306a36Sopenharmony_ci * cap_convert_nscap - check vfs caps
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci * @idmap:	idmap of the mount the inode was found from
51462306a36Sopenharmony_ci * @dentry:	used to retrieve inode to check permissions on
51562306a36Sopenharmony_ci * @ivalue:	vfs caps value which may be modified by this function
51662306a36Sopenharmony_ci * @size:	size of @ivalue
51762306a36Sopenharmony_ci *
51862306a36Sopenharmony_ci * User requested a write of security.capability.  If needed, update the
51962306a36Sopenharmony_ci * xattr to change from v2 to v3, or to fixup the v3 rootid.
52062306a36Sopenharmony_ci *
52162306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of
52262306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then
52362306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking
52462306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be
52562306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap.
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * Return: On success, return the new size; on error, return < 0.
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_ciint cap_convert_nscap(struct mnt_idmap *idmap, struct dentry *dentry,
53062306a36Sopenharmony_ci		      const void **ivalue, size_t size)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct vfs_ns_cap_data *nscap;
53362306a36Sopenharmony_ci	uid_t nsrootid;
53462306a36Sopenharmony_ci	const struct vfs_cap_data *cap = *ivalue;
53562306a36Sopenharmony_ci	__u32 magic, nsmagic;
53662306a36Sopenharmony_ci	struct inode *inode = d_backing_inode(dentry);
53762306a36Sopenharmony_ci	struct user_namespace *task_ns = current_user_ns(),
53862306a36Sopenharmony_ci		*fs_ns = inode->i_sb->s_user_ns;
53962306a36Sopenharmony_ci	kuid_t rootid;
54062306a36Sopenharmony_ci	vfsuid_t vfsrootid;
54162306a36Sopenharmony_ci	size_t newsize;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (!*ivalue)
54462306a36Sopenharmony_ci		return -EINVAL;
54562306a36Sopenharmony_ci	if (!validheader(size, cap))
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci	if (!capable_wrt_inode_uidgid(idmap, inode, CAP_SETFCAP))
54862306a36Sopenharmony_ci		return -EPERM;
54962306a36Sopenharmony_ci	if (size == XATTR_CAPS_SZ_2 && (idmap == &nop_mnt_idmap))
55062306a36Sopenharmony_ci		if (ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP))
55162306a36Sopenharmony_ci			/* user is privileged, just write the v2 */
55262306a36Sopenharmony_ci			return size;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	vfsrootid = rootid_from_xattr(*ivalue, size, task_ns);
55562306a36Sopenharmony_ci	if (!vfsuid_valid(vfsrootid))
55662306a36Sopenharmony_ci		return -EINVAL;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	rootid = from_vfsuid(idmap, fs_ns, vfsrootid);
55962306a36Sopenharmony_ci	if (!uid_valid(rootid))
56062306a36Sopenharmony_ci		return -EINVAL;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	nsrootid = from_kuid(fs_ns, rootid);
56362306a36Sopenharmony_ci	if (nsrootid == -1)
56462306a36Sopenharmony_ci		return -EINVAL;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	newsize = sizeof(struct vfs_ns_cap_data);
56762306a36Sopenharmony_ci	nscap = kmalloc(newsize, GFP_ATOMIC);
56862306a36Sopenharmony_ci	if (!nscap)
56962306a36Sopenharmony_ci		return -ENOMEM;
57062306a36Sopenharmony_ci	nscap->rootid = cpu_to_le32(nsrootid);
57162306a36Sopenharmony_ci	nsmagic = VFS_CAP_REVISION_3;
57262306a36Sopenharmony_ci	magic = le32_to_cpu(cap->magic_etc);
57362306a36Sopenharmony_ci	if (magic & VFS_CAP_FLAGS_EFFECTIVE)
57462306a36Sopenharmony_ci		nsmagic |= VFS_CAP_FLAGS_EFFECTIVE;
57562306a36Sopenharmony_ci	nscap->magic_etc = cpu_to_le32(nsmagic);
57662306a36Sopenharmony_ci	memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	*ivalue = nscap;
57962306a36Sopenharmony_ci	return newsize;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/*
58362306a36Sopenharmony_ci * Calculate the new process capability sets from the capability sets attached
58462306a36Sopenharmony_ci * to a file.
58562306a36Sopenharmony_ci */
58662306a36Sopenharmony_cistatic inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
58762306a36Sopenharmony_ci					  struct linux_binprm *bprm,
58862306a36Sopenharmony_ci					  bool *effective,
58962306a36Sopenharmony_ci					  bool *has_fcap)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct cred *new = bprm->cred;
59262306a36Sopenharmony_ci	int ret = 0;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
59562306a36Sopenharmony_ci		*effective = true;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (caps->magic_etc & VFS_CAP_REVISION_MASK)
59862306a36Sopenharmony_ci		*has_fcap = true;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/*
60162306a36Sopenharmony_ci	 * pP' = (X & fP) | (pI & fI)
60262306a36Sopenharmony_ci	 * The addition of pA' is handled later.
60362306a36Sopenharmony_ci	 */
60462306a36Sopenharmony_ci	new->cap_permitted.val =
60562306a36Sopenharmony_ci		(new->cap_bset.val & caps->permitted.val) |
60662306a36Sopenharmony_ci		(new->cap_inheritable.val & caps->inheritable.val);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (caps->permitted.val & ~new->cap_permitted.val)
60962306a36Sopenharmony_ci		/* insufficient to execute correctly */
61062306a36Sopenharmony_ci		ret = -EPERM;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/*
61362306a36Sopenharmony_ci	 * For legacy apps, with no internal support for recognizing they
61462306a36Sopenharmony_ci	 * do not have enough capabilities, we return an error if they are
61562306a36Sopenharmony_ci	 * missing some "forced" (aka file-permitted) capabilities.
61662306a36Sopenharmony_ci	 */
61762306a36Sopenharmony_ci	return *effective ? ret : 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/**
62162306a36Sopenharmony_ci * get_vfs_caps_from_disk - retrieve vfs caps from disk
62262306a36Sopenharmony_ci *
62362306a36Sopenharmony_ci * @idmap:	idmap of the mount the inode was found from
62462306a36Sopenharmony_ci * @dentry:	dentry from which @inode is retrieved
62562306a36Sopenharmony_ci * @cpu_caps:	vfs capabilities
62662306a36Sopenharmony_ci *
62762306a36Sopenharmony_ci * Extract the on-exec-apply capability sets for an executable file.
62862306a36Sopenharmony_ci *
62962306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of
63062306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then
63162306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking
63262306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be
63362306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap.
63462306a36Sopenharmony_ci */
63562306a36Sopenharmony_ciint get_vfs_caps_from_disk(struct mnt_idmap *idmap,
63662306a36Sopenharmony_ci			   const struct dentry *dentry,
63762306a36Sopenharmony_ci			   struct cpu_vfs_cap_data *cpu_caps)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct inode *inode = d_backing_inode(dentry);
64062306a36Sopenharmony_ci	__u32 magic_etc;
64162306a36Sopenharmony_ci	int size;
64262306a36Sopenharmony_ci	struct vfs_ns_cap_data data, *nscaps = &data;
64362306a36Sopenharmony_ci	struct vfs_cap_data *caps = (struct vfs_cap_data *) &data;
64462306a36Sopenharmony_ci	kuid_t rootkuid;
64562306a36Sopenharmony_ci	vfsuid_t rootvfsuid;
64662306a36Sopenharmony_ci	struct user_namespace *fs_ns;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (!inode)
65162306a36Sopenharmony_ci		return -ENODATA;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	fs_ns = inode->i_sb->s_user_ns;
65462306a36Sopenharmony_ci	size = __vfs_getxattr((struct dentry *)dentry, inode,
65562306a36Sopenharmony_ci			      XATTR_NAME_CAPS, &data, XATTR_CAPS_SZ);
65662306a36Sopenharmony_ci	if (size == -ENODATA || size == -EOPNOTSUPP)
65762306a36Sopenharmony_ci		/* no data, that's ok */
65862306a36Sopenharmony_ci		return -ENODATA;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (size < 0)
66162306a36Sopenharmony_ci		return size;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (size < sizeof(magic_etc))
66462306a36Sopenharmony_ci		return -EINVAL;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps->magic_etc);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	rootkuid = make_kuid(fs_ns, 0);
66962306a36Sopenharmony_ci	switch (magic_etc & VFS_CAP_REVISION_MASK) {
67062306a36Sopenharmony_ci	case VFS_CAP_REVISION_1:
67162306a36Sopenharmony_ci		if (size != XATTR_CAPS_SZ_1)
67262306a36Sopenharmony_ci			return -EINVAL;
67362306a36Sopenharmony_ci		break;
67462306a36Sopenharmony_ci	case VFS_CAP_REVISION_2:
67562306a36Sopenharmony_ci		if (size != XATTR_CAPS_SZ_2)
67662306a36Sopenharmony_ci			return -EINVAL;
67762306a36Sopenharmony_ci		break;
67862306a36Sopenharmony_ci	case VFS_CAP_REVISION_3:
67962306a36Sopenharmony_ci		if (size != XATTR_CAPS_SZ_3)
68062306a36Sopenharmony_ci			return -EINVAL;
68162306a36Sopenharmony_ci		rootkuid = make_kuid(fs_ns, le32_to_cpu(nscaps->rootid));
68262306a36Sopenharmony_ci		break;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	default:
68562306a36Sopenharmony_ci		return -EINVAL;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	rootvfsuid = make_vfsuid(idmap, fs_ns, rootkuid);
68962306a36Sopenharmony_ci	if (!vfsuid_valid(rootvfsuid))
69062306a36Sopenharmony_ci		return -ENODATA;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Limit the caps to the mounter of the filesystem
69362306a36Sopenharmony_ci	 * or the more limited uid specified in the xattr.
69462306a36Sopenharmony_ci	 */
69562306a36Sopenharmony_ci	if (!rootid_owns_currentns(rootvfsuid))
69662306a36Sopenharmony_ci		return -ENODATA;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	cpu_caps->permitted.val = le32_to_cpu(caps->data[0].permitted);
69962306a36Sopenharmony_ci	cpu_caps->inheritable.val = le32_to_cpu(caps->data[0].inheritable);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/*
70262306a36Sopenharmony_ci	 * Rev1 had just a single 32-bit word, later expanded
70362306a36Sopenharmony_ci	 * to a second one for the high bits
70462306a36Sopenharmony_ci	 */
70562306a36Sopenharmony_ci	if ((magic_etc & VFS_CAP_REVISION_MASK) != VFS_CAP_REVISION_1) {
70662306a36Sopenharmony_ci		cpu_caps->permitted.val += (u64)le32_to_cpu(caps->data[1].permitted) << 32;
70762306a36Sopenharmony_ci		cpu_caps->inheritable.val += (u64)le32_to_cpu(caps->data[1].inheritable) << 32;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	cpu_caps->permitted.val &= CAP_VALID_MASK;
71162306a36Sopenharmony_ci	cpu_caps->inheritable.val &= CAP_VALID_MASK;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	cpu_caps->rootid = vfsuid_into_kuid(rootvfsuid);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return 0;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci/*
71962306a36Sopenharmony_ci * Attempt to get the on-exec apply capability sets for an executable file from
72062306a36Sopenharmony_ci * its xattrs and, if present, apply them to the proposed credentials being
72162306a36Sopenharmony_ci * constructed by execve().
72262306a36Sopenharmony_ci */
72362306a36Sopenharmony_cistatic int get_file_caps(struct linux_binprm *bprm, struct file *file,
72462306a36Sopenharmony_ci			 bool *effective, bool *has_fcap)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	int rc = 0;
72762306a36Sopenharmony_ci	struct cpu_vfs_cap_data vcaps;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	cap_clear(bprm->cred->cap_permitted);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (!file_caps_enabled)
73262306a36Sopenharmony_ci		return 0;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (!mnt_may_suid(file->f_path.mnt))
73562306a36Sopenharmony_ci		return 0;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/*
73862306a36Sopenharmony_ci	 * This check is redundant with mnt_may_suid() but is kept to make
73962306a36Sopenharmony_ci	 * explicit that capability bits are limited to s_user_ns and its
74062306a36Sopenharmony_ci	 * descendants.
74162306a36Sopenharmony_ci	 */
74262306a36Sopenharmony_ci	if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns))
74362306a36Sopenharmony_ci		return 0;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	rc = get_vfs_caps_from_disk(file_mnt_idmap(file),
74662306a36Sopenharmony_ci				    file->f_path.dentry, &vcaps);
74762306a36Sopenharmony_ci	if (rc < 0) {
74862306a36Sopenharmony_ci		if (rc == -EINVAL)
74962306a36Sopenharmony_ci			printk(KERN_NOTICE "Invalid argument reading file caps for %s\n",
75062306a36Sopenharmony_ci					bprm->filename);
75162306a36Sopenharmony_ci		else if (rc == -ENODATA)
75262306a36Sopenharmony_ci			rc = 0;
75362306a36Sopenharmony_ci		goto out;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_fcap);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ciout:
75962306a36Sopenharmony_ci	if (rc)
76062306a36Sopenharmony_ci		cap_clear(bprm->cred->cap_permitted);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return rc;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic inline bool root_privileged(void) { return !issecure(SECURE_NOROOT); }
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic inline bool __is_real(kuid_t uid, struct cred *cred)
76862306a36Sopenharmony_ci{ return uid_eq(cred->uid, uid); }
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic inline bool __is_eff(kuid_t uid, struct cred *cred)
77162306a36Sopenharmony_ci{ return uid_eq(cred->euid, uid); }
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic inline bool __is_suid(kuid_t uid, struct cred *cred)
77462306a36Sopenharmony_ci{ return !__is_real(uid, cred) && __is_eff(uid, cred); }
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci/*
77762306a36Sopenharmony_ci * handle_privileged_root - Handle case of privileged root
77862306a36Sopenharmony_ci * @bprm: The execution parameters, including the proposed creds
77962306a36Sopenharmony_ci * @has_fcap: Are any file capabilities set?
78062306a36Sopenharmony_ci * @effective: Do we have effective root privilege?
78162306a36Sopenharmony_ci * @root_uid: This namespace' root UID WRT initial USER namespace
78262306a36Sopenharmony_ci *
78362306a36Sopenharmony_ci * Handle the case where root is privileged and hasn't been neutered by
78462306a36Sopenharmony_ci * SECURE_NOROOT.  If file capabilities are set, they won't be combined with
78562306a36Sopenharmony_ci * set UID root and nothing is changed.  If we are root, cap_permitted is
78662306a36Sopenharmony_ci * updated.  If we have become set UID root, the effective bit is set.
78762306a36Sopenharmony_ci */
78862306a36Sopenharmony_cistatic void handle_privileged_root(struct linux_binprm *bprm, bool has_fcap,
78962306a36Sopenharmony_ci				   bool *effective, kuid_t root_uid)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	const struct cred *old = current_cred();
79262306a36Sopenharmony_ci	struct cred *new = bprm->cred;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (!root_privileged())
79562306a36Sopenharmony_ci		return;
79662306a36Sopenharmony_ci	/*
79762306a36Sopenharmony_ci	 * If the legacy file capability is set, then don't set privs
79862306a36Sopenharmony_ci	 * for a setuid root binary run by a non-root user.  Do set it
79962306a36Sopenharmony_ci	 * for a root user just to cause least surprise to an admin.
80062306a36Sopenharmony_ci	 */
80162306a36Sopenharmony_ci	if (has_fcap && __is_suid(root_uid, new)) {
80262306a36Sopenharmony_ci		warn_setuid_and_fcaps_mixed(bprm->filename);
80362306a36Sopenharmony_ci		return;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	/*
80662306a36Sopenharmony_ci	 * To support inheritance of root-permissions and suid-root
80762306a36Sopenharmony_ci	 * executables under compatibility mode, we override the
80862306a36Sopenharmony_ci	 * capability sets for the file.
80962306a36Sopenharmony_ci	 */
81062306a36Sopenharmony_ci	if (__is_eff(root_uid, new) || __is_real(root_uid, new)) {
81162306a36Sopenharmony_ci		/* pP' = (cap_bset & ~0) | (pI & ~0) */
81262306a36Sopenharmony_ci		new->cap_permitted = cap_combine(old->cap_bset,
81362306a36Sopenharmony_ci						 old->cap_inheritable);
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci	/*
81662306a36Sopenharmony_ci	 * If only the real uid is 0, we do not set the effective bit.
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	if (__is_eff(root_uid, new))
81962306a36Sopenharmony_ci		*effective = true;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci#define __cap_gained(field, target, source) \
82362306a36Sopenharmony_ci	!cap_issubset(target->cap_##field, source->cap_##field)
82462306a36Sopenharmony_ci#define __cap_grew(target, source, cred) \
82562306a36Sopenharmony_ci	!cap_issubset(cred->cap_##target, cred->cap_##source)
82662306a36Sopenharmony_ci#define __cap_full(field, cred) \
82762306a36Sopenharmony_ci	cap_issubset(CAP_FULL_SET, cred->cap_##field)
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic inline bool __is_setuid(struct cred *new, const struct cred *old)
83062306a36Sopenharmony_ci{ return !uid_eq(new->euid, old->uid); }
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic inline bool __is_setgid(struct cred *new, const struct cred *old)
83362306a36Sopenharmony_ci{ return !gid_eq(new->egid, old->gid); }
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci/*
83662306a36Sopenharmony_ci * 1) Audit candidate if current->cap_effective is set
83762306a36Sopenharmony_ci *
83862306a36Sopenharmony_ci * We do not bother to audit if 3 things are true:
83962306a36Sopenharmony_ci *   1) cap_effective has all caps
84062306a36Sopenharmony_ci *   2) we became root *OR* are were already root
84162306a36Sopenharmony_ci *   3) root is supposed to have all caps (SECURE_NOROOT)
84262306a36Sopenharmony_ci * Since this is just a normal root execing a process.
84362306a36Sopenharmony_ci *
84462306a36Sopenharmony_ci * Number 1 above might fail if you don't have a full bset, but I think
84562306a36Sopenharmony_ci * that is interesting information to audit.
84662306a36Sopenharmony_ci *
84762306a36Sopenharmony_ci * A number of other conditions require logging:
84862306a36Sopenharmony_ci * 2) something prevented setuid root getting all caps
84962306a36Sopenharmony_ci * 3) non-setuid root gets fcaps
85062306a36Sopenharmony_ci * 4) non-setuid root gets ambient
85162306a36Sopenharmony_ci */
85262306a36Sopenharmony_cistatic inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
85362306a36Sopenharmony_ci				     kuid_t root, bool has_fcap)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	bool ret = false;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if ((__cap_grew(effective, ambient, new) &&
85862306a36Sopenharmony_ci	     !(__cap_full(effective, new) &&
85962306a36Sopenharmony_ci	       (__is_eff(root, new) || __is_real(root, new)) &&
86062306a36Sopenharmony_ci	       root_privileged())) ||
86162306a36Sopenharmony_ci	    (root_privileged() &&
86262306a36Sopenharmony_ci	     __is_suid(root, new) &&
86362306a36Sopenharmony_ci	     !__cap_full(effective, new)) ||
86462306a36Sopenharmony_ci	    (!__is_setuid(new, old) &&
86562306a36Sopenharmony_ci	     ((has_fcap &&
86662306a36Sopenharmony_ci	       __cap_gained(permitted, new, old)) ||
86762306a36Sopenharmony_ci	      __cap_gained(ambient, new, old))))
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		ret = true;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return ret;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci/**
87562306a36Sopenharmony_ci * cap_bprm_creds_from_file - Set up the proposed credentials for execve().
87662306a36Sopenharmony_ci * @bprm: The execution parameters, including the proposed creds
87762306a36Sopenharmony_ci * @file: The file to pull the credentials from
87862306a36Sopenharmony_ci *
87962306a36Sopenharmony_ci * Set up the proposed credentials for a new execution context being
88062306a36Sopenharmony_ci * constructed by execve().  The proposed creds in @bprm->cred is altered,
88162306a36Sopenharmony_ci * which won't take effect immediately.
88262306a36Sopenharmony_ci *
88362306a36Sopenharmony_ci * Return: 0 if successful, -ve on error.
88462306a36Sopenharmony_ci */
88562306a36Sopenharmony_ciint cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	/* Process setpcap binaries and capabilities for uid 0 */
88862306a36Sopenharmony_ci	const struct cred *old = current_cred();
88962306a36Sopenharmony_ci	struct cred *new = bprm->cred;
89062306a36Sopenharmony_ci	bool effective = false, has_fcap = false, is_setid;
89162306a36Sopenharmony_ci	int ret;
89262306a36Sopenharmony_ci	kuid_t root_uid;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (WARN_ON(!cap_ambient_invariant_ok(old)))
89562306a36Sopenharmony_ci		return -EPERM;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ret = get_file_caps(bprm, file, &effective, &has_fcap);
89862306a36Sopenharmony_ci	if (ret < 0)
89962306a36Sopenharmony_ci		return ret;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	root_uid = make_kuid(new->user_ns, 0);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	handle_privileged_root(bprm, has_fcap, &effective, root_uid);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* if we have fs caps, clear dangerous personality flags */
90662306a36Sopenharmony_ci	if (__cap_gained(permitted, new, old))
90762306a36Sopenharmony_ci		bprm->per_clear |= PER_CLEAR_ON_SETID;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Don't let someone trace a set[ug]id/setpcap binary with the revised
91062306a36Sopenharmony_ci	 * credentials unless they have the appropriate permit.
91162306a36Sopenharmony_ci	 *
91262306a36Sopenharmony_ci	 * In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
91362306a36Sopenharmony_ci	 */
91462306a36Sopenharmony_ci	is_setid = __is_setuid(new, old) || __is_setgid(new, old);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if ((is_setid || __cap_gained(permitted, new, old)) &&
91762306a36Sopenharmony_ci	    ((bprm->unsafe & ~LSM_UNSAFE_PTRACE) ||
91862306a36Sopenharmony_ci	     !ptracer_capable(current, new->user_ns))) {
91962306a36Sopenharmony_ci		/* downgrade; they get no more than they had, and maybe less */
92062306a36Sopenharmony_ci		if (!ns_capable(new->user_ns, CAP_SETUID) ||
92162306a36Sopenharmony_ci		    (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
92262306a36Sopenharmony_ci			new->euid = new->uid;
92362306a36Sopenharmony_ci			new->egid = new->gid;
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci		new->cap_permitted = cap_intersect(new->cap_permitted,
92662306a36Sopenharmony_ci						   old->cap_permitted);
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	new->suid = new->fsuid = new->euid;
93062306a36Sopenharmony_ci	new->sgid = new->fsgid = new->egid;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* File caps or setid cancels ambient. */
93362306a36Sopenharmony_ci	if (has_fcap || is_setid)
93462306a36Sopenharmony_ci		cap_clear(new->cap_ambient);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/*
93762306a36Sopenharmony_ci	 * Now that we've computed pA', update pP' to give:
93862306a36Sopenharmony_ci	 *   pP' = (X & fP) | (pI & fI) | pA'
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_ci	new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/*
94362306a36Sopenharmony_ci	 * Set pE' = (fE ? pP' : pA').  Because pA' is zero if fE is set,
94462306a36Sopenharmony_ci	 * this is the same as pE' = (fE ? pP' : 0) | pA'.
94562306a36Sopenharmony_ci	 */
94662306a36Sopenharmony_ci	if (effective)
94762306a36Sopenharmony_ci		new->cap_effective = new->cap_permitted;
94862306a36Sopenharmony_ci	else
94962306a36Sopenharmony_ci		new->cap_effective = new->cap_ambient;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (WARN_ON(!cap_ambient_invariant_ok(new)))
95262306a36Sopenharmony_ci		return -EPERM;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (nonroot_raised_pE(new, old, root_uid, has_fcap)) {
95562306a36Sopenharmony_ci		ret = audit_log_bprm_fcaps(bprm, new, old);
95662306a36Sopenharmony_ci		if (ret < 0)
95762306a36Sopenharmony_ci			return ret;
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (WARN_ON(!cap_ambient_invariant_ok(new)))
96362306a36Sopenharmony_ci		return -EPERM;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* Check for privilege-elevated exec. */
96662306a36Sopenharmony_ci	if (is_setid ||
96762306a36Sopenharmony_ci	    (!__is_real(root_uid, new) &&
96862306a36Sopenharmony_ci	     (effective ||
96962306a36Sopenharmony_ci	      __cap_grew(permitted, ambient, new))))
97062306a36Sopenharmony_ci		bprm->secureexec = 1;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci/**
97662306a36Sopenharmony_ci * cap_inode_setxattr - Determine whether an xattr may be altered
97762306a36Sopenharmony_ci * @dentry: The inode/dentry being altered
97862306a36Sopenharmony_ci * @name: The name of the xattr to be changed
97962306a36Sopenharmony_ci * @value: The value that the xattr will be changed to
98062306a36Sopenharmony_ci * @size: The size of value
98162306a36Sopenharmony_ci * @flags: The replacement flag
98262306a36Sopenharmony_ci *
98362306a36Sopenharmony_ci * Determine whether an xattr may be altered or set on an inode, returning 0 if
98462306a36Sopenharmony_ci * permission is granted, -ve if denied.
98562306a36Sopenharmony_ci *
98662306a36Sopenharmony_ci * This is used to make sure security xattrs don't get updated or set by those
98762306a36Sopenharmony_ci * who aren't privileged to do so.
98862306a36Sopenharmony_ci */
98962306a36Sopenharmony_ciint cap_inode_setxattr(struct dentry *dentry, const char *name,
99062306a36Sopenharmony_ci		       const void *value, size_t size, int flags)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/* Ignore non-security xattrs */
99562306a36Sopenharmony_ci	if (strncmp(name, XATTR_SECURITY_PREFIX,
99662306a36Sopenharmony_ci			XATTR_SECURITY_PREFIX_LEN) != 0)
99762306a36Sopenharmony_ci		return 0;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/*
100062306a36Sopenharmony_ci	 * For XATTR_NAME_CAPS the check will be done in
100162306a36Sopenharmony_ci	 * cap_convert_nscap(), called by setxattr()
100262306a36Sopenharmony_ci	 */
100362306a36Sopenharmony_ci	if (strcmp(name, XATTR_NAME_CAPS) == 0)
100462306a36Sopenharmony_ci		return 0;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (!ns_capable(user_ns, CAP_SYS_ADMIN))
100762306a36Sopenharmony_ci		return -EPERM;
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci/**
101262306a36Sopenharmony_ci * cap_inode_removexattr - Determine whether an xattr may be removed
101362306a36Sopenharmony_ci *
101462306a36Sopenharmony_ci * @idmap:	idmap of the mount the inode was found from
101562306a36Sopenharmony_ci * @dentry:	The inode/dentry being altered
101662306a36Sopenharmony_ci * @name:	The name of the xattr to be changed
101762306a36Sopenharmony_ci *
101862306a36Sopenharmony_ci * Determine whether an xattr may be removed from an inode, returning 0 if
101962306a36Sopenharmony_ci * permission is granted, -ve if denied.
102062306a36Sopenharmony_ci *
102162306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of
102262306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then
102362306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking
102462306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be
102562306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap.
102662306a36Sopenharmony_ci *
102762306a36Sopenharmony_ci * This is used to make sure security xattrs don't get removed by those who
102862306a36Sopenharmony_ci * aren't privileged to remove them.
102962306a36Sopenharmony_ci */
103062306a36Sopenharmony_ciint cap_inode_removexattr(struct mnt_idmap *idmap,
103162306a36Sopenharmony_ci			  struct dentry *dentry, const char *name)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct user_namespace *user_ns = dentry->d_sb->s_user_ns;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* Ignore non-security xattrs */
103662306a36Sopenharmony_ci	if (strncmp(name, XATTR_SECURITY_PREFIX,
103762306a36Sopenharmony_ci			XATTR_SECURITY_PREFIX_LEN) != 0)
103862306a36Sopenharmony_ci		return 0;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	if (strcmp(name, XATTR_NAME_CAPS) == 0) {
104162306a36Sopenharmony_ci		/* security.capability gets namespaced */
104262306a36Sopenharmony_ci		struct inode *inode = d_backing_inode(dentry);
104362306a36Sopenharmony_ci		if (!inode)
104462306a36Sopenharmony_ci			return -EINVAL;
104562306a36Sopenharmony_ci		if (!capable_wrt_inode_uidgid(idmap, inode, CAP_SETFCAP))
104662306a36Sopenharmony_ci			return -EPERM;
104762306a36Sopenharmony_ci		return 0;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (!ns_capable(user_ns, CAP_SYS_ADMIN))
105162306a36Sopenharmony_ci		return -EPERM;
105262306a36Sopenharmony_ci	return 0;
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci/*
105662306a36Sopenharmony_ci * cap_emulate_setxuid() fixes the effective / permitted capabilities of
105762306a36Sopenharmony_ci * a process after a call to setuid, setreuid, or setresuid.
105862306a36Sopenharmony_ci *
105962306a36Sopenharmony_ci *  1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of
106062306a36Sopenharmony_ci *  {r,e,s}uid != 0, the permitted and effective capabilities are
106162306a36Sopenharmony_ci *  cleared.
106262306a36Sopenharmony_ci *
106362306a36Sopenharmony_ci *  2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective
106462306a36Sopenharmony_ci *  capabilities of the process are cleared.
106562306a36Sopenharmony_ci *
106662306a36Sopenharmony_ci *  3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
106762306a36Sopenharmony_ci *  capabilities are set to the permitted capabilities.
106862306a36Sopenharmony_ci *
106962306a36Sopenharmony_ci *  fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
107062306a36Sopenharmony_ci *  never happen.
107162306a36Sopenharmony_ci *
107262306a36Sopenharmony_ci *  -astor
107362306a36Sopenharmony_ci *
107462306a36Sopenharmony_ci * cevans - New behaviour, Oct '99
107562306a36Sopenharmony_ci * A process may, via prctl(), elect to keep its capabilities when it
107662306a36Sopenharmony_ci * calls setuid() and switches away from uid==0. Both permitted and
107762306a36Sopenharmony_ci * effective sets will be retained.
107862306a36Sopenharmony_ci * Without this change, it was impossible for a daemon to drop only some
107962306a36Sopenharmony_ci * of its privilege. The call to setuid(!=0) would drop all privileges!
108062306a36Sopenharmony_ci * Keeping uid 0 is not an option because uid 0 owns too many vital
108162306a36Sopenharmony_ci * files..
108262306a36Sopenharmony_ci * Thanks to Olaf Kirch and Peter Benie for spotting this.
108362306a36Sopenharmony_ci */
108462306a36Sopenharmony_cistatic inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	kuid_t root_uid = make_kuid(old->user_ns, 0);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if ((uid_eq(old->uid, root_uid) ||
108962306a36Sopenharmony_ci	     uid_eq(old->euid, root_uid) ||
109062306a36Sopenharmony_ci	     uid_eq(old->suid, root_uid)) &&
109162306a36Sopenharmony_ci	    (!uid_eq(new->uid, root_uid) &&
109262306a36Sopenharmony_ci	     !uid_eq(new->euid, root_uid) &&
109362306a36Sopenharmony_ci	     !uid_eq(new->suid, root_uid))) {
109462306a36Sopenharmony_ci		if (!issecure(SECURE_KEEP_CAPS)) {
109562306a36Sopenharmony_ci			cap_clear(new->cap_permitted);
109662306a36Sopenharmony_ci			cap_clear(new->cap_effective);
109762306a36Sopenharmony_ci		}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		/*
110062306a36Sopenharmony_ci		 * Pre-ambient programs expect setresuid to nonroot followed
110162306a36Sopenharmony_ci		 * by exec to drop capabilities.  We should make sure that
110262306a36Sopenharmony_ci		 * this remains the case.
110362306a36Sopenharmony_ci		 */
110462306a36Sopenharmony_ci		cap_clear(new->cap_ambient);
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci	if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
110762306a36Sopenharmony_ci		cap_clear(new->cap_effective);
110862306a36Sopenharmony_ci	if (!uid_eq(old->euid, root_uid) && uid_eq(new->euid, root_uid))
110962306a36Sopenharmony_ci		new->cap_effective = new->cap_permitted;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci/**
111362306a36Sopenharmony_ci * cap_task_fix_setuid - Fix up the results of setuid() call
111462306a36Sopenharmony_ci * @new: The proposed credentials
111562306a36Sopenharmony_ci * @old: The current task's current credentials
111662306a36Sopenharmony_ci * @flags: Indications of what has changed
111762306a36Sopenharmony_ci *
111862306a36Sopenharmony_ci * Fix up the results of setuid() call before the credential changes are
111962306a36Sopenharmony_ci * actually applied.
112062306a36Sopenharmony_ci *
112162306a36Sopenharmony_ci * Return: 0 to grant the changes, -ve to deny them.
112262306a36Sopenharmony_ci */
112362306a36Sopenharmony_ciint cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	switch (flags) {
112662306a36Sopenharmony_ci	case LSM_SETID_RE:
112762306a36Sopenharmony_ci	case LSM_SETID_ID:
112862306a36Sopenharmony_ci	case LSM_SETID_RES:
112962306a36Sopenharmony_ci		/* juggle the capabilities to follow [RES]UID changes unless
113062306a36Sopenharmony_ci		 * otherwise suppressed */
113162306a36Sopenharmony_ci		if (!issecure(SECURE_NO_SETUID_FIXUP))
113262306a36Sopenharmony_ci			cap_emulate_setxuid(new, old);
113362306a36Sopenharmony_ci		break;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	case LSM_SETID_FS:
113662306a36Sopenharmony_ci		/* juggle the capabilities to follow FSUID changes, unless
113762306a36Sopenharmony_ci		 * otherwise suppressed
113862306a36Sopenharmony_ci		 *
113962306a36Sopenharmony_ci		 * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
114062306a36Sopenharmony_ci		 *          if not, we might be a bit too harsh here.
114162306a36Sopenharmony_ci		 */
114262306a36Sopenharmony_ci		if (!issecure(SECURE_NO_SETUID_FIXUP)) {
114362306a36Sopenharmony_ci			kuid_t root_uid = make_kuid(old->user_ns, 0);
114462306a36Sopenharmony_ci			if (uid_eq(old->fsuid, root_uid) && !uid_eq(new->fsuid, root_uid))
114562306a36Sopenharmony_ci				new->cap_effective =
114662306a36Sopenharmony_ci					cap_drop_fs_set(new->cap_effective);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci			if (!uid_eq(old->fsuid, root_uid) && uid_eq(new->fsuid, root_uid))
114962306a36Sopenharmony_ci				new->cap_effective =
115062306a36Sopenharmony_ci					cap_raise_fs_set(new->cap_effective,
115162306a36Sopenharmony_ci							 new->cap_permitted);
115262306a36Sopenharmony_ci		}
115362306a36Sopenharmony_ci		break;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	default:
115662306a36Sopenharmony_ci		return -EINVAL;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	return 0;
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci/*
116362306a36Sopenharmony_ci * Rationale: code calling task_setscheduler, task_setioprio, and
116462306a36Sopenharmony_ci * task_setnice, assumes that
116562306a36Sopenharmony_ci *   . if capable(cap_sys_nice), then those actions should be allowed
116662306a36Sopenharmony_ci *   . if not capable(cap_sys_nice), but acting on your own processes,
116762306a36Sopenharmony_ci *   	then those actions should be allowed
116862306a36Sopenharmony_ci * This is insufficient now since you can call code without suid, but
116962306a36Sopenharmony_ci * yet with increased caps.
117062306a36Sopenharmony_ci * So we check for increased caps on the target process.
117162306a36Sopenharmony_ci */
117262306a36Sopenharmony_cistatic int cap_safe_nice(struct task_struct *p)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	int is_subset, ret = 0;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	rcu_read_lock();
117762306a36Sopenharmony_ci	is_subset = cap_issubset(__task_cred(p)->cap_permitted,
117862306a36Sopenharmony_ci				 current_cred()->cap_permitted);
117962306a36Sopenharmony_ci	if (!is_subset && !ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE))
118062306a36Sopenharmony_ci		ret = -EPERM;
118162306a36Sopenharmony_ci	rcu_read_unlock();
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return ret;
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci/**
118762306a36Sopenharmony_ci * cap_task_setscheduler - Determine if scheduler policy change is permitted
118862306a36Sopenharmony_ci * @p: The task to affect
118962306a36Sopenharmony_ci *
119062306a36Sopenharmony_ci * Determine if the requested scheduler policy change is permitted for the
119162306a36Sopenharmony_ci * specified task.
119262306a36Sopenharmony_ci *
119362306a36Sopenharmony_ci * Return: 0 if permission is granted, -ve if denied.
119462306a36Sopenharmony_ci */
119562306a36Sopenharmony_ciint cap_task_setscheduler(struct task_struct *p)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	return cap_safe_nice(p);
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci/**
120162306a36Sopenharmony_ci * cap_task_setioprio - Determine if I/O priority change is permitted
120262306a36Sopenharmony_ci * @p: The task to affect
120362306a36Sopenharmony_ci * @ioprio: The I/O priority to set
120462306a36Sopenharmony_ci *
120562306a36Sopenharmony_ci * Determine if the requested I/O priority change is permitted for the specified
120662306a36Sopenharmony_ci * task.
120762306a36Sopenharmony_ci *
120862306a36Sopenharmony_ci * Return: 0 if permission is granted, -ve if denied.
120962306a36Sopenharmony_ci */
121062306a36Sopenharmony_ciint cap_task_setioprio(struct task_struct *p, int ioprio)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	return cap_safe_nice(p);
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci/**
121662306a36Sopenharmony_ci * cap_task_setnice - Determine if task priority change is permitted
121762306a36Sopenharmony_ci * @p: The task to affect
121862306a36Sopenharmony_ci * @nice: The nice value to set
121962306a36Sopenharmony_ci *
122062306a36Sopenharmony_ci * Determine if the requested task priority change is permitted for the
122162306a36Sopenharmony_ci * specified task.
122262306a36Sopenharmony_ci *
122362306a36Sopenharmony_ci * Return: 0 if permission is granted, -ve if denied.
122462306a36Sopenharmony_ci */
122562306a36Sopenharmony_ciint cap_task_setnice(struct task_struct *p, int nice)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	return cap_safe_nice(p);
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci/*
123162306a36Sopenharmony_ci * Implement PR_CAPBSET_DROP.  Attempt to remove the specified capability from
123262306a36Sopenharmony_ci * the current task's bounding set.  Returns 0 on success, -ve on error.
123362306a36Sopenharmony_ci */
123462306a36Sopenharmony_cistatic int cap_prctl_drop(unsigned long cap)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct cred *new;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	if (!ns_capable(current_user_ns(), CAP_SETPCAP))
123962306a36Sopenharmony_ci		return -EPERM;
124062306a36Sopenharmony_ci	if (!cap_valid(cap))
124162306a36Sopenharmony_ci		return -EINVAL;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	new = prepare_creds();
124462306a36Sopenharmony_ci	if (!new)
124562306a36Sopenharmony_ci		return -ENOMEM;
124662306a36Sopenharmony_ci	cap_lower(new->cap_bset, cap);
124762306a36Sopenharmony_ci	return commit_creds(new);
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/**
125162306a36Sopenharmony_ci * cap_task_prctl - Implement process control functions for this security module
125262306a36Sopenharmony_ci * @option: The process control function requested
125362306a36Sopenharmony_ci * @arg2: The argument data for this function
125462306a36Sopenharmony_ci * @arg3: The argument data for this function
125562306a36Sopenharmony_ci * @arg4: The argument data for this function
125662306a36Sopenharmony_ci * @arg5: The argument data for this function
125762306a36Sopenharmony_ci *
125862306a36Sopenharmony_ci * Allow process control functions (sys_prctl()) to alter capabilities; may
125962306a36Sopenharmony_ci * also deny access to other functions not otherwise implemented here.
126062306a36Sopenharmony_ci *
126162306a36Sopenharmony_ci * Return: 0 or +ve on success, -ENOSYS if this function is not implemented
126262306a36Sopenharmony_ci * here, other -ve on error.  If -ENOSYS is returned, sys_prctl() and other LSM
126362306a36Sopenharmony_ci * modules will consider performing the function.
126462306a36Sopenharmony_ci */
126562306a36Sopenharmony_ciint cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
126662306a36Sopenharmony_ci		   unsigned long arg4, unsigned long arg5)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	const struct cred *old = current_cred();
126962306a36Sopenharmony_ci	struct cred *new;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	switch (option) {
127262306a36Sopenharmony_ci	case PR_CAPBSET_READ:
127362306a36Sopenharmony_ci		if (!cap_valid(arg2))
127462306a36Sopenharmony_ci			return -EINVAL;
127562306a36Sopenharmony_ci		return !!cap_raised(old->cap_bset, arg2);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	case PR_CAPBSET_DROP:
127862306a36Sopenharmony_ci		return cap_prctl_drop(arg2);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	/*
128162306a36Sopenharmony_ci	 * The next four prctl's remain to assist with transitioning a
128262306a36Sopenharmony_ci	 * system from legacy UID=0 based privilege (when filesystem
128362306a36Sopenharmony_ci	 * capabilities are not in use) to a system using filesystem
128462306a36Sopenharmony_ci	 * capabilities only - as the POSIX.1e draft intended.
128562306a36Sopenharmony_ci	 *
128662306a36Sopenharmony_ci	 * Note:
128762306a36Sopenharmony_ci	 *
128862306a36Sopenharmony_ci	 *  PR_SET_SECUREBITS =
128962306a36Sopenharmony_ci	 *      issecure_mask(SECURE_KEEP_CAPS_LOCKED)
129062306a36Sopenharmony_ci	 *    | issecure_mask(SECURE_NOROOT)
129162306a36Sopenharmony_ci	 *    | issecure_mask(SECURE_NOROOT_LOCKED)
129262306a36Sopenharmony_ci	 *    | issecure_mask(SECURE_NO_SETUID_FIXUP)
129362306a36Sopenharmony_ci	 *    | issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)
129462306a36Sopenharmony_ci	 *
129562306a36Sopenharmony_ci	 * will ensure that the current process and all of its
129662306a36Sopenharmony_ci	 * children will be locked into a pure
129762306a36Sopenharmony_ci	 * capability-based-privilege environment.
129862306a36Sopenharmony_ci	 */
129962306a36Sopenharmony_ci	case PR_SET_SECUREBITS:
130062306a36Sopenharmony_ci		if ((((old->securebits & SECURE_ALL_LOCKS) >> 1)
130162306a36Sopenharmony_ci		     & (old->securebits ^ arg2))			/*[1]*/
130262306a36Sopenharmony_ci		    || ((old->securebits & SECURE_ALL_LOCKS & ~arg2))	/*[2]*/
130362306a36Sopenharmony_ci		    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))	/*[3]*/
130462306a36Sopenharmony_ci		    || (cap_capable(current_cred(),
130562306a36Sopenharmony_ci				    current_cred()->user_ns,
130662306a36Sopenharmony_ci				    CAP_SETPCAP,
130762306a36Sopenharmony_ci				    CAP_OPT_NONE) != 0)			/*[4]*/
130862306a36Sopenharmony_ci			/*
130962306a36Sopenharmony_ci			 * [1] no changing of bits that are locked
131062306a36Sopenharmony_ci			 * [2] no unlocking of locks
131162306a36Sopenharmony_ci			 * [3] no setting of unsupported bits
131262306a36Sopenharmony_ci			 * [4] doing anything requires privilege (go read about
131362306a36Sopenharmony_ci			 *     the "sendmail capabilities bug")
131462306a36Sopenharmony_ci			 */
131562306a36Sopenharmony_ci		    )
131662306a36Sopenharmony_ci			/* cannot change a locked bit */
131762306a36Sopenharmony_ci			return -EPERM;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		new = prepare_creds();
132062306a36Sopenharmony_ci		if (!new)
132162306a36Sopenharmony_ci			return -ENOMEM;
132262306a36Sopenharmony_ci		new->securebits = arg2;
132362306a36Sopenharmony_ci		return commit_creds(new);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	case PR_GET_SECUREBITS:
132662306a36Sopenharmony_ci		return old->securebits;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	case PR_GET_KEEPCAPS:
132962306a36Sopenharmony_ci		return !!issecure(SECURE_KEEP_CAPS);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	case PR_SET_KEEPCAPS:
133262306a36Sopenharmony_ci		if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
133362306a36Sopenharmony_ci			return -EINVAL;
133462306a36Sopenharmony_ci		if (issecure(SECURE_KEEP_CAPS_LOCKED))
133562306a36Sopenharmony_ci			return -EPERM;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci		new = prepare_creds();
133862306a36Sopenharmony_ci		if (!new)
133962306a36Sopenharmony_ci			return -ENOMEM;
134062306a36Sopenharmony_ci		if (arg2)
134162306a36Sopenharmony_ci			new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
134262306a36Sopenharmony_ci		else
134362306a36Sopenharmony_ci			new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
134462306a36Sopenharmony_ci		return commit_creds(new);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	case PR_CAP_AMBIENT:
134762306a36Sopenharmony_ci		if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) {
134862306a36Sopenharmony_ci			if (arg3 | arg4 | arg5)
134962306a36Sopenharmony_ci				return -EINVAL;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci			new = prepare_creds();
135262306a36Sopenharmony_ci			if (!new)
135362306a36Sopenharmony_ci				return -ENOMEM;
135462306a36Sopenharmony_ci			cap_clear(new->cap_ambient);
135562306a36Sopenharmony_ci			return commit_creds(new);
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		if (((!cap_valid(arg3)) | arg4 | arg5))
135962306a36Sopenharmony_ci			return -EINVAL;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci		if (arg2 == PR_CAP_AMBIENT_IS_SET) {
136262306a36Sopenharmony_ci			return !!cap_raised(current_cred()->cap_ambient, arg3);
136362306a36Sopenharmony_ci		} else if (arg2 != PR_CAP_AMBIENT_RAISE &&
136462306a36Sopenharmony_ci			   arg2 != PR_CAP_AMBIENT_LOWER) {
136562306a36Sopenharmony_ci			return -EINVAL;
136662306a36Sopenharmony_ci		} else {
136762306a36Sopenharmony_ci			if (arg2 == PR_CAP_AMBIENT_RAISE &&
136862306a36Sopenharmony_ci			    (!cap_raised(current_cred()->cap_permitted, arg3) ||
136962306a36Sopenharmony_ci			     !cap_raised(current_cred()->cap_inheritable,
137062306a36Sopenharmony_ci					 arg3) ||
137162306a36Sopenharmony_ci			     issecure(SECURE_NO_CAP_AMBIENT_RAISE)))
137262306a36Sopenharmony_ci				return -EPERM;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci			new = prepare_creds();
137562306a36Sopenharmony_ci			if (!new)
137662306a36Sopenharmony_ci				return -ENOMEM;
137762306a36Sopenharmony_ci			if (arg2 == PR_CAP_AMBIENT_RAISE)
137862306a36Sopenharmony_ci				cap_raise(new->cap_ambient, arg3);
137962306a36Sopenharmony_ci			else
138062306a36Sopenharmony_ci				cap_lower(new->cap_ambient, arg3);
138162306a36Sopenharmony_ci			return commit_creds(new);
138262306a36Sopenharmony_ci		}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	default:
138562306a36Sopenharmony_ci		/* No functionality available - continue with default */
138662306a36Sopenharmony_ci		return -ENOSYS;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci/**
139162306a36Sopenharmony_ci * cap_vm_enough_memory - Determine whether a new virtual mapping is permitted
139262306a36Sopenharmony_ci * @mm: The VM space in which the new mapping is to be made
139362306a36Sopenharmony_ci * @pages: The size of the mapping
139462306a36Sopenharmony_ci *
139562306a36Sopenharmony_ci * Determine whether the allocation of a new virtual mapping by the current
139662306a36Sopenharmony_ci * task is permitted.
139762306a36Sopenharmony_ci *
139862306a36Sopenharmony_ci * Return: 1 if permission is granted, 0 if not.
139962306a36Sopenharmony_ci */
140062306a36Sopenharmony_ciint cap_vm_enough_memory(struct mm_struct *mm, long pages)
140162306a36Sopenharmony_ci{
140262306a36Sopenharmony_ci	int cap_sys_admin = 0;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (cap_capable(current_cred(), &init_user_ns,
140562306a36Sopenharmony_ci				CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0)
140662306a36Sopenharmony_ci		cap_sys_admin = 1;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	return cap_sys_admin;
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci/**
141262306a36Sopenharmony_ci * cap_mmap_addr - check if able to map given addr
141362306a36Sopenharmony_ci * @addr: address attempting to be mapped
141462306a36Sopenharmony_ci *
141562306a36Sopenharmony_ci * If the process is attempting to map memory below dac_mmap_min_addr they need
141662306a36Sopenharmony_ci * CAP_SYS_RAWIO.  The other parameters to this function are unused by the
141762306a36Sopenharmony_ci * capability security module.
141862306a36Sopenharmony_ci *
141962306a36Sopenharmony_ci * Return: 0 if this mapping should be allowed or -EPERM if not.
142062306a36Sopenharmony_ci */
142162306a36Sopenharmony_ciint cap_mmap_addr(unsigned long addr)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	int ret = 0;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (addr < dac_mmap_min_addr) {
142662306a36Sopenharmony_ci		ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO,
142762306a36Sopenharmony_ci				  CAP_OPT_NONE);
142862306a36Sopenharmony_ci		/* set PF_SUPERPRIV if it turns out we allow the low mmap */
142962306a36Sopenharmony_ci		if (ret == 0)
143062306a36Sopenharmony_ci			current->flags |= PF_SUPERPRIV;
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci	return ret;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ciint cap_mmap_file(struct file *file, unsigned long reqprot,
143662306a36Sopenharmony_ci		  unsigned long prot, unsigned long flags)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	return 0;
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci#ifdef CONFIG_SECURITY
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic struct security_hook_list capability_hooks[] __ro_after_init = {
144462306a36Sopenharmony_ci	LSM_HOOK_INIT(capable, cap_capable),
144562306a36Sopenharmony_ci	LSM_HOOK_INIT(settime, cap_settime),
144662306a36Sopenharmony_ci	LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
144762306a36Sopenharmony_ci	LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
144862306a36Sopenharmony_ci	LSM_HOOK_INIT(capget, cap_capget),
144962306a36Sopenharmony_ci	LSM_HOOK_INIT(capset, cap_capset),
145062306a36Sopenharmony_ci	LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file),
145162306a36Sopenharmony_ci	LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
145262306a36Sopenharmony_ci	LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
145362306a36Sopenharmony_ci	LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),
145462306a36Sopenharmony_ci	LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
145562306a36Sopenharmony_ci	LSM_HOOK_INIT(mmap_file, cap_mmap_file),
145662306a36Sopenharmony_ci	LSM_HOOK_INIT(task_fix_setuid, cap_task_fix_setuid),
145762306a36Sopenharmony_ci	LSM_HOOK_INIT(task_prctl, cap_task_prctl),
145862306a36Sopenharmony_ci	LSM_HOOK_INIT(task_setscheduler, cap_task_setscheduler),
145962306a36Sopenharmony_ci	LSM_HOOK_INIT(task_setioprio, cap_task_setioprio),
146062306a36Sopenharmony_ci	LSM_HOOK_INIT(task_setnice, cap_task_setnice),
146162306a36Sopenharmony_ci	LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
146262306a36Sopenharmony_ci};
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic int __init capability_init(void)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
146762306a36Sopenharmony_ci				"capability");
146862306a36Sopenharmony_ci	return 0;
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ciDEFINE_LSM(capability) = {
147262306a36Sopenharmony_ci	.name = "capability",
147362306a36Sopenharmony_ci	.order = LSM_ORDER_FIRST,
147462306a36Sopenharmony_ci	.init = capability_init,
147562306a36Sopenharmony_ci};
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci#endif /* CONFIG_SECURITY */
1478