18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Common capabilities, needed by capability.o. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/capability.h> 68c2ecf20Sopenharmony_ci#include <linux/audit.h> 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/lsm_hooks.h> 108c2ecf20Sopenharmony_ci#include <linux/file.h> 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/mman.h> 138c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 148c2ecf20Sopenharmony_ci#include <linux/swap.h> 158c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 168c2ecf20Sopenharmony_ci#include <linux/netlink.h> 178c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 188c2ecf20Sopenharmony_ci#include <linux/xattr.h> 198c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 208c2ecf20Sopenharmony_ci#include <linux/mount.h> 218c2ecf20Sopenharmony_ci#include <linux/sched.h> 228c2ecf20Sopenharmony_ci#include <linux/prctl.h> 238c2ecf20Sopenharmony_ci#include <linux/securebits.h> 248c2ecf20Sopenharmony_ci#include <linux/user_namespace.h> 258c2ecf20Sopenharmony_ci#include <linux/binfmts.h> 268c2ecf20Sopenharmony_ci#include <linux/personality.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * If a non-root user executes a setuid-root binary in 308c2ecf20Sopenharmony_ci * !secure(SECURE_NOROOT) mode, then we raise capabilities. 318c2ecf20Sopenharmony_ci * However if fE is also set, then the intent is for only 328c2ecf20Sopenharmony_ci * the file capabilities to be applied, and the setuid-root 338c2ecf20Sopenharmony_ci * bit is left on either to change the uid (plausible) or 348c2ecf20Sopenharmony_ci * to get full privilege on a kernel without file capabilities 358c2ecf20Sopenharmony_ci * support. So in that case we do not raise capabilities. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Warn if that happens, once per boot. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic void warn_setuid_and_fcaps_mixed(const char *fname) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci static int warned; 428c2ecf20Sopenharmony_ci if (!warned) { 438c2ecf20Sopenharmony_ci printk(KERN_INFO "warning: `%s' has both setuid-root and" 448c2ecf20Sopenharmony_ci " effective capabilities. Therefore not raising all" 458c2ecf20Sopenharmony_ci " capabilities.\n", fname); 468c2ecf20Sopenharmony_ci warned = 1; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/** 518c2ecf20Sopenharmony_ci * cap_capable - Determine whether a task has a particular effective capability 528c2ecf20Sopenharmony_ci * @cred: The credentials to use 538c2ecf20Sopenharmony_ci * @ns: The user namespace in which we need the capability 548c2ecf20Sopenharmony_ci * @cap: The capability to check for 558c2ecf20Sopenharmony_ci * @opts: Bitmask of options defined in include/linux/security.h 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Determine whether the nominated task has the specified capability amongst 588c2ecf20Sopenharmony_ci * its effective set, returning 0 if it does, -ve if it does not. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable() 618c2ecf20Sopenharmony_ci * and has_capability() functions. That is, it has the reverse semantics: 628c2ecf20Sopenharmony_ci * cap_has_capability() returns 0 when a task has a capability, but the 638c2ecf20Sopenharmony_ci * kernel's capable() and has_capability() returns 1 for this case. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ciint cap_capable(const struct cred *cred, struct user_namespace *targ_ns, 668c2ecf20Sopenharmony_ci int cap, unsigned int opts) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct user_namespace *ns = targ_ns; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* See if cred has the capability in the target user namespace 718c2ecf20Sopenharmony_ci * by examining the target user namespace and all of the target 728c2ecf20Sopenharmony_ci * user namespace's parents. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci for (;;) { 758c2ecf20Sopenharmony_ci /* Do we have the necessary capabilities? */ 768c2ecf20Sopenharmony_ci if (ns == cred->user_ns) 778c2ecf20Sopenharmony_ci return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * If we're already at a lower level than we're looking for, 818c2ecf20Sopenharmony_ci * we're done searching. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci if (ns->level <= cred->user_ns->level) 848c2ecf20Sopenharmony_ci return -EPERM; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * The owner of the user namespace in the parent of the 888c2ecf20Sopenharmony_ci * user namespace has all caps. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid)) 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * If you have a capability in a parent user ns, then you have 958c2ecf20Sopenharmony_ci * it over all children user namespaces as well. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci ns = ns->parent; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* We never get here */ 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * cap_settime - Determine whether the current process may set the system clock 1058c2ecf20Sopenharmony_ci * @ts: The time to set 1068c2ecf20Sopenharmony_ci * @tz: The timezone to set 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * Determine whether the current process may set the system clock and timezone 1098c2ecf20Sopenharmony_ci * information, returning 0 if permission granted, -ve if denied. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ciint cap_settime(const struct timespec64 *ts, const struct timezone *tz) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_TIME)) 1148c2ecf20Sopenharmony_ci return -EPERM; 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/** 1198c2ecf20Sopenharmony_ci * cap_ptrace_access_check - Determine whether the current process may access 1208c2ecf20Sopenharmony_ci * another 1218c2ecf20Sopenharmony_ci * @child: The process to be accessed 1228c2ecf20Sopenharmony_ci * @mode: The mode of attachment. 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * If we are in the same or an ancestor user_ns and have all the target 1258c2ecf20Sopenharmony_ci * task's capabilities, then ptrace access is allowed. 1268c2ecf20Sopenharmony_ci * If we have the ptrace capability to the target user_ns, then ptrace 1278c2ecf20Sopenharmony_ci * access is allowed. 1288c2ecf20Sopenharmony_ci * Else denied. 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Determine whether a process may access another, returning 0 if permission 1318c2ecf20Sopenharmony_ci * granted, -ve if denied. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ciint cap_ptrace_access_check(struct task_struct *child, unsigned int mode) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int ret = 0; 1368c2ecf20Sopenharmony_ci const struct cred *cred, *child_cred; 1378c2ecf20Sopenharmony_ci const kernel_cap_t *caller_caps; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci rcu_read_lock(); 1408c2ecf20Sopenharmony_ci cred = current_cred(); 1418c2ecf20Sopenharmony_ci child_cred = __task_cred(child); 1428c2ecf20Sopenharmony_ci if (mode & PTRACE_MODE_FSCREDS) 1438c2ecf20Sopenharmony_ci caller_caps = &cred->cap_effective; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci caller_caps = &cred->cap_permitted; 1468c2ecf20Sopenharmony_ci if (cred->user_ns == child_cred->user_ns && 1478c2ecf20Sopenharmony_ci cap_issubset(child_cred->cap_permitted, *caller_caps)) 1488c2ecf20Sopenharmony_ci goto out; 1498c2ecf20Sopenharmony_ci if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) 1508c2ecf20Sopenharmony_ci goto out; 1518c2ecf20Sopenharmony_ci ret = -EPERM; 1528c2ecf20Sopenharmony_ciout: 1538c2ecf20Sopenharmony_ci rcu_read_unlock(); 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/** 1588c2ecf20Sopenharmony_ci * cap_ptrace_traceme - Determine whether another process may trace the current 1598c2ecf20Sopenharmony_ci * @parent: The task proposed to be the tracer 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * If parent is in the same or an ancestor user_ns and has all current's 1628c2ecf20Sopenharmony_ci * capabilities, then ptrace access is allowed. 1638c2ecf20Sopenharmony_ci * If parent has the ptrace capability to current's user_ns, then ptrace 1648c2ecf20Sopenharmony_ci * access is allowed. 1658c2ecf20Sopenharmony_ci * Else denied. 1668c2ecf20Sopenharmony_ci * 1678c2ecf20Sopenharmony_ci * Determine whether the nominated task is permitted to trace the current 1688c2ecf20Sopenharmony_ci * process, returning 0 if permission is granted, -ve if denied. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ciint cap_ptrace_traceme(struct task_struct *parent) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int ret = 0; 1738c2ecf20Sopenharmony_ci const struct cred *cred, *child_cred; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci rcu_read_lock(); 1768c2ecf20Sopenharmony_ci cred = __task_cred(parent); 1778c2ecf20Sopenharmony_ci child_cred = current_cred(); 1788c2ecf20Sopenharmony_ci if (cred->user_ns == child_cred->user_ns && 1798c2ecf20Sopenharmony_ci cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) 1808c2ecf20Sopenharmony_ci goto out; 1818c2ecf20Sopenharmony_ci if (has_ns_capability(parent, child_cred->user_ns, CAP_SYS_PTRACE)) 1828c2ecf20Sopenharmony_ci goto out; 1838c2ecf20Sopenharmony_ci ret = -EPERM; 1848c2ecf20Sopenharmony_ciout: 1858c2ecf20Sopenharmony_ci rcu_read_unlock(); 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * cap_capget - Retrieve a task's capability sets 1918c2ecf20Sopenharmony_ci * @target: The task from which to retrieve the capability sets 1928c2ecf20Sopenharmony_ci * @effective: The place to record the effective set 1938c2ecf20Sopenharmony_ci * @inheritable: The place to record the inheritable set 1948c2ecf20Sopenharmony_ci * @permitted: The place to record the permitted set 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * This function retrieves the capabilities of the nominated task and returns 1978c2ecf20Sopenharmony_ci * them to the caller. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ciint cap_capget(struct task_struct *target, kernel_cap_t *effective, 2008c2ecf20Sopenharmony_ci kernel_cap_t *inheritable, kernel_cap_t *permitted) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci const struct cred *cred; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Derived from kernel/capability.c:sys_capget. */ 2058c2ecf20Sopenharmony_ci rcu_read_lock(); 2068c2ecf20Sopenharmony_ci cred = __task_cred(target); 2078c2ecf20Sopenharmony_ci *effective = cred->cap_effective; 2088c2ecf20Sopenharmony_ci *inheritable = cred->cap_inheritable; 2098c2ecf20Sopenharmony_ci *permitted = cred->cap_permitted; 2108c2ecf20Sopenharmony_ci rcu_read_unlock(); 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * Determine whether the inheritable capabilities are limited to the old 2168c2ecf20Sopenharmony_ci * permitted set. Returns 1 if they are limited, 0 if they are not. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic inline int cap_inh_is_capped(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci /* they are so limited unless the current task has the CAP_SETPCAP 2218c2ecf20Sopenharmony_ci * capability 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (cap_capable(current_cred(), current_cred()->user_ns, 2248c2ecf20Sopenharmony_ci CAP_SETPCAP, CAP_OPT_NONE) == 0) 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci return 1; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/** 2308c2ecf20Sopenharmony_ci * cap_capset - Validate and apply proposed changes to current's capabilities 2318c2ecf20Sopenharmony_ci * @new: The proposed new credentials; alterations should be made here 2328c2ecf20Sopenharmony_ci * @old: The current task's current credentials 2338c2ecf20Sopenharmony_ci * @effective: A pointer to the proposed new effective capabilities set 2348c2ecf20Sopenharmony_ci * @inheritable: A pointer to the proposed new inheritable capabilities set 2358c2ecf20Sopenharmony_ci * @permitted: A pointer to the proposed new permitted capabilities set 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * This function validates and applies a proposed mass change to the current 2388c2ecf20Sopenharmony_ci * process's capability sets. The changes are made to the proposed new 2398c2ecf20Sopenharmony_ci * credentials, and assuming no error, will be committed by the caller of LSM. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ciint cap_capset(struct cred *new, 2428c2ecf20Sopenharmony_ci const struct cred *old, 2438c2ecf20Sopenharmony_ci const kernel_cap_t *effective, 2448c2ecf20Sopenharmony_ci const kernel_cap_t *inheritable, 2458c2ecf20Sopenharmony_ci const kernel_cap_t *permitted) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (cap_inh_is_capped() && 2488c2ecf20Sopenharmony_ci !cap_issubset(*inheritable, 2498c2ecf20Sopenharmony_ci cap_combine(old->cap_inheritable, 2508c2ecf20Sopenharmony_ci old->cap_permitted))) 2518c2ecf20Sopenharmony_ci /* incapable of using this inheritable set */ 2528c2ecf20Sopenharmony_ci return -EPERM; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!cap_issubset(*inheritable, 2558c2ecf20Sopenharmony_ci cap_combine(old->cap_inheritable, 2568c2ecf20Sopenharmony_ci old->cap_bset))) 2578c2ecf20Sopenharmony_ci /* no new pI capabilities outside bounding set */ 2588c2ecf20Sopenharmony_ci return -EPERM; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* verify restrictions on target's new Permitted set */ 2618c2ecf20Sopenharmony_ci if (!cap_issubset(*permitted, old->cap_permitted)) 2628c2ecf20Sopenharmony_ci return -EPERM; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ 2658c2ecf20Sopenharmony_ci if (!cap_issubset(*effective, *permitted)) 2668c2ecf20Sopenharmony_ci return -EPERM; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci new->cap_effective = *effective; 2698c2ecf20Sopenharmony_ci new->cap_inheritable = *inheritable; 2708c2ecf20Sopenharmony_ci new->cap_permitted = *permitted; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* 2738c2ecf20Sopenharmony_ci * Mask off ambient bits that are no longer both permitted and 2748c2ecf20Sopenharmony_ci * inheritable. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci new->cap_ambient = cap_intersect(new->cap_ambient, 2778c2ecf20Sopenharmony_ci cap_intersect(*permitted, 2788c2ecf20Sopenharmony_ci *inheritable)); 2798c2ecf20Sopenharmony_ci if (WARN_ON(!cap_ambient_invariant_ok(new))) 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/** 2858c2ecf20Sopenharmony_ci * cap_inode_need_killpriv - Determine if inode change affects privileges 2868c2ecf20Sopenharmony_ci * @dentry: The inode/dentry in being changed with change marked ATTR_KILL_PRIV 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV 2898c2ecf20Sopenharmony_ci * affects the security markings on that inode, and if it is, should 2908c2ecf20Sopenharmony_ci * inode_killpriv() be invoked or the change rejected. 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * Returns 1 if security.capability has a value, meaning inode_killpriv() 2938c2ecf20Sopenharmony_ci * is required, 0 otherwise, meaning inode_killpriv() is not required. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ciint cap_inode_need_killpriv(struct dentry *dentry) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 2988c2ecf20Sopenharmony_ci int error; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci error = __vfs_getxattr(dentry, inode, XATTR_NAME_CAPS, NULL, 0); 3018c2ecf20Sopenharmony_ci return error > 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * cap_inode_killpriv - Erase the security markings on an inode 3068c2ecf20Sopenharmony_ci * @dentry: The inode/dentry to alter 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci * Erase the privilege-enhancing security markings on an inode. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * Returns 0 if successful, -ve on error. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ciint cap_inode_killpriv(struct dentry *dentry) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int error; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci error = __vfs_removexattr(dentry, XATTR_NAME_CAPS); 3178c2ecf20Sopenharmony_ci if (error == -EOPNOTSUPP) 3188c2ecf20Sopenharmony_ci error = 0; 3198c2ecf20Sopenharmony_ci return error; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic bool rootid_owns_currentns(kuid_t kroot) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct user_namespace *ns; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!uid_valid(kroot)) 3278c2ecf20Sopenharmony_ci return false; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci for (ns = current_user_ns(); ; ns = ns->parent) { 3308c2ecf20Sopenharmony_ci if (from_kuid(ns, kroot) == 0) 3318c2ecf20Sopenharmony_ci return true; 3328c2ecf20Sopenharmony_ci if (ns == &init_user_ns) 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return false; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic __u32 sansflags(__u32 m) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci return m & ~VFS_CAP_FLAGS_EFFECTIVE; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic bool is_v2header(size_t size, const struct vfs_cap_data *cap) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci if (size != XATTR_CAPS_SZ_2) 3478c2ecf20Sopenharmony_ci return false; 3488c2ecf20Sopenharmony_ci return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_2; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic bool is_v3header(size_t size, const struct vfs_cap_data *cap) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci if (size != XATTR_CAPS_SZ_3) 3548c2ecf20Sopenharmony_ci return false; 3558c2ecf20Sopenharmony_ci return sansflags(le32_to_cpu(cap->magic_etc)) == VFS_CAP_REVISION_3; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* 3598c2ecf20Sopenharmony_ci * getsecurity: We are called for security.* before any attempt to read the 3608c2ecf20Sopenharmony_ci * xattr from the inode itself. 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * This gives us a chance to read the on-disk value and convert it. If we 3638c2ecf20Sopenharmony_ci * return -EOPNOTSUPP, then vfs_getxattr() will call the i_op handler. 3648c2ecf20Sopenharmony_ci * 3658c2ecf20Sopenharmony_ci * Note we are not called by vfs_getxattr_alloc(), but that is only called 3668c2ecf20Sopenharmony_ci * by the integrity subsystem, which really wants the unconverted values - 3678c2ecf20Sopenharmony_ci * so that's good. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ciint cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer, 3708c2ecf20Sopenharmony_ci bool alloc) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int size, ret; 3738c2ecf20Sopenharmony_ci kuid_t kroot; 3748c2ecf20Sopenharmony_ci u32 nsmagic, magic; 3758c2ecf20Sopenharmony_ci uid_t root, mappedroot; 3768c2ecf20Sopenharmony_ci char *tmpbuf = NULL; 3778c2ecf20Sopenharmony_ci struct vfs_cap_data *cap; 3788c2ecf20Sopenharmony_ci struct vfs_ns_cap_data *nscap = NULL; 3798c2ecf20Sopenharmony_ci struct dentry *dentry; 3808c2ecf20Sopenharmony_ci struct user_namespace *fs_ns; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (strcmp(name, "capability") != 0) 3838c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci dentry = d_find_any_alias(inode); 3868c2ecf20Sopenharmony_ci if (!dentry) 3878c2ecf20Sopenharmony_ci return -EINVAL; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci size = sizeof(struct vfs_ns_cap_data); 3908c2ecf20Sopenharmony_ci ret = (int) vfs_getxattr_alloc(dentry, XATTR_NAME_CAPS, 3918c2ecf20Sopenharmony_ci &tmpbuf, size, GFP_NOFS); 3928c2ecf20Sopenharmony_ci dput(dentry); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (ret < 0 || !tmpbuf) { 3958c2ecf20Sopenharmony_ci size = ret; 3968c2ecf20Sopenharmony_ci goto out_free; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci fs_ns = inode->i_sb->s_user_ns; 4008c2ecf20Sopenharmony_ci cap = (struct vfs_cap_data *) tmpbuf; 4018c2ecf20Sopenharmony_ci if (is_v2header((size_t) ret, cap)) { 4028c2ecf20Sopenharmony_ci root = 0; 4038c2ecf20Sopenharmony_ci } else if (is_v3header((size_t) ret, cap)) { 4048c2ecf20Sopenharmony_ci nscap = (struct vfs_ns_cap_data *) tmpbuf; 4058c2ecf20Sopenharmony_ci root = le32_to_cpu(nscap->rootid); 4068c2ecf20Sopenharmony_ci } else { 4078c2ecf20Sopenharmony_ci size = -EINVAL; 4088c2ecf20Sopenharmony_ci goto out_free; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci kroot = make_kuid(fs_ns, root); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* If the root kuid maps to a valid uid in current ns, then return 4148c2ecf20Sopenharmony_ci * this as a nscap. */ 4158c2ecf20Sopenharmony_ci mappedroot = from_kuid(current_user_ns(), kroot); 4168c2ecf20Sopenharmony_ci if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) { 4178c2ecf20Sopenharmony_ci size = sizeof(struct vfs_ns_cap_data); 4188c2ecf20Sopenharmony_ci if (alloc) { 4198c2ecf20Sopenharmony_ci if (!nscap) { 4208c2ecf20Sopenharmony_ci /* v2 -> v3 conversion */ 4218c2ecf20Sopenharmony_ci nscap = kzalloc(size, GFP_ATOMIC); 4228c2ecf20Sopenharmony_ci if (!nscap) { 4238c2ecf20Sopenharmony_ci size = -ENOMEM; 4248c2ecf20Sopenharmony_ci goto out_free; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci nsmagic = VFS_CAP_REVISION_3; 4278c2ecf20Sopenharmony_ci magic = le32_to_cpu(cap->magic_etc); 4288c2ecf20Sopenharmony_ci if (magic & VFS_CAP_FLAGS_EFFECTIVE) 4298c2ecf20Sopenharmony_ci nsmagic |= VFS_CAP_FLAGS_EFFECTIVE; 4308c2ecf20Sopenharmony_ci memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32); 4318c2ecf20Sopenharmony_ci nscap->magic_etc = cpu_to_le32(nsmagic); 4328c2ecf20Sopenharmony_ci } else { 4338c2ecf20Sopenharmony_ci /* use allocated v3 buffer */ 4348c2ecf20Sopenharmony_ci tmpbuf = NULL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci nscap->rootid = cpu_to_le32(mappedroot); 4378c2ecf20Sopenharmony_ci *buffer = nscap; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci goto out_free; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (!rootid_owns_currentns(kroot)) { 4438c2ecf20Sopenharmony_ci size = -EOVERFLOW; 4448c2ecf20Sopenharmony_ci goto out_free; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* This comes from a parent namespace. Return as a v2 capability */ 4488c2ecf20Sopenharmony_ci size = sizeof(struct vfs_cap_data); 4498c2ecf20Sopenharmony_ci if (alloc) { 4508c2ecf20Sopenharmony_ci if (nscap) { 4518c2ecf20Sopenharmony_ci /* v3 -> v2 conversion */ 4528c2ecf20Sopenharmony_ci cap = kzalloc(size, GFP_ATOMIC); 4538c2ecf20Sopenharmony_ci if (!cap) { 4548c2ecf20Sopenharmony_ci size = -ENOMEM; 4558c2ecf20Sopenharmony_ci goto out_free; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci magic = VFS_CAP_REVISION_2; 4588c2ecf20Sopenharmony_ci nsmagic = le32_to_cpu(nscap->magic_etc); 4598c2ecf20Sopenharmony_ci if (nsmagic & VFS_CAP_FLAGS_EFFECTIVE) 4608c2ecf20Sopenharmony_ci magic |= VFS_CAP_FLAGS_EFFECTIVE; 4618c2ecf20Sopenharmony_ci memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32); 4628c2ecf20Sopenharmony_ci cap->magic_etc = cpu_to_le32(magic); 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci /* use unconverted v2 */ 4658c2ecf20Sopenharmony_ci tmpbuf = NULL; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci *buffer = cap; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ciout_free: 4708c2ecf20Sopenharmony_ci kfree(tmpbuf); 4718c2ecf20Sopenharmony_ci return size; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic kuid_t rootid_from_xattr(const void *value, size_t size, 4758c2ecf20Sopenharmony_ci struct user_namespace *task_ns) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci const struct vfs_ns_cap_data *nscap = value; 4788c2ecf20Sopenharmony_ci uid_t rootid = 0; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (size == XATTR_CAPS_SZ_3) 4818c2ecf20Sopenharmony_ci rootid = le32_to_cpu(nscap->rootid); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return make_kuid(task_ns, rootid); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic bool validheader(size_t size, const struct vfs_cap_data *cap) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci return is_v2header(size, cap) || is_v3header(size, cap); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci * User requested a write of security.capability. If needed, update the 4938c2ecf20Sopenharmony_ci * xattr to change from v2 to v3, or to fixup the v3 rootid. 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * If all is ok, we return the new size, on error return < 0. 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_ciint cap_convert_nscap(struct dentry *dentry, void **ivalue, size_t size) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct vfs_ns_cap_data *nscap; 5008c2ecf20Sopenharmony_ci uid_t nsrootid; 5018c2ecf20Sopenharmony_ci const struct vfs_cap_data *cap = *ivalue; 5028c2ecf20Sopenharmony_ci __u32 magic, nsmagic; 5038c2ecf20Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 5048c2ecf20Sopenharmony_ci struct user_namespace *task_ns = current_user_ns(), 5058c2ecf20Sopenharmony_ci *fs_ns = inode->i_sb->s_user_ns; 5068c2ecf20Sopenharmony_ci kuid_t rootid; 5078c2ecf20Sopenharmony_ci size_t newsize; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (!*ivalue) 5108c2ecf20Sopenharmony_ci return -EINVAL; 5118c2ecf20Sopenharmony_ci if (!validheader(size, cap)) 5128c2ecf20Sopenharmony_ci return -EINVAL; 5138c2ecf20Sopenharmony_ci if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP)) 5148c2ecf20Sopenharmony_ci return -EPERM; 5158c2ecf20Sopenharmony_ci if (size == XATTR_CAPS_SZ_2) 5168c2ecf20Sopenharmony_ci if (ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP)) 5178c2ecf20Sopenharmony_ci /* user is privileged, just write the v2 */ 5188c2ecf20Sopenharmony_ci return size; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci rootid = rootid_from_xattr(*ivalue, size, task_ns); 5218c2ecf20Sopenharmony_ci if (!uid_valid(rootid)) 5228c2ecf20Sopenharmony_ci return -EINVAL; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci nsrootid = from_kuid(fs_ns, rootid); 5258c2ecf20Sopenharmony_ci if (nsrootid == -1) 5268c2ecf20Sopenharmony_ci return -EINVAL; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci newsize = sizeof(struct vfs_ns_cap_data); 5298c2ecf20Sopenharmony_ci nscap = kmalloc(newsize, GFP_ATOMIC); 5308c2ecf20Sopenharmony_ci if (!nscap) 5318c2ecf20Sopenharmony_ci return -ENOMEM; 5328c2ecf20Sopenharmony_ci nscap->rootid = cpu_to_le32(nsrootid); 5338c2ecf20Sopenharmony_ci nsmagic = VFS_CAP_REVISION_3; 5348c2ecf20Sopenharmony_ci magic = le32_to_cpu(cap->magic_etc); 5358c2ecf20Sopenharmony_ci if (magic & VFS_CAP_FLAGS_EFFECTIVE) 5368c2ecf20Sopenharmony_ci nsmagic |= VFS_CAP_FLAGS_EFFECTIVE; 5378c2ecf20Sopenharmony_ci nscap->magic_etc = cpu_to_le32(nsmagic); 5388c2ecf20Sopenharmony_ci memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci kvfree(*ivalue); 5418c2ecf20Sopenharmony_ci *ivalue = nscap; 5428c2ecf20Sopenharmony_ci return newsize; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/* 5468c2ecf20Sopenharmony_ci * Calculate the new process capability sets from the capability sets attached 5478c2ecf20Sopenharmony_ci * to a file. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cistatic inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, 5508c2ecf20Sopenharmony_ci struct linux_binprm *bprm, 5518c2ecf20Sopenharmony_ci bool *effective, 5528c2ecf20Sopenharmony_ci bool *has_fcap) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct cred *new = bprm->cred; 5558c2ecf20Sopenharmony_ci unsigned i; 5568c2ecf20Sopenharmony_ci int ret = 0; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) 5598c2ecf20Sopenharmony_ci *effective = true; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (caps->magic_etc & VFS_CAP_REVISION_MASK) 5628c2ecf20Sopenharmony_ci *has_fcap = true; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci CAP_FOR_EACH_U32(i) { 5658c2ecf20Sopenharmony_ci __u32 permitted = caps->permitted.cap[i]; 5668c2ecf20Sopenharmony_ci __u32 inheritable = caps->inheritable.cap[i]; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * pP' = (X & fP) | (pI & fI) 5708c2ecf20Sopenharmony_ci * The addition of pA' is handled later. 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_ci new->cap_permitted.cap[i] = 5738c2ecf20Sopenharmony_ci (new->cap_bset.cap[i] & permitted) | 5748c2ecf20Sopenharmony_ci (new->cap_inheritable.cap[i] & inheritable); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (permitted & ~new->cap_permitted.cap[i]) 5778c2ecf20Sopenharmony_ci /* insufficient to execute correctly */ 5788c2ecf20Sopenharmony_ci ret = -EPERM; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * For legacy apps, with no internal support for recognizing they 5838c2ecf20Sopenharmony_ci * do not have enough capabilities, we return an error if they are 5848c2ecf20Sopenharmony_ci * missing some "forced" (aka file-permitted) capabilities. 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci return *effective ? ret : 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci/* 5908c2ecf20Sopenharmony_ci * Extract the on-exec-apply capability sets for an executable file. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ciint get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 5958c2ecf20Sopenharmony_ci __u32 magic_etc; 5968c2ecf20Sopenharmony_ci unsigned tocopy, i; 5978c2ecf20Sopenharmony_ci int size; 5988c2ecf20Sopenharmony_ci struct vfs_ns_cap_data data, *nscaps = &data; 5998c2ecf20Sopenharmony_ci struct vfs_cap_data *caps = (struct vfs_cap_data *) &data; 6008c2ecf20Sopenharmony_ci kuid_t rootkuid; 6018c2ecf20Sopenharmony_ci struct user_namespace *fs_ns; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!inode) 6068c2ecf20Sopenharmony_ci return -ENODATA; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci fs_ns = inode->i_sb->s_user_ns; 6098c2ecf20Sopenharmony_ci size = __vfs_getxattr((struct dentry *)dentry, inode, 6108c2ecf20Sopenharmony_ci XATTR_NAME_CAPS, &data, XATTR_CAPS_SZ); 6118c2ecf20Sopenharmony_ci if (size == -ENODATA || size == -EOPNOTSUPP) 6128c2ecf20Sopenharmony_ci /* no data, that's ok */ 6138c2ecf20Sopenharmony_ci return -ENODATA; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (size < 0) 6168c2ecf20Sopenharmony_ci return size; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (size < sizeof(magic_etc)) 6198c2ecf20Sopenharmony_ci return -EINVAL; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps->magic_etc); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci rootkuid = make_kuid(fs_ns, 0); 6248c2ecf20Sopenharmony_ci switch (magic_etc & VFS_CAP_REVISION_MASK) { 6258c2ecf20Sopenharmony_ci case VFS_CAP_REVISION_1: 6268c2ecf20Sopenharmony_ci if (size != XATTR_CAPS_SZ_1) 6278c2ecf20Sopenharmony_ci return -EINVAL; 6288c2ecf20Sopenharmony_ci tocopy = VFS_CAP_U32_1; 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci case VFS_CAP_REVISION_2: 6318c2ecf20Sopenharmony_ci if (size != XATTR_CAPS_SZ_2) 6328c2ecf20Sopenharmony_ci return -EINVAL; 6338c2ecf20Sopenharmony_ci tocopy = VFS_CAP_U32_2; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case VFS_CAP_REVISION_3: 6368c2ecf20Sopenharmony_ci if (size != XATTR_CAPS_SZ_3) 6378c2ecf20Sopenharmony_ci return -EINVAL; 6388c2ecf20Sopenharmony_ci tocopy = VFS_CAP_U32_3; 6398c2ecf20Sopenharmony_ci rootkuid = make_kuid(fs_ns, le32_to_cpu(nscaps->rootid)); 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci default: 6438c2ecf20Sopenharmony_ci return -EINVAL; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci /* Limit the caps to the mounter of the filesystem 6468c2ecf20Sopenharmony_ci * or the more limited uid specified in the xattr. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci if (!rootid_owns_currentns(rootkuid)) 6498c2ecf20Sopenharmony_ci return -ENODATA; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci CAP_FOR_EACH_U32(i) { 6528c2ecf20Sopenharmony_ci if (i >= tocopy) 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci cpu_caps->permitted.cap[i] = le32_to_cpu(caps->data[i].permitted); 6558c2ecf20Sopenharmony_ci cpu_caps->inheritable.cap[i] = le32_to_cpu(caps->data[i].inheritable); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; 6598c2ecf20Sopenharmony_ci cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci cpu_caps->rootid = rootkuid; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* 6678c2ecf20Sopenharmony_ci * Attempt to get the on-exec apply capability sets for an executable file from 6688c2ecf20Sopenharmony_ci * its xattrs and, if present, apply them to the proposed credentials being 6698c2ecf20Sopenharmony_ci * constructed by execve(). 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_cistatic int get_file_caps(struct linux_binprm *bprm, struct file *file, 6728c2ecf20Sopenharmony_ci bool *effective, bool *has_fcap) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci int rc = 0; 6758c2ecf20Sopenharmony_ci struct cpu_vfs_cap_data vcaps; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci cap_clear(bprm->cred->cap_permitted); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!file_caps_enabled) 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (!mnt_may_suid(file->f_path.mnt)) 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* 6868c2ecf20Sopenharmony_ci * This check is redundant with mnt_may_suid() but is kept to make 6878c2ecf20Sopenharmony_ci * explicit that capability bits are limited to s_user_ns and its 6888c2ecf20Sopenharmony_ci * descendants. 6898c2ecf20Sopenharmony_ci */ 6908c2ecf20Sopenharmony_ci if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns)) 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps); 6948c2ecf20Sopenharmony_ci if (rc < 0) { 6958c2ecf20Sopenharmony_ci if (rc == -EINVAL) 6968c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Invalid argument reading file caps for %s\n", 6978c2ecf20Sopenharmony_ci bprm->filename); 6988c2ecf20Sopenharmony_ci else if (rc == -ENODATA) 6998c2ecf20Sopenharmony_ci rc = 0; 7008c2ecf20Sopenharmony_ci goto out; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_fcap); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ciout: 7068c2ecf20Sopenharmony_ci if (rc) 7078c2ecf20Sopenharmony_ci cap_clear(bprm->cred->cap_permitted); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return rc; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic inline bool root_privileged(void) { return !issecure(SECURE_NOROOT); } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic inline bool __is_real(kuid_t uid, struct cred *cred) 7158c2ecf20Sopenharmony_ci{ return uid_eq(cred->uid, uid); } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic inline bool __is_eff(kuid_t uid, struct cred *cred) 7188c2ecf20Sopenharmony_ci{ return uid_eq(cred->euid, uid); } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic inline bool __is_suid(kuid_t uid, struct cred *cred) 7218c2ecf20Sopenharmony_ci{ return !__is_real(uid, cred) && __is_eff(uid, cred); } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/* 7248c2ecf20Sopenharmony_ci * handle_privileged_root - Handle case of privileged root 7258c2ecf20Sopenharmony_ci * @bprm: The execution parameters, including the proposed creds 7268c2ecf20Sopenharmony_ci * @has_fcap: Are any file capabilities set? 7278c2ecf20Sopenharmony_ci * @effective: Do we have effective root privilege? 7288c2ecf20Sopenharmony_ci * @root_uid: This namespace' root UID WRT initial USER namespace 7298c2ecf20Sopenharmony_ci * 7308c2ecf20Sopenharmony_ci * Handle the case where root is privileged and hasn't been neutered by 7318c2ecf20Sopenharmony_ci * SECURE_NOROOT. If file capabilities are set, they won't be combined with 7328c2ecf20Sopenharmony_ci * set UID root and nothing is changed. If we are root, cap_permitted is 7338c2ecf20Sopenharmony_ci * updated. If we have become set UID root, the effective bit is set. 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_cistatic void handle_privileged_root(struct linux_binprm *bprm, bool has_fcap, 7368c2ecf20Sopenharmony_ci bool *effective, kuid_t root_uid) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci const struct cred *old = current_cred(); 7398c2ecf20Sopenharmony_ci struct cred *new = bprm->cred; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (!root_privileged()) 7428c2ecf20Sopenharmony_ci return; 7438c2ecf20Sopenharmony_ci /* 7448c2ecf20Sopenharmony_ci * If the legacy file capability is set, then don't set privs 7458c2ecf20Sopenharmony_ci * for a setuid root binary run by a non-root user. Do set it 7468c2ecf20Sopenharmony_ci * for a root user just to cause least surprise to an admin. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci if (has_fcap && __is_suid(root_uid, new)) { 7498c2ecf20Sopenharmony_ci warn_setuid_and_fcaps_mixed(bprm->filename); 7508c2ecf20Sopenharmony_ci return; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci /* 7538c2ecf20Sopenharmony_ci * To support inheritance of root-permissions and suid-root 7548c2ecf20Sopenharmony_ci * executables under compatibility mode, we override the 7558c2ecf20Sopenharmony_ci * capability sets for the file. 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_ci if (__is_eff(root_uid, new) || __is_real(root_uid, new)) { 7588c2ecf20Sopenharmony_ci /* pP' = (cap_bset & ~0) | (pI & ~0) */ 7598c2ecf20Sopenharmony_ci new->cap_permitted = cap_combine(old->cap_bset, 7608c2ecf20Sopenharmony_ci old->cap_inheritable); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci /* 7638c2ecf20Sopenharmony_ci * If only the real uid is 0, we do not set the effective bit. 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_ci if (__is_eff(root_uid, new)) 7668c2ecf20Sopenharmony_ci *effective = true; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci#define __cap_gained(field, target, source) \ 7708c2ecf20Sopenharmony_ci !cap_issubset(target->cap_##field, source->cap_##field) 7718c2ecf20Sopenharmony_ci#define __cap_grew(target, source, cred) \ 7728c2ecf20Sopenharmony_ci !cap_issubset(cred->cap_##target, cred->cap_##source) 7738c2ecf20Sopenharmony_ci#define __cap_full(field, cred) \ 7748c2ecf20Sopenharmony_ci cap_issubset(CAP_FULL_SET, cred->cap_##field) 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic inline bool __is_setuid(struct cred *new, const struct cred *old) 7778c2ecf20Sopenharmony_ci{ return !uid_eq(new->euid, old->uid); } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic inline bool __is_setgid(struct cred *new, const struct cred *old) 7808c2ecf20Sopenharmony_ci{ return !gid_eq(new->egid, old->gid); } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/* 7838c2ecf20Sopenharmony_ci * 1) Audit candidate if current->cap_effective is set 7848c2ecf20Sopenharmony_ci * 7858c2ecf20Sopenharmony_ci * We do not bother to audit if 3 things are true: 7868c2ecf20Sopenharmony_ci * 1) cap_effective has all caps 7878c2ecf20Sopenharmony_ci * 2) we became root *OR* are were already root 7888c2ecf20Sopenharmony_ci * 3) root is supposed to have all caps (SECURE_NOROOT) 7898c2ecf20Sopenharmony_ci * Since this is just a normal root execing a process. 7908c2ecf20Sopenharmony_ci * 7918c2ecf20Sopenharmony_ci * Number 1 above might fail if you don't have a full bset, but I think 7928c2ecf20Sopenharmony_ci * that is interesting information to audit. 7938c2ecf20Sopenharmony_ci * 7948c2ecf20Sopenharmony_ci * A number of other conditions require logging: 7958c2ecf20Sopenharmony_ci * 2) something prevented setuid root getting all caps 7968c2ecf20Sopenharmony_ci * 3) non-setuid root gets fcaps 7978c2ecf20Sopenharmony_ci * 4) non-setuid root gets ambient 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_cistatic inline bool nonroot_raised_pE(struct cred *new, const struct cred *old, 8008c2ecf20Sopenharmony_ci kuid_t root, bool has_fcap) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci bool ret = false; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if ((__cap_grew(effective, ambient, new) && 8058c2ecf20Sopenharmony_ci !(__cap_full(effective, new) && 8068c2ecf20Sopenharmony_ci (__is_eff(root, new) || __is_real(root, new)) && 8078c2ecf20Sopenharmony_ci root_privileged())) || 8088c2ecf20Sopenharmony_ci (root_privileged() && 8098c2ecf20Sopenharmony_ci __is_suid(root, new) && 8108c2ecf20Sopenharmony_ci !__cap_full(effective, new)) || 8118c2ecf20Sopenharmony_ci (!__is_setuid(new, old) && 8128c2ecf20Sopenharmony_ci ((has_fcap && 8138c2ecf20Sopenharmony_ci __cap_gained(permitted, new, old)) || 8148c2ecf20Sopenharmony_ci __cap_gained(ambient, new, old)))) 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci ret = true; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return ret; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/** 8228c2ecf20Sopenharmony_ci * cap_bprm_creds_from_file - Set up the proposed credentials for execve(). 8238c2ecf20Sopenharmony_ci * @bprm: The execution parameters, including the proposed creds 8248c2ecf20Sopenharmony_ci * @file: The file to pull the credentials from 8258c2ecf20Sopenharmony_ci * 8268c2ecf20Sopenharmony_ci * Set up the proposed credentials for a new execution context being 8278c2ecf20Sopenharmony_ci * constructed by execve(). The proposed creds in @bprm->cred is altered, 8288c2ecf20Sopenharmony_ci * which won't take effect immediately. Returns 0 if successful, -ve on error. 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_ciint cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci /* Process setpcap binaries and capabilities for uid 0 */ 8338c2ecf20Sopenharmony_ci const struct cred *old = current_cred(); 8348c2ecf20Sopenharmony_ci struct cred *new = bprm->cred; 8358c2ecf20Sopenharmony_ci bool effective = false, has_fcap = false, is_setid; 8368c2ecf20Sopenharmony_ci int ret; 8378c2ecf20Sopenharmony_ci kuid_t root_uid; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (WARN_ON(!cap_ambient_invariant_ok(old))) 8408c2ecf20Sopenharmony_ci return -EPERM; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ret = get_file_caps(bprm, file, &effective, &has_fcap); 8438c2ecf20Sopenharmony_ci if (ret < 0) 8448c2ecf20Sopenharmony_ci return ret; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci root_uid = make_kuid(new->user_ns, 0); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci handle_privileged_root(bprm, has_fcap, &effective, root_uid); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* if we have fs caps, clear dangerous personality flags */ 8518c2ecf20Sopenharmony_ci if (__cap_gained(permitted, new, old)) 8528c2ecf20Sopenharmony_ci bprm->per_clear |= PER_CLEAR_ON_SETID; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* Don't let someone trace a set[ug]id/setpcap binary with the revised 8558c2ecf20Sopenharmony_ci * credentials unless they have the appropriate permit. 8568c2ecf20Sopenharmony_ci * 8578c2ecf20Sopenharmony_ci * In addition, if NO_NEW_PRIVS, then ensure we get no new privs. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_ci is_setid = __is_setuid(new, old) || __is_setgid(new, old); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if ((is_setid || __cap_gained(permitted, new, old)) && 8628c2ecf20Sopenharmony_ci ((bprm->unsafe & ~LSM_UNSAFE_PTRACE) || 8638c2ecf20Sopenharmony_ci !ptracer_capable(current, new->user_ns))) { 8648c2ecf20Sopenharmony_ci /* downgrade; they get no more than they had, and maybe less */ 8658c2ecf20Sopenharmony_ci if (!ns_capable(new->user_ns, CAP_SETUID) || 8668c2ecf20Sopenharmony_ci (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) { 8678c2ecf20Sopenharmony_ci new->euid = new->uid; 8688c2ecf20Sopenharmony_ci new->egid = new->gid; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci new->cap_permitted = cap_intersect(new->cap_permitted, 8718c2ecf20Sopenharmony_ci old->cap_permitted); 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci new->suid = new->fsuid = new->euid; 8758c2ecf20Sopenharmony_ci new->sgid = new->fsgid = new->egid; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* File caps or setid cancels ambient. */ 8788c2ecf20Sopenharmony_ci if (has_fcap || is_setid) 8798c2ecf20Sopenharmony_ci cap_clear(new->cap_ambient); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* 8828c2ecf20Sopenharmony_ci * Now that we've computed pA', update pP' to give: 8838c2ecf20Sopenharmony_ci * pP' = (X & fP) | (pI & fI) | pA' 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci /* 8888c2ecf20Sopenharmony_ci * Set pE' = (fE ? pP' : pA'). Because pA' is zero if fE is set, 8898c2ecf20Sopenharmony_ci * this is the same as pE' = (fE ? pP' : 0) | pA'. 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_ci if (effective) 8928c2ecf20Sopenharmony_ci new->cap_effective = new->cap_permitted; 8938c2ecf20Sopenharmony_ci else 8948c2ecf20Sopenharmony_ci new->cap_effective = new->cap_ambient; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (WARN_ON(!cap_ambient_invariant_ok(new))) 8978c2ecf20Sopenharmony_ci return -EPERM; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (nonroot_raised_pE(new, old, root_uid, has_fcap)) { 9008c2ecf20Sopenharmony_ci ret = audit_log_bprm_fcaps(bprm, new, old); 9018c2ecf20Sopenharmony_ci if (ret < 0) 9028c2ecf20Sopenharmony_ci return ret; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (WARN_ON(!cap_ambient_invariant_ok(new))) 9088c2ecf20Sopenharmony_ci return -EPERM; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Check for privilege-elevated exec. */ 9118c2ecf20Sopenharmony_ci if (is_setid || 9128c2ecf20Sopenharmony_ci (!__is_real(root_uid, new) && 9138c2ecf20Sopenharmony_ci (effective || 9148c2ecf20Sopenharmony_ci __cap_grew(permitted, ambient, new)))) 9158c2ecf20Sopenharmony_ci bprm->secureexec = 1; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return 0; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/** 9218c2ecf20Sopenharmony_ci * cap_inode_setxattr - Determine whether an xattr may be altered 9228c2ecf20Sopenharmony_ci * @dentry: The inode/dentry being altered 9238c2ecf20Sopenharmony_ci * @name: The name of the xattr to be changed 9248c2ecf20Sopenharmony_ci * @value: The value that the xattr will be changed to 9258c2ecf20Sopenharmony_ci * @size: The size of value 9268c2ecf20Sopenharmony_ci * @flags: The replacement flag 9278c2ecf20Sopenharmony_ci * 9288c2ecf20Sopenharmony_ci * Determine whether an xattr may be altered or set on an inode, returning 0 if 9298c2ecf20Sopenharmony_ci * permission is granted, -ve if denied. 9308c2ecf20Sopenharmony_ci * 9318c2ecf20Sopenharmony_ci * This is used to make sure security xattrs don't get updated or set by those 9328c2ecf20Sopenharmony_ci * who aren't privileged to do so. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ciint cap_inode_setxattr(struct dentry *dentry, const char *name, 9358c2ecf20Sopenharmony_ci const void *value, size_t size, int flags) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct user_namespace *user_ns = dentry->d_sb->s_user_ns; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Ignore non-security xattrs */ 9408c2ecf20Sopenharmony_ci if (strncmp(name, XATTR_SECURITY_PREFIX, 9418c2ecf20Sopenharmony_ci XATTR_SECURITY_PREFIX_LEN) != 0) 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* 9458c2ecf20Sopenharmony_ci * For XATTR_NAME_CAPS the check will be done in 9468c2ecf20Sopenharmony_ci * cap_convert_nscap(), called by setxattr() 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_ci if (strcmp(name, XATTR_NAME_CAPS) == 0) 9498c2ecf20Sopenharmony_ci return 0; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (!ns_capable(user_ns, CAP_SYS_ADMIN)) 9528c2ecf20Sopenharmony_ci return -EPERM; 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/** 9578c2ecf20Sopenharmony_ci * cap_inode_removexattr - Determine whether an xattr may be removed 9588c2ecf20Sopenharmony_ci * @dentry: The inode/dentry being altered 9598c2ecf20Sopenharmony_ci * @name: The name of the xattr to be changed 9608c2ecf20Sopenharmony_ci * 9618c2ecf20Sopenharmony_ci * Determine whether an xattr may be removed from an inode, returning 0 if 9628c2ecf20Sopenharmony_ci * permission is granted, -ve if denied. 9638c2ecf20Sopenharmony_ci * 9648c2ecf20Sopenharmony_ci * This is used to make sure security xattrs don't get removed by those who 9658c2ecf20Sopenharmony_ci * aren't privileged to remove them. 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ciint cap_inode_removexattr(struct dentry *dentry, const char *name) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct user_namespace *user_ns = dentry->d_sb->s_user_ns; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* Ignore non-security xattrs */ 9728c2ecf20Sopenharmony_ci if (strncmp(name, XATTR_SECURITY_PREFIX, 9738c2ecf20Sopenharmony_ci XATTR_SECURITY_PREFIX_LEN) != 0) 9748c2ecf20Sopenharmony_ci return 0; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (strcmp(name, XATTR_NAME_CAPS) == 0) { 9778c2ecf20Sopenharmony_ci /* security.capability gets namespaced */ 9788c2ecf20Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 9798c2ecf20Sopenharmony_ci if (!inode) 9808c2ecf20Sopenharmony_ci return -EINVAL; 9818c2ecf20Sopenharmony_ci if (!capable_wrt_inode_uidgid(inode, CAP_SETFCAP)) 9828c2ecf20Sopenharmony_ci return -EPERM; 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci if (!ns_capable(user_ns, CAP_SYS_ADMIN)) 9878c2ecf20Sopenharmony_ci return -EPERM; 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci/* 9928c2ecf20Sopenharmony_ci * cap_emulate_setxuid() fixes the effective / permitted capabilities of 9938c2ecf20Sopenharmony_ci * a process after a call to setuid, setreuid, or setresuid. 9948c2ecf20Sopenharmony_ci * 9958c2ecf20Sopenharmony_ci * 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of 9968c2ecf20Sopenharmony_ci * {r,e,s}uid != 0, the permitted and effective capabilities are 9978c2ecf20Sopenharmony_ci * cleared. 9988c2ecf20Sopenharmony_ci * 9998c2ecf20Sopenharmony_ci * 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective 10008c2ecf20Sopenharmony_ci * capabilities of the process are cleared. 10018c2ecf20Sopenharmony_ci * 10028c2ecf20Sopenharmony_ci * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective 10038c2ecf20Sopenharmony_ci * capabilities are set to the permitted capabilities. 10048c2ecf20Sopenharmony_ci * 10058c2ecf20Sopenharmony_ci * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should 10068c2ecf20Sopenharmony_ci * never happen. 10078c2ecf20Sopenharmony_ci * 10088c2ecf20Sopenharmony_ci * -astor 10098c2ecf20Sopenharmony_ci * 10108c2ecf20Sopenharmony_ci * cevans - New behaviour, Oct '99 10118c2ecf20Sopenharmony_ci * A process may, via prctl(), elect to keep its capabilities when it 10128c2ecf20Sopenharmony_ci * calls setuid() and switches away from uid==0. Both permitted and 10138c2ecf20Sopenharmony_ci * effective sets will be retained. 10148c2ecf20Sopenharmony_ci * Without this change, it was impossible for a daemon to drop only some 10158c2ecf20Sopenharmony_ci * of its privilege. The call to setuid(!=0) would drop all privileges! 10168c2ecf20Sopenharmony_ci * Keeping uid 0 is not an option because uid 0 owns too many vital 10178c2ecf20Sopenharmony_ci * files.. 10188c2ecf20Sopenharmony_ci * Thanks to Olaf Kirch and Peter Benie for spotting this. 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_cistatic inline void cap_emulate_setxuid(struct cred *new, const struct cred *old) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci kuid_t root_uid = make_kuid(old->user_ns, 0); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if ((uid_eq(old->uid, root_uid) || 10258c2ecf20Sopenharmony_ci uid_eq(old->euid, root_uid) || 10268c2ecf20Sopenharmony_ci uid_eq(old->suid, root_uid)) && 10278c2ecf20Sopenharmony_ci (!uid_eq(new->uid, root_uid) && 10288c2ecf20Sopenharmony_ci !uid_eq(new->euid, root_uid) && 10298c2ecf20Sopenharmony_ci !uid_eq(new->suid, root_uid))) { 10308c2ecf20Sopenharmony_ci if (!issecure(SECURE_KEEP_CAPS)) { 10318c2ecf20Sopenharmony_ci cap_clear(new->cap_permitted); 10328c2ecf20Sopenharmony_ci cap_clear(new->cap_effective); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* 10368c2ecf20Sopenharmony_ci * Pre-ambient programs expect setresuid to nonroot followed 10378c2ecf20Sopenharmony_ci * by exec to drop capabilities. We should make sure that 10388c2ecf20Sopenharmony_ci * this remains the case. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ci cap_clear(new->cap_ambient); 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid)) 10438c2ecf20Sopenharmony_ci cap_clear(new->cap_effective); 10448c2ecf20Sopenharmony_ci if (!uid_eq(old->euid, root_uid) && uid_eq(new->euid, root_uid)) 10458c2ecf20Sopenharmony_ci new->cap_effective = new->cap_permitted; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci/** 10498c2ecf20Sopenharmony_ci * cap_task_fix_setuid - Fix up the results of setuid() call 10508c2ecf20Sopenharmony_ci * @new: The proposed credentials 10518c2ecf20Sopenharmony_ci * @old: The current task's current credentials 10528c2ecf20Sopenharmony_ci * @flags: Indications of what has changed 10538c2ecf20Sopenharmony_ci * 10548c2ecf20Sopenharmony_ci * Fix up the results of setuid() call before the credential changes are 10558c2ecf20Sopenharmony_ci * actually applied, returning 0 to grant the changes, -ve to deny them. 10568c2ecf20Sopenharmony_ci */ 10578c2ecf20Sopenharmony_ciint cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci switch (flags) { 10608c2ecf20Sopenharmony_ci case LSM_SETID_RE: 10618c2ecf20Sopenharmony_ci case LSM_SETID_ID: 10628c2ecf20Sopenharmony_ci case LSM_SETID_RES: 10638c2ecf20Sopenharmony_ci /* juggle the capabilities to follow [RES]UID changes unless 10648c2ecf20Sopenharmony_ci * otherwise suppressed */ 10658c2ecf20Sopenharmony_ci if (!issecure(SECURE_NO_SETUID_FIXUP)) 10668c2ecf20Sopenharmony_ci cap_emulate_setxuid(new, old); 10678c2ecf20Sopenharmony_ci break; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci case LSM_SETID_FS: 10708c2ecf20Sopenharmony_ci /* juggle the capabilties to follow FSUID changes, unless 10718c2ecf20Sopenharmony_ci * otherwise suppressed 10728c2ecf20Sopenharmony_ci * 10738c2ecf20Sopenharmony_ci * FIXME - is fsuser used for all CAP_FS_MASK capabilities? 10748c2ecf20Sopenharmony_ci * if not, we might be a bit too harsh here. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_ci if (!issecure(SECURE_NO_SETUID_FIXUP)) { 10778c2ecf20Sopenharmony_ci kuid_t root_uid = make_kuid(old->user_ns, 0); 10788c2ecf20Sopenharmony_ci if (uid_eq(old->fsuid, root_uid) && !uid_eq(new->fsuid, root_uid)) 10798c2ecf20Sopenharmony_ci new->cap_effective = 10808c2ecf20Sopenharmony_ci cap_drop_fs_set(new->cap_effective); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (!uid_eq(old->fsuid, root_uid) && uid_eq(new->fsuid, root_uid)) 10838c2ecf20Sopenharmony_ci new->cap_effective = 10848c2ecf20Sopenharmony_ci cap_raise_fs_set(new->cap_effective, 10858c2ecf20Sopenharmony_ci new->cap_permitted); 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci default: 10908c2ecf20Sopenharmony_ci return -EINVAL; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci return 0; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci/* 10978c2ecf20Sopenharmony_ci * Rationale: code calling task_setscheduler, task_setioprio, and 10988c2ecf20Sopenharmony_ci * task_setnice, assumes that 10998c2ecf20Sopenharmony_ci * . if capable(cap_sys_nice), then those actions should be allowed 11008c2ecf20Sopenharmony_ci * . if not capable(cap_sys_nice), but acting on your own processes, 11018c2ecf20Sopenharmony_ci * then those actions should be allowed 11028c2ecf20Sopenharmony_ci * This is insufficient now since you can call code without suid, but 11038c2ecf20Sopenharmony_ci * yet with increased caps. 11048c2ecf20Sopenharmony_ci * So we check for increased caps on the target process. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_cistatic int cap_safe_nice(struct task_struct *p) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci int is_subset, ret = 0; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci rcu_read_lock(); 11118c2ecf20Sopenharmony_ci is_subset = cap_issubset(__task_cred(p)->cap_permitted, 11128c2ecf20Sopenharmony_ci current_cred()->cap_permitted); 11138c2ecf20Sopenharmony_ci if (!is_subset && !ns_capable(__task_cred(p)->user_ns, CAP_SYS_NICE)) 11148c2ecf20Sopenharmony_ci ret = -EPERM; 11158c2ecf20Sopenharmony_ci rcu_read_unlock(); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci return ret; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci/** 11218c2ecf20Sopenharmony_ci * cap_task_setscheduler - Detemine if scheduler policy change is permitted 11228c2ecf20Sopenharmony_ci * @p: The task to affect 11238c2ecf20Sopenharmony_ci * 11248c2ecf20Sopenharmony_ci * Detemine if the requested scheduler policy change is permitted for the 11258c2ecf20Sopenharmony_ci * specified task, returning 0 if permission is granted, -ve if denied. 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_ciint cap_task_setscheduler(struct task_struct *p) 11288c2ecf20Sopenharmony_ci{ 11298c2ecf20Sopenharmony_ci return cap_safe_nice(p); 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/** 11338c2ecf20Sopenharmony_ci * cap_task_ioprio - Detemine if I/O priority change is permitted 11348c2ecf20Sopenharmony_ci * @p: The task to affect 11358c2ecf20Sopenharmony_ci * @ioprio: The I/O priority to set 11368c2ecf20Sopenharmony_ci * 11378c2ecf20Sopenharmony_ci * Detemine if the requested I/O priority change is permitted for the specified 11388c2ecf20Sopenharmony_ci * task, returning 0 if permission is granted, -ve if denied. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ciint cap_task_setioprio(struct task_struct *p, int ioprio) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci return cap_safe_nice(p); 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/** 11468c2ecf20Sopenharmony_ci * cap_task_ioprio - Detemine if task priority change is permitted 11478c2ecf20Sopenharmony_ci * @p: The task to affect 11488c2ecf20Sopenharmony_ci * @nice: The nice value to set 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * Detemine if the requested task priority change is permitted for the 11518c2ecf20Sopenharmony_ci * specified task, returning 0 if permission is granted, -ve if denied. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ciint cap_task_setnice(struct task_struct *p, int nice) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci return cap_safe_nice(p); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/* 11598c2ecf20Sopenharmony_ci * Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from 11608c2ecf20Sopenharmony_ci * the current task's bounding set. Returns 0 on success, -ve on error. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_cistatic int cap_prctl_drop(unsigned long cap) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct cred *new; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (!ns_capable(current_user_ns(), CAP_SETPCAP)) 11678c2ecf20Sopenharmony_ci return -EPERM; 11688c2ecf20Sopenharmony_ci if (!cap_valid(cap)) 11698c2ecf20Sopenharmony_ci return -EINVAL; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci new = prepare_creds(); 11728c2ecf20Sopenharmony_ci if (!new) 11738c2ecf20Sopenharmony_ci return -ENOMEM; 11748c2ecf20Sopenharmony_ci cap_lower(new->cap_bset, cap); 11758c2ecf20Sopenharmony_ci return commit_creds(new); 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci/** 11798c2ecf20Sopenharmony_ci * cap_task_prctl - Implement process control functions for this security module 11808c2ecf20Sopenharmony_ci * @option: The process control function requested 11818c2ecf20Sopenharmony_ci * @arg2, @arg3, @arg4, @arg5: The argument data for this function 11828c2ecf20Sopenharmony_ci * 11838c2ecf20Sopenharmony_ci * Allow process control functions (sys_prctl()) to alter capabilities; may 11848c2ecf20Sopenharmony_ci * also deny access to other functions not otherwise implemented here. 11858c2ecf20Sopenharmony_ci * 11868c2ecf20Sopenharmony_ci * Returns 0 or +ve on success, -ENOSYS if this function is not implemented 11878c2ecf20Sopenharmony_ci * here, other -ve on error. If -ENOSYS is returned, sys_prctl() and other LSM 11888c2ecf20Sopenharmony_ci * modules will consider performing the function. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ciint cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, 11918c2ecf20Sopenharmony_ci unsigned long arg4, unsigned long arg5) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci const struct cred *old = current_cred(); 11948c2ecf20Sopenharmony_ci struct cred *new; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci switch (option) { 11978c2ecf20Sopenharmony_ci case PR_CAPBSET_READ: 11988c2ecf20Sopenharmony_ci if (!cap_valid(arg2)) 11998c2ecf20Sopenharmony_ci return -EINVAL; 12008c2ecf20Sopenharmony_ci return !!cap_raised(old->cap_bset, arg2); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci case PR_CAPBSET_DROP: 12038c2ecf20Sopenharmony_ci return cap_prctl_drop(arg2); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* 12068c2ecf20Sopenharmony_ci * The next four prctl's remain to assist with transitioning a 12078c2ecf20Sopenharmony_ci * system from legacy UID=0 based privilege (when filesystem 12088c2ecf20Sopenharmony_ci * capabilities are not in use) to a system using filesystem 12098c2ecf20Sopenharmony_ci * capabilities only - as the POSIX.1e draft intended. 12108c2ecf20Sopenharmony_ci * 12118c2ecf20Sopenharmony_ci * Note: 12128c2ecf20Sopenharmony_ci * 12138c2ecf20Sopenharmony_ci * PR_SET_SECUREBITS = 12148c2ecf20Sopenharmony_ci * issecure_mask(SECURE_KEEP_CAPS_LOCKED) 12158c2ecf20Sopenharmony_ci * | issecure_mask(SECURE_NOROOT) 12168c2ecf20Sopenharmony_ci * | issecure_mask(SECURE_NOROOT_LOCKED) 12178c2ecf20Sopenharmony_ci * | issecure_mask(SECURE_NO_SETUID_FIXUP) 12188c2ecf20Sopenharmony_ci * | issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED) 12198c2ecf20Sopenharmony_ci * 12208c2ecf20Sopenharmony_ci * will ensure that the current process and all of its 12218c2ecf20Sopenharmony_ci * children will be locked into a pure 12228c2ecf20Sopenharmony_ci * capability-based-privilege environment. 12238c2ecf20Sopenharmony_ci */ 12248c2ecf20Sopenharmony_ci case PR_SET_SECUREBITS: 12258c2ecf20Sopenharmony_ci if ((((old->securebits & SECURE_ALL_LOCKS) >> 1) 12268c2ecf20Sopenharmony_ci & (old->securebits ^ arg2)) /*[1]*/ 12278c2ecf20Sopenharmony_ci || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ 12288c2ecf20Sopenharmony_ci || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ 12298c2ecf20Sopenharmony_ci || (cap_capable(current_cred(), 12308c2ecf20Sopenharmony_ci current_cred()->user_ns, 12318c2ecf20Sopenharmony_ci CAP_SETPCAP, 12328c2ecf20Sopenharmony_ci CAP_OPT_NONE) != 0) /*[4]*/ 12338c2ecf20Sopenharmony_ci /* 12348c2ecf20Sopenharmony_ci * [1] no changing of bits that are locked 12358c2ecf20Sopenharmony_ci * [2] no unlocking of locks 12368c2ecf20Sopenharmony_ci * [3] no setting of unsupported bits 12378c2ecf20Sopenharmony_ci * [4] doing anything requires privilege (go read about 12388c2ecf20Sopenharmony_ci * the "sendmail capabilities bug") 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci ) 12418c2ecf20Sopenharmony_ci /* cannot change a locked bit */ 12428c2ecf20Sopenharmony_ci return -EPERM; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci new = prepare_creds(); 12458c2ecf20Sopenharmony_ci if (!new) 12468c2ecf20Sopenharmony_ci return -ENOMEM; 12478c2ecf20Sopenharmony_ci new->securebits = arg2; 12488c2ecf20Sopenharmony_ci return commit_creds(new); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci case PR_GET_SECUREBITS: 12518c2ecf20Sopenharmony_ci return old->securebits; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci case PR_GET_KEEPCAPS: 12548c2ecf20Sopenharmony_ci return !!issecure(SECURE_KEEP_CAPS); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci case PR_SET_KEEPCAPS: 12578c2ecf20Sopenharmony_ci if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */ 12588c2ecf20Sopenharmony_ci return -EINVAL; 12598c2ecf20Sopenharmony_ci if (issecure(SECURE_KEEP_CAPS_LOCKED)) 12608c2ecf20Sopenharmony_ci return -EPERM; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci new = prepare_creds(); 12638c2ecf20Sopenharmony_ci if (!new) 12648c2ecf20Sopenharmony_ci return -ENOMEM; 12658c2ecf20Sopenharmony_ci if (arg2) 12668c2ecf20Sopenharmony_ci new->securebits |= issecure_mask(SECURE_KEEP_CAPS); 12678c2ecf20Sopenharmony_ci else 12688c2ecf20Sopenharmony_ci new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); 12698c2ecf20Sopenharmony_ci return commit_creds(new); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci case PR_CAP_AMBIENT: 12728c2ecf20Sopenharmony_ci if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) { 12738c2ecf20Sopenharmony_ci if (arg3 | arg4 | arg5) 12748c2ecf20Sopenharmony_ci return -EINVAL; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci new = prepare_creds(); 12778c2ecf20Sopenharmony_ci if (!new) 12788c2ecf20Sopenharmony_ci return -ENOMEM; 12798c2ecf20Sopenharmony_ci cap_clear(new->cap_ambient); 12808c2ecf20Sopenharmony_ci return commit_creds(new); 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (((!cap_valid(arg3)) | arg4 | arg5)) 12848c2ecf20Sopenharmony_ci return -EINVAL; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (arg2 == PR_CAP_AMBIENT_IS_SET) { 12878c2ecf20Sopenharmony_ci return !!cap_raised(current_cred()->cap_ambient, arg3); 12888c2ecf20Sopenharmony_ci } else if (arg2 != PR_CAP_AMBIENT_RAISE && 12898c2ecf20Sopenharmony_ci arg2 != PR_CAP_AMBIENT_LOWER) { 12908c2ecf20Sopenharmony_ci return -EINVAL; 12918c2ecf20Sopenharmony_ci } else { 12928c2ecf20Sopenharmony_ci if (arg2 == PR_CAP_AMBIENT_RAISE && 12938c2ecf20Sopenharmony_ci (!cap_raised(current_cred()->cap_permitted, arg3) || 12948c2ecf20Sopenharmony_ci !cap_raised(current_cred()->cap_inheritable, 12958c2ecf20Sopenharmony_ci arg3) || 12968c2ecf20Sopenharmony_ci issecure(SECURE_NO_CAP_AMBIENT_RAISE))) 12978c2ecf20Sopenharmony_ci return -EPERM; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci new = prepare_creds(); 13008c2ecf20Sopenharmony_ci if (!new) 13018c2ecf20Sopenharmony_ci return -ENOMEM; 13028c2ecf20Sopenharmony_ci if (arg2 == PR_CAP_AMBIENT_RAISE) 13038c2ecf20Sopenharmony_ci cap_raise(new->cap_ambient, arg3); 13048c2ecf20Sopenharmony_ci else 13058c2ecf20Sopenharmony_ci cap_lower(new->cap_ambient, arg3); 13068c2ecf20Sopenharmony_ci return commit_creds(new); 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci default: 13108c2ecf20Sopenharmony_ci /* No functionality available - continue with default */ 13118c2ecf20Sopenharmony_ci return -ENOSYS; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci/** 13168c2ecf20Sopenharmony_ci * cap_vm_enough_memory - Determine whether a new virtual mapping is permitted 13178c2ecf20Sopenharmony_ci * @mm: The VM space in which the new mapping is to be made 13188c2ecf20Sopenharmony_ci * @pages: The size of the mapping 13198c2ecf20Sopenharmony_ci * 13208c2ecf20Sopenharmony_ci * Determine whether the allocation of a new virtual mapping by the current 13218c2ecf20Sopenharmony_ci * task is permitted, returning 1 if permission is granted, 0 if not. 13228c2ecf20Sopenharmony_ci */ 13238c2ecf20Sopenharmony_ciint cap_vm_enough_memory(struct mm_struct *mm, long pages) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci int cap_sys_admin = 0; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (cap_capable(current_cred(), &init_user_ns, 13288c2ecf20Sopenharmony_ci CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0) 13298c2ecf20Sopenharmony_ci cap_sys_admin = 1; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci return cap_sys_admin; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci/* 13358c2ecf20Sopenharmony_ci * cap_mmap_addr - check if able to map given addr 13368c2ecf20Sopenharmony_ci * @addr: address attempting to be mapped 13378c2ecf20Sopenharmony_ci * 13388c2ecf20Sopenharmony_ci * If the process is attempting to map memory below dac_mmap_min_addr they need 13398c2ecf20Sopenharmony_ci * CAP_SYS_RAWIO. The other parameters to this function are unused by the 13408c2ecf20Sopenharmony_ci * capability security module. Returns 0 if this mapping should be allowed 13418c2ecf20Sopenharmony_ci * -EPERM if not. 13428c2ecf20Sopenharmony_ci */ 13438c2ecf20Sopenharmony_ciint cap_mmap_addr(unsigned long addr) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci int ret = 0; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (addr < dac_mmap_min_addr) { 13488c2ecf20Sopenharmony_ci ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO, 13498c2ecf20Sopenharmony_ci CAP_OPT_NONE); 13508c2ecf20Sopenharmony_ci /* set PF_SUPERPRIV if it turns out we allow the low mmap */ 13518c2ecf20Sopenharmony_ci if (ret == 0) 13528c2ecf20Sopenharmony_ci current->flags |= PF_SUPERPRIV; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci return ret; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ciint cap_mmap_file(struct file *file, unsigned long reqprot, 13588c2ecf20Sopenharmony_ci unsigned long prot, unsigned long flags) 13598c2ecf20Sopenharmony_ci{ 13608c2ecf20Sopenharmony_ci return 0; 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_cistatic struct security_hook_list capability_hooks[] __lsm_ro_after_init = { 13668c2ecf20Sopenharmony_ci LSM_HOOK_INIT(capable, cap_capable), 13678c2ecf20Sopenharmony_ci LSM_HOOK_INIT(settime, cap_settime), 13688c2ecf20Sopenharmony_ci LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check), 13698c2ecf20Sopenharmony_ci LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme), 13708c2ecf20Sopenharmony_ci LSM_HOOK_INIT(capget, cap_capget), 13718c2ecf20Sopenharmony_ci LSM_HOOK_INIT(capset, cap_capset), 13728c2ecf20Sopenharmony_ci LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file), 13738c2ecf20Sopenharmony_ci LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv), 13748c2ecf20Sopenharmony_ci LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv), 13758c2ecf20Sopenharmony_ci LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity), 13768c2ecf20Sopenharmony_ci LSM_HOOK_INIT(mmap_addr, cap_mmap_addr), 13778c2ecf20Sopenharmony_ci LSM_HOOK_INIT(mmap_file, cap_mmap_file), 13788c2ecf20Sopenharmony_ci LSM_HOOK_INIT(task_fix_setuid, cap_task_fix_setuid), 13798c2ecf20Sopenharmony_ci LSM_HOOK_INIT(task_prctl, cap_task_prctl), 13808c2ecf20Sopenharmony_ci LSM_HOOK_INIT(task_setscheduler, cap_task_setscheduler), 13818c2ecf20Sopenharmony_ci LSM_HOOK_INIT(task_setioprio, cap_task_setioprio), 13828c2ecf20Sopenharmony_ci LSM_HOOK_INIT(task_setnice, cap_task_setnice), 13838c2ecf20Sopenharmony_ci LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory), 13848c2ecf20Sopenharmony_ci}; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic int __init capability_init(void) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), 13898c2ecf20Sopenharmony_ci "capability"); 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ciDEFINE_LSM(capability) = { 13948c2ecf20Sopenharmony_ci .name = "capability", 13958c2ecf20Sopenharmony_ci .order = LSM_ORDER_FIRST, 13968c2ecf20Sopenharmony_ci .init = capability_init, 13978c2ecf20Sopenharmony_ci}; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci#endif /* CONFIG_SECURITY */ 1400