18ac5aef8SEnji Cooper #include <sys/types.h>
28ac5aef8SEnji Cooper #include <sys/stat.h>
38ac5aef8SEnji Cooper #include <fcntl.h>
48ac5aef8SEnji Cooper #include <sys/ioctl.h>
58ac5aef8SEnji Cooper
68ac5aef8SEnji Cooper #include <string>
78ac5aef8SEnji Cooper
88ac5aef8SEnji Cooper #include "capsicum.h"
98ac5aef8SEnji Cooper #include "capsicum-test.h"
108ac5aef8SEnji Cooper #include "syscalls.h"
118ac5aef8SEnji Cooper
128ac5aef8SEnji Cooper // Check an open call works and close the resulting fd.
138ac5aef8SEnji Cooper #define EXPECT_OPEN_OK(f) do { \
14955a3f9aSAlex Richardson SCOPED_TRACE(#f); \
158ac5aef8SEnji Cooper int _fd = f; \
168ac5aef8SEnji Cooper EXPECT_OK(_fd); \
178ac5aef8SEnji Cooper close(_fd); \
188ac5aef8SEnji Cooper } while (0)
198ac5aef8SEnji Cooper
CreateFile(const char * filename,const char * contents)208ac5aef8SEnji Cooper static void CreateFile(const char *filename, const char *contents) {
218ac5aef8SEnji Cooper int fd = open(filename, O_CREAT|O_RDWR, 0644);
228ac5aef8SEnji Cooper EXPECT_OK(fd);
238ac5aef8SEnji Cooper EXPECT_OK(write(fd, contents, strlen(contents)));
248ac5aef8SEnji Cooper close(fd);
258ac5aef8SEnji Cooper }
268ac5aef8SEnji Cooper
278ac5aef8SEnji Cooper // Test openat(2) in a variety of sitations to ensure that it obeys Capsicum
288ac5aef8SEnji Cooper // "strict relative" rules:
298ac5aef8SEnji Cooper //
308ac5aef8SEnji Cooper // 1. Use strict relative lookups in capability mode or when operating
318ac5aef8SEnji Cooper // relative to a capability.
328ac5aef8SEnji Cooper // 2. When performing strict relative lookups, absolute paths (including
338ac5aef8SEnji Cooper // symlinks to absolute paths) are not allowed, nor are paths containing
348ac5aef8SEnji Cooper // '..' components.
358ac5aef8SEnji Cooper //
368ac5aef8SEnji Cooper // These rules apply when:
378ac5aef8SEnji Cooper // - the directory FD is a Capsicum capability
388ac5aef8SEnji Cooper // - the process is in capability mode
398ac5aef8SEnji Cooper // - the openat(2) operation includes the O_BENEATH flag.
FORK_TEST(Openat,Relative)408ac5aef8SEnji Cooper FORK_TEST(Openat, Relative) {
418ac5aef8SEnji Cooper int etc = open("/etc/", O_RDONLY);
428ac5aef8SEnji Cooper EXPECT_OK(etc);
438ac5aef8SEnji Cooper
448ac5aef8SEnji Cooper cap_rights_t r_base;
458ac5aef8SEnji Cooper cap_rights_init(&r_base, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_LOOKUP, CAP_FCNTL, CAP_IOCTL);
468ac5aef8SEnji Cooper cap_rights_t r_ro;
478ac5aef8SEnji Cooper cap_rights_init(&r_ro, CAP_READ);
488ac5aef8SEnji Cooper cap_rights_t r_rl;
498ac5aef8SEnji Cooper cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);
508ac5aef8SEnji Cooper
518ac5aef8SEnji Cooper int etc_cap = dup(etc);
528ac5aef8SEnji Cooper EXPECT_OK(etc_cap);
538ac5aef8SEnji Cooper EXPECT_OK(cap_rights_limit(etc_cap, &r_ro));
548ac5aef8SEnji Cooper int etc_cap_ro = dup(etc);
558ac5aef8SEnji Cooper EXPECT_OK(etc_cap_ro);
568ac5aef8SEnji Cooper EXPECT_OK(cap_rights_limit(etc_cap_ro, &r_rl));
578ac5aef8SEnji Cooper int etc_cap_base = dup(etc);
588ac5aef8SEnji Cooper EXPECT_OK(etc_cap_base);
598ac5aef8SEnji Cooper EXPECT_OK(cap_rights_limit(etc_cap_base, &r_base));
608ac5aef8SEnji Cooper #ifdef HAVE_CAP_FCNTLS_LIMIT
618ac5aef8SEnji Cooper // Also limit fcntl(2) subrights.
628ac5aef8SEnji Cooper EXPECT_OK(cap_fcntls_limit(etc_cap_base, CAP_FCNTL_GETFL));
638ac5aef8SEnji Cooper #endif
648ac5aef8SEnji Cooper #ifdef HAVE_CAP_IOCTLS_LIMIT
658ac5aef8SEnji Cooper // Also limit ioctl(2) subrights.
668ac5aef8SEnji Cooper cap_ioctl_t ioctl_nread = FIONREAD;
678ac5aef8SEnji Cooper EXPECT_OK(cap_ioctls_limit(etc_cap_base, &ioctl_nread, 1));
688ac5aef8SEnji Cooper #endif
698ac5aef8SEnji Cooper
708ac5aef8SEnji Cooper // openat(2) with regular file descriptors in non-capability mode
718ac5aef8SEnji Cooper // Should Just Work (tm).
728ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc, "/etc/passwd", O_RDONLY));
738ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
748ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc, "passwd", O_RDONLY));
758ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc, "../etc/passwd", O_RDONLY));
768ac5aef8SEnji Cooper
778ac5aef8SEnji Cooper // Lookups relative to capabilities should be strictly relative.
788ac5aef8SEnji Cooper // When not in capability mode, we don't actually require CAP_LOOKUP.
798ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc_cap_ro, "passwd", O_RDONLY));
808ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc_cap_base, "passwd", O_RDONLY));
818ac5aef8SEnji Cooper
828ac5aef8SEnji Cooper // Performing openat(2) on a path with leading slash ignores
838ac5aef8SEnji Cooper // the provided directory FD.
848ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc_cap_ro, "/etc/passwd", O_RDONLY));
858ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc_cap_base, "/etc/passwd", O_RDONLY));
868ac5aef8SEnji Cooper // Relative lookups that go upward are not allowed.
878ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc_cap_ro, "../etc/passwd", O_RDONLY);
888ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc_cap_base, "../etc/passwd", O_RDONLY);
898ac5aef8SEnji Cooper
908ac5aef8SEnji Cooper // A file opened relative to a capability should itself be a capability.
918ac5aef8SEnji Cooper int fd = openat(etc_cap_base, "passwd", O_RDONLY);
928ac5aef8SEnji Cooper EXPECT_OK(fd);
938ac5aef8SEnji Cooper cap_rights_t rights;
948ac5aef8SEnji Cooper EXPECT_OK(cap_rights_get(fd, &rights));
958ac5aef8SEnji Cooper EXPECT_RIGHTS_IN(&rights, &r_base);
968ac5aef8SEnji Cooper #ifdef HAVE_CAP_FCNTLS_LIMIT
978ac5aef8SEnji Cooper cap_fcntl_t fcntls;
988ac5aef8SEnji Cooper EXPECT_OK(cap_fcntls_get(fd, &fcntls));
998ac5aef8SEnji Cooper EXPECT_EQ((cap_fcntl_t)CAP_FCNTL_GETFL, fcntls);
1008ac5aef8SEnji Cooper #endif
1018ac5aef8SEnji Cooper #ifdef HAVE_CAP_IOCTLS_LIMIT
1028ac5aef8SEnji Cooper cap_ioctl_t ioctls[16];
1038ac5aef8SEnji Cooper ssize_t nioctls;
1048ac5aef8SEnji Cooper memset(ioctls, 0, sizeof(ioctls));
1058ac5aef8SEnji Cooper nioctls = cap_ioctls_get(fd, ioctls, 16);
1068ac5aef8SEnji Cooper EXPECT_OK(nioctls);
1078ac5aef8SEnji Cooper EXPECT_EQ(1, nioctls);
1088ac5aef8SEnji Cooper EXPECT_EQ((cap_ioctl_t)FIONREAD, ioctls[0]);
1098ac5aef8SEnji Cooper #endif
1108ac5aef8SEnji Cooper close(fd);
1118ac5aef8SEnji Cooper
1128ac5aef8SEnji Cooper // Enter capability mode; now ALL lookups are strictly relative.
1138ac5aef8SEnji Cooper EXPECT_OK(cap_enter());
1148ac5aef8SEnji Cooper
1158ac5aef8SEnji Cooper // Relative lookups on regular files or capabilities with CAP_LOOKUP
1168ac5aef8SEnji Cooper // ought to succeed.
1178ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc, "passwd", O_RDONLY));
1188ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc_cap_ro, "passwd", O_RDONLY));
1198ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(etc_cap_base, "passwd", O_RDONLY));
1208ac5aef8SEnji Cooper
1218ac5aef8SEnji Cooper // Lookup relative to capabilities without CAP_LOOKUP should fail.
1228ac5aef8SEnji Cooper EXPECT_NOTCAPABLE(openat(etc_cap, "passwd", O_RDONLY));
1238ac5aef8SEnji Cooper
1248ac5aef8SEnji Cooper // Absolute lookups should fail.
1258ac5aef8SEnji Cooper EXPECT_CAPMODE(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
1268ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc, "/etc/passwd", O_RDONLY);
1278ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc_cap_ro, "/etc/passwd", O_RDONLY);
1288ac5aef8SEnji Cooper
1298ac5aef8SEnji Cooper // Lookups containing '..' should fail in capability mode.
1308ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc, "../etc/passwd", O_RDONLY);
1318ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc_cap_ro, "../etc/passwd", O_RDONLY);
1328ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(etc_cap_base, "../etc/passwd", O_RDONLY);
1338ac5aef8SEnji Cooper
1348ac5aef8SEnji Cooper fd = openat(etc, "passwd", O_RDONLY);
1358ac5aef8SEnji Cooper EXPECT_OK(fd);
1368ac5aef8SEnji Cooper
1378ac5aef8SEnji Cooper // A file opened relative to a capability should itself be a capability.
1388ac5aef8SEnji Cooper fd = openat(etc_cap_base, "passwd", O_RDONLY);
1398ac5aef8SEnji Cooper EXPECT_OK(fd);
1408ac5aef8SEnji Cooper EXPECT_OK(cap_rights_get(fd, &rights));
1418ac5aef8SEnji Cooper EXPECT_RIGHTS_IN(&rights, &r_base);
1428ac5aef8SEnji Cooper close(fd);
1438ac5aef8SEnji Cooper
1448ac5aef8SEnji Cooper fd = openat(etc_cap_ro, "passwd", O_RDONLY);
1458ac5aef8SEnji Cooper EXPECT_OK(fd);
1468ac5aef8SEnji Cooper EXPECT_OK(cap_rights_get(fd, &rights));
1478ac5aef8SEnji Cooper EXPECT_RIGHTS_IN(&rights, &r_rl);
1488ac5aef8SEnji Cooper close(fd);
1498ac5aef8SEnji Cooper }
1508ac5aef8SEnji Cooper
1518ac5aef8SEnji Cooper #define TOPDIR "cap_topdir"
1528ac5aef8SEnji Cooper #define SUBDIR TOPDIR "/subdir"
1538ac5aef8SEnji Cooper class OpenatTest : public ::testing::Test {
1548ac5aef8SEnji Cooper public:
1558ac5aef8SEnji Cooper // Build a collection of files, subdirs and symlinks:
1568ac5aef8SEnji Cooper // /tmp/cap_topdir/
1578ac5aef8SEnji Cooper // /topfile
1588ac5aef8SEnji Cooper // /subdir/
1598ac5aef8SEnji Cooper // /subdir/bottomfile
1608ac5aef8SEnji Cooper // /symlink.samedir -> topfile
1618ac5aef8SEnji Cooper // /dsymlink.samedir -> ./
1628ac5aef8SEnji Cooper // /symlink.down -> subdir/bottomfile
1638ac5aef8SEnji Cooper // /dsymlink.down -> subdir/
1648ac5aef8SEnji Cooper // /symlink.absolute_out -> /etc/passwd
1658ac5aef8SEnji Cooper // /dsymlink.absolute_out -> /etc/
1668ac5aef8SEnji Cooper // /symlink.relative_in -> ../../tmp/cap_topdir/topfile
1678ac5aef8SEnji Cooper // /dsymlink.relative_in -> ../../tmp/cap_topdir/
1688ac5aef8SEnji Cooper // /symlink.relative_out -> ../../etc/passwd
1698ac5aef8SEnji Cooper // /dsymlink.relative_out -> ../../etc/
1708ac5aef8SEnji Cooper // /subdir/dsymlink.absolute_in -> /tmp/cap_topdir/
1718ac5aef8SEnji Cooper // /subdir/dsymlink.up -> ../
1728ac5aef8SEnji Cooper // /subdir/symlink.absolute_in -> /tmp/cap_topdir/topfile
1738ac5aef8SEnji Cooper // /subdir/symlink.up -> ../topfile
1748ac5aef8SEnji Cooper // (In practice, this is a little more complicated because tmpdir might
1758ac5aef8SEnji Cooper // not be "/tmp".)
OpenatTest()1768ac5aef8SEnji Cooper OpenatTest() {
1778ac5aef8SEnji Cooper // Create a couple of nested directories
1788ac5aef8SEnji Cooper int rc = mkdir(TmpFile(TOPDIR), 0755);
1798ac5aef8SEnji Cooper EXPECT_OK(rc);
1808ac5aef8SEnji Cooper if (rc < 0) {
1818ac5aef8SEnji Cooper EXPECT_EQ(EEXIST, errno);
1828ac5aef8SEnji Cooper }
1838ac5aef8SEnji Cooper rc = mkdir(TmpFile(SUBDIR), 0755);
1848ac5aef8SEnji Cooper EXPECT_OK(rc);
1858ac5aef8SEnji Cooper if (rc < 0) {
1868ac5aef8SEnji Cooper EXPECT_EQ(EEXIST, errno);
1878ac5aef8SEnji Cooper }
1888ac5aef8SEnji Cooper
1898ac5aef8SEnji Cooper // Figure out a path prefix (like "../..") that gets us to the root
1908ac5aef8SEnji Cooper // directory from TmpFile(TOPDIR).
1918ac5aef8SEnji Cooper const char *p = TmpFile(TOPDIR); // maybe "/tmp/somewhere/cap_topdir"
1928ac5aef8SEnji Cooper std::string dots2root = "..";
1938ac5aef8SEnji Cooper while (*p++ != '\0') {
1948ac5aef8SEnji Cooper if (*p == '/') {
1958ac5aef8SEnji Cooper dots2root += "/..";
1968ac5aef8SEnji Cooper }
1978ac5aef8SEnji Cooper }
1988ac5aef8SEnji Cooper
1998ac5aef8SEnji Cooper // Create normal files in each.
2008ac5aef8SEnji Cooper CreateFile(TmpFile(TOPDIR "/topfile"), "Top-level file");
2018ac5aef8SEnji Cooper CreateFile(TmpFile(SUBDIR "/bottomfile"), "File in subdirectory");
2028ac5aef8SEnji Cooper
2038ac5aef8SEnji Cooper // Create various symlinks to files.
2048ac5aef8SEnji Cooper EXPECT_OK(symlink("topfile", TmpFile(TOPDIR "/symlink.samedir")));
2058ac5aef8SEnji Cooper EXPECT_OK(symlink("subdir/bottomfile", TmpFile(TOPDIR "/symlink.down")));
2068ac5aef8SEnji Cooper EXPECT_OK(symlink(TmpFile(TOPDIR "/topfile"), TmpFile(SUBDIR "/symlink.absolute_in")));
2078ac5aef8SEnji Cooper EXPECT_OK(symlink("/etc/passwd", TmpFile(TOPDIR "/symlink.absolute_out")));
2088ac5aef8SEnji Cooper std::string dots2top = dots2root + TmpFile(TOPDIR "/topfile");
2098ac5aef8SEnji Cooper EXPECT_OK(symlink(dots2top.c_str(), TmpFile(TOPDIR "/symlink.relative_in")));
2108ac5aef8SEnji Cooper std::string dots2passwd = dots2root + "/etc/passwd";
2118ac5aef8SEnji Cooper EXPECT_OK(symlink(dots2passwd.c_str(), TmpFile(TOPDIR "/symlink.relative_out")));
2128ac5aef8SEnji Cooper EXPECT_OK(symlink("../topfile", TmpFile(SUBDIR "/symlink.up")));
2138ac5aef8SEnji Cooper
2148ac5aef8SEnji Cooper // Create various symlinks to directories.
2158ac5aef8SEnji Cooper EXPECT_OK(symlink("./", TmpFile(TOPDIR "/dsymlink.samedir")));
2168ac5aef8SEnji Cooper EXPECT_OK(symlink("subdir/", TmpFile(TOPDIR "/dsymlink.down")));
2178ac5aef8SEnji Cooper EXPECT_OK(symlink(TmpFile(TOPDIR "/"), TmpFile(SUBDIR "/dsymlink.absolute_in")));
2188ac5aef8SEnji Cooper EXPECT_OK(symlink("/etc/", TmpFile(TOPDIR "/dsymlink.absolute_out")));
2198ac5aef8SEnji Cooper std::string dots2cwd = dots2root + tmpdir + "/";
2208ac5aef8SEnji Cooper EXPECT_OK(symlink(dots2cwd.c_str(), TmpFile(TOPDIR "/dsymlink.relative_in")));
2218ac5aef8SEnji Cooper std::string dots2etc = dots2root + "/etc/";
2228ac5aef8SEnji Cooper EXPECT_OK(symlink(dots2etc.c_str(), TmpFile(TOPDIR "/dsymlink.relative_out")));
2238ac5aef8SEnji Cooper EXPECT_OK(symlink("../", TmpFile(SUBDIR "/dsymlink.up")));
2248ac5aef8SEnji Cooper
2258ac5aef8SEnji Cooper // Open directory FDs for those directories and for cwd.
2268ac5aef8SEnji Cooper dir_fd_ = open(TmpFile(TOPDIR), O_RDONLY);
2278ac5aef8SEnji Cooper EXPECT_OK(dir_fd_);
2288ac5aef8SEnji Cooper sub_fd_ = open(TmpFile(SUBDIR), O_RDONLY);
2298ac5aef8SEnji Cooper EXPECT_OK(sub_fd_);
2308ac5aef8SEnji Cooper cwd_ = openat(AT_FDCWD, ".", O_RDONLY);
2318ac5aef8SEnji Cooper EXPECT_OK(cwd_);
2328ac5aef8SEnji Cooper // Move into the directory for the test.
2338ac5aef8SEnji Cooper EXPECT_OK(fchdir(dir_fd_));
2348ac5aef8SEnji Cooper }
~OpenatTest()2358ac5aef8SEnji Cooper ~OpenatTest() {
2368ac5aef8SEnji Cooper fchdir(cwd_);
2378ac5aef8SEnji Cooper close(cwd_);
2388ac5aef8SEnji Cooper close(sub_fd_);
2398ac5aef8SEnji Cooper close(dir_fd_);
2408ac5aef8SEnji Cooper unlink(TmpFile(SUBDIR "/symlink.up"));
2418ac5aef8SEnji Cooper unlink(TmpFile(SUBDIR "/symlink.absolute_in"));
2428ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/symlink.absolute_out"));
2438ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/symlink.relative_in"));
2448ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/symlink.relative_out"));
2458ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/symlink.down"));
2468ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/symlink.samedir"));
2478ac5aef8SEnji Cooper unlink(TmpFile(SUBDIR "/dsymlink.up"));
2488ac5aef8SEnji Cooper unlink(TmpFile(SUBDIR "/dsymlink.absolute_in"));
2498ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/dsymlink.absolute_out"));
2508ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/dsymlink.relative_in"));
2518ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/dsymlink.relative_out"));
2528ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/dsymlink.down"));
2538ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/dsymlink.samedir"));
2548ac5aef8SEnji Cooper unlink(TmpFile(SUBDIR "/bottomfile"));
2558ac5aef8SEnji Cooper unlink(TmpFile(TOPDIR "/topfile"));
2568ac5aef8SEnji Cooper rmdir(TmpFile(SUBDIR));
2578ac5aef8SEnji Cooper rmdir(TmpFile(TOPDIR));
2588ac5aef8SEnji Cooper }
2598ac5aef8SEnji Cooper
2608ac5aef8SEnji Cooper // Check openat(2) policing that is common across capabilities, capability mode and O_BENEATH.
CheckPolicing(int oflag)2618ac5aef8SEnji Cooper void CheckPolicing(int oflag) {
2628ac5aef8SEnji Cooper // OK for normal access.
2638ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "topfile", O_RDONLY|oflag));
2648ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "subdir/bottomfile", O_RDONLY|oflag));
2658ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(sub_fd_, "bottomfile", O_RDONLY|oflag));
2668ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(sub_fd_, ".", O_RDONLY|oflag));
2678ac5aef8SEnji Cooper
2688ac5aef8SEnji Cooper // Can't open paths with ".." in them.
2698ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "../topfile", O_RDONLY|oflag);
2708ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "../subdir/bottomfile", O_RDONLY|oflag);
2718ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "..", O_RDONLY|oflag);
2728ac5aef8SEnji Cooper
2738ac5aef8SEnji Cooper #ifdef HAVE_OPENAT_INTERMEDIATE_DOTDOT
2748ac5aef8SEnji Cooper // OK for dotdot lookups that don't escape the top directory
2758ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "subdir/../topfile", O_RDONLY|oflag));
2768ac5aef8SEnji Cooper #endif
2778ac5aef8SEnji Cooper
2788ac5aef8SEnji Cooper // Check that we can't escape the top directory by the cunning
2798ac5aef8SEnji Cooper // ruse of going via a subdirectory.
2808ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "subdir/../../etc/passwd", O_RDONLY|oflag);
2818ac5aef8SEnji Cooper
2828ac5aef8SEnji Cooper // Should only be able to open symlinks that stay within the directory.
2838ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY|oflag));
2848ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY|oflag));
2858ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.absolute_out", O_RDONLY|oflag);
2868ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_in", O_RDONLY|oflag);
2878ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "symlink.relative_out", O_RDONLY|oflag);
2888ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.absolute_in", O_RDONLY|oflag);
2898ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "symlink.up", O_RDONLY|oflag);
2908ac5aef8SEnji Cooper
2918ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.samedir/topfile", O_RDONLY|oflag));
2928ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "dsymlink.down/bottomfile", O_RDONLY|oflag));
2938ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.absolute_out/passwd", O_RDONLY|oflag);
2948ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_in/topfile", O_RDONLY|oflag);
2958ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "dsymlink.relative_out/passwd", O_RDONLY|oflag);
2968ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.absolute_in/topfile", O_RDONLY|oflag);
2978ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "dsymlink.up/topfile", O_RDONLY|oflag);
2988ac5aef8SEnji Cooper
2998ac5aef8SEnji Cooper // Although recall that O_NOFOLLOW prevents symlink following in final component.
3008ac5aef8SEnji Cooper EXPECT_SYSCALL_FAIL(E_TOO_MANY_LINKS, openat(dir_fd_, "symlink.samedir", O_RDONLY|O_NOFOLLOW|oflag));
3018ac5aef8SEnji Cooper EXPECT_SYSCALL_FAIL(E_TOO_MANY_LINKS, openat(dir_fd_, "symlink.down", O_RDONLY|O_NOFOLLOW|oflag));
3028ac5aef8SEnji Cooper }
3038ac5aef8SEnji Cooper
3048ac5aef8SEnji Cooper protected:
3058ac5aef8SEnji Cooper int dir_fd_;
3068ac5aef8SEnji Cooper int sub_fd_;
3078ac5aef8SEnji Cooper int cwd_;
3088ac5aef8SEnji Cooper };
3098ac5aef8SEnji Cooper
TEST_F(OpenatTest,WithCapability)3108ac5aef8SEnji Cooper TEST_F(OpenatTest, WithCapability) {
3118ac5aef8SEnji Cooper // Any kind of symlink can be opened relative to an ordinary directory FD.
3128ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.samedir", O_RDONLY));
3138ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.down", O_RDONLY));
3148ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.absolute_out", O_RDONLY));
3158ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_in", O_RDONLY));
3168ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(dir_fd_, "symlink.relative_out", O_RDONLY));
3178ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(sub_fd_, "symlink.absolute_in", O_RDONLY));
3188ac5aef8SEnji Cooper EXPECT_OPEN_OK(openat(sub_fd_, "symlink.up", O_RDONLY));
3198ac5aef8SEnji Cooper
3208ac5aef8SEnji Cooper // Now make both DFDs into Capsicum capabilities.
3218ac5aef8SEnji Cooper cap_rights_t r_rl;
3228ac5aef8SEnji Cooper cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP, CAP_FCHDIR);
3238ac5aef8SEnji Cooper EXPECT_OK(cap_rights_limit(dir_fd_, &r_rl));
3248ac5aef8SEnji Cooper EXPECT_OK(cap_rights_limit(sub_fd_, &r_rl));
3258ac5aef8SEnji Cooper CheckPolicing(0);
3268ac5aef8SEnji Cooper // Use of AT_FDCWD is independent of use of a capability.
3278ac5aef8SEnji Cooper // Can open paths starting with "/" against a capability dfd, because the dfd is ignored.
3288ac5aef8SEnji Cooper }
3298ac5aef8SEnji Cooper
FORK_TEST_F(OpenatTest,InCapabilityMode)3308ac5aef8SEnji Cooper FORK_TEST_F(OpenatTest, InCapabilityMode) {
3318ac5aef8SEnji Cooper EXPECT_OK(cap_enter()); // Enter capability mode
3328ac5aef8SEnji Cooper CheckPolicing(0);
3338ac5aef8SEnji Cooper
3348ac5aef8SEnji Cooper // Use of AT_FDCWD is banned in capability mode.
3358ac5aef8SEnji Cooper EXPECT_CAPMODE(openat(AT_FDCWD, "topfile", O_RDONLY));
3368ac5aef8SEnji Cooper EXPECT_CAPMODE(openat(AT_FDCWD, "subdir/bottomfile", O_RDONLY));
3378ac5aef8SEnji Cooper EXPECT_CAPMODE(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
3388ac5aef8SEnji Cooper
3398ac5aef8SEnji Cooper // Can't open paths starting with "/" in capability mode.
3408ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "/etc/passwd", O_RDONLY);
3418ac5aef8SEnji Cooper EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "/etc/passwd", O_RDONLY);
3428ac5aef8SEnji Cooper }
3438ac5aef8SEnji Cooper
344*8cf5812aSAlex Richardson #if !defined(O_RESOLVE_BENEATH) && defined(O_BENEATH)
345*8cf5812aSAlex Richardson #define O_RESOLVE_BENEATH O_BENEATH
346*8cf5812aSAlex Richardson #endif
347*8cf5812aSAlex Richardson
348*8cf5812aSAlex Richardson #ifdef O_RESOLVE_BENEATH
TEST_F(OpenatTest,WithFlag)3498ac5aef8SEnji Cooper TEST_F(OpenatTest, WithFlag) {
350*8cf5812aSAlex Richardson CheckPolicing(O_RESOLVE_BENEATH);
3518ac5aef8SEnji Cooper
3528ac5aef8SEnji Cooper // Check with AT_FDCWD.
353*8cf5812aSAlex Richardson EXPECT_OPEN_OK(openat(AT_FDCWD, "topfile", O_RDONLY|O_RESOLVE_BENEATH));
354*8cf5812aSAlex Richardson EXPECT_OPEN_OK(openat(AT_FDCWD, "subdir/bottomfile", O_RDONLY|O_RESOLVE_BENEATH));
3558ac5aef8SEnji Cooper
356*8cf5812aSAlex Richardson // Can't open paths starting with "/" with O_RESOLVE_BENEATH specified.
357*8cf5812aSAlex Richardson EXPECT_OPENAT_FAIL_TRAVERSAL(AT_FDCWD, "/etc/passwd", O_RDONLY|O_RESOLVE_BENEATH);
358*8cf5812aSAlex Richardson EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "/etc/passwd", O_RDONLY|O_RESOLVE_BENEATH);
359*8cf5812aSAlex Richardson EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "/etc/passwd", O_RDONLY|O_RESOLVE_BENEATH);
3608ac5aef8SEnji Cooper }
3618ac5aef8SEnji Cooper
FORK_TEST_F(OpenatTest,WithFlagInCapabilityMode)3628ac5aef8SEnji Cooper FORK_TEST_F(OpenatTest, WithFlagInCapabilityMode) {
3638ac5aef8SEnji Cooper EXPECT_OK(cap_enter()); // Enter capability mode
364*8cf5812aSAlex Richardson CheckPolicing(O_RESOLVE_BENEATH);
3658ac5aef8SEnji Cooper }
3668ac5aef8SEnji Cooper #endif
367