All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexei Starovoitov <ast@kernel.org>
To: "David S . Miller" <davem@davemloft.net>
Cc: <daniel@iogearbox.net>, <luto@amacapital.net>,
	<viro@zeniv.linux.org.uk>, <netdev@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <kernel-team@fb.com>
Subject: [PATCH bpf-next 6/6] selftests/bpf: add a test for BPF_CGROUP_FILE_OPEN
Date: Wed, 3 Oct 2018 19:57:50 -0700	[thread overview]
Message-ID: <20181004025750.498303-7-ast@kernel.org> (raw)
In-Reply-To: <20181004025750.498303-1-ast@kernel.org>

add bpf test for BPF_CGROUP_FILE_OPEN which attaches to a temporary cgroup and
- disallows further access to cgroup v2 file system for processes within this cgroup
- figures out mount_id of /etc, disallows access to this mnt_id,
  checks that /etc/hosts and /etc/hostname are no longer readable,
  re-allows access to /etc.
  Note that /etc is likely mounted as part of /, so the test disallows access to / mount
- figures out dev/inode of /etc/hosts file and disallows access to this file

cgroup local storage is used to pass information between user space control program
and bpf program attached to BPF_CGROUP_FILE_OPEN hook

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
 tools/testing/selftests/bpf/.gitignore        |   1 +
 tools/testing/selftests/bpf/Makefile          |   6 +-
 tools/testing/selftests/bpf/test_file_open.c  | 154 ++++++++++++++++++
 .../selftests/bpf/test_file_open_common.h     |  13 ++
 .../selftests/bpf/test_file_open_kern.c       |  48 ++++++
 5 files changed, 220 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/test_file_open.c
 create mode 100644 tools/testing/selftests/bpf/test_file_open_common.h
 create mode 100644 tools/testing/selftests/bpf/test_file_open_kern.c

diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 8a60c9b9892d..a332b39bed68 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -25,3 +25,4 @@ test_cgroup_storage
 test_select_reuseport
 test_flow_dissector
 flow_dissector_load
+test_file_open
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 1381ab81099c..89a0fd955c8e 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -24,7 +24,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
 	test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
 	test_sock test_btf test_sockmap test_lirc_mode2_user get_cgroup_id_user \
 	test_socket_cookie test_cgroup_storage test_select_reuseport test_section_names \
-	test_netcnt
+	test_netcnt test_file_open
 
 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
 	test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
@@ -36,7 +36,8 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
 	test_get_stack_rawtp.o test_sockmap_kern.o test_sockhash_kern.o \
 	test_lwt_seg6local.o sendmsg4_prog.o sendmsg6_prog.o test_lirc_mode2_kern.o \
 	get_cgroup_id_kern.o socket_cookie_prog.o test_select_reuseport_kern.o \
