Commit 7cd4c5c2 authored by Frederick Lawler's avatar Frederick Lawler Committed by Paul Moore

security, lsm: Introduce security_create_user_ns()

User namespaces are an effective tool to allow programs to run with
permission without requiring the need for a program to run as root. User
namespaces may also be used as a sandboxing technique. However, attackers
sometimes leverage user namespaces as an initial attack vector to perform
some exploit. [1,2,3]

While it is not the unprivileged user namespace functionality, which
causes the kernel to be exploitable, users/administrators might want to
more granularly limit or at least monitor how various processes use this
functionality, while vulnerable kernel subsystems are being patched.

Preventing user namespace already creation comes in a few of forms in
order of granularity:

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

To block a task based on its attributes, the LSM hook cred_prepare is a
decent 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. [4]
Further, security_prepare_creds() will always return a ENOMEM if the
hook returns any non-zero error code.

This hook also does not handle the clone3 case which requires us to
access a user space pointer to know if we're in the CLONE_NEW_USER
call path which may be subject to a TOCTTOU attack.

Lastly, cred_prepare is called in many call paths, and a targeted hook
further limits the frequency of calls which is a beneficial outcome.
Therefore introduce a new function security_create_user_ns() with an
accompanying userns_create LSM hook.

With the new userns_create hook, users will have more control over the
observability and access control over user namespace creation. Users
should expect that normal operation of user namespaces will behave as
usual, and only be impacted when controls are implemented by users or
administrators.

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

Links:
1. https://nvd.nist.gov/vuln/detail/CVE-2022-0492
2. https://nvd.nist.gov/vuln/detail/CVE-2022-25636
3. https://nvd.nist.gov/vuln/detail/CVE-2022-34918
4. https://lore.kernel.org/all/1c4b1c0d-12f6-6e9e-a6a3-cdce7418110c@schaufler-ca.com/Reviewed-by: default avatarChristian Brauner (Microsoft) <brauner@kernel.org>
Reviewed-by: default avatarKP Singh <kpsingh@kernel.org>
Signed-off-by: default avatarFrederick Lawler <fred@cloudflare.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent 4847c0eb
...@@ -224,6 +224,7 @@ LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2, ...@@ -224,6 +224,7 @@ LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5) unsigned long arg3, unsigned long arg4, unsigned long arg5)
LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p, LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p,
struct inode *inode) struct inode *inode)
LSM_HOOK(int, 0, userns_create, const struct cred *cred)
LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag) 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, LSM_HOOK(void, LSM_RET_VOID, ipc_getsecid, struct kern_ipc_perm *ipcp,
u32 *secid) u32 *secid)
......
...@@ -806,6 +806,10 @@ ...@@ -806,6 +806,10 @@
* security attributes, e.g. for /proc/pid inodes. * security attributes, e.g. for /proc/pid inodes.
* @p contains the task_struct for the task. * @p contains the task_struct for the task.
* @inode contains the inode structure for the inode. * @inode contains the inode structure for the inode.
* @userns_create:
* Check permission prior to creating a new user namespace.
* @cred points to prepared creds.
* Return 0 if successful, otherwise < 0 error code.
* *
* Security hooks for Netlink messaging. * Security hooks for Netlink messaging.
* *
......
...@@ -437,6 +437,7 @@ int security_task_kill(struct task_struct *p, struct kernel_siginfo *info, ...@@ -437,6 +437,7 @@ int security_task_kill(struct task_struct *p, struct kernel_siginfo *info,
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5); unsigned long arg4, unsigned long arg5);
void security_task_to_inode(struct task_struct *p, struct inode *inode); void security_task_to_inode(struct task_struct *p, struct inode *inode);
int security_create_user_ns(const struct cred *cred);
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag); int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag);
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid); void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid);
int security_msg_msg_alloc(struct msg_msg *msg); int security_msg_msg_alloc(struct msg_msg *msg);
...@@ -1194,6 +1195,11 @@ static inline int security_task_prctl(int option, unsigned long arg2, ...@@ -1194,6 +1195,11 @@ 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 void security_task_to_inode(struct task_struct *p, struct inode *inode)
{ } { }
static inline int security_create_user_ns(const struct cred *cred)
{
return 0;
}
static inline int security_ipc_permission(struct kern_ipc_perm *ipcp, static inline int security_ipc_permission(struct kern_ipc_perm *ipcp,
short flag) short flag)
{ {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/highuid.h> #include <linux/highuid.h>
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/securebits.h> #include <linux/securebits.h>
#include <linux/security.h>
#include <linux/keyctl.h> #include <linux/keyctl.h>
#include <linux/key-type.h> #include <linux/key-type.h>
#include <keys/user-type.h> #include <keys/user-type.h>
...@@ -113,6 +114,10 @@ int create_user_ns(struct cred *new) ...@@ -113,6 +114,10 @@ int create_user_ns(struct cred *new)
!kgid_has_mapping(parent_ns, group)) !kgid_has_mapping(parent_ns, group))
goto fail_dec; goto fail_dec;
ret = security_create_user_ns(new);
if (ret < 0)
goto fail_dec;
ret = -ENOMEM; ret = -ENOMEM;
ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL); ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL);
if (!ns) if (!ns)
......
...@@ -1909,6 +1909,11 @@ void security_task_to_inode(struct task_struct *p, struct inode *inode) ...@@ -1909,6 +1909,11 @@ void security_task_to_inode(struct task_struct *p, struct inode *inode)
call_void_hook(task_to_inode, p, inode); call_void_hook(task_to_inode, p, inode);
} }
int security_create_user_ns(const struct cred *cred)
{
return call_int_hook(userns_create, 0, cred);
}
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag) int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
{ {
return call_int_hook(ipc_permission, 0, ipcp, flag); return call_int_hook(ipc_permission, 0, ipcp, flag);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment