Page MenuHomeFreeBSD

D28687.id83998.diff
No OneTemporary

D28687.id83998.diff

Index: contrib/capsicum-test/capability-fd.cc
===================================================================
--- contrib/capsicum-test/capability-fd.cc
+++ contrib/capsicum-test/capability-fd.cc
@@ -486,6 +486,7 @@
// Given a file descriptor, create a capability with specific rights and
// make sure only those rights work.
#define TRY_FILE_OPS(fd, ...) do { \
+ SCOPED_TRACE(#__VA_ARGS__); \
cap_rights_t rights; \
cap_rights_init(&rights, __VA_ARGS__); \
TryFileOps((fd), rights); \
@@ -1289,7 +1290,10 @@
pid_t child = fork();
if (child == 0) {
// Child: change uid to a lesser being
- setuid(other_uid);
+ ASSERT_NE(0u, other_uid) << "other_uid not initialized correctly, "
+ "please pass the -u <uid> flag.";
+ EXPECT_EQ(0, setuid(other_uid));
+ EXPECT_EQ(other_uid, getuid());
// Attempt to fchmod the file, and fail.
// Having CAP_FCHMOD doesn't bypass the need to comply with DAC policy.
int rc = fchmod(fd, 0666);
Index: contrib/capsicum-test/capsicum-freebsd.h
===================================================================
--- contrib/capsicum-test/capsicum-freebsd.h
+++ contrib/capsicum-test/capsicum-freebsd.h
@@ -35,6 +35,12 @@
#if __FreeBSD_version >= 1101000
#define HAVE_OPENAT_INTERMEDIATE_DOTDOT
#endif
+#if __FreeBSD_version >= 1300116
+// Since FreeBSD commit 6a9c72d901feeca0b0865be8954a3a29d8613b34 any sequence
+// including ".." is valid with O_BENEATH as long as we return back below the start.
+// Capsicum and the stricter O_RESOLVE_BENEATH do no allow escaping at all.
+#define HAVE_O_BENEATH_INTERMEDIATE_DOTDOT
+#endif
#endif
Index: contrib/capsicum-test/capsicum-test.h
===================================================================
--- contrib/capsicum-test/capsicum-test.h
+++ contrib/capsicum-test/capsicum-test.h
@@ -75,7 +75,7 @@
} \
} else if (pid > 0) { \
int rc, status; \
- int remaining_us = 10000000; \
+ int remaining_us = 30000000; \
while (remaining_us > 0) { \
status = 0; \
rc = waitpid(pid, &status, WNOHANG); \
@@ -139,15 +139,17 @@
// Expect a syscall to fail with the given error.
#define EXPECT_SYSCALL_FAIL(E, C) \
do { \
+ SCOPED_TRACE(#C); \
EXPECT_GT(0, C); \
- EXPECT_EQ(E, errno); \
+ EXPECT_EQ(E, errno) << "expected '" << strerror(E) \
+ << "' but got '" << strerror(errno) << "'"; \
} while (0)
// Expect a syscall to fail with anything other than the given error.
#define EXPECT_SYSCALL_FAIL_NOT(E, C) \
do { \
EXPECT_GT(0, C); \
- EXPECT_NE(E, errno); \
+ EXPECT_NE(E, errno) << strerror(E); \
} while (0)
// Expect a void syscall to fail with anything other than the given error.
@@ -162,19 +164,23 @@
// code is OS-specific.
#ifdef O_BENEATH
#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
- do { \
+ do { \
+ SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \
const int result = openat((fd), (path), (flags)); \
if (((flags) & O_BENEATH) == O_BENEATH) { \
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_O_BENEATH, result); \
} else { \
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
} \
+ if (result >= 0) { close(result); } \
} while (0)
#else
#define EXPECT_OPENAT_FAIL_TRAVERSAL(fd, path, flags) \
do { \
+ SCOPED_TRACE(GTEST_STRINGIFY_(openat((fd), (path), (flags)))); \
const int result = openat((fd), (path), (flags)); \
EXPECT_SYSCALL_FAIL(E_NO_TRAVERSE_CAPABILITY, result); \
+ if (result >= 0) { close(result); } \
} while (0)
#endif
@@ -197,7 +203,8 @@
int rc = C; \
EXPECT_GT(0, rc); \
EXPECT_TRUE(errno == ECAPMODE || errno == ENOTCAPABLE) \
- << #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno; \
+ << #C << " did not fail with ECAPMODE/ENOTCAPABLE but " << errno \
+ << "(" << strerror(errno) << ")"; \
} while (0)
// Ensure that 'rights' are a subset of 'max'.
Index: contrib/capsicum-test/openat.cc
===================================================================
--- contrib/capsicum-test/openat.cc
+++ contrib/capsicum-test/openat.cc
@@ -11,6 +11,7 @@
// Check an open call works and close the resulting fd.
#define EXPECT_OPEN_OK(f) do { \
+ SCOPED_TRACE(#f); \
int _fd = f; \
EXPECT_OK(_fd); \
close(_fd); \
@@ -257,17 +258,30 @@
}
// Check openat(2) policing that is common across capabilities, capability mode and O_BENEATH.
- void CheckPolicing(int oflag) {
+ void CheckPolicing(int oflag, bool allowsIntermediateEscape) {
// OK for normal access.
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(sub_fd_, "bottomfile", O_RDONLY|oflag));
EXPECT_OPEN_OK(openat(sub_fd_, ".", O_RDONLY|oflag));
+ EXPECT_SYSCALL_FAIL(ENOENT, openat(sub_fd_, "foo/../bottomfile", O_RDONLY | oflag));
+ EXPECT_SYSCALL_FAIL(ENOENT, openat(sub_fd_, "foo/../../subdir/bottomfile", O_RDONLY | oflag));
+
// Can't open paths with ".." in them.
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "../topfile", O_RDONLY|oflag);
- EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "../subdir/bottomfile", O_RDONLY|oflag);
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "..", O_RDONLY|oflag);
+ if (allowsIntermediateEscape) {
+ EXPECT_OPEN_OK(openat(sub_fd_, "../subdir/bottomfile", O_RDONLY | oflag));
+ EXPECT_OPEN_OK(openat(sub_fd_, "../../cap_topdir/subdir/bottomfile", O_RDONLY | oflag));
+ EXPECT_OPEN_OK(openat(dir_fd_, "../cap_topdir/subdir/bottomfile", O_RDONLY | oflag));
+ EXPECT_OPEN_OK(openat(dir_fd_, "subdir/../../cap_topdir/topfile", O_RDONLY | oflag));
+ } else {
+ EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "../subdir/bottomfile", O_RDONLY | oflag);
+ EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "../../cap_topdir/subdir/bottomfile", O_RDONLY | oflag);
+ EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "../cap_topdir/subdir/bottomfile", O_RDONLY | oflag);
+ EXPECT_OPENAT_FAIL_TRAVERSAL(dir_fd_, "subdir/../../cap_topdir/topfile", O_RDONLY | oflag);
+ }
#ifdef HAVE_OPENAT_INTERMEDIATE_DOTDOT
// OK for dotdot lookups that don't escape the top directory
@@ -321,14 +335,14 @@
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(sub_fd_, &r_rl));
- CheckPolicing(0);
+ CheckPolicing(0, /*allowsIntermediateEscape=*/false);
// Use of AT_FDCWD is independent of use of a capability.
// Can open paths starting with "/" against a capability dfd, because the dfd is ignored.
}
FORK_TEST_F(OpenatTest, InCapabilityMode) {
EXPECT_OK(cap_enter()); // Enter capability mode
- CheckPolicing(0);
+ CheckPolicing(0, /*allowsIntermediateEscape=*/false);
// Use of AT_FDCWD is banned in capability mode.
EXPECT_CAPMODE(openat(AT_FDCWD, "topfile", O_RDONLY));
@@ -341,8 +355,13 @@
}
#ifdef O_BENEATH
-TEST_F(OpenatTest, WithFlag) {
- CheckPolicing(O_BENEATH);
+TEST_F(OpenatTest, WithFlag_O_BENEATH) {
+ bool allowsIntermediateEscape = false;
+ // FreeBSD13+ O_BENEATH allows intermediate .. as long as the final path is below the start.
+#ifdef HAVE_O_BENEATH_INTERMEDIATE_DOTDOT
+ allowsIntermediateEscape = true;
+#endif
+ CheckPolicing(O_BENEATH, allowsIntermediateEscape);
// Check with AT_FDCWD.
EXPECT_OPEN_OK(openat(AT_FDCWD, "topfile", O_RDONLY|O_BENEATH));
@@ -354,8 +373,33 @@
EXPECT_OPENAT_FAIL_TRAVERSAL(sub_fd_, "/etc/passwd", O_RDONLY|O_BENEATH);
}
-FORK_TEST_F(OpenatTest, WithFlagInCapabilityMode) {
+FORK_TEST_F(OpenatTest, WithFlagInCapabilityMode_O_BENEATH) {
+ EXPECT_OK(cap_enter()); // Enter capability mode
+ CheckPolicing(O_BENEATH, /*allowsIntermediateEscape=*/false);
+}
+#endif
+
+#ifdef O_RESOLVE_BENEATH
+TEST_F(OpenatTest, WithFlag_O_RESOLVE_BENEATH) {
+ // O_RESOLVE_BENEATH is similar to O_BENEATH but prevents any (temporary)
+ // escape from the root during resolving.
+ CheckPolicing(O_RESOLVE_BENEATH, /*allowsIntermediateEscape=*/false);
+}
+
+FORK_TEST_F(OpenatTest, WithFlagInCapabilityMode_O_RESOLVE_BENEATH) {
+ EXPECT_OK(cap_enter()); // Enter capability mode
+ CheckPolicing(O_RESOLVE_BENEATH, /*allowsIntermediateEscape=*/false);
+}
+
+#ifdef O_BENEATH
+TEST_F(OpenatTest, WithFlag_O_RESOLVE_BENEATH_and_O_BENEATH) {
+ // If both O_BENEATH and O_RESOLVE_BENEATH are passed, the latter has priority.
+ CheckPolicing(O_RESOLVE_BENEATH | O_BENEATH, /*allowsIntermediateEscape=*/false);
+}
+
+FORK_TEST_F(OpenatTest, WithFlagInCapabilityMode_O_RESOLVE_BENEATH_and_O_BENEATH) {
EXPECT_OK(cap_enter()); // Enter capability mode
- CheckPolicing(O_BENEATH);
+ CheckPolicing(O_RESOLVE_BENEATH | O_BENEATH, /*allowsIntermediateEscape=*/false);
}
#endif
+#endif
Index: contrib/capsicum-test/procdesc.cc
===================================================================
--- contrib/capsicum-test/procdesc.cc
+++ contrib/capsicum-test/procdesc.cc
@@ -531,7 +531,10 @@
usleep(100);
// Now that the second process has been pdfork()ed, change euid.
- setuid(other_uid);
+ ASSERT_NE(0u, other_uid) << "other_uid not initialized correctly, "
+ "please pass the -u <uid> flag.";
+ EXPECT_EQ(0, setuid(other_uid));
+ EXPECT_EQ(other_uid, getuid());
if (verbose) fprintf(stderr, "uid=%d euid=%d\n", getuid(), geteuid());
// Fail to kill child with normal PID operation.

File Metadata

Mime Type
text/plain
Expires
Mon, Feb 9, 2:17 AM (13 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28525097
Default Alt Text
D28687.id83998.diff (9 KB)

Event Timeline