RE: [POC][USER SPACE][PATCH] Introduce LSM to protect pinned objects

From: Roberto Sassu
Date: Wed Apr 06 2022 - 06:33:10 EST


> From: Casey Schaufler [mailto:casey@xxxxxxxxxxxxxxxx]
> Sent: Wednesday, April 6, 2022 12:48 AM
> On 4/5/2022 6:11 AM, Roberto Sassu wrote:
> > Introduce a new LSM to protect pinned objects in a bpf filesystem
>
> This is *not an LSM*. Do not call it an LSM. It is a set of
> eBPF programs. We have all the opportunities for confusion
> that we need. I suggested that you call this a BPF security
> module (BSM) earlier today. You have any number of things
> you can call this that won't be objectionable.
>
> > instance. This is useful for example to ensure that an LSM will always
> > enforce its policy, even despite root tries to unload the corresponding
> > eBPF program.
>
> How is this going to ensure that SELinux enforces its policy?

I should have said above: that an LSM implemented with eBPF.
Built-in LSMs are not affected by this change.

Ok, next time I call it BSM.

Thanks

Roberto

HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Li Peng, Zhong Ronghua

> AppArmor has no eBPF program that corresponds to its policy,
> neither does any other existing LSM, save BPF. Your claim is
> nonsensical in the face of LSM behavior.
>
> > Achieve the protection by denying inode unlink and unmount of the
> > protected bpf filesystem instance. Since protected inodes hold a
> > reference of the link of loaded programs (e.g. LSM hooks), denying
> > operations on them will prevent the ref count of the links from reaching
> > zero, ensuring that the programs remain always active.
> >
> > Enable the protection only for the instance created by the user space
> > counterpart of the LSM, and don't interfere with other instances, so
> > that their behavior remains unchanged.
> >
> > Suggested-by: Djalal Harouni <tixxdz@xxxxxxxxx>
> > Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
> > ---
> > .gitignore | 4 +++
> > Makefile | 18 ++++++++++++++
> > bpffs_lsm_kern.c | 63
> ++++++++++++++++++++++++++++++++++++++++++++++++
> > bpffs_lsm_user.c | 60
> +++++++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 145 insertions(+)
> > create mode 100644 .gitignore
> > create mode 100644 Makefile
> > create mode 100644 bpffs_lsm_kern.c
> > create mode 100644 bpffs_lsm_user.c
> >
> > diff --git a/.gitignore b/.gitignore
> > new file mode 100644
> > index 000000000000..7fa02964f1dc
> > --- /dev/null
> > +++ b/.gitignore
> > @@ -0,0 +1,4 @@
> > +*.o
> > +vmlinux.h
> > +bpffs_lsm_kern.skel.h
> > +bpffs_lsm_user
> > diff --git a/Makefile b/Makefile
> > new file mode 100644
> > index 000000000000..c3d805759db3
> > --- /dev/null
> > +++ b/Makefile
> > @@ -0,0 +1,18 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +all: bpffs_lsm_user
> > +
> > +clean:
> > + rm -rf bpffs_lsm.skel.h vmlinux.h bpffs_lsm_kern.o bpffs_lsm_user
> > +
> > +vmlinux.h:
> > + /usr/sbin/bpftool btf dump file /sys/kernel/btf/vmlinux format c > \
> > + vmlinux.h
> > +
> > +bpffs_lsm_kern.skel.h: bpffs_lsm_kern.o
> > + bpftool gen skeleton $< > $@
> > +
> > +bpffs_lsm_kern.o: bpffs_lsm_kern.c vmlinux.h
> > + clang -Wall -Werror -g -O2 -target bpf -c $< -o $@
> > +
> > +bpffs_lsm_user: bpffs_lsm_user.c bpffs_lsm_kern.skel.h
> bpffs_lsm_kern.o
> > + cc -Wall -Werror -g -o $@ $< -lbpf
> > diff --git a/bpffs_lsm_kern.c b/bpffs_lsm_kern.c
> > new file mode 100644
> > index 000000000000..b3ccb2a75c95
> > --- /dev/null
> > +++ b/bpffs_lsm_kern.c
> > @@ -0,0 +1,63 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
> > + *
> > + * Authors:
> > + * Roberto Sassu <roberto.sassu@xxxxxxxxxx>
> > + *
> > + * Implement an LSM to protect a bpf filesystem instance.
> > + */
> > +
> > +#include "vmlinux.h"
> > +#include <errno.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include <bpf/bpf_core_read.h>
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +uint32_t monitored_pid = 0;
> > +
> > +struct {
> > + __uint(type, BPF_MAP_TYPE_INODE_STORAGE);
> > + __uint(map_flags, BPF_F_NO_PREALLOC);
> > + __type(key, int);
> > + __type(value, sizeof(uint8_t));
> > +} inode_storage_map SEC(".maps");
> > +
> > +SEC("lsm/sb_set_mnt_opts")
> > +int BPF_PROG(sb_set_mnt_opts, struct super_block *sb, void
> *mnt_opts,
> > + unsigned long kern_flags, unsigned long *set_kern_flags)
> > +{
> > + u32 pid;
> > +
> > + pid = bpf_get_current_pid_tgid() >> 32;
> > + if (pid != monitored_pid)
> > + return 0;
> > +
> > + if (!bpf_inode_storage_get(&inode_storage_map, sb->s_root-
> >d_inode, 0,
> > + BPF_LOCAL_STORAGE_GET_F_CREATE))
> > + return -EPERM;
> > +
> > + return 0;
> > +}
> > +
> > +SEC("lsm/inode_unlink")
> > +int BPF_PROG(inode_unlink, struct inode *dir, struct dentry *dentry)
> > +{
> > + if (bpf_inode_storage_get(&inode_storage_map,
> > + dir->i_sb->s_root->d_inode, 0, 0))
> > + return -EPERM;
> > +
> > + return 0;
> > +}
> > +
> > +SEC("lsm/sb_umount")
> > +int BPF_PROG(sb_umount, struct vfsmount *mnt, int flags)
> > +{
> > + if (bpf_inode_storage_get(&inode_storage_map,
> > + mnt->mnt_sb->s_root->d_inode, 0, 0))
> > + return -EPERM;
> > +
> > + return 0;
> > +}
> > diff --git a/bpffs_lsm_user.c b/bpffs_lsm_user.c
> > new file mode 100644
> > index 000000000000..e20180cc5db9
> > --- /dev/null
> > +++ b/bpffs_lsm_user.c
> > @@ -0,0 +1,60 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
> > + *
> > + * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
> > + *
> > + * Implement the user space side of the LSM for bpffs.
> > + */
> > +
> > +#include <fcntl.h>
> > +#include <unistd.h>
> > +#include <stdio.h>
> > +#include <errno.h>
> > +#include <stdlib.h>
> > +#include <unistd.h>
> > +#include <limits.h>
> > +#include <sys/mount.h>
> > +#include <sys/stat.h>
> > +
> > +#include "bpffs_lsm_kern.skel.h"
> > +
> > +#define MOUNT_FLAGS (MS_NOSUID | MS_NODEV | MS_NOEXEC |
> MS_RELATIME)
> > +
> > +int main(int argc, char *argv[])
> > +{
> > + char mntpoint[] = "/tmp/bpf_private_mountXXXXXX";
> > + char path[PATH_MAX];
> > + struct bpffs_lsm_kern *skel;
> > + int ret, i;
> > +
> > + skel = bpffs_lsm_kern__open_and_load();
> > + if (!skel)
> > + return -EINVAL;
> > +
> > + ret = bpffs_lsm_kern__attach(skel);
> > + if (ret < 0)
> > + goto out_destroy;
> > +
> > + mkdtemp(mntpoint);
> > +
> > + skel->bss->monitored_pid = getpid();
> > + ret = mount(mntpoint, mntpoint, "bpf", MOUNT_FLAGS, NULL);
> > + skel->bss->monitored_pid = 0;
> > +
> > + if (ret < 0)
> > + goto out_destroy;
> > +
> > + for (i = 0; i < skel->skeleton->prog_cnt; i++) {
> > + snprintf(path, sizeof(path), "%s/%s", mntpoint,
> > + skel->skeleton->progs[i].name);
> > + ret = bpf_link__pin(*skel->skeleton->progs[i].link, path);
> > + if (ret < 0)
> > + goto out_destroy;
> > + }
> > +
> > + ret = 0;
> > +out_destroy:
> > + bpffs_lsm_kern__destroy(skel);
> > + return ret;
> > +}