mbox series

[bpf-next,v9,0/8] MAC and Audit policy using eBPF (KRSI)

Message ID 20200329004356.27286-1-kpsingh@chromium.org (mailing list archive)
Headers show
Series MAC and Audit policy using eBPF (KRSI) | expand

Message

KP Singh March 29, 2020, 12:43 a.m. UTC
From: KP Singh <kpsingh@google.com>

# v8 -> v9

  https://lore.kernel.org/bpf/20200327192854.31150-1-kpsingh@chromium.org/

* Fixed a selftest crash when CONFIG_LSM doesn't have "bpf".
* Added James' Ack.
* Rebase.

# v7 -> v8

  https://lore.kernel.org/bpf/20200326142823.26277-1-kpsingh@chromium.org/

* Removed CAP_MAC_ADMIN check from bpf_lsm_verify_prog. LSMs can add it
  in their own bpf_prog hook. This can be revisited as a separate patch.
* Added Andrii and James' Ack/Review tags.
* Fixed an indentation issue and missing newlines in selftest error
  a cases.
* Updated a comment as suggested by Alexei.
* Updated the documentation to use the newer libbpf API and some other
  fixes.
* Rebase

# v6 -> v7

  https://lore.kernel.org/bpf/20200325152629.6904-1-kpsingh@chromium.org/

* Removed __weak from the LSM attachment nops per Kees' suggestion.
  Will send a separate patch (if needed) to update the noinline
  definition in include/linux/compiler_attributes.h.
* waitpid to wait specifically for the forked child in selftests.
* Comment format fixes in security/... as suggested by Casey.
* Added Acks from Kees and Andrii and Casey's Reviewed-by: tags to
  the respective patches.
* Rebase

# v5 -> v6

  https://lore.kernel.org/bpf/20200323164415.12943-1-kpsingh@chromium.org/

* Updated LSM_HOOK macro to define a default value and cleaned up the
  BPF LSM hook declarations.
* Added Yonghong's Acks and Kees' Reviewed-by tags.
* Simplification of the selftest code.
* Rebase and fixes suggested by Andrii and Yonghong and some other minor
  fixes noticed in internal review.

# v4 -> v5

  https://lore.kernel.org/bpf/20200220175250.10795-1-kpsingh@chromium.org/

* Removed static keys and special casing of BPF calls from the LSM
  framework.
* Initialized the BPF callbacks (nops) as proper LSM hooks.
* Updated to using the newly introduced BPF_TRAMP_MODIFY_RETURN
  trampolines in https://lkml.org/lkml/2020/3/4/877
* Addressed Andrii's feedback and rebased.

# v3 -> v4

* Moved away from allocating a separate security_hook_heads and adding a
  new special case for arch_prepare_bpf_trampoline to using BPF fexit
  trampolines called from the right place in the LSM hook and toggled by
  static keys based on the discussion in:

  https://lore.kernel.org/bpf/CAG48ez25mW+_oCxgCtbiGMX07g_ph79UOJa07h=o_6B6+Q-u5g@mail.gmail.com/

* Since the code does not deal with security_hook_heads anymore, it goes
  from "being a BPF LSM" to "BPF program attachment to LSM hooks".
* Added a new test case which ensures that the BPF programs' return value
  is reflected by the LSM hook.

# v2 -> v3 does not change the overall design and has some minor fixes:

