From d2c16619e1bccc70def060c647cb5b31a12504ae Mon Sep 17 00:00:00 2001 From: Andrew G. Morgan Date: Wed, 30 Jan 2008 23:23:32 -0800 Subject: [PATCH] Speculative support for prctl based securebits. Add a quick regression/reference test for the various capability manipulations. See corresponding kernel patch (2008/01/30). --- progs/capsh.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ progs/quicktest.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 0 deletions(-) create mode 100755 progs/quicktest.sh diff --git a/progs/capsh.c b/progs/capsh.c index 13ccf46..8987621 100644 --- a/progs/capsh.c +++ b/progs/capsh.c @@ -15,12 +15,15 @@ #include #include #include +#include /* prctl based API for altering character of current process */ #define PR_GET_KEEPCAPS 7 #define PR_SET_KEEPCAPS 8 #define PR_CAPBSET_READ 23 #define PR_CAPBSET_DROP 24 +#define PR_GET_SECUREBITS 25 +#define PR_SET_SECUREBITS 26 static const cap_value_t raise_setpcap[1] = { CAP_SETPCAP }; static const cap_value_t raise_chroot[1] = { CAP_SYS_CHROOT }; @@ -145,6 +148,17 @@ int main(int argc, char *argv[], char *envp[]) */ cap_free(all); + } else if (!memcmp("--keep=", argv[i], 7)) { + unsigned value; + int set; + + value = strtoul(argv[i]+7, NULL, 0); + set = prctl(PR_SET_KEEPCAPS, value); + if (set < 0) { + fprintf(stderr, "prctl(PR_SET_KEEPCAPS, %u) failed: %s\n", + value, strerror(errno)); + exit(1); + } } else if (!memcmp("--chroot=", argv[i], 9)) { int status; cap_t orig, raised_for_chroot; @@ -184,6 +198,28 @@ int main(int argc, char *argv[], char *envp[]) fprintf(stderr, "Unable to chroot to [%s]", argv[i]+9); exit(1); } + } else if (!memcmp("--secbits=", argv[i], 10)) { + unsigned value; + int status; + + value = strtoul(argv[i]+10, NULL, 0); + status = prctl(PR_SET_SECUREBITS, value); + if (status < 0) { + fprintf(stderr, "failed to set securebits to 0%o/0x%x\n", + value, value); + exit(1); + } + } else if (!memcmp("--uid=", argv[i], 6)) { + unsigned value; + int status; + + value = strtoul(argv[i]+6, NULL, 0); + status = setuid(value); + if (status < 0) { + fprintf(stderr, "Failed to set uid=%u: %s\n", + value, strerror(errno)); + exit(1); + } } else if (!strcmp("--print", argv[i])) { unsigned cap; int set; @@ -214,6 +250,29 @@ int main(int argc, char *argv[], char *envp[]) sep = ","; } printf("\n"); + set = prctl(PR_GET_SECUREBITS); + if (set >= 0) { + printf("Securebits: 0%o/0x%x\n", set, set); + printf(" secure-noroot: %s (%s)\n", + (set & 1) ? "yes":"no", + (set & 2) ? "locked":"unlocked"); + printf(" secure-no-suid-fixup: %s (%s)\n", + (set & 4) ? "yes":"no", + (set & 8) ? "locked":"unlocked"); + printf(" secure-keep-caps: %s (%s)\n", + (set & 16) ? "yes":"no", + (set & 32) ? "locked":"unlocked"); + } else { + printf("[Securebits ABI not supported]\n"); + set = prctl(PR_GET_KEEPCAPS); + if (set >= 0) { + printf(" prctl-keep-caps: %s (locking not supported)\n", + set ? "yes":"no"); + } else { + printf("[Keepcaps ABI not supported]\n"); + } + } + printf("uid=%u\n", getuid()); } else if (!strcmp("--", argv[i])) { argv[i] = strdup("/bin/bash"); argv[argc] = NULL; @@ -226,6 +285,9 @@ int main(int argc, char *argv[], char *envp[]) " --print display capability relevant state\n" " --drop=xxx remove xxx,.. capabilities from bset\n" " --inh=xxx set xxx,.. inheritiable set\n" + " --secbits= write a new value for securebits\n" + " --keep= set keep-capabability bit to \n" + " --uid= set uid to (hint: id )\n" " --chroot=path chroot(2) to this path to invoke bash\n" " -- remaing arguments are for /bin/bash\n" " (without -- [%s] will simply exit(0))\n", diff --git a/progs/quicktest.sh b/progs/quicktest.sh new file mode 100755 index 0000000..47d2800 --- /dev/null +++ b/progs/quicktest.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# +# Run through a series of tests to try out the various capability +# manipulations posible through exec. +# + +try_capsh () { + echo "TEST: ./capsh $*" + ./capsh "$@" + if [ $? -ne 0 ]; then + echo FAILED + return 1 + else + echo PASSED + return 0 + fi +} + +fail_capsh () { + echo -n "EXPECT FAILURE: " + try_capsh "$@" + if [ $? -eq 1 ]; then + return 0 + else + echo "Undesired result - aborting" + echo "PROBLEM TEST: $*" + exit 1 + fi +} + +pass_capsh () { + echo -n "EXPECT SUCCESS: " + try_capsh "$@" + if [ $? -eq 0 ]; then + return 0 + else + echo "Undesired result - aborting" + echo "PROBLEM TEST: $*" + exit 1 + fi +} + +pass_capsh --print +pass_capsh --keep=0 --keep=1 --keep=0 --keep=1 --print +pass_capsh --secbits=42 --print +fail_capsh --secbits=32 --keep=1 --keep=0 --print +pass_capsh --secbits=10 --keep=0 --keep=1 --print +fail_capsh --secbits=47 -- -c "ping -c1 localhost" + +# Suppress uid=0 privilege +fail_capsh --secbits=47 --print -- -c "/bin/ping -c1 localhost" + +# Make a local non-setuid-0 version of ping +cp /bin/ping . && chmod -s ./ping + +# Give it the forced capability it needs +./setcap cap_net_raw=ep ./ping + +# suppress uid=0 privilege and test this ping +pass_capsh --secbits=0x2f --print -- -c "./ping -c1 localhost" + +# observe that the bounding set can be used to suppress this forced capability +fail_capsh --drop=cap_net_raw,cap_chown --secbits=0x2f --print -- -c "./ping -c1 localhost" + +# change the way the capability is obtained (make it inheritable) +./setcap cap_net_raw=ei ./ping + +pass_capsh --secbits=47 --inh=cap_net_raw --drop=cap_net_raw \ + --uid=500 --print -- -c "./ping -c1 localhost" -- 1.5.3.7