On Tue, Jun 16, 2015 at 1:33 PM, Stephen Smalley <sds@xxxxxxxxxxxxx> wrote: > Add a set of tests for INET sockets that exercise the SO_PEERSEC > and SCM_SECURITY functionality and test the peer recv permission check. > Load a NetLabel configuration during testing to enable full SELinux > labeling over loopback. > > Other candidates for future tests: > - Test other INET socket permission checks, such as socket name_bind, > node_bind, name_connect, netif ingress/egress, and node recvfrom/sendto, > - Test SECMARK packet labeling and packet permission checks, > - Test labeled IPSEC peer labeling (over loopback possible?), > - Optionally test cross-machine peer labeling via NetLabel or labeled IPSEC > (requires more complex test setup). > > Presently this test does not work on F22 due to a problem with > netlabel_tools, but it did pass on F21 for me. > > Signed-off-by: Stephen Smalley <sds@xxxxxxxxxxxxx> > --- > > Changes from V1: > Fixed a comment. > Fixed a couple of compiler warnings. Acked-by: Paul Moore <paul@xxxxxxxxxxxxxx> > policy/Makefile | 2 +- > policy/test_inet_socket.te | 60 ++++++++++++++ > tests/Makefile | 2 +- > tests/inet_socket/Makefile | 7 ++ > tests/inet_socket/client.c | 102 +++++++++++++++++++++++ > tests/inet_socket/netlabel-flush | 8 ++ > tests/inet_socket/netlabel-load | 11 +++ > tests/inet_socket/server.c | 174 +++++++++++++++++++++++++++++++++++++++ > tests/inet_socket/test | 50 +++++++++++ > tests/unix_socket/server.c | 1 - > 10 files changed, 414 insertions(+), 3 deletions(-) > create mode 100644 policy/test_inet_socket.te > create mode 100644 tests/inet_socket/Makefile > create mode 100644 tests/inet_socket/client.c > create mode 100755 tests/inet_socket/netlabel-flush > create mode 100755 tests/inet_socket/netlabel-load > create mode 100644 tests/inet_socket/server.c > create mode 100755 tests/inet_socket/test > > diff --git a/policy/Makefile b/policy/Makefile > index 25777bb..742fd03 100644 > --- a/policy/Makefile > +++ b/policy/Makefile > @@ -19,7 +19,7 @@ TARGETS = \ > test_setnice.te test_sigkill.te test_stat.te test_sysctl.te \ > test_task_create.te test_task_getpgid.te test_task_getsched.te \ > test_task_getsid.te test_task_setpgid.te test_task_setsched.te \ > - test_transition.te test_unix_socket.te test_wait.te > + test_transition.te test_inet_socket.te test_unix_socket.te test_wait.te > > ifeq ($(shell [ $(POL_VERS) -ge 24 ] && echo true),true) > TARGETS += test_bounds.te > diff --git a/policy/test_inet_socket.te b/policy/test_inet_socket.te > new file mode 100644 > index 0000000..9e01a8c > --- /dev/null > +++ b/policy/test_inet_socket.te > @@ -0,0 +1,60 @@ > +################################# > +# > +# Policy for testing INET domain sockets. > +# > + > +attribute inetsocketdomain; > + > +# Do not break NFS when we load NetLabel configuration. > +gen_require(` > + type kernel_t; > +') > +corenet_all_recvfrom_unlabeled(kernel_t) > + > +# Domain for server process. > +type test_inet_server_t; > +domain_type(test_inet_server_t) > +unconfined_runs_test(test_inet_server_t) > +typeattribute test_inet_server_t testdomain; > +typeattribute test_inet_server_t inetsocketdomain; > +allow test_inet_server_t self:tcp_socket create_stream_socket_perms; > +allow test_inet_server_t self:udp_socket create_socket_perms; > +corenet_tcp_bind_generic_port(test_inet_server_t) > +corenet_udp_bind_generic_port(test_inet_server_t) > +corenet_tcp_bind_all_nodes(test_inet_server_t) > +corenet_udp_bind_all_nodes(test_inet_server_t) > +corenet_inout_generic_if(test_inet_server_t) > +corenet_inout_generic_node(test_inet_server_t) > + > +# Domain for client process. > +type test_inet_client_t; > +domain_type(test_inet_client_t) > +unconfined_runs_test(test_inet_client_t) > +typeattribute test_inet_client_t testdomain; > +typeattribute test_inet_client_t inetsocketdomain; > +allow test_inet_client_t self:tcp_socket create_stream_socket_perms; > +allow test_inet_client_t self:udp_socket create_socket_perms; > +corenet_tcp_connect_generic_port(test_inet_client_t) > +corenet_inout_generic_if(test_inet_client_t) > +corenet_inout_generic_node(test_inet_client_t) > + > +# The server can receive labeled packets from the client. > +allow test_inet_server_t test_inet_client_t:peer recv; > +# And vice versa. > +allow test_inet_client_t test_inet_server_t:peer recv; > + > +# Domain for a client process not authorized to communicate with the server. > +type test_inet_bad_client_t; > +domain_type(test_inet_bad_client_t) > +unconfined_runs_test(test_inet_bad_client_t) > +typeattribute test_inet_bad_client_t testdomain; > +typeattribute test_inet_bad_client_t inetsocketdomain; > +allow test_inet_bad_client_t self:tcp_socket create_stream_socket_perms; > +allow test_inet_bad_client_t self:udp_socket create_socket_perms; > +corenet_tcp_connect_generic_port(test_inet_bad_client_t) > +corenet_inout_generic_if(test_inet_bad_client_t) > +corenet_inout_generic_node(test_inet_bad_client_t) > + > +# Allow all of these domains to be entered from the sysadm domain. > +miscfiles_domain_entry_test_files(inetsocketdomain) > +userdom_sysadm_entry_spec_domtrans_to(inetsocketdomain) > diff --git a/tests/Makefile b/tests/Makefile > index 5e45bf9..507123b 100644 > --- a/tests/Makefile > +++ b/tests/Makefile > @@ -5,7 +5,7 @@ DISTRO=$(shell ./os_detect) > > SUBDIRS_COMMON:=domain_trans entrypoint execshare exectrace execute_no_trans fdreceive inherit link mkdir msg open ptrace readlink relabel rename rxdir sem setattr setnice shm sigkill stat sysctl task_create task_setnice task_setscheduler task_getscheduler task_getsid task_getpgid task_setpgid wait file ioctl capable_file capable_net capable_sys unix_socket > > -SUBDIRS:= $(SUBDIRS_COMMON) dyntrans dyntrace bounds nnp > +SUBDIRS:= $(SUBDIRS_COMMON) dyntrans dyntrace bounds nnp inet_socket > > ifeq ($(DISTRO),RHEL4) > SUBDIRS:=$(SUBDIRS_COMMON) > diff --git a/tests/inet_socket/Makefile b/tests/inet_socket/Makefile > new file mode 100644 > index 0000000..5266096 > --- /dev/null > +++ b/tests/inet_socket/Makefile > @@ -0,0 +1,7 @@ > +TARGETS=client server > + > +LDLIBS+= -lselinux > + > +all: $(TARGETS) > +clean: > + rm -f $(TARGETS) > diff --git a/tests/inet_socket/client.c b/tests/inet_socket/client.c > new file mode 100644 > index 0000000..ffc154c > --- /dev/null > +++ b/tests/inet_socket/client.c > @@ -0,0 +1,102 @@ > +#include <sys/types.h> > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <arpa/inet.h> > +#include <sys/un.h> > +#include <stdlib.h> > +#include <stdio.h> > +#include <stddef.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <selinux/selinux.h> > + > +void usage(char *progname) > +{ > + fprintf(stderr, > + "usage: %s [stream|dgram] port\n", > + progname); > + exit(1); > +} > + > +int > +main(int argc, char **argv) > +{ > + char byte, label[256]; > + int sock; > + int result; > + struct sockaddr_in sin; > + socklen_t sinlen; > + int type; > + char *mycon; > + unsigned short port; > + > + if (argc != 3) > + usage(argv[0]); > + > + if (!strcmp(argv[1], "stream")) > + type = SOCK_STREAM; > + else if (!strcmp(argv[1], "dgram")) > + type = SOCK_DGRAM; > + else > + usage(argv[0]); > + > + port = atoi(argv[2]); > + if (!port) > + usage(argv[0]); > + > + sock = socket(AF_INET, type, 0); > + if (sock < 0) { > + perror("socket"); > + exit(1); > + } > + > + bzero(&sin, sizeof(struct sockaddr_in)); > + sin.sin_family = AF_INET; > + sin.sin_port = htons(port); > + if (inet_aton("127.0.0.1", &sin.sin_addr) == 0) { > + fprintf(stderr, "%s: inet_ntoa: invalid address\n", argv[0]); > + close(sock); > + exit(1); > + } > + > + sinlen = sizeof(sin); > + result = connect(sock, (struct sockaddr *) &sin, sinlen); > + if (result < 0) { > + perror("connect"); > + close(sock); > + exit(1); > + } > + > + byte = 0; > + result = write(sock, &byte, 1); > + if (result < 0) { > + perror("write"); > + close(sock); > + exit(1); > + } > + result = read(sock, label, sizeof(label)); > + if (result < 0) { > + perror("read"); > + close(sock); > + exit(1); > + } > + label[result] = 0; > + > + result = getcon(&mycon); > + if (result < 0) { > + perror("getcon"); > + close(sock); > + exit(1); > + } > + > + if (strcmp(mycon, label)) { > + fprintf(stderr, "%s: expected %s, got %s\n", > + argv[0], mycon, label); > + exit(1); > + } > + > + close(sock); > + exit(0); > +} > diff --git a/tests/inet_socket/netlabel-flush b/tests/inet_socket/netlabel-flush > new file mode 100755 > index 0000000..32a5b73 > --- /dev/null > +++ b/tests/inet_socket/netlabel-flush > @@ -0,0 +1,8 @@ > +#!/bin/sh > +# Reset NetLabel configuration to unlabeled for all. > +netlabelctl map del default > +netlabelctl cipsov4 del doi:1 > +netlabelctl map add default protocol:unlbl > +# Display the configuration. > +netlabelctl -p map list > +netlabelctl -p cipsov4 list > diff --git a/tests/inet_socket/netlabel-load b/tests/inet_socket/netlabel-load > new file mode 100755 > index 0000000..8110d18 > --- /dev/null > +++ b/tests/inet_socket/netlabel-load > @@ -0,0 +1,11 @@ > +#!/bin/sh > +# Define a localhost/loopback doi and apply it to the loopback address > +# so that we get full SELinux labels over loopback connections. > +netlabelctl cipsov4 add local doi:1 > +netlabelctl map del default > +netlabelctl map add default address:0.0.0.0/0 protocol:unlbl > +netlabelctl map add default address:::/0 protocol:unlbl > +netlabelctl map add default address:127.0.0.1 protocol:cipsov4,1 > +# Display the configuration. > +netlabelctl -p cipsov4 list > +netlabelctl -p map list > diff --git a/tests/inet_socket/server.c b/tests/inet_socket/server.c > new file mode 100644 > index 0000000..f7f4fdd > --- /dev/null > +++ b/tests/inet_socket/server.c > @@ -0,0 +1,174 @@ > +#include <sys/types.h> > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <stdlib.h> > +#include <stddef.h> > +#include <string.h> > +#include <unistd.h> > +#include <stdio.h> > + > +#ifndef SO_PEERSEC > +#define SO_PEERSEC 31 > +#endif > + > +#ifndef SCM_SECURITY > +#define SCM_SECURITY 0x03 > +#endif > + > +void usage(char *progname) > +{ > + fprintf(stderr, "usage: %s [stream|dgram] port\n", progname); > + exit(1); > +} > + > +static const int on = 1; > + > +int > +main(int argc, char **argv) > +{ > + int sock; > + int result; > + struct sockaddr_in sin; > + socklen_t sinlen; > + int type; > + char byte; > + unsigned short port; > + > + if (argc != 3) > + usage(argv[0]); > + > + if (!strcmp(argv[1], "stream")) > + type = SOCK_STREAM; > + else if (!strcmp(argv[1], "dgram")) > + type = SOCK_DGRAM; > + else > + usage(argv[0]); > + > + port = atoi(argv[2]); > + if (!port) > + usage(argv[0]); > + > + sock = socket(AF_INET, type, 0); > + if (sock < 0) { > + perror("socket"); > + exit(1); > + } > + > + result = setsockopt(sock, SOL_IP, IP_PASSSEC, &on, sizeof(on)); > + if (result < 0) { > + perror("setsockopt: SO_PASSSEC"); > + close(sock); > + exit(1); > + } > + > + result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); > + if (result < 0) { > + perror("setsockopt: SO_PASSSEC"); > + close(sock); > + exit(1); > + } > + > + bzero(&sin, sizeof(struct sockaddr_in)); > + sin.sin_family = AF_INET; > + sin.sin_port = htons(port); > + sin.sin_addr.s_addr = INADDR_ANY; > + sinlen = sizeof(sin); > + if (bind(sock, (struct sockaddr *) &sin, sinlen) < 0) { > + perror("bind"); > + close(sock); > + exit(1); > + } > + > + if (type == SOCK_STREAM) { > + int newsock; > + char peerlabel[256]; > + socklen_t labellen = sizeof(peerlabel); > + > + if (listen(sock, SOMAXCONN)) { > + perror("listen"); > + close(sock); > + exit(1); > + } > + > + sinlen = sizeof(sin); > + newsock = accept(sock, (struct sockaddr *)&sin, > + &sinlen); > + if (newsock < 0) { > + perror("accept"); > + close(sock); > + exit(1); > + } > + > + peerlabel[0] = 0; > + result = getsockopt(newsock, SOL_SOCKET, SO_PEERSEC, peerlabel, > + &labellen); > + if (result < 0) { > + perror("getsockopt: SO_PEERSEC"); > + exit(1); > + } > + printf("%s: Got peer label=%s\n", argv[0], peerlabel); > + > + result = read(newsock, &byte, 1); > + if (result < 0) { > + perror("read"); > + exit(1); > + } > + > + result = write(newsock, peerlabel, strlen(peerlabel)); > + if (result < 0) { > + perror("write"); > + exit(1); > + } > + close(newsock); > + } else { > + struct iovec iov; > + struct msghdr msg; > + struct cmsghdr *cmsg; > + char msglabel[256]; > + union { > + struct cmsghdr cmsghdr; > + char buf[CMSG_SPACE(sizeof(msglabel))]; > + } control; > + > + memset(&iov, 0, sizeof(iov)); > + iov.iov_base = &byte; > + iov.iov_len = 1; > + memset(&msg, 0, sizeof(msg)); > + msglabel[0] = 0; > + msg.msg_name = &sin; > + msg.msg_namelen = sizeof(sin); > + msg.msg_iov = &iov; > + msg.msg_iovlen = 1; > + msg.msg_control = &control; > + msg.msg_controllen = sizeof(control); > + result = recvmsg(sock, &msg, 0); > + if (result < 0) { > + perror("recvmsg"); > + exit(1); > + } > + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; > + cmsg = CMSG_NXTHDR(&msg, cmsg)) { > + if (cmsg->cmsg_level == SOL_IP && > + cmsg->cmsg_type == SCM_SECURITY) { > + size_t len = cmsg->cmsg_len - CMSG_LEN(0); > + > + if (len > 0 && len < sizeof(msglabel)) { > + memcpy(msglabel, CMSG_DATA(cmsg), len); > + msglabel[len] = 0; > + printf("%s: Got SCM_SECURITY=%s\n", > + argv[0], msglabel); > + } > + } > + } > + > + result = sendto(sock, msglabel, strlen(msglabel), 0, > + msg.msg_name, msg.msg_namelen); > + if (result < 0) { > + perror("sendto"); > + exit(1); > + } > + } > + > + close(sock); > + exit(0); > +} > diff --git a/tests/inet_socket/test b/tests/inet_socket/test > new file mode 100755 > index 0000000..58debd9 > --- /dev/null > +++ b/tests/inet_socket/test > @@ -0,0 +1,50 @@ > +#!/usr/bin/perl > + > +use Test; > +BEGIN { plan tests => 4} > + > +$basedir = $0; $basedir =~ s|(.*)/[^/]*|$1|; > + > +# Load NetLabel configuration. > +system "$basedir/netlabel-load"; > + > +# Start the stream server. > +if (($pid = fork()) == 0) { > + exec "runcon -t test_inet_server_t $basedir/server stream 65535"; > +} > + > +sleep 1; # Give it a moment to initialize. > + > +# Verify that authorized client can communicate with the server. > +$result = system "runcon -t test_inet_client_t $basedir/client stream 65535"; > +ok($result, 0); > + > +# Verify that unauthorized client cannot communicate with the server. > +$result = system "runcon -t test_inet_bad_client_t -- $basedir/client stream 65535 2>&1"; > +ok($result); > + > +# Kill the server. > +kill TERM, $pid; > + > +# Start the dgram server. > +if (($pid = fork()) == 0) { > + exec "runcon -t test_inet_server_t $basedir/server dgram 65535"; > +} > + > +sleep 1; # Give it a moment to initialize > + > +# Verify that authorized client can communicate with the server. > +$result = system "runcon -t test_inet_client_t $basedir/client dgram 65535"; > +ok($result, 0); > + > +# Verify that unauthorized client cannot communicate with the server. > +$result = system "runcon -t test_inet_bad_client_t -- $basedir/client dgram 65535 2>&1"; > +ok($result); > + > +# Kill the server. > +kill TERM, $pid; > + > +# Flush NetLabel configuration. > +system "$basedir/netlabel-flush"; > + > +exit; > diff --git a/tests/unix_socket/server.c b/tests/unix_socket/server.c > index 2532de7..89bfdb3 100644 > --- a/tests/unix_socket/server.c > +++ b/tests/unix_socket/server.c > @@ -1,4 +1,3 @@ > -#define _GNU_SOURCE > #include <sys/types.h> > #include <sys/socket.h> > #include <sys/un.h> > -- > 2.1.0 > -- paul moore www.paul-moore.com _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.