diff mbox

selinux-testsuite: Add tests for extended socket classes.

Message ID 1481299051-27863-1-git-send-email-sds@tycho.nsa.gov (mailing list archive)
State Not Applicable
Headers show

Commit Message

Stephen Smalley Dec. 9, 2016, 3:57 p.m. UTC
Add tests for the extended_socket_class policy capability.
This change includes the following tests:
- Test that ICMP datagram sockets are mapped to the new icmp_socket
class and not to rawip_socket for both IPv4 and IPv6.

- Test that SCTP stream and seqpacket sockets are mapped to the
new sctp_socket class and not to rawip_socket for both IPv4 and IPv6.

- Test that Bluetooth sockets are mapped to the new bluetooth_socket
class and not to socket.

- Test that AF_ALG sockets are mapped to the new alg_socket class
and not to socket.

The tests are only run if the extended_socket_class policy capability
is present and enabled in the kernel and the base policy, and only if
the new classes are defined in the base policy.  This avoids breaking
the testsuite on systems with older kernels, older policies, or
policies that do not enable the policy capability.

Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
---
 README                                   |  8 +++
 policy/Makefile                          |  4 ++
 policy/test_extended_socket_class.te     | 64 +++++++++++++++++++++++
 tests/Makefile                           |  4 ++
 tests/extended_socket_class/Makefile     |  4 ++
 tests/extended_socket_class/sockcreate.c | 89 ++++++++++++++++++++++++++++++++
 tests/extended_socket_class/test         | 76 +++++++++++++++++++++++++++
 7 files changed, 249 insertions(+)
 create mode 100644 policy/test_extended_socket_class.te
 create mode 100644 tests/extended_socket_class/Makefile
 create mode 100644 tests/extended_socket_class/sockcreate.c
 create mode 100755 tests/extended_socket_class/test

Comments

Stephen Smalley Dec. 9, 2016, 4:14 p.m. UTC | #1
On 12/09/2016 10:57 AM, Stephen Smalley wrote:
> Add tests for the extended_socket_class policy capability.
> This change includes the following tests:
> - Test that ICMP datagram sockets are mapped to the new icmp_socket
> class and not to rawip_socket for both IPv4 and IPv6.
> 
> - Test that SCTP stream and seqpacket sockets are mapped to the
> new sctp_socket class and not to rawip_socket for both IPv4 and IPv6.
> 
> - Test that Bluetooth sockets are mapped to the new bluetooth_socket
> class and not to socket.
> 
> - Test that AF_ALG sockets are mapped to the new alg_socket class
> and not to socket.
> 
> The tests are only run if the extended_socket_class policy capability
> is present and enabled in the kernel and the base policy, and only if
> the new classes are defined in the base policy.  This avoids breaking
> the testsuite on systems with older kernels, older policies, or
> policies that do not enable the policy capability.

BTW, while creating these tests, I was also trying to test AF_BRIDGE
originally, but it doesn't appear that you can in fact create AF_BRIDGE
sockets AFAICT; socket(AF_BRIDGE, <any>, <any>) seems to always return
EAFNOSUPPORT even though CONFIG_BRIDGE_* is enabled in the kernel
config.  So possibly we don't need a bridge_socket security class?  Not
sure what other address families are similar?

