1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sandbox/linux/services/credentials.h"
11 #include <sys/types.h>
14 #include "base/file_util.h"
15 #include "base/files/scoped_file.h"
16 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "sandbox/linux/tests/unit_tests.h"
19 #include "testing/gtest/include/gtest/gtest.h"
25 bool DirectoryExists(const char* path
) {
28 int ret
= stat(path
, &dir
);
29 return -1 != ret
|| ENOENT
!= errno
;
32 bool WorkingDirectoryIsRoot() {
33 char current_dir
[PATH_MAX
];
34 char* cwd
= getcwd(current_dir
, sizeof(current_dir
));
36 if (strcmp("/", cwd
)) return false;
38 // The current directory is the root. Add a few paranoid checks.
40 CHECK_EQ(0, stat(".", ¤t
));
42 CHECK_EQ(0, stat("..", &parrent
));
43 CHECK_EQ(current
.st_dev
, parrent
.st_dev
);
44 CHECK_EQ(current
.st_ino
, parrent
.st_ino
);
45 CHECK_EQ(current
.st_mode
, parrent
.st_mode
);
46 CHECK_EQ(current
.st_uid
, parrent
.st_uid
);
47 CHECK_EQ(current
.st_gid
, parrent
.st_gid
);
51 // Give dynamic tools a simple thing to test.
52 TEST(Credentials
, CreateAndDestroy
) {
57 scoped_ptr
<Credentials
> cred2(new Credentials
);
60 TEST(Credentials
, CountOpenFds
) {
61 base::ScopedFD
proc_fd(open("/proc", O_RDONLY
| O_DIRECTORY
));
62 ASSERT_TRUE(proc_fd
.is_valid());
64 int fd_count
= creds
.CountOpenFds(proc_fd
.get());
65 int fd
= open("/dev/null", O_RDONLY
);
67 EXPECT_EQ(fd_count
+ 1, creds
.CountOpenFds(proc_fd
.get()));
68 ASSERT_EQ(0, IGNORE_EINTR(close(fd
)));
69 EXPECT_EQ(fd_count
, creds
.CountOpenFds(proc_fd
.get()));
72 TEST(Credentials
, HasOpenDirectory
) {
74 // No open directory should exist at startup.
75 EXPECT_FALSE(creds
.HasOpenDirectory(-1));
77 // Have a "/dev" file descriptor around.
78 int dev_fd
= open("/dev", O_RDONLY
| O_DIRECTORY
);
79 base::ScopedFD
dev_fd_closer(dev_fd
);
80 EXPECT_TRUE(creds
.HasOpenDirectory(-1));
82 EXPECT_FALSE(creds
.HasOpenDirectory(-1));
85 TEST(Credentials
, HasOpenDirectoryWithFD
) {
88 int proc_fd
= open("/proc", O_RDONLY
| O_DIRECTORY
);
89 base::ScopedFD
proc_fd_closer(proc_fd
);
90 ASSERT_LE(0, proc_fd
);
92 // Don't pass |proc_fd|, an open directory (proc_fd) should
94 EXPECT_TRUE(creds
.HasOpenDirectory(-1));
95 // Pass |proc_fd| and no open directory should be detected.
96 EXPECT_FALSE(creds
.HasOpenDirectory(proc_fd
));
99 // Have a "/dev" file descriptor around.
100 int dev_fd
= open("/dev", O_RDONLY
| O_DIRECTORY
);
101 base::ScopedFD
dev_fd_closer(dev_fd
);
102 EXPECT_TRUE(creds
.HasOpenDirectory(proc_fd
));
105 // The "/dev" file descriptor should now be closed, |proc_fd| is the only
106 // directory file descriptor open.
107 EXPECT_FALSE(creds
.HasOpenDirectory(proc_fd
));
110 SANDBOX_TEST(Credentials
, DropAllCaps
) {
112 CHECK(creds
.DropAllCapabilities());
113 CHECK(!creds
.HasAnyCapability());
116 SANDBOX_TEST(Credentials
, GetCurrentCapString
) {
118 CHECK(creds
.DropAllCapabilities());
119 const char kNoCapabilityText
[] = "=";
120 CHECK(*creds
.GetCurrentCapString() == kNoCapabilityText
);
123 SANDBOX_TEST(Credentials
, MoveToNewUserNS
) {
125 creds
.DropAllCapabilities();
126 bool moved_to_new_ns
= creds
.MoveToNewUserNS();
128 "Unprivileged CLONE_NEWUSER supported: %s\n",
129 moved_to_new_ns
? "true." : "false.");
131 if (!moved_to_new_ns
) {
132 fprintf(stdout
, "This kernel does not support unprivileged namespaces. "
133 "USERNS tests will succeed without running.\n");
137 CHECK(creds
.HasAnyCapability());
138 creds
.DropAllCapabilities();
139 CHECK(!creds
.HasAnyCapability());
142 SANDBOX_TEST(Credentials
, SupportsUserNS
) {
144 creds
.DropAllCapabilities();
145 bool user_ns_supported
= Credentials::SupportsNewUserNS();
146 bool moved_to_new_ns
= creds
.MoveToNewUserNS();
147 CHECK_EQ(user_ns_supported
, moved_to_new_ns
);
150 SANDBOX_TEST(Credentials
, UidIsPreserved
) {
152 creds
.DropAllCapabilities();
153 uid_t old_ruid
, old_euid
, old_suid
;
154 gid_t old_rgid
, old_egid
, old_sgid
;
155 PCHECK(0 == getresuid(&old_ruid
, &old_euid
, &old_suid
));
156 PCHECK(0 == getresgid(&old_rgid
, &old_egid
, &old_sgid
));
157 // Probably missing kernel support.
158 if (!creds
.MoveToNewUserNS()) return;
159 uid_t new_ruid
, new_euid
, new_suid
;
160 PCHECK(0 == getresuid(&new_ruid
, &new_euid
, &new_suid
));
161 CHECK(old_ruid
== new_ruid
);
162 CHECK(old_euid
== new_euid
);
163 CHECK(old_suid
== new_suid
);
165 gid_t new_rgid
, new_egid
, new_sgid
;
166 PCHECK(0 == getresgid(&new_rgid
, &new_egid
, &new_sgid
));
167 CHECK(old_rgid
== new_rgid
);
168 CHECK(old_egid
== new_egid
);
169 CHECK(old_sgid
== new_sgid
);
172 bool NewUserNSCycle(Credentials
* creds
) {
174 if (!creds
->MoveToNewUserNS() ||
175 !creds
->HasAnyCapability() ||
176 !creds
->DropAllCapabilities() ||
177 creds
->HasAnyCapability()) {
183 SANDBOX_TEST(Credentials
, NestedUserNS
) {
185 CHECK(creds
.DropAllCapabilities());
186 // Probably missing kernel support.
187 if (!creds
.MoveToNewUserNS()) return;
188 creds
.DropAllCapabilities();
189 // As of 3.12, the kernel has a limit of 32. See create_user_ns().
190 const int kNestLevel
= 10;
191 for (int i
= 0; i
< kNestLevel
; ++i
) {
192 CHECK(NewUserNSCycle(&creds
)) << "Creating new user NS failed at iteration "
197 // Test the WorkingDirectoryIsRoot() helper.
198 TEST(Credentials
, CanDetectRoot
) {
199 ASSERT_EQ(0, chdir("/proc/"));
200 ASSERT_FALSE(WorkingDirectoryIsRoot());
201 ASSERT_EQ(0, chdir("/"));
202 ASSERT_TRUE(WorkingDirectoryIsRoot());
205 SANDBOX_TEST(Credentials
, DISABLE_ON_LSAN(DropFileSystemAccessIsSafe
)) {
207 CHECK(creds
.DropAllCapabilities());
208 // Probably missing kernel support.
209 if (!creds
.MoveToNewUserNS()) return;
210 CHECK(creds
.DropFileSystemAccess());
211 CHECK(!DirectoryExists("/proc"));
212 CHECK(WorkingDirectoryIsRoot());
213 // We want the chroot to never have a subdirectory. A subdirectory
214 // could allow a chroot escape.
215 CHECK_NE(0, mkdir("/test", 0700));
218 // Check that after dropping filesystem access and dropping privileges
219 // it is not possible to regain capabilities.
220 SANDBOX_TEST(Credentials
, DISABLE_ON_LSAN(CannotRegainPrivileges
)) {
222 CHECK(creds
.DropAllCapabilities());
223 // Probably missing kernel support.
224 if (!creds
.MoveToNewUserNS()) return;
225 CHECK(creds
.DropFileSystemAccess());
226 CHECK(creds
.DropAllCapabilities());
228 // The kernel should now prevent us from regaining capabilities because we
230 CHECK(!Credentials::SupportsNewUserNS());
231 CHECK(!creds
.MoveToNewUserNS());
236 } // namespace sandbox.