@@ -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
@@ -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)
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(-)