[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