diff mbox series

[v4,1/4] security, lsm: Introduce security_create_user_ns()

Message ID 20220801180146.1157914-2-fred@cloudflare.com (mailing list archive)
State Superseded
Headers show
Series Introduce security_create_user_ns() | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
netdev/tree_selection success Guessed tree name to be net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 7269 this patch: 7269
netdev/cc_maintainers warning 4 maintainers not CCed: shakeelb@google.com akpm@linux-foundation.org vasily.averin@linux.dev tkjos@google.com
netdev/build_clang success Errors and warnings before: 1587 this patch: 1587
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 7534 this patch: 7534
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 63 lines checked
netdev/kdoc success Errors and warnings before: 21 this patch: 21
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-1 success Logs for Kernel LATEST on ubuntu-latest with gcc
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Kernel LATEST on ubuntu-latest with llvm-16
bpf/vmtest-bpf-next-VM_Test-3 fail Logs for Kernel LATEST on z15 with gcc

Commit Message

Frederick Lawler Aug. 1, 2022, 6:01 p.m. UTC
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
userns_create LSM hook.

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.

Signed-off-by: Frederick Lawler <fred@cloudflare.com>
Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org>

---
Changes since v3:
- No changes
Changes since v2:
- Rename create_user_ns hook to userns_create
Changes since v1:
- Changed commit wording
- Moved execution to be after id mapping check
- Changed signature to only accept a const struct cred *
---
 include/linux/lsm_hook_defs.h | 1 +
 include/linux/lsm_hooks.h     | 4 ++++
 include/linux/security.h      | 6 ++++++
 kernel/user_namespace.c       | 5 +++++
 security/security.c           | 5 +++++
 5 files changed, 21 insertions(+)

Comments

KP Singh Aug. 2, 2022, 9:47 p.m. UTC | #1
On Mon, Aug 1, 2022 at 8:02 PM Frederick Lawler <fred@cloudflare.com> wrote:
>
> 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
> userns_create LSM hook.
>
> 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.
>
> Signed-off-by: Frederick Lawler <fred@cloudflare.com>
> Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org>

Reviewed-by: KP Singh <kpsingh@kernel.org>

This looks useful, and I would also like folks to consider the
observability aspects of BPF LSM as
brought up here:

https://lore.kernel.org/all/CAEiveUdPhEPAk7Y0ZXjPsD=Vb5hn453CHzS9aG-tkyRa8bf_eg@mail.gmail.com/

Frederick, what about adding the observability aspects to the commit
description as well.

- KP

