One of the desirable features in security is the ability to restrict import of data to a given system based on data authenticity. If data import can be restricted, it would be possible to enforce a system-wide policy based on the signing keys the system owner trusts.
This feature is widely used in the kernel. For example, if the restriction is enabled, kernel modules can be plugged in only if they are signed with a key whose public part is in the primary or secondary keyring.
For eBPF, it can be useful as well. For example, it might be useful to authenticate data an eBPF program makes security decisions on.
The initial idea for this feature was to provide an helper that eBPF programs might call to authenticate data whenever necessary. However, this restricts the ability to use that helper only in sleepable programs (due to crypto operations). Furthermore, data authentication would have been responsibility of eBPF programs.
The proposed implementation instead shifts the responsibility of data authentication to the eBPF subsystem, upon request by the users. Whenever the users desire such feature, they just have to set a new map flag called BPF_F_VERIFY_ELEM. The eBPF subsystem ensures that only authenticated data can be added to the map. The check is performed during the execution of the bpf() system call when the commands are BPF_MAP_UPDATE_ELEM or BPF_MAP_UPDATE_BATCH. Since memory regions are not verified, usage of the BPF_F_MMAPABLE map flag is forbidden when BPF_F_VERIFY_ELEM is set.
An advantage of shifting the responsibility of data authentication to the eBPF subsystem is that it can be offered to any kind of eBPF programs, not only the sleepable ones.
When the new map flag BPF_F_VERIFY_ELEM is set, users have to provide a map value in the following format:
+-------------------------------+---------------+-----+-----------------+ | verified data+sig size (be32) | verified data | sig | unverified data | +-------------------------------+---------------+-----+-----------------+
This is mostly the same format adopted for kernel modules, with the exception of the first field, as the size cannot be determined otherwise due to the fixed map value size. More details can be found in patch 1.
Since the kernel already parses the format above, it was convenient to introduce also a new helper, called bpf_map_verified_data_size(), to return the size of verified data to the caller. This is done in patch 2.
Finally, the added functionality is tested in patch 3.
Roberto Sassu (3): bpf: Add BPF_F_VERIFY_ELEM to require signature verification on map values bpf: Introduce bpf_map_verified_data_size() helper bpf: Add tests for signed map values
include/linux/bpf.h | 7 + include/uapi/linux/bpf.h | 11 + kernel/bpf/arraymap.c | 2 +- kernel/bpf/helpers.c | 15 ++ kernel/bpf/syscall.c | 70 ++++++ tools/include/uapi/linux/bpf.h | 11 + .../bpf/prog_tests/test_map_value_sig.c | 212 ++++++++++++++++++ .../selftests/bpf/progs/map_value_sig.c | 50 +++++ 8 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/test_map_value_sig.c create mode 100644 tools/testing/selftests/bpf/progs/map_value_sig.c