mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-10-26 10:57:38 +08:00
Automated merge of 'lsm/dev' into 'stable-7.4'
Manual merge fixup in ns_common_types.h:ns_common to work around a conflict with the nsfs RCU grace period fix. * lsm/dev: lsm: remove redundant NULL check in security_init() selftests/landlock: Check full ioctl commands in audit records lsm: Preserve full ioctl commands in audit records doc: fix LSM ordering description for /sys/kernel/security/lsm doc: describe CONFIG_LSM and lsm= as the selection mechanism selftests/bpf: verify mount idmaps reach inode hooks lsm: expose mount idmaps to inode hooks rust: lsm: replace `core::mem::zeroed` with `pin_init::zeroed` cred: prevent slab cache merging for cred_jar lsm: don't call security_backing_file_free() multiple times lsm: delete duplicate assignment in lsm_prepare() lsm: update the BUILD_BUG_ON() in audit_log_lsm_data() lsm: add LSM_AUDIT_DATA_NS for namespace audit records lsm: add LSM blob and hooks for namespaces ns: Free anonymous mount namespaces via ns_common_free() cred: clarify that task_struct::cred is only for the current task
This commit is contained in:
commit
436485132d
26 files changed
+599
-111
No files matched your search
@@ -6,9 +6,11 @@ The Linux Security Module (LSM) framework provides a mechanism for
|
||||
various security checks to be hooked by new kernel extensions. The name
|
||||
"module" is a bit of a misnomer since these extensions are not actually
|
||||
loadable kernel modules. Instead, they are selectable at build-time via
|
||||
CONFIG_DEFAULT_SECURITY and can be overridden at boot-time via the
|
||||
``"security=..."`` kernel command line argument, in the case where multiple
|
||||
LSMs were built into a given kernel.
|
||||
CONFIG_LSM, an ordered list of the LSMs to enable, and can be
|
||||
overridden at boot-time via the ``"lsm=..."`` kernel command line
|
||||
argument. The ``"security=..."`` kernel command line argument remains
|
||||
available to choose a legacy "major" security module, but has been
|
||||
deprecated by the ``"lsm=..."`` parameter.
|
||||
|
||||
The primary users of the LSM interface are Mandatory Access Control
|
||||
(MAC) extensions which provide a comprehensive security policy. Examples
|
||||
@@ -25,9 +27,15 @@ man-pages project.
|
||||
A list of the active security modules can be found by reading
|
||||
``/sys/kernel/security/lsm``. This is a comma separated list, and
|
||||
will always include the capability module. The list reflects the
|
||||
order in which checks are made. The capability module will always
|
||||
be first, followed by any "minor" modules (e.g. Yama) and then
|
||||
the one "major" module (e.g. SELinux) if there is one configured.
|
||||
order in which checks are made. The capability module will be
|
||||
first, unless CONFIG_SECURITY_LOCKDOWN_LSM_EARLY is enabled, in
|
||||
which case the lockdown module will precede it. The integrity
|
||||
modules (e.g. IMA and EVM), if enabled in the kernel
|
||||
configuration, are always placed at the end of the list. Any
|
||||
other "minor" modules (e.g. Yama) and the one "major" module
|
||||
(e.g. SELinux), if there is one configured, appear in between,
|
||||
in the order given by CONFIG_LSM or the ``"lsm=..."`` kernel
|
||||
command line parameter.
|
||||
|
||||
Process attributes associated with "major" security modules should
|
||||
be accessed and maintained using the special files in ``/proc/.../attr``.
|
||||
|
||||
@@ -51,14 +51,14 @@ static int cachefiles_check_cache_dir(struct cachefiles_cache *cache,
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = security_inode_mkdir(d_backing_inode(root), root, 0);
|
||||
ret = security_inode_mkdir(&nop_mnt_idmap, d_backing_inode(root), root, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("Security denies permission to make dirs: error %d",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = security_inode_create(d_backing_inode(root), root, 0);
|
||||
ret = security_inode_create(&nop_mnt_idmap, d_backing_inode(root), root, 0);
|
||||
if (ret < 0)
|
||||
pr_err("Security denies permission to create files: error %d",
|
||||
ret);
|
||||
|
||||
+9
-9
@@ -658,7 +658,7 @@ int inode_permission(struct mnt_idmap *idmap,
|
||||
if (unlikely(retval))
|
||||
return retval;
|
||||
|
||||
return security_inode_permission(inode, mask);
|
||||
return security_inode_permission(idmap, inode, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(inode_permission);
|
||||
|
||||
@@ -695,7 +695,7 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id
|
||||
if (unlikely(((inode->i_mode & 0111) != 0111) || !no_acl_inode(inode)))
|
||||
return inode_permission(idmap, inode, mask);
|
||||
|
||||
return security_inode_permission(inode, mask);
|
||||
return security_inode_permission(idmap, inode, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4188,7 +4188,7 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
|
||||
return -EACCES; /* shouldn't it be ENOSYS? */
|
||||
|
||||
mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG);
|
||||
error = security_inode_create(dir, dentry, mode);
|
||||
error = security_inode_create(idmap, dir, dentry, mode);
|
||||
if (error)
|
||||
return error;
|
||||
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
|
||||
@@ -4212,7 +4212,7 @@ int vfs_mkobj(struct dentry *dentry, umode_t mode,
|
||||
|
||||
mode &= S_IALLUGO;
|
||||
mode |= S_IFREG;
|
||||
error = security_inode_create(dir, dentry, mode);
|
||||
error = security_inode_create(&nop_mnt_idmap, dir, dentry, mode);
|
||||
if (error)
|
||||
return error;
|
||||
error = f(dentry, mode, arg);
|
||||
@@ -4328,7 +4328,7 @@ static int may_o_create(struct mnt_idmap *idmap,
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return security_inode_create(dir->dentry->d_inode, dentry, mode);
|
||||
return security_inode_create(idmap, dir->dentry->d_inode, dentry, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5258,7 +5258,7 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = security_inode_mknod(dir, dentry, mode, dev);
|
||||
error = security_inode_mknod(idmap, dir, dentry, mode, dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -5395,7 +5395,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
goto err;
|
||||
|
||||
mode = vfs_prepare_mode(idmap, dir, mode, S_IRWXUGO | S_ISVTX, S_IFDIR);
|
||||
error = security_inode_mkdir(dir, dentry, mode);
|
||||
error = security_inode_mkdir(idmap, dir, dentry, mode);
|
||||
if (error)
|
||||
goto err;
|
||||
|
||||
@@ -5783,7 +5783,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
if (!dir->i_op->symlink)
|
||||
return -EPERM;
|
||||
|
||||
error = security_inode_symlink(dir, dentry, oldname);
|
||||
error = security_inode_symlink(idmap, dir, dentry, oldname);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -5907,7 +5907,7 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
return -EPERM;
|
||||
|
||||
error = security_inode_link(old_dentry, dir, new_dentry);
|
||||
error = security_inode_link(idmap, old_dentry, dir, new_dentry);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
||||
+1
-2
@@ -4193,8 +4193,7 @@ static void dec_mnt_namespaces(struct ucounts *ucounts)
|
||||
|
||||
static void free_mnt_ns(struct mnt_namespace *ns)
|
||||
{
|
||||
if (!is_anon_ns(ns))
|
||||
ns_common_free(ns);
|
||||
ns_common_free(ns);
|
||||
dec_mnt_namespaces(ns->ucounts);
|
||||
mnt_ns_tree_remove(ns);
|
||||
}
|
||||
|
||||
+11
-6
@@ -180,12 +180,18 @@ static inline bool cap_ambient_invariant_ok(const struct cred *cred)
|
||||
|
||||
static inline const struct cred *override_creds(const struct cred *override_cred)
|
||||
{
|
||||
return rcu_replace_pointer(current->cred, override_cred, 1);
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
current->cred = override_cred;
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline const struct cred *revert_creds(const struct cred *revert_cred)
|
||||
{
|
||||
return rcu_replace_pointer(current->cred, revert_cred, 1);
|
||||
const struct cred *override_cred = current->cred;
|
||||
|
||||
current->cred = revert_cred;
|
||||
return override_cred;
|
||||
}
|
||||
|
||||
DEFINE_CLASS(override_creds,
|
||||
@@ -293,11 +299,10 @@ DEFINE_FREE(put_cred, struct cred *, if (!IS_ERR_OR_NULL(_T)) put_cred(_T))
|
||||
/**
|
||||
* current_cred - Access the current task's subjective credentials
|
||||
*
|
||||
* Access the subjective credentials of the current task. RCU-safe,
|
||||
* since nobody else can modify it.
|
||||
* Access the subjective credentials of the current task.
|
||||
* Nobody else can modify it.
|
||||
*/
|
||||
#define current_cred() \
|
||||
rcu_dereference_protected(current->cred, 1)
|
||||
#define current_cred() (current->cred)
|
||||
|
||||
/**
|
||||
* current_real_cred - Access the current task's objective credentials
|
||||
|
||||
@@ -44,7 +44,7 @@ struct lsm_network_audit {
|
||||
|
||||
struct lsm_ioctlop_audit {
|
||||
struct path path;
|
||||
u16 cmd;
|
||||
unsigned int cmd;
|
||||
};
|
||||
|
||||
struct lsm_ibpkey_audit {
|
||||
@@ -78,6 +78,7 @@ struct common_audit_data {
|
||||
#define LSM_AUDIT_DATA_NOTIFICATION 16
|
||||
#define LSM_AUDIT_DATA_ANONINODE 17
|
||||
#define LSM_AUDIT_DATA_NLMSGTYPE 18
|
||||
#define LSM_AUDIT_DATA_NS 19
|
||||
union {
|
||||
struct path path;
|
||||
struct dentry *dentry;
|
||||
@@ -100,6 +101,10 @@ struct common_audit_data {
|
||||
int reason;
|
||||
const char *anonclass;
|
||||
u16 nlmsg_type;
|
||||
struct {
|
||||
u32 ns_type;
|
||||
u64 ns_id;
|
||||
} ns;
|
||||
} u;
|
||||
/* this union contains LSM specific data */
|
||||
union {
|
||||
|
||||
@@ -120,26 +120,27 @@ LSM_HOOK(int, -EOPNOTSUPP, inode_init_security, struct inode *inode,
|
||||
int *xattr_count)
|
||||
LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,
|
||||
const struct qstr *name, const struct inode *context_inode)
|
||||
LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
LSM_HOOK(int, 0, inode_create, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
LSM_HOOK(void, LSM_RET_VOID, inode_post_create_tmpfile, struct mnt_idmap *idmap,
|
||||
struct inode *inode)
|
||||
LSM_HOOK(int, 0, inode_link, struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
LSM_HOOK(int, 0, inode_link, struct mnt_idmap *idmap,
|
||||
struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
|
||||
LSM_HOOK(int, 0, inode_unlink, struct inode *dir, struct dentry *dentry)
|
||||
LSM_HOOK(int, 0, inode_symlink, struct inode *dir, struct dentry *dentry,
|
||||
const char *old_name)
|
||||
LSM_HOOK(int, 0, inode_mkdir, struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
LSM_HOOK(int, 0, inode_symlink, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *old_name)
|
||||
LSM_HOOK(int, 0, inode_mkdir, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
LSM_HOOK(int, 0, inode_rmdir, struct inode *dir, struct dentry *dentry)
|
||||
LSM_HOOK(int, 0, inode_mknod, struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, dev_t dev)
|
||||
LSM_HOOK(int, 0, inode_mknod, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
LSM_HOOK(int, 0, inode_rename, struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
LSM_HOOK(int, 0, inode_readlink, struct dentry *dentry)
|
||||
LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode,
|
||||
bool rcu)
|
||||
LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask)
|
||||
LSM_HOOK(int, 0, inode_permission, struct mnt_idmap *idmap,
|
||||
struct inode *inode, int mask)
|
||||
LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap,
|
||||
@@ -265,6 +266,9 @@ LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2,
|
||||
LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p,
|
||||
struct inode *inode)
|
||||
LSM_HOOK(int, 0, userns_create, const struct cred *cred)
|
||||
LSM_HOOK(int, 0, namespace_init, struct ns_common *ns)
|
||||
LSM_HOOK(void, LSM_RET_VOID, namespace_free, struct ns_common *ns)
|
||||
LSM_HOOK(int, 0, namespace_install, const struct nsset *nsset, struct ns_common *ns)
|
||||
LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
|
||||
LSM_HOOK(void, LSM_RET_VOID, ipc_getlsmprop, struct kern_ipc_perm *ipcp,
|
||||
struct lsm_prop *prop)
|
||||
|
||||
@@ -112,6 +112,7 @@ struct lsm_blob_sizes {
|
||||
unsigned int lbs_ipc;
|
||||
unsigned int lbs_key;
|
||||
unsigned int lbs_msg_msg;
|
||||
unsigned int lbs_ns;
|
||||
unsigned int lbs_perf_event;
|
||||
unsigned int lbs_task;
|
||||
unsigned int lbs_xattr_count; /* num xattr slots in new_xattrs array */
|
||||
|
||||
@@ -116,6 +116,9 @@ struct ns_common {
|
||||
struct dentry *stashed;
|
||||
const struct proc_ns_operations *ops;
|
||||
unsigned int inum;
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *ns_security;
|
||||
#endif
|
||||
struct ns_tree;
|
||||
struct rcu_head ns_rcu;
|
||||
};
|
||||
|
||||
@@ -1172,8 +1172,12 @@ struct task_struct {
|
||||
/* Objective and real subjective task credentials (COW): */
|
||||
const struct cred __rcu *real_cred;
|
||||
|
||||
/* Effective (overridable) subjective task credentials (COW): */
|
||||
const struct cred __rcu *cred;
|
||||
/*
|
||||
* Effective (overridable) subjective task credentials (COW).
|
||||
* Only accessible for the current task and during task creation/freeing.
|
||||
* This pointer is not managed by RCU!
|
||||
*/
|
||||
const struct cred *cred;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
/* Cached requested key. */
|
||||
|
||||
+54
-24
@@ -67,6 +67,7 @@ enum fs_value_type;
|
||||
struct watch;
|
||||
struct watch_notification;
|
||||
struct lsm_ctx;
|
||||
struct nsset;
|
||||
|
||||
/* Default (no) options for the capable function */
|
||||
#define CAP_OPT_NONE 0x0
|
||||
@@ -80,6 +81,7 @@ struct lsm_ctx;
|
||||
|
||||
struct ctl_table;
|
||||
struct audit_krule;
|
||||
struct ns_common;
|
||||
struct user_namespace;
|
||||
struct timezone;
|
||||
|
||||
@@ -404,24 +406,28 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
|
||||
int security_inode_init_security_anon(struct inode *inode,
|
||||
const struct qstr *name,
|
||||
const struct inode *context_inode);
|
||||
int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
|
||||
int security_inode_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode);
|
||||
void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
|
||||
struct inode *inode);
|
||||
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry);
|
||||
int security_inode_link(struct mnt_idmap *idmap, struct dentry *old_dentry,
|
||||
struct inode *dir, struct dentry *new_dentry);
|
||||
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
|
||||
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *old_name);
|
||||
int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
|
||||
int security_inode_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *old_name);
|
||||
int security_inode_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode);
|
||||
int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
|
||||
int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev);
|
||||
int security_inode_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t dev);
|
||||
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags);
|
||||
int security_inode_readlink(struct dentry *dentry);
|
||||
int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
|
||||
bool rcu);
|
||||
int security_inode_permission(struct inode *inode, int mask);
|
||||
int security_inode_permission(struct mnt_idmap *idmap, struct inode *inode,
|
||||
int mask);
|
||||
int security_inode_setattr(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, struct iattr *attr);
|
||||
void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
@@ -540,6 +546,9 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5);
|
||||
void security_task_to_inode(struct task_struct *p, struct inode *inode);
|
||||
int security_create_user_ns(const struct cred *cred);
|
||||
int security_namespace_init(struct ns_common *ns);
|
||||
void security_namespace_free(struct ns_common *ns);
|
||||
int security_namespace_install(const struct nsset *nsset, struct ns_common *ns);
|
||||
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
|
||||
void security_ipc_getlsmprop(struct kern_ipc_perm *ipcp, struct lsm_prop *prop);
|
||||
int security_msg_msg_alloc(struct msg_msg *msg);
|
||||
@@ -902,9 +911,10 @@ static inline int security_inode_init_security_anon(struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_inode_create(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
umode_t mode)
|
||||
static inline int security_inode_create(struct mnt_idmap *idmap,
|
||||
struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
umode_t mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -913,9 +923,10 @@ static inline void
|
||||
security_inode_post_create_tmpfile(struct mnt_idmap *idmap, struct inode *inode)
|
||||
{ }
|
||||
|
||||
static inline int security_inode_link(struct dentry *old_dentry,
|
||||
struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
static inline int security_inode_link(struct mnt_idmap *idmap,
|
||||
struct dentry *old_dentry,
|
||||
struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -926,16 +937,18 @@ static inline int security_inode_unlink(struct inode *dir,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_inode_symlink(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
const char *old_name)
|
||||
static inline int security_inode_symlink(struct mnt_idmap *idmap,
|
||||
struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
const char *old_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_inode_mkdir(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
int mode)
|
||||
static inline int security_inode_mkdir(struct mnt_idmap *idmap,
|
||||
struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
int mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -946,9 +959,10 @@ static inline int security_inode_rmdir(struct inode *dir,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_inode_mknod(struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
int mode, dev_t dev)
|
||||
static inline int security_inode_mknod(struct mnt_idmap *idmap,
|
||||
struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
int mode, dev_t dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -974,7 +988,8 @@ static inline int security_inode_follow_link(struct dentry *dentry,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_inode_permission(struct inode *inode, int mask)
|
||||
static inline int security_inode_permission(struct mnt_idmap *idmap,
|
||||
struct inode *inode, int mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1431,6 +1446,21 @@ static inline int security_create_user_ns(const struct cred *cred)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_namespace_init(struct ns_common *ns)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_namespace_free(struct ns_common *ns)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int security_namespace_install(const struct nsset *nsset,
|
||||
struct ns_common *ns)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_ipc_permission(struct kern_ipc_perm *ipcp,
|
||||
short flag)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ enum init_ns_ino {
|
||||
MNT_NS_INIT_INO = 0xEFFFFFF8U,
|
||||
#ifdef __KERNEL__
|
||||
MNT_NS_ANON_INO = 0xEFFFFFF7U,
|
||||
MNT_NS_INO_SPECIAL_MAX = MNT_NS_ANON_INO,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -160,7 +160,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = {
|
||||
.sibling = LIST_HEAD_INIT(init_task.sibling),
|
||||
.group_leader = &init_task,
|
||||
RCU_POINTER_INITIALIZER(real_cred, &init_cred),
|
||||
RCU_POINTER_INITIALIZER(cred, &init_cred),
|
||||
.cred = &init_cred,
|
||||
.comm = INIT_TASK_COMM,
|
||||
.thread = INIT_THREAD,
|
||||
.real_fs = &init_fs,
|
||||
|
||||
+3
-2
@@ -459,7 +459,7 @@ static int audit_field_compare(struct task_struct *tsk,
|
||||
*
|
||||
* If task_creation is true, this is an explicit indication that we are
|
||||
* filtering a task rule at task creation time. This and tsk == current are
|
||||
* the only situations where tsk->cred may be accessed without an rcu read lock.
|
||||
* the only situations where tsk->cred may be accessed.
|
||||
*/
|
||||
static int audit_filter_rules(struct task_struct *tsk,
|
||||
struct audit_krule *rule,
|
||||
@@ -476,7 +476,8 @@ static int audit_filter_rules(struct task_struct *tsk,
|
||||
if (ctx && rule->prio <= ctx->prio)
|
||||
return 0;
|
||||
|
||||
cred = rcu_dereference_check(tsk->cred, tsk == current || task_creation);
|
||||
WARN_ON(tsk != current && !task_creation);
|
||||
cred = tsk->cred;
|
||||
|
||||
for (i = 0; i < rule->field_count; i++) {
|
||||
struct audit_field *f = &rule->fields[i];
|
||||
|
||||
+3
-2
@@ -414,7 +414,7 @@ int commit_creds(struct cred *new)
|
||||
inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1);
|
||||
|
||||
rcu_assign_pointer(task->real_cred, new);
|
||||
rcu_assign_pointer(task->cred, new);
|
||||
task->cred = new;
|
||||
if (new->user != old->user || new->user_ns != old->user_ns)
|
||||
dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1);
|
||||
if (new->user_ns != old->user_ns)
|
||||
@@ -537,7 +537,8 @@ void __init cred_init(void)
|
||||
{
|
||||
/* allocate a slab in which we can store credentials */
|
||||
cred_jar = KMEM_CACHE(cred,
|
||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
|
||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT |
|
||||
SLAB_NO_MERGE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
-1
@@ -4,6 +4,7 @@
|
||||
#include <linux/ns_common.h>
|
||||
#include <linux/nstree.h>
|
||||
#include <linux/proc_ns.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <linux/vfsdebug.h>
|
||||
|
||||
@@ -59,6 +60,9 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
|
||||
|
||||
refcount_set(&ns->__ns_ref, 1);
|
||||
ns->stashed = NULL;
|
||||
#ifdef CONFIG_SECURITY
|
||||
ns->ns_security = NULL;
|
||||
#endif
|
||||
ns->ops = ops;
|
||||
ns->ns_id = 0;
|
||||
ns->ns_type = ns_type;
|
||||
@@ -77,6 +81,14 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
|
||||
ret = proc_alloc_inum(&ns->inum);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = security_namespace_init(ns);
|
||||
if (ret) {
|
||||
if (!inum)
|
||||
proc_free_inum(ns->inum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tree ref starts at 0. It's incremented when namespace enters
|
||||
* active use (installed in nsproxy) and decremented when all
|
||||
@@ -91,7 +103,10 @@ int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_ope
|
||||
|
||||
void __ns_common_free(struct ns_common *ns)
|
||||
{
|
||||
proc_free_inum(ns->inum);
|
||||
security_namespace_free(ns);
|
||||
|
||||
if (ns->inum > MNT_NS_INO_SPECIAL_MAX)
|
||||
proc_free_inum(ns->inum);
|
||||
}
|
||||
|
||||
struct ns_common *__must_check ns_owner(struct ns_common *ns)
|
||||
|
||||
@@ -385,6 +385,12 @@ out:
|
||||
|
||||
static inline int validate_ns(struct nsset *nsset, struct ns_common *ns)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = security_namespace_install(nsset, ns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ns->ops->install(nsset, ns);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,7 @@ impl SecurityCtx {
|
||||
/// Get the security context given its id.
|
||||
#[inline]
|
||||
pub fn from_secid(secid: u32) -> Result<Self> {
|
||||
// SAFETY: `struct lsm_context` can be initialized to all zeros.
|
||||
let mut ctx: bindings::lsm_context = unsafe { core::mem::zeroed() };
|
||||
let mut ctx: bindings::lsm_context = pin_init::zeroed();
|
||||
|
||||
// SAFETY: Just a C FFI call. The pointer is valid for writes.
|
||||
to_result(unsafe { bindings::security_secid_to_secctx(secid, &mut ctx) })?;
|
||||
|
||||
@@ -182,7 +182,7 @@ void audit_log_lsm_data(struct audit_buffer *ab,
|
||||
* start making this union too large! See struct lsm_network_audit
|
||||
* as an example of how to deal with large data.
|
||||
*/
|
||||
BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
|
||||
BUILD_BUG_ON(sizeof(a->u) > (sizeof(u64) * 2));
|
||||
|
||||
switch (a->type) {
|
||||
case LSM_AUDIT_DATA_NONE:
|
||||
@@ -231,7 +231,7 @@ void audit_log_lsm_data(struct audit_buffer *ab,
|
||||
audit_log_format(ab, " ino=%llu", inode->i_ino);
|
||||
}
|
||||
|
||||
audit_log_format(ab, " ioctlcmd=0x%hx", a->u.op->cmd);
|
||||
audit_log_format(ab, " ioctlcmd=0x%x", a->u.op->cmd);
|
||||
break;
|
||||
}
|
||||
case LSM_AUDIT_DATA_DENTRY: {
|
||||
@@ -403,6 +403,10 @@ void audit_log_lsm_data(struct audit_buffer *ab,
|
||||
case LSM_AUDIT_DATA_NLMSGTYPE:
|
||||
audit_log_format(ab, " nl-msgtype=%hu", a->u.nlmsg_type);
|
||||
break;
|
||||
case LSM_AUDIT_DATA_NS:
|
||||
audit_log_format(ab, " namespace_type=0x%x namespace_id=%llu",
|
||||
a->u.ns.ns_type, a->u.ns.ns_id);
|
||||
break;
|
||||
} /* switch (a->type) */
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -290,7 +290,6 @@ static void __init lsm_prepare(struct lsm_info *lsm)
|
||||
return;
|
||||
|
||||
/* Register the LSM blob sizes. */
|
||||
blobs = lsm->blobs;
|
||||
lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
|
||||
lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
|
||||
lsm_blob_size_update(&blobs->lbs_backing_file,
|
||||
@@ -303,6 +302,7 @@ static void __init lsm_prepare(struct lsm_info *lsm)
|
||||
lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
|
||||
lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
|
||||
lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
|
||||
lsm_blob_size_update(&blobs->lbs_ns, &blob_sizes.lbs_ns);
|
||||
lsm_blob_size_update(&blobs->lbs_perf_event,
|
||||
&blob_sizes.lbs_perf_event);
|
||||
lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
|
||||
@@ -431,8 +431,7 @@ int __init security_init(void)
|
||||
}
|
||||
|
||||
if (lsm_order_cmdline) {
|
||||
if (lsm_order_legacy)
|
||||
lsm_order_legacy = NULL;
|
||||
lsm_order_legacy = NULL;
|
||||
lsm_order_parse(lsm_order_cmdline, "cmdline");
|
||||
} else
|
||||
lsm_order_parse(lsm_order_builtin, "builtin");
|
||||
@@ -450,6 +449,7 @@ int __init security_init(void)
|
||||
lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
|
||||
lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
|
||||
lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
|
||||
lsm_pr("blob(ns) size %d\n", blob_sizes.lbs_ns);
|
||||
lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
|
||||
lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
|
||||
lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
|
||||
@@ -476,8 +476,7 @@ int __init security_init(void)
|
||||
blob_sizes.lbs_inode, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
|
||||
if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred),
|
||||
GFP_KERNEL))
|
||||
if (lsm_cred_alloc((struct cred *)current->cred, GFP_KERNEL))
|
||||
panic("early LSM cred alloc failed\n");
|
||||
if (lsm_task_alloc(current))
|
||||
panic("early LSM task alloc failed\n");
|
||||
|
||||
+97
-18
@@ -26,6 +26,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/msg.h>
|
||||
#include <linux/ns_common.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/fs.h>
|
||||
@@ -381,6 +382,19 @@ static int lsm_superblock_alloc(struct super_block *sb)
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_ns_alloc - allocate a composite namespace blob
|
||||
* @ns: the namespace that needs a blob
|
||||
*
|
||||
* Allocate the namespace blob for all the modules
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_ns_alloc(struct ns_common *ns)
|
||||
{
|
||||
return lsm_blob_alloc(&ns->ns_security, blob_sizes.lbs_ns, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_fill_user_ctx - Fill a user space lsm_ctx structure
|
||||
* @uctx: a userspace LSM context to be filled
|
||||
@@ -1614,6 +1628,7 @@ int security_path_chroot(const struct path *path)
|
||||
|
||||
/**
|
||||
* security_inode_create() - Check if creating a file is allowed
|
||||
* @idmap: idmap of the mount
|
||||
* @dir: the parent directory
|
||||
* @dentry: the file being created
|
||||
* @mode: requested file mode
|
||||
@@ -1622,12 +1637,12 @@ int security_path_chroot(const struct path *path)
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_inode_create(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
int security_inode_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(dir)))
|
||||
return 0;
|
||||
return call_int_hook(inode_create, dir, dentry, mode);
|
||||
return call_int_hook(inode_create, idmap, dir, dentry, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(security_inode_create);
|
||||
|
||||
@@ -1648,6 +1663,7 @@ void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
|
||||
|
||||
/**
|
||||
* security_inode_link() - Check if creating a hard link is allowed
|
||||
* @idmap: idmap of the mount
|
||||
* @old_dentry: existing file
|
||||
* @dir: new parent directory
|
||||
* @new_dentry: new link
|
||||
@@ -1656,12 +1672,12 @@ void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
int security_inode_link(struct mnt_idmap *idmap, struct dentry *old_dentry,
|
||||
struct inode *dir, struct dentry *new_dentry)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
|
||||
return 0;
|
||||
return call_int_hook(inode_link, old_dentry, dir, new_dentry);
|
||||
return call_int_hook(inode_link, idmap, old_dentry, dir, new_dentry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1682,6 +1698,7 @@ int security_inode_unlink(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
/**
|
||||
* security_inode_symlink() - Check if creating a symbolic link is allowed
|
||||
* @idmap: idmap of the mount
|
||||
* @dir: parent directory
|
||||
* @dentry: symbolic link
|
||||
* @old_name: existing filename
|
||||
@@ -1690,16 +1707,17 @@ int security_inode_unlink(struct inode *dir, struct dentry *dentry)
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *old_name)
|
||||
int security_inode_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *old_name)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(dir)))
|
||||
return 0;
|
||||
return call_int_hook(inode_symlink, dir, dentry, old_name);
|
||||
return call_int_hook(inode_symlink, idmap, dir, dentry, old_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_inode_mkdir() - Check if creating a new directory is allowed
|
||||
* @idmap: idmap of the mount
|
||||
* @dir: parent directory
|
||||
* @dentry: new directory
|
||||
* @mode: new directory mode
|
||||
@@ -1709,11 +1727,12 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry,
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
int security_inode_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(dir)))
|
||||
return 0;
|
||||
return call_int_hook(inode_mkdir, dir, dentry, mode);
|
||||
return call_int_hook(inode_mkdir, idmap, dir, dentry, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(security_inode_mkdir);
|
||||
|
||||
@@ -1735,6 +1754,7 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
|
||||
/**
|
||||
* security_inode_mknod() - Check if creating a special file is allowed
|
||||
* @idmap: idmap of the mount
|
||||
* @dir: parent directory
|
||||
* @dentry: new file
|
||||
* @mode: new file mode
|
||||
@@ -1747,12 +1767,12 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_inode_mknod(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, dev_t dev)
|
||||
int security_inode_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(dir)))
|
||||
return 0;
|
||||
return call_int_hook(inode_mknod, dir, dentry, mode, dev);
|
||||
return call_int_hook(inode_mknod, idmap, dir, dentry, mode, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1823,6 +1843,7 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
|
||||
|
||||
/**
|
||||
* security_inode_permission() - Check if accessing an inode is allowed
|
||||
* @idmap: idmap of the mount
|
||||
* @inode: inode
|
||||
* @mask: access mask
|
||||
*
|
||||
@@ -1835,11 +1856,12 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_inode_permission(struct inode *inode, int mask)
|
||||
int security_inode_permission(struct mnt_idmap *idmap, struct inode *inode,
|
||||
int mask)
|
||||
{
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
return call_int_hook(inode_permission, inode, mask);
|
||||
return call_int_hook(inode_permission, idmap, inode, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2486,9 +2508,8 @@ void security_backing_file_free(struct file *backing_file)
|
||||
{
|
||||
void *blob = backing_file_security(backing_file);
|
||||
|
||||
call_void_hook(backing_file_free, backing_file);
|
||||
|
||||
if (blob) {
|
||||
call_void_hook(backing_file_free, backing_file);
|
||||
backing_file_set_security(backing_file, NULL);
|
||||
kmem_cache_free(lsm_backing_file_cache, blob);
|
||||
}
|
||||
@@ -3357,6 +3378,64 @@ int security_create_user_ns(const struct cred *cred)
|
||||
return call_int_hook(userns_create, cred);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_namespace_init() - Initialize LSM security data for a namespace
|
||||
* @ns: the namespace being initialized
|
||||
*
|
||||
* Initialize the LSM security blob attached to the namespace. The namespace type
|
||||
* is available via ns->ns_type, and the owning user namespace (if any)
|
||||
* via ns->ops->owner(ns).
|
||||
*
|
||||
* Return: Returns 0 if successful, otherwise < 0 error code.
|
||||
*/
|
||||
int security_namespace_init(struct ns_common *ns)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = lsm_ns_alloc(ns);
|
||||
if (unlikely(rc))
|
||||
return rc;
|
||||
|
||||
rc = call_int_hook(namespace_init, ns);
|
||||
if (unlikely(rc))
|
||||
security_namespace_free(ns);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_namespace_free() - Release LSM security data from a namespace
|
||||
* @ns: the namespace being freed
|
||||
*
|
||||
* Release security data attached to the namespace. Called before the
|
||||
* namespace structure is freed.
|
||||
*/
|
||||
void security_namespace_free(struct ns_common *ns)
|
||||
{
|
||||
if (!ns->ns_security)
|
||||
return;
|
||||
|
||||
call_void_hook(namespace_free, ns);
|
||||
|
||||
kfree(ns->ns_security);
|
||||
ns->ns_security = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_namespace_install() - Check permission to install a namespace
|
||||
* @nsset: the target nsset being configured
|
||||
* @ns: the namespace being installed
|
||||
*
|
||||
* Check permission before allowing a namespace to be installed into the
|
||||
* process's set of namespaces via setns(2).
|
||||
*
|
||||
* Return: Returns 0 if permission is granted, otherwise < 0 error code.
|
||||
*/
|
||||
int security_namespace_install(const struct nsset *nsset, struct ns_common *ns)
|
||||
{
|
||||
return call_int_hook(namespace_install, nsset, ns);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_ipc_permission() - Check if sysv ipc access is allowed
|
||||
* @ipcp: ipc permission structure
|
||||
|
||||
@@ -3074,12 +3074,14 @@ static int selinux_inode_init_security_anon(struct inode *inode,
|
||||
&ad);
|
||||
}
|
||||
|
||||
static int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
static int selinux_inode_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
return may_create(dir, dentry, SECCLASS_FILE);
|
||||
}
|
||||
|
||||
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
|
||||
static int selinux_inode_link(struct mnt_idmap *idmap, struct dentry *old_dentry,
|
||||
struct inode *dir, struct dentry *new_dentry)
|
||||
{
|
||||
return may_link(dir, old_dentry, MAY_LINK);
|
||||
}
|
||||
@@ -3089,12 +3091,14 @@ static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
|
||||
return may_link(dir, dentry, MAY_UNLINK);
|
||||
}
|
||||
|
||||
static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
|
||||
static int selinux_inode_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *name)
|
||||
{
|
||||
return may_create(dir, dentry, SECCLASS_LNK_FILE);
|
||||
}
|
||||
|
||||
static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mask)
|
||||
static int selinux_inode_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mask)
|
||||
{
|
||||
return may_create(dir, dentry, SECCLASS_DIR);
|
||||
}
|
||||
@@ -3104,7 +3108,8 @@ static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
return may_link(dir, dentry, MAY_RMDIR);
|
||||
}
|
||||
|
||||
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
static int selinux_inode_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t dev)
|
||||
{
|
||||
return may_create(dir, dentry, inode_mode_to_security_class(mode));
|
||||
}
|
||||
@@ -3236,13 +3241,15 @@ static inline void task_avdcache_update(struct task_security_struct *tsec,
|
||||
|
||||
/**
|
||||
* selinux_inode_permission - Check if the current task can access an inode
|
||||
* @idmap: idmap of the mount
|
||||
* @inode: the inode that is being accessed
|
||||
* @requested: the accesses being requested
|
||||
*
|
||||
* Check if the current task is allowed to access @inode according to
|
||||
* @requested. Returns 0 if allowed, negative values otherwise.
|
||||
*/
|
||||
static int selinux_inode_permission(struct inode *inode, int requested)
|
||||
static int selinux_inode_permission(struct mnt_idmap *idmap,
|
||||
struct inode *inode, int requested)
|
||||
{
|
||||
int mask;
|
||||
u32 perms;
|
||||
@@ -3859,7 +3866,7 @@ static int selinux_backing_file_alloc(struct file *backing_file,
|
||||
* operation to an inode.
|
||||
*/
|
||||
static int ioctl_has_perm(const struct cred *cred, struct file *file,
|
||||
u32 requested, u16 cmd)
|
||||
u32 requested, unsigned int cmd)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
@@ -3930,14 +3937,14 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
case FIOCLEX:
|
||||
case FIONCLEX:
|
||||
if (!selinux_policycap_ioctl_skip_cloexec())
|
||||
error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
|
||||
error = ioctl_has_perm(cred, file, FILE__IOCTL, cmd);
|
||||
break;
|
||||
|
||||
/* default case assumes that the command will go
|
||||
* to the file's ioctl() function.
|
||||
*/
|
||||
default:
|
||||
error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
|
||||
error = ioctl_has_perm(cred, file, FILE__IOCTL, cmd);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1089,14 +1089,15 @@ instant_inode:
|
||||
|
||||
/**
|
||||
* smack_inode_link - Smack check on link
|
||||
* @idmap: idmap of the mount
|
||||
* @old_dentry: the existing object
|
||||
* @dir: unused
|
||||
* @new_dentry: the new object
|
||||
*
|
||||
* Returns 0 if access is permitted, an error code otherwise
|
||||
*/
|
||||
static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry)
|
||||
static int smack_inode_link(struct mnt_idmap *idmap, struct dentry *old_dentry,
|
||||
struct inode *dir, struct dentry *new_dentry)
|
||||
{
|
||||
struct smack_known *isp;
|
||||
struct smk_audit_info ad;
|
||||
@@ -1226,6 +1227,7 @@ static int smack_inode_rename(struct inode *old_inode,
|
||||
|
||||
/**
|
||||
* smack_inode_permission - Smack version of permission()
|
||||
* @idmap: idmap of the mount
|
||||
* @inode: the inode in question
|
||||
* @mask: the access requested
|
||||
*
|
||||
@@ -1233,7 +1235,8 @@ static int smack_inode_rename(struct inode *old_inode,
|
||||
*
|
||||
* Returns 0 if access is permitted, an error code otherwise
|
||||
*/
|
||||
static int smack_inode_permission(struct inode *inode, int mask)
|
||||
static int smack_inode_permission(struct mnt_idmap *idmap, struct inode *inode,
|
||||
int mask)
|
||||
{
|
||||
struct superblock_smack *sbsp = smack_superblock(inode->i_sb);
|
||||
struct smk_audit_info ad;
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/mount.h>
|
||||
|
||||
#include "lsm.skel.h"
|
||||
#include "lsm_tailcall.skel.h"
|
||||
|
||||
char *CMD_ARGS[] = {"true", NULL};
|
||||
|
||||
enum {
|
||||
INODE_IDMAP_ALL = (1U << 6) - 1,
|
||||
};
|
||||
|
||||
int exec_cmd(int *monitored_pid)
|
||||
{
|
||||
int child_pid, child_status;
|
||||
@@ -30,6 +42,219 @@ int exec_cmd(int *monitored_pid)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t write_nointr(int fd, const void *buf, size_t count)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
do {
|
||||
ret = write(fd, buf, count);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_file(const char *path, const char *value)
|
||||
{
|
||||
size_t len = strlen(value);
|
||||
int fd, saved_errno = 0;
|
||||
ssize_t ret;
|
||||
|
||||
fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
ret = write_nointr(fd, value, len);
|
||||
if (ret < 0)
|
||||
saved_errno = errno;
|
||||
else if ((size_t)ret != len)
|
||||
saved_errno = EIO;
|
||||
close(fd);
|
||||
if (saved_errno) {
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_userns_file(pid_t pid, const char *name, const char *value)
|
||||
{
|
||||
char path[64];
|
||||
int len;
|
||||
|
||||
len = snprintf(path, sizeof(path), "/proc/%d/%s", pid, name);
|
||||
if (len < 0 || (size_t)len >= sizeof(path)) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return write_file(path, value);
|
||||
}
|
||||
|
||||
static int create_userns_fd(void)
|
||||
{
|
||||
char path[64];
|
||||
pid_t pid, waited;
|
||||
int fd = -1, len, saved_errno, status;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return -1;
|
||||
if (pid == 0) {
|
||||
if (unshare(CLONE_NEWUSER))
|
||||
_exit(1);
|
||||
raise(SIGSTOP);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
do {
|
||||
waited = waitpid(pid, &status, WUNTRACED);
|
||||
} while (waited < 0 && errno == EINTR);
|
||||
if (waited != pid)
|
||||
goto out;
|
||||
if (!WIFSTOPPED(status)) {
|
||||
pid = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* A one-entry map is identity for root but remains distinct from nop_mnt_idmap. */
|
||||
if (write_userns_file(pid, "setgroups", "deny") && errno != ENOENT)
|
||||
goto out;
|
||||
if (write_userns_file(pid, "uid_map", "0 0 1") ||
|
||||
write_userns_file(pid, "gid_map", "0 0 1"))
|
||||
goto out;
|
||||
|
||||
len = snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
|
||||
if (len < 0 || (size_t)len >= sizeof(path)) {
|
||||
errno = EOVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
|
||||
out:
|
||||
saved_errno = errno;
|
||||
if (pid > 0) {
|
||||
kill(pid, SIGKILL);
|
||||
do {
|
||||
waited = waitpid(pid, NULL, 0);
|
||||
} while (waited < 0 && errno == EINTR);
|
||||
}
|
||||
errno = saved_errno;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int create_idmapped_tmpfs(void)
|
||||
{
|
||||
struct mount_attr attr = {
|
||||
.attr_set = MOUNT_ATTR_IDMAP,
|
||||
};
|
||||
int fsfd = -1, mntfd = -1, saved_errno, userns_fd = -1;
|
||||
|
||||
userns_fd = create_userns_fd();
|
||||
if (userns_fd < 0)
|
||||
goto out;
|
||||
|
||||
/* A detached tmpfs avoids relying on the host test directory supporting idmaps. */
|
||||
fsfd = syscall(__NR_fsopen, "tmpfs", FSOPEN_CLOEXEC);
|
||||
if (fsfd < 0)
|
||||
goto out;
|
||||
if (syscall(__NR_fsconfig, fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0))
|
||||
goto out;
|
||||
|
||||
mntfd = syscall(__NR_fsmount, fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
if (mntfd < 0)
|
||||
goto out;
|
||||
|
||||
attr.userns_fd = userns_fd;
|
||||
if (syscall(__NR_mount_setattr, mntfd, "", AT_EMPTY_PATH, &attr,
|
||||
sizeof(attr))) {
|
||||
close(mntfd);
|
||||
mntfd = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
saved_errno = errno;
|
||||
if (fsfd >= 0)
|
||||
close(fsfd);
|
||||
if (userns_fd >= 0)
|
||||
close(userns_fd);
|
||||
errno = saved_errno;
|
||||
return mntfd;
|
||||
}
|
||||
|
||||
static int exercise_inode_idmap_hooks(int dirfd)
|
||||
{
|
||||
int fd = -1, ret = -1;
|
||||
|
||||
fd = openat(dirfd, "file", O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC,
|
||||
0600);
|
||||
if (!ASSERT_GE(fd, 0, "create"))
|
||||
goto out;
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
if (!ASSERT_OK(mkdirat(dirfd, "dir", 0700), "mkdir"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(symlinkat("target", dirfd, "symlink"), "symlink"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(linkat(dirfd, "file", dirfd, "link", 0), "link"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(mkfifoat(dirfd, "fifo", 0600), "mknod"))
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
unlinkat(dirfd, "link", 0);
|
||||
unlinkat(dirfd, "fifo", 0);
|
||||
unlinkat(dirfd, "symlink", 0);
|
||||
unlinkat(dirfd, "file", 0);
|
||||
unlinkat(dirfd, "dir", AT_REMOVEDIR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_lsm_inode_idmap(struct lsm *skel)
|
||||
{
|
||||
char tmpdir[] = "/var/tmp/test_lsm_idmap.XXXXXX";
|
||||
__u32 expected = INODE_IDMAP_ALL;
|
||||
int dirfd = -1, idmapped_dirfd = -1;
|
||||
int ret = -1;
|
||||
|
||||
if (!ASSERT_OK_PTR(mkdtemp(tmpdir), "mkdtemp"))
|
||||
return -1;
|
||||
|
||||
dirfd = open(tmpdir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
||||
if (!ASSERT_GE(dirfd, 0, "open_tmpdir"))
|
||||
goto out;
|
||||
|
||||
idmapped_dirfd = create_idmapped_tmpfs();
|
||||
if (!ASSERT_GE(idmapped_dirfd, 0, "create_idmapped_tmpfs"))
|
||||
goto out;
|
||||
|
||||
skel->bss->inode_identity_idmap_seen = 0;
|
||||
skel->bss->inode_idmapped_mount_seen = 0;
|
||||
|
||||
if (!ASSERT_OK(exercise_inode_idmap_hooks(dirfd), "identity_idmap"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(exercise_inode_idmap_hooks(idmapped_dirfd),
|
||||
"idmapped_mount"))
|
||||
goto out;
|
||||
|
||||
if (!ASSERT_EQ(skel->bss->inode_identity_idmap_seen, expected,
|
||||
"inode_identity_idmap_seen"))
|
||||
goto out;
|
||||
ret = ASSERT_EQ(skel->bss->inode_idmapped_mount_seen, expected,
|
||||
"inode_idmapped_mount_seen") ? 0 : -1;
|
||||
|
||||
out:
|
||||
if (idmapped_dirfd >= 0)
|
||||
close(idmapped_dirfd);
|
||||
if (dirfd >= 0)
|
||||
close(dirfd);
|
||||
rmdir(tmpdir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_lsm(struct lsm *skel)
|
||||
{
|
||||
struct bpf_link *link;
|
||||
@@ -53,6 +278,10 @@ static int test_lsm(struct lsm *skel)
|
||||
|
||||
skel->bss->monitored_pid = getpid();
|
||||
|
||||
err = test_lsm_inode_idmap(skel);
|
||||
if (!ASSERT_OK(err, "test_lsm_inode_idmap"))
|
||||
return err;
|
||||
|
||||
err = stack_mprotect();
|
||||
if (!ASSERT_EQ(err, -1, "stack_mprotect") ||
|
||||
!ASSERT_EQ(errno, EPERM, "stack_mprotect"))
|
||||
@@ -71,6 +300,8 @@ static int test_lsm(struct lsm *skel)
|
||||
skel->bss->copy_test = 0;
|
||||
skel->bss->bprm_count = 0;
|
||||
skel->bss->mprotect_count = 0;
|
||||
skel->bss->inode_identity_idmap_seen = 0;
|
||||
skel->bss->inode_idmapped_mount_seen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,85 @@ char _license[] SEC("license") = "GPL";
|
||||
int monitored_pid = 0;
|
||||
int mprotect_count = 0;
|
||||
int bprm_count = 0;
|
||||
__u32 inode_identity_idmap_seen = 0;
|
||||
__u32 inode_idmapped_mount_seen = 0;
|
||||
|
||||
enum {
|
||||
INODE_IDMAP_CREATE = 1U << 0,
|
||||
INODE_IDMAP_LINK = 1U << 1,
|
||||
INODE_IDMAP_SYMLINK = 1U << 2,
|
||||
INODE_IDMAP_MKDIR = 1U << 3,
|
||||
INODE_IDMAP_MKNOD = 1U << 4,
|
||||
INODE_IDMAP_PERMISSION = 1U << 5,
|
||||
};
|
||||
|
||||
static __always_inline bool is_monitored_idmap(struct mnt_idmap *idmap)
|
||||
{
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
|
||||
return monitored_pid == pid && idmap;
|
||||
}
|
||||
|
||||
static __always_inline bool is_identity_idmap(struct mnt_idmap *idmap)
|
||||
{
|
||||
return idmap->uid_map.nr_extents == 0 &&
|
||||
idmap->gid_map.nr_extents == 0;
|
||||
}
|
||||
|
||||
static __always_inline int record_inode_idmap(struct mnt_idmap *idmap,
|
||||
__u32 hook, int ret)
|
||||
{
|
||||
if (ret || !is_monitored_idmap(idmap))
|
||||
return ret;
|
||||
if (is_identity_idmap(idmap))
|
||||
inode_identity_idmap_seen |= hook;
|
||||
else
|
||||
inode_idmapped_mount_seen |= hook;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_create")
|
||||
int BPF_PROG(test_inode_create, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, int ret)
|
||||
{
|
||||
return record_inode_idmap(idmap, INODE_IDMAP_CREATE, ret);
|
||||
}
|
||||
|
||||
SEC("lsm/inode_link")
|
||||
int BPF_PROG(test_inode_link, struct mnt_idmap *idmap,
|
||||
struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *new_dentry, int ret)
|
||||
{
|
||||
return record_inode_idmap(idmap, INODE_IDMAP_LINK, ret);
|
||||
}
|
||||
|
||||
SEC("lsm/inode_symlink")
|
||||
int BPF_PROG(test_inode_symlink, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, const char *old_name, int ret)
|
||||
{
|
||||
return record_inode_idmap(idmap, INODE_IDMAP_SYMLINK, ret);
|
||||
}
|
||||
|
||||
SEC("lsm/inode_mkdir")
|
||||
int BPF_PROG(test_inode_mkdir, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, int ret)
|
||||
{
|
||||
return record_inode_idmap(idmap, INODE_IDMAP_MKDIR, ret);
|
||||
}
|
||||
|
||||
SEC("lsm/inode_mknod")
|
||||
int BPF_PROG(test_inode_mknod, struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t dev, int ret)
|
||||
{
|
||||
return record_inode_idmap(idmap, INODE_IDMAP_MKNOD, ret);
|
||||
}
|
||||
|
||||
SEC("lsm/inode_permission")
|
||||
int BPF_PROG(test_inode_permission, struct mnt_idmap *idmap,
|
||||
struct inode *inode, int mask, int ret)
|
||||
{
|
||||
return record_inode_idmap(idmap, INODE_IDMAP_PERMISSION, ret);
|
||||
}
|
||||
|
||||
SEC("lsm/file_mprotect")
|
||||
int BPF_PROG(test_int_hook, struct vm_area_struct *vma,
|
||||
|
||||
@@ -4146,6 +4146,9 @@ TEST_F_FORK(layout1, o_path_ftruncate_and_ioctl)
|
||||
ASSERT_EQ(0, close(fd));
|
||||
}
|
||||
|
||||
/* Arbitrary command with nonzero bits in both 16-bit halves. */
|
||||
static const unsigned int unknown_ioctl_cmd = 0xc00ffeee;
|
||||
|
||||
/*
|
||||
* ioctl_error - generically call the given ioctl with a pointer to a
|
||||
* sufficiently large zeroed-out memory region.
|
||||
@@ -4249,7 +4252,7 @@ TEST_F_FORK(layout1, blanket_permitted_ioctls)
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FS_IOC_ZERO_RANGE));
|
||||
|
||||
/* Default case is also blocked. */
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, 0xc00ffeee));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, unknown_ioctl_cmd));
|
||||
|
||||
ASSERT_EQ(0, close(fd));
|
||||
}
|
||||
@@ -7943,6 +7946,7 @@ TEST_F(audit_layout1, truncate)
|
||||
EXPECT_EQ(1, records.domain);
|
||||
}
|
||||
|
||||
/* Checks that audit records preserve every ioctl command bit. */
|
||||
TEST_F(audit_layout1, ioctl_dev)
|
||||
{
|
||||
struct audit_records records;
|
||||
@@ -7952,10 +7956,10 @@ TEST_F(audit_layout1, ioctl_dev)
|
||||
|
||||
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
ASSERT_LE(0, fd);
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, FIONREAD));
|
||||
EXPECT_EQ(EACCES, ioctl_error(_metadata, fd, unknown_ioctl_cmd));
|
||||
EXPECT_EQ(0, matches_log_fs_extra(_metadata, self->audit_fd,
|
||||
"fs\\.ioctl_dev", "/dev/null",
|
||||
" ioctlcmd=0x541b"));
|
||||
" ioctlcmd=0xc00ffeee"));
|
||||
|
||||
EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
|
||||
EXPECT_EQ(0, records.access);
|
||||
|
||||
Reference in new issue
Block a user