---
V2 Change: Rework error handling
V3 Change: Remove rawip_socket allow rule as not required.
defconfig | 4 ++
policy/Makefile | 4 ++
policy/test_key_socket.te | 70 ++++++++++++++++++
tests/Makefile | 4 ++
tests/key_socket/.gitignore | 1 +
tests/key_socket/Makefile | 7 ++
tests/key_socket/key_sock.c | 137 ++++++++++++++++++++++++++++++++++++
tests/key_socket/test | 45 ++++++++++++
8 files changed, 272 insertions(+)
create mode 100644 policy/test_key_socket.te
create mode 100644 tests/key_socket/.gitignore
create mode 100644 tests/key_socket/Makefile
create mode 100644 tests/key_socket/key_sock.c
create mode 100755 tests/key_socket/test
diff --git a/defconfig b/defconfig
index b13075d..0574f1d 100644
--- a/defconfig
+++ b/defconfig
@@ -74,3 +74,7 @@ CONFIG_BPF_SYSCALL=y
CONFIG_KEYS=y
CONFIG_KEYS_COMPAT=y
CONFIG_KEY_DH_OPERATIONS=y
+
+# Test key management socket.
+# This is not required for SELinux operation itself.
+CONFIG_NET_KEY=m
diff --git a/policy/Makefile b/policy/Makefile
index ff65153..ad94c43 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -90,6 +90,10 @@ ifeq ($(shell grep -q all_file_perms.*watch $(POLDEV)/include/support/all_perms.
TARGETS+=test_notify.te
endif
+ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && echo true),true)
+TARGETS += test_key_socket.te
+endif
+
ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS))
endif
diff --git a/policy/test_key_socket.te b/policy/test_key_socket.te
new file mode 100644
index 0000000..cde426b
--- /dev/null
+++ b/policy/test_key_socket.te
@@ -0,0 +1,70 @@
+#
+############## Test key management socket 'key_socket' #####################
+#
+attribute keysockdomain;
+
+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 keysockdomain;
+
+# key_socket rules:
+allow test_key_sock_t self:capability { net_admin };
+allow test_key_sock_t self:key_socket { create write read setopt };
+# For CONFIG_NET_KEY=m
+allow test_key_sock_t kernel_t:system { module_request };
+
+################## Deny capability { net_admin } ##########################
+#
+# Note that when capability { net_admin } is removed for the test
+# there will not be an audit message in the log as the Fedora policy
+# is built with 'hide_broken_symptoms' that adds the following:
+# dontaudit test_key_sock_no_net_admin_t self:capability { net_admin sys_module };
+#
+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 keysockdomain;
+
+allow test_key_sock_no_net_admin_t self:key_socket { create write read setopt };
+allow test_key_sock_no_net_admin_t kernel_t:system { module_request };
+
+####################### 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 keysockdomain;
+
+allow test_key_sock_no_create_t self:capability { net_admin };
+allow test_key_sock_no_create_t self:key_socket { write read setopt };
+
+####################### Deny key_socket { write } ##########################
+type test_key_sock_no_write_t;
+domain_type(test_key_sock_no_write_t)
+unconfined_runs_test(test_key_sock_no_write_t)
+typeattribute test_key_sock_no_write_t testdomain;
+typeattribute test_key_sock_no_write_t keysockdomain;
+
+allow test_key_sock_no_write_t self:capability { net_admin };
+allow test_key_sock_no_write_t self:key_socket { create read setopt };
+allow test_key_sock_no_write_t kernel_t:system { module_request };
+
+####################### Deny key_socket { read } ##########################
+type test_key_sock_no_read_t;
+domain_type(test_key_sock_no_read_t)
+unconfined_runs_test(test_key_sock_no_read_t)
+typeattribute test_key_sock_no_read_t testdomain;
+typeattribute test_key_sock_no_read_t keysockdomain;
+
+allow test_key_sock_no_read_t self:capability { net_admin };
+allow test_key_sock_no_read_t self:key_socket { create write setopt };
+allow test_key_sock_no_read_t kernel_t:system { module_request };
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(keysockdomain)
+userdom_sysadm_entry_spec_domtrans_to(keysockdomain)
diff --git a/tests/Makefile b/tests/Makefile
index 0021590..cca6648 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -52,6 +52,10 @@ ifeq ($(shell grep -q all_key_perms $(POLDEV)/include/support/all_perms.spt && e
SUBDIRS += keys
endif
+ifeq ($(shell grep -q key_socket $(POLDEV)/include/support/all_perms.spt && test -e $(INCLUDEDIR)/keyutils.h && echo true),true)
+SUBDIRS += key_socket
+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/key_socket/.gitignore b/tests/key_socket/.gitignore
new file mode 100644
index 0000000..1a532c0
--- /dev/null
+++ b/tests/key_socket/.gitignore
@@ -0,0 +1 @@
+key_sock
diff --git a/tests/key_socket/Makefile b/tests/key_socket/Makefile
new file mode 100644
index 0000000..e5e6a58
--- /dev/null
+++ b/tests/key_socket/Makefile
@@ -0,0 +1,7 @@
+TARGETS = key_sock
+LDLIBS += -lselinux
+
+all: $(TARGETS)
+
+clean:
+ rm -f $(TARGETS)
diff --git a/tests/key_socket/key_sock.c b/tests/key_socket/key_sock.c
new file mode 100644
index 0000000..29beb0e
--- /dev/null
+++ b/tests/key_socket/key_sock.c
@@ -0,0 +1,137 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <linux/pfkeyv2.h>
+#include <selinux/selinux.h>
+
+static void print_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;
+ struct timeval tm;
+ struct sadb_msg w_msg, r_msg;
+ int mlen = sizeof(struct sadb_msg);
+
+ while ((opt = getopt(argc, argv, "v")) != -1) {
+ switch (opt) {
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ print_usage(argv[0]);
+ }
+ }
+
+ result = getcon(&context);
+ if (result < 0) {
+ fprintf(stderr, "Failed to obtain process context\n");
+ exit(-1);
+ }
+
+ if (verbose)
+ printf("Process context:\n\t%s\n", context);
+
+ free(context);
+
+ sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+ if (sock < 0) {
+ fprintf(stderr, "Failed to open key management socket: %s\n",
+ strerror(errno));
+ /* Return errno as denying net_admin=EPERM, create=EACCES */
+ exit(errno);
+ }
+
+ if (verbose)
+ printf("Opened key management socket\n");
+
+ /* Set socket timeout for read in case no response from kernel */
+ tm.tv_sec = 3;
+ tm.tv_usec = 0;
+ result = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm));
+ if (result < 0) {
+ fprintf(stderr, "Failed setsockopt SO_RCVTIMEO: %s\n",
+ strerror(errno));
+ close(sock);
+ exit(-1);
+ }
+
+ if (verbose)
+ printf("setsocketopt: SO_RCVTIMEO - %ld seconds\n", tm.tv_sec);
+
+ memset(&w_msg, 0, mlen);
+ w_msg.sadb_msg_version = PF_KEY_V2;
+ w_msg.sadb_msg_type = SADB_FLUSH;
+ w_msg.sadb_msg_satype = SADB_SATYPE_AH;
+ /* sadb_msg_len contains length in 64-bit words */
+ w_msg.sadb_msg_len = (mlen / sizeof(uint64_t));
+ w_msg.sadb_msg_seq = 99;
+ w_msg.sadb_msg_pid = getpid();
+
+ result = write(sock, &w_msg, mlen);
+ if (result < 0) {
+ fprintf(stderr, "Failed write to key management socket: %s\n",
+ strerror(errno));
+ close(sock);
+ exit(errno); /* Return errno to test if EACCES */
+ }
+
+ if (verbose) {
+ printf("Write sadb_msg data to key management socket:\n");
+ printf("\tver: PF_KEY_V2 type: SADB_FLUSH sa_type: SADB_SATYPE_AH\n");
+ printf("\tseq: %d pid: %d\n", w_msg.sadb_msg_seq,
+ w_msg.sadb_msg_pid);
+ }
+
+ memset(&r_msg, 0, mlen);
+
+ result = read(sock, &r_msg, mlen);
+ if (result < 0) {
+ fprintf(stderr, "Failed to read key management socket: %s\n",
+ strerror(errno));
+ close(sock);
+ exit(errno); /* Return errno to test if EACCES */
+ }
+
+ if (r_msg.sadb_msg_version != w_msg.sadb_msg_version ||
+ r_msg.sadb_msg_type != w_msg.sadb_msg_type ||
+ r_msg.sadb_msg_satype != w_msg.sadb_msg_satype ||
+ r_msg.sadb_msg_seq != w_msg.sadb_msg_seq ||
+ r_msg.sadb_msg_pid != getpid()) {
+ fprintf(stderr, "Failed to read correct sadb_msg data:\n");
+ fprintf(stderr, "\tSent - ver: %d type: %d sa_type: %d seq: %d pid: %d\n",
+ w_msg.sadb_msg_version, w_msg.sadb_msg_type,
+ w_msg.sadb_msg_satype, w_msg.sadb_msg_seq,
+ w_msg.sadb_msg_pid);
+ fprintf(stderr, "\tRecv - ver: %d type: %d sa_type: %d seq: %d pid: %d\n",
+ r_msg.sadb_msg_version, r_msg.sadb_msg_type,
+ r_msg.sadb_msg_satype, r_msg.sadb_msg_seq,
+ r_msg.sadb_msg_pid);
+ close(sock);
+ exit(-1);
+ }
+
+ if (verbose) {
+ printf("Read sadb_msg data from key management socket:\n");
+ printf("\tver: PF_KEY_V2 type: SADB_FLUSH sa_type: SADB_SATYPE_AH\n");
+ printf("\tseq: %d pid: %d\n", r_msg.sadb_msg_seq,
+ r_msg.sadb_msg_pid);
+ }
+
+ close(sock);
+ return 0;
+}
diff --git a/tests/key_socket/test b/tests/key_socket/test
new file mode 100755
index 0000000..a13327f
--- /dev/null
+++ b/tests/key_socket/test
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+ $basedir = $0;
+ $basedir =~ s|(.*)/[^/]*|$1|;
+
+ # 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 = " ";
+ }
+
+ plan tests => 5;
+}
+
+############ Test key_socket #############
+print "Test key management key_socket\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 );
+
+# Deny key_socket { write } - EACCES
+$result = system "runcon -t test_key_sock_no_write_t $basedir/key_sock $v 2>&1";
+ok( $result >> 8 eq 13 );
+
+# Deny key_socket { read } - EACCES
+$result = system "runcon -t test_key_sock_no_read_t $basedir/key_sock $v 2>&1";
+ok( $result >> 8 eq 13 );
+
+exit;