summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/admin-guide/LSM/index.rst20
-rw-r--r--fs/cachefiles/security.c4
-rw-r--r--fs/namei.c18
-rw-r--r--fs/namespace.c3
-rw-r--r--include/linux/cred.h17
-rw-r--r--include/linux/lsm_audit.h7
-rw-r--r--include/linux/lsm_hook_defs.h28
-rw-r--r--include/linux/lsm_hooks.h1
-rw-r--r--include/linux/ns/ns_common_types.h3
-rw-r--r--include/linux/sched.h8
-rw-r--r--include/linux/security.h82
-rw-r--r--include/uapi/linux/nsfs.h1
-rw-r--r--init/init_task.c2
-rw-r--r--kernel/auditsc.c5
-rw-r--r--kernel/cred.c5
-rw-r--r--kernel/nscommon.c17
-rw-r--r--kernel/nsproxy.c6
-rw-r--r--rust/kernel/security.rs3
-rw-r--r--security/apparmor/af_unix.c2
-rw-r--r--security/apparmor/file.c10
-rw-r--r--security/apparmor/include/af_unix.h2
-rw-r--r--security/apparmor/include/file.h4
-rw-r--r--security/apparmor/include/net.h2
-rw-r--r--security/apparmor/lsm.c4
-rw-r--r--security/apparmor/net.c2
-rw-r--r--security/lsm_audit.c8
-rw-r--r--security/lsm_init.c9
-rw-r--r--security/security.c117
-rw-r--r--security/selinux/hooks.c29
-rw-r--r--security/smack/smack_lsm.c9
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_lsm.c231
-rw-r--r--tools/testing/selftests/bpf/progs/lsm.c79
-rw-r--r--tools/testing/selftests/landlock/fs_test.c10
33 files changed, 618 insertions, 130 deletions
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index b44ef68f6e4d..9518495edfbc 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -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``.
diff --git a/fs/cachefiles/security.c b/fs/cachefiles/security.c
index fc6611886b3b..eefe5453b904 100644
--- a/fs/cachefiles/security.c
+++ b/fs/cachefiles/security.c
@@ -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);
diff --git a/fs/namei.c b/fs/namei.c
index 59c8a669081a..54ad61923548 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -658,7 +658,7 @@ int inode_permission(const 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(const struct mnt_idm
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(const struct mnt_idmap *idmap, struct dentry *dentry, umode_t mod
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(const 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);
}
/**
@@ -5271,7 +5271,7 @@ int vfs_mknod(const 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;
@@ -5408,7 +5408,7 @@ struct dentry *vfs_mkdir(const 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;
@@ -5796,7 +5796,7 @@ int vfs_symlink(const 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;
@@ -5920,7 +5920,7 @@ int vfs_link(struct dentry *old_dentry, const 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;
diff --git a/fs/namespace.c b/fs/namespace.c
index 973efee4b968..d01b5402e847 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4206,8 +4206,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);
}
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 6ef1750c93e2..49c26af37349 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -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
diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 584db296e43b..5cf0b4795065 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -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 {
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index c9561564585e..ef16e5343e09 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -121,26 +121,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, const struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode)
LSM_HOOK(void, LSM_RET_VOID, inode_post_create_tmpfile, const 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, const 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, const struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, const char *old_name)
+LSM_HOOK(int, 0, inode_mkdir, const 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, const 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, const struct mnt_idmap *idmap,
+ struct inode *inode, int mask)
LSM_HOOK(int, 0, inode_setattr, const struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr)
LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, const struct mnt_idmap *idmap,
@@ -188,7 +189,7 @@ LSM_HOOK(int, 0, inode_setintegrity, const struct inode *inode,
enum lsm_integrity_type type, const void *value, size_t size)
LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
struct kernfs_node *kn)
-LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
+LSM_HOOK(int, 0, file_permission, const struct file *file, int mask)
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
@@ -266,6 +267,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)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index c4488c4a6d8a..13621e9e233e 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -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 */
diff --git a/include/linux/ns/ns_common_types.h b/include/linux/ns/ns_common_types.h
index 6ed6b497831c..fddc62be3b62 100644
--- a/include/linux/ns/ns_common_types.h
+++ b/include/linux/ns/ns_common_types.h
@@ -118,6 +118,9 @@ struct ns_common {
unsigned int inum;
struct ns_tree;
struct rcu_head ns_rcu;
+#ifdef CONFIG_SECURITY
+ void *ns_security;
+#endif
};
#define to_ns_common(__ns) \
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 87ed6705c427..64b7581e2012 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -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. */
diff --git a/include/linux/security.h b/include/linux/security.h
index f7ff72ff956b..8b8d6b8b802f 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -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;
@@ -405,24 +407,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(const struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode);
void security_inode_post_create_tmpfile(const 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(const 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(const struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, const char *old_name);
+int security_inode_mkdir(const 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(const 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(const struct mnt_idmap *idmap, struct inode *inode,
+ int mask);
int security_inode_setattr(const struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr);
void security_inode_post_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
@@ -469,7 +475,7 @@ int security_inode_setintegrity(const struct inode *inode,
size_t size);
int security_kernfs_init_security(struct kernfs_node *kn_dir,
struct kernfs_node *kn);
-int security_file_permission(struct file *file, int mask);
+int security_file_permission(const struct file *file, int mask);
int security_file_alloc(struct file *file);
void security_file_release(struct file *file);
void security_file_free(struct file *file);
@@ -541,6 +547,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);
@@ -909,9 +918,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(const struct mnt_idmap *idmap,
+ struct inode *dir,
+ struct dentry *dentry,
+ umode_t mode)
{
return 0;
}
@@ -920,9 +930,10 @@ static inline void
security_inode_post_create_tmpfile(const 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(const struct mnt_idmap *idmap,
+ struct dentry *old_dentry,
+ struct inode *dir,
+ struct dentry *new_dentry)
{
return 0;
}
@@ -933,16 +944,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(const 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(const struct mnt_idmap *idmap,
+ struct inode *dir,
+ struct dentry *dentry,
+ int mode)
{
return 0;
}
@@ -953,9 +966,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(const struct mnt_idmap *idmap,
+ struct inode *dir,
+ struct dentry *dentry,
+ int mode, dev_t dev)
{
return 0;
}
@@ -981,7 +995,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(const struct mnt_idmap *idmap,
+ struct inode *inode, int mask)
{
return 0;
}
@@ -1139,7 +1154,7 @@ static inline int security_inode_copy_up_xattr(struct dentry *src, const char *n
return -EOPNOTSUPP;
}
-static inline int security_file_permission(struct file *file, int mask)
+static inline int security_file_permission(const struct file *file, int mask)
{
return 0;
}
@@ -1438,6 +1453,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)
{
diff --git a/include/uapi/linux/nsfs.h b/include/uapi/linux/nsfs.h
index 007fed5971b4..e5909266ec3f 100644
--- a/include/uapi/linux/nsfs.h
+++ b/include/uapi/linux/nsfs.h
@@ -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
};
diff --git a/init/init_task.c b/init/init_task.c
index adb207cd987c..ce7c2b07d855 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -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,
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 2b9ce0b52511..1b9cf25291e0 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -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];
diff --git a/kernel/cred.c b/kernel/cred.c
index 3df4e15bd67f..d01df40a9a38 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -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);
}
/**
diff --git a/kernel/nscommon.c b/kernel/nscommon.c
index 3166c1fd844a..e72426bba29a 100644
--- a/kernel/nscommon.c
+++ b/kernel/nscommon.c
@@ -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)
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index d9d3d5973bf5..0f1b208d8eef 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -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);
}
diff --git a/rust/kernel/security.rs b/rust/kernel/security.rs
index 9d271695265f..4dc3eba6ce84 100644
--- a/rust/kernel/security.rs
+++ b/rust/kernel/security.rs
@@ -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) })?;
diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c
index b908e744818c..de80561348fc 100644
--- a/security/apparmor/af_unix.c
+++ b/security/apparmor/af_unix.c
@@ -715,7 +715,7 @@ static void update_peer_ctx(struct sock *sk, struct aa_sk_ctx *ctx,
* boundaries. Otherwise cached info off file is sufficient
*/
int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
- const char *op, u32 request, struct file *file)
+ const char *op, u32 request, const struct file *file)
{
struct socket *sock = (struct socket *) file->private_data;
struct sockaddr_un *addr, *peer_addr;
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 3e74b613db32..305fdb341ddb 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -489,7 +489,7 @@ static void update_file_ctx(struct aa_file_ctx *fctx, struct aa_label *label,
static int __file_path_perm(const char *op, const struct cred *subj_cred,
struct aa_label *label,
- struct aa_label *flabel, struct file *file,
+ struct aa_label *flabel, const struct file *file,
u32 request, u32 denied, bool in_atomic)
{
struct aa_profile *profile;
@@ -550,7 +550,7 @@ static int __file_path_perm(const char *op, const struct cred *subj_cred,
static int __file_sock_perm(const char *op, const struct cred *subj_cred,
struct aa_label *label,
- struct aa_label *flabel, struct file *file,
+ struct aa_label *flabel, const struct file *file,
u32 request, u32 denied)
{
int error;
@@ -579,7 +579,7 @@ static bool __file_is_delegated(struct aa_label *obj_label)
return unconfined(obj_label);
}
-static bool __is_unix_file(struct file *file)
+static bool __is_unix_file(const struct file *file)
{
struct socket *sock = (struct socket *) file->private_data;
@@ -595,7 +595,7 @@ static bool __is_unix_file(struct file *file)
return false;
}
-static bool __unix_needs_revalidation(struct file *file, struct aa_label *label,
+static bool __unix_needs_revalidation(const struct file *file, struct aa_label *label,
u32 request)
{
struct socket *sock = (struct socket *) file->private_data;
@@ -624,7 +624,7 @@ static bool __unix_needs_revalidation(struct file *file, struct aa_label *label,
* Returns: %0 if access allowed else error
*/
int aa_file_perm(const char *op, const struct cred *subj_cred,
- struct aa_label *label, struct file *file,
+ struct aa_label *label, const struct file *file,
u32 request, bool in_atomic)
{
struct aa_file_ctx *fctx;
diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h
index 4a62e600d82b..62e3269273eb 100644
--- a/security/apparmor/include/af_unix.h
+++ b/security/apparmor/include/af_unix.h
@@ -50,6 +50,6 @@ int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock,
int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level,
int optname);
int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label,
- const char *op, u32 request, struct file *file);
+ const char *op, u32 request, const struct file *file);
#endif /* __AA_AF_UNIX_H */
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 1614c07fc53e..d69a0611a981 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -29,7 +29,7 @@ struct path;
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \
AA_EXEC_MMAP | AA_MAY_LINK)
-static inline struct aa_file_ctx *file_ctx(struct file *file)
+static inline struct aa_file_ctx *file_ctx(const struct file *file)
{
return file->f_security + apparmor_blob_sizes.lbs_file;
}
@@ -97,7 +97,7 @@ int aa_path_link(const struct cred *subj_cred, struct aa_label *label,
struct dentry *new_dentry);
int aa_file_perm(const char *op, const struct cred *subj_cred,
- struct aa_label *label, struct file *file,
+ struct aa_label *label, const struct file *file,
u32 request, bool in_atomic);
void aa_inherit_files(const struct cred *cred, struct files_struct *files);
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index 375341929cb6..e387b50920b3 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -112,7 +112,7 @@ int aa_label_sk_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request, const struct sock *sk);
int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request,
- struct file *file);
+ const struct file *file);
int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, const struct sock *sk);
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index c73681d820a0..9d572cf8486b 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -525,7 +525,7 @@ static void apparmor_file_free_security(struct file *file)
aa_put_label(rcu_access_pointer(ctx->label));
}
-static int common_file_perm(const char *op, struct file *file, u32 mask)
+static int common_file_perm(const char *op, const struct file *file, u32 mask)
{
struct aa_label *label;
bool needput;
@@ -543,7 +543,7 @@ static int apparmor_file_receive(struct file *file)
return common_file_perm(OP_FRECEIVE, file, aa_map_file_to_perms(file));
}
-static int apparmor_file_permission(struct file *file, int mask)
+static int apparmor_file_permission(const struct file *file, int mask)
{
return common_file_perm(OP_FPERM, file, mask);
}
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index a333e6aff926..6048e3c5533d 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -325,7 +325,7 @@ int aa_sk_perm(const char *op, u32 request, const struct sock *sk)
int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
- const char *op, u32 request, struct file *file)
+ const char *op, u32 request, const struct file *file)
{
struct socket *sock = (struct socket *) file->private_data;
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 737f5a263a8f..955b2e7b2c8e 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -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) */
}
diff --git a/security/lsm_init.c b/security/lsm_init.c
index a1ad641811de..8d5ecea99c46 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -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");
diff --git a/security/security.c b/security/security.c
index f476b0736c9a..da8d48cd0a2a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -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>
@@ -382,6 +383,19 @@ static int lsm_superblock_alloc(struct super_block *sb)
}
/**
+ * 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
* @uctx_len: available uctx size (input), used uctx size (output)
@@ -1639,6 +1653,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
@@ -1647,12 +1662,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(const 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);
@@ -1673,6 +1688,7 @@ void security_inode_post_create_tmpfile(const 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
@@ -1681,12 +1697,12 @@ void security_inode_post_create_tmpfile(const 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(const 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);
}
/**
@@ -1707,6 +1723,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
@@ -1715,16 +1732,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(const 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
@@ -1734,11 +1752,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(const 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);
@@ -1760,6 +1779,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
@@ -1772,12 +1792,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(const 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);
}
/**
@@ -1848,6 +1868,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
*
@@ -1860,11 +1881,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(const 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);
}
/**
@@ -2412,7 +2434,7 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
*
* Return: Returns 0 if permission is granted.
*/
-int security_file_permission(struct file *file, int mask)
+int security_file_permission(const struct file *file, int mask)
{
return call_int_hook(file_permission, file, mask);
}
@@ -2511,9 +2533,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);
}
@@ -3383,6 +3404,64 @@ int security_create_user_ns(const struct cred *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
* @flag: requested permissions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 73496690ab52..88f3a5858c2a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3110,12 +3110,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(const 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(const struct mnt_idmap *idmap, struct dentry *old_dentry,
+ struct inode *dir, struct dentry *new_dentry)
{
return may_link(dir, old_dentry, MAY_LINK);
}
@@ -3125,12 +3127,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(const 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(const struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mask)
{
return may_create(dir, dentry, SECCLASS_DIR);
}
@@ -3140,7 +3144,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(const 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));
}
@@ -3272,13 +3277,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(const struct mnt_idmap *idmap,
+ struct inode *inode, int requested)
{
int mask;
u32 perms;
@@ -3835,7 +3842,7 @@ static int selinux_kernfs_init_security(struct kernfs_node *kn_dir,
/* file security operations */
-static int selinux_revalidate_file_permission(struct file *file, int mask)
+static int selinux_revalidate_file_permission(const struct file *file, int mask)
{
const struct cred *cred = current_cred();
struct inode *inode = file_inode(file);
@@ -3848,7 +3855,7 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
file_mask_to_av(inode->i_mode, mask));
}
-static int selinux_file_permission(struct file *file, int mask)
+static int selinux_file_permission(const struct file *file, int mask)
{
struct inode *inode = file_inode(file);
struct file_security_struct *fsec = selinux_file(file);
@@ -3952,7 +3959,7 @@ static void selinux_backing_file_free(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);
@@ -4023,14 +4030,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;
}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index bb78569b3d5b..28a8e12603ce 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -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(const 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(const struct mnt_idmap *idmap, struct inode *inode,
+ int mask)
{
struct superblock_smack *sbsp = smack_superblock(inode->i_sb);
struct smk_audit_info ad;
diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
index d7495efd4a56..c0ae813698ae 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
@@ -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;
}
diff --git a/tools/testing/selftests/bpf/progs/lsm.c b/tools/testing/selftests/bpf/progs/lsm.c
index 7441d66c080c..e210e07d3a37 100644
--- a/tools/testing/selftests/bpf/progs/lsm.c
+++ b/tools/testing/selftests/bpf/progs/lsm.c
@@ -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,
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index 6e979cef884d..fd20a2b3c0a5 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -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);