Changeset View
Changeset View
Standalone View
Standalone View
contrib/capsicum-test/openat.cc
Show First 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | #endif | ||||
fd = openat(etc_cap_ro, "passwd", O_RDONLY); | fd = openat(etc_cap_ro, "passwd", O_RDONLY); | ||||
EXPECT_OK(fd); | EXPECT_OK(fd); | ||||
EXPECT_OK(cap_rights_get(fd, &rights)); | EXPECT_OK(cap_rights_get(fd, &rights)); | ||||
EXPECT_RIGHTS_IN(&rights, &r_rl); | EXPECT_RIGHTS_IN(&rights, &r_rl); | ||||
close(fd); | close(fd); | ||||
} | } | ||||
#define TOPDIR "cap_topdir" | #define TOPDIR "cap_topdir" | ||||
#define SUBDIR_ABS TOPDIR "/subdir" | #define SUBDIR TOPDIR "/subdir" | ||||
class OpenatTest : public ::testing::Test { | class OpenatTest : public ::testing::Test { | ||||
public: | public: | ||||
// Build a collection of files, subdirs and symlinks: | // Build a collection of files, subdirs and symlinks: | ||||
// /tmp/cap_topdir/ | // /tmp/cap_topdir/ | ||||
// /topfile | // /topfile | ||||
// /subdir/ | // /subdir/ | ||||
// /subdir/bottomfile | // /subdir/bottomfile | ||||
// /symlink.samedir -> topfile | // /symlink.samedir -> topfile | ||||
// /dsymlink.samedir -> ./ | // /dsymlink.samedir -> ./ | ||||
// /symlink.down -> subdir/bottomfile | // /symlink.down -> subdir/bottomfile | ||||
// /dsymlink.down -> subdir/ | // /dsymlink.down -> subdir/ | ||||
// /symlink.absolute_in -> /tmp/cap_topdir/topfile | |||||
// /dsymlink.absolute_in -> /tmp/cap_topdir/ | |||||
// /symlink.absolute_out -> /etc/passwd | // /symlink.absolute_out -> /etc/passwd | ||||
// /dsymlink.absolute_out -> /etc/ | // /dsymlink.absolute_out -> /etc/ | ||||
// /symlink.relative_in -> ../../tmp/cap_topdir/topfile | // /symlink.relative_in -> ../../tmp/cap_topdir/topfile | ||||
// /dsymlink.relative_in -> ../../tmp/cap_topdir/ | // /dsymlink.relative_in -> ../../tmp/cap_topdir/ | ||||
// /symlink.relative_out -> ../../etc/passwd | // /symlink.relative_out -> ../../etc/passwd | ||||
// /dsymlink.relative_out -> ../../etc/ | // /dsymlink.relative_out -> ../../etc/ | ||||
// /subdir/symlink.up -> ../topfile | // /subdir/dsymlink.absolute_in -> /tmp/cap_topdir/ | ||||
// /subdir/dsymlink.up -> ../ | // /subdir/dsymlink.up -> ../ | ||||
// /subdir/symlink.absolute_in -> /tmp/cap_topdir/topfile | |||||
// /subdir/symlink.up -> ../topfile | |||||
// (In practice, this is a little more complicated because tmpdir might | // (In practice, this is a little more complicated because tmpdir might | ||||
// not be "/tmp".) | // not be "/tmp".) | ||||
OpenatTest() { | OpenatTest() { | ||||
// Create a couple of nested directories | // Create a couple of nested directories | ||||
int rc = mkdir(TmpFile(TOPDIR), 0755); | int rc = mkdir(TmpFile(TOPDIR), 0755); | ||||
EXPECT_OK(rc); | EXPECT_OK(rc); | ||||
if (rc < 0) { | if (rc < 0) { | ||||
EXPECT_EQ(EEXIST, errno); | EXPECT_EQ(EEXIST, errno); | ||||
} | } | ||||
rc = mkdir(TmpFile(SUBDIR_ABS), 0755); | rc = mkdir(TmpFile(SUBDIR), 0755); | ||||
EXPECT_OK(rc); | EXPECT_OK(rc); | ||||
if (rc < 0) { | if (rc < 0) { | ||||
EXPECT_EQ(EEXIST, errno); | EXPECT_EQ(EEXIST, errno); | ||||
} | } | ||||
// Figure out a path prefix (like "../..") that gets us to the root | // Figure out a path prefix (like "../..") that gets us to the root | ||||
// directory from TmpFile(TOPDIR). | // directory from TmpFile(TOPDIR). | ||||
const char *p = TmpFile(TOPDIR); // maybe "/tmp/somewhere/cap_topdir" | const char *p = TmpFile(TOPDIR); // maybe "/tmp/somewhere/cap_topdir" | ||||
std::string dots2root = ".."; | std::string dots2root = ".."; | ||||
while (*p++ != '\0') { | while (*p++ != '\0') { | ||||
if (*p == '/') { | if (*p == '/') { | ||||
dots2root += "/.."; | dots2root += "/.."; | ||||
} | } | ||||
} | } | ||||
// Create normal files in each. | // Create normal files in each. | ||||
CreateFile(TmpFile(TOPDIR "/topfile"), "Top-level file"); | CreateFile(TmpFile(TOPDIR "/topfile"), "Top-level file"); | ||||
CreateFile(TmpFile(SUBDIR_ABS "/bottomfile"), "File in subdirectory"); | CreateFile(TmpFile(SUBDIR "/bottomfile"), "File in subdirectory"); | ||||
// Create various symlinks to files. | // Create various symlinks to files. | ||||
EXPECT_OK(symlink("topfile", TmpFile(TOPDIR "/symlink.samedir"))); | EXPECT_OK(symlink("topfile", TmpFile(TOPDIR "/symlink.samedir"))); | ||||
EXPECT_OK(symlink("subdir/bottomfile", TmpFile(TOPDIR "/symlink.down"))); | EXPECT_OK(symlink("subdir/bottomfile", TmpFile(TOPDIR "/symlink.down"))); | ||||
EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(TOPDIR "/symlink.absolute_in"))); | EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(SUBDIR "/symlink.absolute_in"))); | ||||
EXPECT_OK(symlink("/etc/passwd", TmpFile(TOPDIR "/symlink.absolute_out"))); | EXPECT_OK(symlink("/etc/passwd", TmpFile(TOPDIR "/symlink.absolute_out"))); | ||||
std::string dots2top = dots2root + TmpFile(TOPDIR "/topfile"); | std::string dots2top = dots2root + TmpFile(TOPDIR "/topfile"); | ||||
EXPECT_OK(symlink(dots2top.c_str(), TmpFile(TOPDIR "/symlink.relative_in"))); | EXPECT_OK(symlink(dots2top.c_str(), TmpFile(TOPDIR "/symlink.relative_in"))); | ||||
std::string dots2passwd = dots2root + "/etc/passwd"; | std::string dots2passwd = dots2root + "/etc/passwd"; | ||||
EXPECT_OK(symlink(dots2passwd.c_str(), TmpFile(TOPDIR "/symlink.relative_out"))); | EXPECT_OK(symlink(dots2passwd.c_str(), TmpFile(TOPDIR "/symlink.relative_out"))); | ||||
EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR_ABS "/symlink.up"))); | EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR "/symlink.up"))); | ||||
// Create various symlinks to directories. | // Create various symlinks to directories. | ||||
EXPECT_OK(symlink("./", TmpFile(TOPDIR "/dsymlink.samedir"))); | EXPECT_OK(symlink("./", TmpFile(TOPDIR "/dsymlink.samedir"))); | ||||
EXPECT_OK(symlink("subdir/", TmpFile(TOPDIR "/dsymlink.down"))); | EXPECT_OK(symlink("subdir/", TmpFile(TOPDIR "/dsymlink.down"))); | ||||
EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(TOPDIR "/dsymlink.absolute_in"))); | EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(SUBDIR "/dsymlink.absolute_in"))); | ||||
EXPECT_OK(symlink("/etc/", TmpFile(TOPDIR "/dsymlink.absolute_out"))); | EXPECT_OK(symlink("/etc/", TmpFile(TOPDIR "/dsymlink.absolute_out"))); | ||||
std::string dots2cwd = dots2root + tmpdir + "/"; | std::string dots2cwd = dots2root + tmpdir + "/"; | ||||
EXPECT_OK(symlink(dots2cwd.c_str(), TmpFile(TOPDIR "/dsymlink.relative_in"))); | EXPECT_OK(symlink(dots2cwd.c_str(), TmpFile(TOPDIR "/dsymlink.relative_in"))); | ||||
std::string dots2etc = dots2root + "/etc/"; | std::string dots2etc = dots2root + "/etc/"; | ||||
EXPECT_OK(symlink(dots2etc.c_str(), TmpFile(TOPDIR "/dsymlink.relative_out"))); | EXPECT_OK(symlink(dots2etc.c_str(), TmpFile(TOPDIR "/dsymlink.relative_out"))); | ||||
EXPECT_OK(symlink("../", TmpFile(SUBDIR_ABS "/dsymlink.up"))); | EXPECT_OK(symlink("../", TmpFile(SUBDIR "/dsymlink.up"))); | ||||
// Open directory FDs for those directories and for cwd. | // Open directory FDs for those directories and for cwd. | ||||
dir_fd_ = open(TmpFile(TOPDIR), O_RDONLY); | dir_fd_ = open(TmpFile(TOPDIR), O_RDONLY); | ||||
EXPECT_OK(dir_fd_); | EXPECT_OK(dir_fd_); | ||||
sub_fd_ = open(TmpFile(SUBDIR_ABS), O_RDONLY); | sub_fd_ = open(TmpFile(SUBDIR), O_RDONLY); | ||||
EXPECT_OK(sub_fd_); | EXPECT_OK(sub_fd_); | ||||
cwd_ = openat(AT_FDCWD, ".", O_RDONLY); | cwd_ = openat(AT_FDCWD, ".", O_RDONLY); | ||||
EXPECT_OK(cwd_); | EXPECT_OK(cwd_); | ||||
// Move into the directory for the test. | // Move into the directory for the test. | ||||
EXPECT_OK(fchdir(dir_fd_)); | EXPECT_OK(fchdir(dir_fd_)); | ||||
} | } | ||||
~OpenatTest() { | ~OpenatTest() { | ||||
fchdir(cwd_); | fchdir(cwd_); | ||||
close(cwd_); | close(cwd_); | ||||
close(sub_fd_); | close(sub_fd_); | ||||
close(dir_fd_); | close(dir_fd_); | ||||
unlink(TmpFile(SUBDIR_ABS "/symlink.up")); | unlink(TmpFile(SUBDIR "/symlink.up")); | ||||
unlink(TmpFile(TOPDIR "/symlink.absolute_in")); | unlink(TmpFile(SUBDIR "/symlink.absolute_in")); | ||||
unlink(TmpFile(TOPDIR "/symlink.absolute_out")); | unlink(TmpFile(TOPDIR "/symlink.absolute_out")); | ||||
unlink(TmpFile(TOPDIR "/symlink.relative_in")); | unlink(TmpFile(TOPDIR "/symlink.relative_in")); | ||||
unlink(TmpFile(TOPDIR "/symlink.relative_out")); | unlink(TmpFile(TOPDIR "/symlink.relative_out")); | ||||
unlink(TmpFile(TOPDIR "/symlink.down")); | unlink(TmpFile(TOPDIR "/symlink.down")); | ||||
unlink(TmpFile(TOPDIR "/symlink.samedir")); | unlink(TmpFile(TOPDIR "/symlink.samedir")); | ||||
unlink(TmpFile(SUBDIR_ABS "/dsymlink.up")); | unlink(TmpFile(SUBDIR "/dsymlink.up")); | ||||
unlink(TmpFile(TOPDIR "/dsymlink.absolute_in")); | unlink(TmpFile(SUBDIR "/dsymlink.absolute_in")); | ||||
unlink(TmpFile(TOPDIR "/dsymlink.absolute_out")); | unlink(TmpFile(TOPDIR "/dsymlink.absolute_out")); | ||||
unlink(TmpFile(TOPDIR "/dsymlink.relative_in")); | unlink(TmpFile(TOPDIR "/dsymlink.relative_in")); | ||||
unlink(TmpFile(TOPDIR "/dsymlink.relative_out")); | unlink(TmpFile(TOPDIR "/dsymlink.relative_out")); | ||||
unlink(TmpFile(TOPDIR "/dsymlink.down")); | unlink(TmpFile(TOPDIR "/dsymlink.down")); | ||||
unlink(TmpFile(TOPDIR "/dsymlink.samedir")); | unlink(TmpFile(TOPDIR "/dsymlink.samedir")); | ||||
unlink(TmpFile(SUBDIR_ABS "/bottomfile")); | unlink(TmpFile(SUBDIR "/bottomfile")); | ||||
unlink(TmpFile(TOPDIR "/topfile")); | unlink(TmpFile(TOPDIR "/topfile")); | ||||
rmdir(TmpFile(SUBDIR_ABS)); | rmdir(TmpFile(SUBDIR)); | ||||
rmdir(TmpFile(TOPDIR)); | rmdir(TmpFile(TOPDIR)); | ||||
} | } | ||||
// Check openat(2) policing that is common across capabilities, capability mode and O_BENEATH. | // Check openat(2) policing that is common across capabilities, capability mode and O_BENEATH. | ||||
void CheckPolicing(int oflag) { | void CheckPolicing(int oflag) { | ||||
// OK for normal access. | // OK for normal access. | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "topfile", O_RDONLY|oflag)); | EXPECT_OPEN_OK(openat(dir_fd_, "topfile", O_RDONLY|oflag)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "subdir/bottomfile", O_RDONLY|oflag)); | EXPECT_OPEN_OK(openat(dir_fd_, "subdir/bottomfile", O_RDONLY|oflag)); | ||||
Show All 12 Lines | #endif | ||||
// Check that we can't escape the top directory by the cunning | // Check that we can't escape the top directory by the cunning | ||||
// ruse of going via a subdirectory. | // ruse of going via a subdirectory. | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "subdir/../../etc/passwd", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "subdir/../../etc/passwd", O_RDONLY|oflag); | ||||
// Should only be able to open symlinks that stay within the directory. | // Should only be able to open symlinks that stay within the directory. | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY|oflag)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY|oflag)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY|oflag)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY|oflag)); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_in", O_RDONLY|oflag); | |||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_out", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_out", O_RDONLY|oflag); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_in", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_in", O_RDONLY|oflag); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_out", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_out", O_RDONLY|oflag); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.absolute_in", O_RDONLY|oflag); | |||||
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.up", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.up", O_RDONLY|oflag); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.samedir/topfile", O_RDONLY|oflag)); | EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.samedir/topfile", O_RDONLY|oflag)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.down/bottomfile", O_RDONLY|oflag)); | EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.down/bottomfile", O_RDONLY|oflag)); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag); | |||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_out/passwd", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_out/passwd", O_RDONLY|oflag); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_in/topfile", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_in/topfile", O_RDONLY|oflag); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_out/passwd", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_out/passwd", O_RDONLY|oflag); | ||||
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag); | |||||
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.up/topfile", O_RDONLY|oflag); | EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.up/topfile", O_RDONLY|oflag); | ||||
// Although recall that O_NOFOLLOW prevents symlink following in final component. | // Although recall that O_NOFOLLOW prevents symlink following in final component. | ||||
EXPECT_SYSCALL_FAIL(E_TOO_MANY_LINKS, openat(dir_fd_, "symlink.samedir", O_RDONLY|O_NOFOLLOW|oflag)); | EXPECT_SYSCALL_FAIL(E_TOO_MANY_LINKS, openat(dir_fd_, "symlink.samedir", O_RDONLY|O_NOFOLLOW|oflag)); | ||||
EXPECT_SYSCALL_FAIL(E_TOO_MANY_LINKS, openat(dir_fd_, "symlink.down", O_RDONLY|O_NOFOLLOW|oflag)); | EXPECT_SYSCALL_FAIL(E_TOO_MANY_LINKS, openat(dir_fd_, "symlink.down", O_RDONLY|O_NOFOLLOW|oflag)); | ||||
} | } | ||||
protected: | protected: | ||||
int dir_fd_; | int dir_fd_; | ||||
int sub_fd_; | int sub_fd_; | ||||
int cwd_; | int cwd_; | ||||
}; | }; | ||||
TEST_F(OpenatTest, WithCapability) { | TEST_F(OpenatTest, WithCapability) { | ||||
// Any kind of symlink can be opened relative to an ordinary directory FD. | // Any kind of symlink can be opened relative to an ordinary directory FD. | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_in", O_RDONLY)); | |||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_out", O_RDONLY)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_out", O_RDONLY)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_in", O_RDONLY)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_in", O_RDONLY)); | ||||
EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_out", O_RDONLY)); | EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_out", O_RDONLY)); | ||||
EXPECT_OPEN_OK(openat(sub_fd_, "symlink.absolute_in", O_RDONLY)); | |||||
EXPECT_OPEN_OK(openat(sub_fd_, "symlink.up", O_RDONLY)); | EXPECT_OPEN_OK(openat(sub_fd_, "symlink.up", O_RDONLY)); | ||||
// Now make both DFDs into Capsicum capabilities. | // Now make both DFDs into Capsicum capabilities. | ||||
cap_rights_t r_rl; | cap_rights_t r_rl; | ||||
cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP, CAP_FCHDIR); | cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP, CAP_FCHDIR); | ||||
EXPECT_OK(cap_rights_limit(dir_fd_, &r_rl)); | EXPECT_OK(cap_rights_limit(dir_fd_, &r_rl)); | ||||
EXPECT_OK(cap_rights_limit(sub_fd_, &r_rl)); | EXPECT_OK(cap_rights_limit(sub_fd_, &r_rl)); | ||||
CheckPolicing(0); | CheckPolicing(0); | ||||
Show All 37 Lines |