[apparmor] [PATCH 2/2] tests: Create socketpair test for checking labeling on fds
Tyler Hicks
tyhicks at canonical.com
Wed May 7 00:20:32 UTC 2014
Bug: https://bugs.launchpad.net/bugs/1235478
This is a test to check the label on file descriptors returned from
socketpair().
In its simple form, it simply calls socketpair() and checks the
labels on both fds.
In its complex form, it has the ability to do the simple test, then set
up an exec transition using aa_change_onexec(), and re-exec itself to
check the labeling after the file descriptors have been passed across an
exec transition.
The complex form is meant to test revalidation at exec. AppArmor
currently keeps the original labeling in place across the exec
transition.
Note that this test does not currently test read/write access to the
file descriptors. It only checks the label, as returned by
aa_getpeercon(2).
Signed-off-by: Tyler Hicks <tyhicks at canonical.com>
---
tests/regression/apparmor/Makefile | 2 +
tests/regression/apparmor/socketpair.c | 192 ++++++++++++++++++++++++++++++++
tests/regression/apparmor/socketpair.sh | 90 +++++++++++++++
3 files changed, 284 insertions(+)
create mode 100644 tests/regression/apparmor/socketpair.c
create mode 100755 tests/regression/apparmor/socketpair.sh
diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
index 08afdf6..55f59a3 100644
--- a/tests/regression/apparmor/Makefile
+++ b/tests/regression/apparmor/Makefile
@@ -97,6 +97,7 @@ SRC=access.c \
rename.c \
readdir.c \
rw.c \
+ socketpair.c \
symlink.c \
syscall_mknod.c \
swap.c \
@@ -174,6 +175,7 @@ TESTS=access \
rename \
readdir \
rw \
+ socketpair \
swap \
sd_flags \
setattr \
diff --git a/tests/regression/apparmor/socketpair.c b/tests/regression/apparmor/socketpair.c
new file mode 100644
index 0000000..9a64ba7
--- /dev/null
+++ b/tests/regression/apparmor/socketpair.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Canonical Ltd.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/apparmor.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define NO_MODE "(null)"
+
+#define ENV_FD0 "_SOCKETPAIR_FD0"
+#define ENV_FD1 "_SOCKETPAIR_FD1"
+
+static int get_socketpair(int pair[2])
+{
+ char *fd0, *fd1;
+
+ fd0 = getenv(ENV_FD0);
+ fd1 = getenv(ENV_FD1);
+
+ if (fd0 && fd1) {
+ pair[0] = atoi(fd0);
+ pair[1] = atoi(fd1);
+ } else {
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
+ perror("FAIL - socketpair");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int verify_confinement_context(int fd, const char *fd_name,
+ const char *expected_con,
+ const char *expected_mode)
+{
+ char *con, *mode;
+ int rc;
+
+ rc = aa_getpeercon(fd, &con, &mode);
+ if (rc < 0) {
+ fprintf(stderr, "FAIL - %s: aa_getpeercon(%d, , ): %m",
+ fd_name, fd);
+ return 1;
+ }
+
+ if (!mode)
+ mode = NO_MODE;
+
+ if (strcmp(con, expected_con)) {
+ fprintf(stderr,
+ "FAIL - %s: con \"%s\" != expected_con \"%s\"\n",
+ fd_name, con, expected_con);
+ rc = 2;
+ goto out;
+ }
+
+ if (strcmp(mode, expected_mode)) {
+ fprintf(stderr,
+ "FAIL - %s: mode \"%s\" != expected_mode \"%s\"\n",
+ fd_name, mode, expected_mode);
+ rc = 3;
+ goto out;
+ }
+
+ rc = 0;
+out:
+ free(con);
+ return rc;
+}
+
+static int reexec(int pair[2], int argc, char **argv)
+{
+ char *new_profile;
+ char fd_str[32];
+
+ /* Less than 4 arguments means that no <CHANGE_ONEXEC> args exist */
+ if (argc < 4)
+ return 0;
+
+ /**
+ * Save off the first <CHANGE_ONEXEC> arg and then shift all preceeding
+ * args by one to effectively pop off the first <CHANGE_ONEXEC>
+ */
+ new_profile = argv[3];
+ argv[3] = argv[2];
+ argv[2] = argv[1];
+ argv[1] = argv[0];
+ argv++;
+
+ if (aa_change_onexec(new_profile) < 0) {
+ perror("FAIL - aa_change_onexec");
+ return 1;
+ }
+
+ snprintf(fd_str, sizeof(fd_str), "%d", pair[0]);
+ if (setenv(ENV_FD0, fd_str, 1) < 0) {
+ perror("FAIL - setenv");
+ return 2;
+ }
+
+ snprintf(fd_str, sizeof(fd_str), "%d", pair[1]);
+ if (setenv(ENV_FD1, fd_str, 1) < 0) {
+ perror("FAIL - setenv");
+ return 3;
+ }
+
+ execv(argv[0], argv);
+
+ perror("FAIL - execv");
+ return 4;
+}
+
+int main(int argc, char **argv)
+{
+ char *expected_con, *expected_mode;
+ int pair[2], rc;
+
+ if (argc < 3) {
+ fprintf(stderr,
+ "FAIL - usage: %s <CON> <MODE> [<CHANGE_ONEXEC> ...]\n\n"
+ " <CON>\t\tThe expected confinement context\n"
+ " <MODE>\tThe expected confinement mode\n"
+ " <CHANGE_ONEXEC>\tThe profile to change to on exec\n\n"
+ "This program gets a socket pair and then verifies \n"
+ "the confinement context and mode of each file \n"
+ "descriptor. If there is no expected mode string, \n"
+ "<MODE> should be \"%s\".\n\n"
+ "Multiple <CHANGE_ONEXEC> profiles can be specified \n"
+ "and the test will run normally for the first pair, \n"
+ "then call aa_change_onexec() to rexec itself under \n"
+ "the next <CHANGE_ONEXEC> and verify the passed in \n"
+ "socket pairs still have the correct labeling.\n" ,
+ argv[0], NO_MODE);
+ exit(1);
+ }
+
+ /**
+ * If ENV_FD0 and ENV_FD1 are set, they'll point to fds that were
+ * passed in. If they're not set, call socketpair().
+ */
+ if (get_socketpair(pair))
+ exit(2);
+
+ expected_con = argv[1];
+ expected_mode = argv[2];
+
+ if (verify_confinement_context(pair[0], "pair[0]",
+ expected_con, expected_mode)) {
+ rc = 3;
+ goto out;
+ }
+
+ if (verify_confinement_context(pair[1], "pair[1]",
+ expected_con, expected_mode)) {
+ rc = 4;
+ goto out;
+ }
+
+ if (reexec(pair, argc, argv)) {
+ rc = 5;
+ goto out;
+ }
+
+ printf("PASS\n");
+ rc = 0;
+out:
+ close(pair[0]);
+ close(pair[1]);
+ exit(rc);
+}
+
diff --git a/tests/regression/apparmor/socketpair.sh b/tests/regression/apparmor/socketpair.sh
new file mode 100755
index 0000000..9e6a145
--- /dev/null
+++ b/tests/regression/apparmor/socketpair.sh
@@ -0,0 +1,90 @@
+#! /bin/bash
+# Copyright (C) 2014 Canonical, Ltd.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 2 of the
+# License.
+
+#=NAME socketpair
+#=DESCRIPTION
+# This test verifies that the fds returned from the socketpair syscall are
+# correctly labeled
+#=END
+
+pwd=`dirname $0`
+pwd=`cd $pwd ; /bin/pwd`
+
+bin=$pwd
+
+. $bin/prologue.inc
+
+do_test()
+{
+ local desc="SOCKETPAIR ($1)"
+ shift
+
+ runchecktest "$desc" "$@"
+}
+
+exec="/proc/*/attr/exec:w"
+np1="new_profile_1"
+np2="new_profile_2"
+
+# Ensure everything works as expected when unconfined
+do_test "unconfined" pass "unconfined" "(null)"
+
+# Test the test
+do_test "unconfined bad con" fail "uncon" "(null)"
+do_test "unconfined bad mode" fail "unconfined" "(null)XXX"
+
+# Ensure correct labeling under confinement
+genprofile
+do_test "confined" pass "$test" "enforce"
+
+# Test the test
+do_test "confined bad con" fail "/bad${test}" "enforce"
+do_test "confined bad mode" fail "$test" "inforce"
+
+# Ensure correct mode when using the complain flag
+genprofile flag:complain
+do_test "complain" pass "$test" "complain"
+
+# Test the test
+genprofile flag:complain
+do_test "complain bad mode" fail "$test" "enforce"
+
+# Ensure correct mode when using the audit flag
+genprofile flag:audit
+do_test "complain" pass "$test" "enforce"
+
+# Ensure correct labeling after passing fd pair across exec
+genprofile $exec 'change_profile->':$np1 -- image=$np1 addimage:$test
+do_test "confined exec transition" pass "$test" "enforce" "$np1"
+
+# Ensure correct labeling after passing fd pair across a no-transition exec
+# NOTE: The test still calls aa_change_onexec(), so change_profile -> $test
+# is still needed
+genprofile $exec 'change_profile->':$test
+do_test "confined exec no transition" pass "$test" "enforce" "$test"
+
+# Ensure correct complain mode after passing fd pair across exec
+genprofile flag:complain $exec 'change_profile->':$np1 -- \
+ image=$np1 addimage:$test
+do_test "confined exec transition from complain" pass "$test" "complain" "$np1"
+
+# Ensure correct enforce mode after passing fd pair across exec
+genprofile $exec 'change_profile->':$np1 -- \
+ image=$np1 addimage:$test flag:complain
+do_test "confined exec transition to complain" pass "$test" "enforce" "$np1"
+
+# Ensure correct labeling after passing fd pair across 2 execs
+gp_args="$exec change_profile->:$np1 -- \
+ image=$np1 addimage:$test $exec change_profile->:$np2 -- \
+ image=$np2 addimage:$test"
+genprofile $gp_args
+do_test "confined 2 exec transitions" pass "$test" "enforce" "$np1" "$np2"
+
+# Test the test
+do_test "confined 2 exec transitions bad con" fail "$test" "enforce" "$np1" "$np1"
+do_test "confined 2 exec transitions bad mode" fail "$test" "complain" "$np1" "$np2"
--
1.9.1
More information about the AppArmor
mailing list