>
> ---
> Changes since v3:
> - No changes
> Changes since v2:
> - Rename create_user_ns hook to userns_create
> Changes since v1:
> - Changed commit wording
> - Moved execution to be after id mapping check
> - Changed signature to only accept a const struct cred *
> ---
>  include/linux/lsm_hook_defs.h | 1 +
>  include/linux/lsm_hooks.h     | 4 ++++
>  include/linux/security.h      | 6 ++++++
>  kernel/user_namespace.c       | 5 +++++
>  security/security.c           | 5 +++++
>  5 files changed, 21 insertions(+)
>
> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
> index eafa1d2489fd..7ff93cb8ca8d 100644
> --- a/include/linux/lsm_hook_defs.h
> +++ b/include/linux/lsm_hook_defs.h
> @@ -223,6 +223,7 @@ 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, userns_create, const struct cred *cred)
>  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..54fe534d0e01 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -799,6 +799,10 @@
>   *     security attributes, e.g. for /proc/pid inodes.
>   *     @p contains the task_struct for the task.
>   *     @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.
>   *
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 7fc4e9f49f54..a195bf33246a 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -435,6 +435,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,
>                         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_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 +1186,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 int security_create_user_ns(const struct cred *cred)
> +{
> +       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..3f464bbda0e9 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>
> @@ -113,6 +114,10 @@ int create_user_ns(struct cred *new)
>             !kgid_has_mapping(parent_ns, group))
>                 goto fail_dec;
>
> +       ret = security_create_user_ns(new);
> +       if (ret < 0)
> +               goto fail_dec;
> +
>         ret = -ENOMEM;
>         ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL);
>         if (!ns)
> diff --git a/security/security.c b/security/security.c
> index 188b8f782220..ec9b4696e86c 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -1903,6 +1903,11 @@ 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)
> +{
> +       return call_int_hook(userns_create, 0, cred);
> +}
> +
>  int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
>  {
>         return call_int_hook(ipc_permission, 0, ipcp, flag);
> --
> 2.30.2
>
Frederick Lawler Aug. 3, 2022, 1:13 p.m. UTC | #2
On 8/2/22 4:47 PM, KP Singh wrote:
> On Mon, Aug 1, 2022 at 8:02 PM Frederick Lawler <fred@cloudflare.com> wrote:
>>
>> 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
>> userns_create LSM hook.
>>
>> 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.
>>
>> Signed-off-by: Frederick Lawler <fred@cloudflare.com>
>> Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org>
> 
> Reviewed-by: KP Singh <kpsingh@kernel.org>
> 
> This looks useful, and I would also like folks to consider the
> observability aspects of BPF LSM as
> brought up here:
> 
> https://lore.kernel.org/all/CAEiveUdPhEPAk7Y0ZXjPsD=Vb5hn453CHzS9aG-tkyRa8bf_eg@mail.gmail.com/
> 
> Frederick, what about adding the observability aspects to the commit
> description as well.

Agreed. I'll include that in v5.

> 
> - KP
> 
>>
>> ---
>> Changes since v3:
>> - No changes
>> Changes since v2:
>> - Rename create_user_ns hook to userns_create
>> Changes since v1:
>> - Changed commit wording
>> - Moved execution to be after id mapping check
>> - Changed signature to only accept a const struct cred *
>> ---
>>   include/linux/lsm_hook_defs.h | 1 +
>>   include/linux/lsm_hooks.h     | 4 ++++
>>   include/linux/security.h      | 6 ++++++
>>   kernel/user_namespace.c       | 5 +++++
>>   security/security.c           | 5 +++++
>>   5 files changed, 21 insertions(+)
>>
>> diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
>> index eafa1d2489fd..7ff93cb8ca8d 100644
>> --- a/include/linux/lsm_hook_defs.h
>> +++ b/include/linux/lsm_hook_defs.h
>> @@ -223,6 +223,7 @@ 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, userns_create, const struct cred *cred)
>>   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..54fe534d0e01 100644
>> --- a/include/linux/lsm_hooks.h
>> +++ b/include/linux/lsm_hooks.h
>> @@ -799,6 +799,10 @@
>>    *     security attributes, e.g. for /proc/pid inodes.
>>    *     @p contains the task_struct for the task.
>>    *     @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.
>>    *
>> diff --git a/include/linux/security.h b/include/linux/security.h
>> index 7fc4e9f49f54..a195bf33246a 100644
>> --- a/include/linux/security.h
>> +++ b/include/linux/security.h
>> @@ -435,6 +435,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,
>>                          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_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 +1186,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 int security_create_user_ns(const struct cred *cred)
>> +{
>> +       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..3f464bbda0e9 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>
>> @@ -113,6 +114,10 @@ int create_user_ns(struct cred *new)
>>              !kgid_has_mapping(parent_ns, group))
>>                  goto fail_dec;
>>
>> +       ret = security_create_user_ns(new);
>> +       if (ret < 0)
>> +               goto fail_dec;
>> +
>>          ret = -ENOMEM;
>>          ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL);
>>          if (!ns)
>> diff --git a/security/security.c b/security/security.c
>> index 188b8f782220..ec9b4696e86c 100644
>> --- a/security/security.c
>> +++ b/security/security.c
>> @@ -1903,6 +1903,11 @@ 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)
>> +{
>> +       return call_int_hook(userns_create, 0, cred);
>> +}
>> +
>>   int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
>>   {
>>          return call_int_hook(ipc_permission, 0, ipcp, flag);
>> --
>> 2.30.2
>>
diff mbox series

Patch

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index eafa1d2489fd..7ff93cb8ca8d 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -223,6 +223,7 @@  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, userns_create, const struct cred *cred)
 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..54fe534d0e01 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -799,6 +799,10 @@ 
  *	security attributes, e.g. for /proc/pid inodes.
  *	@p contains the task_struct for the task.
  *	@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.
  *
diff --git a/include/linux/security.h b/include/linux/security.h
index 7fc4e9f49f54..a195bf33246a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -435,6 +435,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,
 			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_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 +1186,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 int security_create_user_ns(const struct cred *cred)
+{
+	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..3f464bbda0e9 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>
@@ -113,6 +114,10 @@  int create_user_ns(struct cred *new)
 	    !kgid_has_mapping(parent_ns, group))
 		goto fail_dec;
 
+	ret = security_create_user_ns(new);
+	if (ret < 0)
+		goto fail_dec;
+
 	ret = -ENOMEM;
 	ns = kmem_cache_zalloc(user_ns_cachep, GFP_KERNEL);
 	if (!ns)
diff --git a/security/security.c b/security/security.c
index 188b8f782220..ec9b4696e86c 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1903,6 +1903,11 @@  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)
+{
+	return call_int_hook(userns_create, 0, cred);
+}
+
 int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
 {
 	return call_int_hook(ipc_permission, 0, ipcp, flag);