Skip to content

Commit cf9bf71

Browse files
qmonnetborkmann
authored andcommitted
tools: bpftool: Allow unprivileged users to probe features
There is demand for a way to identify what BPF helper functions are available to unprivileged users. To do so, allow unprivileged users to run "bpftool feature probe" to list BPF-related features. This will only show features accessible to those users, and may not reflect the full list of features available (to administrators) on the system. To avoid the case where bpftool is inadvertently run as non-root and would list only a subset of the features supported by the system when it would be expected to list all of them, running as unprivileged is gated behind the "unprivileged" keyword passed to the command line. When used by a privileged user, this keyword allows to drop the CAP_SYS_ADMIN and to list the features available to unprivileged users. Note that this addsd a dependency on libpcap for compiling bpftool. Note that there is no particular reason why the probes were restricted to root, other than the fact I did not need them for unprivileged and did not bother with the additional checks at the time probes were added. Signed-off-by: Quentin Monnet <quentin@isovalent.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: John Fastabend <john.fastabend@gmail.com> Link: https://lore.kernel.org/bpf/20200429144506.8999-3-quentin@isovalent.com
1 parent e3450b7 commit cf9bf71

File tree

4 files changed

+100
-16
lines changed

4 files changed

+100
-16
lines changed

tools/bpf/bpftool/Documentation/bpftool-feature.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ SYNOPSIS
1919
FEATURE COMMANDS
2020
================
2121

22-
| **bpftool** **feature probe** [*COMPONENT*] [**full**] [**macros** [**prefix** *PREFIX*]]
22+
| **bpftool** **feature probe** [*COMPONENT*] [**full**] [**unprivileged**] [**macros** [**prefix** *PREFIX*]]
2323
| **bpftool** **feature help**
2424
|
2525
| *COMPONENT* := { **kernel** | **dev** *NAME* }
@@ -49,6 +49,14 @@ DESCRIPTION
4949
Keyword **kernel** can be omitted. If no probe target is
5050
specified, probing the kernel is the default behaviour.
5151

52+
When the **unprivileged** keyword is used, bpftool will dump
53+
only the features available to a user who does not have the
54+
**CAP_SYS_ADMIN** capability set. The features available in
55+
that case usually represent a small subset of the parameters
56+
supported by the system. Unprivileged users MUST use the
57+
**unprivileged** keyword: This is to avoid misdetection if
58+
bpftool is inadvertently run as non-root, for example.
59+
5260
**bpftool feature probe dev** *NAME* [**full**] [**macros** [**prefix** *PREFIX*]]
5361
Probe network device for supported eBPF features and dump
5462
results to the console.

tools/bpf/bpftool/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ ifneq ($(EXTRA_LDFLAGS),)
5555
LDFLAGS += $(EXTRA_LDFLAGS)
5656
endif
5757

58-
LIBS = $(LIBBPF) -lelf -lz
58+
LIBS = $(LIBBPF) -lelf -lz -lcap
5959

6060
INSTALL ?= install
6161
RM ?= rm -f

tools/bpf/bpftool/bash-completion/bpftool

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,7 @@ _bpftool()
10791079
COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )
10801080
fi
10811081
_bpftool_one_of_list 'kernel dev'
1082-
_bpftool_once_attr 'full'
1082+
_bpftool_once_attr 'full unprivileged'
10831083
return 0
10841084
;;
10851085
*)

tools/bpf/bpftool/feature.c

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <string.h>
77
#include <unistd.h>
88
#include <net/if.h>
9+
#include <sys/capability.h>
910
#include <sys/utsname.h>
1011
#include <sys/vfs.h>
1112

@@ -36,6 +37,7 @@ static const char * const helper_name[] = {
3637
#undef BPF_HELPER_MAKE_ENTRY
3738

3839
static bool full_mode;
40+
static bool run_as_unprivileged;
3941

4042
/* Miscellaneous utility functions */
4143

@@ -473,6 +475,11 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
473475
}
474476

475477
res = bpf_probe_prog_type(prog_type, ifindex);
478+
/* Probe may succeed even if program load fails, for unprivileged users
479+
* check that we did not fail because of insufficient permissions
480+
*/
481+
if (run_as_unprivileged && errno == EPERM)
482+
res = false;
476483

477484
supported_types[prog_type] |= res;
478485

@@ -501,6 +508,10 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
501508

502509
res = bpf_probe_map_type(map_type, ifindex);
503510

