Index: bin/cat/cat.c =================================================================== --- bin/cat/cat.c +++ bin/cat/cat.c @@ -155,7 +155,7 @@ err(EXIT_FAILURE, "unable to create Casper"); fa = fileargs_cinit(casper, argc, argv, O_RDONLY, 0, - cap_rights_init(&rights, CAP_READ | CAP_FSTAT | CAP_FCNTL), + cap_rights_init(&rights, CAP_READ | CAP_FSTAT | CAP_FCNTL | CAP_SEEK), FA_OPEN | FA_REALPATH); if (fa == NULL) err(EXIT_FAILURE, "unable to create fileargs"); Index: contrib/capsicum-test/copy_file_range.cc =================================================================== --- /dev/null +++ contrib/capsicum-test/copy_file_range.cc @@ -0,0 +1,159 @@ +#include +#include + +#include + +#include "capsicum.h" +#include "capsicum-test.h" +#include "syscalls.h" + +#define TOPDIR "cap_copy_file_range" +#define INFILE "infile" +#define OUTFILE "outfile" + +/* Test that copy_file_range() checks capabilities correctly. + * When used without offset arguments, copy_file_range() should + * require only CAP_READ on the source and CAP_WRITE on the destination + * file descriptors, respectively. + * When used with offset arguments, copy_file_range() should + * additionally require CAP_SEEK. + */ +class CopyFileRangeTest : public ::testing::Test { + public: + CopyFileRangeTest() { + int rc = mkdir(TmpFile(TOPDIR), 0755); + EXPECT_OK(rc); + if (rc < 0) { + EXPECT_EQ(EEXIST, errno); + } + wd_ = open(TmpFile(TOPDIR), O_DIRECTORY); + EXPECT_OK(wd_); + CreateFile(TmpFile(TOPDIR "/" INFILE)); + CreateFile(TmpFile(TOPDIR "/" OUTFILE)); + } + ~CopyFileRangeTest() { + close(wd_); + unlink(TmpFile(TOPDIR "/" INFILE)); + unlink(TmpFile(TOPDIR "/" OUTFILE)); + rmdir(TmpFile(TOPDIR)); + } + + private: + void CreateFile(const char *filename) { + int fd = open(filename, O_CREAT|O_RDWR, 0644); + const char *contents = "lorem ipsum dolor sit amet"; + EXPECT_OK(fd); + for (int i = 0; i < 100; i++) { + EXPECT_OK(write(fd, contents, strlen(contents))); + } + close(fd); + } + + protected: + int wd_; + + int openInFile(cap_rights_t *rights) { + int fd = openat(wd_, INFILE, O_RDONLY); + EXPECT_OK(fd); + EXPECT_OK(cap_rights_limit(fd, rights)); + return fd; + } + int openOutFile(cap_rights_t *rights) { + int fd = openat(wd_, OUTFILE, O_WRONLY); + EXPECT_OK(fd); + EXPECT_OK(cap_rights_limit(fd, rights)); + return fd; + } +}; + +TEST_F(CopyFileRangeTest, ReadWrite) { + cap_rights_t rights_in, rights_out; + + cap_rights_init(&rights_in, CAP_READ); + cap_rights_init(&rights_out, CAP_WRITE); + + int fd_in = openInFile(&rights_in); + int fd_out = openOutFile(&rights_out); + off_t off_in = 0, off_out = 0; + + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + off_in = 20; + off_out = 20; + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + close(fd_in); + close(fd_out); +} + +TEST_F(CopyFileRangeTest, ReadSeekWrite) { + cap_rights_t rights_in, rights_out; + + cap_rights_init(&rights_in, CAP_READ, CAP_SEEK); + cap_rights_init(&rights_out, CAP_WRITE); + + int fd_in = openInFile(&rights_in); + int fd_out = openOutFile(&rights_out); + off_t off_in = 0, off_out = 0; + + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + off_in = 20; + off_out = 20; + EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + close(fd_in); + close(fd_out); +} + +TEST_F(CopyFileRangeTest, ReadWriteSeek) { + cap_rights_t rights_in, rights_out; + + cap_rights_init(&rights_in, CAP_READ); + cap_rights_init(&rights_out, CAP_WRITE, CAP_SEEK); + + int fd_in = openInFile(&rights_in); + int fd_out = openOutFile(&rights_out); + off_t off_in = 0, off_out = 0; + + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + off_in = 20; + off_out = 20; + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_NOTCAPABLE(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + close(fd_in); + close(fd_out); +} + +TEST_F(CopyFileRangeTest, ReadSeekWriteSeek) { + cap_rights_t rights_in, rights_out; + + cap_rights_init(&rights_in, CAP_READ, CAP_SEEK); + cap_rights_init(&rights_out, CAP_WRITE, CAP_SEEK); + + int fd_in = openInFile(&rights_in); + int fd_out = openOutFile(&rights_out); + off_t off_in = 0, off_out = 0; + + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, NULL, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + off_in = 20; + off_out = 20; + EXPECT_OK(copy_file_range(fd_in, NULL, fd_out, &off_out, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, NULL, 8, 0)); + EXPECT_OK(copy_file_range(fd_in, &off_in, fd_out, &off_out, 8, 0)); + close(fd_in); + close(fd_out); +} Index: contrib/capsicum-test/makefile =================================================================== --- contrib/capsicum-test/makefile +++ contrib/capsicum-test/makefile @@ -1,5 +1,5 @@ all: capsicum-test smoketest mini-me mini-me.noexec mini-me.setuid $(EXTRA_PROGS) -OBJECTS=capsicum-test-main.o capsicum-test.o capability-fd.o fexecve.o procdesc.o capmode.o fcntl.o ioctl.o openat.o sysctl.o select.o mqueue.o socket.o sctp.o capability-fd-pair.o linux.o overhead.o rename.o +OBJECTS=capsicum-test-main.o capsicum-test.o capability-fd.o copy_file_range.o fexecve.o procdesc.o capmode.o fcntl.o ioctl.o openat.o sysctl.o select.o mqueue.o socket.o sctp.o capability-fd-pair.o linux.o overhead.o rename.o GTEST_DIR=gtest-1.10.0 GTEST_INCS=-I$(GTEST_DIR)/include -I$(GTEST_DIR) Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -4879,6 +4879,7 @@ size_t retlen; void *rl_rcookie, *rl_wcookie; off_t savinoff, savoutoff; + cap_rights_t *incap, *outcap; infp = outfp = NULL; rl_rcookie = rl_wcookie = NULL; @@ -4898,8 +4899,16 @@ */ len = SSIZE_MAX; + incap = &cap_read_rights; + if (inoffp != NULL) { + incap = &cap_pread_rights; + } + outcap = &cap_write_rights; + if (outoffp != NULL) { + outcap = &cap_pwrite_rights; + } /* Get the file structures for the file descriptors. */ - error = fget_read(td, infd, &cap_read_rights, &infp); + error = fget_read(td, infd, incap, &infp); if (error != 0) goto out; if (infp->f_ops == &badfileops) { @@ -4910,7 +4919,7 @@ error = EINVAL; goto out; } - error = fget_write(td, outfd, &cap_write_rights, &outfp); + error = fget_write(td, outfd, outcap, &outfp); if (error != 0) goto out; if (outfp->f_ops == &badfileops) {