Just fyi, here are the ltp tests I try to use to test file capabilities. It's a patch against the 20070430 ltp release, though it won't actually be hooked in until i write a reliable little test for whether fscaps are in the kernel and supported by userspace so compilation and testing don't erroneously fail.
So for now to use this cd ltp/testcases/kernel/security/filecaps make noltp_check thanks, -serge diff -Nrup ltp-full-20070430/runltp ltp-full-20070430-filecaps/runltp --- ltp-full-20070430/runltp 2007-04-26 13:02:48.000000000 +0200 +++ ltp-full-20070430-filecaps/runltp 2007-05-23 00:32:02.000000000 +0200 @@ -284,7 +284,8 @@ main() ${LTPROOT}/runtest/mm ${LTPROOT}/runtest/ipc \ ${LTPROOT}/runtest/sched ${LTPROOT}/runtest/math \ ${LTPROOT}/runtest/nptl ${LTPROOT}/runtest/pty \ - ${LTPROOT}/runtest/containers + ${LTPROOT}/runtest/containers \ + ${LTPROOT}/runtest/filecaps do [ -a "$SCENFILES" ] || \ { diff -Nrup ltp-full-20070430/runtest/filecaps ltp-full-20070430-filecaps/runtest/filecaps --- ltp-full-20070430/runtest/filecaps 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/runtest/filecaps 2007-05-23 00:04:33.000000000 +0200 @@ -0,0 +1,2 @@ +#DESCRIPTION:file capabilities +Filecaps filecapstest.sh diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/Makefile ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/Makefile --- ltp-full-20070430/testcases/kernel/security/filecaps/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/Makefile 2007-11-17 03:47:34.000000000 +0100 @@ -0,0 +1,33 @@ +CC=gcc + +CFLAGS += -I../../../../include -Wall +LDLIBS += -L../../../../lib -lltp -lcap + +SRCS = $(wildcard *.c) +TARGETS = $(patsubst %.c,%,$(SRCS)) +NOLTP_TARGETS = $(patsubst %.c,%_noltp,$(SRCS)) +SIGTARGETS = fcap_executable suid_executable fcapsuid_executable +NOLTP_SIGTARGETS = fcap_executable_noltp suid_executable_noltp fcapsuid_executable_noltp + +%_noltp : %.c + $(CC) -g -DNO_LTP -o $@ $< -lcap + cp plain_executable_noltp fcap_executable_noltp + setfcaps -c cap_sys_admin=p -e fcap_executable_noltp + cp plain_executable_noltp suid_executable_noltp + chmod u+s suid_executable_noltp + cp plain_executable_noltp fcapsuid_executable_noltp + chmod u+s fcapsuid_executable_noltp + setfcaps -c cap_sys_admin=p -e fcap_executable_noltp + +all: $(TARGETS) $(SIGTARGETS) + +noltp: $(NOLTP_TARGETS) $(NOLTP_SIGTARGETS) + +clean: + rm -f $(TARGETS) *.o $(NOLTP_TARGETS) caps_fifo $(SIGTARGETS) $(NOLTP_SIGTARGETS) + +install: + @set -e; for i in $(TARGETS) $(SIGTARGETS) filecapstest.sh; do ln -f $$i ../../../bin/$$i ; chmod +x ../../../bin/$$i; done + +noltp_check: noltp + ./runtests_noltp.sh diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/filecapstest.sh ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/filecapstest.sh --- ltp-full-20070430/testcases/kernel/security/filecaps/filecapstest.sh 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/filecapstest.sh 2007-11-17 03:50:57.000000000 +0100 @@ -0,0 +1,29 @@ +#!/bin/sh + +echo "Running in:" +cp $LTPROOT/testcases/bin/print_caps . +mkfifo caps_fifo +chmod 777 caps_fifo +exit_code=0 +echo "cap_sys_admin tests" +testfilecaps 0 +tmp=$? +if [ $tmp -ne 0 ]; then + exit_code=$tmp +fi +echo "testing for correct caps" +testfilecaps 1 +tmp=$? +if [ $tmp -ne 0 ]; then + exit_code=$tmp +fi + +for i in `seq 1 10`; do + ./signals_noltp $i + tmp=$? + if [ $tmp -ne 0 ]; then + exit_code=$tmp; + fi +done + +exit $exit_code diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/plain_executable.c ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/plain_executable.c --- ltp-full-20070430/testcases/kernel/security/filecaps/plain_executable.c 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/plain_executable.c 2007-11-17 00:24:15.000000000 +0100 @@ -0,0 +1,44 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <attr/xattr.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/capability.h> +#include <sys/prctl.h> + +#ifdef NO_LTP +#define TFAIL "FAILURE: " +#define TPASS "PASS: " +#define TINFO "INFO: " +#define tst_resm(x, format, arg...) printf("%s:" format, x, ## arg) +#define tst_exit(x) exit(x) +#define TSTPATH "./print_caps_noltp" +#else +#define TSTPATH "./print_caps" +#include <test.h> +char *TCID = "filecaps"; +int TST_TOTAL=1; +#endif + +#define GOT_SIGNAL 1 +#define NO_SIGNAL 2 + +void sighandler(int sig) +{ + exit(GOT_SIGNAL); +} + +int main(int argc, char *argv[]) +{ + signal(SIGUSR1, sighandler); + signal(SIGCONT, sighandler); + sleep(5); + exit(NO_SIGNAL); +} diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/print_caps.c ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/print_caps.c --- ltp-full-20070430/testcases/kernel/security/filecaps/print_caps.c 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/print_caps.c 2007-10-15 23:58:40.000000000 +0200 @@ -0,0 +1,50 @@ +/* + * File: print_caps.c + * Author: Serge Hallyn + * Copyright 2007 IBM Corp + * Purpose: print out the POSIX capabilities with which it runs + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/capability.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define FIFOFILE "caps_fifo" +//#define DEBUG 1 + +int main(int argc, char *argv[]) +{ + cap_t cap = cap_get_proc(); + int fd; + int seqno = 0; + char buf[2000]; + + if (argc>1) + seqno = atoi(argv[1]); + + if (!cap) { + perror("print_caps - cap_get_proc"); + exit(1); + } + + fd = open(FIFOFILE, O_WRONLY); + if (!fd) { + perror("print_caps: open fifo"); + exit(2); + } + + snprintf(buf, 2000, "%d.%s", seqno, cap_to_text(cap, NULL)); + write(fd, buf, strlen(buf)+1); + close(fd); +#ifdef DEBUG + printf("%s: running with caps %s\n", argv[0], cap_to_text(cap, NULL)); +#endif + + cap_free(cap); + + return 0; +} diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/runtests_noltp.sh ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/runtests_noltp.sh --- ltp-full-20070430/testcases/kernel/security/filecaps/runtests_noltp.sh 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/runtests_noltp.sh 2007-11-17 00:17:10.000000000 +0100 @@ -0,0 +1,29 @@ +#!/bin/sh + +mkfifo caps_fifo +chmod 777 caps_fifo +exit_code=0 +echo "cap_sys_admin tests" +./testfilecaps_noltp 0 +tmp=$? +if [ $tmp -ne 0 ]; then + exit_code=$tmp +fi +echo "testing for correct caps" +./testfilecaps_noltp 1 +tmp=$? +if [ $tmp -ne 0 ]; then + exit_code=$tmp +fi + +rm caps_fifo + +for i in `seq 1 10`; do + ./signals_noltp $i + tmp=$? + if [ $tmp -ne 0 ]; then + exit_code=$tmp; + fi +done + +exit $exit_code diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/signals.c ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/signals.c --- ltp-full-20070430/testcases/kernel/security/filecaps/signals.c 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/signals.c 2007-11-17 00:33:06.000000000 +0100 @@ -0,0 +1,408 @@ +/* + * File: signals.c + * Author: Serge Hallyn + * Copyright 2007 IBM Corp + * Purpose: test signal sending authorization for file capabilities + */ + +/* + * Tests: + * 1. chown(500); fork; P1:exec; P2:send SIGUSR1 to P1 + * should succeed + * 2. chown(500); fork; P1: exec with filecaps; P2: send SIGUSR1 to P1 + * should fail + * 3. chown(500); fork; P1: exec with filecaps; P2: send SIGCONT to P1 + * should succeed + * 4. chown(500); fork; P1: setsid(); exec with filecaps; + * P2: send SIGCONT to P1 + * should fail + * 5. chown(500); fork; P1: exec setuid(0) program; P2: send SIGUSR1 to P1 + * should fail + * 6. chown(500); fork; P1: exec setuid(0) program; P2: send SIGCONT to P1 + * should succeed + * 7. chown(500); fork; P1: exec setuid(501)+filecap program; + * P2: send SIGUSR1 to P1 + * should fail + * 8. chown(500); fork; P1: exec setuid(501)+filecap program; + * P2: send SIGCONT to P1 + * should succeed + * 9. chown(500); fork; P1: setsid(); exec setuid(501)+filecap program; + * P2: send SIGUSR1 to P1 + * should fail + * 10. chown(500); fork; P1: setsid(); exec setuid(501)+filecap program; + * P2: send SIGCONT to P1 + * should fail + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <attr/xattr.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/capability.h> +#include <sys/prctl.h> + +#ifdef NO_LTP +#define FNAM_PLAINFILE "./plain_executable_noltp" +#define FNAM_FCAPFILE "./fcap_executable_noltp" +#define FNAM_SUIDFILE "./suid_executable_noltp" +#define FNAM_FCAPSUIDFILE "./fcapsuid_executable_noltp" +#define TFAIL "FAILURE: " +#define TPASS "PASS: " +#define TINFO "INFO: " +#define tst_resm(x, format, arg...) printf("%s:" format, x, ## arg) +#define tst_exit(x) exit(x) +#else +#define FNAM_PLAINFILE "./plain_executable" +#define FNAM_FCAPFILE "./fcap_executable" +#define FNAM_SUIDFILE "./suid_executable" +#define FNAM_FCAPSUIDFILE "./fcapsuid_executable" +#include <test.h> +char *TCID = "filecaps"; +int TST_TOTAL=1; +#endif + +int errno; + +void usage(char *me) +{ + tst_resm(TFAIL, "Usage: %s <test_num>\n", me); + tst_resm(TFAIL, " test_num is between 1 and 10 inclusive\n"); + tst_exit(1); +} + +#define GOT_SIGNAL 1 +#define NO_SIGNAL 2 + +int P1(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_PLAINFILE, FNAM_PLAINFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGUSR1); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TPASS, "test 1 got signal\n"); + tst_exit(0); + } else if (status == NO_SIGNAL) { + tst_resm(TFAIL, "test 1 no signal\n"); + tst_exit(1); + } else { + tst_resm(TFAIL, "test 1 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P2(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_FCAPFILE, FNAM_FCAPFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGUSR1); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TFAIL, "test 2 got signal\n"); + tst_exit(1); + } else if (status == NO_SIGNAL) { + tst_resm(TPASS, "test 2 no signal\n"); + tst_exit(0); + } else { + tst_resm(TFAIL, "test 2 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P3(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_FCAPFILE, FNAM_FCAPFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGCONT); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TPASS, "test 3 got signal\n"); + tst_exit(0); + } else if (status == NO_SIGNAL) { + tst_resm(TFAIL, "test 3 no signal\n"); + tst_exit(1); + } else { + tst_resm(TFAIL, "test 3 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P4(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + setsid(); + execl(FNAM_FCAPFILE, FNAM_FCAPFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGCONT); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TFAIL, "test 4 got signal\n"); + tst_exit(1); + } else if (status == NO_SIGNAL) { + tst_resm(TPASS, "test 4 no signal\n"); + tst_exit(0); + } else { + tst_resm(TFAIL, "test 4 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P5(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_SUIDFILE, FNAM_SUIDFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGUSR1); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TFAIL, "test 5 got signal\n"); + tst_exit(1); + } else if (status == NO_SIGNAL) { + tst_resm(TPASS, "test 5 no signal\n"); + tst_exit(0); + } else { + tst_resm(TFAIL, "test 5 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P6(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_SUIDFILE, FNAM_SUIDFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGCONT); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TPASS, "test 6 got signal\n"); + tst_exit(0); + } else if (status == NO_SIGNAL) { + tst_resm(TFAIL, "test 6 no signal\n"); + tst_exit(1); + } else { + tst_resm(TFAIL, "test 6 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P7(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_FCAPSUIDFILE, FNAM_FCAPSUIDFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGUSR1); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TFAIL, "test 7 got signal\n"); + tst_exit(1); + } else if (status == NO_SIGNAL) { + tst_resm(TPASS, "test 7 no signal\n"); + tst_exit(0); + } else { + tst_resm(TFAIL, "test 7 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P8(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + execl(FNAM_FCAPSUIDFILE, FNAM_FCAPSUIDFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGCONT); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TPASS, "test 8 got signal\n"); + tst_exit(0); + } else if (status == NO_SIGNAL) { + tst_resm(TFAIL, "test 8 no signal\n"); + tst_exit(1); + } else { + tst_resm(TFAIL, "test 8 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P9(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + setsid(); + execl(FNAM_FCAPSUIDFILE, FNAM_FCAPSUIDFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGUSR1); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TFAIL, "test 9 got signal\n"); + tst_exit(1); + } else if (status == NO_SIGNAL) { + tst_resm(TPASS, "test 9 no signal\n"); + tst_exit(0); + } else { + tst_resm(TFAIL, "test 9 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int P10(void) +{ + int status; + int pid = fork(); + + if (pid < 0) { + tst_resm(TFAIL, "failed to fork\n"); + tst_exit(1); + } + if (pid == 0) { + setsid(); + execl(FNAM_FCAPSUIDFILE, FNAM_FCAPSUIDFILE, NULL); + tst_resm(TFAIL, "failed to exec\n"); + tst_exit(1); + } + sleep(1); + kill(pid, SIGCONT); + waitpid(pid, &status, 0); + status = WEXITSTATUS(status); + if (status == GOT_SIGNAL) { + tst_resm(TFAIL, "test 10 got signal\n"); + tst_exit(1); + } else if (status == NO_SIGNAL) { + tst_resm(TPASS, "test 10 no signal\n"); + tst_exit(0); + } else { + tst_resm(TFAIL, "test 10 got bogus result(%d)\n", status); + tst_exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int tstnum; + + setuid(500); + if (argc < 2) + usage(argv[0]); + tstnum = atoi(argv[1]); + switch(tstnum) { + case 1: P1(); + case 2: P2(); + case 3: P3(); + case 4: P4(); + case 5: P5(); + case 6: P6(); + case 7: P7(); + case 8: P8(); + case 9: P9(); + case 10: P10(); + default: usage(argv[0]); + } +} diff -Nrup ltp-full-20070430/testcases/kernel/security/filecaps/testfilecaps.c ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/testfilecaps.c --- ltp-full-20070430/testcases/kernel/security/filecaps/testfilecaps.c 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testcases/kernel/security/filecaps/testfilecaps.c 2007-11-05 23:14:22.000000000 +0100 @@ -0,0 +1,423 @@ +/* + * File: testfscaps.c + * Author: Serge Hallyn + * Copyright 2007 IBM Corp + * Purpose: perform several tests of file capabilities: + * 1. try setting caps without CAP_SYS_ADMIN + * 2. try setting valid caps, drop rights, and run the executable, + * make sure we get the rights + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#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> + +#ifdef NO_LTP +#define TFAIL "FAILURE: " +#define TPASS "PASS: " +#define TINFO "INFO: " +#define tst_resm(x, format, arg...) printf("%s:" format, x, ## arg) +#define tst_exit(x) exit(x) +#define TSTPATH "./print_caps_noltp" +#else +#define TSTPATH "./print_caps" +#include <test.h> +char *TCID = "filecaps"; +int TST_TOTAL=1; +#endif + +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, " 1: test that file caps are set correctly on exec\n"); + tst_exit(1); +} + +#define DROP_PERMS 0 +#define KEEP_PERMS 1 + +void print_my_caps() +{ + cap_t cap = cap_get_proc(); + tst_resm(TINFO, "\ncaps are %s\n", cap_to_text(cap, NULL)); +} + +int drop_root(int keep_perms) +{ + int ret; + + if (keep_perms) + prctl(PR_SET_KEEPCAPS, 1); + ret = setresuid(1000, 1000, 1000); + if (ret) { + perror("setresuid"); + tst_resm(TFAIL, "Error dropping root privs\n"); + tst_exit(4); + } + if (keep_perms) { + cap_t cap = cap_from_text("=eip cap_setpcap-eip"); + cap_set_proc(cap); + } + + return 1; +} + +#if BYTE_ORDER == LITTLE_ENDIAN +#define cpu_to_le32(x) x +#else +#define cpu_to_le32(x) bswap_32(x) +#endif + +#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; + + 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); + if (ret) { + perror("setxattr"); + tst_resm(TPASS, "could not set capabilities as non-root\n"); + ret = 0; + } else { + tst_resm(TFAIL, "could set capabilities as non-root\n"); + ret = 1; + } + + return ret; +} + +#define FIFOFILE "caps_fifo" +void create_fifo(void) +{ + int ret; + + ret = mkfifo(FIFOFILE, S_IRWXU | S_IRWXG | S_IRWXO); + if (ret == -1 && errno != EEXIST) { + perror("mkfifo"); + tst_resm(TFAIL, "failed creating %s\n", FIFOFILE); + tst_exit(1); + } +} + +void write_to_fifo(char *buf) +{ + int fd; + + fd = open(FIFOFILE, O_WRONLY); + write(fd, buf, strlen(buf)); + close(fd); +} + +void read_from_fifo(char *buf) +{ + int fd; + + memset(buf, 0, 200); + fd = open(FIFOFILE, O_RDONLY); + if (fd < 0) { + perror("open"); + tst_resm(TFAIL, "Failed opening fifo\n"); + tst_exit(1); + } + read(fd, buf, 199); + close(fd); +} + +int compare_caps(char *buf1, char *buf2) +{ + int res; + + res = strcmp(buf1, buf2) == 0; + return res; +} + +int fork_drop_and_exec(int keepperms, char *capstxt) +{ + int pid; + int ret = 0; + char buf[200], *p; + static int seqno = 0; + + //printf("execing with %s\n", capstxt); + pid = fork(); + if (pid < 0) { + perror("fork"); + tst_resm(TFAIL, "%s: failed fork\n", __FUNCTION__); + tst_exit(1); + } + if (pid == 0) { + drop_root(keepperms); + print_my_caps(); + sprintf(buf, "%d", seqno); + ret = execlp(TSTPATH, TSTPATH, buf, NULL); + perror("execl"); + tst_resm(TFAIL, "%s: exec failed\n", __FUNCTION__); + snprintf(buf, 200, "failed to run as %s\n", capstxt); + write_to_fifo(buf); + tst_exit(1); + } else { + p = buf; + while (1) { + int c, s; + read_from_fifo(buf); + c = sscanf(buf, "%d", &s); + if (c==1 && s==seqno) + break; + printf("got a bad seqno (c=%d, s=%d, seqno=%d)", + c, s, seqno); + } + p = index(buf, '.')+1; + if (p==(char *)1) { + tst_resm(TFAIL, "got a bad message from print_caps\n"); + tst_exit(1); + } + tst_resm(TINFO, "Expected to run as .%s., ran as .%s..\n", + capstxt, p); + if (strcmp(p, capstxt) != 0) { + tst_resm(TINFO, "those are not the same\n"); + ret = -1; + } + seqno++; + } + return ret; +} + +int caps_actually_set_test(void) +{ + int i, whichset, whichcap, finalret = 0, ret; + cap_t cap; + char *capstxt; + unsigned int value[3]; + cap_value_t capvalue[1]; + unsigned int magic; + + magic = VFS_CAP_REVISION; + + cap = cap_init(); + if (!cap) { + perror("cap_init"); + exit(2); + } + + 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 */ + 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); + if (ret) { + tst_resm(TINFO, "%d %d\n", whichset, whichcap); + perror("setxattr"); + continue; + } + /* do a sanity check */ + cap_clear(cap); + cap_set_flag(cap, CAP_PERMITTED, 1, capvalue, CAP_SET); + capstxt = cap_to_text(cap, NULL); + ret = fork_drop_and_exec(DROP_PERMS, capstxt); + if (ret) { + tst_resm(TINFO, "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=0\n", + whichcap); + 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); + if (ret) { + tst_resm(TINFO, "%d %d\n", whichset, whichcap); + perror("setxattr"); + 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); + 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); + if (ret) { + tst_resm(TINFO, "Failed CAP_PERMITTED=%d CAP_EFFECTIVE=1\n", + whichcap); + if (!finalret) + finalret = ret; + } + } + + + /* + * next try each bit in fI + * The first two attemps have the bit which is in fI in pI. + * This should result in the bit being in pP'. + * If fE was set then it should also be in pE'. + * The last attempt starts with an empty pI. + * 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; + /* + * bit is in fI and pI, so should be in pI'. + * but fE=0, so cap is in pP' but not pE'. + */ + value[0] = cpu_to_le32(magic); + 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); + 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); + ret = fork_drop_and_exec(KEEP_PERMS, capstxt); + if (ret) { + tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d " + "CAP_EFFECTIVE=0\n", whichcap); + 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'. + */ + + 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); + 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); + 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); + if (ret) { + tst_resm(TINFO, "Failed with_perms CAP_INHERITABLE=%d " + "CAP_EFFECTIVE=1\n", whichcap); + if (!finalret) + finalret = ret; + } + + /* + * bit is in fI but not in pI + * So pP' is empty. + * pE' must be empty. + */ + 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); + ret = fork_drop_and_exec(DROP_PERMS, capstxt); + if (ret) { + tst_resm(TINFO, "Failed without_perms CAP_INHERITABLE=%d", + whichcap); + if (!finalret) + finalret = ret; + } + } + + cap_free(cap); + return finalret; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + if (argc < 2) + usage(argv[0]); + + switch(atoi(argv[1])) { + case 0: + ret = perms_test(); + break; + case 1: + ret = caps_actually_set_test(); + if (ret) + tst_resm(TFAIL, "Some tests failed\n"); + else + tst_resm(TPASS, "All tests passed\n"); + break; + default: usage(argv[0]); + } + + tst_exit(ret); +} diff -Nrup ltp-full-20070430/testscripts/test_filecaps.sh ltp-full-20070430-filecaps/testscripts/test_filecaps.sh --- ltp-full-20070430/testscripts/test_filecaps.sh 1970-01-01 01:00:00.000000000 +0100 +++ ltp-full-20070430-filecaps/testscripts/test_filecaps.sh 2007-05-23 00:00:10.000000000 +0200 @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Copyright 2007 IBM +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# test_filecaps.sh - Run the file capabilities test suite. + +# Must be root to run the containers testsuite +if [ $UID != 0 ] +then + echo "FAILED: Must be root to execute this script" + exit 1 +fi + +# set the LTPROOT directory +cd `dirname $0` +LTPROOT=${PWD} +echo $LTPROOT | grep testscripts > /dev/null 2>&1 +if [ $? -eq 0 ] +then + cd .. + LTPROOT=${PWD} +fi + +# set the PATH to include testcase/bin + +export PATH=$PATH:/usr/sbin:$LTPROOT/testcases/bin +export LTPBIN=$LTPROOT/testcases/bin + +# We will store the logfiles in $LTPROOT/results, so make sure +# it exists. +if [ ! -d $LTPROOT/results ] +then + mkdir $LTPROOT/results +fi + +# Check the role and mode testsuite is being executed under. +echo "Running the file capabilities testsuite..." + +$LTPROOT/pan/pan -S -a $LTPROOT/results/filecaps -n ltp-filecaps -l $LTPROOT/results/filecaps.logfile -o $LTPROOT/results/filecaps.outfile -p -f $LTPROOT/runtest/filecaps + +echo "Done." +exit 0 - To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html