Commit 73a4f040 authored by Quentin Monnet's avatar Quentin Monnet Committed by Alexei Starovoitov

tools, bpftool: Make capability check account for new BPF caps

Following the introduction of CAP_BPF, and the switch from CAP_SYS_ADMIN
to other capabilities for various BPF features, update the capability
checks (and potentially, drops) in bpftool for feature probes. Because
bpftool and/or the system might not know of CAP_BPF yet, some caution is
necessary:

- If compiled and run on a system with CAP_BPF, check CAP_BPF,
  CAP_SYS_ADMIN, CAP_PERFMON, CAP_NET_ADMIN.

- Guard against CAP_BPF being undefined, to allow compiling bpftool from
  latest sources on older systems. If the system where feature probes
  are run does not know of CAP_BPF, stop checking after CAP_SYS_ADMIN,
  as this should be the only capability required for all the BPF
  probing.

- If compiled from latest sources on a system without CAP_BPF, but later
  executed on a newer system with CAP_BPF knowledge, then we only test
  CAP_SYS_ADMIN. Some probes may fail if the bpftool process has
  CAP_SYS_ADMIN but misses the other capabilities. The alternative would
  be to redefine the value for CAP_BPF in bpftool, but this does not
  look clean, and the case sounds relatively rare anyway.

Note that libcap offers a cap_to_name() function to retrieve the name of
a given capability (e.g. "cap_sys_admin"). We do not use it because
deriving the names from the macros looks simpler than using
cap_to_name() (doing a strdup() on the string) + cap_free() + handling
the case of failed allocations, when we just want to use the name of the
capability in an error message.

The checks when compiling without libcap (i.e. root versus non-root) are
unchanged.

v2:
- Do not allocate cap_list dynamically.
- Drop BPF-related capabilities when running with "unprivileged", even
  if we didn't have the full set in the first place (in v1, we would
  skip dropping them in that case).
- Keep track of what capabilities we have, print the names of the
  missing ones for privileged probing.
- Attempt to drop only the capabilities we actually have.
- Rename a couple variables.
Signed-off-by: default avatarQuentin Monnet <quentin@isovalent.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20200523010247.20654-1-quentin@isovalent.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 90040351
...@@ -758,11 +758,29 @@ static void section_misc(const char *define_prefix, __u32 ifindex) ...@@ -758,11 +758,29 @@ static void section_misc(const char *define_prefix, __u32 ifindex)
print_end_section(); print_end_section();
} }
#ifdef USE_LIBCAP
#define capability(c) { c, false, #c }
#define capability_msg(a, i) a[i].set ? "" : a[i].name, a[i].set ? "" : ", "
#endif
static int handle_perms(void) static int handle_perms(void)
{ {
#ifdef USE_LIBCAP #ifdef USE_LIBCAP
cap_value_t cap_list[1] = { CAP_SYS_ADMIN }; struct {
bool has_sys_admin_cap = false; cap_value_t cap;
bool set;
char name[14]; /* strlen("CAP_SYS_ADMIN") */
} bpf_caps[] = {
capability(CAP_SYS_ADMIN),
#ifdef CAP_BPF
capability(CAP_BPF),
capability(CAP_NET_ADMIN),
capability(CAP_PERFMON),
#endif
};
cap_value_t cap_list[ARRAY_SIZE(bpf_caps)];
unsigned int i, nb_bpf_caps = 0;
bool cap_sys_admin_only = true;
cap_flag_value_t val; cap_flag_value_t val;
int res = -1; int res = -1;
cap_t caps; cap_t caps;
...@@ -774,35 +792,64 @@ static int handle_perms(void) ...@@ -774,35 +792,64 @@ static int handle_perms(void)
return -1; return -1;
} }
if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &val)) { #ifdef CAP_BPF
p_err("bug: failed to retrieve CAP_SYS_ADMIN status"); if (CAP_IS_SUPPORTED(CAP_BPF))
cap_sys_admin_only = false;
#endif
for (i = 0; i < ARRAY_SIZE(bpf_caps); i++) {
const char *cap_name = bpf_caps[i].name;
cap_value_t cap = bpf_caps[i].cap;
if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val)) {
p_err("bug: failed to retrieve %s status: %s", cap_name,
strerror(errno));
goto exit_free; goto exit_free;
} }
if (val == CAP_SET)
has_sys_admin_cap = true;
if (!run_as_unprivileged && !has_sys_admin_cap) { if (val == CAP_SET) {
p_err("full feature probing requires CAP_SYS_ADMIN, run as root or use 'unprivileged'"); bpf_caps[i].set = true;
goto exit_free; cap_list[nb_bpf_caps++] = cap;
}
if (cap_sys_admin_only)
/* System does not know about CAP_BPF, meaning that
* CAP_SYS_ADMIN is the only capability required. We
* just checked it, break.
*/
break;
} }
if ((run_as_unprivileged && !has_sys_admin_cap) || if ((run_as_unprivileged && !nb_bpf_caps) ||
(!run_as_unprivileged && has_sys_admin_cap)) { (!run_as_unprivileged && nb_bpf_caps == ARRAY_SIZE(bpf_caps)) ||
(!run_as_unprivileged && cap_sys_admin_only && nb_bpf_caps)) {
/* We are all good, exit now */ /* We are all good, exit now */
res = 0; res = 0;
goto exit_free; goto exit_free;
} }
/* if (run_as_unprivileged && has_sys_admin_cap), drop CAP_SYS_ADMIN */ if (!run_as_unprivileged) {
if (cap_sys_admin_only)
p_err("missing %s, required for full feature probing; run as root or use 'unprivileged'",
bpf_caps[0].name);
else
p_err("missing %s%s%s%s%s%s%s%srequired for full feature probing; run as root or use 'unprivileged'",
capability_msg(bpf_caps, 0),
capability_msg(bpf_caps, 1),
capability_msg(bpf_caps, 2),
capability_msg(bpf_caps, 3));
goto exit_free;
}
if (cap_set_flag(caps, CAP_EFFECTIVE, ARRAY_SIZE(cap_list), cap_list, /* if (run_as_unprivileged && nb_bpf_caps > 0), drop capabilities. */
if (cap_set_flag(caps, CAP_EFFECTIVE, nb_bpf_caps, cap_list,
CAP_CLEAR)) { CAP_CLEAR)) {
p_err("bug: failed to clear CAP_SYS_ADMIN from capabilities"); p_err("bug: failed to clear capabilities: %s", strerror(errno));
goto exit_free; goto exit_free;
} }
if (cap_set_proc(caps)) { if (cap_set_proc(caps)) {
p_err("failed to drop CAP_SYS_ADMIN: %s", strerror(errno)); p_err("failed to drop capabilities: %s", strerror(errno));
goto exit_free; goto exit_free;
} }
...@@ -817,7 +864,7 @@ static int handle_perms(void) ...@@ -817,7 +864,7 @@ static int handle_perms(void)
return res; return res;
#else #else
/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN). /* Detection assumes user has specific privileges.
* We do not use libpcap so let's approximate, and restrict usage to * We do not use libpcap so let's approximate, and restrict usage to
* root user only. * root user only.
*/ */
...@@ -901,7 +948,7 @@ static int do_probe(int argc, char **argv) ...@@ -901,7 +948,7 @@ static int do_probe(int argc, char **argv)
} }
} }
/* Full feature detection requires CAP_SYS_ADMIN privilege. /* Full feature detection requires specific privileges.
* Let's approximate, and warn if user is not root. * Let's approximate, and warn if user is not root.
*/ */
if (handle_perms()) if (handle_perms())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment