From ba0491d42dfbf9fd15df492513d6b140cdf1efee Mon Sep 17 00:00:00 2001 From: Richard Haines Date: Mon, 9 Oct 2017 15:17:40 +0100 Subject: [PATCH] lksctp-tools: Add SELinux support to sctp_test and sctp_darn Add getcon, getpeercon and fgetfilecon to display the process, peer and socket fd contexts if the -Z option is set. The changes are only for sctp_test.c and sctp_darn.c to allow testing of the SELinux kernel updates to support SCTP. Also added: 1) get_ip_options to sctp_test that is useful for testing fragmentation when CIPSO/CALIPSO ip options are added (as it can send many large data packets). 2) Netlabel with CIPSO/CALIPSO configured returns error EPROTONOSUPPORT when illegal address, therefore update test_1_to_1_connect test 5 to reflect this. Signed-off-by: Richard Haines --- configure.ac | 3 + src/apps/Makefile.am | 4 ++ src/apps/sctp_darn.c | 56 ++++++++++++++++++- src/apps/sctp_test.c | 105 ++++++++++++++++++++++++++++++++++- src/apps/selinux.h | 25 +++++++++ src/func_tests/test_1_to_1_connect.c | 3 +- 6 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 src/apps/selinux.h diff --git a/configure.ac b/configure.ac index 68fef7b..0429b90 100644 --- a/configure.ac +++ b/configure.ac @@ -38,6 +38,9 @@ AC_LIBTOOL_DLOPEN AC_PROG_LIBTOOL AC_SUBST(LIBTOOL_DEPS) +AC_CHECK_LIB([selinux], [getcon]) +AM_CONDITIONAL([USE_SELINUX], [test $ac_cv_lib_selinux_getcon = yes]) + dnl Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT diff --git a/src/apps/Makefile.am b/src/apps/Makefile.am index 7e33b9c..da0f889 100644 --- a/src/apps/Makefile.am +++ b/src/apps/Makefile.am @@ -10,6 +10,10 @@ AM_CPPFLAGS = -I. -I$(top_srcdir)/src/include -I$(top_srcdir)/src/testlib \ AM_LDFLAGS = +if USE_SELINUX +AM_CFLAGS = -DHAVE_SELINUX +endif + LDADD = $(top_builddir)/src/testlib/libsctputil.la \ $(top_builddir)/src/lib/libsctp.la diff --git a/src/apps/sctp_darn.c b/src/apps/sctp_darn.c index 90a5c42..d135c12 100644 --- a/src/apps/sctp_darn.c +++ b/src/apps/sctp_darn.c @@ -71,6 +71,8 @@ #include #include "sctp_darn.h" +#include "selinux.h" + char *TCID = __FILE__; int TST_TOTAL = 1; int TST_CNT = 0; @@ -204,6 +206,26 @@ static sctp_assoc_t test_verify_assoc_change(struct msghdr *); void print_addr_buf(void * laddrs, int n_laddrs); int print_sockaddr(struct sockaddr *sa_addr); +char *context; +const char *no_ctx = "unavailable"; +int print_ctx = 0; + +void print_context(int fd, char *text) +{ + if (is_selinux_enabled() <= 0 || print_ctx == 0) + return; + + if (getpeercon(fd, &context) < 0) + context = strdup(no_ctx); + printf("%s peer context=%s\n", text, context); + free(context); + + if (fgetfilecon(fd, &context) < 0) + context = strdup(no_ctx); + printf("%s fd context=%s\n", text, context); + free(context); +} + int main(int argc, char *argv[]) { int sk = -1; @@ -214,6 +236,14 @@ main(int argc, char *argv[]) { parse_arguments(argc, argv); + if (print_ctx) { + if (getcon(&context) < 0) + context = strdup(no_ctx); + + printf("Process security context=%s\n", context); + free(context); + } + switch(command) { case COMMAND_NONE: fprintf(stderr, "%s: Please specify a command.\n", @@ -295,7 +325,7 @@ parse_arguments(int argc, char *argv[]) { /* Parse the arguments. */ while (1) { - c = getopt_long (argc, argv, "B:H:IP:b:h:i:p:lm:nstz:ec:", + c = getopt_long (argc, argv, "B:H:IP:b:h:i:p:lm:nstz:ec:Z", long_options, &option_index); if (c == -1) break; @@ -439,6 +469,13 @@ parse_arguments(int argc, char *argv[]) { case 'e': echo = 1; break; + case 'Z': + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "SELinux is not enabled.\n"); + exit(2); + } + print_ctx = 1; + break; case '?': usage(argv[0]); exit(1); @@ -539,6 +576,7 @@ build_endpoint(char *argv0, int portnum) argv0, strerror(errno)); exit(1); } + print_context(retval, "create sock"); if (SOCK_SEQPACKET == socket_type) { memset(&subscribe, 0, sizeof(subscribe)); @@ -560,6 +598,7 @@ build_endpoint(char *argv0, int portnum) strerror(errno)); exit(1); } + print_context(retval, "bind"); /* Do we need to do bindx() to add any additional addresses? */ if (bindx_add_addrs) { @@ -626,6 +665,7 @@ command_listen(char *argv0, int sk) strerror(errno)); exit(1); } + print_context(sk, "listen"); if (nonblocking) { if (!interactive_mode) { @@ -700,6 +740,7 @@ command_listen(char *argv0, int sk) exit(1); } } + print_context(sk, "accept"); } else { recvsk = sk; @@ -722,6 +763,7 @@ command_listen(char *argv0, int sk) } break; } + print_context(recvsk, "recvmsg"); /* Update the associd when a notification is received on a * UDP-style socket. @@ -917,6 +959,7 @@ command_send(char *argv0, int *skp) } printf("New connection, peer addresses\n"); print_addr_buf(addrs, rc); + print_context(sk, "connect"); ra_family = addrs[0].sa_family; switch (ra_family) { case AF_INET: @@ -952,8 +995,10 @@ command_send(char *argv0, int *skp) outmsg.msg_namelen = ra_len; error = sendmsg(sk, &outmsg, 0); + print_context(sk, "sendmsg"); } else { error = send(sk, message, msglen, 0); + print_context(sk, "send"); if (error == -1 && errno == EPIPE) { error = close(sk); if (error != 0) { @@ -1003,6 +1048,7 @@ command_send(char *argv0, int *skp) } printf("New connection, peer addresses\n"); print_addr_buf(addrs, rc); + print_context(sk, "connect"); sctp_freepaddrs(addrs); new_connection = 0; } @@ -1103,6 +1149,7 @@ command_poll(char *argv0) } } printf("%s listening...\n", argv0); + print_context(poll_fds[i].fd, "listen_poll"); } else { for (i = 0; i < poll_skn; i++) { error = listen(poll_sks[i], 1); @@ -1117,6 +1164,7 @@ command_poll(char *argv0) } } printf("%s listening...\n", argv0); + print_context(poll_sks[i], "listen_select"); size = howmany(max_fd + 1, NFDBITS) * sizeof(fd_mask); if ((ibitsp = (fd_set *)malloc(size)) == NULL) { @@ -1217,6 +1265,7 @@ command_poll(char *argv0) fprintf(stderr, "sent a message, msglen = %d\n", msglen); + print_context(temp_fd, "sendmsg"); if (error != msglen) { fprintf(stderr, "%s: error: %s.\n", @@ -1259,6 +1308,7 @@ command_poll(char *argv0) } } test_print_message(temp_fd, &inmessage, error); + print_context(temp_fd, "recvmsg"); inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); iov.iov_len = REALLY_BIG; @@ -1360,7 +1410,8 @@ usage(char *argv0) " -t\t\t\tuse SOCK_STREAM tcp-style sockets.\n" " -z\t\t\tspecify the message size to be sent. The default\n" "\t\t\tmessage size generated would be 16K.\n" - " --interface=\"ifname\"\tselect interface for sin6_scope_id.\n", + " --interface=\"ifname\"\tselect interface for sin6_scope_id.\n" + " -Z\t\t\tdisplay process, peer and socket fd SELinux contexts.\n", argv0); } @@ -2324,6 +2375,7 @@ test_recv_assoc_change(int sk) exit(1); } + print_context(sk, "recvmsg"); return test_verify_assoc_change(&inmessage); } diff --git a/src/apps/sctp_test.c b/src/apps/sctp_test.c index 0961e0d..e66702c 100644 --- a/src/apps/sctp_test.c +++ b/src/apps/sctp_test.c @@ -60,6 +60,7 @@ #include +#include "selinux.h" #define REALLY_BIG 65536 @@ -172,6 +173,10 @@ struct sockaddr *connectx_addrs = NULL; int connectx_count = 0; int if_index = 0; +char *context; +const char *no_ctx = "unavailable"; +int print_selinux_info = 0; + unsigned char msg[] = "012345678901234567890123456789012345678901234567890"; static int msg_sizes[NCASES][MSG_CNT] = @@ -263,11 +268,70 @@ void usage(char *argv0) fprintf(stderr, "\t For example, '-C 10.0.0.1 -C 20.0.0.2'.\n"); fprintf(stderr, "\t This option is incompatible with the -h option.\n"); fprintf(stderr, "\t-O time to live (default value 0)\n"); + fprintf(stderr, "\t-Z display process, peer and socket fd contexts, also IP options when SELinux enabled.\n"); fprintf(stderr, "\n"); fflush(stderr); } /* usage() */ +void print_context(int fd, char *text) +{ + if (is_selinux_enabled() <= 0 || print_selinux_info == 0) + return; + + if (getpeercon(fd, &context) < 0) + context = strdup(no_ctx); + DEBUG_PRINT(DEBUG_MIN, "\t%s peer context=%s\n", text, context); + free(context); + + if (fgetfilecon(fd, &context) < 0) + context = strdup(no_ctx); + DEBUG_PRINT(DEBUG_MIN, "\t%s fd context=%s\n", text, context); + free(context); +} + +void print_ip_option(int fd, int family, char *text) +{ + int result, i; + unsigned char ip_options[1024]; + socklen_t len = sizeof(ip_options); + char *ip_optbuf; + + if (is_selinux_enabled() <= 0 || print_selinux_info == 0) + return; + + if (family == AF_INET) + result = getsockopt(fd, IPPROTO_IP, IP_OPTIONS, + ip_options, &len); + else + result = getsockopt(fd, IPPROTO_IPV6, IPV6_HOPOPTS, + ip_options, &len); + + if (result < 0) { + perror("get ip options error"); + exit(1); + } + + ip_optbuf = calloc(1, len * 2 + 1); + if (!ip_optbuf) { + perror("get ip options malloc error"); + exit(1); + } + + if (len > 0) { + for (i = 0; i < len; i++) + sprintf(&ip_optbuf[i * 2], "%02x", ip_options[i]); + + DEBUG_PRINT(DEBUG_MIN, + "\t%s IP Options - Family: %s Length: %d\n\tEntry: %s\n", + text, family == AF_INET ? "IPv4" : "IPv6", + len, ip_optbuf); + free(ip_optbuf); + } else { + DEBUG_PRINT(DEBUG_MIN, "\t%s No IP Options set\n", text); + } +} + void * build_msg(int len) { @@ -622,6 +686,7 @@ int socket_r(void) } } DEBUG_PRINT(DEBUG_MIN, " -> sk=%d\n", sk); + print_context(sk, "create sock"); memset(&subscribe, 0, sizeof(subscribe)); subscribe.sctp_data_io_event = 1; @@ -697,6 +762,7 @@ int bind_r(int sk, struct sockaddr_storage *saddr) } } while (error < 0 && i < MAX_BIND_RETRYS); + print_context(sk, "bind"); return 0; } /* bind_r() */ @@ -759,7 +825,9 @@ int listen_r(int sk, int listen_count) } else return -1; } - return 0; + + print_context(sk, "listen"); + return 0; } /* listen_r() */ @@ -775,6 +843,8 @@ int accept_r(int sk){ exit(1); } + print_context(sk, "accept"); + print_ip_option(sk, s_loc.ss_family, "accept"); return subsk; } /* accept_r() */ @@ -794,6 +864,9 @@ int connect_r(int sk, const struct sockaddr *serv_addr, socklen_t addrlen) } else return -1; } + + print_context(sk, "connect"); + print_ip_option(sk, s_loc.ss_family, "connect"); return 0; } /* connect_r() */ @@ -834,6 +907,9 @@ int connectx_r(int sk, struct sockaddr *addrs, int count) exit(1); } + print_context(sk, "connectx"); + print_ip_option(sk, s_loc.ss_family, "connectx"); + return 0; } /* connectx_r() */ @@ -897,6 +973,9 @@ int receive_r(int sk, int once) if (print_message(recvsk, &inmessage, error) > 0) continue; /* got a notification... */ + print_context(recvsk, "recvmsg"); + print_ip_option(sk, s_loc.ss_family, "recvmsg"); + inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); iov.iov_len = REALLY_BIG; @@ -1080,6 +1159,9 @@ int send_r(int sk, int stream, int order, int send_size, int assoc_i) } } + print_context(sk, "sendmsg"); + print_ip_option(sk, s_loc.ss_family, "sendmsg"); + if (send_size > 0) free(message); return 0; error_out: @@ -1283,6 +1365,9 @@ mixed_mode_test(void) print_message(poll_sks[i].sk, &inmessage, error); + print_context(poll_sks[i].sk, "recvmsg"); + print_ip_option(poll_sks[i].sk, s_loc.ss_family, + "recvmsg"); inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); @@ -1388,6 +1473,15 @@ void start_test(int role) DEBUG_PRINT(DEBUG_NONE, "\nStarting tests...\n"); + if (print_selinux_info) { + if (getcon(&context) < 0) + context = strdup(no_ctx); + + DEBUG_PRINT(DEBUG_MIN, "\tProcess security context=%s\n", + context); + free(context); + } + repeat_count = repeat; @@ -1449,7 +1543,7 @@ main(int argc, char *argv[]) struct sockaddr *tmp_addrs = NULL; /* Parse the arguments. */ - while ((c = getopt(argc, argv, ":H:L:P:S:a:h:p:c:d:lm:sx:X:o:t:M:r:w:Di:TB:C:O:")) >= 0 ) { + while ((c = getopt(argc, argv, ":H:L:P:S:a:h:p:c:d:lm:sx:X:o:t:M:r:w:Di:TB:C:O:Z")) >= 0) { switch (c) { case 'H': @@ -1616,6 +1710,13 @@ main(int argc, char *argv[]) } connectx_addrs = tmp_addrs; break; + case 'Z': + if (is_selinux_enabled() <= 0) { + fprintf(stderr, "SELinux is not enabled.\n"); + exit(1); + } + print_selinux_info = 1; + break; case '?': default: usage(argv[0]); diff --git a/src/apps/selinux.h b/src/apps/selinux.h new file mode 100644 index 0000000..85c7746 --- /dev/null +++ b/src/apps/selinux.h @@ -0,0 +1,25 @@ +#if HAVE_SELINUX +#include +#else +/* Stubs for SELinux functions */ +static int is_selinux_enabled(void) +{ + return -1; +} +static int getcon(char **context) +{ + *context = NULL; + return -1; +} +static int getpeercon(int fd, char **context) +{ + *context = NULL; + return -1; +} + +static int fgetfilecon(int fd, char **context) +{ + *context = NULL; + return -1; +} +#endif diff --git a/src/func_tests/test_1_to_1_connect.c b/src/func_tests/test_1_to_1_connect.c index 3ebc599..aa7f9a4 100644 --- a/src/func_tests/test_1_to_1_connect.c +++ b/src/func_tests/test_1_to_1_connect.c @@ -157,7 +157,8 @@ main(int argc, char *argv[]) /*connect () TEST5: Invalid address family, EINVAL Expect error*/ conn_addr.sin_family = 9090; /*Assigning invalid address family*/ error = connect(sk, (const struct sockaddr *) &conn_addr, len); - if (error != -1 || errno != EINVAL) + if ((error != -1 || errno != EINVAL) && + (error != -1 || errno != EPROTONOSUPPORT)) tst_brkm(TBROK, tst_exit, "connect with invalid address family " "error:%d, errno:%d", error, errno); -- 2.13.6