511+
/* Probe result depends on the success of map creation, no additional
512+
* check required for unprivileged users
513+
*/
514+
504515
maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
505516
if (strlen(map_type_name[map_type]) > maxlen) {
506517
p_info("map type name too long");
@@ -520,12 +531,17 @@ probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
520531
const char *define_prefix, unsigned int id,
521532
const char *ptype_name, __u32 ifindex)
522533
{
523-
bool res;
534+
bool res = false;
524535

525-
if (!supported_type)
526-
res = false;
527-
else
536+
if (supported_type) {
528537
res = bpf_probe_helper(id, prog_type, ifindex);
538+
/* Probe may succeed even if program load fails, for
539+
* unprivileged users check that we did not fail because of
540+
* insufficient permissions
541+
*/
542+
if (run_as_unprivileged && errno == EPERM)
543+
res = false;
544+
}
529545

530546
if (json_output) {
531547
if (res)
@@ -720,6 +736,65 @@ static void section_misc(const char *define_prefix, __u32 ifindex)
720736
print_end_section();
721737
}
722738

739+
static int handle_perms(void)
740+
{
741+
cap_value_t cap_list[1] = { CAP_SYS_ADMIN };
742+
bool has_sys_admin_cap = false;
743+
cap_flag_value_t val;
744+
int res = -1;
745+
cap_t caps;
746+
747+
caps = cap_get_proc();
748+
if (!caps) {
749+
p_err("failed to get capabilities for process: %s",
750+
strerror(errno));
751+
return -1;
752+
}
753+
754+
if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &val)) {
755+
p_err("bug: failed to retrieve CAP_SYS_ADMIN status");
756+
goto exit_free;
757+
}
758+
if (val == CAP_SET)
759+
has_sys_admin_cap = true;
760+
761+
if (!run_as_unprivileged && !has_sys_admin_cap) {
762+
p_err("full feature probing requires CAP_SYS_ADMIN, run as root or use 'unprivileged'");
763+
goto exit_free;
764+
}
765+
766+
if ((run_as_unprivileged && !has_sys_admin_cap) ||
767+
(!run_as_unprivileged && has_sys_admin_cap)) {
768+
/* We are all good, exit now */
769+
res = 0;
770+
goto exit_free;
771+
}
772+
773+
/* if (run_as_unprivileged && has_sys_admin_cap), drop CAP_SYS_ADMIN */
774+
775+
if (cap_set_flag(caps, CAP_EFFECTIVE, ARRAY_SIZE(cap_list), cap_list,
776+
CAP_CLEAR)) {
777+
p_err("bug: failed to clear CAP_SYS_ADMIN from capabilities");
778+
goto exit_free;
779+
}
780+
781+
if (cap_set_proc(caps)) {
782+
p_err("failed to drop CAP_SYS_ADMIN: %s", strerror(errno));
783+
goto exit_free;
784+
}
785+
786+
res = 0;
787+
788+
exit_free:
789+
if (cap_free(caps) && !res) {
790+
p_err("failed to clear storage object for capabilities: %s",
791+
strerror(errno));
792+
res = -1;
793+
}
794+
795+
return res;
796+
}
797+
723798
static int do_probe(int argc, char **argv)
724799
{
725800
enum probe_component target = COMPONENT_UNSPEC;
@@ -728,14 +803,6 @@ static int do_probe(int argc, char **argv)
728803
__u32 ifindex = 0;
729804
char *ifname;
730805

731-
/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
732-
* Let's approximate, and restrict usage to root user only.
733-
*/
734-
if (geteuid()) {
735-
p_err("please run this command as root user");
736-
return -1;
737-
}
738-
739806
set_max_rlimit();
740807

741808
while (argc) {
@@ -784,13 +851,22 @@ static int do_probe(int argc, char **argv)
784851
if (!REQ_ARGS(1))
785852
return -1;
786853
define_prefix = GET_ARG();
854+
} else if (is_prefix(*argv, "unprivileged")) {
855+
run_as_unprivileged = true;
856+
NEXT_ARG();
787857
} else {
788858
p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",
789859
*argv);
790860
return -1;
791861
}
792862
}
793863

864+
/* Full feature detection requires CAP_SYS_ADMIN privilege.
865+
* Let's approximate, and warn if user is not root.
866+
*/
867+
if (handle_perms())
868+
return -1;
869+
794870
if (json_output) {
795871
define_prefix = NULL;
796872
jsonw_start_object(json_wtr);
@@ -821,7 +897,7 @@ static int do_help(int argc, char **argv)
821897
}
822898

823899
fprintf(stderr,
824-
"Usage: %s %s probe [COMPONENT] [full] [macros [prefix PREFIX]]\n"
900+
"Usage: %s %s probe [COMPONENT] [full] [unprivileged] [macros [prefix PREFIX]]\n"
825901
" %s %s help\n"
826902
"\n"
827903
" COMPONENT := { kernel | dev NAME }\n"

0 commit comments

Comments
 (0)