diff mbox series

[RFC,bpf-next,1/2] bpf: with CONFIG_BPF_UNPRIV_MAP_ACCESS=y, allow unprivileged access to BPF maps

Message ID 1652275168-18630-2-git-send-email-alan.maguire@oracle.com (mailing list archive)
State RFC
Delegated to: BPF
Headers show
Series bpf: allow unprivileged map access to some map types | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR pending PR summary
bpf/vmtest-bpf-next-VM_Test-2 pending Logs for Kernel LATEST on z15 + selftests
netdev/tree_selection success Clearly marked for bpf-next
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: 15 this patch: 15
netdev/cc_maintainers warning 1 maintainers not CCed: netdev@vger.kernel.org
netdev/build_clang success Errors and warnings before: 11 this patch: 11
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 15 this patch: 15
netdev/checkpatch warning WARNING: line length of 83 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-1 fail Logs for Kernel LATEST on ubuntu-latest + selftests

Commit Message

Alan Maguire May 11, 2022, 1:19 p.m. UTC
With unprivileged BPF disabled, all cmds associated with the BPF syscall
are blocked to users without CAP_BPF/CAP_SYS_ADMIN.  However there are
use cases where we may wish to allow interactions with BPF programs
without being able to load and attach them.  So for example, a process
with required capabilities loads/attaches a BPF program, and a process
with less capabilities interacts with it; retrieving perf/ring buffer
events, modifying map-specified config etc.  With all BPF syscall
commands blocked as a result of unprivileged BPF being disabled,
this mode of interaction becomes impossible for processes without
CAP_BPF.

Here we propose allowing BPF_OBJ_GET (to retrieve pinned map/prog) and
BPF_MAP_* commands to work, even in cases where unprivileged BPF is
disabled and appropriate capabilities are not present.  This mode of
operation is not enabled by default; it depends on setting
CONFIG_BPF_UNPRIV_MAP_ACCESS=y (it defaults to n).

Note that the program responsible for loading and attaching the BPF program
can still control access to its pinned representation by restricting
permissions on the pin path.

For map-related BPF syscalls under CONFIG_BPF_UNPRIV_MAP_ACCESS=y,
map access is restricted to the following map types:

BPF_MAP_TYPE_ARRAY
BPF_MAP_TYPE_HASH
BPF_MAP_TYPE_PERF_EVENT_ARRAY
BPF_MAP_TYPE_PERCPU_ARRAY
BPF_MAP_TYPE_PERCPU_HASH
BPF_MAP_TYPE_RINGBUF

The set of unprivileged BPF syscall commands that are permitted for the
above map types with CONFIG_BPF_UNPRIV_MAP_ACCESS=y is

BPF_MAP_LOOKUP_ELEM
BPF_MAP_UPDATE_ELEM
BPF_MAP_DELETE_ELEM
BPF_MAP_NEXT_KEY

..and the following unprivileged BPF syscall commands are permitted
in order to allow map retrieval:

BPF_OBJ_GET
BPF_OBJ_GET_INFO_BY_FD

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 kernel/bpf/Kconfig   | 15 ++++++++++++
 kernel/bpf/syscall.c | 57 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 71 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index d56ee177d5f8..30e6f559ad08 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -84,6 +84,21 @@  config BPF_UNPRIV_DEFAULT_OFF
 
 	  If you are unsure how to answer this question, answer Y.
 
+config BPF_UNPRIV_MAP_ACCESS
+	bool "Allow unprivileged access to BPF map-related actions"
+	default n
+	depends on BPF_SYSCALL
+	help
+	   Allow unprivileged access to retrieve pinned objects, lookup,
+	   update and delete map elements.  Only specific BPF map types
+	   are permitted - (per-cpu) array maps, (per-cpu) hash maps,
+	   perf event and ring buffer maps.
+
+	   This allows limited use of the BPF syscall for unprivileged
+	   users; note however that this does not include loading or
+	   attaching BPF programs.  Pinned maps can also specify
+	   pin path permissions to prevent unwanted access.
+
 source "kernel/bpf/preload/Kconfig"
 
 config BPF_LSM
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 72e53489165d..951491836bbb 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1278,6 +1278,22 @@  static void *___bpf_copy_key(bpfptr_t ukey, u64 key_size)
 	return NULL;
 }
 
+static bool map_type_prevent_unprivileged_access(struct bpf_map *map)
+{
+#ifdef CONFIG_BPF_UNPRIV_MAP_ACCESS
+	return sysctl_unprivileged_bpf_disabled && !bpf_capable() &&
+	       map->map_type != BPF_MAP_TYPE_ARRAY &&
+	       map->map_type != BPF_MAP_TYPE_HASH &&
+	       map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+	       map->map_type != BPF_MAP_TYPE_PERCPU_ARRAY &&
+	       map->map_type != BPF_MAP_TYPE_PERCPU_HASH &&
+	       map->map_type != BPF_MAP_TYPE_RINGBUF;
+#else
+	/* earlier checks prevent unprivileged access */
+	return false;
+#endif
+}
+
 /* last field in 'union bpf_attr' used by this command */
 #define BPF_MAP_LOOKUP_ELEM_LAST_FIELD flags
 
@@ -1313,6 +1329,11 @@  static int map_lookup_elem(union bpf_attr *attr)
 		goto err_put;
 	}
 
+	if (map_type_prevent_unprivileged_access(map)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = __bpf_copy_key(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -1386,6 +1407,11 @@  static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
 		goto err_put;
 	}
 
+	if (map_type_prevent_unprivileged_access(map)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = ___bpf_copy_key(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -1439,6 +1465,11 @@  static int map_delete_elem(union bpf_attr *attr)
 		goto err_put;
 	}
 
+	if (map_type_prevent_unprivileged_access(map)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	key = __bpf_copy_key(ukey, map->key_size);
 	if (IS_ERR(key)) {
 		err = PTR_ERR(key);
@@ -1494,6 +1525,11 @@  static int map_get_next_key(union bpf_attr *attr)
 		goto err_put;
 	}
 
+	if (map_type_prevent_unprivileged_access(map)) {
+		err = -EPERM;
+		goto err_put;
+	}
+
 	if (ukey) {
 		key = __bpf_copy_key(ukey, map->key_size);
 		if (IS_ERR(key)) {
@@ -4863,10 +4899,29 @@  static int bpf_prog_bind_map(union bpf_attr *attr)
 static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size)
 {
 	union bpf_attr attr;
+	bool capable;
 	int err;
 
-	if (sysctl_unprivileged_bpf_disabled && !bpf_capable())
+	capable = bpf_capable() || !sysctl_unprivileged_bpf_disabled;
+
+#ifdef CONFIG_BPF_UNPRIV_MAP_ACCESS
+	/* A subset of cmds are allowed to unprivileged users, principally to allow
+	 * them to interact with pinned BPF maps to retrieve events, update map
+	 * values etc.  The pinning program can adjust pin path permissions
+	 * to prevent unwanted access by unprivileged users.
+	 */
+	if (!capable &&
+	    cmd != BPF_MAP_LOOKUP_ELEM &&
+	    cmd != BPF_MAP_UPDATE_ELEM &&
+	    cmd != BPF_MAP_DELETE_ELEM &&
+	    cmd != BPF_MAP_GET_NEXT_KEY &&
+	    cmd != BPF_OBJ_GET &&
+	    cmd != BPF_OBJ_GET_INFO_BY_FD)
 		return -EPERM;
+#else
+	if (!capable)
+		return -EPERM;
+#endif
 
 	err = bpf_check_uarg_tail_zero(uattr, sizeof(attr), size);
 	if (err)