-	test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o test_sk_lookup_kern.o
+	test_skb_cgroup_id_kern.o bpf_flow.o netcnt_prog.o test_sk_lookup_kern.o \
+	test_file_open_kern.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
@@ -74,6 +75,7 @@ $(OUTPUT)/test_progs: trace_helpers.c
 $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
 $(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
 $(OUTPUT)/test_netcnt: cgroup_helpers.c
+$(OUTPUT)/test_file_open: cgroup_helpers.c
 
 .PHONY: force
 
diff --git a/tools/testing/selftests/bpf/test_file_open.c b/tools/testing/selftests/bpf/test_file_open.c
new file mode 100644
index 000000000000..33716adafecf
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_file_open.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Facebook */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/kdev_t.h>
+
+#include <linux/bpf.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include "cgroup_helpers.h"
+#include "bpf_rlimit.h"
+#include "test_file_open_common.h"
+
+#define CGROUP_PROG "./test_file_open_kern.o"
+
+#define TEST_CGROUP "/test-bpf-based-device-cgroup/"
+
+int main(int argc, char **argv)
+{
+	struct bpf_cgroup_storage_key key;
+	struct file_handle fhp[1] = {};
+	struct test_file_open_config cfg = {};
+	struct bpf_object *obj;
+	int error = EXIT_FAILURE;
+	int prog_fd, cgroup_fd;
+	struct bpf_map *map;
+	__u32 prog_cnt;
+	struct stat st;
+	int map_fd;
+
+	if (bpf_prog_load(CGROUP_PROG, BPF_PROG_TYPE_FILE_FILTER,
+			  &obj, &prog_fd)) {
+		printf("Failed to load FILE_FILTER program\n");
+		goto out;
+	}
+
+	map = bpf_object__find_map_by_name(obj, "local_storage");
+	if (!map) {
+		printf("Failed to find cgroup local storage map");
+		goto err;
+	}
+	map_fd = bpf_map__fd(map);
+
+	if (setup_cgroup_environment()) {
+		printf("Failed to load FILE_FILTER program\n");
+		goto err;
+	}
+
+	/* Create a cgroup, get fd, and join it */
+	cgroup_fd = create_and_get_cgroup(TEST_CGROUP);
+	if (!cgroup_fd) {
+		printf("Failed to create test cgroup\n");
+		goto err;
+	}
+
+	if (join_cgroup(TEST_CGROUP)) {
+		printf("Failed to join cgroup\n");
+		goto err;
+	}
+
+	/* few sanity checks before bpf prog is attached */
+	assert(system("cat /mnt/cgroup-test-work-dir" TEST_CGROUP "cgroup.procs >& /dev/null") == 0);
+	assert(system("cat /etc/hosts >& /dev/null") == 0);
+
+	/* Attach bpf program */
+	if (bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_FILE_OPEN, 0)) {
+		perror("Failed to attach CGROUP_FILE_OPEN program");
+		goto err;
+	}
+
+	if (bpf_map_get_next_key(map_fd, NULL, &key)) {
+		printf("Failed to get key in cgroup storage\n");
+		goto err;
+	}
+
+	if (bpf_prog_query(cgroup_fd, BPF_CGROUP_FILE_OPEN, 0, NULL, NULL,
+			   &prog_cnt)) {
+		perror("Failed to query attached programs");
+		goto err;
+	}
+	assert(prog_cnt == 1);
+
+	/* check that this process cannot make any further changes to cgroup */
+	assert(system("cat /mnt/cgroup-test-work-dir" TEST_CGROUP "cgroup.procs >& /dev/null") != 0);
+
+	/* figure out the mnt_id of /etc */
+	if (name_to_handle_at(-1, "/etc", fhp, &cfg.mnt_id, 0) != -1 ||
+	    errno != EOVERFLOW) {
+		perror("name_to_handle_at failed");
+		goto err;
+	}
+
+	/* let bpf prog know /etc's mnt_id via cgroup local storage */
+	if (bpf_map_update_elem(map_fd, &key, &cfg, 0)) {
+		printf("Failed to update cgroup local storage\n");
+		goto err;
+	}
+
+	/* check that this process cannot read /etc any more */
+	assert(system("cat /etc/hosts >& /dev/null") != 0);
+	assert(system("cat /etc/hostname >& /dev/null") != 0);
+
+	/* set mnt_id back to zero */
+	cfg.mnt_id = 0;
+	if (bpf_map_update_elem(map_fd, &key, &cfg, 0)) {
+		printf("Failed to update cgroup local storage\n");
+		goto err;
+	}
+	/* access to /etc should work again */
+	assert(system("cat /etc/hosts >& /dev/null") == 0);
+
+	/* figure out inode of /etc/hosts */
+	if (stat("/etc/hosts", &st)) {
+		perror("stat failed");
+		goto err;
+	}
+	cfg.inode = st.st_ino;
+	cfg.dev_major = MAJOR(st.st_dev);
+	cfg.dev_minor = MINOR(st.st_dev);
+	if (bpf_map_update_elem(map_fd, &key, &cfg, 0)) {
+		printf("Failed to update cgroup local storage\n");
+		goto err;
+	}
+	/* check that this process cannot read /etc/hosts any more */
+	assert(system("cat /etc/hosts >& /dev/null") != 0);
+	/* but /etc/hostname is still ok */
+	assert(system("cat /etc/hostname >& /dev/null") == 0);
+
+	/*
+	 * detach from cgroup. Otherwise our own bpf prog will prevent us
+	 * from cleaning up the cgroup environment
+	 */
+	if (bpf_prog_detach(cgroup_fd, BPF_CGROUP_FILE_OPEN)) {
+		perror("Failed to detach");
+		goto err;
+	}
+
+	error = 0;
+	printf("test_file_open:PASS\n");
+
+err:
+	cleanup_cgroup_environment();
+
+out:
+	return error;
+}
diff --git a/tools/testing/selftests/bpf/test_file_open_common.h b/tools/testing/selftests/bpf/test_file_open_common.h
new file mode 100644
index 000000000000..d07b1f0ba28b
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_file_open_common.h
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Facebook */
+#ifndef __TEST_FILE_OPEN_COMMON_H
+#define __TEST_FILE_OPEN_COMMON_H
+
+struct test_file_open_config {
+	int mnt_id;
+	int dev_major;
+	int dev_minor;
+	int inode;
+};
+
+#endif
diff --git a/tools/testing/selftests/bpf/test_file_open_kern.c b/tools/testing/selftests/bpf/test_file_open_kern.c
new file mode 100644
index 000000000000..ea4b7a576b38
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_file_open_kern.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Facebook */
+#include <linux/bpf.h>
+#include <linux/magic.h>
+#include "bpf_helpers.h"
+#include "test_file_open_common.h"
+
+struct bpf_map_def SEC("maps") local_storage = {
+	.type = BPF_MAP_TYPE_CGROUP_STORAGE,
+	.key_size = sizeof(struct bpf_cgroup_storage_key),
+	.value_size = sizeof(struct test_file_open_config),
+};
+
+SEC("cgroup/file_open")
+int bpf_file_filter(struct bpf_file_info *f)
+{
+	char fmt1[] = "magic 0x%x mnt %d inode %ld\n";
+	char fmt2[] = "dev 0x%x link %d file %s\n";
+	char fmt3[] = "mode %o flags %o /etc mnt_id %d\n";
+	char path[400];
+	struct test_file_open_config *cfg;
+
+	cfg = bpf_get_local_storage(&local_storage, 0);
+
+	/* debugging prints */
+	bpf_get_file_path(f, path, sizeof(path));
+	bpf_trace_printk(fmt1, sizeof(fmt1), f->fs_magic, f->mnt_id, f->inode);
+	bpf_trace_printk(fmt2, sizeof(fmt2), (f->dev_major << 8) | f->dev_minor,
+			 f->nlink, path);
+	bpf_trace_printk(fmt3, sizeof(fmt3), f->mode, f->flags, cfg->mnt_id);
+
+	/* disallow access to cgroupv2 */
+	if (f->fs_magic == CGROUP2_SUPER_MAGIC)
+		return 0;
+
+	/* disallow access to given mount */
+	if (f->mnt_id == cfg->mnt_id)
+		return 0;
+
+	/* disallow access to a given file */
+	if (f->dev_major == cfg->dev_major &&
+	    f->dev_minor == cfg->dev_minor &&
+	    f->inode == cfg->inode)
+		return 0;
+	return 1;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.17.1


      parent reply	other threads:[~2018-10-04  2:58 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-04  2:57 [PATCH bpf-next 0/6] bpf: introduce BPF_CGROUP_FILE_OPEN Alexei Starovoitov
2018-10-04  2:57 ` [PATCH bpf-next 1/6] bpf: introduce BPF_PROG_TYPE_FILE_FILTER Alexei Starovoitov
2018-10-04 19:41   ` Roman Gushchin
2018-10-04 19:51     ` Andy Lutomirski
2018-10-04 22:23       ` Alexei Starovoitov
2018-10-05  4:46   ` Al Viro
2018-10-05 22:05     ` Alexei Starovoitov
2018-10-05 22:09       ` Andy Lutomirski
2018-10-05 22:27         ` Alexei Starovoitov
2018-10-05 23:47           ` Al Viro
2018-10-06  0:22             ` Alexei Starovoitov
2018-10-08  0:56   ` Jann Horn
2018-10-08  2:22     ` Alexei Starovoitov
2018-10-08  9:06       ` Mickaël Salaün
2018-10-04  2:57 ` [PATCH bpf-next 2/6] fs: wire in BPF_CGROUP_FILE_OPEN hook Alexei Starovoitov
2018-10-04  2:57 ` [PATCH bpf-next 3/6] tools/bpf: sync uapi/bpf.h Alexei Starovoitov
2018-10-04  2:57 ` [PATCH bpf-next 4/6] trace/bpf: allow %o modifier in bpf_trace_printk Alexei Starovoitov
2018-10-04  2:57 ` [PATCH bpf-next 5/6] libbpf: support BPF_CGROUP_FILE_OPEN in libbpf Alexei Starovoitov
2018-10-04  2:57 ` Alexei Starovoitov [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181004025750.498303-7-ast@kernel.org \
    --to=ast@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=kernel-team@fb.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=netdev@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.