mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![Serge E. Hallyn](/assets/img/avatar_default.png)
Root in a non-initial user ns cannot be trusted to write a traditional security.capability xattr. If it were allowed to do so, then any unprivileged user on the host could map his own uid to root in a private namespace, write the xattr, and execute the file with privilege on the host. However supporting file capabilities in a user namespace is very desirable. Not doing so means that any programs designed to run with limited privilege must continue to support other methods of gaining and dropping privilege. For instance a program installer must detect whether file capabilities can be assigned, and assign them if so but set setuid-root otherwise. The program in turn must know how to drop partial capabilities, and do so only if setuid-root. This patch introduces v3 of the security.capability xattr. It builds a vfs_ns_cap_data struct by appending a uid_t rootid to struct vfs_cap_data. This is the absolute uid_t (that is, the uid_t in user namespace which mounted the filesystem, usually init_user_ns) of the root id in whose namespaces the file capabilities may take effect. When a task asks to write a v2 security.capability xattr, if it is privileged with respect to the userns which mounted the filesystem, then nothing should change. Otherwise, the kernel will transparently rewrite the xattr as a v3 with the appropriate rootid. This is done during the execution of setxattr() to catch user-space-initiated capability writes. Subsequently, any task executing the file which has the noted kuid as its root uid, or which is in a descendent user_ns of such a user_ns, will run the file with capabilities. Similarly when asking to read file capabilities, a v3 capability will be presented as v2 if it applies to the caller's namespace. If a task writes a v3 security.capability, then it can provide a uid for the xattr so long as the uid is valid in its own user namespace, and it is privileged with CAP_SETFCAP over its namespace. The kernel will translate that rootid to an absolute uid, and write that to disk. After this, a task in the writer's namespace will not be able to use those capabilities (unless rootid was 0), but a task in a namespace where the given uid is root will. Only a single security.capability xattr may exist at a time for a given file. A task may overwrite an existing xattr so long as it is privileged over the inode. Note this is a departure from previous semantics, which required privilege to remove a security.capability xattr. This check can be re-added if deemed useful. This allows a simple setxattr to work, allows tar/untar to work, and allows us to tar in one namespace and untar in another while preserving the capability, without risking leaking privilege into a parent namespace. Example using tar: $ cp /bin/sleep sleepx $ mkdir b1 b2 $ lxc-usernsexec -m b:0:100000:1 -m b:1:$(id -u):1 -- chown 0:0 b1 $ lxc-usernsexec -m b:0:100001:1 -m b:1:$(id -u):1 -- chown 0:0 b2 $ lxc-usernsexec -m b:0:100000:1000 -- tar --xattrs-include=security.capability --xattrs -cf b1/sleepx.tar sleepx $ lxc-usernsexec -m b:0:100001:1000 -- tar --xattrs-include=security.capability --xattrs -C b2 -xf b1/sleepx.tar $ lxc-usernsexec -m b:0:100001:1000 -- getcap b2/sleepx b2/sleepx = cap_sys_admin+ep # /opt/ltp/testcases/bin/getv3xattr b2/sleepx v3 xattr, rootid is 100001 A patch to linux-test-project adding a new set of tests for this functionality is in the nsfscaps branch at github.com/hallyn/ltp Changelog: Nov 02 2016: fix invalid check at refuse_fcap_overwrite() Nov 07 2016: convert rootid from and to fs user_ns (From ebiederm: mar 28 2017) commoncap.c: fix typos - s/v4/v3 get_vfs_caps_from_disk: clarify the fs_ns root access check nsfscaps: change the code split for cap_inode_setxattr() Apr 09 2017: don't return v3 cap for caps owned by current root. return a v2 cap for a true v2 cap in non-init ns Apr 18 2017: . Change the flow of fscap writing to support s_user_ns writing. . Remove refuse_fcap_overwrite(). The value of the previous xattr doesn't matter. Apr 24 2017: . incorporate Eric's incremental diff . move cap_convert_nscap to setxattr and simplify its usage May 8, 2017: . fix leaking dentry refcount in cap_inode_getsecurity Signed-off-by: Serge Hallyn <serge@hallyn.com> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
253 lines
7.6 KiB
C
253 lines
7.6 KiB
C
/*
|
|
* This is <linux/capability.h>
|
|
*
|
|
* Andrew G. Morgan <morgan@kernel.org>
|
|
* Alexander Kjeldaas <astor@guardian.no>
|
|
* with help from Aleph1, Roland Buresund and Andrew Main.
|
|
*
|
|
* See here for the libcap library ("POSIX draft" compliance):
|
|
*
|
|
* ftp://www.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.6/
|
|
*/
|
|
#ifndef _LINUX_CAPABILITY_H
|
|
#define _LINUX_CAPABILITY_H
|
|
|
|
#include <uapi/linux/capability.h>
|
|
|
|
|
|
#define _KERNEL_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_3
|
|
#define _KERNEL_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3
|
|
|
|
extern int file_caps_enabled;
|
|
|
|
typedef struct kernel_cap_struct {
|
|
__u32 cap[_KERNEL_CAPABILITY_U32S];
|
|
} kernel_cap_t;
|
|
|
|
/* exact same as vfs_cap_data but in cpu endian and always filled completely */
|
|
struct cpu_vfs_cap_data {
|
|
__u32 magic_etc;
|
|
kernel_cap_t permitted;
|
|
kernel_cap_t inheritable;
|
|
};
|
|
|
|
#define _USER_CAP_HEADER_SIZE (sizeof(struct __user_cap_header_struct))
|
|
#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t))
|
|
|
|
|
|
struct file;
|
|
struct inode;
|
|
struct dentry;
|
|
struct task_struct;
|
|
struct user_namespace;
|
|
|
|
extern const kernel_cap_t __cap_empty_set;
|
|
extern const kernel_cap_t __cap_init_eff_set;
|
|
|
|
/*
|
|
* Internal kernel functions only
|
|
*/
|
|
|
|
#define CAP_FOR_EACH_U32(__capi) \
|
|
for (__capi = 0; __capi < _KERNEL_CAPABILITY_U32S; ++__capi)
|
|
|
|
/*
|
|
* CAP_FS_MASK and CAP_NFSD_MASKS:
|
|
*
|
|
* The fs mask is all the privileges that fsuid==0 historically meant.
|
|
* At one time in the past, that included CAP_MKNOD and CAP_LINUX_IMMUTABLE.
|
|
*
|
|
* It has never meant setting security.* and trusted.* xattrs.
|
|
*
|
|
* We could also define fsmask as follows:
|
|
* 1. CAP_FS_MASK is the privilege to bypass all fs-related DAC permissions
|
|
* 2. The security.* and trusted.* xattrs are fs-related MAC permissions
|
|
*/
|
|
|
|
# define CAP_FS_MASK_B0 (CAP_TO_MASK(CAP_CHOWN) \
|
|
| CAP_TO_MASK(CAP_MKNOD) \
|
|
| CAP_TO_MASK(CAP_DAC_OVERRIDE) \
|
|
| CAP_TO_MASK(CAP_DAC_READ_SEARCH) \
|
|
| CAP_TO_MASK(CAP_FOWNER) \
|
|
| CAP_TO_MASK(CAP_FSETID))
|
|
|
|
# define CAP_FS_MASK_B1 (CAP_TO_MASK(CAP_MAC_OVERRIDE))
|
|
|
|
#if _KERNEL_CAPABILITY_U32S != 2
|
|
# error Fix up hand-coded capability macro initializers
|
|
#else /* HAND-CODED capability initializers */
|
|
|
|
#define CAP_LAST_U32 ((_KERNEL_CAPABILITY_U32S) - 1)
|
|
#define CAP_LAST_U32_VALID_MASK (CAP_TO_MASK(CAP_LAST_CAP + 1) -1)
|
|
|
|
# define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }})
|
|
# define CAP_FULL_SET ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }})
|
|
# define CAP_FS_SET ((kernel_cap_t){{ CAP_FS_MASK_B0 \
|
|
| CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \
|
|
CAP_FS_MASK_B1 } })
|
|
# define CAP_NFSD_SET ((kernel_cap_t){{ CAP_FS_MASK_B0 \
|
|
| CAP_TO_MASK(CAP_SYS_RESOURCE), \
|
|
CAP_FS_MASK_B1 } })
|
|
|
|
#endif /* _KERNEL_CAPABILITY_U32S != 2 */
|
|
|
|
# define cap_clear(c) do { (c) = __cap_empty_set; } while (0)
|
|
|
|
#define cap_raise(c, flag) ((c).cap[CAP_TO_INDEX(flag)] |= CAP_TO_MASK(flag))
|
|
#define cap_lower(c, flag) ((c).cap[CAP_TO_INDEX(flag)] &= ~CAP_TO_MASK(flag))
|
|
#define cap_raised(c, flag) ((c).cap[CAP_TO_INDEX(flag)] & CAP_TO_MASK(flag))
|
|
|
|
#define CAP_BOP_ALL(c, a, b, OP) \
|
|
do { \
|
|
unsigned __capi; \
|
|
CAP_FOR_EACH_U32(__capi) { \
|
|
c.cap[__capi] = a.cap[__capi] OP b.cap[__capi]; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CAP_UOP_ALL(c, a, OP) \
|
|
do { \
|
|
unsigned __capi; \
|
|
CAP_FOR_EACH_U32(__capi) { \
|
|
c.cap[__capi] = OP a.cap[__capi]; \
|
|
} \
|
|
} while (0)
|
|
|
|
static inline kernel_cap_t cap_combine(const kernel_cap_t a,
|
|
const kernel_cap_t b)
|
|
{
|
|
kernel_cap_t dest;
|
|
CAP_BOP_ALL(dest, a, b, |);
|
|
return dest;
|
|
}
|
|
|
|
static inline kernel_cap_t cap_intersect(const kernel_cap_t a,
|
|
const kernel_cap_t b)
|
|
{
|
|
kernel_cap_t dest;
|
|
CAP_BOP_ALL(dest, a, b, &);
|
|
return dest;
|
|
}
|
|
|
|
static inline kernel_cap_t cap_drop(const kernel_cap_t a,
|
|
const kernel_cap_t drop)
|
|
{
|
|
kernel_cap_t dest;
|
|
CAP_BOP_ALL(dest, a, drop, &~);
|
|
return dest;
|
|
}
|
|
|
|
static inline kernel_cap_t cap_invert(const kernel_cap_t c)
|
|
{
|
|
kernel_cap_t dest;
|
|
CAP_UOP_ALL(dest, c, ~);
|
|
return dest;
|
|
}
|
|
|
|
static inline bool cap_isclear(const kernel_cap_t a)
|
|
{
|
|
unsigned __capi;
|
|
CAP_FOR_EACH_U32(__capi) {
|
|
if (a.cap[__capi] != 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Check if "a" is a subset of "set".
|
|
* return true if ALL of the capabilities in "a" are also in "set"
|
|
* cap_issubset(0101, 1111) will return true
|
|
* return false if ANY of the capabilities in "a" are not in "set"
|
|
* cap_issubset(1111, 0101) will return false
|
|
*/
|
|
static inline bool cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
|
|
{
|
|
kernel_cap_t dest;
|
|
dest = cap_drop(a, set);
|
|
return cap_isclear(dest);
|
|
}
|
|
|
|
/* Used to decide between falling back on the old suser() or fsuser(). */
|
|
|
|
static inline kernel_cap_t cap_drop_fs_set(const kernel_cap_t a)
|
|
{
|
|
const kernel_cap_t __cap_fs_set = CAP_FS_SET;
|
|
return cap_drop(a, __cap_fs_set);
|
|
}
|
|
|
|
static inline kernel_cap_t cap_raise_fs_set(const kernel_cap_t a,
|
|
const kernel_cap_t permitted)
|
|
{
|
|
const kernel_cap_t __cap_fs_set = CAP_FS_SET;
|
|
return cap_combine(a,
|
|
cap_intersect(permitted, __cap_fs_set));
|
|
}
|
|
|
|
static inline kernel_cap_t cap_drop_nfsd_set(const kernel_cap_t a)
|
|
{
|
|
const kernel_cap_t __cap_fs_set = CAP_NFSD_SET;
|
|
return cap_drop(a, __cap_fs_set);
|
|
}
|
|
|
|
static inline kernel_cap_t cap_raise_nfsd_set(const kernel_cap_t a,
|
|
const kernel_cap_t permitted)
|
|
{
|
|
const kernel_cap_t __cap_nfsd_set = CAP_NFSD_SET;
|
|
return cap_combine(a,
|
|
cap_intersect(permitted, __cap_nfsd_set));
|
|
}
|
|
|
|
#ifdef CONFIG_MULTIUSER
|
|
extern bool has_capability(struct task_struct *t, int cap);
|
|
extern bool has_ns_capability(struct task_struct *t,
|
|
struct user_namespace *ns, int cap);
|
|
extern bool has_capability_noaudit(struct task_struct *t, int cap);
|
|
extern bool has_ns_capability_noaudit(struct task_struct *t,
|
|
struct user_namespace *ns, int cap);
|
|
extern bool capable(int cap);
|
|
extern bool ns_capable(struct user_namespace *ns, int cap);
|
|
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
|
|
#else
|
|
static inline bool has_capability(struct task_struct *t, int cap)
|
|
{
|
|
return true;
|
|
}
|
|
static inline bool has_ns_capability(struct task_struct *t,
|
|
struct user_namespace *ns, int cap)
|
|
{
|
|
return true;
|
|
}
|
|
static inline bool has_capability_noaudit(struct task_struct *t, int cap)
|
|
{
|
|
return true;
|
|
}
|
|
static inline bool has_ns_capability_noaudit(struct task_struct *t,
|
|
struct user_namespace *ns, int cap)
|
|
{
|
|
return true;
|
|
}
|
|
static inline bool capable(int cap)
|
|
{
|
|
return true;
|
|
}
|
|
static inline bool ns_capable(struct user_namespace *ns, int cap)
|
|
{
|
|
return true;
|
|
}
|
|
static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
|
|
{
|
|
return true;
|
|
}
|
|
#endif /* CONFIG_MULTIUSER */
|
|
extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
|
|
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
|
|
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
|
|
extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
|
|
|
|
/* audit system wants to get cap info from files as well */
|
|
extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
|
|
|
|
extern int cap_convert_nscap(struct dentry *dentry, void **ivalue, size_t size);
|
|
|
|
#endif /* !_LINUX_CAPABILITY_H */
|