> 
> Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
> ---
>  README                                   |  8 +++
>  policy/Makefile                          |  4 ++
>  policy/test_extended_socket_class.te     | 64 +++++++++++++++++++++++
>  tests/Makefile                           |  4 ++
>  tests/extended_socket_class/Makefile     |  4 ++
>  tests/extended_socket_class/sockcreate.c | 89 ++++++++++++++++++++++++++++++++
>  tests/extended_socket_class/test         | 76 +++++++++++++++++++++++++++
>  7 files changed, 249 insertions(+)
>  create mode 100644 policy/test_extended_socket_class.te
>  create mode 100644 tests/extended_socket_class/Makefile
>  create mode 100644 tests/extended_socket_class/sockcreate.c
>  create mode 100755 tests/extended_socket_class/test
> 
> diff --git a/README b/README
> index 8dbbbda..521e965 100644
> --- a/README
> +++ b/README
> @@ -39,6 +39,14 @@ CONFIG_JFS_SECURITY=y
>  CONFIG_XFS_SECURITY=y
>  CONFIG_JFFS2_FS_SECURITY=y
>  
> +# Network protocol implementations.
> +# These are enabled to test the extended socket classes in
> +# tests/extended_socket_class; they are not required
> +# for SELinux operation itself.
> +CONFIG_IP_SCTP=m
> +CONFIG_BT=m
> +CONFIG_CRYPTO_USER_API=m
> +
>  Do not set CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX; it is an
>  option for legacy distributions (Fedora 3 and 4).
>  
> diff --git a/policy/Makefile b/policy/Makefile
> index 16ab3b9..992278b 100644
> --- a/policy/Makefile
> +++ b/policy/Makefile
> @@ -30,6 +30,10 @@ ifeq ($(shell grep -q cap_userns $(POLDEV)/include/support/all_perms.spt && echo
>  TARGETS += test_cap_userns.te
>  endif
>  
> +ifeq ($(shell grep -q icmp_socket $(POLDEV)/include/support/all_perms.spt && echo true),true)
> +TARGETS += test_extended_socket_class.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_extended_socket_class.te b/policy/test_extended_socket_class.te
> new file mode 100644
> index 0000000..4f30c9f
> --- /dev/null
> +++ b/policy/test_extended_socket_class.te
> @@ -0,0 +1,64 @@
> +########################################
> +#
> +# Policy for testing the new socket classes
> +# introduced when the extended_socket_class
> +# policy capability is enabled in policy and
> +# supported by the kernel.
> +
> +attribute extsocktestdomain;
> +
> +#
> +# extended_socket_test(newclass, oldclass)
> +#
> +# Generate a pair of test domains and rules to test
> +# that when the extended_socket_class policy capability
> +# is enabled, the kernel checks permission against the
> +# 'newclass' security class rather than the 'oldclass'
> +# security class.
> +#
> +define(`extended_socket_class_test', `
> +# Domain that is allowed to create $1_socket.
> +type test_$1_t;
> +domain_type(test_$1_t)
> +unconfined_runs_test(test_$1_t)
> +typeattribute test_$1_t extsocktestdomain;
> +typeattribute test_$1_t testdomain;
> +
> +# Allow $1 but not $2.
> +# This is to ensure that the kernel is checking the right class.
> +allow test_$1_t self:$1 create_socket_perms;
> +
> +# Domain that is not allowed to create $1.
> +type test_no_$1_t;
> +domain_type(test_no_$1_t)
> +unconfined_runs_test(test_no_$1_t)
> +typeattribute test_no_$1_t extsocktestdomain;
> +typeattribute test_no_$1_t testdomain;
> +
> +# Allow $2 but not $1.
> +# This is to ensure that the kernel is checking the right class.
> +allow test_no_$1_t self:$2 create_socket_perms;
> +')
> +
> +# Test use of icmp_socket class for ICMP datagram sockets instead of rawip_socket.
> +extended_socket_class_test(icmp_socket, rawip_socket)
> +
> +# Test use of sctp_socket class for SCTP sockets instead of rawip_socket.
> +extended_socket_class_test(sctp_socket, rawip_socket)
> +
> +# Test use of bluetooth_socket for Bluetooth sockets instead of socket.
> +extended_socket_class_test(bluetooth_socket, socket)
> +
> +# Test use of alg_socket for Alg (Crypto API) sockets instead of socket.
> +extended_socket_class_test(alg_socket, socket)
> +
> +#
> +# Common rules for all extended_socket_class test domains.
> +#
> +
> +# Trigger kernel module auto-loading of the network protocol implementations.
> +kernel_request_load_module(extsocktestdomain)
> +
> +# Entry into the test domains via the test program.
> +miscfiles_domain_entry_test_files(extsocktestdomain)
> +userdom_sysadm_entry_spec_domtrans_to(extsocktestdomain)
> diff --git a/tests/Makefile b/tests/Makefile
> index 57a5d12..228b764 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -16,6 +16,10 @@ ifeq ($(shell grep -q cap_userns $(POLDEV)/include/support/all_perms.spt && echo
>  SUBDIRS += cap_userns
>  endif
>  
> +ifeq ($(shell grep -q icmp_socket $(POLDEV)/include/support/all_perms.spt && grep -q 1 /sys/fs/selinux/policy_capabilities/extended_socket_class && echo true),true)
> +SUBDIRS += extended_socket_class
> +endif
> +
>  ifeq ($(DISTRO),RHEL4)
>      SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp overlay unix_socket, $(SUBDIRS))
>  endif
> diff --git a/tests/extended_socket_class/Makefile b/tests/extended_socket_class/Makefile
> new file mode 100644
> index 0000000..8dce555
> --- /dev/null
> +++ b/tests/extended_socket_class/Makefile
> @@ -0,0 +1,4 @@
> +TARGETS=$(patsubst %.c,%,$(wildcard *.c))
> +all: $(TARGETS)
> +clean:
> +	rm -f $(TARGETS)
> diff --git a/tests/extended_socket_class/sockcreate.c b/tests/extended_socket_class/sockcreate.c
> new file mode 100644
> index 0000000..c5802ae
> --- /dev/null
> +++ b/tests/extended_socket_class/sockcreate.c
> @@ -0,0 +1,89 @@
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +struct nameval {
> +	const char *name;
> +	const int value;
> +};
> +
> +static struct nameval domains[] = {
> +	{ "inet", AF_INET },
> +	{ "inet6", AF_INET6 },
> +	{ "bluetooth", AF_BLUETOOTH },
> +	{ "alg", AF_ALG },
> +	{ NULL, 0 }
> +};
> +
> +static struct nameval types[] = {
> +	{ "stream", SOCK_STREAM },
> +	{ "dgram", SOCK_DGRAM },
> +	{ "seqpacket", SOCK_SEQPACKET },
> +	{ "raw", SOCK_RAW },
> +	{ NULL, 0 }
> +};
> +
> +static struct nameval protocols[] = {
> +	{ "icmp", IPPROTO_ICMP },
> +	{ "icmpv6", IPPROTO_ICMPV6 },
> +	{ "sctp", IPPROTO_SCTP },
> +	{ "default", 0 },
> +	{ NULL, 0 }
> +};
> +
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> +
> +static int lookup_value(const char *name, const struct nameval *nvlist)
> +{
> +	const struct nameval *nv;
> +
> +	for (nv = nvlist; nv->name; nv++) {
> +		if (!strcmp(nv->name, name))
> +			return nv->value;
> +	}
> +	return -1;
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int sock;
> +	int domain, type, protocol;
> +
> +	if (argc != 4) {
> +		fprintf(stderr, "usage: %s domain type protocol\n", argv[0]);
> +		exit(1);
> +	}
> +
> +	domain = lookup_value(argv[1], domains);
> +	if (domain < 0) {
> +		fprintf(stderr, "%s: unknown domain %s\n", argv[0], argv[1]);
> +		exit(1);
> +	}
> +
> +	type = lookup_value(argv[2], types);
> +	if (type < 0) {
> +		fprintf(stderr, "%s: unknown type %s\n", argv[0], argv[2]);
> +		exit(1);
> +	}
> +
> +	protocol = lookup_value(argv[3], protocols);
> +	if (protocol < 0) {
> +		fprintf(stderr, "%s: unknown protocol %s\n", argv[0], argv[3]);
> +		exit(1);
> +	}
> +
> +	sock = socket(domain, type, protocol);
> +	if (sock < 0) {
> +		fprintf(stderr, "%s: socket(%s/%d, %s/%d, %s/%d): %s\n",
> +			argv[0], argv[1], domain, argv[2], type,
> +			argv[3], protocol, strerror(errno));
> +		exit(1);
> +	}
> +	close(sock);
> +	exit(0);
> +}
> diff --git a/tests/extended_socket_class/test b/tests/extended_socket_class/test
> new file mode 100755
> index 0000000..eb100ee
> --- /dev/null
> +++ b/tests/extended_socket_class/test
> @@ -0,0 +1,76 @@
> +#!/usr/bin/perl
> +
> +use Test;
> +BEGIN { plan tests => 16 };
> +
> +$basedir = $0;  $basedir =~ s|(.*)/[^/]*|$1|;
> +
> +# Enable gid 0 to create ICMP sockets for testing.
> +system("echo 0 0 > /proc/sys/net/ipv4/ping_group_range");
> +
> +# Verify that test_icmp_socket_t can create an ICMP socket.
> +$result = system("runcon -t test_icmp_socket_t -- $basedir/sockcreate inet dgram icmp 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_icmp_socket_t cannot create an ICMP socket.
> +$result = system("runcon -t test_no_icmp_socket_t -- $basedir/sockcreate inet dgram icmp 2>&1");
> +ok($result);
> +
> +# Verify that test_icmp_socket_t can create an ICMPv6 socket.
> +$result = system("runcon -t test_icmp_socket_t -- $basedir/sockcreate inet6 dgram icmpv6 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_icmp_socket_t cannot create an ICMPv6 socket.
> +$result = system("runcon -t test_no_icmp_socket_t -- $basedir/sockcreate inet6 dgram icmpv6 2>&1");
> +ok($result);
> +
> +# Restore to the kernel defaults - no one allowed to create ICMP sockets.
> +system("echo 1 0 > /proc/sys/net/ipv4/ping_group_range");
> +
> +# Verify that test_sctp_socket_t can create an IPv4 stream SCTP socket.
> +$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet stream sctp 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_sctp_socket_t cannot create an IPv4 stream SCTP socket.
> +$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet stream sctp 2>&1");
> +ok($result);
> +
> +# Verify that test_sctp_socket_t can create an IPv4 seqpacket SCTP socket.
> +$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet seqpacket sctp 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_sctp_socket_t cannot create an IPv4 seqpacket SCTP socket.
> +$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet seqpacket sctp 2>&1");
> +ok($result);
> +
> +# Verify that test_sctp_socket_t can create an IPv6 stream SCTP socket.
> +$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet6 stream sctp 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_sctp_socket_t cannot create an IPv6 stream SCTP socket.
> +$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet6 stream sctp 2>&1");
> +ok($result);
> +
> +# Verify that test_sctp_socket_t can create an IPv6 seqpacket SCTP socket.
> +$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet6 seqpacket sctp 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_sctp_socket_t cannot create an IPv6 seqpacket SCTP socket.
> +$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet6 seqpacket sctp 2>&1");
> +ok($result);
> +
> +# Verify that test_bluetooth_socket_t can create a Bluetooth socket.
> +$result = system("runcon -t test_bluetooth_socket_t -- $basedir/sockcreate bluetooth stream default 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_bluetooth_socket_t cannot create a Bluetooth socket.
> +$result = system("runcon -t test_no_bluetooth_socket_t -- $basedir/sockcreate bluetooth stream default 2>&1");
> +ok($result);
> +
> +# Verify that test_alg_socket_t can create a Crypto API socket.
> +$result = system("runcon -t test_alg_socket_t -- $basedir/sockcreate alg seqpacket default 2>&1");
> +ok($result, 0);
> +
> +# Verify that test_no_alg_socket_t cannot create a Crypto API socket.
> +$result = system("runcon -t test_no_alg_socket_t -- $basedir/sockcreate alg seqpacket default 2>&1");
> +ok($result);
>
Paul Moore Dec. 23, 2016, 10 p.m. UTC | #2
On Fri, Dec 9, 2016 at 11:14 AM, Stephen Smalley <sds@tycho.nsa.gov> wrote:
> On 12/09/2016 10:57 AM, Stephen Smalley wrote:
>> Add tests for the extended_socket_class policy capability.
>> This change includes the following tests:
>> - Test that ICMP datagram sockets are mapped to the new icmp_socket
>> class and not to rawip_socket for both IPv4 and IPv6.
>>
>> - Test that SCTP stream and seqpacket sockets are mapped to the
>> new sctp_socket class and not to rawip_socket for both IPv4 and IPv6.
>>
>> - Test that Bluetooth sockets are mapped to the new bluetooth_socket
>> class and not to socket.
>>
>> - Test that AF_ALG sockets are mapped to the new alg_socket class
>> and not to socket.
>>
>> The tests are only run if the extended_socket_class policy capability
>> is present and enabled in the kernel and the base policy, and only if
>> the new classes are defined in the base policy.  This avoids breaking
>> the testsuite on systems with older kernels, older policies, or
>> policies that do not enable the policy capability.
>
> BTW, while creating these tests, I was also trying to test AF_BRIDGE
> originally, but it doesn't appear that you can in fact create AF_BRIDGE
> sockets AFAICT; socket(AF_BRIDGE, <any>, <any>) seems to always return
> EAFNOSUPPORT even though CONFIG_BRIDGE_* is enabled in the kernel
> config.  So possibly we don't need a bridge_socket security class?  Not
> sure what other address families are similar?

It sounds like a good idea to try each of the address families we're
adding in your previous kernel patch; if the socket family isn't
readily accessible from userspace I'm not sure it makes sense to add
the object class at this point in time.
diff mbox

Patch

diff --git a/README b/README
index 8dbbbda..521e965 100644
--- a/README
+++ b/README
@@ -39,6 +39,14 @@  CONFIG_JFS_SECURITY=y
 CONFIG_XFS_SECURITY=y
 CONFIG_JFFS2_FS_SECURITY=y
 
+# Network protocol implementations.
+# These are enabled to test the extended socket classes in
+# tests/extended_socket_class; they are not required
+# for SELinux operation itself.
+CONFIG_IP_SCTP=m
+CONFIG_BT=m
+CONFIG_CRYPTO_USER_API=m
+
 Do not set CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX; it is an
 option for legacy distributions (Fedora 3 and 4).
 
diff --git a/policy/Makefile b/policy/Makefile
index 16ab3b9..992278b 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -30,6 +30,10 @@  ifeq ($(shell grep -q cap_userns $(POLDEV)/include/support/all_perms.spt && echo
 TARGETS += test_cap_userns.te
 endif
 
+ifeq ($(shell grep -q icmp_socket $(POLDEV)/include/support/all_perms.spt && echo true),true)
+TARGETS += test_extended_socket_class.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_extended_socket_class.te b/policy/test_extended_socket_class.te
new file mode 100644
index 0000000..4f30c9f
--- /dev/null
+++ b/policy/test_extended_socket_class.te
@@ -0,0 +1,64 @@ 
+########################################
+#
+# Policy for testing the new socket classes
+# introduced when the extended_socket_class
+# policy capability is enabled in policy and
+# supported by the kernel.
+
+attribute extsocktestdomain;
+
+#
+# extended_socket_test(newclass, oldclass)
+#
+# Generate a pair of test domains and rules to test
+# that when the extended_socket_class policy capability
+# is enabled, the kernel checks permission against the
+# 'newclass' security class rather than the 'oldclass'
+# security class.
+#
+define(`extended_socket_class_test', `
+# Domain that is allowed to create $1_socket.
+type test_$1_t;
+domain_type(test_$1_t)
+unconfined_runs_test(test_$1_t)
+typeattribute test_$1_t extsocktestdomain;
+typeattribute test_$1_t testdomain;
+
+# Allow $1 but not $2.
+# This is to ensure that the kernel is checking the right class.
+allow test_$1_t self:$1 create_socket_perms;
+
+# Domain that is not allowed to create $1.
+type test_no_$1_t;
+domain_type(test_no_$1_t)
+unconfined_runs_test(test_no_$1_t)
+typeattribute test_no_$1_t extsocktestdomain;
+typeattribute test_no_$1_t testdomain;
+
+# Allow $2 but not $1.
+# This is to ensure that the kernel is checking the right class.
+allow test_no_$1_t self:$2 create_socket_perms;
+')
+
+# Test use of icmp_socket class for ICMP datagram sockets instead of rawip_socket.
+extended_socket_class_test(icmp_socket, rawip_socket)
+
+# Test use of sctp_socket class for SCTP sockets instead of rawip_socket.
+extended_socket_class_test(sctp_socket, rawip_socket)
+
+# Test use of bluetooth_socket for Bluetooth sockets instead of socket.
+extended_socket_class_test(bluetooth_socket, socket)
+
+# Test use of alg_socket for Alg (Crypto API) sockets instead of socket.
+extended_socket_class_test(alg_socket, socket)
+
+#
+# Common rules for all extended_socket_class test domains.
+#
+
+# Trigger kernel module auto-loading of the network protocol implementations.
+kernel_request_load_module(extsocktestdomain)
+
+# Entry into the test domains via the test program.
+miscfiles_domain_entry_test_files(extsocktestdomain)
+userdom_sysadm_entry_spec_domtrans_to(extsocktestdomain)
diff --git a/tests/Makefile b/tests/Makefile
index 57a5d12..228b764 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -16,6 +16,10 @@  ifeq ($(shell grep -q cap_userns $(POLDEV)/include/support/all_perms.spt && echo
 SUBDIRS += cap_userns
 endif
 
+ifeq ($(shell grep -q icmp_socket $(POLDEV)/include/support/all_perms.spt && grep -q 1 /sys/fs/selinux/policy_capabilities/extended_socket_class && echo true),true)
+SUBDIRS += extended_socket_class
+endif
+
 ifeq ($(DISTRO),RHEL4)
     SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp overlay unix_socket, $(SUBDIRS))
 endif
diff --git a/tests/extended_socket_class/Makefile b/tests/extended_socket_class/Makefile
new file mode 100644
index 0000000..8dce555
--- /dev/null
+++ b/tests/extended_socket_class/Makefile
@@ -0,0 +1,4 @@ 
+TARGETS=$(patsubst %.c,%,$(wildcard *.c))
+all: $(TARGETS)
+clean:
+	rm -f $(TARGETS)
diff --git a/tests/extended_socket_class/sockcreate.c b/tests/extended_socket_class/sockcreate.c
new file mode 100644
index 0000000..c5802ae
--- /dev/null
+++ b/tests/extended_socket_class/sockcreate.c
@@ -0,0 +1,89 @@ 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+struct nameval {
+	const char *name;
+	const int value;
+};
+
+static struct nameval domains[] = {
+	{ "inet", AF_INET },
+	{ "inet6", AF_INET6 },
+	{ "bluetooth", AF_BLUETOOTH },
+	{ "alg", AF_ALG },
+	{ NULL, 0 }
+};
+
+static struct nameval types[] = {
+	{ "stream", SOCK_STREAM },
+	{ "dgram", SOCK_DGRAM },
+	{ "seqpacket", SOCK_SEQPACKET },
+	{ "raw", SOCK_RAW },
+	{ NULL, 0 }
+};
+
+static struct nameval protocols[] = {
+	{ "icmp", IPPROTO_ICMP },
+	{ "icmpv6", IPPROTO_ICMPV6 },
+	{ "sctp", IPPROTO_SCTP },
+	{ "default", 0 },
+	{ NULL, 0 }
+};
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static int lookup_value(const char *name, const struct nameval *nvlist)
+{
+	const struct nameval *nv;
+
+	for (nv = nvlist; nv->name; nv++) {
+		if (!strcmp(nv->name, name))
+			return nv->value;
+	}
+	return -1;
+}
+
+int main(int argc, char **argv)
+{
+	int sock;
+	int domain, type, protocol;
+
+	if (argc != 4) {
+		fprintf(stderr, "usage: %s domain type protocol\n", argv[0]);
+		exit(1);
+	}
+
+	domain = lookup_value(argv[1], domains);
+	if (domain < 0) {
+		fprintf(stderr, "%s: unknown domain %s\n", argv[0], argv[1]);
+		exit(1);
+	}
+
+	type = lookup_value(argv[2], types);
+	if (type < 0) {
+		fprintf(stderr, "%s: unknown type %s\n", argv[0], argv[2]);
+		exit(1);
+	}
+
+	protocol = lookup_value(argv[3], protocols);
+	if (protocol < 0) {
+		fprintf(stderr, "%s: unknown protocol %s\n", argv[0], argv[3]);
+		exit(1);
+	}
+
+	sock = socket(domain, type, protocol);
+	if (sock < 0) {
+		fprintf(stderr, "%s: socket(%s/%d, %s/%d, %s/%d): %s\n",
+			argv[0], argv[1], domain, argv[2], type,
+			argv[3], protocol, strerror(errno));
+		exit(1);
+	}
+	close(sock);
+	exit(0);
+}
diff --git a/tests/extended_socket_class/test b/tests/extended_socket_class/test
new file mode 100755
index 0000000..eb100ee
--- /dev/null
+++ b/tests/extended_socket_class/test
@@ -0,0 +1,76 @@ 
+#!/usr/bin/perl
+
+use Test;
+BEGIN { plan tests => 16 };
+
+$basedir = $0;  $basedir =~ s|(.*)/[^/]*|$1|;
+
+# Enable gid 0 to create ICMP sockets for testing.
+system("echo 0 0 > /proc/sys/net/ipv4/ping_group_range");
+
+# Verify that test_icmp_socket_t can create an ICMP socket.
+$result = system("runcon -t test_icmp_socket_t -- $basedir/sockcreate inet dgram icmp 2>&1");
+ok($result, 0);
+
+# Verify that test_no_icmp_socket_t cannot create an ICMP socket.
+$result = system("runcon -t test_no_icmp_socket_t -- $basedir/sockcreate inet dgram icmp 2>&1");
+ok($result);
+
+# Verify that test_icmp_socket_t can create an ICMPv6 socket.
+$result = system("runcon -t test_icmp_socket_t -- $basedir/sockcreate inet6 dgram icmpv6 2>&1");
+ok($result, 0);
+
+# Verify that test_no_icmp_socket_t cannot create an ICMPv6 socket.
+$result = system("runcon -t test_no_icmp_socket_t -- $basedir/sockcreate inet6 dgram icmpv6 2>&1");
+ok($result);
+
+# Restore to the kernel defaults - no one allowed to create ICMP sockets.
+system("echo 1 0 > /proc/sys/net/ipv4/ping_group_range");
+
+# Verify that test_sctp_socket_t can create an IPv4 stream SCTP socket.
+$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet stream sctp 2>&1");
+ok($result, 0);
+
+# Verify that test_no_sctp_socket_t cannot create an IPv4 stream SCTP socket.
+$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet stream sctp 2>&1");
+ok($result);
+
+# Verify that test_sctp_socket_t can create an IPv4 seqpacket SCTP socket.
+$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet seqpacket sctp 2>&1");
+ok($result, 0);
+
+# Verify that test_no_sctp_socket_t cannot create an IPv4 seqpacket SCTP socket.
+$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet seqpacket sctp 2>&1");
+ok($result);
+
+# Verify that test_sctp_socket_t can create an IPv6 stream SCTP socket.
+$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet6 stream sctp 2>&1");
+ok($result, 0);
+
+# Verify that test_no_sctp_socket_t cannot create an IPv6 stream SCTP socket.
+$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet6 stream sctp 2>&1");
+ok($result);
+
+# Verify that test_sctp_socket_t can create an IPv6 seqpacket SCTP socket.
+$result = system("runcon -t test_sctp_socket_t -- $basedir/sockcreate inet6 seqpacket sctp 2>&1");
+ok($result, 0);
+
+# Verify that test_no_sctp_socket_t cannot create an IPv6 seqpacket SCTP socket.
+$result = system("runcon -t test_no_sctp_socket_t -- $basedir/sockcreate inet6 seqpacket sctp 2>&1");
+ok($result);
+
+# Verify that test_bluetooth_socket_t can create a Bluetooth socket.
+$result = system("runcon -t test_bluetooth_socket_t -- $basedir/sockcreate bluetooth stream default 2>&1");
+ok($result, 0);
+
+# Verify that test_no_bluetooth_socket_t cannot create a Bluetooth socket.
+$result = system("runcon -t test_no_bluetooth_socket_t -- $basedir/sockcreate bluetooth stream default 2>&1");
+ok($result);
+
+# Verify that test_alg_socket_t can create a Crypto API socket.
+$result = system("runcon -t test_alg_socket_t -- $basedir/sockcreate alg seqpacket default 2>&1");
+ok($result, 0);
+
+# Verify that test_no_alg_socket_t cannot create a Crypto API socket.
+$result = system("runcon -t test_no_alg_socket_t -- $basedir/sockcreate alg seqpacket default 2>&1");
+ok($result);