From: Stephen Smalley <sds@tycho.nsa.gov>
To: Richard Haines <richard_c_haines@btinternet.com>,
selinux@vger.kernel.org, paul@paul-moore.com
Subject: Re: [PATCH] selinux-testsuite: Add key and key_socket tests
Date: Mon, 16 Sep 2019 15:11:48 -0400 [thread overview]
Message-ID: <e31a9342-2970-7d06-fcaa-57af3d05399d@tycho.nsa.gov> (raw)
In-Reply-To: <416a31737aec3b57b929098b15c02636faa68d4e.camel@btinternet.com>
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;
>>>
>
next prev parent reply other threads:[~2019-09-16 19:19 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-09 13:17 [PATCH] selinux-testsuite: Add key and key_socket tests Richard Haines
2019-09-16 17:58 ` Stephen Smalley
2019-09-16 18:55 ` Richard Haines
2019-09-16 19:11 ` Stephen Smalley [this message]
2019-09-16 19:23 ` Richard Haines
2019-09-16 19:39 ` Stephen Smalley
2019-09-17 7:04 ` Ondrej Mosnacek
2019-09-19 16:29 ` Richard Haines
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e31a9342-2970-7d06-fcaa-57af3d05399d@tycho.nsa.gov \
--to=sds@tycho.nsa.gov \
--cc=paul@paul-moore.com \
--cc=richard_c_haines@btinternet.com \
--cc=selinux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).