[apparmor] [PATCH] tests: Add pivot_root tests
Tyler Hicks
tyhicks at canonical.com
Mon Apr 14 22:17:14 UTC 2014
On 2014-04-14 17:06:00, Tyler Hicks wrote:
> This test attempts to clone itself in a new mount namespace, pivot root
> into a new filesystem (ext2 disk image mounted over loopback), and then
> verify that a profile transition, if one was specified in the pivot_root
> rule, has properly occurred.
>
> Signed-off-by: Tyler Hicks <tyhicks at canonical.com>
> ---
>
> Note that this patch already contains the changes to include trailing '/'
> characters, as John alluded to in
> https://bugs.launchpad.net/ubuntu/+source/apparmor/+bug/1305244/comments/3
>
> tests/regression/apparmor/Makefile | 2 +
> tests/regression/apparmor/mkprofile.pl | 16 ++++
> tests/regression/apparmor/pivot_root.c | 124 ++++++++++++++++++++++++
> tests/regression/apparmor/pivot_root.sh | 164 ++++++++++++++++++++++++++++++++
> 4 files changed, 306 insertions(+)
> create mode 100644 tests/regression/apparmor/pivot_root.c
> create mode 100755 tests/regression/apparmor/pivot_root.sh
>
> diff --git a/tests/regression/apparmor/Makefile b/tests/regression/apparmor/Makefile
> index 3ab22d1..948ad85 100644
> --- a/tests/regression/apparmor/Makefile
> +++ b/tests/regression/apparmor/Makefile
> @@ -92,6 +92,7 @@ SRC=access.c \
> open.c \
> openat.c \
> pipe.c \
> + pivot_root.c \
> ptrace.c \
> ptrace_helper.c \
> pwrite.c \
> @@ -160,6 +161,7 @@ TESTS=access \
> open \
> openat \
> pipe \
> + pivot_root \
> ptrace \
> pwrite \
> query_label \
> diff --git a/tests/regression/apparmor/mkprofile.pl b/tests/regression/apparmor/mkprofile.pl
> index e3f1598..1773f74 100755
> --- a/tests/regression/apparmor/mkprofile.pl
> +++ b/tests/regression/apparmor/mkprofile.pl
> @@ -246,6 +246,20 @@ sub gen_umount($) {
> }
> }
>
> +sub gen_pivot_root($) {
> + my $rule = shift;
> + my @rules = split (/:/, $rule);
> + if (@rules == 2) {
> + if ($rules[1] =~ /^ALL$/) {
> + push (@{$output_rules{$hat}}, " pivot_root,\n");
> + } else {
> + push (@{$output_rules{$hat}}, " pivot_root $rules[1],\n");
> + }
> + } else {
> + (!$nowarn) && print STDERR "Warning: invalid pivot_root description '$rule', ignored\n";
> + }
> +}
> +
> sub gen_file($) {
> my $rule = shift;
> my @rules = split (/:/, $rule);
> @@ -338,6 +352,8 @@ sub gen_from_args() {
> gen_remount($rule);
> } elsif ($rule =~ /^umount:/) {
> gen_umount($rule);
> + } elsif ($rule =~ /^pivot_root:/) {
> + gen_pivot_root($rule);
> } elsif ($rule =~ /^flag:/) {
> gen_flag($rule);
> } elsif ($rule =~ /^hat:/) {
> diff --git a/tests/regression/apparmor/pivot_root.c b/tests/regression/apparmor/pivot_root.c
> new file mode 100644
> index 0000000..e5008b1
> --- /dev/null
> +++ b/tests/regression/apparmor/pivot_root.c
> @@ -0,0 +1,124 @@
I forgot to add the license to the top of this file. I'll send out a v2
patch in a moment.
Tyler
> +#define _GNU_SOURCE
> +
> +#include <alloca.h>
> +#include <errno.h>
> +#include <sched.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/apparmor.h>
> +#include <sys/syscall.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +struct clone_arg {
> + const char *put_old;
> + const char *new_root;
> + const char *expected_con;
> +};
> +
> +static int _pivot_root(const char *new_root, const char *put_old)
> +{
> +#ifdef __NR_pivot_root
> + return syscall(__NR_pivot_root, new_root, put_old);
> +#else
> + errno = ENOSYS;
> + return -1;
> +#endif
> +}
> +
> +static int pivot_and_verify_con(void *arg)
> +{
> + const char *put_old = ((struct clone_arg *)arg)->put_old;
> + const char *new_root = ((struct clone_arg *)arg)->new_root;
> + const char *expected_con = ((struct clone_arg *)arg)->expected_con;
> + char *con;
> + int rc;
> +
> + rc = chdir(new_root);
> + if (rc < 0) {
> + perror("FAIL - chdir");
> + exit(100);
> + }
> +
> + rc = _pivot_root(new_root, put_old);
> + if (rc < 0) {
> + perror("FAIL - pivot_root");
> + exit(101);
> + }
> +
> + rc = aa_getcon(&con, NULL);
> + if (rc < 0) {
> + perror("FAIL - aa_getcon");
> + exit(102);
> + }
> +
> + if (strcmp(expected_con, con)) {
> + fprintf(stderr, "FAIL - expected_con (%s) != con (%s)\n",
> + expected_con, con);
> + exit(103);
> + }
> +
> + free(con);
> + exit(0);
> +}
> +
> +static pid_t _clone(int (*fn)(void *), void *arg)
> +{
> + size_t stack_size = sysconf(_SC_PAGESIZE);
> + void *stack = alloca(stack_size);
> +
> +#ifdef __ia64__
> + return __clone2(pivot_and_verify_con, stack, stack_size,
> + CLONE_NEWNS | SIGCHLD, arg);
> +#else
> + return clone(pivot_and_verify_con, stack + stack_size,
> + CLONE_NEWNS | SIGCHLD, arg);
> +#endif
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct clone_arg arg;
> + pid_t child;
> + int child_status, rc;
> +
> + if (argc != 4) {
> + fprintf(stderr,
> + "FAIL - usage: %s <PUT_OLD> <NEW_ROOT> <PROFILE>\n\n"
> + " <PUT_OLD>\t\tThe put_old param of pivot_root()\n"
> + " <NEW_ROOT>\t\tThe new_root param of pivot_root()\n"
> + " <PROFILE>\t\tThe expected AA context after pivoting\n\n"
> + "This program clones itself in a new mount namespace, \n"
> + "does a pivot and then calls aa_getcon(). The test fails \n"
> + "if <PROFILE> does not match the context returned by \n"
> + "aa_getcon().\n", argv[0]);
> + exit(1);
> + }
> +
> + arg.put_old = argv[1];
> + arg.new_root = argv[2];
> + arg.expected_con = argv[3];
> +
> + child = _clone(pivot_and_verify_con, &arg);
> + if (child < 0) {
> + perror("FAIL - clone");
> + exit(2);
> + }
> +
> + rc = waitpid(child, &child_status, 0);
> + if (rc < 0) {
> + perror("FAIL - waitpid");
> + exit(3);
> + } else if (!WIFEXITED(child_status)) {
> + fprintf(stderr, "FAIL - child didn't exit\n");
> + exit(4);
> + } else if (WEXITSTATUS(child_status)) {
> + /* The child has already printed a FAIL message */
> + exit(WEXITSTATUS(child_status));
> + }
> +
> + printf("PASS\n");
> + exit (0);
> +}
> diff --git a/tests/regression/apparmor/pivot_root.sh b/tests/regression/apparmor/pivot_root.sh
> new file mode 100755
> index 0000000..4e57845
> --- /dev/null
> +++ b/tests/regression/apparmor/pivot_root.sh
> @@ -0,0 +1,164 @@
> +#! /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 mount
> +#=DESCRIPTION
> +# This test verifies that the pivot_root syscall is indeed restricted for
> +# confined processes.
> +#=END
> +
> +pwd=`dirname $0`
> +pwd=`cd $pwd ; /bin/pwd`
> +
> +bin=$pwd
> +
> +. $bin/prologue.inc
> +
> +disk_img=$tmpdir/disk_img
> +new_root=$tmpdir/new_root/
> +put_old=${new_root}put_old/
> +bad=$tmpdir/BAD/
> +proc=$new_root/proc
> +fstype="ext2"
> +
> +pivot_root_cleanup() {
> + mountpoint -q "$proc"
> + if [ $? -eq 0 ] ; then
> + umount "$proc"
> + fi
> +
> + mountpoint -q "$new_root"
> + if [ $? -eq 0 ] ; then
> + umount "$new_root"
> + fi
> +}
> +do_onexit="pivot_root_cleanup"
> +
> +# Create disk image since pivot_root doesn't allow old root and new root to be
> +# on the same filesystem
> +dd if=/dev/zero of="$disk_img" bs=1024 count=512 2> /dev/null
> +/sbin/mkfs -t "$fstype" -F "$disk_img" > /dev/null 2> /dev/null
> +/bin/mkdir "$new_root"
> +/bin/mount -o loop -t "$fstype" "$disk_img" "$new_root"
> +
> +# Must mount proc because the pivot_root test program calls aa_getcon() after
> +# pivot_root() and aa_getcon() reads /proc/<PID>/attr/current
> +mkdir "$proc"
> +mount -t proc proc "$proc"
> +
> +# Will be used for pivot_root()'s put_old parameter
> +mkdir "$put_old"
> +
> +do_test()
> +{
> + local desc="PIVOT_ROOT ($1)"
> + shift
> +
> + runchecktest "$desc" "$@"
> +}
> +
> +# Needed for aa_getcon()
> +cur="/proc/*/attr/current:r"
> +
> +# Needed for clone(CLONE_NEWNS) and pivot_root()
> +cap=capability:sys_admin
> +
> +# A profile name that'll be used to test AA's transitions during pivot_root()
> +new_prof=/sbin/init
> +
> +
> +# Ensure everything works as expected when unconfined
> +do_test "unconfined" pass "$put_old" "$new_root" unconfined
> +
> +# Ensure the test binary is accurately doing post pivot_root profile verification
> +do_test "unconfined, bad context" fail "$put_old" "$new_root" "$bad"
> +
> +# Ensure failure when no perms are granted
> +genprofile
> +do_test "no perms" fail "$put_old" "$new_root" "$test"
> +
> +if [ "$(have_features mount)" != "true" ] ; then
> + # pivot_root mediation isn't supported by this kernel, so verify that
> + # capability sys_admin is sufficient and skip the remaining tests
> + genprofile $cur $cap
> + do_test "cap" pass "$put_old" "$new_root" "$test"
> +
> + exit
> +fi
> +
> +# Ensure failure when no pivot_root perms are granted
> +genprofile $cur $cap
> +do_test "cap only" fail "$put_old" "$new_root" "$test"
> +
> +# Ensure failure when everything except capability sys_admin is granted
> +genprofile $cur "pivot_root:ALL"
> +do_test "bare rule, no cap" fail "$put_old" "$new_root" "$test"
> +
> +# Give sufficient perms with full pivot_root access
> +genprofile $cur $cap "pivot_root:ALL"
> +do_test "bare rule" pass "$put_old" "$new_root" "$test"
> +
> +# Give sufficient perms and specify new_root
> +genprofile $cur $cap "pivot_root:$new_root"
> +do_test "new_root" pass "$put_old" "$new_root" "$test"
> +
> +# Ensure failure when new_root is bad
> +genprofile $cur $cap "pivot_root:$bad"
> +do_test "bad new_root" fail "$put_old" "$new_root" "$test"
> +
> +# Give sufficient perms and specify put_old
> +genprofile $cur $cap "pivot_root:oldroot=$put_old"
> +do_test "put_old" pass "$put_old" "$new_root" "$test"
> +
> +# Ensure failure when put_old is bad
> +genprofile $cur $cap "pivot_root:oldroot=$bad"
> +do_test "bad put_old" fail "$put_old" "$new_root" "$test"
> +
> +# Give sufficient perms and specify put_old and new_root
> +genprofile $cur $cap "pivot_root:oldroot=$put_old $new_root"
> +do_test "put_old, new_root" pass "$put_old" "$new_root" "$test"
> +
> +# Ensure failure when put_old is bad
> +genprofile $cur $cap "pivot_root:oldroot=$bad $new_root"
> +do_test "bad put_old, new_root" fail "$put_old" "$new_root" "$test"
> +
> +# Ensure failure when new_root is bad
> +genprofile $cur $cap "pivot_root:oldroot=$put_old $bad"
> +do_test "put_old, bad new_root" fail "$put_old" "$new_root" "$test"
> +
> +# Give sufficient perms and perform a profile transition
> +genprofile $cap "pivot_root:-> $new_prof" -- image=$new_prof $cur
> +do_test "transition" pass "$put_old" "$new_root" "$new_prof"
> +
> +# Ensure failure when the the new profile can't read /proc/<PID>/attr/current
> +genprofile $cap "pivot_root:-> $new_prof" -- image=$new_prof
> +do_test "transition, no perms" fail "$put_old" "$new_root" "$new_prof"
> +
> +# Ensure failure when the new profile doesn't exist
> +genprofile $cap "pivot_root:-> $bad" -- image=$new_prof $cur
> +do_test "bad transition" fail "$put_old" "$new_root" "$new_prof"
> +
> +# Ensure the test binary is accurately doing post pivot_root profile verification
> +genprofile $cap "pivot_root:-> $new_prof" -- image=$new_prof $cur
> +do_test "bad transition comparison" fail "$put_old" "$new_root" "$test"
> +
> +# Give sufficient perms with new_root and a transition
> +genprofile $cap "pivot_root:$new_root -> $new_prof" -- image=$new_prof $cur
> +do_test "new_root, transition" pass "$put_old" "$new_root" "$new_prof"
> +
> +# Ensure failure when the new profile doesn't exist and new_root is specified
> +genprofile $cap "pivot_root:$new_root -> $bad" -- image=$new_prof $cur
> +do_test "new_root, bad transition" fail "$put_old" "$new_root" "$new_prof"
> +
> +# Give sufficient perms with new_root, put_old, and a transition
> +genprofile $cap "pivot_root:oldroot=$put_old $new_root -> $new_prof" -- image=$new_prof $cur
> +do_test "put_old, new_root, transition" pass "$put_old" "$new_root" "$new_prof"
> +
> +# Ensure failure when the new profile doesn't exist and new_root and put_old are specified
> +genprofile $cap "pivot_root:oldroot=$put_old $new_root -> $bad" -- image=$new_prof $cur
> +do_test "put_old, new_root, bad transition" fail "$put_old" "$new_root" "$new_prof"
> --
> 1.9.1
>
>
> --
> AppArmor mailing list
> AppArmor at lists.ubuntu.com
> Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20140414/2ff15c74/attachment.pgp>
More information about the AppArmor
mailing list