freebsd-dev/ioctl.cc
Enji Cooper e5a5dd6cc4 Import capsicum-test into ^/vendor/google/capsicum-test/dist
The following change imports google/capsicum-test@9333154 from GitHub, omitting
the embedded version of googletest, as well as the incomplete libcasper.

This test suite helps verify capsicum(3) support via functional tests
written in the GoogleTest test framework.

Kernel support for capsicum(4) is tested by side-effect of testing
capsicum(3).

NB: as discussed in a previous [closed] PR [1], the casper(3) tests are
incomplete/buggy and will not pass on FreeBSD. Thus, I have no intention of
integrating them into the build/test on FreeBSD as-is.

The import command used was:
```
curl -L https://github.com/google/capsicum-test/tarball/9333154 | tar --strip-components=1 -xvzf - -C dist/
rm -Rf dist/*/
```

1. https://github.com/google/capsicum-test/pull/26

Reviewed by:	emaste (mentor)
Differential Revision:	https://reviews.freebsd.org/D19261
2019-03-12 01:43:01 +00:00

235 lines
6.9 KiB
C++

// Test that ioctl works in capability mode.
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "capsicum.h"
#include "capsicum-test.h"
// Ensure that ioctl() works consistently for both regular file descriptors and
// capability-wrapped ones.
TEST(Ioctl, Basic) {
cap_rights_t rights_ioctl;
cap_rights_init(&rights_ioctl, CAP_IOCTL);
cap_rights_t rights_many;
cap_rights_init(&rights_many, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_FSTAT, CAP_FSYNC);
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
int fd_no = dup(fd);
EXPECT_OK(fd_no);
EXPECT_OK(cap_rights_limit(fd, &rights_ioctl));
EXPECT_OK(cap_rights_limit(fd_no, &rights_many));
// Check that CAP_IOCTL is required.
int bytes;
EXPECT_OK(ioctl(fd, FIONREAD, &bytes));
EXPECT_NOTCAPABLE(ioctl(fd_no, FIONREAD, &bytes));
int one = 1;
EXPECT_OK(ioctl(fd, FIOCLEX, &one));
EXPECT_NOTCAPABLE(ioctl(fd_no, FIOCLEX, &one));
close(fd);
close(fd_no);
}
#ifdef HAVE_CAP_IOCTLS_LIMIT
TEST(Ioctl, SubRightNormalFD) {
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
// Restrict the ioctl(2) subrights of a normal FD.
cap_ioctl_t ioctl_nread = FIONREAD;
EXPECT_OK(cap_ioctls_limit(fd, &ioctl_nread, 1));
int bytes;
EXPECT_OK(ioctl(fd, FIONREAD, &bytes));
int one = 1;
EXPECT_NOTCAPABLE(ioctl(fd, FIOCLEX, &one));
// Expect to have all primary rights.
cap_rights_t rights;
EXPECT_OK(cap_rights_get(fd, &rights));
cap_rights_t all;
CAP_SET_ALL(&all);
EXPECT_RIGHTS_EQ(&all, &rights);
cap_ioctl_t ioctls[16];
memset(ioctls, 0, sizeof(ioctls));
ssize_t nioctls = cap_ioctls_get(fd, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(1, nioctls);
EXPECT_EQ((cap_ioctl_t)FIONREAD, ioctls[0]);
// Can't widen the subrights.
cap_ioctl_t both_ioctls[2] = {FIONREAD, FIOCLEX};
EXPECT_NOTCAPABLE(cap_ioctls_limit(fd, both_ioctls, 2));
close(fd);
}
TEST(Ioctl, PreserveSubRights) {
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
cap_rights_t rights;
cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_IOCTL);
EXPECT_OK(cap_rights_limit(fd, &rights));
cap_ioctl_t ioctl_nread = FIONREAD;
EXPECT_OK(cap_ioctls_limit(fd, &ioctl_nread, 1));
cap_rights_t cur_rights;
cap_ioctl_t ioctls[16];
ssize_t nioctls;
EXPECT_OK(cap_rights_get(fd, &cur_rights));
EXPECT_RIGHTS_EQ(&rights, &cur_rights);
nioctls = cap_ioctls_get(fd, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(1, nioctls);
EXPECT_EQ((cap_ioctl_t)FIONREAD, ioctls[0]);
// Limiting the top-level rights leaves the subrights unaffected...
cap_rights_clear(&rights, CAP_READ);
EXPECT_OK(cap_rights_limit(fd, &rights));
nioctls = cap_ioctls_get(fd, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(1, nioctls);
EXPECT_EQ((cap_ioctl_t)FIONREAD, ioctls[0]);
// ... until we remove CAP_IOCTL
cap_rights_clear(&rights, CAP_IOCTL);
EXPECT_OK(cap_rights_limit(fd, &rights));
nioctls = cap_ioctls_get(fd, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(0, nioctls);
EXPECT_EQ(-1, cap_ioctls_limit(fd, &ioctl_nread, 1));
close(fd);
}
TEST(Ioctl, SubRights) {
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
cap_ioctl_t ioctls[16];
ssize_t nioctls;
memset(ioctls, 0, sizeof(ioctls));
nioctls = cap_ioctls_get(fd, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(CAP_IOCTLS_ALL, nioctls);
cap_rights_t rights_ioctl;
cap_rights_init(&rights_ioctl, CAP_IOCTL);
EXPECT_OK(cap_rights_limit(fd, &rights_ioctl));
nioctls = cap_ioctls_get(fd, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(CAP_IOCTLS_ALL, nioctls);
// Check operations that need CAP_IOCTL with subrights pristine => OK.
int bytes;
EXPECT_OK(ioctl(fd, FIONREAD, &bytes));
int one = 1;
EXPECT_OK(ioctl(fd, FIOCLEX, &one));
// Check operations that need CAP_IOCTL with all relevant subrights => OK.
cap_ioctl_t both_ioctls[2] = {FIONREAD, FIOCLEX};
EXPECT_OK(cap_ioctls_limit(fd, both_ioctls, 2));
EXPECT_OK(ioctl(fd, FIONREAD, &bytes));
EXPECT_OK(ioctl(fd, FIOCLEX, &one));
// Check what happens if we ask for subrights but don't have the space for them.
cap_ioctl_t before = 0xBBBBBBBB;
cap_ioctl_t one_ioctl = 0;
cap_ioctl_t after = 0xAAAAAAAA;
nioctls = cap_ioctls_get(fd, &one_ioctl, 1);
EXPECT_EQ(2, nioctls);
EXPECT_EQ(0xBBBBBBBB, before);
EXPECT_TRUE(one_ioctl == FIONREAD || one_ioctl == FIOCLEX);
EXPECT_EQ(0xAAAAAAAA, after);
// Check operations that need CAP_IOCTL with particular subrights.
int fd_nread = dup(fd);
int fd_clex = dup(fd);
cap_ioctl_t ioctl_nread = FIONREAD;
cap_ioctl_t ioctl_clex = FIOCLEX;
EXPECT_OK(cap_ioctls_limit(fd_nread, &ioctl_nread, 1));
EXPECT_OK(cap_ioctls_limit(fd_clex, &ioctl_clex, 1));
EXPECT_OK(ioctl(fd_nread, FIONREAD, &bytes));
EXPECT_NOTCAPABLE(ioctl(fd_clex, FIONREAD, &bytes));
EXPECT_OK(ioctl(fd_clex, FIOCLEX, &one));
EXPECT_NOTCAPABLE(ioctl(fd_nread, FIOCLEX, &one));
// Also check we can retrieve the subrights.
memset(ioctls, 0, sizeof(ioctls));
nioctls = cap_ioctls_get(fd_nread, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(1, nioctls);
EXPECT_EQ((cap_ioctl_t)FIONREAD, ioctls[0]);
memset(ioctls, 0, sizeof(ioctls));
nioctls = cap_ioctls_get(fd_clex, ioctls, 16);
EXPECT_OK(nioctls);
EXPECT_EQ(1, nioctls);
EXPECT_EQ((cap_ioctl_t)FIOCLEX, ioctls[0]);
// And that we can't widen the subrights.
EXPECT_NOTCAPABLE(cap_ioctls_limit(fd_nread, both_ioctls, 2));
EXPECT_NOTCAPABLE(cap_ioctls_limit(fd_clex, both_ioctls, 2));
close(fd_nread);
close(fd_clex);
// Check operations that need CAP_IOCTL with no subrights => ENOTCAPABLE.
EXPECT_OK(cap_ioctls_limit(fd, NULL, 0));
EXPECT_NOTCAPABLE(ioctl(fd, FIONREAD, &bytes));
EXPECT_NOTCAPABLE(ioctl(fd, FIOCLEX, &one));
close(fd);
}
#ifdef CAP_IOCTLS_LIMIT_MAX
TEST(Ioctl, TooManySubRights) {
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
cap_ioctl_t ioctls[CAP_IOCTLS_LIMIT_MAX + 1];
for (int ii = 0; ii <= CAP_IOCTLS_LIMIT_MAX; ii++) {
ioctls[ii] = ii + 1;
}
cap_rights_t rights_ioctl;
cap_rights_init(&rights_ioctl, CAP_IOCTL);
EXPECT_OK(cap_rights_limit(fd, &rights_ioctl));
// Can only limit to a certain number of ioctls
EXPECT_EQ(-1, cap_ioctls_limit(fd, ioctls, CAP_IOCTLS_LIMIT_MAX + 1));
EXPECT_EQ(EINVAL, errno);
EXPECT_OK(cap_ioctls_limit(fd, ioctls, CAP_IOCTLS_LIMIT_MAX));
close(fd);
}
#else
TEST(Ioctl, ManySubRights) {
int fd = open("/etc/passwd", O_RDONLY);
EXPECT_OK(fd);
const int nioctls = 150000;
cap_ioctl_t* ioctls = (cap_ioctl_t*)calloc(nioctls, sizeof(cap_ioctl_t));
for (int ii = 0; ii < nioctls; ii++) {
ioctls[ii] = ii + 1;
}
cap_rights_t rights_ioctl;
cap_rights_init(&rights_ioctl, CAP_IOCTL);
EXPECT_OK(cap_rights_limit(fd, &rights_ioctl));
EXPECT_OK(cap_ioctls_limit(fd, ioctls, nioctls));
// Limit to a subset; if this takes a long time then there's an
// O(N^2) implementation of the ioctl list comparison.
EXPECT_OK(cap_ioctls_limit(fd, ioctls, nioctls - 1));
close(fd);
}
#endif
#endif