* LSM_ORDER_LAST is introduced to represent the behaviour of the BPF LSM
* Fixed the inadvertent clobbering of the LSM Hook error codes
* Added GPL license requirement to the commit log
* The lsm_hook_idx is now the more conventional 0-based index
* Some changes were split into a separate patch ("Load btf_vmlinux only
  once per object")

  https://lore.kernel.org/bpf/20200117212825.11755-1-kpsingh@chromium.org/

* Addressed Andrii's feedback on the BTF implementation
* Documentation update for using generated vmlinux.h to simplify
  programs
* Rebase

# Changes since v1

  https://lore.kernel.org/bpf/20191220154208.15895-1-kpsingh@chromium.org

* Eliminate the requirement to maintain LSM hooks separately in
  security/bpf/hooks.h Use BPF trampolines to dynamically allocate
  security hooks
* Drop the use of securityfs as bpftool provides the required
  introspection capabilities.  Update the tests to use the bpf_skeleton
  and global variables
* Use O_CLOEXEC anonymous fds to represent BPF attachment in line with
  the other BPF programs with the possibility to use bpf program pinning
  in the future to provide "permanent attachment".
* Drop the logic based on prog names for handling re-attachment.
* Drop bpf_lsm_event_output from this series and send it as a separate
  patch.

# Motivation

Google does analysis of rich runtime security data to detect and thwart
threats in real-time. Currently, this is done in custom kernel modules
but we would like to replace this with something that's upstream and
useful to others.

The current kernel infrastructure for providing telemetry (Audit, Perf
etc.) is disjoint from access enforcement (i.e. LSMs).  Augmenting the
information provided by audit requires kernel changes to audit, its
policy language and user-space components. Furthermore, building a MAC
policy based on the newly added telemetry data requires changes to
various LSMs and their respective policy languages.

This patchset allows BPF programs to be attached to LSM hooks This
facilitates a unified and dynamic (not requiring re-compilation of the
kernel) audit and MAC policy.

# Why an LSM?

Linux Security Modules target security behaviours rather than the
kernel's API. For example, it's easy to miss out a newly added system
call for executing processes (eg. execve, execveat etc.) but the LSM
framework ensures that all process executions trigger the relevant hooks
irrespective of how the process was executed.

Allowing users to implement LSM hooks at runtime also benefits the LSM
eco-system by enabling a quick feedback loop from the security community
about the kind of behaviours that the LSM Framework should be targeting.

# How does it work?

The patchset introduces a new eBPF (https://docs.cilium.io/en/v1.6/bpf/)
program type BPF_PROG_TYPE_LSM which can only be attached to LSM hooks.
Loading and attachment of BPF programs requires CAP_SYS_ADMIN.

The new LSM registers nop functions (bpf_lsm_<hook_name>) as LSM hook
callbacks. Their purpose is to provide a definite point where BPF
programs can be attached as BPF_TRAMP_MODIFY_RETURN trampoline programs
for hooks that return an int, and BPF_TRAMP_FEXIT trampoline programs
for void LSM hooks.

Audit logs can be written using a format chosen by the eBPF program to
the perf events buffer or to global eBPF variables or maps and can be
further processed in user-space.

# BTF Based Design

The current design uses BTF:

  * https://facebookmicrosites.github.io/bpf/blog/2018/11/14/btf-enhancement.html
  * https://lwn.net/Articles/803258

which allows verifiable read-only structure accesses by field names
rather than fixed offsets. This allows accessing the hook parameters
using a dynamically created context which provides a certain degree of
ABI stability:


// Only declare the structure and fields intended to be used
// in the program
struct vm_area_struct {
  unsigned long vm_start;
} __attribute__((preserve_access_index));

// Declare the eBPF program mprotect_audit which attaches to
// to the file_mprotect LSM hook and accepts three arguments.
SEC("lsm/file_mprotect")
int BPF_PROG(mprotect_audit, struct vm_area_struct *vma,
       unsigned long reqprot, unsigned long prot, int ret)
{
  unsigned long vm_start = vma->vm_start;

  return 0;
}

By relocating field offsets, BTF makes a large portion of kernel data
structures readily accessible across kernel versions without requiring a
large corpus of BPF helper functions and requiring recompilation with
every kernel version. The BTF type information is also used by the BPF
verifier to validate memory accesses within the BPF program and also
prevents arbitrary writes to the kernel memory.

The limitations of BTF compatibility are described in BPF Co-Re
(http://vger.kernel.org/bpfconf2019_talks/bpf-core.pdf, i.e. field
renames, #defines and changes to the signature of LSM hooks).  This
design imposes that the MAC policy (eBPF programs) be updated when the
inspected kernel structures change outside of BTF compatibility
guarantees. In practice, this is only required when a structure field
used by a current policy is removed (or renamed) or when the used LSM
hooks change. We expect the maintenance cost of these changes to be
acceptable as compared to the design presented in the RFC.

(https://lore.kernel.org/bpf/20190910115527.5235-1-kpsingh@chromium.org/).

# Usage Examples

A simple example and some documentation is included in the patchset.
In order to better illustrate the capabilities of the framework some
more advanced prototype (not-ready for review) code has also been
published separately:

* Logging execution events (including environment variables and
  arguments)
  https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_audit_env.c

* Detecting deletion of running executables:
  https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_detect_exec_unlink.c

* Detection of writes to /proc/<pid>/mem:
  https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_audit_env.c

We have updated Google's internal telemetry infrastructure and have
started deploying this LSM on our Linux Workstations. This gives us more
confidence in the real-world applications of such a system.

KP Singh (8):
  bpf: Introduce BPF_PROG_TYPE_LSM
  security: Refactor declaration of LSM hooks
  bpf: lsm: provide attachment points for BPF LSM programs
  bpf: lsm: Implement attach, detach and execution
  bpf: lsm: Initialize the BPF LSM hooks
  tools/libbpf: Add support for BPF_PROG_TYPE_LSM
  bpf: lsm: Add selftests for BPF_PROG_TYPE_LSM
  bpf: lsm: Add Documentation

 Documentation/bpf/bpf_lsm.rst                 | 142 ++++
 Documentation/bpf/index.rst                   |   1 +
 MAINTAINERS                                   |   1 +
 include/linux/bpf.h                           |   3 +
 include/linux/bpf_lsm.h                       |  33 +
 include/linux/bpf_types.h                     |   4 +
 include/linux/lsm_hook_defs.h                 | 381 +++++++++++
 include/linux/lsm_hooks.h                     | 628 +-----------------
 include/uapi/linux/bpf.h                      |   2 +
 init/Kconfig                                  |  12 +
 kernel/bpf/Makefile                           |   1 +
 kernel/bpf/bpf_lsm.c                          |  54 ++
 kernel/bpf/btf.c                              |  16 +-
 kernel/bpf/syscall.c                          |  57 +-
 kernel/bpf/trampoline.c                       |  17 +-
 kernel/bpf/verifier.c                         |  19 +-
 kernel/trace/bpf_trace.c                      |  12 +-
 security/Kconfig                              |  10 +-
 security/Makefile                             |   2 +
 security/bpf/Makefile                         |   5 +
 security/bpf/hooks.c                          |  26 +
 security/security.c                           |  41 +-
 tools/include/uapi/linux/bpf.h                |   2 +
 tools/lib/bpf/bpf.c                           |   3 +-
 tools/lib/bpf/libbpf.c                        |  39 +-
 tools/lib/bpf/libbpf.h                        |   4 +
 tools/lib/bpf/libbpf.map                      |   3 +
 tools/lib/bpf/libbpf_probes.c                 |   1 +
 tools/testing/selftests/bpf/config            |   2 +
 .../selftests/bpf/prog_tests/test_lsm.c       |  86 +++
 tools/testing/selftests/bpf/progs/lsm.c       |  48 ++
 31 files changed, 985 insertions(+), 670 deletions(-)
 create mode 100644 Documentation/bpf/bpf_lsm.rst
 create mode 100644 include/linux/bpf_lsm.h
 create mode 100644 include/linux/lsm_hook_defs.h
 create mode 100644 kernel/bpf/bpf_lsm.c
 create mode 100644 security/bpf/Makefile
 create mode 100644 security/bpf/hooks.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_lsm.c
 create mode 100644 tools/testing/selftests/bpf/progs/lsm.c

Comments

Daniel Borkmann March 29, 2020, 11:46 p.m. UTC | #1
On 3/29/20 1:43 AM, KP Singh wrote:
> From: KP Singh <kpsingh@google.com>
> 
> # v8 -> v9
> 
>    https://lore.kernel.org/bpf/20200327192854.31150-1-kpsingh@chromium.org/
> 
> * Fixed a selftest crash when CONFIG_LSM doesn't have "bpf".
> * Added James' Ack.
> * Rebase.

Applied, thanks for all the hard work!
Mikko Ylinen April 29, 2020, 12:31 p.m. UTC | #2
Hi,

On 29/03/2020 02:43, KP Singh wrote:
> # How does it work?
> 
> The patchset introduces a new eBPF (https://docs.cilium.io/en/v1.6/bpf/)
> program type BPF_PROG_TYPE_LSM which can only be attached to LSM hooks.
> Loading and attachment of BPF programs requires CAP_SYS_ADMIN.
> 
> The new LSM registers nop functions (bpf_lsm_<hook_name>) as LSM hook
> callbacks. Their purpose is to provide a definite point where BPF
> programs can be attached as BPF_TRAMP_MODIFY_RETURN trampoline programs
> for hooks that return an int, and BPF_TRAMP_FEXIT trampoline programs
> for void LSM hooks.

I have two systems (a NUC and a qemu VM) that fail to boot if I enable
the BPF LSM without enabling SELinux first. Anything I might be missing
or are you able to trigger it too?

For instance, the following additional cmdline args: "lsm.debug=1
lsm="capability,apparmor,bpf" results in:

[    1.251889] Call Trace:
[    1.252344]  dump_stack+0x57/0x7a
[    1.252951]  panic+0xe6/0x2a4
[    1.253497]  ? printk+0x43/0x45
[    1.254075]  mount_block_root+0x30c/0x31b
[    1.254798]  mount_root+0x78/0x7b
[    1.255417]  prepare_namespace+0x13a/0x16b
[    1.256168]  kernel_init_freeable+0x210/0x222
[    1.257021]  ? rest_init+0xa5/0xa5
[    1.257639]  kernel_init+0x9/0xfb
[    1.258074]  ret_from_fork+0x35/0x40
[    1.258885] Kernel Offset: 0x11000000 from 0xffffffff81000000 
(relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[    1.264046] ---[ end Kernel panic - not syncing: VFS: Unable to mount 
root fs on unknown-block(253,3)

Taking out "bpf" or adding "selinux" before it boots OK. I've tried
with both 5.7-rc2 and -rc3.

LSM logs:

[    0.267219] LSM: Security Framework initializing
[    0.267844] LSM: first ordering: capability (enabled)
[    0.267870] LSM: cmdline ignored: capability
[    0.268869] LSM: cmdline ordering: apparmor (enabled)
[    0.269508] LSM: cmdline ordering: bpf (enabled)
[    0.269869] LSM: cmdline disabled: selinux
[    0.270377] LSM: cmdline disabled: integrity
[    0.270869] LSM: exclusive chosen: apparmor
[    0.271869] LSM: cred blob size     = 8
[    0.272354] LSM: file blob size     = 24
[    0.272869] LSM: inode blob size    = 0
[    0.273362] LSM: ipc blob size      = 0
[    0.273869] LSM: msg_msg blob size  = 0
[    0.274352] LSM: task blob size     = 32
[    0.274873] LSM: initializing capability
[    0.275381] LSM: initializing apparmor
[    0.275880] AppArmor: AppArmor initialized
[    0.276437] LSM: initializing bpf
[    0.276871] LSM support for eBPF active

-- Regards, Mikko
KP Singh April 29, 2020, 12:34 p.m. UTC | #3
Thanks for reporting this! Can you share your Kconfig please?


On Wed, Apr 29, 2020 at 2:31 PM Mikko Ylinen
<mikko.ylinen@linux.intel.com> wrote:
>
> Hi,
>
> On 29/03/2020 02:43, KP Singh wrote:
> > # How does it work?
> >
> > The patchset introduces a new eBPF (https://docs.cilium.io/en/v1.6/bpf/)
> > program type BPF_PROG_TYPE_LSM which can only be attached to LSM hooks.
> > Loading and attachment of BPF programs requires CAP_SYS_ADMIN.
> >
> > The new LSM registers nop functions (bpf_lsm_<hook_name>) as LSM hook
> > callbacks. Their purpose is to provide a definite point where BPF
> > programs can be attached as BPF_TRAMP_MODIFY_RETURN trampoline programs
> > for hooks that return an int, and BPF_TRAMP_FEXIT trampoline programs
> > for void LSM hooks.
>
> I have two systems (a NUC and a qemu VM) that fail to boot if I enable
> the BPF LSM without enabling SELinux first. Anything I might be missing
> or are you able to trigger it too?
>
> For instance, the following additional cmdline args: "lsm.debug=1
> lsm="capability,apparmor,bpf" results in:
>
> [    1.251889] Call Trace:
> [    1.252344]  dump_stack+0x57/0x7a
> [    1.252951]  panic+0xe6/0x2a4
> [    1.253497]  ? printk+0x43/0x45
> [    1.254075]  mount_block_root+0x30c/0x31b
> [    1.254798]  mount_root+0x78/0x7b
> [    1.255417]  prepare_namespace+0x13a/0x16b
> [    1.256168]  kernel_init_freeable+0x210/0x222
> [    1.257021]  ? rest_init+0xa5/0xa5
> [    1.257639]  kernel_init+0x9/0xfb
> [    1.258074]  ret_from_fork+0x35/0x40
> [    1.258885] Kernel Offset: 0x11000000 from 0xffffffff81000000
> (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
> [    1.264046] ---[ end Kernel panic - not syncing: VFS: Unable to mount
> root fs on unknown-block(253,3)
>
> Taking out "bpf" or adding "selinux" before it boots OK. I've tried
> with both 5.7-rc2 and -rc3.
>
> LSM logs:
>
> [    0.267219] LSM: Security Framework initializing
> [    0.267844] LSM: first ordering: capability (enabled)
> [    0.267870] LSM: cmdline ignored: capability
> [    0.268869] LSM: cmdline ordering: apparmor (enabled)
> [    0.269508] LSM: cmdline ordering: bpf (enabled)
> [    0.269869] LSM: cmdline disabled: selinux
> [    0.270377] LSM: cmdline disabled: integrity
> [    0.270869] LSM: exclusive chosen: apparmor
> [    0.271869] LSM: cred blob size     = 8
> [    0.272354] LSM: file blob size     = 24
> [    0.272869] LSM: inode blob size    = 0
> [    0.273362] LSM: ipc blob size      = 0
> [    0.273869] LSM: msg_msg blob size  = 0
> [    0.274352] LSM: task blob size     = 32
> [    0.274873] LSM: initializing capability
> [    0.275381] LSM: initializing apparmor
> [    0.275880] AppArmor: AppArmor initialized
> [    0.276437] LSM: initializing bpf
> [    0.276871] LSM support for eBPF active
>
> -- Regards, Mikko
Mikko Ylinen April 29, 2020, 12:45 p.m. UTC | #4
On 29/04/2020 15:34, KP Singh wrote:
> Thanks for reporting this! Can you share your Kconfig please?

This is what I originally started with
https://raw.githubusercontent.com/clearlinux-pkgs/linux-mainline/master/config

but I also tried your _LSM_ settings found in this
https://lore.kernel.org/bpf/20200402040357.GA217889@google.com/

-- Regards, Mikko
KP Singh April 29, 2020, 4:17 p.m. UTC | #5
So I was able to reproduce the issue and also fix it (will separately
send a patch).

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 9cd4455528e5..1bdd027766d4 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -55,7 +55,7 @@ LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm)
 LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)
 LSM_HOOK(int, 0, fs_context_dup, struct fs_context *fc,
         struct fs_context *src_sc)
-LSM_HOOK(int, 0, fs_context_parse_param, struct fs_context *fc,
+LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc,
         struct fs_parameter *param)
 LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
 LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)

So what was happening was that:

bpf_lsm hook for fs_context_parse_param was returning 0 which led this
bit of logic to believe the parameter was parsed by the LSM.

int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter
*param)
{

[...]
        ret = security_fs_context_parse_param(fc, param);
        if (ret != -ENOPARAM)
                /* Param belongs to the LSM or is disallowed by the
                 * LSM; so
                 * don't pass to the FS.
                 */
                return ret;

        if (fc->ops->parse_param) {
                ret = fc->ops->parse_param(fc, param);
                if (ret != -ENOPARAM)
                        return ret;
        }
[...]

This resulted in the fs_context->dev_name being NULL and the following
chain to throw an -EINVAL resulting in unsuccessful mount of the root
file-system:

- do_mount_root -> do_mount -> do_new_mount -> vfs_get_tree ->
-> fc->ops->get_tree -> legacy->get_tree -> fc->fs_type->mount ->
ext4_mount -> mount_bdev -> blkdev_get_by_path -> lookup_bdev

- KP


On 29-Apr 15:45, Mikko Ylinen wrote:
> 
> 
> On 29/04/2020 15:34, KP Singh wrote:
> > Thanks for reporting this! Can you share your Kconfig please?
> 
> This is what I originally started with
> https://raw.githubusercontent.com/clearlinux-pkgs/linux-mainline/master/config
> 
> but I also tried your _LSM_ settings found in this
> https://lore.kernel.org/bpf/20200402040357.GA217889@google.com/
> 
> -- Regards, Mikko