From: Andrew G. M. <mo...@ke...> - 2008-03-06 06:36:46
|
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 se...@ha... wrote: | Figure in the meantime I can at least send it out for some | more comments from Andrew. Andrew, I'm also attaching the | new verify_caps_exec.c verbatim for easier review. Thanks. As per email to Chris F. the 'all' shorthand in 2.07 is broken when running on 32-bit capabilities systems. I've fixed it in the git tree, but need to make a 2.08 to release that fix. Perhaps this is related to your problem? Comments inline below... | | -serge | | Signed-off-by: Serge E. Hallyn <se...@us...> | --- | Makefile | 8 + | makenumcapsh.c | 21 ++++ | verify_caps_exec.c | 225 +++++++++++++++++++++-------------------------------- | 3 files changed, 121 insertions(+), 133 deletions(-) | | diff --git a/testcases/kernel/security/filecaps/Makefile b/testcases/kernel/security/filecaps/Makefile | index ec5cf8f..651769b 100644 | --- a/testcases/kernel/security/filecaps/Makefile | +++ b/testcases/kernel/security/filecaps/Makefile | @@ -37,9 +37,15 @@ all: | TARGETS = | endif | | +numcaps.h: makenumcapsh.c | + gcc -o makenumcapsh makenumcapsh.c | + ./makenumcapsh > numcaps.h | + | +verify_caps_exec: verify_caps_exec.c numcaps.h | + | INSTALLTARGETS = $(TARGETS) $(SCRIPTS) checkforlibcap | install: $(INSTALLTARGETS) | @set -e; for i in $(INSTALLTARGETS); do ln -f $$i ../../../bin/$$i ; chmod +x ../../../bin/$$i; done | | clean: | - rm -f $(TARGETS) *.o caps_fifo checkforlibcap | + rm -f $(TARGETS) *.o caps_fifo checkforlibcap numcaps.h makenumcapsh | diff --git a/testcases/kernel/security/filecaps/makenumcapsh.c b/testcases/kernel/security/filecaps/makenumcapsh.c | new file mode 100644 | index 0000000..6676176 | --- /dev/null | +++ b/testcases/kernel/security/filecaps/makenumcapsh.c | @@ -0,0 +1,21 @@ | +#include <stdio.h> | +#include <errno.h> | +#include <sys/capability.h> | +#include <sys/prctl.h> | + | +#ifndef PR_CAPBSET_READ | +#define PR_CAPBSET_READ 23 | +#endif | + | +int main(int argc, char *argv[]) | +{ | + int i, ret = 0; | + | + for (i=0; ret != -1; i++) { | + ret = prctl(PR_CAPBSET_READ, i); | + if (ret == -1) | + break; | + } | + printf("#define NUM_CAPS %d\n", i); | + return 0; | +} | diff --git a/testcases/kernel/security/filecaps/verify_caps_exec.c b/testcases/kernel/security/filecaps/verify_caps_exec.c | index 38aa1ad..77f995a 100644 | --- a/testcases/kernel/security/filecaps/verify_caps_exec.c | +++ b/testcases/kernel/security/filecaps/verify_caps_exec.c | @@ -21,7 +21,7 @@ | * File: verify_caps_exec.c | * Author: Serge Hallyn | * Purpose: perform several tests of file capabilities: | - * 1. try setting caps without CAP_SYS_ADMIN | + * 1. try setting caps without privilege | * 2. test proper calculation of pI', pE', and pP'. | * Try setting valid caps, drop rights, and run the executable, | * make sure we get the rights | @@ -35,13 +35,14 @@ | #include <sys/types.h> | #include <sys/stat.h> | #include <sys/wait.h> | -#include <attr/xattr.h> | #include <errno.h> | #include <fcntl.h> | #include <sys/capability.h> | #include <sys/prctl.h> | #include <test.h> | | +#include "numcaps.h" | + | #define TSTPATH "./print_caps" | char *TCID = "filecaps"; | int TST_TOTAL=1; | @@ -51,7 +52,7 @@ int errno; | void usage(char *me) | { | tst_resm(TFAIL, "Usage: %s <0|1> [arg]\n", me); | - tst_resm(TINFO, " 0: set file caps without CAP_SYS_ADMIN\n"); | + tst_resm(TINFO, " 0: set file caps without privilegeCAP_SYS_ADMIN\n"); Did the text above get garbled? | tst_resm(TINFO, " 1: test that file caps are set correctly on exec\n"); | tst_exit(1); | } | @@ -63,6 +64,7 @@ void print_my_caps() | { | cap_t cap = cap_get_proc(); | tst_resm(TINFO, "\ncaps are %s\n", cap_to_text(cap, NULL)); cap_to_text() malloc's some memory too - you might want to free that. | + cap_free(cap); | } | | int drop_root(int keep_perms) | @@ -78,52 +80,27 @@ int drop_root(int keep_perms) | tst_exit(4); | } | if (keep_perms) { | - cap_t cap = cap_from_text("=eip cap_setpcap-eip"); | + cap_t cap = cap_from_text("=eip"); | cap_set_proc(cap); | + cap_free(cap); | } | | return 1; | } | | -#if BYTE_ORDER == LITTLE_ENDIAN | -#define cpu_to_le32(x) x | -#else | -#define cpu_to_le32(x) bswap_32(x) | -#endif | - | -/* | - * TODO: find a better way to do this. Emulate libcap's | - * way, or just take it from linux/capability.h | - */ | -/* | - * TODO: accomodate 64-bit capabilities | - */ | -#define CAPNAME "security.capability" | -#ifndef __CAP_BITS | -#define __CAP_BITS 31 | -#endif | - | -#define XATTR_CAPS_SZ (3*sizeof(__le32)) | -#define VFS_CAP_REVISION_MASK 0xFF000000 | -#define VFS_CAP_REVISION 0x01000000 | - | -#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK | -#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 | - | int perms_test(void) | { | int ret; | - unsigned int value[3]; | - unsigned int v; | + cap_t cap; | | drop_root(DROP_PERMS); | - v = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE; | - value[0] = cpu_to_le32(v); | - value[1] = 1; | - value[2] = 1; | - ret = setxattr(TSTPATH, CAPNAME, value, 3*sizeof(unsigned int), 0); | + cap = cap_from_text("all=eip"); | + if (!cap) { | + tst_resm(TFAIL, "could not get cap from text for perms test\n"); | + return 1; | + } | + ret = cap_set_file(TSTPATH, cap); | if (ret) { | - perror("setxattr"); | tst_resm(TPASS, "could not set capabilities as non-root\n"); | ret = 0; | } else { | @@ -131,6 +108,7 @@ int perms_test(void) | ret = 1; | } | | + cap_free(cap); | return ret; | } | | @@ -231,17 +209,15 @@ int fork_drop_and_exec(int keepperms, char *capstxt) | | int caps_actually_set_test(void) | { | - int i, whichset, whichcap, finalret = 0, ret; | - cap_t cap; | + int whichcap, finalret = 0, ret; | + cap_t fcap, pcap, cap_fullpi; | char *capstxt; | - unsigned int value[3]; | cap_value_t capvalue[1]; | - unsigned int magic; | - | - magic = VFS_CAP_REVISION; | + int i; | | - cap = cap_init(); | - if (!cap) { | + fcap = cap_init(); | + pcap = cap_init(); | + if (!fcap || !pcap) { | perror("cap_init"); | exit(2); | } | @@ -249,61 +225,64 @@ int caps_actually_set_test(void) | create_fifo(); | | /* first, try each bit in fP (forced) with fE on and off. */ | - value[1] = value[2] = cpu_to_le32(0); | - for (whichcap=0; whichcap < __CAP_BITS; whichcap++) { | - if (whichcap == 8) | - continue; | - /* fE = 0, don't gain the perm */ | + for (whichcap=0; whichcap < NUM_CAPS; whichcap++) { | + /* | + * fP=whichcap, fE=fI=0 | + * pP'=whichcap, pI'=pE'=0 | + */ | capvalue[0] = whichcap; | - value[0] = cpu_to_le32(magic); | - value[1] = cpu_to_le32(1 << whichcap); | - ret = setxattr(TSTPATH, CAPNAME, value, 3*sizeof(unsigned int), 0); | + cap_clear(fcap); | + cap_set_flag(fcap, CAP_PERMITTED, 1, capvalue, CAP_SET); | + ret = cap_set_file(TSTPATH, fcap); | if (ret) { | - tst_resm(TINFO, "%d %d\n", whichset, whichcap); | - perror("setxattr"); | + tst_resm(TINFO, "%d\n", whichcap); | continue; | } | - /* do a sanity check */ | - cap_clear(cap); | - cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); | - capstxt = cap_to_text(cap, NULL); | + capstxt = cap_to_text(fcap, NULL); | ret = fork_drop_and_exec(DROP_PERMS, capstxt); | + cap_free(capstxt); | if (ret) { | tst_resm(TINFO, "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=0\n", | whichcap); | - if (!finalret) | - finalret = ret; | + if (!finalret) finalret = ret; | } | | - /* fE = 1, do gain the perm */ | - value[0] = cpu_to_le32(magic | VFS_CAP_FLAGS_EFFECTIVE); | - value[1] = cpu_to_le32(1 << whichcap); | - ret = setxattr(TSTPATH, CAPNAME, value, 3*sizeof(unsigned int), 0); | +/* SERGE here */ | + /* | + * fP = fE = whichcap, fI = 0 | + * pP = pE = whichcap, pI = 0 | + */ | + cap_clear(fcap); | + cap_set_flag(fcap, CAP_PERMITTED, 1, capvalue, CAP_SET); | + cap_set_flag(fcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); | + ret = cap_set_file(TSTPATH, fcap); | if (ret) { | - tst_resm(TINFO, "%d %d\n", whichset, whichcap); | - perror("setxattr"); | + tst_resm(TINFO, "%d\n", whichcap); | continue; | } | - /* do a sanity check */ | - cap_clear(cap); | - cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); | - cap_set_flag(cap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); | - capstxt = cap_to_text(cap, NULL); | + capstxt = cap_to_text(fcap, NULL); | if (strcmp(capstxt, "=")==0) { | tst_resm(TINFO, "%s: libcap doesn't know about cap %d, not running\n", | __FUNCTION__, whichcap); | ret = 0; | } else | ret = fork_drop_and_exec(DROP_PERMS, capstxt); | + cap_free(capstxt); | if (ret) { | tst_resm(TINFO, "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=1\n", | whichcap); | - if (!finalret) | - finalret = ret; | + if (!finalret) finalret = ret; | } | } | | | + cap_free(pcap); | + cap_free(fcap); | + cap_fullpi = cap_init(); does this ever get cap_free()d? Rest looks ok. Cheers Andrew | + for (i=0; i<NUM_CAPS; i++) { | + capvalue[0] = i; | + cap_set_flag(cap_fullpi, CAP_INHERITABLE, 1, capvalue, CAP_SET); | + } | /* | * next try each bit in fI | * The first two attemps have the bit which is in fI in pI. | @@ -313,72 +292,60 @@ int caps_actually_set_test(void) | * This should result in empty capability, as there were | * no bits to be inherited from the original process. | */ | - value[1] = value[2] = cpu_to_le32(0); | - for (whichcap=0; whichcap < __CAP_BITS; whichcap++) { | - if (whichcap == 8) | - continue; | + for (whichcap=0; whichcap < NUM_CAPS; whichcap++) { | + capvalue[0] = whichcap; | + | /* | - * bit is in fI and pI, so should be in pI'. | - * but fE=0, so cap is in pP' but not pE'. | + * fI=whichcap, fP=fE=0 | + * pI=full | + * pI'=full, pP'=whichcap, pE'=0 | */ | - value[0] = cpu_to_le32(magic); | - value[2] = cpu_to_le32(1 << whichcap); | - ret = setxattr(TSTPATH, CAPNAME, value, 3*sizeof(unsigned int), 0); | + /* fill pI' */ | + pcap = cap_dup(cap_fullpi); | + /* pP' = whichcap */ | + cap_set_flag(pcap, CAP_PERMITTED, 1, capvalue, CAP_SET); | + | + /* fI = whichcap */ | + fcap = cap_init(); | + cap_set_flag(fcap, CAP_INHERITABLE, 1, capvalue, CAP_SET); | + ret = cap_set_file(TSTPATH, fcap); | if (ret) { | - tst_resm(TINFO, "%d %d\n", whichset, whichcap); | - perror("setxattr"); | + tst_resm(TINFO, "%d\n", whichcap); | continue; | } | - /* do a sanity check */ | - cap_clear(cap); | - for (i=0; i<32; i++) { | - if (i != 8) { | - capvalue[0] = i; | - cap_set_flag(cap, CAP_INHERITABLE, 1, capvalue, CAP_SET); | - } | - } | - capvalue[0] = whichcap; | - cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); | - capstxt = cap_to_text(cap, NULL); | + capstxt = cap_to_text(pcap, NULL); | ret = fork_drop_and_exec(KEEP_PERMS, capstxt); | + cap_free(capstxt); | if (ret) { | tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d " | "CAP_EFFECTIVE=0\n", whichcap); | - if (!finalret) | - finalret = ret; | + if (!finalret) finalret = ret; | } | | /* | - * bit is in fI and pI, so should be in pI'. | - * and fE=1, so cap is in pP' and pE'. | + * fI=fE=whichcap, fP=0 | + * pI=full | + * pI'=full, pP'=whichcap, pE'=whichcap | + * | + * Note that only fE and pE' change, so keep prior | + * fcap and pcap and set those bits. | */ | | - value[0] = cpu_to_le32(magic | VFS_CAP_FLAGS_EFFECTIVE); | - value[2] = cpu_to_le32(1 << whichcap); | - ret = setxattr(TSTPATH, CAPNAME, value, 3*sizeof(unsigned int), 0); | + cap_set_flag(fcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); | + cap_set_flag(pcap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); | + ret = cap_set_file(TSTPATH, fcap); | if (ret) { | - tst_resm(TINFO, "%d %d\n", whichset, whichcap); | - perror("setxattr"); | + tst_resm(TINFO, "%d\n", whichcap); | continue; | } | - /* do a sanity check */ | - cap_clear(cap); | - for (i=0; i<32; i++) { | - if (i != 8) { | - capvalue[0] = i; | - cap_set_flag(cap, CAP_INHERITABLE, 1, capvalue, CAP_SET); | - } | - } | - capvalue[0] = whichcap; | - cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); | - cap_set_flag(cap, CAP_EFFECTIVE, 1, capvalue, CAP_SET); | - capstxt = cap_to_text(cap, NULL); | + capstxt = cap_to_text(pcap, NULL); | if (strcmp(capstxt, "=")==0) { | tst_resm(TINFO, "%s: libcap doesn't know about cap %d, not running\n", | __FUNCTION__, whichcap); | ret = 0; | } else | ret = fork_drop_and_exec(KEEP_PERMS, capstxt); | + cap_free(capstxt); | if (ret) { | tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d " | "CAP_EFFECTIVE=1\n", whichcap); | @@ -387,31 +354,25 @@ int caps_actually_set_test(void) | } | | /* | - * bit is in fI but not in pI | - * So pP' is empty. | - * pE' must be empty. | + * fI=fE=whichcap, fP=0 (so fcap is same as before) | + * pI=0 (achieved using DROP_PERMS) | + * pI'=pP'=pE'=0 | */ | - value[0] = cpu_to_le32(magic | VFS_CAP_FLAGS_EFFECTIVE); | - value[2] = cpu_to_le32(1 << whichcap); | - ret = setxattr(TSTPATH, CAPNAME, value, 3*sizeof(unsigned int), 0); | - if (ret) { | - tst_resm(TINFO, "%d %d\n", whichset, whichcap); | - perror("setxattr"); | - continue; | - } | - /* do a sanity check */ | - cap_clear(cap); | - capstxt = cap_to_text(cap, NULL); | + cap_clear(pcap); | + capstxt = cap_to_text(pcap, NULL); | ret = fork_drop_and_exec(DROP_PERMS, capstxt); | + cap_free(capstxt); | if (ret) { | tst_resm(TINFO, "Failed without_perms CAP_INHERITABLE=%d", | whichcap); | if (!finalret) | finalret = ret; | } | + | + cap_free(fcap); | + cap_free(pcap); | } | | - cap_free(cap); | return finalret; | } | | -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.6 (GNU/Linux) iD8DBQFHz5D5+bHCR3gb8jsRAkC/AKDW+9WkkNwYNU6IEFjxjLYAQjTJ/wCdGVzD 8yrHzTHzGXzC3LrUoL4eqVI= =qyn6 -----END PGP SIGNATURE----- |