Message ID | 20190925165910.32589-1-richard_c_haines@btinternet.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | [V2] selinux-testsuite: Add keys tests | expand |
On 9/25/19 12:59 PM, Richard Haines wrote: > Test all permissions associated with the key class. > > Note that kernel 5.3 commit keys: Fix request_key() lack of Link perm > check on found key ("504b69eb3c95180bc59f1ae9096ad4b10bbbf254") > added an additional check for link perm on request_key(). The tests > will support earlier kernels. > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> > --- > V2 Changes: > Added test permission checks between a keyring created by another process > Split out key_socket tests > Tested on latest Fedora 30 + Rawhide > > README.md | 4 +- > defconfig | 7 + > policy/Makefile | 4 + > policy/test_keys.te | 269 +++++++++++++++++++++++++++++++++++ > setenforce | 0 This seems to have been unintentionally added. > tests/Makefile | 4 + > tests/keys/.gitignore | 4 + > tests/keys/Makefile | 7 + > tests/keys/keyctl.c | 235 ++++++++++++++++++++++++++++++ > tests/keys/keyctl_relabel.c | 93 ++++++++++++ > tests/keys/keyring_service.c | 183 ++++++++++++++++++++++++ > tests/keys/keys_common.h | 16 +++ > tests/keys/request_keys.c | 227 +++++++++++++++++++++++++++++ > tests/keys/test | 126 ++++++++++++++++ > 14 files changed, 1178 insertions(+), 1 deletion(-) > create mode 100644 policy/test_keys.te > create mode 100644 setenforce > create mode 100644 tests/keys/.gitignore > create mode 100644 tests/keys/Makefile > create mode 100644 tests/keys/keyctl.c > create mode 100644 tests/keys/keyctl_relabel.c > create mode 100644 tests/keys/keyring_service.c > create mode 100644 tests/keys/keys_common.h > create mode 100644 tests/keys/request_keys.c > create mode 100755 tests/keys/test I still see failures with stock F30: keys/test ................... Failed KEYCTL_SESSION_TO_PARENT: Operation not permitted keys/test ................... 1/17 # Failed test at keys/test line 38. # Failed test at keys/test line 89. # Looks like you failed 2 tests of 17. keys/test ................... Dubious, test returned 2 (wstat 512, 0x200) Failed 2/17 subtests Test Summary Report ------------------- keys/test (Wstat: 512 Tests: 17 Failed: 2) Failed tests: 1, 11 Non-zero exit status: 2 The first test fails even if I switch to permissive before running it. Looking at the kernel code, keyctl_session_to_parent() returns -EPERM for any of the following cases: 1) parent is init or a kernel thread 2) parent is multi-threaded 3) parent has different uid/gid 4) parent keyring has different uid This is running the testsuite from su or sudo from a non-root account mapped to unconfined_u (i.e. default). For the first test, you aren't creating a parent/child relationship so you can't strictly guarantee that these conditions are met by the parent. For the 11th test, the test passes if I execute it in permissive mode so it is a SELinux denial. I see these denials that appear to be related: type=AVC msg=audit(09/25/2019 14:21:51.893:5717) : avc: denied { view } for pid=11069 comm=request_keys scontext=unconfined_u:unconfined_r:test_request_keys_t:s0-s0:c0.c1023 tcontext=system_u:system_r:init_t:s0 tclass=key permissive=1 type=AVC msg=audit(09/25/2019 14:21:51.893:5716) : avc: denied { link } for pid=11069 comm=request_keys scontext=unconfined_u:unconfined_r:test_request_keys_t:s0-s0:c0.c1023 tcontext=system_u:system_r:init_t:s0 tclass=key permissive=1 It appears that the parent keyring was created by init? > > diff --git a/README.md b/README.md > index 1396c8e..e845df8 100644 > --- a/README.md > +++ b/README.md > @@ -52,6 +52,7 @@ similar dependencies): > * lksctp-tools-devel _(to build the SCTP test programs)_ > * attr _(tools used by the overlayfs tests)_ > * libbpf-devel _(tools used by the bpf tests)_ > +* keyutils-libs-devel _(tools used by the keys tests)_ > > On a modern Fedora system you can install these dependencies with the > following command: > @@ -67,7 +68,8 @@ following command: > iptables \ > lksctp-tools-devel \ > attr \ > - libbpf-devel > + libbpf-devel \ > + keyutils-libs-devel > > The testsuite requires a pre-existing base policy configuration of SELinux, > using either the old example policy or the reference policy as the baseline. > diff --git a/defconfig b/defconfig > index cb57f22..b13075d 100644 > --- a/defconfig > +++ b/defconfig > @@ -67,3 +67,10 @@ CONFIG_ANDROID_BINDERFS=y > # They are not required for SELinux operation itself. > CONFIG_BPF=y > CONFIG_BPF_SYSCALL=y > + > +# Keys implementation. > +# These are enabled to test the key controls in tests/keys; they are > +# not required for SELinux operation itself. > +CONFIG_KEYS=y > +CONFIG_KEYS_COMPAT=y > +CONFIG_KEY_DH_OPERATIONS=y > diff --git a/policy/Makefile b/policy/Makefile > index a5942b3..5c2c438 100644 > --- a/policy/Makefile > +++ b/policy/Makefile > @@ -82,6 +82,10 @@ ifeq ($(shell grep -q bpf $(POLDEV)/include/support/all_perms.spt && echo true), > TARGETS += test_bpf.te test_fdreceive_bpf.te test_binder_bpf.te > endif > > +ifeq ($(shell grep -q all_key_perms $(POLDEV)/include/support/all_perms.spt && echo true),true) > +TARGETS += test_keys.te > +endif > + > ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) > TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS)) > endif > diff --git a/policy/test_keys.te b/policy/test_keys.te > new file mode 100644 > index 0000000..c184409 > --- /dev/null > +++ b/policy/test_keys.te > @@ -0,0 +1,269 @@ > +# > +################# KEY selinux-testsuite policy module ###################### > +# > +attribute keydomain; > + > +# > +############################## Define Macro ################################ > +# > +# Do not use domain_type() macro as it has allow 'key { link search }' > +# in base module so 'allow domain self:key ~{ link search };' will not work > +# here. Add these instead to allow key perms to be controlled by this module. > +# > +gen_require(` > + type setrans_var_run_t, syslogd_t; > +') > + > +define(`key_domain_type',` > + allow $1 proc_t:dir { search }; > + allow $1 proc_t:lnk_file { read }; > + allow $1 self:dir { search }; > + allow $1 self:file { open read write }; > + dontaudit init_t syslogd_t:fd use; > + dontaudit $1 security_t:filesystem getattr; > + dontaudit $1 self:file getattr; > + dontaudit $1 setrans_var_run_t:dir search; > + dontaudit unconfined_t $1:process { noatsecure rlimitinh siginh }; > +') > + > +# > +####################### Main key class tests ##################### > +# > +type test_key_t; > +key_domain_type(test_key_t) > +unconfined_runs_test(test_key_t) > +typeattribute test_key_t testdomain; > +typeattribute test_key_t keydomain; > + > +allow test_key_t self:process { setkeycreate }; > +allow test_key_t self:key { create write search read view link setattr }; > + > +# Set new context on a keyring: > +type test_newcon_key_t; > +key_domain_type(test_newcon_key_t) > +unconfined_runs_test(test_newcon_key_t) > +typeattribute test_newcon_key_t testdomain; > +typeattribute test_newcon_key_t keydomain; > + > +allow test_key_t test_newcon_key_t:key { create write search view }; > + > +################# Deny process { setkeycreate } ####################### > +type test_no_setkeycreate_t; > +key_domain_type(test_no_setkeycreate_t) > +unconfined_runs_test(test_no_setkeycreate_t) > +typeattribute test_no_setkeycreate_t testdomain; > +typeattribute test_no_setkeycreate_t keydomain; > + > +###################### Deny key { create } ########################### > +type test_key_no_create_t; > +key_domain_type(test_key_no_create_t) > +unconfined_runs_test(test_key_no_create_t) > +typeattribute test_key_no_create_t testdomain; > +typeattribute test_key_no_create_t keydomain; > + > +allow test_key_no_create_t self:process { setkeycreate }; > +allow test_key_no_create_t self:key { write search read view link setattr }; > + > +###################### Deny key { write } ########################### > +type test_key_no_write_t; > +key_domain_type(test_key_no_write_t) > +unconfined_runs_test(test_key_no_write_t) > +typeattribute test_key_no_write_t testdomain; > +typeattribute test_key_no_write_t keydomain; > + > +allow test_key_no_write_t self:process { setkeycreate }; > +allow test_key_no_write_t self:key { create search read view link setattr }; > + > +###################### Deny key { search } ########################### > +type test_key_no_search_t; > +key_domain_type(test_key_no_search_t) > +unconfined_runs_test(test_key_no_search_t) > +typeattribute test_key_no_search_t testdomain; > +typeattribute test_key_no_search_t keydomain; > + > +allow test_key_no_search_t self:process { setkeycreate }; > +allow test_key_no_search_t self:key { create write read view link setattr }; > +dontaudit test_key_no_search_t test_key_t:key search; > + > +###################### Deny key { view } ########################### > +type test_key_no_view_t; > +key_domain_type(test_key_no_view_t) > +unconfined_runs_test(test_key_no_view_t) > +typeattribute test_key_no_view_t testdomain; > +typeattribute test_key_no_view_t keydomain; > + > +allow test_key_no_view_t self:process { setkeycreate }; > +allow test_key_no_view_t self:key { create write search read link setattr }; > +dontaudit test_key_no_view_t test_key_t:key search; > + > +###################### Deny key { read } ########################### > +type test_key_no_read_t; > +key_domain_type(test_key_no_read_t) > +unconfined_runs_test(test_key_no_read_t) > +typeattribute test_key_no_read_t testdomain; > +typeattribute test_key_no_read_t keydomain; > + > +allow test_key_no_read_t self:process { setkeycreate }; > +allow test_key_no_read_t self:key { create write search view link setattr }; > + > +###################### Deny key { link } ########################### > +type test_key_no_link_t; > +key_domain_type(test_key_no_link_t) > +unconfined_runs_test(test_key_no_link_t) > +typeattribute test_key_no_link_t testdomain; > +typeattribute test_key_no_link_t keydomain; > + > +allow test_key_no_link_t self:process { setkeycreate }; > +allow test_key_no_link_t self:key { create write search read view setattr }; > + > +###################### Deny key { setattr } ########################### > +type test_key_no_setattr_t; > +key_domain_type(test_key_no_setattr_t) > +unconfined_runs_test(test_key_no_setattr_t) > +typeattribute test_key_no_setattr_t testdomain; > +typeattribute test_key_no_setattr_t keydomain; > + > +allow test_key_no_setattr_t self:process { setkeycreate }; > +allow test_key_no_setattr_t self:key { create write search read view link }; > +dontaudit test_key_no_setattr_t test_key_t:key search; > + > +# > +############## keyring_service / request_keys process tests ############### > +# > +# Check between a process and a keyring created by another process in a > +# different security context. > +# > +type test_keyring_service_t; > +key_domain_type(test_keyring_service_t) > +unconfined_runs_test(test_keyring_service_t) > +typeattribute test_keyring_service_t testdomain; > +typeattribute test_keyring_service_t keydomain; > + > +allow test_keyring_service_t self:process { setkeycreate }; > +allow test_keyring_service_t self:key { create write search read view link setattr }; > +allow test_keyring_service_t init_t:key { write search }; > +allow test_keyring_service_t test_key_t:key { create write search }; > + > +allow test_keyring_service_t test_file_t:file execute_no_trans; > +allow test_keyring_service_t self : process { dyntransition }; > +allow test_keyring_service_t test_request_keys_t:process dyntransition; > +allow test_keyring_service_t test_request_keys_no_search_t:process dyntransition; > +allow test_keyring_service_t test_request_keys_no_read_t:process dyntransition; > +allow test_keyring_service_t test_request_keys_no_write_t:process dyntransition; > +allow test_keyring_service_t test_request_keys_no_view_t:process dyntransition; > +allow test_keyring_service_t test_request_keys_no_setattr_t:process dyntransition; > +allow test_keyring_service_t test_request_keys_no_link_t:process dyntransition; > + > +################################# request_keys ############################ > +type test_request_keys_t; > +key_domain_type(test_request_keys_t) > +unconfined_runs_test(test_request_keys_t) > +typeattribute test_request_keys_t testdomain; > +typeattribute test_request_keys_t keydomain; > + > +allow test_request_keys_t self:key { create write search read view link setattr }; > +allow test_request_keys_t init_t:key { search }; > +allow test_request_keys_t test_keyring_service_t:key { search read write view link setattr }; > +allow test_request_keys_t test_key_t:key { search view link }; > + > +################### request_keys deny { search } ############################ > +type test_request_keys_no_search_t; > +key_domain_type(test_request_keys_no_search_t) > +unconfined_runs_test(test_request_keys_no_search_t) > +typeattribute test_request_keys_no_search_t testdomain; > +typeattribute test_request_keys_no_search_t keydomain; > + > +allow test_request_keys_no_search_t self:key { create write search read view link setattr }; > +allow test_request_keys_no_search_t init_t:key { search }; > +allow test_request_keys_no_search_t test_keyring_service_t:key { write view setattr }; > +allow test_request_keys_no_search_t test_key_t:key search; > + > +################### request_keys deny { read } ############################ > +type test_request_keys_no_read_t; > +key_domain_type(test_request_keys_no_read_t) > +unconfined_runs_test(test_request_keys_no_read_t) > +typeattribute test_request_keys_no_read_t testdomain; > +typeattribute test_request_keys_no_read_t keydomain; > + > +allow test_request_keys_no_read_t self:key { create write search read view link setattr }; > +allow test_request_keys_no_read_t init_t:key { search }; > +allow test_request_keys_no_read_t test_keyring_service_t:key { write search view setattr link }; # link for 5.3 > +allow test_request_keys_no_read_t test_key_t:key search; > + > +################### request_keys deny { write } ############################ > +type test_request_keys_no_write_t; > +key_domain_type(test_request_keys_no_write_t) > +unconfined_runs_test(test_request_keys_no_write_t) > +typeattribute test_request_keys_no_write_t testdomain; > +typeattribute test_request_keys_no_write_t keydomain; > + > +allow test_request_keys_no_write_t self:key { create write search read view link setattr }; > +allow test_request_keys_no_write_t init_t:key { search }; > +allow test_request_keys_no_write_t test_keyring_service_t:key { search view setattr }; > +allow test_request_keys_no_write_t test_key_t:key search; > + > +################### request_keys deny { view } ############################ > +type test_request_keys_no_view_t; > +key_domain_type(test_request_keys_no_view_t) > +unconfined_runs_test(test_request_keys_no_view_t) > +typeattribute test_request_keys_no_view_t testdomain; > +typeattribute test_request_keys_no_view_t keydomain; > + > +allow test_request_keys_no_view_t self:key { create write search read view link setattr }; > +allow test_request_keys_no_view_t init_t:key { search }; > +allow test_request_keys_no_view_t test_keyring_service_t:key { search write setattr link }; # link for 5.3 > +allow test_request_keys_no_view_t test_key_t:key search; > + > +################### request_keys deny { setattr } ############################ > +type test_request_keys_no_setattr_t; > +key_domain_type(test_request_keys_no_setattr_t) > +unconfined_runs_test(test_request_keys_no_setattr_t) > +typeattribute test_request_keys_no_setattr_t testdomain; > +typeattribute test_request_keys_no_setattr_t keydomain; > + > +allow test_request_keys_no_setattr_t self:key { create write search read view link setattr }; > +allow test_request_keys_no_setattr_t init_t:key { search }; > +allow test_request_keys_no_setattr_t test_keyring_service_t:key { search read write link view }; > +allow test_request_keys_no_setattr_t test_key_t:key search; > + > +################### request_keys deny { link } ############################ > +type test_request_keys_no_link_t; > +key_domain_type(test_request_keys_no_link_t) > +unconfined_runs_test(test_request_keys_no_link_t) > +typeattribute test_request_keys_no_link_t testdomain; > +typeattribute test_request_keys_no_link_t keydomain; > + > +allow test_request_keys_no_link_t self:key { create write search read view link setattr }; > +allow test_request_keys_no_link_t init_t:key { search }; > +allow test_request_keys_no_link_t test_keyring_service_t:key { read write search view setattr }; > +allow test_request_keys_no_link_t test_key_t:key search; > + > +# > +########################## Test Watch Queue ############################## > +# > +type test_watch_t; > +key_domain_type(test_watch_t) > +unconfined_runs_test(test_watch_t) > +typeattribute test_watch_t testdomain; > +typeattribute test_watch_t keydomain; > + > +allow test_watch_t device_t:chr_file { ioctl open read write }; > +allow test_watch_t self:key { create write search read view link setattr }; > +allow_map(test_watch_t, device_t, chr_file) > + > +################# Deny Watch Queue key { view } ########################## > +type test_watch_no_view_t; > +key_domain_type(test_watch_no_view_t) > +unconfined_runs_test(test_watch_no_view_t) > +typeattribute test_watch_no_view_t testdomain; > +typeattribute test_watch_no_view_t keydomain; > + > +allow test_watch_no_view_t device_t:chr_file { ioctl open read write }; > +allow test_watch_no_view_t self:key { create write search read link setattr }; > +allow_map(test_watch_no_view_t, device_t, chr_file) > + > +# > +########### Allow these domains to be entered from sysadm domain ############ > +# > +miscfiles_domain_entry_test_files(keydomain) > +userdom_sysadm_entry_spec_domtrans_to(keydomain) > diff --git a/setenforce b/setenforce > new file mode 100644 > index 0000000..e69de29 > diff --git a/tests/Makefile b/tests/Makefile > index e5bdfff..42f7f40 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -48,6 +48,10 @@ export CFLAGS += -DHAVE_BPF > endif > endif > > +ifeq ($(shell grep -q all_key_perms $(POLDEV)/include/support/all_perms.spt && echo true),true) > +SUBDIRS += keys > +endif > + > ifeq ($(shell grep "^SELINUX_INFINIBAND_ENDPORT_TEST=" infiniband_endport/ibendport_test.conf | cut -d'=' -f 2),1) > SUBDIRS += infiniband_endport > endif > diff --git a/tests/keys/.gitignore b/tests/keys/.gitignore > new file mode 100644 > index 0000000..670a61e > --- /dev/null > +++ b/tests/keys/.gitignore > @@ -0,0 +1,4 @@ > +keyctl > +keyctl_relabel > +keyring_service > +request_key > diff --git a/tests/keys/Makefile b/tests/keys/Makefile > new file mode 100644 > index 0000000..d3793db > --- /dev/null > +++ b/tests/keys/Makefile > @@ -0,0 +1,7 @@ > +TARGETS = keyctl keyctl_relabel keyring_service request_keys > +LDLIBS += -lselinux -lkeyutils > + > +all: $(TARGETS) > + > +clean: > + rm -f $(TARGETS) > diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c > new file mode 100644 > index 0000000..d482a8f > --- /dev/null > +++ b/tests/keys/keyctl.c > @@ -0,0 +1,235 @@ > +#include "keys_common.h" > + > +static void usage(char *progname) > +{ > + fprintf(stderr, > + "usage: %s [-v]\n" > + "Where:\n\t" > + "-v Print information.\n", progname); > + exit(-1); > +} > + > +int main(int argc, char *argv[]) > +{ > + int opt, result; > + char *context, *keycreate_con; > + char r_con[256]; > + bool verbose = false; > + key_serial_t retrieved, search, link, compute, newring, > + private, prime, base, test_key; > + struct keyctl_dh_params params; > + > + while ((opt = getopt(argc, argv, "v")) != -1) { > + switch (opt) { > + case 'v': > + verbose = true; > + break; > + default: > + usage(argv[0]); > + } > + } > + > + result = getcon(&context); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain process context\n"); > + exit(1); > + } > + if (verbose) > + printf("Process context: %s\n", context); > + > + result = getkeycreatecon(&keycreate_con); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain keycreate context\n"); > + exit(2); > + } > + if (verbose) > + printf("Current keycreate context: %s\n", keycreate_con); > + free(keycreate_con); > + > + /* Set context requires process { setkeycreate } and key { create } */ > + result = setkeycreatecon(context); > + if (result < 0) { > + fprintf(stderr, "Failed setkeycreatecon(): %s\n", > + strerror(errno)); > + exit(3); > + } > + if (verbose) > + printf("Set keycreate context: %s\n", context); > + free(context); > + > + result = getkeycreatecon(&keycreate_con); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain keycreate context\n"); > + exit(4); > + } > + if (verbose) > + printf("New keycreate context: %s\n", keycreate_con); > + free(keycreate_con); > + > + /* > + * Add three keys as these will be required by the > + * keyctl(KEYCTL_DH_COMPUTE, ..) function. > + * These require key { create write } permissions. > + */ > + private = add_key("user", "private", payload, strlen(payload), > + KEY_SPEC_PROCESS_KEYRING); > + if (private < 0) { > + fprintf(stderr, "Failed add_key(private): %s\n", > + strerror(errno)); > + exit(5); > + } > + > + prime = add_key("user", "prime", payload, strlen(payload), > + KEY_SPEC_PROCESS_KEYRING); > + if (prime < 0) { > + fprintf(stderr, "Failed add_key(prime): %s\n", > + strerror(errno)); > + exit(6); > + } > + > + base = add_key("user", "base", payload, strlen(payload), > + KEY_SPEC_PROCESS_KEYRING); > + if (base < 0) { > + fprintf(stderr, "Failed add_key(base): %s\n", > + strerror(errno)); > + exit(7); > + } > + > + if (verbose) { > + printf("Private key ID: 0x%x\n", private); > + printf("Prime key ID: 0x%x\n", prime); > + printf("Base key ID: 0x%x\n", base); > + } > + > + /* Requires key { search }. From kernel 5.3 requires { link } */ > + retrieved = request_key("user", "private", NULL, > + KEY_SPEC_PROCESS_KEYRING); > + if (retrieved < 0) { > + fprintf(stderr, "Failed to request 'private' key: %s\n", > + strerror(errno)); > + exit(8); > + } > + > + /* Requires key { search } */ > + search = keyctl(KEYCTL_SEARCH, KEY_SPEC_PROCESS_KEYRING, "user", > + "base", 0); > + if (search < 0) { > + fprintf(stderr, "Failed to find 'base' key: %s\n", > + strerror(errno)); > + exit(9); > + } > + > + /* Requires key { view } */ > + result = keyctl(KEYCTL_GET_SECURITY, search, r_con, sizeof(r_con)); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain key context: %s\n", > + strerror(errno)); > + exit(10); > + } > + > + if (verbose) { > + printf("Requested 'private' key ID: 0x%x\n", retrieved); > + printf("Searched 'base' key ID: 0x%x\n", search); > + printf("Searched 'base' key context:\n\t%s\n", r_con); > + } > + > + /* Compute DH key, only obtain the length for test, not the key. */ > + params.priv = private; > + params.prime = prime; > + params.base = base; > + > + /* Requires key { create read write } */ > + compute = keyctl(KEYCTL_DH_COMPUTE, ¶ms, NULL, 0, 0); > + if (compute < 0) { > + fprintf(stderr, "Failed KEYCTL_DH_COMPUTE: %s\n", > + strerror(errno)); > + exit(11); > + } > + if (verbose) > + printf("KEYCTL_DH_COMPUTE key ID size: %d\n", compute); > + > + /* To test key { link }, need to generate a new keyring ID first */ > + newring = add_key("keyring", "my-keyring", NULL, 0, > + KEY_SPEC_PROCESS_KEYRING); > + if (newring < 0) { > + fprintf(stderr, "Failed to add new keyring: %s\n", > + strerror(errno)); > + exit(12); > + } > + if (verbose) > + printf("New keyring ID: 0x%x\n", newring); > + > + /* Requires key { write link } */ > + link = keyctl(KEYCTL_LINK, base, newring); > + if (link < 0) { > + fprintf(stderr, "Failed KEYCTL_LINK: %s\n", > + strerror(errno)); > + exit(13); > + } > + if (verbose) > + printf("Link key ID: 0x%x\n", newring); > + > + /* Requires key { setattr } */ > + link = keyctl(KEYCTL_SET_TIMEOUT, base, 1); > + if (link < 0) { > + fprintf(stderr, "Failed KEYCTL_SET_TIMEOUT: %s\n", > + strerror(errno)); > + exit(14); > + } > + if (verbose) > + printf("Set timeout\n"); > + > + /* Requires key { search } from 5.X key { inval } */ > + test_key = keyctl(KEYCTL_INVALIDATE, private); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_INVALIDATE(private): %s\n", > + strerror(errno)); > + exit(15); > + } > + if (verbose) > + printf("Invalidated 'private' key\n"); > + > + /* Requires key { write setattr } from 5.X key { revoke } */ > + test_key = keyctl(KEYCTL_REVOKE, prime); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_REVOKE(prime): %s\n", > + strerror(errno)); > + exit(16); > + } > + if (verbose) > + printf("Revoked 'prime' key\n"); > + > + /* Requires key { write } from 5.X key { clear } */ > + test_key = keyctl(KEYCTL_CLEAR, newring); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_CLEAR(newring): %s\n", > + strerror(errno)); > + exit(17); > + } > + if (verbose) > + printf("Cleared 'newring' keyring\n"); > + > + /* > + * To test key { join }, need to join session first. > + * Note that this call sets up the environment that will be > + * used by the 'request_keys' service when it calls: > + * keyctl(KEYCTL_SESSION_TO_PARENT) > + */ > + test_key = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "user"); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_JOIN_SESSION_KEYRING,: %s\n", > + strerror(errno)); > + exit(18); > + } > + /* Requires key { link } from 5.X key { join } */ > + test_key = keyctl(KEYCTL_SESSION_TO_PARENT); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_SESSION_TO_PARENT: %s\n", > + strerror(errno)); > + exit(19); > + } > + if (verbose) > + printf("Joined session to parent\n"); > + > + return 0; > +} > diff --git a/tests/keys/keyctl_relabel.c b/tests/keys/keyctl_relabel.c > new file mode 100644 > index 0000000..7bab510 > --- /dev/null > +++ b/tests/keys/keyctl_relabel.c > @@ -0,0 +1,93 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <errno.h> > +#include <stdbool.h> > +#include <keyutils.h> > +#include <selinux/selinux.h> > + > +static void usage(char *progname) > +{ > + fprintf(stderr, > + "usage: %s [-v] newcon\n" > + "Where:\n\t" > + "-v Print information.\n\t" > + "newcon New keyring context.\n", progname); > + exit(-1); > +} > + > +int main(int argc, char *argv[]) > +{ > + int opt, result; > + char *context, *keycreate_con; > + char r_con[256]; > + bool verbose = false; > + key_serial_t newring; > + > + while ((opt = getopt(argc, argv, "v")) != -1) { > + switch (opt) { > + case 'v': > + verbose = true; > + break; > + default: > + usage(argv[0]); > + } > + } > + > + if (optind >= argc) > + usage(argv[0]); > + > + result = getcon(&context); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain process context\n"); > + exit(1); > + } > + if (verbose) > + printf("Process context: %s\n", context); > + free(context); > + > + result = setkeycreatecon(argv[optind]); > + if (result < 0) { > + fprintf(stderr, "Failed setkeycreatecon(): %s\n", > + strerror(errno)); > + exit(2); > + } > + > + result = getkeycreatecon(&keycreate_con); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain keycreate context\n"); > + exit(3); > + } > + if (verbose) > + printf("New keycreate context: %s\n", keycreate_con); > + free(keycreate_con); > + > + newring = add_key("keyring", "my-keyring", NULL, 0, > + KEY_SPEC_PROCESS_KEYRING); > + if (newring < 0) { > + fprintf(stderr, "Failed to add new keyring: %s\n", > + strerror(errno)); > + exit(4); > + } > + > + result = keyctl(KEYCTL_GET_SECURITY, newring, r_con, sizeof(r_con)); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain key context: %s\n", > + strerror(errno)); > + exit(5); > + } > + > + if (strcmp(argv[optind], r_con)) { > + fprintf(stderr, "Relabel error - expected: %s got: %s\n", > + argv[optind], r_con); > + exit(6); > + } > + > + if (verbose) { > + printf("'my-keyring' key ID: 0x%x\n", newring); > + printf("'my-keyring' context:\n\t%s\n", r_con); > + } > + > + return 0; > +} > diff --git a/tests/keys/keyring_service.c b/tests/keys/keyring_service.c > new file mode 100644 > index 0000000..f150a6d > --- /dev/null > +++ b/tests/keys/keyring_service.c > @@ -0,0 +1,183 @@ > +#include "keys_common.h" > + > +static void usage(char *progname) > +{ > + fprintf(stderr, > + "usage: %s [-v] newdomain program\n" > + "Where:\n\t" > + "newdomain Type for new domain.\n\t" > + "program Program to be exec'd.\n\t" > + "-v Print information.\n", progname); > + exit(-1); > +} > + > +int main(int argc, char **argv) > +{ > + int opt, pid, result, status; > + bool verbose; > + char *context_s, *request_keys_argv[4]; > + context_t context; > + key_serial_t private, newring, link, test_key_1, test_key_2; > + char newringid_str[30]; > + > + verbose = false; > + request_keys_argv[3] = NULL; > + > + while ((opt = getopt(argc, argv, "v")) != -1) { > + switch (opt) { > + case 'v': > + verbose = true; > + request_keys_argv[3] = strdup("-v"); > + break; > + default: > + usage(argv[0]); > + } > + } > + > + if (argc - optind != 2) > + usage(argv[0]); > + > + if (verbose) > + printf("%s process information:\n", argv[0]); > + > + result = getcon(&context_s); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain process context\n"); > + exit(1); > + } > + if (verbose) > + printf("\tProcess context:\n\t\t%s\n", context_s); > + > + /* Set context requires process { setkeycreate } and key { create } */ > + result = setkeycreatecon(context_s); > + if (result < 0) { > + fprintf(stderr, "Failed setkeycreatecon(): %s\n", > + strerror(errno)); > + exit(3); > + } > + if (verbose) > + printf("\tSet keycreate context:\n\t\t%s\n", context_s); > + > + context = context_new(context_s); > + if (!context) { > + fprintf(stderr, "Unable to create context structure\n"); > + exit(2); > + } > + free(context_s); > + > + if (context_type_set(context, argv[optind])) { > + fprintf(stderr, "Unable to set new type\n"); > + exit(3); > + } > + > + context_s = context_str(context); > + if (!context_s) { > + fprintf(stderr, "Unable to obtain new context string\n"); > + exit(4); > + } > + if (verbose) > + printf("\t%s process context will transition to:\n\t\t%s\n", > + argv[optind], context_s); > + > + /* > + * Requires key { create write } permissions. > + * The 'newring' key ID is passed over to the 'request_keys' > + * service for various permission checks. > + */ > + newring = add_key("keyring", "keyring-service", NULL, 0, > + KEY_SPEC_SESSION_KEYRING); > + if (newring < 0) { > + fprintf(stderr, "Failed to add new keyring: %s\n", > + strerror(errno)); > + exit(5); > + } > + if (verbose) > + printf("\tNew keyring ID: 0x%x\n", newring); > + > + private = add_key("user", "private", payload, strlen(payload), > + KEY_SPEC_SESSION_KEYRING); > + if (private < 0) { > + fprintf(stderr, "Failed add_key(private): %s\n", > + strerror(errno)); > + exit(6); > + } > + if (verbose) > + printf("\tAdded 'private' key ID: 0x%x\n", private); > + > + test_key_1 = add_key("user", "test-key-1", payload, strlen(payload), > + KEY_SPEC_SESSION_KEYRING); > + if (test_key_1 < 0) { > + fprintf(stderr, "Failed add_key(test-key-1): %s\n", > + strerror(errno)); > + exit(6); > + } > + if (verbose) > + printf("\tAdded 'test-key-1' key ID: 0x%x\n", test_key_1); > + > + test_key_2 = add_key("user", "test-key-2", payload, strlen(payload), > + KEY_SPEC_SESSION_KEYRING); > + if (test_key_2 < 0) { > + fprintf(stderr, "Failed add_key(test-key-2): %s\n", > + strerror(errno)); > + exit(6); > + } > + if (verbose) > + printf("\tAdded 'test-key-2' key ID: 0x%x\n", test_key_2); > + > + /* Requires key { write link } */ > + link = keyctl(KEYCTL_LINK, private, newring); > + if (link < 0) { > + fprintf(stderr, "Failed KEYCTL_LINK: %s\n", > + strerror(errno)); > + exit(7); > + } > + if (verbose) > + printf("\tLink newring ID: 0x%x\n", newring); > + > + pid = fork(); > + if (pid < 0) { > + fprintf(stderr, "fork failed: %s\n", strerror(errno)); > + exit(9); > + } else if (pid == 0) { > + signal(SIGTRAP, SIG_IGN); > + request_keys_argv[0] = strdup(argv[optind + 1]); > + request_keys_argv[1] = strdup(context_s); > + sprintf(newringid_str, "%d", newring); > + request_keys_argv[2] = strdup(newringid_str); > + > + if (verbose) > + printf("\tExec parameters:\n\t\t%s\n\t\t%s\n\t\t0x%x\n\t\t%s\n", > + request_keys_argv[0], > + request_keys_argv[1], > + atoi(request_keys_argv[2]), > + request_keys_argv[3]); > + execv(request_keys_argv[0], request_keys_argv); > + fprintf(stderr, "execv of: %s failed: %s\n", > + request_keys_argv[0], strerror(errno)); > + exit(10); > + } > + > + pid = wait(&status); > + if (pid < 0) { > + fprintf(stderr, "wait() failed: %s\n", strerror(errno)); > + exit(11); > + } > + > + if (WIFEXITED(status)) { > + fprintf(stderr, "%s exited with status: %d\n", argv[optind + 1], > + WEXITSTATUS(status)); > + exit(WEXITSTATUS(status)); > + } > + > + if (WIFSIGNALED(status)) { > + fprintf(stderr, "%s terminated by signal: %d\n", argv[optind + 1], > + WTERMSIG(status)); > + fprintf(stderr, > + "..This is consistent with a %s permission denial - check the audit log.\n", > + argv[optind + 1]); > + exit(12); > + } > + > + fprintf(stderr, "Unexpected exit status 0x%x\n", status); > + exit(-1); > +} > diff --git a/tests/keys/keys_common.h b/tests/keys/keys_common.h > new file mode 100644 > index 0000000..df15280 > --- /dev/null > +++ b/tests/keys/keys_common.h > @@ -0,0 +1,16 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <string.h> > +#include <errno.h> > +#include <signal.h> > +#include <stdbool.h> > +#include <sys/wait.h> > +#include <keyutils.h> > +#include <selinux/selinux.h> > +#include <selinux/context.h> > + > +/* This is used as the payload for each add_key() */ > +static const char payload[] = > + " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\nQDEiR/cfhZrNUPgHRresult+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\nWw4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; > diff --git a/tests/keys/request_keys.c b/tests/keys/request_keys.c > new file mode 100644 > index 0000000..15ec136 > --- /dev/null > +++ b/tests/keys/request_keys.c > @@ -0,0 +1,227 @@ > +#include "keys_common.h" > + > +int main(int argc, char **argv) > +{ > + int nfi, result; > + char r_con[512], type[20], desc[30], *context; > + bool verbose = false; > + key_serial_t private, prime, base, compute, link, test_key; > + key_serial_t test_key_1, test_key_2; > + struct keyctl_dh_params params; > + > + /* > + * There are three parameters passed: > + * 1 - The security context for setcon(3) > + * 2 - A string containing the 'newring' key ID required > + * for various permission checks. > + * 3 - Verbose mode > + */ > + if (argv[3] != NULL) > + verbose = true; > + > + if (verbose) > + printf("%s process information:\n", argv[0]); > + > + /* > + * Use setcon() as policy will not allow multiple type_transition > + * statements using the same target with different process types. > + */ > + result = setcon(argv[1]); > + if (result < 0) { > + fprintf(stderr, "setcon() failed to set process context: %s\n", > + argv[1]); > + exit(1); > + } > + > + result = getcon(&context); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain process context\n"); > + exit(2); > + } > + if (verbose) > + printf("\tProcess context:\n\t\t%s\n", context); > + > + free(context); > + > + /* Requires key { search write }. From kernel 5.3 requires { link } */ > + private = request_key("user", "private", NULL, atoi(argv[2])); > + if (private < 0) { > + fprintf(stderr, "Failed to request 'private' key: %s\n", > + strerror(errno)); > + exit(3); > + } > + if (verbose) > + printf("\tRequested 'private' key ID: 0x%x\n", private); > + > + /* Requires key { search }. From kernel 5.3 requires { link } */ > + test_key_1 = request_key("user", "test-key-1", NULL, atoi(argv[2])); > + if (test_key_1 < 0) { > + fprintf(stderr, "Failed to request 'test-key-1' key: %s\n", > + strerror(errno)); > + exit(3); > + } > + if (verbose) > + printf("\tRequested 'test-key-1' key ID: 0x%x\n", private); > + > + test_key_2 = request_key("user", "test-key-2", NULL, atoi(argv[2])); > + if (test_key_2 < 0) { > + fprintf(stderr, "Failed to request 'test-key-2' key: %s\n", > + strerror(errno)); > + exit(3); > + } > + if (verbose) > + printf("\tRequested 'test-key-2' key ID: 0x%x\n", private); > + > + /* Requires key { view } */ > + result = keyctl(KEYCTL_GET_SECURITY, private, r_con, sizeof(r_con)); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain key context: %s\n", > + strerror(errno)); > + exit(4); > + } > + if (verbose) > + printf("\t'private' key context:\n\t\t%s\n", r_con); > + > + prime = add_key("user", "prime", payload, strlen(payload), > + atoi(argv[2])); > + if (prime < 0) { > + fprintf(stderr, "Failed add_key(prime): %s\n", > + strerror(errno)); > + exit(5); > + } > + if (verbose) > + printf("\tAdded 'prime' key ID: 0x%x\n", prime); > + > + result = keyctl(KEYCTL_GET_SECURITY, prime, r_con, sizeof(r_con)); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain 'prime' key context: %s\n", > + strerror(errno)); > + exit(6); > + } > + if (verbose) > + printf("\t'prime' key context:\n\t\t%s\n", r_con); > + > + base = add_key("user", "base", payload, strlen(payload), > + KEY_SPEC_PROCESS_KEYRING); > + if (base < 0) { > + fprintf(stderr, "Failed add_key(base): %s\n", > + strerror(errno)); > + exit(7); > + } > + if (verbose) > + printf("\tAdded 'base' key ID: 0x%x\n", base); > + > + result = keyctl(KEYCTL_GET_SECURITY, prime, r_con, sizeof(r_con)); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain 'base' key context: %s\n", > + strerror(errno)); > + exit(8); > + } > + if (verbose) > + printf("\t'base' key context:\n\t\t%s\n", r_con); > + > + /* Compute DH key, only obtain the length for test, not the key. */ > + params.priv = private; > + params.prime = prime; > + params.base = base; > + > + /* Requires key { create(local) read write } */ > + compute = keyctl(KEYCTL_DH_COMPUTE, ¶ms, NULL, 0, 0); > + if (compute < 0) { > + fprintf(stderr, "Failed KEYCTL_DH_COMPUTE: %s\n", > + strerror(errno)); > + exit(9); > + } > + if (verbose) > + printf("\tKEYCTL_DH_COMPUTE key ID size: %d\n", compute); > + > + /* Requires key { write link } */ > + link = keyctl(KEYCTL_LINK, private, atoi(argv[2])); /* newring */ > + if (link < 0) { > + fprintf(stderr, "Failed KEYCTL_LINK: %s\n", > + strerror(errno)); > + exit(10); > + } > + if (verbose) > + printf("\tLink key ID: 0x%x\n", atoi(argv[2])); > + > + test_key = keyctl(KEYCTL_DESCRIBE, atoi(argv[2]), r_con, sizeof(r_con)); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_DESCRIBE: %s\n", > + strerror(errno)); > + exit(11); > + } > + /* Requires key { setattr } */ > + test_key = keyctl(KEYCTL_SET_TIMEOUT, atoi(argv[2]), 2); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_SET_TIMEOUT: %s\n", > + strerror(errno)); > + exit(12); > + } > + if (verbose) { > + nfi = sscanf(r_con, "%[^;];%d;%d;%x;%s", > + type, &nfi, &nfi, &nfi, desc); > + printf("\tSet timeout on Key Type: '%s' Description: '%s'\n", > + type, desc); > + } > + > + /* Requires key { search } from 5.X key { inval } */ > + test_key = keyctl(KEYCTL_INVALIDATE, test_key_1); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_INVALIDATE(test-key-1): %s\n", > + strerror(errno)); > + exit(13); > + } > + if (verbose) > + printf("\tInvalidated 'test-key-1' key\n"); > + > + /* Requires key { write setattr } from 5.X key { revoke } */ > + test_key = keyctl(KEYCTL_REVOKE, test_key_2); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_REVOKE(test-key-2): %s\n", > + strerror(errno)); > + exit(14); > + } > + if (verbose) > + printf("\tRevoked 'test-key-2' key\n"); > + > + /* Requires key { write } from 5.X key { clear } */ > + test_key = keyctl(KEYCTL_CLEAR, atoi(argv[2])); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_CLEAR(newring): %s\n", > + strerror(errno)); > + exit(15); > + } > + if (verbose) > + printf("\tCleared 'newring' keyring\n"); > + > + /* > + * The ./keyctl program executed: > + * keyctl(KEYCTL_JOIN_SESSION_KEYRING, "user") > + * for testing joining a keyring. The session security context will > + * therefore be from ./keyctl as described in keyctl(1): > + * Each process subscribes to a session keyring that is > + * inherited across (v)fork, exec and clone. > + * > + * Requires key { link } from 5.X key { join } > + */ > + test_key = keyctl(KEYCTL_SESSION_TO_PARENT); > + if (test_key < 0) { > + fprintf(stderr, "Failed KEYCTL_SESSION_TO_PARENT: %s\n", > + strerror(errno)); > + exit(16); > + } > + > + result = keyctl(KEYCTL_GET_SECURITY, KEY_SPEC_SESSION_KEYRING, > + r_con, sizeof(r_con)); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain 'KEY_SPEC_SESSION_KEYRING' context: %s\n", > + strerror(errno)); > + exit(17); > + } > + if (verbose) > + printf("\tJoined session to parent. The parent keyring context is:\n\t\t%s\n", > + r_con); > + > + exit(0); > +} > diff --git a/tests/keys/test b/tests/keys/test > new file mode 100755 > index 0000000..e469b9b > --- /dev/null > +++ b/tests/keys/test > @@ -0,0 +1,126 @@ > +#!/usr/bin/perl > +use Test::More; > + > +BEGIN { > + $basedir = $0; > + $basedir =~ s|(.*)/[^/]*|$1|; > + > + $test_count = 17; > + > + # allow info to be shown during tests > + $v = $ARGV[0]; > + if ($v) { > + if ( $v ne "-v" ) { > + plan skip_all => "Invalid option (use -v)"; > + } > + } > + else { > + $v = " "; > + } > + > + # From kernel 5.3 request_key() requires additional check of key { link } > + $kvercur = `uname -r`; > + chomp($kvercur); > + $kverminstream = "5.3"; > + $test_link_53 = 0; > + > + $result = `$basedir/../kvercmp $kvercur $kverminstream`; > + if ( $result >= 0 ) { > + $test_link_53 = 1; > + } > + > + plan tests => $test_count; > +} > + > +############ Test keyctl ############# > +print "Test key class permissions\n"; > +$result = system "runcon -t test_key_t $basedir/keyctl $v"; > +ok( $result eq 0 ); > + > +# Deny process { setkeycreate } > +$result = system "runcon -t test_no_setkeycreate_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 3 ); > + > +# Deny key { create } > +$result = system "runcon -t test_key_no_create_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 3 ); > + > +# Deny key { write } > +$result = system "runcon -t test_key_no_write_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 5 ); > + > +# Deny key { search } > +$result = system "runcon -t test_key_no_search_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 8 ); > + > +# Deny key { view } > +$result = system "runcon -t test_key_no_view_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 10 ); > + > +# Deny key { read } > +$result = system "runcon -t test_key_no_read_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 11 ); > + > +# Deny key { link } > +$result = system "runcon -t test_key_no_link_t $basedir/keyctl $v 2>&1"; > +if ($test_link_53) { > + ok( $result >> 8 eq 8 ); > +} > +else { > + ok( $result >> 8 eq 13 ); > +} > + > +# Deny key { setattr } > +$result = system "runcon -t test_key_no_setattr_t $basedir/keyctl $v 2>&1"; > +ok( $result >> 8 eq 14 ); > + > +########### Change keyring context ############## > +print "Change keyring context\n"; > +$result = system > +"runcon -t test_key_t $basedir/keyctl_relabel $v system_u:system_r:test_newcon_key_t:s0"; > +ok( $result eq 0 ); > + > +##### Test permission checks between a keyring created by a process and ############ > +##### a service in a different security context. > +print "Test permission checks between a keyring created by another process\n"; > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_t $basedir/request_keys 2>&1" > +); > +ok( $result eq 0 ); > + > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_search_t $basedir/request_keys 2>&1" > +); > +ok( $result >> 8 eq 3 ); > + > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_read_t $basedir/request_keys 2>&1" > +); > +ok( $result >> 8 eq 9 ); > + > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_write_t $basedir/request_keys 2>&1" > +); > +ok( $result >> 8 eq 3 ); > + > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_view_t $basedir/request_keys 2>&1" > +); > +ok( $result >> 8 eq 4 ); > + > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_setattr_t $basedir/request_keys 2>&1" > +); > +ok( $result >> 8 eq 12 ); > + > +$result = system( > +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_link_t $basedir/request_keys 2>&1" > +); > +if ($test_link_53) { > + ok( $result >> 8 eq 3 ); > +} > +else { > + ok( $result >> 8 eq 10 ); > +} > + > +exit; >
diff --git a/README.md b/README.md index 1396c8e..e845df8 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ similar dependencies): * lksctp-tools-devel _(to build the SCTP test programs)_ * attr _(tools used by the overlayfs tests)_ * libbpf-devel _(tools used by the bpf tests)_ +* keyutils-libs-devel _(tools used by the keys tests)_ On a modern Fedora system you can install these dependencies with the following command: @@ -67,7 +68,8 @@ following command: iptables \ lksctp-tools-devel \ attr \ - libbpf-devel + libbpf-devel \ + keyutils-libs-devel The testsuite requires a pre-existing base policy configuration of SELinux, using either the old example policy or the reference policy as the baseline. diff --git a/defconfig b/defconfig index cb57f22..b13075d 100644 --- a/defconfig +++ b/defconfig @@ -67,3 +67,10 @@ CONFIG_ANDROID_BINDERFS=y # They are not required for SELinux operation itself. CONFIG_BPF=y CONFIG_BPF_SYSCALL=y + +# Keys implementation. +# These are enabled to test the key controls in tests/keys; they are +# not required for SELinux operation itself. +CONFIG_KEYS=y +CONFIG_KEYS_COMPAT=y +CONFIG_KEY_DH_OPERATIONS=y diff --git a/policy/Makefile b/policy/Makefile index a5942b3..5c2c438 100644 --- a/policy/Makefile +++ b/policy/Makefile @@ -82,6 +82,10 @@ ifeq ($(shell grep -q bpf $(POLDEV)/include/support/all_perms.spt && echo true), TARGETS += test_bpf.te test_fdreceive_bpf.te test_binder_bpf.te endif +ifeq ($(shell grep -q all_key_perms $(POLDEV)/include/support/all_perms.spt && echo true),true) +TARGETS += test_keys.te +endif + ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS)) endif diff --git a/policy/test_keys.te b/policy/test_keys.te new file mode 100644 index 0000000..c184409 --- /dev/null +++ b/policy/test_keys.te @@ -0,0 +1,269 @@ +# +################# KEY selinux-testsuite policy module ###################### +# +attribute keydomain; + +# +############################## Define Macro ################################ +# +# Do not use domain_type() macro as it has allow 'key { link search }' +# in base module so 'allow domain self:key ~{ link search };' will not work +# here. Add these instead to allow key perms to be controlled by this module. +# +gen_require(` + type setrans_var_run_t, syslogd_t; +') + +define(`key_domain_type',` + allow $1 proc_t:dir { search }; + allow $1 proc_t:lnk_file { read }; + allow $1 self:dir { search }; + allow $1 self:file { open read write }; + dontaudit init_t syslogd_t:fd use; + dontaudit $1 security_t:filesystem getattr; + dontaudit $1 self:file getattr; + dontaudit $1 setrans_var_run_t:dir search; + dontaudit unconfined_t $1:process { noatsecure rlimitinh siginh }; +') + +# +####################### Main key class tests ##################### +# +type test_key_t; +key_domain_type(test_key_t) +unconfined_runs_test(test_key_t) +typeattribute test_key_t testdomain; +typeattribute test_key_t keydomain; + +allow test_key_t self:process { setkeycreate }; +allow test_key_t self:key { create write search read view link setattr }; + +# Set new context on a keyring: +type test_newcon_key_t; +key_domain_type(test_newcon_key_t) +unconfined_runs_test(test_newcon_key_t) +typeattribute test_newcon_key_t testdomain; +typeattribute test_newcon_key_t keydomain; + +allow test_key_t test_newcon_key_t:key { create write search view }; + +################# Deny process { setkeycreate } ####################### +type test_no_setkeycreate_t; +key_domain_type(test_no_setkeycreate_t) +unconfined_runs_test(test_no_setkeycreate_t) +typeattribute test_no_setkeycreate_t testdomain; +typeattribute test_no_setkeycreate_t keydomain; + +###################### Deny key { create } ########################### +type test_key_no_create_t; +key_domain_type(test_key_no_create_t) +unconfined_runs_test(test_key_no_create_t) +typeattribute test_key_no_create_t testdomain; +typeattribute test_key_no_create_t keydomain; + +allow test_key_no_create_t self:process { setkeycreate }; +allow test_key_no_create_t self:key { write search read view link setattr }; + +###################### Deny key { write } ########################### +type test_key_no_write_t; +key_domain_type(test_key_no_write_t) +unconfined_runs_test(test_key_no_write_t) +typeattribute test_key_no_write_t testdomain; +typeattribute test_key_no_write_t keydomain; + +allow test_key_no_write_t self:process { setkeycreate }; +allow test_key_no_write_t self:key { create search read view link setattr }; + +###################### Deny key { search } ########################### +type test_key_no_search_t; +key_domain_type(test_key_no_search_t) +unconfined_runs_test(test_key_no_search_t) +typeattribute test_key_no_search_t testdomain; +typeattribute test_key_no_search_t keydomain; + +allow test_key_no_search_t self:process { setkeycreate }; +allow test_key_no_search_t self:key { create write read view link setattr }; +dontaudit test_key_no_search_t test_key_t:key search; + +###################### Deny key { view } ########################### +type test_key_no_view_t; +key_domain_type(test_key_no_view_t) +unconfined_runs_test(test_key_no_view_t) +typeattribute test_key_no_view_t testdomain; +typeattribute test_key_no_view_t keydomain; + +allow test_key_no_view_t self:process { setkeycreate }; +allow test_key_no_view_t self:key { create write search read link setattr }; +dontaudit test_key_no_view_t test_key_t:key search; + +###################### Deny key { read } ########################### +type test_key_no_read_t; +key_domain_type(test_key_no_read_t) +unconfined_runs_test(test_key_no_read_t) +typeattribute test_key_no_read_t testdomain; +typeattribute test_key_no_read_t keydomain; + +allow test_key_no_read_t self:process { setkeycreate }; +allow test_key_no_read_t self:key { create write search view link setattr }; + +###################### Deny key { link } ########################### +type test_key_no_link_t; +key_domain_type(test_key_no_link_t) +unconfined_runs_test(test_key_no_link_t) +typeattribute test_key_no_link_t testdomain; +typeattribute test_key_no_link_t keydomain; + +allow test_key_no_link_t self:process { setkeycreate }; +allow test_key_no_link_t self:key { create write search read view setattr }; + +###################### Deny key { setattr } ########################### +type test_key_no_setattr_t; +key_domain_type(test_key_no_setattr_t) +unconfined_runs_test(test_key_no_setattr_t) +typeattribute test_key_no_setattr_t testdomain; +typeattribute test_key_no_setattr_t keydomain; + +allow test_key_no_setattr_t self:process { setkeycreate }; +allow test_key_no_setattr_t self:key { create write search read view link }; +dontaudit test_key_no_setattr_t test_key_t:key search; + +# +############## keyring_service / request_keys process tests ############### +# +# Check between a process and a keyring created by another process in a +# different security context. +# +type test_keyring_service_t; +key_domain_type(test_keyring_service_t) +unconfined_runs_test(test_keyring_service_t) +typeattribute test_keyring_service_t testdomain; +typeattribute test_keyring_service_t keydomain; + +allow test_keyring_service_t self:process { setkeycreate }; +allow test_keyring_service_t self:key { create write search read view link setattr }; +allow test_keyring_service_t init_t:key { write search }; +allow test_keyring_service_t test_key_t:key { create write search }; + +allow test_keyring_service_t test_file_t:file execute_no_trans; +allow test_keyring_service_t self : process { dyntransition }; +allow test_keyring_service_t test_request_keys_t:process dyntransition; +allow test_keyring_service_t test_request_keys_no_search_t:process dyntransition; +allow test_keyring_service_t test_request_keys_no_read_t:process dyntransition; +allow test_keyring_service_t test_request_keys_no_write_t:process dyntransition; +allow test_keyring_service_t test_request_keys_no_view_t:process dyntransition; +allow test_keyring_service_t test_request_keys_no_setattr_t:process dyntransition; +allow test_keyring_service_t test_request_keys_no_link_t:process dyntransition; + +################################# request_keys ############################ +type test_request_keys_t; +key_domain_type(test_request_keys_t) +unconfined_runs_test(test_request_keys_t) +typeattribute test_request_keys_t testdomain; +typeattribute test_request_keys_t keydomain; + +allow test_request_keys_t self:key { create write search read view link setattr }; +allow test_request_keys_t init_t:key { search }; +allow test_request_keys_t test_keyring_service_t:key { search read write view link setattr }; +allow test_request_keys_t test_key_t:key { search view link }; + +################### request_keys deny { search } ############################ +type test_request_keys_no_search_t; +key_domain_type(test_request_keys_no_search_t) +unconfined_runs_test(test_request_keys_no_search_t) +typeattribute test_request_keys_no_search_t testdomain; +typeattribute test_request_keys_no_search_t keydomain; + +allow test_request_keys_no_search_t self:key { create write search read view link setattr }; +allow test_request_keys_no_search_t init_t:key { search }; +allow test_request_keys_no_search_t test_keyring_service_t:key { write view setattr }; +allow test_request_keys_no_search_t test_key_t:key search; + +################### request_keys deny { read } ############################ +type test_request_keys_no_read_t; +key_domain_type(test_request_keys_no_read_t) +unconfined_runs_test(test_request_keys_no_read_t) +typeattribute test_request_keys_no_read_t testdomain; +typeattribute test_request_keys_no_read_t keydomain; + +allow test_request_keys_no_read_t self:key { create write search read view link setattr }; +allow test_request_keys_no_read_t init_t:key { search }; +allow test_request_keys_no_read_t test_keyring_service_t:key { write search view setattr link }; # link for 5.3 +allow test_request_keys_no_read_t test_key_t:key search; + +################### request_keys deny { write } ############################ +type test_request_keys_no_write_t; +key_domain_type(test_request_keys_no_write_t) +unconfined_runs_test(test_request_keys_no_write_t) +typeattribute test_request_keys_no_write_t testdomain; +typeattribute test_request_keys_no_write_t keydomain; + +allow test_request_keys_no_write_t self:key { create write search read view link setattr }; +allow test_request_keys_no_write_t init_t:key { search }; +allow test_request_keys_no_write_t test_keyring_service_t:key { search view setattr }; +allow test_request_keys_no_write_t test_key_t:key search; + +################### request_keys deny { view } ############################ +type test_request_keys_no_view_t; +key_domain_type(test_request_keys_no_view_t) +unconfined_runs_test(test_request_keys_no_view_t) +typeattribute test_request_keys_no_view_t testdomain; +typeattribute test_request_keys_no_view_t keydomain; + +allow test_request_keys_no_view_t self:key { create write search read view link setattr }; +allow test_request_keys_no_view_t init_t:key { search }; +allow test_request_keys_no_view_t test_keyring_service_t:key { search write setattr link }; # link for 5.3 +allow test_request_keys_no_view_t test_key_t:key search; + +################### request_keys deny { setattr } ############################ +type test_request_keys_no_setattr_t; +key_domain_type(test_request_keys_no_setattr_t) +unconfined_runs_test(test_request_keys_no_setattr_t) +typeattribute test_request_keys_no_setattr_t testdomain; +typeattribute test_request_keys_no_setattr_t keydomain; + +allow test_request_keys_no_setattr_t self:key { create write search read view link setattr }; +allow test_request_keys_no_setattr_t init_t:key { search }; +allow test_request_keys_no_setattr_t test_keyring_service_t:key { search read write link view }; +allow test_request_keys_no_setattr_t test_key_t:key search; + +################### request_keys deny { link } ############################ +type test_request_keys_no_link_t; +key_domain_type(test_request_keys_no_link_t) +unconfined_runs_test(test_request_keys_no_link_t) +typeattribute test_request_keys_no_link_t testdomain; +typeattribute test_request_keys_no_link_t keydomain; + +allow test_request_keys_no_link_t self:key { create write search read view link setattr }; +allow test_request_keys_no_link_t init_t:key { search }; +allow test_request_keys_no_link_t test_keyring_service_t:key { read write search view setattr }; +allow test_request_keys_no_link_t test_key_t:key search; + +# +########################## Test Watch Queue ############################## +# +type test_watch_t; +key_domain_type(test_watch_t) +unconfined_runs_test(test_watch_t) +typeattribute test_watch_t testdomain; +typeattribute test_watch_t keydomain; + +allow test_watch_t device_t:chr_file { ioctl open read write }; +allow test_watch_t self:key { create write search read view link setattr }; +allow_map(test_watch_t, device_t, chr_file) + +################# Deny Watch Queue key { view } ########################## +type test_watch_no_view_t; +key_domain_type(test_watch_no_view_t) +unconfined_runs_test(test_watch_no_view_t) +typeattribute test_watch_no_view_t testdomain; +typeattribute test_watch_no_view_t keydomain; + +allow test_watch_no_view_t device_t:chr_file { ioctl open read write }; +allow test_watch_no_view_t self:key { create write search read link setattr }; +allow_map(test_watch_no_view_t, device_t, chr_file) + +# +########### Allow these domains to be entered from sysadm domain ############ +# +miscfiles_domain_entry_test_files(keydomain) +userdom_sysadm_entry_spec_domtrans_to(keydomain) diff --git a/setenforce b/setenforce new file mode 100644 index 0000000..e69de29 diff --git a/tests/Makefile b/tests/Makefile index e5bdfff..42f7f40 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -48,6 +48,10 @@ export CFLAGS += -DHAVE_BPF endif endif +ifeq ($(shell grep -q all_key_perms $(POLDEV)/include/support/all_perms.spt && echo true),true) +SUBDIRS += keys +endif + ifeq ($(shell grep "^SELINUX_INFINIBAND_ENDPORT_TEST=" infiniband_endport/ibendport_test.conf | cut -d'=' -f 2),1) SUBDIRS += infiniband_endport endif diff --git a/tests/keys/.gitignore b/tests/keys/.gitignore new file mode 100644 index 0000000..670a61e --- /dev/null +++ b/tests/keys/.gitignore @@ -0,0 +1,4 @@ +keyctl +keyctl_relabel +keyring_service +request_key diff --git a/tests/keys/Makefile b/tests/keys/Makefile new file mode 100644 index 0000000..d3793db --- /dev/null +++ b/tests/keys/Makefile @@ -0,0 +1,7 @@ +TARGETS = keyctl keyctl_relabel keyring_service request_keys +LDLIBS += -lselinux -lkeyutils + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c new file mode 100644 index 0000000..d482a8f --- /dev/null +++ b/tests/keys/keyctl.c @@ -0,0 +1,235 @@ +#include "keys_common.h" + +static void usage(char *progname) +{ + fprintf(stderr, + "usage: %s [-v]\n" + "Where:\n\t" + "-v Print information.\n", progname); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int opt, result; + char *context, *keycreate_con; + char r_con[256]; + bool verbose = false; + key_serial_t retrieved, search, link, compute, newring, + private, prime, base, test_key; + struct keyctl_dh_params params; + + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + verbose = true; + break; + default: + usage(argv[0]); + } + } + + result = getcon(&context); + if (result < 0) { + fprintf(stderr, "Failed to obtain process context\n"); + exit(1); + } + if (verbose) + printf("Process context: %s\n", context); + + result = getkeycreatecon(&keycreate_con); + if (result < 0) { + fprintf(stderr, "Failed to obtain keycreate context\n"); + exit(2); + } + if (verbose) + printf("Current keycreate context: %s\n", keycreate_con); + free(keycreate_con); + + /* Set context requires process { setkeycreate } and key { create } */ + result = setkeycreatecon(context); + if (result < 0) { + fprintf(stderr, "Failed setkeycreatecon(): %s\n", + strerror(errno)); + exit(3); + } + if (verbose) + printf("Set keycreate context: %s\n", context); + free(context); + + result = getkeycreatecon(&keycreate_con); + if (result < 0) { + fprintf(stderr, "Failed to obtain keycreate context\n"); + exit(4); + } + if (verbose) + printf("New keycreate context: %s\n", keycreate_con); + free(keycreate_con); + + /* + * Add three keys as these will be required by the + * keyctl(KEYCTL_DH_COMPUTE, ..) function. + * These require key { create write } permissions. + */ + private = add_key("user", "private", payload, strlen(payload), + KEY_SPEC_PROCESS_KEYRING); + if (private < 0) { + fprintf(stderr, "Failed add_key(private): %s\n", + strerror(errno)); + exit(5); + } + + prime = add_key("user", "prime", payload, strlen(payload), + KEY_SPEC_PROCESS_KEYRING); + if (prime < 0) { + fprintf(stderr, "Failed add_key(prime): %s\n", + strerror(errno)); + exit(6); + } + + base = add_key("user", "base", payload, strlen(payload), + KEY_SPEC_PROCESS_KEYRING); + if (base < 0) { + fprintf(stderr, "Failed add_key(base): %s\n", + strerror(errno)); + exit(7); + } + + if (verbose) { + printf("Private key ID: 0x%x\n", private); + printf("Prime key ID: 0x%x\n", prime); + printf("Base key ID: 0x%x\n", base); + } + + /* Requires key { search }. From kernel 5.3 requires { link } */ + retrieved = request_key("user", "private", NULL, + KEY_SPEC_PROCESS_KEYRING); + if (retrieved < 0) { + fprintf(stderr, "Failed to request 'private' key: %s\n", + strerror(errno)); + exit(8); + } + + /* Requires key { search } */ + search = keyctl(KEYCTL_SEARCH, KEY_SPEC_PROCESS_KEYRING, "user", + "base", 0); + if (search < 0) { + fprintf(stderr, "Failed to find 'base' key: %s\n", + strerror(errno)); + exit(9); + } + + /* Requires key { view } */ + result = keyctl(KEYCTL_GET_SECURITY, search, r_con, sizeof(r_con)); + if (result < 0) { + fprintf(stderr, "Failed to obtain key context: %s\n", + strerror(errno)); + exit(10); + } + + if (verbose) { + printf("Requested 'private' key ID: 0x%x\n", retrieved); + printf("Searched 'base' key ID: 0x%x\n", search); + printf("Searched 'base' key context:\n\t%s\n", r_con); + } + + /* Compute DH key, only obtain the length for test, not the key. */ + params.priv = private; + params.prime = prime; + params.base = base; + + /* Requires key { create read write } */ + compute = keyctl(KEYCTL_DH_COMPUTE, ¶ms, NULL, 0, 0); + if (compute < 0) { + fprintf(stderr, "Failed KEYCTL_DH_COMPUTE: %s\n", + strerror(errno)); + exit(11); + } + if (verbose) + printf("KEYCTL_DH_COMPUTE key ID size: %d\n", compute); + + /* To test key { link }, need to generate a new keyring ID first */ + newring = add_key("keyring", "my-keyring", NULL, 0, + KEY_SPEC_PROCESS_KEYRING); + if (newring < 0) { + fprintf(stderr, "Failed to add new keyring: %s\n", + strerror(errno)); + exit(12); + } + if (verbose) + printf("New keyring ID: 0x%x\n", newring); + + /* Requires key { write link } */ + link = keyctl(KEYCTL_LINK, base, newring); + if (link < 0) { + fprintf(stderr, "Failed KEYCTL_LINK: %s\n", + strerror(errno)); + exit(13); + } + if (verbose) + printf("Link key ID: 0x%x\n", newring); + + /* Requires key { setattr } */ + link = keyctl(KEYCTL_SET_TIMEOUT, base, 1); + if (link < 0) { + fprintf(stderr, "Failed KEYCTL_SET_TIMEOUT: %s\n", + strerror(errno)); + exit(14); + } + if (verbose) + printf("Set timeout\n"); + + /* Requires key { search } from 5.X key { inval } */ + test_key = keyctl(KEYCTL_INVALIDATE, private); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_INVALIDATE(private): %s\n", + strerror(errno)); + exit(15); + } + if (verbose) + printf("Invalidated 'private' key\n"); + + /* Requires key { write setattr } from 5.X key { revoke } */ + test_key = keyctl(KEYCTL_REVOKE, prime); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_REVOKE(prime): %s\n", + strerror(errno)); + exit(16); + } + if (verbose) + printf("Revoked 'prime' key\n"); + + /* Requires key { write } from 5.X key { clear } */ + test_key = keyctl(KEYCTL_CLEAR, newring); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_CLEAR(newring): %s\n", + strerror(errno)); + exit(17); + } + if (verbose) + printf("Cleared 'newring' keyring\n"); + + /* + * To test key { join }, need to join session first. + * Note that this call sets up the environment that will be + * used by the 'request_keys' service when it calls: + * keyctl(KEYCTL_SESSION_TO_PARENT) + */ + test_key = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "user"); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_JOIN_SESSION_KEYRING,: %s\n", + strerror(errno)); + exit(18); + } + /* Requires key { link } from 5.X key { join } */ + test_key = keyctl(KEYCTL_SESSION_TO_PARENT); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_SESSION_TO_PARENT: %s\n", + strerror(errno)); + exit(19); + } + if (verbose) + printf("Joined session to parent\n"); + + return 0; +} diff --git a/tests/keys/keyctl_relabel.c b/tests/keys/keyctl_relabel.c new file mode 100644 index 0000000..7bab510 --- /dev/null +++ b/tests/keys/keyctl_relabel.c @@ -0,0 +1,93 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdbool.h> +#include <keyutils.h> +#include <selinux/selinux.h> + +static void usage(char *progname) +{ + fprintf(stderr, + "usage: %s [-v] newcon\n" + "Where:\n\t" + "-v Print information.\n\t" + "newcon New keyring context.\n", progname); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int opt, result; + char *context, *keycreate_con; + char r_con[256]; + bool verbose = false; + key_serial_t newring; + + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + verbose = true; + break; + default: + usage(argv[0]); + } + } + + if (optind >= argc) + usage(argv[0]); + + result = getcon(&context); + if (result < 0) { + fprintf(stderr, "Failed to obtain process context\n"); + exit(1); + } + if (verbose) + printf("Process context: %s\n", context); + free(context); + + result = setkeycreatecon(argv[optind]); + if (result < 0) { + fprintf(stderr, "Failed setkeycreatecon(): %s\n", + strerror(errno)); + exit(2); + } + + result = getkeycreatecon(&keycreate_con); + if (result < 0) { + fprintf(stderr, "Failed to obtain keycreate context\n"); + exit(3); + } + if (verbose) + printf("New keycreate context: %s\n", keycreate_con); + free(keycreate_con); + + newring = add_key("keyring", "my-keyring", NULL, 0, + KEY_SPEC_PROCESS_KEYRING); + if (newring < 0) { + fprintf(stderr, "Failed to add new keyring: %s\n", + strerror(errno)); + exit(4); + } + + result = keyctl(KEYCTL_GET_SECURITY, newring, r_con, sizeof(r_con)); + if (result < 0) { + fprintf(stderr, "Failed to obtain key context: %s\n", + strerror(errno)); + exit(5); + } + + if (strcmp(argv[optind], r_con)) { + fprintf(stderr, "Relabel error - expected: %s got: %s\n", + argv[optind], r_con); + exit(6); + } + + if (verbose) { + printf("'my-keyring' key ID: 0x%x\n", newring); + printf("'my-keyring' context:\n\t%s\n", r_con); + } + + return 0; +} diff --git a/tests/keys/keyring_service.c b/tests/keys/keyring_service.c new file mode 100644 index 0000000..f150a6d --- /dev/null +++ b/tests/keys/keyring_service.c @@ -0,0 +1,183 @@ +#include "keys_common.h" + +static void usage(char *progname) +{ + fprintf(stderr, + "usage: %s [-v] newdomain program\n" + "Where:\n\t" + "newdomain Type for new domain.\n\t" + "program Program to be exec'd.\n\t" + "-v Print information.\n", progname); + exit(-1); +} + +int main(int argc, char **argv) +{ + int opt, pid, result, status; + bool verbose; + char *context_s, *request_keys_argv[4]; + context_t context; + key_serial_t private, newring, link, test_key_1, test_key_2; + char newringid_str[30]; + + verbose = false; + request_keys_argv[3] = NULL; + + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + verbose = true; + request_keys_argv[3] = strdup("-v"); + break; + default: + usage(argv[0]); + } + } + + if (argc - optind != 2) + usage(argv[0]); + + if (verbose) + printf("%s process information:\n", argv[0]); + + result = getcon(&context_s); + if (result < 0) { + fprintf(stderr, "Failed to obtain process context\n"); + exit(1); + } + if (verbose) + printf("\tProcess context:\n\t\t%s\n", context_s); + + /* Set context requires process { setkeycreate } and key { create } */ + result = setkeycreatecon(context_s); + if (result < 0) { + fprintf(stderr, "Failed setkeycreatecon(): %s\n", + strerror(errno)); + exit(3); + } + if (verbose) + printf("\tSet keycreate context:\n\t\t%s\n", context_s); + + context = context_new(context_s); + if (!context) { + fprintf(stderr, "Unable to create context structure\n"); + exit(2); + } + free(context_s); + + if (context_type_set(context, argv[optind])) { + fprintf(stderr, "Unable to set new type\n"); + exit(3); + } + + context_s = context_str(context); + if (!context_s) { + fprintf(stderr, "Unable to obtain new context string\n"); + exit(4); + } + if (verbose) + printf("\t%s process context will transition to:\n\t\t%s\n", + argv[optind], context_s); + + /* + * Requires key { create write } permissions. + * The 'newring' key ID is passed over to the 'request_keys' + * service for various permission checks. + */ + newring = add_key("keyring", "keyring-service", NULL, 0, + KEY_SPEC_SESSION_KEYRING); + if (newring < 0) { + fprintf(stderr, "Failed to add new keyring: %s\n", + strerror(errno)); + exit(5); + } + if (verbose) + printf("\tNew keyring ID: 0x%x\n", newring); + + private = add_key("user", "private", payload, strlen(payload), + KEY_SPEC_SESSION_KEYRING); + if (private < 0) { + fprintf(stderr, "Failed add_key(private): %s\n", + strerror(errno)); + exit(6); + } + if (verbose) + printf("\tAdded 'private' key ID: 0x%x\n", private); + + test_key_1 = add_key("user", "test-key-1", payload, strlen(payload), + KEY_SPEC_SESSION_KEYRING); + if (test_key_1 < 0) { + fprintf(stderr, "Failed add_key(test-key-1): %s\n", + strerror(errno)); + exit(6); + } + if (verbose) + printf("\tAdded 'test-key-1' key ID: 0x%x\n", test_key_1); + + test_key_2 = add_key("user", "test-key-2", payload, strlen(payload), + KEY_SPEC_SESSION_KEYRING); + if (test_key_2 < 0) { + fprintf(stderr, "Failed add_key(test-key-2): %s\n", + strerror(errno)); + exit(6); + } + if (verbose) + printf("\tAdded 'test-key-2' key ID: 0x%x\n", test_key_2); + + /* Requires key { write link } */ + link = keyctl(KEYCTL_LINK, private, newring); + if (link < 0) { + fprintf(stderr, "Failed KEYCTL_LINK: %s\n", + strerror(errno)); + exit(7); + } + if (verbose) + printf("\tLink newring ID: 0x%x\n", newring); + + pid = fork(); + if (pid < 0) { + fprintf(stderr, "fork failed: %s\n", strerror(errno)); + exit(9); + } else if (pid == 0) { + signal(SIGTRAP, SIG_IGN); + request_keys_argv[0] = strdup(argv[optind + 1]); + request_keys_argv[1] = strdup(context_s); + sprintf(newringid_str, "%d", newring); + request_keys_argv[2] = strdup(newringid_str); + + if (verbose) + printf("\tExec parameters:\n\t\t%s\n\t\t%s\n\t\t0x%x\n\t\t%s\n", + request_keys_argv[0], + request_keys_argv[1], + atoi(request_keys_argv[2]), + request_keys_argv[3]); + execv(request_keys_argv[0], request_keys_argv); + fprintf(stderr, "execv of: %s failed: %s\n", + request_keys_argv[0], strerror(errno)); + exit(10); + } + + pid = wait(&status); + if (pid < 0) { + fprintf(stderr, "wait() failed: %s\n", strerror(errno)); + exit(11); + } + + if (WIFEXITED(status)) { + fprintf(stderr, "%s exited with status: %d\n", argv[optind + 1], + WEXITSTATUS(status)); + exit(WEXITSTATUS(status)); + } + + if (WIFSIGNALED(status)) { + fprintf(stderr, "%s terminated by signal: %d\n", argv[optind + 1], + WTERMSIG(status)); + fprintf(stderr, + "..This is consistent with a %s permission denial - check the audit log.\n", + argv[optind + 1]); + exit(12); + } + + fprintf(stderr, "Unexpected exit status 0x%x\n", status); + exit(-1); +} diff --git a/tests/keys/keys_common.h b/tests/keys/keys_common.h new file mode 100644 index 0000000..df15280 --- /dev/null +++ b/tests/keys/keys_common.h @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <stdbool.h> +#include <sys/wait.h> +#include <keyutils.h> +#include <selinux/selinux.h> +#include <selinux/context.h> + +/* This is used as the payload for each add_key() */ +static const char payload[] = + " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\nQDEiR/cfhZrNUPgHRresult+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\nWw4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; diff --git a/tests/keys/request_keys.c b/tests/keys/request_keys.c new file mode 100644 index 0000000..15ec136 --- /dev/null +++ b/tests/keys/request_keys.c @@ -0,0 +1,227 @@ +#include "keys_common.h" + +int main(int argc, char **argv) +{ + int nfi, result; + char r_con[512], type[20], desc[30], *context; + bool verbose = false; + key_serial_t private, prime, base, compute, link, test_key; + key_serial_t test_key_1, test_key_2; + struct keyctl_dh_params params; + + /* + * There are three parameters passed: + * 1 - The security context for setcon(3) + * 2 - A string containing the 'newring' key ID required + * for various permission checks. + * 3 - Verbose mode + */ + if (argv[3] != NULL) + verbose = true; + + if (verbose) + printf("%s process information:\n", argv[0]); + + /* + * Use setcon() as policy will not allow multiple type_transition + * statements using the same target with different process types. + */ + result = setcon(argv[1]); + if (result < 0) { + fprintf(stderr, "setcon() failed to set process context: %s\n", + argv[1]); + exit(1); + } + + result = getcon(&context); + if (result < 0) { + fprintf(stderr, "Failed to obtain process context\n"); + exit(2); + } + if (verbose) + printf("\tProcess context:\n\t\t%s\n", context); + + free(context); + + /* Requires key { search write }. From kernel 5.3 requires { link } */ + private = request_key("user", "private", NULL, atoi(argv[2])); + if (private < 0) { + fprintf(stderr, "Failed to request 'private' key: %s\n", + strerror(errno)); + exit(3); + } + if (verbose) + printf("\tRequested 'private' key ID: 0x%x\n", private); + + /* Requires key { search }. From kernel 5.3 requires { link } */ + test_key_1 = request_key("user", "test-key-1", NULL, atoi(argv[2])); + if (test_key_1 < 0) { + fprintf(stderr, "Failed to request 'test-key-1' key: %s\n", + strerror(errno)); + exit(3); + } + if (verbose) + printf("\tRequested 'test-key-1' key ID: 0x%x\n", private); + + test_key_2 = request_key("user", "test-key-2", NULL, atoi(argv[2])); + if (test_key_2 < 0) { + fprintf(stderr, "Failed to request 'test-key-2' key: %s\n", + strerror(errno)); + exit(3); + } + if (verbose) + printf("\tRequested 'test-key-2' key ID: 0x%x\n", private); + + /* Requires key { view } */ + result = keyctl(KEYCTL_GET_SECURITY, private, r_con, sizeof(r_con)); + if (result < 0) { + fprintf(stderr, "Failed to obtain key context: %s\n", + strerror(errno)); + exit(4); + } + if (verbose) + printf("\t'private' key context:\n\t\t%s\n", r_con); + + prime = add_key("user", "prime", payload, strlen(payload), + atoi(argv[2])); + if (prime < 0) { + fprintf(stderr, "Failed add_key(prime): %s\n", + strerror(errno)); + exit(5); + } + if (verbose) + printf("\tAdded 'prime' key ID: 0x%x\n", prime); + + result = keyctl(KEYCTL_GET_SECURITY, prime, r_con, sizeof(r_con)); + if (result < 0) { + fprintf(stderr, "Failed to obtain 'prime' key context: %s\n", + strerror(errno)); + exit(6); + } + if (verbose) + printf("\t'prime' key context:\n\t\t%s\n", r_con); + + base = add_key("user", "base", payload, strlen(payload), + KEY_SPEC_PROCESS_KEYRING); + if (base < 0) { + fprintf(stderr, "Failed add_key(base): %s\n", + strerror(errno)); + exit(7); + } + if (verbose) + printf("\tAdded 'base' key ID: 0x%x\n", base); + + result = keyctl(KEYCTL_GET_SECURITY, prime, r_con, sizeof(r_con)); + if (result < 0) { + fprintf(stderr, "Failed to obtain 'base' key context: %s\n", + strerror(errno)); + exit(8); + } + if (verbose) + printf("\t'base' key context:\n\t\t%s\n", r_con); + + /* Compute DH key, only obtain the length for test, not the key. */ + params.priv = private; + params.prime = prime; + params.base = base; + + /* Requires key { create(local) read write } */ + compute = keyctl(KEYCTL_DH_COMPUTE, ¶ms, NULL, 0, 0); + if (compute < 0) { + fprintf(stderr, "Failed KEYCTL_DH_COMPUTE: %s\n", + strerror(errno)); + exit(9); + } + if (verbose) + printf("\tKEYCTL_DH_COMPUTE key ID size: %d\n", compute); + + /* Requires key { write link } */ + link = keyctl(KEYCTL_LINK, private, atoi(argv[2])); /* newring */ + if (link < 0) { + fprintf(stderr, "Failed KEYCTL_LINK: %s\n", + strerror(errno)); + exit(10); + } + if (verbose) + printf("\tLink key ID: 0x%x\n", atoi(argv[2])); + + test_key = keyctl(KEYCTL_DESCRIBE, atoi(argv[2]), r_con, sizeof(r_con)); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_DESCRIBE: %s\n", + strerror(errno)); + exit(11); + } + /* Requires key { setattr } */ + test_key = keyctl(KEYCTL_SET_TIMEOUT, atoi(argv[2]), 2); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_SET_TIMEOUT: %s\n", + strerror(errno)); + exit(12); + } + if (verbose) { + nfi = sscanf(r_con, "%[^;];%d;%d;%x;%s", + type, &nfi, &nfi, &nfi, desc); + printf("\tSet timeout on Key Type: '%s' Description: '%s'\n", + type, desc); + } + + /* Requires key { search } from 5.X key { inval } */ + test_key = keyctl(KEYCTL_INVALIDATE, test_key_1); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_INVALIDATE(test-key-1): %s\n", + strerror(errno)); + exit(13); + } + if (verbose) + printf("\tInvalidated 'test-key-1' key\n"); + + /* Requires key { write setattr } from 5.X key { revoke } */ + test_key = keyctl(KEYCTL_REVOKE, test_key_2); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_REVOKE(test-key-2): %s\n", + strerror(errno)); + exit(14); + } + if (verbose) + printf("\tRevoked 'test-key-2' key\n"); + + /* Requires key { write } from 5.X key { clear } */ + test_key = keyctl(KEYCTL_CLEAR, atoi(argv[2])); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_CLEAR(newring): %s\n", + strerror(errno)); + exit(15); + } + if (verbose) + printf("\tCleared 'newring' keyring\n"); + + /* + * The ./keyctl program executed: + * keyctl(KEYCTL_JOIN_SESSION_KEYRING, "user") + * for testing joining a keyring. The session security context will + * therefore be from ./keyctl as described in keyctl(1): + * Each process subscribes to a session keyring that is + * inherited across (v)fork, exec and clone. + * + * Requires key { link } from 5.X key { join } + */ + test_key = keyctl(KEYCTL_SESSION_TO_PARENT); + if (test_key < 0) { + fprintf(stderr, "Failed KEYCTL_SESSION_TO_PARENT: %s\n", + strerror(errno)); + exit(16); + } + + result = keyctl(KEYCTL_GET_SECURITY, KEY_SPEC_SESSION_KEYRING, + r_con, sizeof(r_con)); + if (result < 0) { + fprintf(stderr, "Failed to obtain 'KEY_SPEC_SESSION_KEYRING' context: %s\n", + strerror(errno)); + exit(17); + } + if (verbose) + printf("\tJoined session to parent. The parent keyring context is:\n\t\t%s\n", + r_con); + + exit(0); +} diff --git a/tests/keys/test b/tests/keys/test new file mode 100755 index 0000000..e469b9b --- /dev/null +++ b/tests/keys/test @@ -0,0 +1,126 @@ +#!/usr/bin/perl +use Test::More; + +BEGIN { + $basedir = $0; + $basedir =~ s|(.*)/[^/]*|$1|; + + $test_count = 17; + + # allow info to be shown during tests + $v = $ARGV[0]; + if ($v) { + if ( $v ne "-v" ) { + plan skip_all => "Invalid option (use -v)"; + } + } + else { + $v = " "; + } + + # From kernel 5.3 request_key() requires additional check of key { link } + $kvercur = `uname -r`; + chomp($kvercur); + $kverminstream = "5.3"; + $test_link_53 = 0; + + $result = `$basedir/../kvercmp $kvercur $kverminstream`; + if ( $result >= 0 ) { + $test_link_53 = 1; + } + + plan tests => $test_count; +} + +############ Test keyctl ############# +print "Test key class permissions\n"; +$result = system "runcon -t test_key_t $basedir/keyctl $v"; +ok( $result eq 0 ); + +# Deny process { setkeycreate } +$result = system "runcon -t test_no_setkeycreate_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 3 ); + +# Deny key { create } +$result = system "runcon -t test_key_no_create_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 3 ); + +# Deny key { write } +$result = system "runcon -t test_key_no_write_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 5 ); + +# Deny key { search } +$result = system "runcon -t test_key_no_search_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 8 ); + +# Deny key { view } +$result = system "runcon -t test_key_no_view_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 10 ); + +# Deny key { read } +$result = system "runcon -t test_key_no_read_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 11 ); + +# Deny key { link } +$result = system "runcon -t test_key_no_link_t $basedir/keyctl $v 2>&1"; +if ($test_link_53) { + ok( $result >> 8 eq 8 ); +} +else { + ok( $result >> 8 eq 13 ); +} + +# Deny key { setattr } +$result = system "runcon -t test_key_no_setattr_t $basedir/keyctl $v 2>&1"; +ok( $result >> 8 eq 14 ); + +########### Change keyring context ############## +print "Change keyring context\n"; +$result = system +"runcon -t test_key_t $basedir/keyctl_relabel $v system_u:system_r:test_newcon_key_t:s0"; +ok( $result eq 0 ); + +##### Test permission checks between a keyring created by a process and ############ +##### a service in a different security context. +print "Test permission checks between a keyring created by another process\n"; +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_t $basedir/request_keys 2>&1" +); +ok( $result eq 0 ); + +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_search_t $basedir/request_keys 2>&1" +); +ok( $result >> 8 eq 3 ); + +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_read_t $basedir/request_keys 2>&1" +); +ok( $result >> 8 eq 9 ); + +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_write_t $basedir/request_keys 2>&1" +); +ok( $result >> 8 eq 3 ); + +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_view_t $basedir/request_keys 2>&1" +); +ok( $result >> 8 eq 4 ); + +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_setattr_t $basedir/request_keys 2>&1" +); +ok( $result >> 8 eq 12 ); + +$result = system( +"runcon -t test_keyring_service_t $basedir/keyring_service $v test_request_keys_no_link_t $basedir/request_keys 2>&1" +); +if ($test_link_53) { + ok( $result >> 8 eq 3 ); +} +else { + ok( $result >> 8 eq 10 ); +} + +exit;
Test all permissions associated with the key class. Note that kernel 5.3 commit keys: Fix request_key() lack of Link perm check on found key ("504b69eb3c95180bc59f1ae9096ad4b10bbbf254") added an additional check for link perm on request_key(). The tests will support earlier kernels. Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> --- V2 Changes: Added test permission checks between a keyring created by another process Split out key_socket tests Tested on latest Fedora 30 + Rawhide README.md | 4 +- defconfig | 7 + policy/Makefile | 4 + policy/test_keys.te | 269 +++++++++++++++++++++++++++++++++++ setenforce | 0 tests/Makefile | 4 + tests/keys/.gitignore | 4 + tests/keys/Makefile | 7 + tests/keys/keyctl.c | 235 ++++++++++++++++++++++++++++++ tests/keys/keyctl_relabel.c | 93 ++++++++++++ tests/keys/keyring_service.c | 183 ++++++++++++++++++++++++ tests/keys/keys_common.h | 16 +++ tests/keys/request_keys.c | 227 +++++++++++++++++++++++++++++ tests/keys/test | 126 ++++++++++++++++ 14 files changed, 1178 insertions(+), 1 deletion(-) create mode 100644 policy/test_keys.te create mode 100644 setenforce create mode 100644 tests/keys/.gitignore create mode 100644 tests/keys/Makefile create mode 100644 tests/keys/keyctl.c create mode 100644 tests/keys/keyctl_relabel.c create mode 100644 tests/keys/keyring_service.c create mode 100644 tests/keys/keys_common.h create mode 100644 tests/keys/request_keys.c create mode 100755 tests/keys/test