aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2016-02-07 20:22:35 -0800
committerAndrew G. Morgan <morgan@kernel.org>2016-02-07 20:22:35 -0800
commit8c67abc3f5cfa8310dae21e5db464fd990cb71a2 (patch)
tree4e1862705fba626a8c1bf8c7022b4f174dbc652d
parent0f0eca489e979b4a8526e521f962455e474a27a0 (diff)
downloadlibcap-8c67abc3f5cfa8310dae21e5db464fd990cb71a2.tar.gz
Clean up ambient support and add a smoke test for them.HEADmaster
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rwxr-xr-xkdebug/test-kernel.sh2
-rw-r--r--libcap/cap_proc.c4
-rw-r--r--progs/capsh.c248
-rwxr-xr-xprogs/quicktest.sh59
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