diff mbox series

[POC,USER,SPACE] Introduce LSM to protect pinned objects

Message ID 20220405131116.3810418-1-roberto.sassu@huawei.com (mailing list archive)
State Not Applicable
Delegated to: BPF
Headers show
Series [POC,USER,SPACE] Introduce LSM to protect pinned objects | expand

Checks

Context Check Description
bpf/vmtest-bpf-PR fail merge-conflict
netdev/tree_selection success Not a local patch

Commit Message

Roberto Sassu April 5, 2022, 1:11 p.m. UTC
Introduce a new LSM to protect pinned objects in a bpf filesystem
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.

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@gmail.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 .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

Comments

Casey Schaufler April 5, 2022, 10:47 p.m. UTC | #1
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?
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@gmail.com>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>   .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@huawei.com>
> + *
> + * 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@huawei.com>
> + *
> + * 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;
> +}
Roberto Sassu April 6, 2022, 6:55 a.m. UTC | #2
> From: Casey Schaufler [mailto:casey@schaufler-ca.com]
> 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@gmail.com>
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >   .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@huawei.com>
> > + *
> > + * 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@huawei.com>
> > + *
> > + * 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;
> > +}
diff mbox series

Patch

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@huawei.com>
+ *
+ * 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@huawei.com>
+ *
+ * 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;
+}