diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2008-02-03 22:30:03 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2008-02-03 22:30:03 -0800 |
commit | d3ea8da3c1acaabf5accbec15a29919ed0170598 (patch) | |
tree | 9964062b445e9ea46dd3793fc147a20f22a42d9a | |
parent | ddfb354d4e2c6a7f51e734cb917e5c6e68dfbd45 (diff) | |
download | libcap-d3ea8da3c1acaabf5accbec15a29919ed0170598.tar.gz |
Support for prctl based securebits.
Add a quick regression/reference test for the various capability
manipulations. (Run it as root.)
-rw-r--r-- | progs/capsh.c | 68 | ||||
-rwxr-xr-x | progs/quicktest.sh | 69 |
2 files changed, 134 insertions, 3 deletions
diff --git a/progs/capsh.c b/progs/capsh.c index 13ccf46..897beb4 100644 --- a/progs/capsh.c +++ b/progs/capsh.c @@ -15,12 +15,15 @@ #include <sys/prctl.h> #include <sys/capability.h> #include <unistd.h> +#include <errno.h> /* 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 }; @@ -97,13 +100,14 @@ int main(int argc, char *argv[], char *envp[]) raised_for_setpcap = cap_dup(all); if ((raised_for_setpcap != NULL) - || (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, + && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, raise_setpcap, CAP_SET) != 0)) { cap_free(raised_for_setpcap); raised_for_setpcap = NULL; } text = cap_to_text(all, NULL); + cap_free(all); if (text == NULL) { perror("Fatal error concerning process capabilities"); exit(1); @@ -114,7 +118,6 @@ int main(int argc, char *argv[], char *envp[]) exit(1); } sprintf(ptr, "%s all-i %s+i", text, argv[i]+6); - cap_free(all); all = cap_from_text(ptr); if (all == NULL) { @@ -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; @@ -199,7 +235,7 @@ int main(int argc, char *argv[], char *envp[]) printf("Bounding set ="); sep = ""; - for (cap=0; (set = prctl(PR_CAPBSET_READ, cap)) != -1; cap++) { + for (cap=0; (set = prctl(PR_CAPBSET_READ, cap)) >= 0; cap++) { const char *ptr; if (!set) { continue; @@ -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=<n> write a new value for securebits\n" + " --keep=<n> set keep-capabability bit to <n>\n" + " --uid=<n> set uid to <n> (hint: id <username>)\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" |