Re: [PATCH v2] selinux-testsuite: add inet_socket tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.



[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux