diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2016-02-07 20:22:35 -0800 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2016-02-07 20:22:35 -0800 |
commit | 8c67abc3f5cfa8310dae21e5db464fd990cb71a2 (patch) | |
tree | 4e1862705fba626a8c1bf8c7022b4f174dbc652d | |
parent | 0f0eca489e979b4a8526e521f962455e474a27a0 (diff) | |
download | libcap-8c67abc3f5cfa8310dae21e5db464fd990cb71a2.tar.gz |
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rwxr-xr-x | kdebug/test-kernel.sh | 2 | ||||
-rw-r--r-- | libcap/cap_proc.c | 4 | ||||
-rw-r--r-- | progs/capsh.c | 248 | ||||
-rwxr-xr-x | progs/quicktest.sh | 59 |
4 files changed, 230 insertions, 83 deletions
diff --git a/kdebug/test-kernel.sh b/kdebug/test-kernel.sh index c8ce144..d480a63 100755 --- a/kdebug/test-kernel.sh +++ b/kdebug/test-kernel.sh @@ -47,7 +47,7 @@ file /root/capsh $HERE/../progs/capsh 0755 0 0 file /root/getpcaps $HERE/../progs/getpcaps 0755 0 0 EOF -COMMANDS="ls ln cp id pwd mkdir rmdir cat rm sh mount umount chmod less" +COMMANDS="ls ln cp dmesg id pwd mkdir rmdir cat rm sh mount umount chmod less vi" for f in $COMMANDS; do echo slink /bin/$f /sbin/busybox 0755 0 0 >> fs.conf done diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c index ffa0d91..f70b0e3 100644 --- a/libcap/cap_proc.c +++ b/libcap/cap_proc.c @@ -166,8 +166,8 @@ int cap_set_ambient(cap_value_t cap, cap_flag_value_t set) errno = EINVAL; return -1; } - result = prctl(PR_CAP_AMBIENT, pr_arg(PR_CAP_AMBIENT_RAISE), - pr_arg(cap), pr_arg(val), pr_arg(0)); + result = prctl(PR_CAP_AMBIENT, pr_arg(val), pr_arg(cap), + pr_arg(0), pr_arg(0)); if (result < 0) { errno = -result; return -1; diff --git a/progs/capsh.c b/progs/capsh.c index 9c907a7..a1d6e2b 100644 --- a/progs/capsh.c +++ b/progs/capsh.c @@ -26,9 +26,6 @@ #define MAX_GROUPS 100 /* max number of supplementary groups for user */ -static const cap_value_t raise_setpcap[1] = { CAP_SETPCAP }; -static const cap_value_t raise_chroot[1] = { CAP_SYS_CHROOT }; - static char *binary(unsigned long value) { static char string[8*sizeof(unsigned long) + 1]; @@ -140,85 +137,199 @@ static void arg_print(void) printf("\n"); } -int main(int argc, char *argv[], char *envp[]) +static const cap_value_t raise_setpcap[1] = { CAP_SETPCAP }; +static const cap_value_t raise_chroot[1] = { CAP_SYS_CHROOT }; + +static void push_pcap(cap_t *orig_p, cap_t *raised_for_setpcap_p) { - pid_t child; - unsigned i; + /* + * We need to do this here because --inh=XXX may have reset + * orig and it isn't until we are within the --drop code that + * we know what the prevailing (orig) pI value is. + */ + *orig_p = cap_get_proc(); + if (NULL == *orig_p) { + perror("Capabilities not available"); + exit(1); + } - child = 0; + *raised_for_setpcap_p = cap_dup(*orig_p); + if (NULL == *raised_for_setpcap_p) { + fprintf(stderr, "modification requires CAP_SETPCAP\n"); + exit(1); + } + if (cap_set_flag(*raised_for_setpcap_p, CAP_EFFECTIVE, 1, + raise_setpcap, CAP_SET) != 0) { + perror("unable to select CAP_SETPCAP"); + exit(1); + } +} - for (i=1; i<argc; ++i) { - if (!memcmp("--drop=", argv[i], 4)) { - char *ptr; - cap_t orig, raised_for_setpcap; +static void pop_pcap(cap_t orig, cap_t raised_for_setpcap) +{ + cap_free(raised_for_setpcap); + cap_free(orig); +} - /* - * We need to do this here because --inh=XXX may have reset - * orig and it isn't until we are within the --drop code that - * we know what the prevailing (orig) pI value is. - */ - orig = cap_get_proc(); - if (orig == NULL) { - perror("Capabilities not available"); +static void arg_drop(const char *arg_names) +{ + char *ptr; + cap_t orig, raised_for_setpcap; + char *names; + + push_pcap(&orig, &raised_for_setpcap); + if (strcmp("all", arg_names) == 0) { + unsigned j = 0; + while (CAP_IS_SUPPORTED(j)) { + int status; + if (cap_set_proc(raised_for_setpcap) != 0) { + perror("unable to raise CAP_SETPCAP for BSET changes"); + exit(1); + } + status = cap_drop_bound(j); + if (cap_set_proc(orig) != 0) { + perror("unable to lower CAP_SETPCAP post BSET change"); exit(1); } + if (status != 0) { + char *name_ptr; - raised_for_setpcap = cap_dup(orig); - if (raised_for_setpcap == NULL) { - fprintf(stderr, "BSET modification requires CAP_SETPCAP\n"); + name_ptr = cap_to_name(j); + fprintf(stderr, "Unable to drop bounding capability [%s]\n", + name_ptr); + cap_free(name_ptr); + exit(1); + } + j++; + } + pop_pcap(orig, raised_for_setpcap); + return; + } + + names = strdup(arg_names); + if (NULL == names) { + fprintf(stderr, "failed to allocate names\n"); + exit(1); + } + for (ptr = names; (ptr = strtok(ptr, ",")); ptr = NULL) { + /* find name for token */ + cap_value_t cap; + int status; + + if (cap_from_name(ptr, &cap) != 0) { + fprintf(stderr, "capability [%s] is unknown to libcap\n", ptr); + exit(1); + } + if (cap_set_proc(raised_for_setpcap) != 0) { + perror("unable to raise CAP_SETPCAP for BSET changes"); + exit(1); + } + status = cap_drop_bound(cap); + if (cap_set_proc(orig) != 0) { + perror("unable to lower CAP_SETPCAP post BSET change"); + exit(1); + } + if (status != 0) { + fprintf(stderr, "failed to drop [%s=%u]\n", ptr, cap); + exit(1); + } + } + pop_pcap(orig, raised_for_setpcap); + free(names); +} + +static void arg_change_amb(const char *arg_names, cap_flag_value_t set) +{ + char *ptr; + cap_t orig, raised_for_setpcap; + char *names; + + push_pcap(&orig, &raised_for_setpcap); + if (strcmp("all", arg_names) == 0) { + unsigned j = 0; + while (CAP_IS_SUPPORTED(j)) { + int status; + if (cap_set_proc(raised_for_setpcap) != 0) { + perror("unable to raise CAP_SETPCAP for AMBIENT changes"); exit(1); } + status = cap_set_ambient(j, set); + if (cap_set_proc(orig) != 0) { + perror("unable to lower CAP_SETPCAP post AMBIENT change"); + exit(1); + } + if (status != 0) { + char *name_ptr; - if (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, - raise_setpcap, CAP_SET) != 0) { - perror("unable to select CAP_SETPCAP"); + name_ptr = cap_to_name(j); + fprintf(stderr, "Unable to %s ambient capability [%s]\n", + set == CAP_CLEAR ? "clear":"raise", name_ptr); + cap_free(name_ptr); exit(1); } + j++; + } + pop_pcap(orig, raised_for_setpcap); + return; + } - if (strcmp("all", argv[i]+7) == 0) { - unsigned j = 0; - while (CAP_IS_SUPPORTED(j)) { - if (cap_drop_bound(j) != 0) { - char *name_ptr; + names = strdup(arg_names); + if (NULL == names) { + fprintf(stderr, "failed to allocate names\n"); + exit(1); + } + for (ptr = names; (ptr = strtok(ptr, ",")); ptr = NULL) { + /* find name for token */ + cap_value_t cap; + int status; - name_ptr = cap_to_name(j); - fprintf(stderr, - "Unable to drop bounding capability [%s]\n", - name_ptr); - cap_free(name_ptr); - exit(1); - } - j++; - } - } else { - for (ptr = argv[i]+7; (ptr = strtok(ptr, ",")); ptr = NULL) { - /* find name for token */ - cap_value_t cap; - int status; - - if (cap_from_name(ptr, &cap) != 0) { - fprintf(stderr, - "capability [%s] is unknown to libcap\n", - ptr); - exit(1); - } - if (cap_set_proc(raised_for_setpcap) != 0) { - perror("unable to raise CAP_SETPCAP for BSET changes"); - exit(1); - } - status = prctl(PR_CAPBSET_DROP, cap); - if (cap_set_proc(orig) != 0) { - perror("unable to lower CAP_SETPCAP post BSET change"); - exit(1); - } - if (status) { - fprintf(stderr, "failed to drop [%s=%u]\n", ptr, cap); - exit(1); - } - } + if (cap_from_name(ptr, &cap) != 0) { + fprintf(stderr, "capability [%s] is unknown to libcap\n", ptr); + exit(1); + } + if (cap_set_proc(raised_for_setpcap) != 0) { + perror("unable to raise CAP_SETPCAP for AMBIENT changes"); + exit(1); + } + status = cap_set_ambient(cap, set); + if (cap_set_proc(orig) != 0) { + perror("unable to lower CAP_SETPCAP post AMBIENT change"); + exit(1); + } + if (status != 0) { + fprintf(stderr, "failed to %s ambient [%s=%u]\n", + set == CAP_CLEAR ? "clear":"raise", ptr, cap); + exit(1); + } + } + pop_pcap(orig, raised_for_setpcap); + free(names); +} + +int main(int argc, char *argv[], char *envp[]) +{ + pid_t child; + unsigned i; + + child = 0; + + for (i=1; i<argc; ++i) { + if (!memcmp("--drop=", argv[i], 4)) { + arg_drop(argv[i]+7); + } else if (!strcmp("--has-ambient", argv[i])) { + if (!CAP_AMBIENT_SUPPORTED()) { + fprintf(stderr, "ambient set not supported\n"); + exit(1); + } + } else if (!memcmp("--addamb=", argv[i], 9)) { + arg_change_amb(argv[i]+9, CAP_SET); + } else if (!memcmp("--delamb=", argv[i], 9)) { + arg_change_amb(argv[i]+9, CAP_CLEAR); + } else if (!memcmp("--noamb", argv[i], 7)) { + if (cap_reset_ambient() != 0) { + fprintf(stderr, "failed to reset ambient set\n"); + exit(1); } - cap_free(raised_for_setpcap); - cap_free(orig); } else if (!memcmp("--inh=", argv[i], 6)) { cap_t all, raised_for_setpcap; char *text; @@ -594,6 +705,9 @@ int main(int argc, char *argv[], char *envp[]) " --decode=xxx decode a hex string to a list of caps\n" " --supports=xxx exit 1 if capability xxx unsupported\n" " --drop=xxx remove xxx,.. capabilities from bset\n" + " --addamb=xxx add xxx,... capabilities to ambient set\n" + " --delamb=xxx remove xxx,... capabilities from ambient\n" + " --noamb=xxx reset the ambient capabilities\n" " --caps=xxx set caps as per cap_from_text()\n" " --inh=xxx set xxx,.. inheritiable set\n" " --secbits=<n> write a new value for securebits\n" diff --git a/progs/quicktest.sh b/progs/quicktest.sh index e8b2c8e..fc7b4cc 100755 --- a/progs/quicktest.sh +++ b/progs/quicktest.sh @@ -46,7 +46,7 @@ pass_capsh --print # Make a local non-setuid-0 version of capsh and call it privileged -cp ./capsh ./privileged && chmod -s ./privileged +cp ./capsh ./privileged && /bin/chmod -s ./privileged if [ $? -ne 0 ]; then echo "Failed to copy capsh for capability manipulation" exit 1 @@ -67,11 +67,11 @@ fi # Explore keep_caps support pass_capsh --keep=0 --keep=1 --keep=0 --keep=1 --print -rm -f tcapsh -cp capsh tcapsh -chown root.root tcapsh -chmod u+s tcapsh -ls -l tcapsh +/bin/rm -f tcapsh +/bin/cp capsh tcapsh +/bin/chown root.root tcapsh +/bin/chmod u+s tcapsh +/bin/ls -l tcapsh # leverage keep caps maintain capabilities accross a change of uid # from setuid root to capable luser (as per wireshark/dumpcap 0.99.7) @@ -98,7 +98,7 @@ fail_capsh --secbits=32 --keep=1 --keep=0 --print pass_capsh --secbits=10 --keep=0 --keep=1 --print fail_capsh --secbits=47 -- -c "./tcapsh --uid=$nouid" -rm -f tcapsh +/bin/rm -f tcapsh # Suppress uid=0 privilege fail_capsh --secbits=47 --print -- -c "./capsh --uid=$nouid" @@ -117,10 +117,10 @@ fail_capsh --drop=cap_setuid --secbits=0x2f --print -- -c "./privileged --uid=$n pass_capsh --secbits=47 --inh=cap_setuid,cap_setgid --drop=cap_setuid \ --uid=500 --print -- -c "./privileged --uid=$nouid" -rm -f ./privileged +/bin/rm -f ./privileged # test that we do not support capabilities on setuid shell-scripts -cat > hack.sh <<EOF +/bin/cat > hack.sh <<EOF #!/bin/bash /usr/bin/id mypid=\$\$ @@ -134,20 +134,53 @@ else fi exit 0 EOF -chmod +xs hack.sh +/bin/chmod +xs hack.sh ./capsh --uid=500 --inh=none --print -- ./hack.sh status=$? -rm -f ./hack.sh +/bin/rm -f ./hack.sh if [ $status -ne 0 ]; then echo "shell scripts can have capabilities (bug)" exit 1 fi -# Max lockdown +# Max lockdown (ie., pure capability model as POSIX.1e intended). +secbits=0x2f +if ./capsh --has-ambient ; then + secbits="0xef --noamb" +fi pass_capsh --keep=1 --uid=$nouid --caps=cap_setpcap=ep \ - --drop=all --secbits=0x2f --caps= --print + --drop=all --secbits=$secbits --caps= --print # Verify we can chroot pass_capsh --chroot=$(/bin/pwd) pass_capsh --chroot=$(/bin/pwd) == fail_capsh --chroot=$(/bin/pwd) -- -c "echo oops" + +exit_early () { + echo "$*" + exit 0 +} + +./capsh --has-ambient || exit_early "skipping ambient tests" + +# Ambient capabilities (any file can inherit capabilities) +pass_capsh --noamb + +# test that shell scripts can inherit through ambient capabilities +/bin/cat > hack.sh <<EOF +#!/bin/bash +/usr/bin/id +mypid=\$\$ +caps=\$(./getpcaps \$mypid 2>&1 | /usr/bin/cut -d: -f2) +if [ "\$caps" != " = cap_setuid+i" ]; then + echo "Shell script got [\$caps]" + exit 0 +fi +ls -l \$0 +echo "no capabilities [\$caps] for this shell script" +exit 1 +EOF +/bin/chmod +x hack.sh +pass_capsh --keep=1 --uid=$nouid --inh=cap_setuid --addamb=cap_setuid -- ./hack.sh + +/bin/rm -f hack.sh |