[PATCH 1/2] security, lsm: Introduce security_create_user_ns()

From: Frederick Lawler
Date: Tue Jun 21 2022 - 19:39:59 EST


Preventing user namespace (privileged or otherwise) creation comes in a
few of forms in order of granularity:

1. /proc/sys/user/max_user_namespaces sysctl
2. OS specific patch(es)
3. CONFIG_USER_NS

To block a task based on its attributes, the LSM hook cred_prepare is a
good candidate for use because it provides more granular control, and
it is called before create_user_ns():

cred = prepare_creds()
security_prepare_creds()
call_int_hook(cred_prepare, ...
if (cred)
create_user_ns(cred)

Since security_prepare_creds() is meant for LSMs to copy and prepare
credentials, access control is an unintended use of the hook. Therefore
introduce a new function security_create_user_ns() with an accompanying
create_user_ns LSM hook.

This hook takes the prepared creds and the newly created user namespace
for LSM authors to write policy against. On success, the new namespace
is applied to credentials, otherwise an error is returned.

Signed-off-by: Frederick Lawler <fred@xxxxxxxxxxxxxx>
---
include/linux/lsm_hook_defs.h | 2 ++
include/linux/lsm_hooks.h | 5 +++++
include/linux/security.h | 8 ++++++++
kernel/user_namespace.c | 5 +++++
security/security.c | 6 ++++++
5 files changed, 26 insertions(+)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index eafa1d2489fd..bd9b38db4d03 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -223,6 +223,8 @@ LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5)
LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p,
struct inode *inode)
+LSM_HOOK(int, 0, create_user_ns, const struct cred *new,
+ const struct user_namespace *new_userns)
LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
LSM_HOOK(void, LSM_RET_VOID, ipc_getsecid, struct kern_ipc_perm *ipcp,
u32 *secid)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 91c8146649f5..1356a792a6bd 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -799,6 +799,11 @@
* security attributes, e.g. for /proc/pid inodes.
* @p contains the task_struct for the task.
* @inode contains the inode structure for the inode.
+ * @create_user_ns:
+ * Check permission prior to assigning the new namespace to @cred->user_ns.
+ * @cred points to prepared creds.
+ * @new_userns points to the newly created user namespace.
+ * Return 0 if successful, otherwise < 0 error code.
*
* Security hooks for Netlink messaging.
*
diff --git a/include/linux/security.h b/include/linux/security.h
index 7fc4e9f49f54..a656dbe7b65a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -435,6 +435,8 @@ int security_task_kill(struct task_struct *p, struct kernel_siginfo *info,
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,
+ const struct user_namespace *new_userns);
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
int security_msg_msg_alloc(struct msg_msg *msg);
@@ -1185,6 +1187,12 @@ static inline int security_task_prctl(int option, unsigned long arg2,
static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
{ }

+static inline int security_create_user_ns(const struct cred *cred,
+ const struct user_namespace *new_userns)
+{
+ return 0;
+}
+
static inline int security_ipc_permission(struct kern_ipc_perm *ipcp,
short flag)
{
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 5481ba44a8d6..8c5e5592a503 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -9,6 +9,7 @@
#include <linux/highuid.h>
#include <linux/cred.h>
#include <linux/securebits.h>
+#include <linux/security.h>
#include <linux/keyctl.h>
#include <linux/key-type.h>
#include <keys/user-type.h>
@@ -153,6 +154,10 @@ int create_user_ns(struct cred *new)
if (!setup_userns_sysctls(ns))
goto fail_keyring;

+ ret = security_create_user_ns(new, ns);
+ if (ret < 0)
+ goto fail_keyring;
+
set_cred_user_ns(new, ns);
return 0;
fail_keyring:
diff --git a/security/security.c b/security/security.c
index 188b8f782220..d6b1751805ca 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1903,6 +1903,12 @@ void security_task_to_inode(struct task_struct *p, struct inode *inode)
call_void_hook(task_to_inode, p, inode);
}

+int security_create_user_ns(const struct cred *cred,
+ const struct user_namespace *new_userns)
+{
+ return call_int_hook(create_user_ns, 0, cred, new_userns);
+}
+
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
{
return call_int_hook(ipc_permission, 0, ipcp, flag);
--
2.30.2