[Devel] [RFC][cr_tests PATCH] checkpoint/restart: define selinux tests
Serge E. Hallyn
serue at us.ibm.com
Wed Sep 23 23:19:37 PDT 2009
[This is a test for the selinux context restoration behavior
of the application checkpoint/restart kernel feature.]
Define a policy module which defines three domains,
ckpt_test_{1,2,3}_t. We run a self-checkpoint program
under ckpt_test_1_t to create a labeled checkpoint file.
ckpt_test_2_t is allowed to restore a task with
ckpt_test_1_t labels, while ckpt_test_3_t may not. We
try:
1. restarting ckpt from context ckpt_test_2_t,
without --keeplsm, meaning task label at restart
should be ckpt_test_2_t.
2. restarting ckpt from context ckpt_test_3_t,
with --keeplsm, meaning task label at restart
should be ckpt_test_1_t. But that isn't allowed,
so restart should fail.
3. restarting ckpt from context ckpt_test_2_t,
with --keeplsm, meaning task label at restart
should be ckpt_test_2_t.
After doing self-checkpoint, ckpt copies the content of
/proc/self/attr/current to ./context. We use the
contents of that file to verify that the task was restarted
with the proper task label.
Eventually tests should be written to test ipc labels.
This is a part of the c/r tests at
git://git.sr71.net/~hallyn/cr_tests.git
The lsm c/r patches are so far not integrated in the main
checkpoint/restart patchset, but can be seen at
http://git.kernel.org/gitweb.cgi?p=linux/kernel/git/sergeh/linux-cr.git;a=shortlog;h=ckpt-v18.lsm.1
This set has been tested on RHEL5.3 running with refpolicy.
Signed-off-by: Serge E. Hallyn <serue at us.ibm.com>
---
Makefile | 9 ++
README | 23 +++++++
ckpt.c | 76 ++++++++++++++++++++++++
cr-tests-policy.fc | 5 +
cr-tests-policy.if | 42 +++++++++++++
cr-tests-policy.te | 62 ++++++++++++++++++++
runtest.sh | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++
wrap.c | 22 +++++++
8 files changed, 402 insertions(+)
diff --git a/selinux/Makefile b/selinux/Makefile
new file mode 100644
index 0000000..2ae8d96
--- /dev/null
+++ b/selinux/Makefile
@@ -0,0 +1,9 @@
+targets = ckpt wrap
+
+all: $(targets)
+
+ckpt: ckpt.c ../cr.h
+ gcc -o ckpt ckpt.c
+
+clean:
+ rm -rf $(targets) out context cr-test.out cr-test-module restart wrap
diff --git a/selinux/README b/selinux/README
new file mode 100644
index 0000000..fc59c3c
--- /dev/null
+++ b/selinux/README
@@ -0,0 +1,23 @@
+Make sure
+ expand-check = 0
+is in /etc/selinux/semanage.conf.
+
+You also need to add 'restore' to the definitions of
+all_file_perms, all_process_perms, all_ipc_perms, and all_msg_perms
+in /usr/share/selinux/devel/include/support/all_perms.spt. The
+refpolicy source likewise must be updated to know of these perms.
+
+Test sequence:
+
+ 1. load policy
+ 2. run ckpt as ckpt_test_1_t to create a checkpoint image
+ with tasks etc under that label
+ 3. run restart as ckpt_test_2_t without KEEP_LSM, making
+ sure tasks are under ckpt_test_2_t label.
+ 4. run restart as ckpt_test_2_t with KEEP_LSM, making
+ sure tasks are under ckpt_test_1_t label.
+ 5. run restart as ckpt_test_3_t, which does not have
+ restore rights to ckpt_test_1_t, with KEEP_LSM,
+ making sure we get -EPERM.
+
+Later we may want to also test file and ipc labels.
diff --git a/selinux/ckpt.c b/selinux/ckpt.c
new file mode 100644
index 0000000..d34918d
--- /dev/null
+++ b/selinux/ckpt.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 Oren Laadan
+ */
+
+#define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <sys/syscall.h>
+#include "../cr.h"
+
+#define OUTFILE "./cr-test.out"
+
+int main(int argc, char *argv[])
+{
+ pid_t pid = getpid();
+ FILE *file;
+ int ret;
+ int fd, n;
+ char ctx[200];
+
+ fd = open("out", O_RDWR|O_CREAT, 0644);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ close(0);
+ close(2);
+
+ unlink(OUTFILE);
+ file = fopen(OUTFILE, "w+");
+ if (!file) {
+ perror("open");
+ exit(1);
+ }
+
+ close(1);
+ dup2(fd, 1);
+
+ if (dup2(0,2) < 0) {
+ perror("dups");
+ exit(1);
+ }
+
+ fprintf(file, "hello, world!\n");
+ fflush(file);
+
+ ret = syscall(__NR_checkpoint, pid, STDOUT_FILENO, CHECKPOINT_SUBTREE);
+ if (ret < 0) {
+ perror("checkpoint");
+ exit(2);
+ }
+
+ fprintf(file, "world, hello!\n");
+ fprintf(file, "ret = %d\n", ret);
+ fflush(file);
+ file = fopen("/proc/self/attr/current", "r");
+ if (!file)
+ return 3;
+ n = fread(ctx, 1, 200, file);
+ fclose(file);
+ file = fopen("./context", "w");
+ if (!file)
+ return 4;
+ fwrite(ctx, 1, n, file);
+ fclose(file);
+
+ return 0;
+}
+
diff --git a/selinux/cr-tests-policy.fc b/selinux/cr-tests-policy.fc
new file mode 100644
index 0000000..b35d9a7
--- /dev/null
+++ b/selinux/cr-tests-policy.fc
@@ -0,0 +1,5 @@
+# cr_tests/selinux/ckpt executable will have:
+# label: system_u:object_r:ckpt_test_exec_t
+# MLS sensitivity: s0
+# MCS categories: <none>
+
diff --git a/selinux/cr-tests-policy.if b/selinux/cr-tests-policy.if
new file mode 100644
index 0000000..d13d033
--- /dev/null
+++ b/selinux/cr-tests-policy.if
@@ -0,0 +1,42 @@
+########################################
+## <summary>
+## Execute a domain transition to run myapp.
+## </summary>
+## <param name="domain">
+## Domain allowed to transition.
+## </param>
+#
+interface(`ckpt_test_domtrans',`
+ gen_require(`
+ type ckpt_test_1_t, ckpt_test_exec_t;
+ type ckpt_test_2_t, ckpt_test_3_t;
+ type ckpt_test_file_t;
+ ')
+
+ role $2 types ckpt_test_1_t;
+ role $2 types ckpt_test_2_t;
+ role $2 types ckpt_test_3_t;
+
+ spec_domtrans_pattern($1,ckpt_test_exec_t,ckpt_test_1_t);
+ spec_domtrans_pattern($1,ckpt_test_exec_t,ckpt_test_2_t);
+ spec_domtrans_pattern($1,ckpt_test_exec_t,ckpt_test_3_t);
+
+ allow $1 ckpt_test_1_t:fd use;
+ allow $1 ckpt_test_2_t:fd use;
+ allow $1 ckpt_test_3_t:fd use;
+ allow ckpt_test_1_t $1:fd use;
+ allow ckpt_test_2_t $1:fd use;
+ allow ckpt_test_3_t $1:fd use;
+ allow $1 ckpt_test_1_t:fifo_file rw_file_perms;
+ allow $1 ckpt_test_2_t:fifo_file rw_file_perms;
+ allow $1 ckpt_test_3_t:fifo_file rw_file_perms;
+ allow ckpt_test_1_t $1:process { sigchld };
+ allow ckpt_test_2_t $1:process { sigchld };
+ allow ckpt_test_3_t $1:process { sigchld };
+
+ allow $1 ckpt_test_file_t:file manage_file_perms;
+# need some way to give pty access... is there an automatic
+# way to guess at that type, or do we just assume that
+# caller is in staff_t or unconfined_t?
+')
+
diff --git a/selinux/cr-tests-policy.te b/selinux/cr-tests-policy.te
new file mode 100644
index 0000000..9efd0da
--- /dev/null
+++ b/selinux/cr-tests-policy.te
@@ -0,0 +1,62 @@
+policy_module(cr-tests-policy,1.0.0)
+
+########################################
+#
+# Declarations
+#
+
+attribute ckpt_test_domain;
+type ckpt_test_exec_t;
+files_type(ckpt_test_exec_t);
+
+type ckpt_test_1_t;
+typeattribute ckpt_test_1_t ckpt_test_domain;
+domain_type(ckpt_test_1_t)
+domain_entry_file(ckpt_test_1_t, ckpt_test_exec_t)
+
+type ckpt_test_2_t;
+domain_type(ckpt_test_2_t)
+typeattribute ckpt_test_2_t ckpt_test_domain;
+domain_entry_file(ckpt_test_2_t, ckpt_test_exec_t)
+
+type ckpt_test_3_t;
+domain_type(ckpt_test_3_t)
+typeattribute ckpt_test_3_t ckpt_test_domain;
+domain_entry_file(ckpt_test_3_t, ckpt_test_exec_t)
+
+type ckpt_test_file_t;
+files_type(ckpt_test_file_t);
+
+########################################
+#
+# local policy
+#
+
+
+# Some things all the test domains may do:
+manage_dirs_pattern(ckpt_test_domain, ckpt_test_file_t, ckpt_test_file_t)
+allow ckpt_test_domain { ckpt_test_exec_t ckpt_test_file_t }:file *;
+files_tmp_filetrans(ckpt_test_domain, ckpt_test_file_t, file)
+term_use_all_terms(ckpt_test_domain)
+#allow ckpt_test_domain self:process { fork setexec setfscreate setkeycreate setsockcreate setpgid sigkill setcap execmem };
+allow ckpt_test_domain self:process *;
+allow ckpt_test_domain self:fifo_file *;
+allow ckpt_test_domain self:capability *;
+
+# hardcode perms to unconfined pty
+gen_require(`
+ type unconfined_devpts_t;
+ type local_login_t;
+')
+allow ckpt_test_domain unconfined_devpts_t:chr_file { read write ioctl getattr };
+allow ckpt_test_domain local_login_t:fd *;
+
+allow ckpt_test_2_t ckpt_test_1_t:process { restore setcap };
+allow ckpt_test_2_t ckpt_test_1_t:msg restore;
+allow ckpt_test_2_t ckpt_test_1_t:ipc restore;
+allow ckpt_test_2_t ckpt_test_1_t:file restore;
+allow ckpt_test_2_t ckpt_test_1_t:fd use;
+allow ckpt_test_1_t ckpt_test_2_t:file entrypoint;
+allow ckpt_test_1_t ckpt_test_2_t:fd use;
+allow ckpt_test_1_t ckpt_test_2_t:fifo_file *;
+allow ckpt_test_1_t ckpt_test_2_t:process sigchld;
diff --git a/selinux/runtest.sh b/selinux/runtest.sh
new file mode 100644
index 0000000..65907b2
--- /dev/null
+++ b/selinux/runtest.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+# Copyright 2009 IBM Corp.
+# Author: Serge Hallyn
+
+selinuxload() {
+ if [ ! -d /usr/share/selinux/devel ]; then
+ echo install selinux-policy-devel
+ exit 1
+ fi
+ rm -rf cr-test-module
+ cp -r /usr/share/selinux/devel cr-test-module
+ rm -f cr-test-module/example.??
+ cp cr-tests-policy.* cr-test-module/
+ # plug our dirname into the file contexts file
+ dn=`pwd`
+ echo "$dn/ckpt -- gen_context(system_u:object_r:ckpt_test_exec_t,s0)" \
+ >> cr-test-module/cr-tests-policy.fc
+ # allow our context to transition to the test dirs
+ myrole=`cat /proc/self/attr/current |awk -F: '{ print $2 '}`
+ myctx=`cat /proc/self/attr/current |awk -F: '{ print $3 '}`
+ dirctx=`attr -qS -g selinux . | awk -F: '{ print $3 '}`
+cat >> cr-test-module/cr-tests-policy.te << EOF
+gen_require(\`
+ role $myrole;
+ type $myctx;
+ type $dirctx;
+')
+ckpt_test_domtrans($myctx,$myrole)
+allow $myctx ckpt_test_file_t:file rw_file_perms;
+EOF
+ dir=`pwd`
+ stop=0
+ while [ $stop -ne 1 ]; do
+ dirctx=`attr -qS -g selinux $dir | awk -F: '{ print $3 '}`
+cat >> cr-test-module/cr-tests-policy.te << EOF
+gen_require(\`
+ type $dirctx;
+')
+list_dirs_pattern(ckpt_test_domain,$dirctx,$dirctx)
+EOF
+ if [ $dir == "/" ]; then
+ stop=1
+ fi
+ dir=`dirname $dir`
+ done
+ (cd cr-test-module; make; semodule -i cr-tests-policy.pp)
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo failed to load policy
+ fi
+ echo "policy loaded"
+}
+
+selinuxunload() {
+ semodule -r cr-tests-policy
+}
+
+source ../common.sh
+verify_freezer
+verify_paths
+
+cp `which restart` restart
+selinuxload
+
+rm -f ./cr-test.out out context
+
+dirctx=`attr -qS -g selinux . | awk -F: '{ print $3 '}`
+filctx=`attr -qS -g selinux ckpt.c | awk -F: '{ print $3 '}`
+chcon -t ckpt_test_file_t .
+chcon -t ckpt_test_exec_t ./restart
+chcon -t ckpt_test_file_t ./ckpt
+chcon -t ckpt_test_exec_t ./wrap
+
+trap '\
+setenforce 0; \
+semodule -B; \
+selinuxunload; \
+echo "Unloaded selinux policy, exiting"; \
+chcon -t $dirctx . ; \
+chcon -t $filctx ./ckpt ; \
+chcon -t $filctx ./context ; \
+chcon -t $filctx ./cr-test.out; \
+chcon -t $filctx ./wrap ; \
+chcon -t $filctx ./out ; \
+chcon -t $filctx ./restart ' EXIT
+
+semodule -BD
+setenforce 1
+
+# create a checkpoint image with task as type ckpt_test_1_t
+echo "Creating checkpoint image as ckpt_test_1_t"
+runcon -t ckpt_test_1_t ./wrap ./ckpt
+
+echo ab > context
+chcon -t ckpt_test_file_t context
+
+# restart from image starting as ckpt_test_2_t
+# make sure it was restarted as ckpt_test_2_t
+echo "Test 1: restart without KEEP_LSM and verify original task context"
+runcon -t ckpt_test_2_t -- ./restart < out
+ret=$?
+if [ $ret -ne 0 ]; then
+ echo "Restart failed, returned $ret"
+ exit 1
+fi
+context=`cat context | awk -F: '{ print $3 '}`
+if [ -z "$context" -o "$context" != "ckpt_test_2_t" ]; then
+ echo "Fail, context was $context instead of ckpt_test_2_t"
+ exit 1
+fi
+echo Pass
+
+echo ab > context
+chcon -t ckpt_test_file_t context
+# restart with KEEP_LSM from image as ckpt_test_3_t
+# make sure it fails
+echo "Test 2: restart with KEEP_LSM from unauthorized context"
+runcon -t ckpt_test_3_t -- ./restart -k < out
+if [ $? -ne 1 ]; then
+ echo "Fail"
+ exit 1
+fi
+echo Pass
+
+echo ab > context
+chcon -t ckpt_test_file_t context
+# restart with KEEP_LSM from image as ckpt_test2_t
+# make sure it was restarted as ckpt_test_t
+echo "Test 3: restart with KEEP_LSM and verify restored task context"
+runcon -t ckpt_test_2_t -- ./restart -k < out
+ret=$?
+if [ $ret -ne 0 ]; then
+ echo "Restart failed, returned $ret"
+ exit 1
+fi
+context=`cat context | awk -F: '{ print $3 '}`
+if [ -z "$context" -o "$context" != "ckpt_test_1_t" ]; then
+ echo "Fail"
+ exit 1
+fi
+echo Pass
+
+# END that is it for tests define so far
+echo "REST of tests are not yet implemented in policy, exiting."
+echo "All tests passed."
+exit 0
+
+cg=${freezermountpoint}/1
+mkdir -p $cg
+
+# restart from type ckpt_test3_t which creates files of type ckpt_testf2_t
+# make sure open file is ckpt_testf2_t
+echo "Test 4: restart without KEEP_LSM and verify open file context"
+runcon -t ckpt_test3_t -- ./restart -F $cg < out
+sleep 1
+pid=`pidof ckpt`
+context=`ls -lZ /proc/$pid/fd | grep cr-test.out | awk '{ print $3 '}`
+thaw
+if [ -z "$context" -o "$context" != "ckpt_testf2_t" ]; then
+ echo "Fail"
+ exit 1
+fi
+echo Pass
diff --git a/selinux/wrap.c b/selinux/wrap.c
new file mode 100644
index 0000000..3e92cc3
--- /dev/null
+++ b/selinux/wrap.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2009 IBM Corp.
+ * Author: Serge Hallyn
+ *
+ * if i do
+ * runcon -t ckpt_test_1_t ./ckpt
+ * then the file->f_cred for ckpt will actually be runcon's
+ * before the context switch. We don't want to have to give
+ * the restarter the rights to process:restore unconfined_t,
+ * so we'll do
+ * runcon -t ckpt_test_1_t ./wrap ./ckpt
+ * so that ckpt is actually opened by a task with type
+ * ckpt_test_1_t, so that all file->f_creds are in that context.
+ */
+
+int main(int argc, char *argv[])
+{
+ char *newcmd, **newargv;
+ newargv = argv+1;
+ newcmd = argv[0];
+ return execv(newcmd, newargv);
+}
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list