@@ -72,7 +72,7 @@ TARGETS += test_sctp.te
endif
ifeq ($(shell grep -q bpf $(POLDEV)/include/support/all_perms.spt && echo true),true)
-TARGETS += test_bpf.te test_fdreceive_bpf.te
+TARGETS += test_bpf.te test_fdreceive_bpf.te test_binder_bpf.te
endif
ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
new file mode 100644
@@ -0,0 +1,73 @@
+####### Policy for testing BPF file descriptor transfers via binder #########
+
+attribute binderbpfdomain;
+
+#
+################################## Manager ##################################
+#
+type test_binder_bpf_mgr_t;
+domain_type(test_binder_bpf_mgr_t)
+unconfined_runs_test(test_binder_bpf_mgr_t)
+typeattribute test_binder_bpf_mgr_t testdomain;
+typeattribute test_binder_bpf_mgr_t binderdomain;
+allow test_binder_bpf_mgr_t test_binder_bpf_client_t:binder { transfer };
+allow test_binder_bpf_mgr_t test_binder_client_no_bpf_perm_t:binder { transfer };
+allow test_binder_bpf_mgr_t device_t:chr_file { ioctl open read write };
+allow_map(test_binder_bpf_mgr_t, device_t, chr_file)
+allow test_binder_bpf_mgr_t self:binder { set_context_mgr };
+# For writing to flag file:
+allow test_binder_bpf_mgr_t test_file_t:fifo_file { rw_file_perms };
+
+#
+########################### Service Provider ################################
+#
+type test_binder_bpf_provider_t;
+domain_type(test_binder_bpf_provider_t)
+unconfined_runs_test(test_binder_bpf_provider_t)
+typeattribute test_binder_bpf_provider_t testdomain;
+typeattribute test_binder_bpf_provider_t binderbpfdomain;
+allow test_binder_bpf_provider_t test_binder_bpf_mgr_t:binder { call transfer };
+allow test_binder_bpf_provider_t device_t:chr_file { ioctl open read write };
+allow_map(test_binder_bpf_provider_t, device_t, chr_file)
+# For writing to flag file:
+allow test_binder_bpf_provider_t test_file_t:fifo_file { rw_file_perms };
+# For testing BPF map fd transfer:
+allow test_binder_bpf_provider_t self:bpf { map_create map_read map_write prog_load prog_run };
+allow test_binder_bpf_provider_t self:capability { sys_resource };
+allow test_binder_bpf_provider_t self:process { setrlimit };
+
+#
+################################# Client ####################################
+#
+type test_binder_bpf_client_t;
+domain_type(test_binder_bpf_client_t)
+unconfined_runs_test(test_binder_bpf_client_t)
+typeattribute test_binder_bpf_client_t testdomain;
+typeattribute test_binder_bpf_client_t binderbpfdomain;
+allow test_binder_bpf_client_t test_binder_bpf_provider_t:binder { call impersonate };
+allow test_binder_bpf_client_t test_binder_bpf_mgr_t:binder { call };
+allow test_binder_bpf_client_t test_binder_bpf_provider_t:fd { use };
+allow test_binder_bpf_client_t device_t:chr_file { getattr ioctl open read write };
+allow_map(test_binder_bpf_client_t, device_t, chr_file)
+# For testing BPF map fd transfer:
+allow test_binder_bpf_client_t test_binder_bpf_provider_t:bpf { map_read map_write prog_load prog_run };
+
+#
+######################## Client no BPF perms #############################
+#
+type test_binder_client_no_bpf_perm_t;
+domain_type(test_binder_client_no_bpf_perm_t)
+unconfined_runs_test(test_binder_client_no_bpf_perm_t)
+typeattribute test_binder_client_no_bpf_perm_t testdomain;
+typeattribute test_binder_client_no_bpf_perm_t binderbpfdomain;
+allow test_binder_client_no_bpf_perm_t test_binder_bpf_provider_t:binder { call impersonate };
+allow test_binder_client_no_bpf_perm_t test_binder_bpf_mgr_t:binder { call };
+allow test_binder_client_no_bpf_perm_t test_binder_bpf_provider_t:fd { use };
+allow test_binder_client_no_bpf_perm_t device_t:chr_file { getattr ioctl open read write };
+allow_map(test_binder_client_no_bpf_perm_t, device_t, chr_file)
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(binderbpfdomain)
+userdom_sysadm_entry_spec_domtrans_to(binderbpfdomain)
@@ -10,6 +10,11 @@ CFLAGS += -DHAVE_BINDERFS
TARGETS += check_binderfs
endif
+ifneq (,$(findstring -DHAVE_BPF,$(CFLAGS)))
+ DEPS += ../bpf/bpf_common.c ../bpf/bpf_common.h
+ LDLIBS += -lbpf
+endif
+
all: $(TARGETS)
clean:
@@ -3,13 +3,15 @@
* the raw ioctl commands to test the SELinux binder permissions:
* set_context_mgr, call, transfer, impersonate.
*
+ * If configured, the BPF permissions are also tested.
+ *
* Using binder test policy the following will be validated:
* security_binder_set_context_mgr() binder { set_context_mgr }
* security_binder_transaction() binder { call impersonate }
* security_binder_transfer_binder() binder { transfer }
* security_binder_transfer_file() fd { use }
- *
- * TODO security_binder_transfer_file() uses BPF if configured in kernel.
+ * bpf { map_create map_read map_write };
+ * bpf { prog_load prog_run };
*/
#include "binder_common.h"
@@ -67,8 +69,8 @@ void print_trans_data(const struct binder_transaction_data *txn_in)
case TEST_SERVICE_GET:
printf("\tcode: TEST_SERVICE_GET\n");
break;
- case TEST_SERVICE_SEND_CLIENT_SP_FD:
- printf("\tcode: TEST_SERVICE_SEND_CLIENT_SP_FD\n");
+ case TEST_SERVICE_SEND_FD:
+ printf("\tcode: TEST_SERVICE_SEND_FD\n");
break;
default:
printf("Unknown binder_transaction_data->code: %x\n",
@@ -15,6 +15,9 @@
#if HAVE_BINDERFS
#include <linux/android/binderfs.h>
#endif
+#if HAVE_BPF
+#include "../bpf/bpf_common.h"
+#endif
#define BINDER_DEV "/dev/binder"
#define BINDERFS_DEV "/dev/binderfs"
@@ -26,12 +29,20 @@
/* These are the Binder txn->code values used by the Service Provider, Client
* and Manager to request/retrieve a binder handle or file descriptor.
*/
-#define TEST_SERVICE_ADD 240616 /* Sent by Service Provider */
-#define TEST_SERVICE_GET 290317 /* Sent by Client */
-#define TEST_SERVICE_SEND_CLIENT_SP_FD 120419 /* Sent by Client */
+#define TEST_SERVICE_ADD 240616 /* Sent by Service Provider */
+#define TEST_SERVICE_GET 290317 /* Sent by Client */
+#define TEST_SERVICE_SEND_FD 311019 /* Sent by Client */
bool verbose;
const char *cmd_name(uint32_t cmd);
void print_trans_data(const struct binder_transaction_data *txn_in);
int binder_write(int fd, void *data, size_t len);
+
+enum {
+ BINDER_FD,
+ BPF_MAP_FD,
+ BPF_PROG_FD,
+ BPF_TEST
+} fd_type;
+char *fd_type_str;
@@ -6,7 +6,7 @@ static int transactions_complete;
static void usage(char *progname)
{
fprintf(stderr,
- "usage: %s [-c] [-n] [-r replies] [-v]\n"
+ "usage: %s [-c] [-n] [-r replies] [-m|-p] [-v]\n"
"Where:\n\t"
"-c Use the number of replies for the BR_TRANSACTION_COMPLETE"
" count.\n\t"
@@ -15,6 +15,8 @@ static void usage(char *progname)
" It can be the number of BR_TRANSACTION_COMPLETE if\n\t"
" the -c option is set or number of times to issue the\n\t"
" ioctl - BINDER_WRITE_READ command if -c not set.\n\t"
+ "-m Service Provider sending BPF map fd.\n\t"
+ "-p Service Provider sending BPF prog fd.\n\t"
"-v Print context and command information.\n\t"
"\nNote: Ensure this boolean command is run when "
"testing after a reboot:\n\t"
@@ -67,8 +69,12 @@ static void extract_fd_and_respond(const struct binder_transaction_data *txn_in)
}
if (verbose)
- printf("Client retrieved Service Providers fd: %d st_dev: %ld\n",
- obj->fd, sb.st_dev);
+ printf("Client retrieved %s fd: %d st_dev: %ld\n",
+ fd_type_str, obj->fd, sb.st_dev);
+
+ /* If testing BPF, then cannot do impersonate check */
+ if (fd_type > BINDER_FD)
+ return;
memset(&writebuf, 0, sizeof(writebuf));
memset(readbuf, 0, sizeof(readbuf));
@@ -141,7 +147,7 @@ static void request_service_provider_fd(int fd, uint32_t handle)
writebuf.cmd = BC_TRANSACTION;
writebuf.txn.target.handle = handle;
writebuf.txn.cookie = 0;
- writebuf.txn.code = TEST_SERVICE_SEND_CLIENT_SP_FD;
+ writebuf.txn.code = TEST_SERVICE_SEND_FD;
writebuf.txn.flags = TF_ACCEPT_FDS;
writebuf.txn.data_size = 0;
@@ -270,7 +276,7 @@ static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size)
if (txn->code == TEST_SERVICE_GET)
extract_handle_and_acquire(fd, txn);
- if (txn->code == TEST_SERVICE_SEND_CLIENT_SP_FD)
+ if (txn->code == TEST_SERVICE_SEND_FD)
extract_fd_and_respond(txn);
ptr += sizeof(*txn);
@@ -313,8 +319,10 @@ int main(int argc, char **argv)
unsigned int readbuf[32];
transactions_complete = 0;
+ fd_type = BINDER_FD;
+ fd_type_str = "SP";
- while ((opt = getopt(argc, argv, "cnr:v")) != -1) {
+ while ((opt = getopt(argc, argv, "cnr:vmp")) != -1) {
switch (opt) {
case 'c':
use_transactions_complete = true;
@@ -328,6 +336,14 @@ int main(int argc, char **argv)
case 'v':
verbose = true;
break;
+ case 'm':
+ fd_type = BPF_MAP_FD;
+ fd_type_str = "BPF map";
+ break;
+ case 'p':
+ fd_type = BPF_PROG_FD;
+ fd_type_str = "BPF prog";
+ break;
default:
usage(argv[0]);
}
@@ -91,7 +91,7 @@ static void do_service_manager(int fd, struct binder_transaction_data *txn_in)
reply_with_handle(fd, txn_in);
break;
- case TEST_SERVICE_SEND_CLIENT_SP_FD:
+ case TEST_SERVICE_SEND_FD:
if (verbose)
printf("Manager Rx'ed SEND_CLIENT_YOUR_BINDER_FD for handle: %d\n",
txn_in->target.handle);
@@ -6,11 +6,14 @@ static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size);
static void usage(char *progname)
{
fprintf(stderr,
- "usage: %s [-e expected_ctx] [-f file] [-n] [-v]\n"
+ "usage: %s -e expected_ctx] [-f file] [-n] [-m|-p|-t] [-v]\n"
"Where:\n\t"
"-e Expected security context.\n\t"
"-f Write a line to the file when listening starts.\n\t"
"-n Use the /dev/binderfs name service.\n\t"
+ "-m Use BPF map fd for transfer.\n\t"
+ "-p Use BPF prog fd for transfer.\n\t"
+ "-t Test if BPF enabled.\n\t"
"-v Print context and command information.\n\t"
"\nNote: Ensure this boolean command is run when "
"testing after a reboot:\n\t"
@@ -34,7 +37,7 @@ static void request_service_provider_fd(int fd,
}
if (verbose)
- printf("Service Provider sending BC_REPLY with its FD\n");
+ printf("Service Provider sending BC_REPLY with an FD\n");
memset(writebuf, 0, sizeof(writebuf));
memset(&bwr, 0, sizeof(bwr));
@@ -56,17 +59,47 @@ static void request_service_provider_fd(int fd,
obj.pad_flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
#endif
obj.cookie = txn->cookie;
- /* The Service Providers binder fd is used for testing as it allows
+
+ /*
+ * The Service Providers binder fd is used for testing as it allows
* policy to set whether the Service Provider and Client can be
* allowed access (fd use) or not.
* This also allows a check for the impersonate permission later as
* the Client will use the Service Provider fd to send a transaction.
+ *
+ * If a BPF fd is required, it is generated, however it cannot be
+ * used to check the impersonate permission.
*/
- obj.fd = fd;
+ switch (fd_type) {
+ case BINDER_FD:
+ obj.fd = fd;
+ break;
+#if HAVE_BPF
+ case BPF_MAP_FD:
+ obj.fd = create_bpf_map();
+ if (obj.fd < 0)
+ exit(70);
+ break;
+ case BPF_PROG_FD:
+ obj.fd = create_bpf_prog();
+ if (obj.fd < 0)
+ exit(71);
+ break;
+#else
+ case BPF_MAP_FD:
+ case BPF_PROG_FD:
+ fprintf(stderr, "BPF not supported - Service Provider\n");
+ exit(72);
+ break;
+#endif
+ default:
+ fprintf(stderr, "Invalid fd_type: %d\n", fd_type);
+ exit(73);
+ }
if (verbose)
- printf("Service Provider handle: %d and its FD: %d\n",
- txn->target.handle, fd);
+ printf("Service Provider handle: %d and %s FD: %d\n",
+ txn->target.handle, fd_type_str, obj.fd);
txn->data_size = sizeof(obj);
txn->data.ptr.buffer = (binder_uintptr_t)&obj;
@@ -81,7 +114,7 @@ static void request_service_provider_fd(int fd,
fprintf(stderr,
"Service Provider ioctl BINDER_WRITE_READ error: %s\n",
strerror(errno));
- exit(70);
+ exit(74);
}
}
@@ -119,7 +152,7 @@ static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size)
print_trans_data(txn);
}
- if (txn->code == TEST_SERVICE_SEND_CLIENT_SP_FD)
+ if (txn->code == TEST_SERVICE_SEND_FD)
request_service_provider_fd(fd, txn);
ptr += sizeof(*txn);
@@ -148,8 +181,7 @@ static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size)
}
}
- if (txn_ctx->transaction_data.code ==
- TEST_SERVICE_SEND_CLIENT_SP_FD)
+ if (txn_ctx->transaction_data.code == TEST_SERVICE_SEND_FD)
request_service_provider_fd(fd,
&txn_ctx->transaction_data);
@@ -209,8 +241,10 @@ int main(int argc, char **argv)
unsigned int readbuf[32];
expected_ctx = NULL;
+ fd_type = BINDER_FD;
+ fd_type_str = "SP";
- while ((opt = getopt(argc, argv, "e:f:nv")) != -1) {
+ while ((opt = getopt(argc, argv, "e:f:nvmpt")) != -1) {
switch (opt) {
case 'e':
expected_ctx = optarg;
@@ -224,11 +258,37 @@ int main(int argc, char **argv)
case 'v':
verbose = true;
break;
+ case 'm':
+ fd_type = BPF_MAP_FD;
+ fd_type_str = "BPF map";
+ break;
+ case 'p':
+ fd_type = BPF_PROG_FD;
+ fd_type_str = "BPF prog";
+ break;
+ case 't':
+ fd_type = BPF_TEST;
+ break;
default:
usage(argv[0]);
}
}
+
+#if HAVE_BPF
+ if (fd_type == BPF_TEST)
+ exit(0);
+
+ /* If BPF enabed, then need to set limits */
+ if (fd_type == BPF_MAP_FD || fd_type == BPF_PROG_FD)
+ bpf_setrlimit();
+#else
+ if (fd_type == BPF_TEST) {
+ fprintf(stderr, "BPF not supported\n");
+ exit(-1);
+ }
+#endif
+
/* Get our context and pid */
result = getcon(&context);
if (result < 0) {
@@ -267,19 +327,6 @@ int main(int argc, char **argv)
exit(63);
}
- if (flag_file) {
- flag_fd = fopen(flag_file, "w");
- if (!flag_fd) {
- fprintf(stderr,
- "Service Provider failed to open %s: %s\n",
- flag_file, strerror(errno));
- result = 64;
- goto brexit;
- }
- fprintf(flag_fd, "listening\n");
- fclose(flag_fd);
- }
-
memset(&writebuf, 0, sizeof(writebuf));
memset(&obj, 0, sizeof(obj));
memset(readbuf, 0, sizeof(readbuf));
@@ -322,7 +369,7 @@ int main(int argc, char **argv)
fprintf(stderr,
"Service Provider ioctl BINDER_WRITE_READ error: %s\n",
strerror(errno));
- result = 65;
+ result = 64;
goto brexit;
}
@@ -338,7 +385,7 @@ int main(int argc, char **argv)
cmd == BR_DEAD_BINDER) {
fprintf(stderr, "Service Provider %s() failing command %s, exiting.\n",
__func__, cmd_name(cmd));
- result = 66;
+ result = 65;
goto brexit;
}
@@ -358,10 +405,27 @@ int main(int argc, char **argv)
fprintf(stderr,
"Service Provider ioctl BINDER_WRITE_READ error: %s\n",
strerror(errno));
- result = 67;
+ result = 66;
goto brexit;
}
+ /*
+ * Ensure the Manager and Service Provider have completed the
+ * TEST_SERVICE_ADD sequence before the Client is allowed to start.
+ */
+ if (flag_file) {
+ flag_fd = fopen(flag_file, "w");
+ if (!flag_fd) {
+ fprintf(stderr,
+ "Service Provider failed to open %s: %s\n",
+ flag_file, strerror(errno));
+ result = 67;
+ goto brexit;
+ }
+ fprintf(flag_fd, "listening\n");
+ fclose(flag_fd);
+ }
+
while (true) {
memset(readbuf, 0, sizeof(readbuf));
bwr.read_size = sizeof(readbuf);
@@ -5,7 +5,7 @@ LDLIBS += -lselinux -lbpf
# export so that BPF_ENABLED entries get built correctly on local build
export CFLAGS += -DHAVE_BPF
-BPF_ENABLED = ../fdreceive
+BPF_ENABLED = ../fdreceive ../binder
all: $(TARGETS)
@set -e; for i in $(BPF_ENABLED); do $(MAKE) -C $$i all ; done
@@ -4,10 +4,12 @@ use Test::More;
BEGIN {
$basedir = $0;
$basedir =~ s|(.*)/[^/]*|$1|;
- $fdr_basedir = "$basedir/../fdreceive/";
+ $fdr_basedir = "$basedir/../fdreceive/";
+ $binder_basedir = "$basedir/../binder/";
$test_count = 7;
$test_fdreceive = 0;
+ $test_binder = 0;
# allow info to be shown during tests
$v = $ARGV[0];
@@ -28,6 +30,17 @@ BEGIN {
$test_count += 4;
}
+ # Test if Binder is supported and BPF enabled
+ $result = system("$binder_basedir/check_binder $v 2>/dev/null");
+
+ if ( $result >> 8 eq 0 ) {
+ $result = system("$binder_basedir/service_provider -t 2>/dev/null");
+ if ( $result >> 8 eq 0 ) {
+ $test_binder = 1;
+ $test_count += 4;
+ }
+ }
+
plan tests => $test_count;
}
@@ -114,4 +127,73 @@ if ($test_fdreceive) {
system "rm -rf $basedir/test_sock $basedir/flag";
}
+#
+################ BPF Tests for binder #######################
+#
+sub service_start {
+ my ( $service, $runcon_args, $args ) = @_;
+ my $pid;
+ my $flag = $service . "_flag";
+
+ system("mkfifo $basedir/$flag");
+
+ if ( ( $pid = fork() ) == 0 ) {
+ exec
+"runcon $runcon_args $binder_basedir/$service -f $basedir/$flag $args";
+ }
+
+ # Wait for it to initialize.
+ system("read -t 5 <>$basedir/$flag");
+ return $pid;
+}
+
+sub service_end {
+ my ( $service, $pid ) = @_;
+ my $flag = $service . "_flag";
+
+ kill KILL, $pid;
+ waitpid $pid, 0;
+ system("rm -f $basedir/$flag");
+}
+
+if ($test_binder) {
+ ### Test BPF map fd on transfer ##################
+ $sm_pid = service_start( "manager", "-t test_binder_bpf_mgr_t", "$v" );
+ $sp_pid =
+ service_start( "service_provider", "-t test_binder_bpf_provider_t",
+ "-m $v" );
+
+ # Verify that the BPF map fd can be transferred.
+ $result =
+ system
+ "runcon -t test_binder_bpf_client_t $binder_basedir/client $v -m -r 1";
+ ok( $result eq 0 );
+
+ # Verify BPF no map perms.
+ $result = system
+"runcon -t test_binder_client_no_bpf_perm_t $binder_basedir/client $v -m -r 2 2>&1";
+ ok( $result >> 8 eq 141 );
+
+ ### Test BPF prog fd on transfer ##################
+ service_end( "service_provider", $sp_pid );
+ $sp_pid =
+ service_start( "service_provider", "-t test_binder_bpf_provider_t",
+ "-p $v" );
+
+ # Verify that the BPF prog fd can be transferred.
+ $result =
+ system
+ "runcon -t test_binder_bpf_client_t $binder_basedir/client $v -p -r 1";
+ ok( $result eq 0 );
+
+ # Verify BPF no prog perms.
+ $result = system
+"runcon -t test_binder_client_no_bpf_perm_t $binder_basedir/client $v -p -r 2 2>&1";
+ ok( $result >> 8 eq 141 );
+
+ # Kill the service provider & manager.
+ service_end( "service_provider", $sp_pid );
+ service_end( "manager", $sm_pid );
+}
+
exit;
Add BPF map & prog functions to test binder security_binder_transfer_file() Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> --- policy/Makefile | 2 +- policy/test_binder_bpf.te | 73 ++++++++++++++++++++ tests/binder/Makefile | 5 ++ tests/binder/binder_common.c | 10 +-- tests/binder/binder_common.h | 17 ++++- tests/binder/client.c | 28 ++++++-- tests/binder/manager.c | 2 +- tests/binder/service_provider.c | 118 ++++++++++++++++++++++++-------- tests/bpf/Makefile | 2 +- tests/bpf/test | 84 ++++++++++++++++++++++- 10 files changed, 297 insertions(+), 44 deletions(-) create mode 100644 policy/test_binder_bpf.te