Message ID | 20190909131701.29588-1-richard_c_haines@btinternet.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Series | selinux-testsuite: Add key and key_socket tests | expand |
On 9/9/19 9:17 AM, Richard Haines wrote: > Test all permissions associated with the key and key_socket classes. > > 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. I'm not sure why you coupled key and key_socket together; they don't have anything to do with each other, and were introduced in very different kernel and probably refpolicy releases. I would recommend splitting them. SECCLASS_KEY and its permission checks were introduced in Linux v2.6.18; SECCLASS_KEY_SOCKET was part of the original SELinux merge for Linux 2.6.0. You only appear to be testing self access, not permission checks between a process and a keyring created by another process in a different security context. 1 test fails for me, keys/test ................... Failed KEYCTL_SESSION_TO_PARENT: Operation not permitted keys/test ................... 1/13 # Failed test at keys/test line 38. # Looks like you failed 1 test of 13. keys/test ................... Dubious, test returned 1 (wstat 256, 0x100) > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> > --- > README.md | 3 +- > defconfig | 8 ++ > policy/Makefile | 4 + > policy/test_keys.te | 161 ++++++++++++++++++++++++ > tests/Makefile | 4 + > tests/keys/.gitignore | 3 + > tests/keys/Makefile | 8 ++ > tests/keys/key_sock.c | 67 ++++++++++ > tests/keys/keyctl.c | 241 ++++++++++++++++++++++++++++++++++++ > tests/keys/keyctl_relabel.c | 93 ++++++++++++++ > tests/keys/test | 98 +++++++++++++++ > 11 files changed, 689 insertions(+), 1 deletion(-) > create mode 100644 policy/test_keys.te > create mode 100644 tests/keys/.gitignore > create mode 100644 tests/keys/Makefile > create mode 100644 tests/keys/key_sock.c > create mode 100644 tests/keys/keyctl.c > create mode 100644 tests/keys/keyctl_relabel.c > create mode 100755 tests/keys/test > > diff --git a/README.md b/README.md > index 26784f8..fe72a91 100644 > --- a/README.md > +++ b/README.md > @@ -65,7 +65,8 @@ following command: > netlabel_tools \ > iptables \ > lksctp-tools-devel \ > - attr > + attr \ > + 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 d7f0ea5..c00e291 100644 > --- a/defconfig > +++ b/defconfig > @@ -62,3 +62,11 @@ CONFIG_ANDROID_BINDER_IPC=y > # This will configure the Dynamically Allocated Binder Devices added > # to 5.0+ kernels: > CONFIG_ANDROID_BINDERFS=y > + > +# Key implementations. > +# These are enabled to test the key and key_socket controls in > +# tests/keys; they are not required for SELinux operation itself. > +CONFIG_KEYS=y > +CONFIG_KEYS_COMPAT=y > +CONFIG_KEY_DH_OPERATIONS=y > +CONFIG_NET_KEY=m > diff --git a/policy/Makefile b/policy/Makefile > index 305b572..9258a93 100644 > --- a/policy/Makefile > +++ b/policy/Makefile > @@ -71,6 +71,10 @@ ifeq ($(shell grep -q corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren > TARGETS += test_sctp.te > endif > > +ifeq ($(shell grep -q key_socket $(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, $(TARGETS)) > endif > diff --git a/policy/test_keys.te b/policy/test_keys.te > new file mode 100644 > index 0000000..9c65ec8 > --- /dev/null > +++ b/policy/test_keys.te > @@ -0,0 +1,161 @@ > +# > +################# 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: > +# > +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 }; > +') > + > +# > +####################### 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 }; > + > +###################### 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 }; > + > +###################### 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 }; > + > +# > +######################## Test key_socket class ########################### > +# > +type test_key_sock_t; > +domain_type(test_key_sock_t) > +unconfined_runs_test(test_key_sock_t) > +typeattribute test_key_sock_t testdomain; > +typeattribute test_key_sock_t keydomain; > + > +# key_socket rules: > +allow test_key_sock_t self:rawip_socket { create }; > +allow test_key_sock_t self:capability { net_admin }; > +allow test_key_sock_t self:key_socket { create write }; > +# For CONFIG_NET_KEY=m > +allow test_key_sock_t kernel_t:system { module_request }; > + > +################## Deny capability { net_admin } ########################## > +type test_key_sock_no_net_admin_t; > +domain_type(test_key_sock_no_net_admin_t) > +unconfined_runs_test(test_key_sock_no_net_admin_t) > +typeattribute test_key_sock_no_net_admin_t testdomain; > +typeattribute test_key_sock_no_net_admin_t keydomain; > + > +# key_socket rules: > +allow test_key_sock_no_net_admin_t self:rawip_socket { create }; > +allow test_key_sock_no_net_admin_t self:key_socket { create write }; > + > +####################### Deny key_socket { create } ########################## > +type test_key_sock_no_create_t; > +domain_type(test_key_sock_no_create_t) > +unconfined_runs_test(test_key_sock_no_create_t) > +typeattribute test_key_sock_no_create_t testdomain; > +typeattribute test_key_sock_no_create_t keydomain; > + > +# key_socket rules: > +allow test_key_sock_no_create_t self:rawip_socket { create }; > +allow test_key_sock_no_create_t self:capability { net_admin }; > +allow test_key_sock_no_create_t self:key_socket { write }; > + > +# > +########### 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/tests/Makefile b/tests/Makefile > index 63aa325..d1dbf38 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -42,6 +42,10 @@ ifeq ($(shell grep -q binder $(POLDEV)/include/support/all_perms.spt && test -e > SUBDIRS += binder > endif > > +ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && test -e $(INCLUDEDIR)/keyutils.h && 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..4a0a48d > --- /dev/null > +++ b/tests/keys/.gitignore > @@ -0,0 +1,3 @@ > +keyctl > +keyctl_relabel > +key_sock > diff --git a/tests/keys/Makefile b/tests/keys/Makefile > new file mode 100644 > index 0000000..3a00df5 > --- /dev/null > +++ b/tests/keys/Makefile > @@ -0,0 +1,8 @@ > +TARGETS = keyctl key_sock keyctl_relabel > + > +LDLIBS += -lselinux -lkeyutils > + > +all: $(TARGETS) > + > +clean: > + rm -f $(TARGETS) > diff --git a/tests/keys/key_sock.c b/tests/keys/key_sock.c > new file mode 100644 > index 0000000..8ac1f45 > --- /dev/null > +++ b/tests/keys/key_sock.c > @@ -0,0 +1,67 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <errno.h> > +#include <stdbool.h> > +#include <sys/socket.h> > +#include <linux/pfkeyv2.h> > +#include <selinux/selinux.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[]) > +{ > + char *context; > + int opt, sock, result; > + bool verbose = false; > + > + 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); > + > + sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); > + if (sock < 0) { > + fprintf(stderr, "Failed to open PF_KEY socket: %s\n", > + strerror(errno)); > + exit(errno); > + } > + if (verbose) > + printf("Opened PF_KEY socket\n"); > + > + /* Write nothing to socket for test, expect EMSGSIZE error */ > + result = write(sock, NULL, 0); > + if (result < 0 && errno == EMSGSIZE) { > + result = 0; > + if (verbose) > + printf("Written to PF_KEY socket\n"); > + } else if (result < 0 && errno != EMSGSIZE) { > + result = -1; > + fprintf(stderr, "Failed write to PF_KEY socket: %s\n", > + strerror(errno)); > + } > + > + close(sock); > + return result; > +} > diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c > new file mode 100644 > index 0000000..6d85be7 > --- /dev/null > +++ b/tests/keys/keyctl.c > @@ -0,0 +1,241 @@ > +#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> > + > +/* This is used as the payload for each add_key() */ > +static const char payload[] = > + " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\nQDEiR/cfhZrNUPgHRrC+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\nWw4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; > + > +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 also 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_THREAD_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_RESTRICT_KEYRING, newring, NULL, NULL); > + if (link < 0) { > + fprintf(stderr, "Failed KEYCTL_RESTRICT_KEYRING: %s\n", > + strerror(errno)); > + exit(14); > + } > + if (verbose) > + printf("Restricted keyring\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 */ > + 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..0276c7a > --- /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_THREAD_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/test b/tests/keys/test > new file mode 100755 > index 0000000..4916e7c > --- /dev/null > +++ b/tests/keys/test > @@ -0,0 +1,98 @@ > +#!/usr/bin/perl > +use Test::More; > + > +BEGIN { > + $basedir = $0; > + $basedir =~ s|(.*)/[^/]*|$1|; > + > + $test_count = 13; > + > + # 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 key_socket ############# > +print "Test key_socket class\n"; > +$result = system "runcon -t test_key_sock_t $basedir/key_sock $v"; > +ok( $result eq 0 ); > + > +# Deny capability { net_admin } - EPERM > +$result = > + system "runcon -t test_key_sock_no_net_admin_t $basedir/key_sock $v 2>&1"; > +ok( $result >> 8 eq 1 ); > + > +# Deny key_socket { create } - EACCES > +$result = > + system "runcon -t test_key_sock_no_create_t $basedir/key_sock $v 2>&1"; > +ok( $result >> 8 eq 13 ); > + > +exit; >
On Mon, 2019-09-16 at 13:58 -0400, Stephen Smalley wrote: > On 9/9/19 9:17 AM, Richard Haines wrote: > > Test all permissions associated with the key and key_socket > > classes. > > > > 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. > > I'm not sure why you coupled key and key_socket together; they don't > have anything to do with each other, and were introduced in very > different kernel and probably refpolicy releases. I would recommend > splitting them. SECCLASS_KEY and its permission checks were > introduced > in Linux v2.6.18; SECCLASS_KEY_SOCKET was part of the original > SELinux > merge for Linux 2.6.0. I'll split them. > > You only appear to be testing self access, not permission checks > between > a process and a keyring created by another process in a different > security context. Okay I'll add these tests > > 1 test fails for me, > keys/test ................... Failed KEYCTL_SESSION_TO_PARENT: > Operation > not permitted > keys/test ................... 1/13 > # Failed test at keys/test line 38. > # Looks like you failed 1 test of 13. > keys/test ................... Dubious, test returned 1 (wstat 256, > 0x100) You must have systems that don't like my patches - I can't get this fail. Using Fedora 30 and also Rawhide from a few weeks ago. I don't know if this is of any interest (It works on Rawhide with kernel from [1]): I've been building 'key' tests to add the new permissions defined in kernel-next [1]. To test these with new policy supporting the new perms + old policy that does not, I added the kernel test patch below. This patch handles security_key_permission() passing a single permission, as checking the current keys code I only see it passing a single permission at a time. I've also an sepol patch + selinux-testsuite tests [1] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/security/selinux?h=next-20190904&id=1f96e0f129eb2bea15a00c154eee8b85aa181d1a diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 901cc052f..9c8f90648 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6502,7 +6502,7 @@ static int selinux_key_permission(key_ref_t key_ref, { struct key *key; struct key_security_struct *ksec; - unsigned oldstyle_perm; + unsigned int key_perm = 0; u32 sid; /* if no specific permissions are requested, we skip the @@ -6511,18 +6511,57 @@ static int selinux_key_permission(key_ref_t key_ref, if (perm == 0) return 0; - oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | KEY_NEED_WRITE | - KEY_NEED_SEARCH | KEY_NEED_LINK); - if (perm & KEY_NEED_SETSEC) - oldstyle_perm |= OLD_KEY_NEED_SETATTR; - if (perm & KEY_NEED_INVAL) - oldstyle_perm |= KEY_NEED_SEARCH; - if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR)) - oldstyle_perm |= KEY_NEED_WRITE; - if (perm & KEY_NEED_JOIN) - oldstyle_perm |= KEY_NEED_SEARCH; - if (perm & KEY_NEED_CLEAR) - oldstyle_perm |= KEY_NEED_WRITE; + /* selinux_key_permission() is called with only one permission set. */ + switch (perm) { + case KEY_NEED_VIEW: + key_perm = KEY__VIEW; + break; + case KEY_NEED_READ: + key_perm = KEY__READ; + break; + case KEY_NEED_WRITE: + key_perm = KEY__WRITE; + break; + case KEY_NEED_SEARCH: + key_perm = KEY__SEARCH; + break; + case KEY_NEED_LINK: + key_perm = KEY__LINK; + break; + case KEY_NEED_SETSEC: /* Keep this as "setattr" in policy */ + key_perm = KEY__SETATTR; + break; + case KEY_NEED_INVAL: + key_perm = KEY__INVAL; + break; + case KEY_NEED_REVOKE: + key_perm = KEY__REVOKE; + break; + case KEY_NEED_JOIN: + key_perm = KEY__JOIN; + break; + case KEY_NEED_CLEAR: + key_perm = KEY__CLEAR; + break; + } + + /* If old policy, then reset new perms to orig. */ + if (!selinux_policycap_key_perms()) { + switch (perm) { + case KEY_NEED_INVAL: + key_perm = KEY__SEARCH; + break; + case KEY_NEED_REVOKE: + key_perm = KEY__WRITE; + break; + case KEY_NEED_JOIN: + key_perm = KEY__SEARCH; + break; + case KEY_NEED_CLEAR: + key_perm = KEY__WRITE; + break; + } + } sid = cred_sid(cred); @@ -6530,7 +6569,7 @@ static int selinux_key_permission(key_ref_t key_ref, ksec = key->security; return avc_has_perm(&selinux_state, - sid, ksec->sid, SECCLASS_KEY, oldstyle_perm, NULL); + sid, ksec->sid, SECCLASS_KEY, key_perm, NULL); } static int selinux_key_getsecurity(struct key *key, char **_buffer) @@ -6555,7 +6594,7 @@ static int selinux_watch_key(struct key *key) u32 sid = current_sid(); return avc_has_perm(&selinux_state, - sid, ksec->sid, SECCLASS_KEY, KEY_NEED_VIEW, NULL); + sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, NULL); } #endif #endif diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 201f7e588..a51ab9bd9 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -158,7 +158,7 @@ struct security_class_mapping secclass_map[] = { { "send", "recv", "relabelto", "forward_in", "forward_out", NULL } }, { "key", { "view", "read", "write", "search", "link", "setattr", "create", - NULL } }, + "inval", "revoke", "join", "clear", NULL } }, { "dccp_socket", { COMMON_SOCK_PERMS, "node_bind", "name_connect", NULL } }, diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 111121281..a248eef75 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -78,6 +78,7 @@ enum { POLICYDB_CAPABILITY_ALWAYSNETWORK, POLICYDB_CAPABILITY_CGROUPSECLABEL, POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION, + POLICYDB_CAPABILITY_KEYPERMS, __POLICYDB_CAPABILITY_MAX }; #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) @@ -177,6 +178,13 @@ static inline bool selinux_policycap_nnp_nosuid_transition(void) return state- >policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION]; } +static inline bool selinux_policycap_key_perms(void) +{ + struct selinux_state *state = &selinux_state; + + return state->policycap[POLICYDB_CAPABILITY_KEYPERMS]; +} + int security_mls_enabled(struct selinux_state *state); int security_load_policy(struct selinux_state *state, void *data, size_t len); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index d61563a36..eb3949fc8 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -73,7 +73,8 @@ const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { "extended_socket_class", "always_check_network", "cgroup_seclabel", - "nnp_nosuid_transition" + "nnp_nosuid_transition", + "key_perms" }; static struct selinux_ss selinux_ss; > > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> > > --- > > README.md | 3 +- > > defconfig | 8 ++ > > policy/Makefile | 4 + > > policy/test_keys.te | 161 ++++++++++++++++++++++++ > > tests/Makefile | 4 + > > tests/keys/.gitignore | 3 + > > tests/keys/Makefile | 8 ++ > > tests/keys/key_sock.c | 67 ++++++++++ > > tests/keys/keyctl.c | 241 > > ++++++++++++++++++++++++++++++++++++ > > tests/keys/keyctl_relabel.c | 93 ++++++++++++++ > > tests/keys/test | 98 +++++++++++++++ > > 11 files changed, 689 insertions(+), 1 deletion(-) > > create mode 100644 policy/test_keys.te > > create mode 100644 tests/keys/.gitignore > > create mode 100644 tests/keys/Makefile > > create mode 100644 tests/keys/key_sock.c > > create mode 100644 tests/keys/keyctl.c > > create mode 100644 tests/keys/keyctl_relabel.c > > create mode 100755 tests/keys/test > > > > diff --git a/README.md b/README.md > > index 26784f8..fe72a91 100644 > > --- a/README.md > > +++ b/README.md > > @@ -65,7 +65,8 @@ following command: > > netlabel_tools \ > > iptables \ > > lksctp-tools-devel \ > > - attr > > + attr \ > > + 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 d7f0ea5..c00e291 100644 > > --- a/defconfig > > +++ b/defconfig > > @@ -62,3 +62,11 @@ CONFIG_ANDROID_BINDER_IPC=y > > # This will configure the Dynamically Allocated Binder Devices > > added > > # to 5.0+ kernels: > > CONFIG_ANDROID_BINDERFS=y > > + > > +# Key implementations. > > +# These are enabled to test the key and key_socket controls in > > +# tests/keys; they are not required for SELinux operation itself. > > +CONFIG_KEYS=y > > +CONFIG_KEYS_COMPAT=y > > +CONFIG_KEY_DH_OPERATIONS=y > > +CONFIG_NET_KEY=m > > diff --git a/policy/Makefile b/policy/Makefile > > index 305b572..9258a93 100644 > > --- a/policy/Makefile > > +++ b/policy/Makefile > > @@ -71,6 +71,10 @@ ifeq ($(shell grep -q > > corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren > > TARGETS += test_sctp.te > > endif > > > > +ifeq ($(shell grep -q key_socket > > $(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, > > $(TARGETS)) > > endif > > diff --git a/policy/test_keys.te b/policy/test_keys.te > > new file mode 100644 > > index 0000000..9c65ec8 > > --- /dev/null > > +++ b/policy/test_keys.te > > @@ -0,0 +1,161 @@ > > +# > > +################# 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: > > +# > > +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 }; > > +') > > + > > +# > > +####################### 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 }; > > + > > +###################### 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 }; > > + > > +###################### 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 }; > > + > > +# > > +######################## Test key_socket class > > ########################### > > +# > > +type test_key_sock_t; > > +domain_type(test_key_sock_t) > > +unconfined_runs_test(test_key_sock_t) > > +typeattribute test_key_sock_t testdomain; > > +typeattribute test_key_sock_t keydomain; > > + > > +# key_socket rules: > > +allow test_key_sock_t self:rawip_socket { create }; > > +allow test_key_sock_t self:capability { net_admin }; > > +allow test_key_sock_t self:key_socket { create write }; > > +# For CONFIG_NET_KEY=m > > +allow test_key_sock_t kernel_t:system { module_request }; > > + > > +################## Deny capability { net_admin } > > ########################## > > +type test_key_sock_no_net_admin_t; > > +domain_type(test_key_sock_no_net_admin_t) > > +unconfined_runs_test(test_key_sock_no_net_admin_t) > > +typeattribute test_key_sock_no_net_admin_t testdomain; > > +typeattribute test_key_sock_no_net_admin_t keydomain; > > + > > +# key_socket rules: > > +allow test_key_sock_no_net_admin_t self:rawip_socket { create }; > > +allow test_key_sock_no_net_admin_t self:key_socket { create write > > }; > > + > > +####################### Deny key_socket { create } > > ########################## > > +type test_key_sock_no_create_t; > > +domain_type(test_key_sock_no_create_t) > > +unconfined_runs_test(test_key_sock_no_create_t) > > +typeattribute test_key_sock_no_create_t testdomain; > > +typeattribute test_key_sock_no_create_t keydomain; > > + > > +# key_socket rules: > > +allow test_key_sock_no_create_t self:rawip_socket { create }; > > +allow test_key_sock_no_create_t self:capability { net_admin }; > > +allow test_key_sock_no_create_t self:key_socket { write }; > > + > > +# > > +########### 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/tests/Makefile b/tests/Makefile > > index 63aa325..d1dbf38 100644 > > --- a/tests/Makefile > > +++ b/tests/Makefile > > @@ -42,6 +42,10 @@ ifeq ($(shell grep -q binder > > $(POLDEV)/include/support/all_perms.spt && test -e > > SUBDIRS += binder > > endif > > > > +ifeq ($(shell grep -q key_socket > > $(POLDEV)/include/support/all_perms.spt && test -e > > $(INCLUDEDIR)/keyutils.h && 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..4a0a48d > > --- /dev/null > > +++ b/tests/keys/.gitignore > > @@ -0,0 +1,3 @@ > > +keyctl > > +keyctl_relabel > > +key_sock > > diff --git a/tests/keys/Makefile b/tests/keys/Makefile > > new file mode 100644 > > index 0000000..3a00df5 > > --- /dev/null > > +++ b/tests/keys/Makefile > > @@ -0,0 +1,8 @@ > > +TARGETS = keyctl key_sock keyctl_relabel > > + > > +LDLIBS += -lselinux -lkeyutils > > + > > +all: $(TARGETS) > > + > > +clean: > > + rm -f $(TARGETS) > > diff --git a/tests/keys/key_sock.c b/tests/keys/key_sock.c > > new file mode 100644 > > index 0000000..8ac1f45 > > --- /dev/null > > +++ b/tests/keys/key_sock.c > > @@ -0,0 +1,67 @@ > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <string.h> > > +#include <unistd.h> > > +#include <errno.h> > > +#include <stdbool.h> > > +#include <sys/socket.h> > > +#include <linux/pfkeyv2.h> > > +#include <selinux/selinux.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[]) > > +{ > > + char *context; > > + int opt, sock, result; > > + bool verbose = false; > > + > > + 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); > > + > > + sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); > > + if (sock < 0) { > > + fprintf(stderr, "Failed to open PF_KEY socket: %s\n", > > + strerror(errno)); > > + exit(errno); > > + } > > + if (verbose) > > + printf("Opened PF_KEY socket\n"); > > + > > + /* Write nothing to socket for test, expect EMSGSIZE error */ > > + result = write(sock, NULL, 0); > > + if (result < 0 && errno == EMSGSIZE) { > > + result = 0; > > + if (verbose) > > + printf("Written to PF_KEY socket\n"); > > + } else if (result < 0 && errno != EMSGSIZE) { > > + result = -1; > > + fprintf(stderr, "Failed write to PF_KEY socket: %s\n", > > + strerror(errno)); > > + } > > + > > + close(sock); > > + return result; > > +} > > diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c > > new file mode 100644 > > index 0000000..6d85be7 > > --- /dev/null > > +++ b/tests/keys/keyctl.c > > @@ -0,0 +1,241 @@ > > +#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> > > + > > +/* This is used as the payload for each add_key() */ > > +static const char payload[] = > > + " -----BEGIN PUBLIC KEY----- > > \nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\ > > nQDEiR/cfhZrNUPgHRrC+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\n > > Ww4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4 > > aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; > > + > > +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 also 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_THREAD_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_RESTRICT_KEYRING, newring, NULL, NULL); > > + if (link < 0) { > > + fprintf(stderr, "Failed KEYCTL_RESTRICT_KEYRING: %s\n", > > + strerror(errno)); > > + exit(14); > > + } > > + if (verbose) > > + printf("Restricted keyring\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 */ > > + 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..0276c7a > > --- /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_THREAD_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/test b/tests/keys/test > > new file mode 100755 > > index 0000000..4916e7c > > --- /dev/null > > +++ b/tests/keys/test > > @@ -0,0 +1,98 @@ > > +#!/usr/bin/perl > > +use Test::More; > > + > > +BEGIN { > > + $basedir = $0; > > + $basedir =~ s|(.*)/[^/]*|$1|; > > + > > + $test_count = 13; > > + > > + # 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 key_socket ############# > > +print "Test key_socket class\n"; > > +$result = system "runcon -t test_key_sock_t $basedir/key_sock $v"; > > +ok( $result eq 0 ); > > + > > +# Deny capability { net_admin } - EPERM > > +$result = > > + system "runcon -t test_key_sock_no_net_admin_t $basedir/key_sock > > $v 2>&1"; > > +ok( $result >> 8 eq 1 ); > > + > > +# Deny key_socket { create } - EACCES > > +$result = > > + system "runcon -t test_key_sock_no_create_t $basedir/key_sock $v > > 2>&1"; > > +ok( $result >> 8 eq 13 ); > > + > > +exit; > >
On 9/16/19 2:55 PM, Richard Haines wrote: > On Mon, 2019-09-16 at 13:58 -0400, Stephen Smalley wrote: >> On 9/9/19 9:17 AM, Richard Haines wrote: >>> Test all permissions associated with the key and key_socket >>> classes. >>> >>> 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. >> >> I'm not sure why you coupled key and key_socket together; they don't >> have anything to do with each other, and were introduced in very >> different kernel and probably refpolicy releases. I would recommend >> splitting them. SECCLASS_KEY and its permission checks were >> introduced >> in Linux v2.6.18; SECCLASS_KEY_SOCKET was part of the original >> SELinux >> merge for Linux 2.6.0. > > I'll split them. > >> >> You only appear to be testing self access, not permission checks >> between >> a process and a keyring created by another process in a different >> security context. > > Okay I'll add these tests >> >> 1 test fails for me, >> keys/test ................... Failed KEYCTL_SESSION_TO_PARENT: >> Operation >> not permitted >> keys/test ................... 1/13 >> # Failed test at keys/test line 38. >> # Looks like you failed 1 test of 13. >> keys/test ................... Dubious, test returned 1 (wstat 256, >> 0x100) > > You must have systems that don't like my patches - I can't get this > fail. Using Fedora 30 and also Rawhide from a few weeks ago. I'll have to look into it further, but it was on stock F30. > I don't know if this is of any interest (It works on Rawhide with > kernel from [1]): > > I've been building 'key' tests to add the new permissions defined in > kernel-next [1]. > To test these with new policy supporting the new perms + old policy > that does not, I added the kernel test patch below. > This patch handles security_key_permission() passing a single > permission, as checking the current keys code I only see it passing a > single permission at a time. > > I've also an sepol patch + selinux-testsuite tests > > [1] > https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/security/selinux?h=next-20190904&id=1f96e0f129eb2bea15a00c154eee8b85aa181d1a Yes, that's probably worth submitting for real. Be sure to include David Howells on the distribution for it. I wouldn't assume that only a single permission can ever be passed unless key_permission() itself asserts that invariant. > > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 901cc052f..9c8f90648 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -6502,7 +6502,7 @@ static int selinux_key_permission(key_ref_t > key_ref, > { > struct key *key; > struct key_security_struct *ksec; > - unsigned oldstyle_perm; > + unsigned int key_perm = 0; > u32 sid; > > /* if no specific permissions are requested, we skip the > @@ -6511,18 +6511,57 @@ static int selinux_key_permission(key_ref_t > key_ref, > if (perm == 0) > return 0; > > - oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | > KEY_NEED_WRITE | > - KEY_NEED_SEARCH | KEY_NEED_LINK); > - if (perm & KEY_NEED_SETSEC) > - oldstyle_perm |= OLD_KEY_NEED_SETATTR; > - if (perm & KEY_NEED_INVAL) > - oldstyle_perm |= KEY_NEED_SEARCH; > - if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR)) > - oldstyle_perm |= KEY_NEED_WRITE; > - if (perm & KEY_NEED_JOIN) > - oldstyle_perm |= KEY_NEED_SEARCH; > - if (perm & KEY_NEED_CLEAR) > - oldstyle_perm |= KEY_NEED_WRITE; > + /* selinux_key_permission() is called with only one permission > set. */ > + switch (perm) { > + case KEY_NEED_VIEW: > + key_perm = KEY__VIEW; > + break; > + case KEY_NEED_READ: > + key_perm = KEY__READ; > + break; > + case KEY_NEED_WRITE: > + key_perm = KEY__WRITE; > + break; > + case KEY_NEED_SEARCH: > + key_perm = KEY__SEARCH; > + break; > + case KEY_NEED_LINK: > + key_perm = KEY__LINK; > + break; > + case KEY_NEED_SETSEC: /* Keep this as "setattr" in policy */ > + key_perm = KEY__SETATTR; > + break; > + case KEY_NEED_INVAL: > + key_perm = KEY__INVAL; > + break; > + case KEY_NEED_REVOKE: > + key_perm = KEY__REVOKE; > + break; > + case KEY_NEED_JOIN: > + key_perm = KEY__JOIN; > + break; > + case KEY_NEED_CLEAR: > + key_perm = KEY__CLEAR; > + break; > + } > + > + /* If old policy, then reset new perms to orig. */ > + if (!selinux_policycap_key_perms()) { > + switch (perm) { > + case KEY_NEED_INVAL: > + key_perm = KEY__SEARCH; > + break; > + case KEY_NEED_REVOKE: > + key_perm = KEY__WRITE; > + break; > + case KEY_NEED_JOIN: > + key_perm = KEY__SEARCH; > + break; > + case KEY_NEED_CLEAR: > + key_perm = KEY__WRITE; > + break; > + } > + } > > sid = cred_sid(cred); > > @@ -6530,7 +6569,7 @@ static int selinux_key_permission(key_ref_t > key_ref, > ksec = key->security; > > return avc_has_perm(&selinux_state, > - sid, ksec->sid, SECCLASS_KEY, > oldstyle_perm, NULL); > + sid, ksec->sid, SECCLASS_KEY, key_perm, > NULL); > } > > static int selinux_key_getsecurity(struct key *key, char **_buffer) > @@ -6555,7 +6594,7 @@ static int selinux_watch_key(struct key *key) > u32 sid = current_sid(); > > return avc_has_perm(&selinux_state, > - sid, ksec->sid, SECCLASS_KEY, > KEY_NEED_VIEW, NULL); > + sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, > NULL); > } > #endif > #endif > diff --git a/security/selinux/include/classmap.h > b/security/selinux/include/classmap.h > index 201f7e588..a51ab9bd9 100644 > --- a/security/selinux/include/classmap.h > +++ b/security/selinux/include/classmap.h > @@ -158,7 +158,7 @@ struct security_class_mapping secclass_map[] = { > { "send", "recv", "relabelto", "forward_in", "forward_out", > NULL } }, > { "key", > { "view", "read", "write", "search", "link", "setattr", > "create", > - NULL } }, > + "inval", "revoke", "join", "clear", NULL } }, > { "dccp_socket", > { COMMON_SOCK_PERMS, > "node_bind", "name_connect", NULL } }, > diff --git a/security/selinux/include/security.h > b/security/selinux/include/security.h > index 111121281..a248eef75 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -78,6 +78,7 @@ enum { > POLICYDB_CAPABILITY_ALWAYSNETWORK, > POLICYDB_CAPABILITY_CGROUPSECLABEL, > POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION, > + POLICYDB_CAPABILITY_KEYPERMS, > __POLICYDB_CAPABILITY_MAX > }; > #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) > @@ -177,6 +178,13 @@ static inline bool > selinux_policycap_nnp_nosuid_transition(void) > return state- >> policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION]; > } > > +static inline bool selinux_policycap_key_perms(void) > +{ > + struct selinux_state *state = &selinux_state; > + > + return state->policycap[POLICYDB_CAPABILITY_KEYPERMS]; > +} > + > int security_mls_enabled(struct selinux_state *state); > int security_load_policy(struct selinux_state *state, > void *data, size_t len); > diff --git a/security/selinux/ss/services.c > b/security/selinux/ss/services.c > index d61563a36..eb3949fc8 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c > @@ -73,7 +73,8 @@ const char > *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { > "extended_socket_class", > "always_check_network", > "cgroup_seclabel", > - "nnp_nosuid_transition" > + "nnp_nosuid_transition", > + "key_perms" > }; > > static struct selinux_ss selinux_ss; > > > >> >>> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> >>> --- >>> README.md | 3 +- >>> defconfig | 8 ++ >>> policy/Makefile | 4 + >>> policy/test_keys.te | 161 ++++++++++++++++++++++++ >>> tests/Makefile | 4 + >>> tests/keys/.gitignore | 3 + >>> tests/keys/Makefile | 8 ++ >>> tests/keys/key_sock.c | 67 ++++++++++ >>> tests/keys/keyctl.c | 241 >>> ++++++++++++++++++++++++++++++++++++ >>> tests/keys/keyctl_relabel.c | 93 ++++++++++++++ >>> tests/keys/test | 98 +++++++++++++++ >>> 11 files changed, 689 insertions(+), 1 deletion(-) >>> create mode 100644 policy/test_keys.te >>> create mode 100644 tests/keys/.gitignore >>> create mode 100644 tests/keys/Makefile >>> create mode 100644 tests/keys/key_sock.c >>> create mode 100644 tests/keys/keyctl.c >>> create mode 100644 tests/keys/keyctl_relabel.c >>> create mode 100755 tests/keys/test >>> >>> diff --git a/README.md b/README.md >>> index 26784f8..fe72a91 100644 >>> --- a/README.md >>> +++ b/README.md >>> @@ -65,7 +65,8 @@ following command: >>> netlabel_tools \ >>> iptables \ >>> lksctp-tools-devel \ >>> - attr >>> + attr \ >>> + 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 d7f0ea5..c00e291 100644 >>> --- a/defconfig >>> +++ b/defconfig >>> @@ -62,3 +62,11 @@ CONFIG_ANDROID_BINDER_IPC=y >>> # This will configure the Dynamically Allocated Binder Devices >>> added >>> # to 5.0+ kernels: >>> CONFIG_ANDROID_BINDERFS=y >>> + >>> +# Key implementations. >>> +# These are enabled to test the key and key_socket controls in >>> +# tests/keys; they are not required for SELinux operation itself. >>> +CONFIG_KEYS=y >>> +CONFIG_KEYS_COMPAT=y >>> +CONFIG_KEY_DH_OPERATIONS=y >>> +CONFIG_NET_KEY=m >>> diff --git a/policy/Makefile b/policy/Makefile >>> index 305b572..9258a93 100644 >>> --- a/policy/Makefile >>> +++ b/policy/Makefile >>> @@ -71,6 +71,10 @@ ifeq ($(shell grep -q >>> corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren >>> TARGETS += test_sctp.te >>> endif >>> >>> +ifeq ($(shell grep -q key_socket >>> $(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, >>> $(TARGETS)) >>> endif >>> diff --git a/policy/test_keys.te b/policy/test_keys.te >>> new file mode 100644 >>> index 0000000..9c65ec8 >>> --- /dev/null >>> +++ b/policy/test_keys.te >>> @@ -0,0 +1,161 @@ >>> +# >>> +################# 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: >>> +# >>> +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 }; >>> +') >>> + >>> +# >>> +####################### 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 }; >>> + >>> +###################### 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 }; >>> + >>> +###################### 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 }; >>> + >>> +# >>> +######################## Test key_socket class >>> ########################### >>> +# >>> +type test_key_sock_t; >>> +domain_type(test_key_sock_t) >>> +unconfined_runs_test(test_key_sock_t) >>> +typeattribute test_key_sock_t testdomain; >>> +typeattribute test_key_sock_t keydomain; >>> + >>> +# key_socket rules: >>> +allow test_key_sock_t self:rawip_socket { create }; >>> +allow test_key_sock_t self:capability { net_admin }; >>> +allow test_key_sock_t self:key_socket { create write }; >>> +# For CONFIG_NET_KEY=m >>> +allow test_key_sock_t kernel_t:system { module_request }; >>> + >>> +################## Deny capability { net_admin } >>> ########################## >>> +type test_key_sock_no_net_admin_t; >>> +domain_type(test_key_sock_no_net_admin_t) >>> +unconfined_runs_test(test_key_sock_no_net_admin_t) >>> +typeattribute test_key_sock_no_net_admin_t testdomain; >>> +typeattribute test_key_sock_no_net_admin_t keydomain; >>> + >>> +# key_socket rules: >>> +allow test_key_sock_no_net_admin_t self:rawip_socket { create }; >>> +allow test_key_sock_no_net_admin_t self:key_socket { create write >>> }; >>> + >>> +####################### Deny key_socket { create } >>> ########################## >>> +type test_key_sock_no_create_t; >>> +domain_type(test_key_sock_no_create_t) >>> +unconfined_runs_test(test_key_sock_no_create_t) >>> +typeattribute test_key_sock_no_create_t testdomain; >>> +typeattribute test_key_sock_no_create_t keydomain; >>> + >>> +# key_socket rules: >>> +allow test_key_sock_no_create_t self:rawip_socket { create }; >>> +allow test_key_sock_no_create_t self:capability { net_admin }; >>> +allow test_key_sock_no_create_t self:key_socket { write }; >>> + >>> +# >>> +########### 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/tests/Makefile b/tests/Makefile >>> index 63aa325..d1dbf38 100644 >>> --- a/tests/Makefile >>> +++ b/tests/Makefile >>> @@ -42,6 +42,10 @@ ifeq ($(shell grep -q binder >>> $(POLDEV)/include/support/all_perms.spt && test -e >>> SUBDIRS += binder >>> endif >>> >>> +ifeq ($(shell grep -q key_socket >>> $(POLDEV)/include/support/all_perms.spt && test -e >>> $(INCLUDEDIR)/keyutils.h && 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..4a0a48d >>> --- /dev/null >>> +++ b/tests/keys/.gitignore >>> @@ -0,0 +1,3 @@ >>> +keyctl >>> +keyctl_relabel >>> +key_sock >>> diff --git a/tests/keys/Makefile b/tests/keys/Makefile >>> new file mode 100644 >>> index 0000000..3a00df5 >>> --- /dev/null >>> +++ b/tests/keys/Makefile >>> @@ -0,0 +1,8 @@ >>> +TARGETS = keyctl key_sock keyctl_relabel >>> + >>> +LDLIBS += -lselinux -lkeyutils >>> + >>> +all: $(TARGETS) >>> + >>> +clean: >>> + rm -f $(TARGETS) >>> diff --git a/tests/keys/key_sock.c b/tests/keys/key_sock.c >>> new file mode 100644 >>> index 0000000..8ac1f45 >>> --- /dev/null >>> +++ b/tests/keys/key_sock.c >>> @@ -0,0 +1,67 @@ >>> +#include <stdio.h> >>> +#include <stdlib.h> >>> +#include <string.h> >>> +#include <unistd.h> >>> +#include <errno.h> >>> +#include <stdbool.h> >>> +#include <sys/socket.h> >>> +#include <linux/pfkeyv2.h> >>> +#include <selinux/selinux.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[]) >>> +{ >>> + char *context; >>> + int opt, sock, result; >>> + bool verbose = false; >>> + >>> + 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); >>> + >>> + sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); >>> + if (sock < 0) { >>> + fprintf(stderr, "Failed to open PF_KEY socket: %s\n", >>> + strerror(errno)); >>> + exit(errno); >>> + } >>> + if (verbose) >>> + printf("Opened PF_KEY socket\n"); >>> + >>> + /* Write nothing to socket for test, expect EMSGSIZE error */ >>> + result = write(sock, NULL, 0); >>> + if (result < 0 && errno == EMSGSIZE) { >>> + result = 0; >>> + if (verbose) >>> + printf("Written to PF_KEY socket\n"); >>> + } else if (result < 0 && errno != EMSGSIZE) { >>> + result = -1; >>> + fprintf(stderr, "Failed write to PF_KEY socket: %s\n", >>> + strerror(errno)); >>> + } >>> + >>> + close(sock); >>> + return result; >>> +} >>> diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c >>> new file mode 100644 >>> index 0000000..6d85be7 >>> --- /dev/null >>> +++ b/tests/keys/keyctl.c >>> @@ -0,0 +1,241 @@ >>> +#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> >>> + >>> +/* This is used as the payload for each add_key() */ >>> +static const char payload[] = >>> + " -----BEGIN PUBLIC KEY----- >>> \nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\ >>> nQDEiR/cfhZrNUPgHRrC+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\n >>> Ww4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4 >>> aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; >>> + >>> +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 also 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_THREAD_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_RESTRICT_KEYRING, newring, NULL, NULL); >>> + if (link < 0) { >>> + fprintf(stderr, "Failed KEYCTL_RESTRICT_KEYRING: %s\n", >>> + strerror(errno)); >>> + exit(14); >>> + } >>> + if (verbose) >>> + printf("Restricted keyring\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 */ >>> + 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..0276c7a >>> --- /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_THREAD_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/test b/tests/keys/test >>> new file mode 100755 >>> index 0000000..4916e7c >>> --- /dev/null >>> +++ b/tests/keys/test >>> @@ -0,0 +1,98 @@ >>> +#!/usr/bin/perl >>> +use Test::More; >>> + >>> +BEGIN { >>> + $basedir = $0; >>> + $basedir =~ s|(.*)/[^/]*|$1|; >>> + >>> + $test_count = 13; >>> + >>> + # 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 key_socket ############# >>> +print "Test key_socket class\n"; >>> +$result = system "runcon -t test_key_sock_t $basedir/key_sock $v"; >>> +ok( $result eq 0 ); >>> + >>> +# Deny capability { net_admin } - EPERM >>> +$result = >>> + system "runcon -t test_key_sock_no_net_admin_t $basedir/key_sock >>> $v 2>&1"; >>> +ok( $result >> 8 eq 1 ); >>> + >>> +# Deny key_socket { create } - EACCES >>> +$result = >>> + system "runcon -t test_key_sock_no_create_t $basedir/key_sock $v >>> 2>&1"; >>> +ok( $result >> 8 eq 13 ); >>> + >>> +exit; >>> >
On Mon, 2019-09-16 at 15:11 -0400, Stephen Smalley wrote: > On 9/16/19 2:55 PM, Richard Haines wrote: > > On Mon, 2019-09-16 at 13:58 -0400, Stephen Smalley wrote: > > > On 9/9/19 9:17 AM, Richard Haines wrote: > > > > Test all permissions associated with the key and key_socket > > > > classes. > > > > > > > > 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. > > > > > > I'm not sure why you coupled key and key_socket together; they > > > don't > > > have anything to do with each other, and were introduced in very > > > different kernel and probably refpolicy releases. I would > > > recommend > > > splitting them. SECCLASS_KEY and its permission checks were > > > introduced > > > in Linux v2.6.18; SECCLASS_KEY_SOCKET was part of the original > > > SELinux > > > merge for Linux 2.6.0. > > > > I'll split them. > > > > > You only appear to be testing self access, not permission checks > > > between > > > a process and a keyring created by another process in a different > > > security context. > > > > Okay I'll add these tests > > > 1 test fails for me, > > > keys/test ................... Failed KEYCTL_SESSION_TO_PARENT: > > > Operation > > > not permitted > > > keys/test ................... 1/13 > > > # Failed test at keys/test line 38. > > > # Looks like you failed 1 test of 13. > > > keys/test ................... Dubious, test returned 1 (wstat > > > 256, > > > 0x100) > > > > You must have systems that don't like my patches - I can't get this > > fail. Using Fedora 30 and also Rawhide from a few weeks ago. > > I'll have to look into it further, but it was on stock F30. > > > I don't know if this is of any interest (It works on Rawhide with > > kernel from [1]): > > > > I've been building 'key' tests to add the new permissions defined > > in > > kernel-next [1]. > > To test these with new policy supporting the new perms + old policy > > that does not, I added the kernel test patch below. > > This patch handles security_key_permission() passing a single > > permission, as checking the current keys code I only see it passing > > a > > single permission at a time. > > > > I've also an sepol patch + selinux-testsuite tests > > > > [1] > > https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/security/selinux?h=next-20190904&id=1f96e0f129eb2bea15a00c154eee8b85aa181d1a > > Yes, that's probably worth submitting for real. Be sure to include > David Howells on the distribution for it. I wouldn't assume that only > a > single permission can ever be passed unless key_permission() itself > asserts that invariant. > That's okay as I've also tested the patch below that handles multiple permissions from keys: Any view on what is best !! diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 901cc052f..78413277c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6502,7 +6502,8 @@ static int selinux_key_permission(key_ref_t key_ref, { struct key *key; struct key_security_struct *ksec; - unsigned oldstyle_perm; + unsigned int key_perm = 0, switch_perm = 0; + int x = KEY_NEED_ALL, bit = 1; u32 sid; /* if no specific permissions are requested, we skip the @@ -6511,18 +6512,67 @@ static int selinux_key_permission(key_ref_t key_ref, if (perm == 0) return 0; - oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | KEY_NEED_WRITE | - KEY_NEED_SEARCH | KEY_NEED_LINK); - if (perm & KEY_NEED_SETSEC) - oldstyle_perm |= OLD_KEY_NEED_SETATTR; - if (perm & KEY_NEED_INVAL) - oldstyle_perm |= KEY_NEED_SEARCH; - if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR)) - oldstyle_perm |= KEY_NEED_WRITE; - if (perm & KEY_NEED_JOIN) - oldstyle_perm |= KEY_NEED_SEARCH; - if (perm & KEY_NEED_CLEAR) - oldstyle_perm |= KEY_NEED_WRITE; + /* + * selinux_key_permission() is called with only one permission set. + * However this will handle multiple bits set. + */ + while (x) { + switch_perm = bit & perm; + switch (switch_perm) { + case KEY_NEED_VIEW: + key_perm |= KEY__VIEW; + break; + case KEY_NEED_READ: + key_perm |= KEY__READ; + break; + case KEY_NEED_WRITE: + key_perm |= KEY__WRITE; + break; + case KEY_NEED_SEARCH: + key_perm |= KEY__SEARCH; + break; + case KEY_NEED_LINK: + key_perm |= KEY__LINK; + break; + case KEY_NEED_SETSEC: /* Keep this as "setattr" in policy */ + key_perm |= KEY__SETATTR; + break; + case KEY_NEED_INVAL: + key_perm |= KEY__INVAL; + break; + case KEY_NEED_REVOKE: + key_perm |= KEY__REVOKE; + break; + case KEY_NEED_JOIN: + key_perm |= KEY__JOIN; + break; + case KEY_NEED_CLEAR: + key_perm |= KEY__CLEAR; + break; + } + bit <<= 1; + x >>= 1; + } + + /* If old policy, then reset new perms to orig. */ + if (!selinux_policycap_key_perms()) { + if (perm & KEY_NEED_INVAL) { + key_perm &= ~KEY__INVAL; + key_perm |= KEY__SEARCH; + } + if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR)) { + key_perm &= ~KEY__REVOKE; + key_perm |= KEY__WRITE; + } + if (perm & KEY_NEED_JOIN) { + key_perm &= ~KEY__JOIN; + key_perm |= KEY__SEARCH; + } + if (perm & KEY_NEED_CLEAR) { + key_perm &= ~KEY__CLEAR; + key_perm |= KEY__WRITE; + } + } sid = cred_sid(cred); @@ -6530,7 +6580,7 @@ static int selinux_key_permission(key_ref_t key_ref, ksec = key->security; return avc_has_perm(&selinux_state, - sid, ksec->sid, SECCLASS_KEY, oldstyle_perm, NULL); + sid, ksec->sid, SECCLASS_KEY, key_perm, NULL); } static int selinux_key_getsecurity(struct key *key, char **_buffer) @@ -6555,7 +6605,7 @@ static int selinux_watch_key(struct key *key) u32 sid = current_sid(); return avc_has_perm(&selinux_state, - sid, ksec->sid, SECCLASS_KEY, KEY_NEED_VIEW, NULL); + sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, NULL); } #endif #endif diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 201f7e588..a51ab9bd9 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -158,7 +158,7 @@ struct security_class_mapping secclass_map[] = { { "send", "recv", "relabelto", "forward_in", "forward_out", NULL } }, { "key", { "view", "read", "write", "search", "link", "setattr", "create", - NULL } }, + "inval", "revoke", "join", "clear", NULL } }, { "dccp_socket", { COMMON_SOCK_PERMS, "node_bind", "name_connect", NULL } }, diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 111121281..a248eef75 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -78,6 +78,7 @@ enum { POLICYDB_CAPABILITY_ALWAYSNETWORK, POLICYDB_CAPABILITY_CGROUPSECLABEL, POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION, + POLICYDB_CAPABILITY_KEYPERMS, __POLICYDB_CAPABILITY_MAX }; #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) @@ -177,6 +178,13 @@ static inline bool selinux_policycap_nnp_nosuid_transition(void) return state- >policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION]; } +static inline bool selinux_policycap_key_perms(void) +{ + struct selinux_state *state = &selinux_state; + + return state->policycap[POLICYDB_CAPABILITY_KEYPERMS]; +} + int security_mls_enabled(struct selinux_state *state); int security_load_policy(struct selinux_state *state, void *data, size_t len); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index d61563a36..eb3949fc8 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -73,7 +73,8 @@ const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { "extended_socket_class", "always_check_network", "cgroup_seclabel", - "nnp_nosuid_transition" + "nnp_nosuid_transition", + "key_perms" }; static struct selinux_ss selinux_ss;
On 9/16/19 3:23 PM, Richard Haines wrote: > On Mon, 2019-09-16 at 15:11 -0400, Stephen Smalley wrote: >> On 9/16/19 2:55 PM, Richard Haines wrote: >>> On Mon, 2019-09-16 at 13:58 -0400, Stephen Smalley wrote: >>>> On 9/9/19 9:17 AM, Richard Haines wrote: >>>>> Test all permissions associated with the key and key_socket >>>>> classes. >>>>> >>>>> 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. >>>> >>>> I'm not sure why you coupled key and key_socket together; they >>>> don't >>>> have anything to do with each other, and were introduced in very >>>> different kernel and probably refpolicy releases. I would >>>> recommend >>>> splitting them. SECCLASS_KEY and its permission checks were >>>> introduced >>>> in Linux v2.6.18; SECCLASS_KEY_SOCKET was part of the original >>>> SELinux >>>> merge for Linux 2.6.0. >>> >>> I'll split them. >>> >>>> You only appear to be testing self access, not permission checks >>>> between >>>> a process and a keyring created by another process in a different >>>> security context. >>> >>> Okay I'll add these tests >>>> 1 test fails for me, >>>> keys/test ................... Failed KEYCTL_SESSION_TO_PARENT: >>>> Operation >>>> not permitted >>>> keys/test ................... 1/13 >>>> # Failed test at keys/test line 38. >>>> # Looks like you failed 1 test of 13. >>>> keys/test ................... Dubious, test returned 1 (wstat >>>> 256, >>>> 0x100) >>> >>> You must have systems that don't like my patches - I can't get this >>> fail. Using Fedora 30 and also Rawhide from a few weeks ago. >> >> I'll have to look into it further, but it was on stock F30. >> >>> I don't know if this is of any interest (It works on Rawhide with >>> kernel from [1]): >>> >>> I've been building 'key' tests to add the new permissions defined >>> in >>> kernel-next [1]. >>> To test these with new policy supporting the new perms + old policy >>> that does not, I added the kernel test patch below. >>> This patch handles security_key_permission() passing a single >>> permission, as checking the current keys code I only see it passing >>> a >>> single permission at a time. >>> >>> I've also an sepol patch + selinux-testsuite tests >>> >>> [1] >>> https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/security/selinux?h=next-20190904&id=1f96e0f129eb2bea15a00c154eee8b85aa181d1a >> >> Yes, that's probably worth submitting for real. Be sure to include >> David Howells on the distribution for it. I wouldn't assume that only >> a >> single permission can ever be passed unless key_permission() itself >> asserts that invariant. >> > That's okay as I've also tested the patch below that handles multiple > permissions from keys: > Any view on what is best !! Unless key_task_permission() prohibits passing multiple permissions or David Howells tells you it will never happen, I'd support it in the security hook. selinux_inode_permission() and selinux_ipc_permission() support it for their checks. I don't think I'd do the work of mapping to the new permissions unless the policycap is set. > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c > index 901cc052f..78413277c 100644 > --- a/security/selinux/hooks.c > +++ b/security/selinux/hooks.c > @@ -6502,7 +6502,8 @@ static int selinux_key_permission(key_ref_t > key_ref, > { > struct key *key; > struct key_security_struct *ksec; > - unsigned oldstyle_perm; > + unsigned int key_perm = 0, switch_perm = 0; > + int x = KEY_NEED_ALL, bit = 1; > u32 sid; > > /* if no specific permissions are requested, we skip the > @@ -6511,18 +6512,67 @@ static int selinux_key_permission(key_ref_t > key_ref, > if (perm == 0) > return 0; > > - oldstyle_perm = perm & (KEY_NEED_VIEW | KEY_NEED_READ | > KEY_NEED_WRITE | > - KEY_NEED_SEARCH | KEY_NEED_LINK); > - if (perm & KEY_NEED_SETSEC) > - oldstyle_perm |= OLD_KEY_NEED_SETATTR; > - if (perm & KEY_NEED_INVAL) > - oldstyle_perm |= KEY_NEED_SEARCH; > - if (perm & KEY_NEED_REVOKE && !(perm & OLD_KEY_NEED_SETATTR)) > - oldstyle_perm |= KEY_NEED_WRITE; > - if (perm & KEY_NEED_JOIN) > - oldstyle_perm |= KEY_NEED_SEARCH; > - if (perm & KEY_NEED_CLEAR) > - oldstyle_perm |= KEY_NEED_WRITE; > + /* > + * selinux_key_permission() is called with only one permission > set. > + * However this will handle multiple bits set. > + */ > + while (x) { > + switch_perm = bit & perm; > + switch (switch_perm) { > + case KEY_NEED_VIEW: > + key_perm |= KEY__VIEW; > + break; > + case KEY_NEED_READ: > + key_perm |= KEY__READ; > + break; > + case KEY_NEED_WRITE: > + key_perm |= KEY__WRITE; > + break; > + case KEY_NEED_SEARCH: > + key_perm |= KEY__SEARCH; > + break; > + case KEY_NEED_LINK: > + key_perm |= KEY__LINK; > + break; > + case KEY_NEED_SETSEC: /* Keep this as "setattr" in > policy */ > + key_perm |= KEY__SETATTR; > + break; > + case KEY_NEED_INVAL: > + key_perm |= KEY__INVAL; > + break; > + case KEY_NEED_REVOKE: > + key_perm |= KEY__REVOKE; > + break; > + case KEY_NEED_JOIN: > + key_perm |= KEY__JOIN; > + break; > + case KEY_NEED_CLEAR: > + key_perm |= KEY__CLEAR; > + break; > + } > + bit <<= 1; > + x >>= 1; > + } > + > + /* If old policy, then reset new perms to orig. */ > + if (!selinux_policycap_key_perms()) { > + if (perm & KEY_NEED_INVAL) { > + key_perm &= ~KEY__INVAL; > + key_perm |= KEY__SEARCH; > + } > + if (perm & KEY_NEED_REVOKE && !(perm & > OLD_KEY_NEED_SETATTR)) { > + key_perm &= ~KEY__REVOKE; > + key_perm |= KEY__WRITE; > + } > + if (perm & KEY_NEED_JOIN) { > + key_perm &= ~KEY__JOIN; > + key_perm |= KEY__SEARCH; > + } > + if (perm & KEY_NEED_CLEAR) { > + key_perm &= ~KEY__CLEAR; > + key_perm |= KEY__WRITE; > + } > + } > > sid = cred_sid(cred); > > @@ -6530,7 +6580,7 @@ static int selinux_key_permission(key_ref_t > key_ref, > ksec = key->security; > > return avc_has_perm(&selinux_state, > - sid, ksec->sid, SECCLASS_KEY, > oldstyle_perm, NULL); > + sid, ksec->sid, SECCLASS_KEY, key_perm, > NULL); > } > > static int selinux_key_getsecurity(struct key *key, char **_buffer) > @@ -6555,7 +6605,7 @@ static int selinux_watch_key(struct key *key) > u32 sid = current_sid(); > > return avc_has_perm(&selinux_state, > - sid, ksec->sid, SECCLASS_KEY, > KEY_NEED_VIEW, NULL); > + sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, > NULL); > } > #endif > #endif > diff --git a/security/selinux/include/classmap.h > b/security/selinux/include/classmap.h > index 201f7e588..a51ab9bd9 100644 > --- a/security/selinux/include/classmap.h > +++ b/security/selinux/include/classmap.h > @@ -158,7 +158,7 @@ struct security_class_mapping secclass_map[] = { > { "send", "recv", "relabelto", "forward_in", "forward_out", > NULL } }, > { "key", > { "view", "read", "write", "search", "link", "setattr", > "create", > - NULL } }, > + "inval", "revoke", "join", "clear", NULL } }, > { "dccp_socket", > { COMMON_SOCK_PERMS, > "node_bind", "name_connect", NULL } }, > diff --git a/security/selinux/include/security.h > b/security/selinux/include/security.h > index 111121281..a248eef75 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -78,6 +78,7 @@ enum { > POLICYDB_CAPABILITY_ALWAYSNETWORK, > POLICYDB_CAPABILITY_CGROUPSECLABEL, > POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION, > + POLICYDB_CAPABILITY_KEYPERMS, > __POLICYDB_CAPABILITY_MAX > }; > #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1) > @@ -177,6 +178,13 @@ static inline bool > selinux_policycap_nnp_nosuid_transition(void) > return state- >> policycap[POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION]; > } > > +static inline bool selinux_policycap_key_perms(void) > +{ > + struct selinux_state *state = &selinux_state; > + > + return state->policycap[POLICYDB_CAPABILITY_KEYPERMS]; > +} > + > int security_mls_enabled(struct selinux_state *state); > int security_load_policy(struct selinux_state *state, > void *data, size_t len); > diff --git a/security/selinux/ss/services.c > b/security/selinux/ss/services.c > index d61563a36..eb3949fc8 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c > @@ -73,7 +73,8 @@ const char > *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = { > "extended_socket_class", > "always_check_network", > "cgroup_seclabel", > - "nnp_nosuid_transition" > + "nnp_nosuid_transition", > + "key_perms" > }; > > static struct selinux_ss selinux_ss; > >
On Mon, Sep 9, 2019 at 3:17 PM Richard Haines <richard_c_haines@btinternet.com> wrote: > Test all permissions associated with the key and key_socket classes. > > 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> > --- > README.md | 3 +- > defconfig | 8 ++ > policy/Makefile | 4 + > policy/test_keys.te | 161 ++++++++++++++++++++++++ > tests/Makefile | 4 + > tests/keys/.gitignore | 3 + > tests/keys/Makefile | 8 ++ > tests/keys/key_sock.c | 67 ++++++++++ > tests/keys/keyctl.c | 241 ++++++++++++++++++++++++++++++++++++ > tests/keys/keyctl_relabel.c | 93 ++++++++++++++ > tests/keys/test | 98 +++++++++++++++ > 11 files changed, 689 insertions(+), 1 deletion(-) > create mode 100644 policy/test_keys.te > create mode 100644 tests/keys/.gitignore > create mode 100644 tests/keys/Makefile > create mode 100644 tests/keys/key_sock.c > create mode 100644 tests/keys/keyctl.c > create mode 100644 tests/keys/keyctl_relabel.c > create mode 100755 tests/keys/test > > diff --git a/README.md b/README.md > index 26784f8..fe72a91 100644 > --- a/README.md > +++ b/README.md > @@ -65,7 +65,8 @@ following command: > netlabel_tools \ > iptables \ > lksctp-tools-devel \ > - attr > + attr \ > + 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 d7f0ea5..c00e291 100644 > --- a/defconfig > +++ b/defconfig > @@ -62,3 +62,11 @@ CONFIG_ANDROID_BINDER_IPC=y > # This will configure the Dynamically Allocated Binder Devices added > # to 5.0+ kernels: > CONFIG_ANDROID_BINDERFS=y > + > +# Key implementations. > +# These are enabled to test the key and key_socket controls in > +# tests/keys; they are not required for SELinux operation itself. > +CONFIG_KEYS=y > +CONFIG_KEYS_COMPAT=y > +CONFIG_KEY_DH_OPERATIONS=y > +CONFIG_NET_KEY=m > diff --git a/policy/Makefile b/policy/Makefile > index 305b572..9258a93 100644 > --- a/policy/Makefile > +++ b/policy/Makefile > @@ -71,6 +71,10 @@ ifeq ($(shell grep -q corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren > TARGETS += test_sctp.te > endif > > +ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && echo true),true) You might want to include a grep for KEYCTL_RESTRICT_KEYRING in keyutils.h to make sure a recent enough keyutils library is installed. For example, on RHEL-8 there is some old keyutils version (1.5.10+) and the test fails to build there. > +TARGETS += test_keys.te > +endif > + > ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) > TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te, $(TARGETS)) > endif > diff --git a/policy/test_keys.te b/policy/test_keys.te > new file mode 100644 > index 0000000..9c65ec8 > --- /dev/null > +++ b/policy/test_keys.te > @@ -0,0 +1,161 @@ > +# > +################# 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: > +# > +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 }; > +') > + > +# > +####################### 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 }; > + > +###################### 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 }; > + > +###################### 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 }; > + > +# > +######################## Test key_socket class ########################### > +# > +type test_key_sock_t; > +domain_type(test_key_sock_t) > +unconfined_runs_test(test_key_sock_t) > +typeattribute test_key_sock_t testdomain; > +typeattribute test_key_sock_t keydomain; > + > +# key_socket rules: > +allow test_key_sock_t self:rawip_socket { create }; > +allow test_key_sock_t self:capability { net_admin }; > +allow test_key_sock_t self:key_socket { create write }; > +# For CONFIG_NET_KEY=m > +allow test_key_sock_t kernel_t:system { module_request }; > + > +################## Deny capability { net_admin } ########################## > +type test_key_sock_no_net_admin_t; > +domain_type(test_key_sock_no_net_admin_t) > +unconfined_runs_test(test_key_sock_no_net_admin_t) > +typeattribute test_key_sock_no_net_admin_t testdomain; > +typeattribute test_key_sock_no_net_admin_t keydomain; > + > +# key_socket rules: > +allow test_key_sock_no_net_admin_t self:rawip_socket { create }; > +allow test_key_sock_no_net_admin_t self:key_socket { create write }; > + > +####################### Deny key_socket { create } ########################## > +type test_key_sock_no_create_t; > +domain_type(test_key_sock_no_create_t) > +unconfined_runs_test(test_key_sock_no_create_t) > +typeattribute test_key_sock_no_create_t testdomain; > +typeattribute test_key_sock_no_create_t keydomain; > + > +# key_socket rules: > +allow test_key_sock_no_create_t self:rawip_socket { create }; > +allow test_key_sock_no_create_t self:capability { net_admin }; > +allow test_key_sock_no_create_t self:key_socket { write }; > + > +# > +########### 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/tests/Makefile b/tests/Makefile > index 63aa325..d1dbf38 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -42,6 +42,10 @@ ifeq ($(shell grep -q binder $(POLDEV)/include/support/all_perms.spt && test -e > SUBDIRS += binder > endif > > +ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && test -e $(INCLUDEDIR)/keyutils.h && 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..4a0a48d > --- /dev/null > +++ b/tests/keys/.gitignore > @@ -0,0 +1,3 @@ > +keyctl > +keyctl_relabel > +key_sock > diff --git a/tests/keys/Makefile b/tests/keys/Makefile > new file mode 100644 > index 0000000..3a00df5 > --- /dev/null > +++ b/tests/keys/Makefile > @@ -0,0 +1,8 @@ > +TARGETS = keyctl key_sock keyctl_relabel > + > +LDLIBS += -lselinux -lkeyutils > + > +all: $(TARGETS) > + > +clean: > + rm -f $(TARGETS) > diff --git a/tests/keys/key_sock.c b/tests/keys/key_sock.c > new file mode 100644 > index 0000000..8ac1f45 > --- /dev/null > +++ b/tests/keys/key_sock.c > @@ -0,0 +1,67 @@ > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <errno.h> > +#include <stdbool.h> > +#include <sys/socket.h> > +#include <linux/pfkeyv2.h> > +#include <selinux/selinux.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[]) > +{ > + char *context; > + int opt, sock, result; > + bool verbose = false; > + > + 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); > + > + sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); > + if (sock < 0) { > + fprintf(stderr, "Failed to open PF_KEY socket: %s\n", > + strerror(errno)); > + exit(errno); > + } > + if (verbose) > + printf("Opened PF_KEY socket\n"); > + > + /* Write nothing to socket for test, expect EMSGSIZE error */ > + result = write(sock, NULL, 0); > + if (result < 0 && errno == EMSGSIZE) { > + result = 0; > + if (verbose) > + printf("Written to PF_KEY socket\n"); > + } else if (result < 0 && errno != EMSGSIZE) { > + result = -1; > + fprintf(stderr, "Failed write to PF_KEY socket: %s\n", > + strerror(errno)); > + } > + > + close(sock); > + return result; > +} > diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c > new file mode 100644 > index 0000000..6d85be7 > --- /dev/null > +++ b/tests/keys/keyctl.c > @@ -0,0 +1,241 @@ > +#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> > + > +/* This is used as the payload for each add_key() */ > +static const char payload[] = > + " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\nQDEiR/cfhZrNUPgHRrC+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\nWw4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; > + > +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 also 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_THREAD_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_RESTRICT_KEYRING, newring, NULL, NULL); > + if (link < 0) { > + fprintf(stderr, "Failed KEYCTL_RESTRICT_KEYRING: %s\n", > + strerror(errno)); > + exit(14); > + } > + if (verbose) > + printf("Restricted keyring\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 */ > + 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..0276c7a > --- /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_THREAD_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/test b/tests/keys/test > new file mode 100755 > index 0000000..4916e7c > --- /dev/null > +++ b/tests/keys/test > @@ -0,0 +1,98 @@ > +#!/usr/bin/perl > +use Test::More; > + > +BEGIN { > + $basedir = $0; > + $basedir =~ s|(.*)/[^/]*|$1|; > + > + $test_count = 13; > + > + # 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 key_socket ############# > +print "Test key_socket class\n"; > +$result = system "runcon -t test_key_sock_t $basedir/key_sock $v"; > +ok( $result eq 0 ); > + > +# Deny capability { net_admin } - EPERM > +$result = > + system "runcon -t test_key_sock_no_net_admin_t $basedir/key_sock $v 2>&1"; > +ok( $result >> 8 eq 1 ); > + > +# Deny key_socket { create } - EACCES > +$result = > + system "runcon -t test_key_sock_no_create_t $basedir/key_sock $v 2>&1"; > +ok( $result >> 8 eq 13 ); > + > +exit; > -- > 2.21.0 > -- Ondrej Mosnacek <omosnace at redhat dot com> Software Engineer, Security Technologies Red Hat, Inc.
On Tue, 2019-09-17 at 09:04 +0200, Ondrej Mosnacek wrote: > On Mon, Sep 9, 2019 at 3:17 PM Richard Haines > <richard_c_haines@btinternet.com> wrote: > > Test all permissions associated with the key and key_socket > > classes. > > > > 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> > > --- > > README.md | 3 +- > > defconfig | 8 ++ > > policy/Makefile | 4 + > > policy/test_keys.te | 161 ++++++++++++++++++++++++ > > tests/Makefile | 4 + > > tests/keys/.gitignore | 3 + > > tests/keys/Makefile | 8 ++ > > tests/keys/key_sock.c | 67 ++++++++++ > > tests/keys/keyctl.c | 241 > > ++++++++++++++++++++++++++++++++++++ > > tests/keys/keyctl_relabel.c | 93 ++++++++++++++ > > tests/keys/test | 98 +++++++++++++++ > > 11 files changed, 689 insertions(+), 1 deletion(-) > > create mode 100644 policy/test_keys.te > > create mode 100644 tests/keys/.gitignore > > create mode 100644 tests/keys/Makefile > > create mode 100644 tests/keys/key_sock.c > > create mode 100644 tests/keys/keyctl.c > > create mode 100644 tests/keys/keyctl_relabel.c > > create mode 100755 tests/keys/test > > > > diff --git a/README.md b/README.md > > index 26784f8..fe72a91 100644 > > --- a/README.md > > +++ b/README.md > > @@ -65,7 +65,8 @@ following command: > > netlabel_tools \ > > iptables \ > > lksctp-tools-devel \ > > - attr > > + attr \ > > + 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 d7f0ea5..c00e291 100644 > > --- a/defconfig > > +++ b/defconfig > > @@ -62,3 +62,11 @@ CONFIG_ANDROID_BINDER_IPC=y > > # This will configure the Dynamically Allocated Binder Devices > > added > > # to 5.0+ kernels: > > CONFIG_ANDROID_BINDERFS=y > > + > > +# Key implementations. > > +# These are enabled to test the key and key_socket controls in > > +# tests/keys; they are not required for SELinux operation itself. > > +CONFIG_KEYS=y > > +CONFIG_KEYS_COMPAT=y > > +CONFIG_KEY_DH_OPERATIONS=y > > +CONFIG_NET_KEY=m > > diff --git a/policy/Makefile b/policy/Makefile > > index 305b572..9258a93 100644 > > --- a/policy/Makefile > > +++ b/policy/Makefile > > @@ -71,6 +71,10 @@ ifeq ($(shell grep -q > > corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren > > TARGETS += test_sctp.te > > endif > > > > +ifeq ($(shell grep -q key_socket > > $(POLDEV)/include/support/all_perms.spt && echo true),true) > > You might want to include a grep for KEYCTL_RESTRICT_KEYRING in > keyutils.h to make sure a recent enough keyutils library is > installed. > For example, on RHEL-8 there is some old keyutils version (1.5.10+) > and the test fails to build there. > Thanks for the info. I'll remove the KEYCTL_RESTRICT_KEYRING function as I use it only to check setattr permission. I can use KEYCTL_SET_TIMEOUT instead. > > +TARGETS += test_keys.te > > +endif > > + > > ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) > > TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te, > > $(TARGETS)) > > endif > > diff --git a/policy/test_keys.te b/policy/test_keys.te > > new file mode 100644 > > index 0000000..9c65ec8 > > --- /dev/null > > +++ b/policy/test_keys.te > > @@ -0,0 +1,161 @@ > > +# > > +################# 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: > > +# > > +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 }; > > +') > > + > > +# > > +####################### 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 }; > > + > > +###################### 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 }; > > + > > +###################### 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 }; > > + > > +# > > +######################## Test key_socket class > > ########################### > > +# > > +type test_key_sock_t; > > +domain_type(test_key_sock_t) > > +unconfined_runs_test(test_key_sock_t) > > +typeattribute test_key_sock_t testdomain; > > +typeattribute test_key_sock_t keydomain; > > + > > +# key_socket rules: > > +allow test_key_sock_t self:rawip_socket { create }; > > +allow test_key_sock_t self:capability { net_admin }; > > +allow test_key_sock_t self:key_socket { create write }; > > +# For CONFIG_NET_KEY=m > > +allow test_key_sock_t kernel_t:system { module_request }; > > + > > +################## Deny capability { net_admin } > > ########################## > > +type test_key_sock_no_net_admin_t; > > +domain_type(test_key_sock_no_net_admin_t) > > +unconfined_runs_test(test_key_sock_no_net_admin_t) > > +typeattribute test_key_sock_no_net_admin_t testdomain; > > +typeattribute test_key_sock_no_net_admin_t keydomain; > > + > > +# key_socket rules: > > +allow test_key_sock_no_net_admin_t self:rawip_socket { create }; > > +allow test_key_sock_no_net_admin_t self:key_socket { create write > > }; > > + > > +####################### Deny key_socket { create } > > ########################## > > +type test_key_sock_no_create_t; > > +domain_type(test_key_sock_no_create_t) > > +unconfined_runs_test(test_key_sock_no_create_t) > > +typeattribute test_key_sock_no_create_t testdomain; > > +typeattribute test_key_sock_no_create_t keydomain; > > + > > +# key_socket rules: > > +allow test_key_sock_no_create_t self:rawip_socket { create }; > > +allow test_key_sock_no_create_t self:capability { net_admin }; > > +allow test_key_sock_no_create_t self:key_socket { write }; > > + > > +# > > +########### 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/tests/Makefile b/tests/Makefile > > index 63aa325..d1dbf38 100644 > > --- a/tests/Makefile > > +++ b/tests/Makefile > > @@ -42,6 +42,10 @@ ifeq ($(shell grep -q binder > > $(POLDEV)/include/support/all_perms.spt && test -e > > SUBDIRS += binder > > endif > > > > +ifeq ($(shell grep -q key_socket > > $(POLDEV)/include/support/all_perms.spt && test -e > > $(INCLUDEDIR)/keyutils.h && 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..4a0a48d > > --- /dev/null > > +++ b/tests/keys/.gitignore > > @@ -0,0 +1,3 @@ > > +keyctl > > +keyctl_relabel > > +key_sock > > diff --git a/tests/keys/Makefile b/tests/keys/Makefile > > new file mode 100644 > > index 0000000..3a00df5 > > --- /dev/null > > +++ b/tests/keys/Makefile > > @@ -0,0 +1,8 @@ > > +TARGETS = keyctl key_sock keyctl_relabel > > + > > +LDLIBS += -lselinux -lkeyutils > > + > > +all: $(TARGETS) > > + > > +clean: > > + rm -f $(TARGETS) > > diff --git a/tests/keys/key_sock.c b/tests/keys/key_sock.c > > new file mode 100644 > > index 0000000..8ac1f45 > > --- /dev/null > > +++ b/tests/keys/key_sock.c > > @@ -0,0 +1,67 @@ > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <string.h> > > +#include <unistd.h> > > +#include <errno.h> > > +#include <stdbool.h> > > +#include <sys/socket.h> > > +#include <linux/pfkeyv2.h> > > +#include <selinux/selinux.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[]) > > +{ > > + char *context; > > + int opt, sock, result; > > + bool verbose = false; > > + > > + 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); > > + > > + sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); > > + if (sock < 0) { > > + fprintf(stderr, "Failed to open PF_KEY socket: > > %s\n", > > + strerror(errno)); > > + exit(errno); > > + } > > + if (verbose) > > + printf("Opened PF_KEY socket\n"); > > + > > + /* Write nothing to socket for test, expect EMSGSIZE error > > */ > > + result = write(sock, NULL, 0); > > + if (result < 0 && errno == EMSGSIZE) { > > + result = 0; > > + if (verbose) > > + printf("Written to PF_KEY socket\n"); > > + } else if (result < 0 && errno != EMSGSIZE) { > > + result = -1; > > + fprintf(stderr, "Failed write to PF_KEY socket: > > %s\n", > > + strerror(errno)); > > + } > > + > > + close(sock); > > + return result; > > +} > > diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c > > new file mode 100644 > > index 0000000..6d85be7 > > --- /dev/null > > +++ b/tests/keys/keyctl.c > > @@ -0,0 +1,241 @@ > > +#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> > > + > > +/* This is used as the payload for each add_key() */ > > +static const char payload[] = > > + " -----BEGIN PUBLIC KEY----- > > \nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\ > > nQDEiR/cfhZrNUPgHRrC+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\n > > Ww4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4 > > aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; > > + > > +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 also 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_THREAD_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_RESTRICT_KEYRING, newring, NULL, > > NULL); > > + if (link < 0) { > > + fprintf(stderr, "Failed KEYCTL_RESTRICT_KEYRING: > > %s\n", > > + strerror(errno)); > > + exit(14); > > + } > > + if (verbose) > > + printf("Restricted keyring\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 */ > > + 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..0276c7a > > --- /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_THREAD_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/test b/tests/keys/test > > new file mode 100755 > > index 0000000..4916e7c > > --- /dev/null > > +++ b/tests/keys/test > > @@ -0,0 +1,98 @@ > > +#!/usr/bin/perl > > +use Test::More; > > + > > +BEGIN { > > + $basedir = $0; > > + $basedir =~ s|(.*)/[^/]*|$1|; > > + > > + $test_count = 13; > > + > > + # 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 key_socket ############# > > +print "Test key_socket class\n"; > > +$result = system "runcon -t test_key_sock_t $basedir/key_sock $v"; > > +ok( $result eq 0 ); > > + > > +# Deny capability { net_admin } - EPERM > > +$result = > > + system "runcon -t test_key_sock_no_net_admin_t $basedir/key_sock > > $v 2>&1"; > > +ok( $result >> 8 eq 1 ); > > + > > +# Deny key_socket { create } - EACCES > > +$result = > > + system "runcon -t test_key_sock_no_create_t $basedir/key_sock $v > > 2>&1"; > > +ok( $result >> 8 eq 13 ); > > + > > +exit; > > -- > > 2.21.0 > > > > -- > Ondrej Mosnacek <omosnace at redhat dot com> > Software Engineer, Security Technologies > Red Hat, Inc. >
diff --git a/README.md b/README.md index 26784f8..fe72a91 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ following command: netlabel_tools \ iptables \ lksctp-tools-devel \ - attr + attr \ + 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 d7f0ea5..c00e291 100644 --- a/defconfig +++ b/defconfig @@ -62,3 +62,11 @@ CONFIG_ANDROID_BINDER_IPC=y # This will configure the Dynamically Allocated Binder Devices added # to 5.0+ kernels: CONFIG_ANDROID_BINDERFS=y + +# Key implementations. +# These are enabled to test the key and key_socket controls in +# tests/keys; they are not required for SELinux operation itself. +CONFIG_KEYS=y +CONFIG_KEYS_COMPAT=y +CONFIG_KEY_DH_OPERATIONS=y +CONFIG_NET_KEY=m diff --git a/policy/Makefile b/policy/Makefile index 305b572..9258a93 100644 --- a/policy/Makefile +++ b/policy/Makefile @@ -71,6 +71,10 @@ ifeq ($(shell grep -q corenet_sctp_bind_all_nodes $(POLDEV)/include/kernel/coren TARGETS += test_sctp.te endif +ifeq ($(shell grep -q key_socket $(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, $(TARGETS)) endif diff --git a/policy/test_keys.te b/policy/test_keys.te new file mode 100644 index 0000000..9c65ec8 --- /dev/null +++ b/policy/test_keys.te @@ -0,0 +1,161 @@ +# +################# 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: +# +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 }; +') + +# +####################### 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 }; + +###################### 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 }; + +###################### 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 }; + +# +######################## Test key_socket class ########################### +# +type test_key_sock_t; +domain_type(test_key_sock_t) +unconfined_runs_test(test_key_sock_t) +typeattribute test_key_sock_t testdomain; +typeattribute test_key_sock_t keydomain; + +# key_socket rules: +allow test_key_sock_t self:rawip_socket { create }; +allow test_key_sock_t self:capability { net_admin }; +allow test_key_sock_t self:key_socket { create write }; +# For CONFIG_NET_KEY=m +allow test_key_sock_t kernel_t:system { module_request }; + +################## Deny capability { net_admin } ########################## +type test_key_sock_no_net_admin_t; +domain_type(test_key_sock_no_net_admin_t) +unconfined_runs_test(test_key_sock_no_net_admin_t) +typeattribute test_key_sock_no_net_admin_t testdomain; +typeattribute test_key_sock_no_net_admin_t keydomain; + +# key_socket rules: +allow test_key_sock_no_net_admin_t self:rawip_socket { create }; +allow test_key_sock_no_net_admin_t self:key_socket { create write }; + +####################### Deny key_socket { create } ########################## +type test_key_sock_no_create_t; +domain_type(test_key_sock_no_create_t) +unconfined_runs_test(test_key_sock_no_create_t) +typeattribute test_key_sock_no_create_t testdomain; +typeattribute test_key_sock_no_create_t keydomain; + +# key_socket rules: +allow test_key_sock_no_create_t self:rawip_socket { create }; +allow test_key_sock_no_create_t self:capability { net_admin }; +allow test_key_sock_no_create_t self:key_socket { write }; + +# +########### 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/tests/Makefile b/tests/Makefile index 63aa325..d1dbf38 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -42,6 +42,10 @@ ifeq ($(shell grep -q binder $(POLDEV)/include/support/all_perms.spt && test -e SUBDIRS += binder endif +ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && test -e $(INCLUDEDIR)/keyutils.h && 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..4a0a48d --- /dev/null +++ b/tests/keys/.gitignore @@ -0,0 +1,3 @@ +keyctl +keyctl_relabel +key_sock diff --git a/tests/keys/Makefile b/tests/keys/Makefile new file mode 100644 index 0000000..3a00df5 --- /dev/null +++ b/tests/keys/Makefile @@ -0,0 +1,8 @@ +TARGETS = keyctl key_sock keyctl_relabel + +LDLIBS += -lselinux -lkeyutils + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) diff --git a/tests/keys/key_sock.c b/tests/keys/key_sock.c new file mode 100644 index 0000000..8ac1f45 --- /dev/null +++ b/tests/keys/key_sock.c @@ -0,0 +1,67 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdbool.h> +#include <sys/socket.h> +#include <linux/pfkeyv2.h> +#include <selinux/selinux.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[]) +{ + char *context; + int opt, sock, result; + bool verbose = false; + + 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); + + sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (sock < 0) { + fprintf(stderr, "Failed to open PF_KEY socket: %s\n", + strerror(errno)); + exit(errno); + } + if (verbose) + printf("Opened PF_KEY socket\n"); + + /* Write nothing to socket for test, expect EMSGSIZE error */ + result = write(sock, NULL, 0); + if (result < 0 && errno == EMSGSIZE) { + result = 0; + if (verbose) + printf("Written to PF_KEY socket\n"); + } else if (result < 0 && errno != EMSGSIZE) { + result = -1; + fprintf(stderr, "Failed write to PF_KEY socket: %s\n", + strerror(errno)); + } + + close(sock); + return result; +} diff --git a/tests/keys/keyctl.c b/tests/keys/keyctl.c new file mode 100644 index 0000000..6d85be7 --- /dev/null +++ b/tests/keys/keyctl.c @@ -0,0 +1,241 @@ +#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> + +/* This is used as the payload for each add_key() */ +static const char payload[] = + " -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN4FHsPjlJf03r9KfNt1Ma9/D6\nQDEiR/cfhZrNUPgHRrC+E4dj52VJSonPFJ6HaLlUi5pZq2t1LqPNrMfFKCNn12m+\nWw4aduBJM7u1RUPSNxrlfDAJZkdtNALOO/ds3U93hZrxOYNetzbnjILDu5JT1nbI\n4aC60SkdlCw1TxmvXwIDAQAB\n-----END PUBLIC KEY-----\n"; + +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 also 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_THREAD_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_RESTRICT_KEYRING, newring, NULL, NULL); + if (link < 0) { + fprintf(stderr, "Failed KEYCTL_RESTRICT_KEYRING: %s\n", + strerror(errno)); + exit(14); + } + if (verbose) + printf("Restricted keyring\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 */ + 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..0276c7a --- /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_THREAD_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/test b/tests/keys/test new file mode 100755 index 0000000..4916e7c --- /dev/null +++ b/tests/keys/test @@ -0,0 +1,98 @@ +#!/usr/bin/perl +use Test::More; + +BEGIN { + $basedir = $0; + $basedir =~ s|(.*)/[^/]*|$1|; + + $test_count = 13; + + # 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 key_socket ############# +print "Test key_socket class\n"; +$result = system "runcon -t test_key_sock_t $basedir/key_sock $v"; +ok( $result eq 0 ); + +# Deny capability { net_admin } - EPERM +$result = + system "runcon -t test_key_sock_no_net_admin_t $basedir/key_sock $v 2>&1"; +ok( $result >> 8 eq 1 ); + +# Deny key_socket { create } - EACCES +$result = + system "runcon -t test_key_sock_no_create_t $basedir/key_sock $v 2>&1"; +ok( $result >> 8 eq 13 ); + +exit;
Test all permissions associated with the key and key_socket classes. 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> --- README.md | 3 +- defconfig | 8 ++ policy/Makefile | 4 + policy/test_keys.te | 161 ++++++++++++++++++++++++ tests/Makefile | 4 + tests/keys/.gitignore | 3 + tests/keys/Makefile | 8 ++ tests/keys/key_sock.c | 67 ++++++++++ tests/keys/keyctl.c | 241 ++++++++++++++++++++++++++++++++++++ tests/keys/keyctl_relabel.c | 93 ++++++++++++++ tests/keys/test | 98 +++++++++++++++ 11 files changed, 689 insertions(+), 1 deletion(-) create mode 100644 policy/test_keys.te create mode 100644 tests/keys/.gitignore create mode 100644 tests/keys/Makefile create mode 100644 tests/keys/key_sock.c create mode 100644 tests/keys/keyctl.c create mode 100644 tests/keys/keyctl_relabel.c create mode 100755 tests/keys/test