Re: [PATCH net-next v6 02/11] bpf,landlock: Define an eBPF program type for Landlock
From: Kees Cook <hidden>
Date: 2017-04-18 21:58:40
Also in:
linux-api, linux-security-module, lkml
On Tue, Mar 28, 2017 at 4:46 PM, Mickaël Salaün [off-list ref] wrote:
Add a new type of eBPF program used by Landlock rules.
This new BPF program type will be registered with the Landlock LSM
initialization.
Add an initial Landlock Kconfig.
Changes since v5:
* rename file hooks.c to init.c
* fix spelling
Changes since v4:
* merge a minimal (not enabled) LSM code and Kconfig in this commit
Changes since v3:
* split commit
* revamp the landlock_context:
* add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to
cross-check action with the event type
* replace args array with dedicated fields to ease the addition of new
fields
Signed-off-by: Mickaël Salaün <redacted>
Cc: Alexei Starovoitov <redacted>
Cc: Andy Lutomirski <redacted>
Cc: Daniel Borkmann <redacted>
Cc: David S. Miller <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
Cc: James Morris <redacted>
Cc: Kees Cook <redacted>
Cc: Serge E. Hallyn <redacted>
---
[...]
+static inline bool bpf_landlock_is_valid_subtype(
+ union bpf_prog_subtype *prog_subtype)
+{
+ if (WARN_ON(!prog_subtype))
+ return false;
+
+ switch (prog_subtype->landlock_rule.event) {
+ case LANDLOCK_SUBTYPE_EVENT_FS:
+ break;
+ case LANDLOCK_SUBTYPE_EVENT_UNSPEC:
+ default:
+ return false;
+ }
+
+ if (!prog_subtype->landlock_rule.version ||
+ prog_subtype->landlock_rule.version > LANDLOCK_VERSION)
+ return false;
+ if (!prog_subtype->landlock_rule.event ||
+ prog_subtype->landlock_rule.event > _LANDLOCK_SUBTYPE_EVENT_LAST)
+ return false;
+ if (prog_subtype->landlock_rule.ability & ~_LANDLOCK_SUBTYPE_ABILITY_MASK)
+ return false;
+ if (prog_subtype->landlock_rule.option & ~_LANDLOCK_SUBTYPE_OPTION_MASK)
+ return false;
+
+ /* check ability flags */
+ if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_WRITE &&
+ !capable(CAP_SYS_ADMIN))
+ return false;
+ if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_DEBUG &&
+ !capable(CAP_SYS_ADMIN))
+ return false;
+
+ return true;
+}I would add more comments for the rule and ability tests just to help people read this.
quoted hunk ↗ jump to hunk
+ +static inline const struct bpf_func_proto *bpf_landlock_func_proto( + enum bpf_func_id func_id, union bpf_prog_subtype *prog_subtype) +{ + bool event_fs = (prog_subtype->landlock_rule.event == + LANDLOCK_SUBTYPE_EVENT_FS); + bool ability_write = !!(prog_subtype->landlock_rule.ability & + LANDLOCK_SUBTYPE_ABILITY_WRITE); + bool ability_debug = !!(prog_subtype->landlock_rule.ability & + LANDLOCK_SUBTYPE_ABILITY_DEBUG); + + switch (func_id) { + case BPF_FUNC_map_lookup_elem: + return &bpf_map_lookup_elem_proto; + + /* ability_write */ + case BPF_FUNC_map_delete_elem: + if (ability_write) + return &bpf_map_delete_elem_proto; + return NULL; + case BPF_FUNC_map_update_elem: + if (ability_write) + return &bpf_map_update_elem_proto; + return NULL; + + /* ability_debug */ + case BPF_FUNC_get_current_comm: + if (ability_debug) + return &bpf_get_current_comm_proto; + return NULL; + case BPF_FUNC_get_current_pid_tgid: + if (ability_debug) + return &bpf_get_current_pid_tgid_proto; + return NULL; + case BPF_FUNC_get_current_uid_gid: + if (ability_debug) + return &bpf_get_current_uid_gid_proto; + return NULL; + case BPF_FUNC_trace_printk: + if (ability_debug) + return bpf_get_trace_printk_proto(); + return NULL; + + default: + return NULL; + } +}
I find this switch statement mixed with the "if (ability...)" kind of
hard to read and a bit fragile. I think it'd be better written as:
switch (func_id) {
case BPF_FUNC_map_lookup_elem:
return ...
}
if (ability_write) {
switch (func_id) {
...
}
}
if (ability_debug) {
switch (func_id) {
...
}
}
return NULL;
Then it's self-documenting and it's harder to add a case without the
desired ability check...
quoted hunk ↗ jump to hunk
+static const struct bpf_verifier_ops bpf_landlock_ops = { + .get_func_proto = bpf_landlock_func_proto, + .is_valid_access = bpf_landlock_is_valid_access, + .is_valid_subtype = bpf_landlock_is_valid_subtype, +}; + +static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = { + .ops = &bpf_landlock_ops, + .type = BPF_PROG_TYPE_LANDLOCK, +};
Yay const and ro_after_init! :) -Kees -- Kees Cook Pixel Security