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.
4
5 #include "sandbox/linux/services/credentials.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 #include "base/file_util.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "sandbox/linux/tests/unit_tests.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using file_util::ScopedFD;
21
22 namespace sandbox {
23
24 namespace {
25
DirectoryExists(const char * path)26 bool DirectoryExists(const char* path) {
27 struct stat dir;
28 errno = 0;
29 int ret = stat(path, &dir);
30 return -1 != ret || ENOENT != errno;
31 }
32
WorkingDirectoryIsRoot()33 bool WorkingDirectoryIsRoot() {
34 char current_dir[PATH_MAX];
35 char* cwd = getcwd(current_dir, sizeof(current_dir));
36 PCHECK(cwd);
37 if (strcmp("/", cwd)) return false;
38
39 // The current directory is the root. Add a few paranoid checks.
40 struct stat current;
41 CHECK_EQ(0, stat(".", ¤t));
42 struct stat parrent;
43 CHECK_EQ(0, stat("..", &parrent));
44 CHECK_EQ(current.st_dev, parrent.st_dev);
45 CHECK_EQ(current.st_ino, parrent.st_ino);
46 CHECK_EQ(current.st_mode, parrent.st_mode);
47 CHECK_EQ(current.st_uid, parrent.st_uid);
48 CHECK_EQ(current.st_gid, parrent.st_gid);
49 return true;
50 }
51
52 // Give dynamic tools a simple thing to test.
TEST(Credentials,CreateAndDestroy)53 TEST(Credentials, CreateAndDestroy) {
54 {
55 Credentials cred1;
56 (void) cred1;
57 }
58 scoped_ptr<Credentials> cred2(new Credentials);
59 }
60
TEST(Credentials,HasOpenDirectory)61 TEST(Credentials, HasOpenDirectory) {
62 Credentials creds;
63 // No open directory should exist at startup.
64 EXPECT_FALSE(creds.HasOpenDirectory(-1));
65 {
66 // Have a "/dev" file descriptor around.
67 int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
68 ScopedFD dev_fd_closer(&dev_fd);
69 EXPECT_TRUE(creds.HasOpenDirectory(-1));
70 }
71 EXPECT_FALSE(creds.HasOpenDirectory(-1));
72 }
73
TEST(Credentials,HasOpenDirectoryWithFD)74 TEST(Credentials, HasOpenDirectoryWithFD) {
75 Credentials creds;
76
77 int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
78 ScopedFD proc_fd_closer(&proc_fd);
79 ASSERT_LE(0, proc_fd);
80
81 // Don't pass |proc_fd|, an open directory (proc_fd) should
82 // be detected.
83 EXPECT_TRUE(creds.HasOpenDirectory(-1));
84 // Pass |proc_fd| and no open directory should be detected.
85 EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
86
87 {
88 // Have a "/dev" file descriptor around.
89 int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
90 ScopedFD dev_fd_closer(&dev_fd);
91 EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
92 }
93
94 // The "/dev" file descriptor should now be closed, |proc_fd| is the only
95 // directory file descriptor open.
96 EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
97 }
98
SANDBOX_TEST(Credentials,DropAllCaps)99 SANDBOX_TEST(Credentials, DropAllCaps) {
100 Credentials creds;
101 CHECK(creds.DropAllCapabilities());
102 CHECK(!creds.HasAnyCapability());
103 }
104
SANDBOX_TEST(Credentials,GetCurrentCapString)105 SANDBOX_TEST(Credentials, GetCurrentCapString) {
106 Credentials creds;
107 CHECK(creds.DropAllCapabilities());
108 const char kNoCapabilityText[] = "=";
109 CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
110 }
111
SANDBOX_TEST(Credentials,MoveToNewUserNS)112 SANDBOX_TEST(Credentials, MoveToNewUserNS) {
113 Credentials creds;
114 creds.DropAllCapabilities();
115 bool userns_supported = creds.MoveToNewUserNS();
116 fprintf(stdout, "Unprivileged CLONE_NEWUSER supported: %s\n",
117 userns_supported ? "true." : "false.");
118 fflush(stdout);
119 if (!userns_supported) {
120 fprintf(stdout, "This kernel does not support unprivileged namespaces. "
121 "USERNS tests will succeed without running.\n");
122 fflush(stdout);
123 return;
124 }
125 CHECK(creds.HasAnyCapability());
126 creds.DropAllCapabilities();
127 CHECK(!creds.HasAnyCapability());
128 }
129
SANDBOX_TEST(Credentials,UidIsPreserved)130 SANDBOX_TEST(Credentials, UidIsPreserved) {
131 Credentials creds;
132 creds.DropAllCapabilities();
133 uid_t old_ruid, old_euid, old_suid;
134 gid_t old_rgid, old_egid, old_sgid;
135 PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
136 PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
137 // Probably missing kernel support.
138 if (!creds.MoveToNewUserNS()) return;
139 uid_t new_ruid, new_euid, new_suid;
140 PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
141 CHECK(old_ruid == new_ruid);
142 CHECK(old_euid == new_euid);
143 CHECK(old_suid == new_suid);
144
145 gid_t new_rgid, new_egid, new_sgid;
146 PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
147 CHECK(old_rgid == new_rgid);
148 CHECK(old_egid == new_egid);
149 CHECK(old_sgid == new_sgid);
150 }
151
NewUserNSCycle(Credentials * creds)152 bool NewUserNSCycle(Credentials* creds) {
153 DCHECK(creds);
154 if (!creds->MoveToNewUserNS() ||
155 !creds->HasAnyCapability() ||
156 !creds->DropAllCapabilities() ||
157 creds->HasAnyCapability()) {
158 return false;
159 }
160 return true;
161 }
162
SANDBOX_TEST(Credentials,NestedUserNS)163 SANDBOX_TEST(Credentials, NestedUserNS) {
164 Credentials creds;
165 CHECK(creds.DropAllCapabilities());
166 // Probably missing kernel support.
167 if (!creds.MoveToNewUserNS()) return;
168 creds.DropAllCapabilities();
169 // As of 3.12, the kernel has a limit of 32. See create_user_ns().
170 const int kNestLevel = 10;
171 for (int i = 0; i < kNestLevel; ++i) {
172 CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration "
173 << i << ".";
174 }
175 }
176
177 // Test the WorkingDirectoryIsRoot() helper.
TEST(Credentials,CanDetectRoot)178 TEST(Credentials, CanDetectRoot) {
179 ASSERT_EQ(0, chdir("/proc/"));
180 ASSERT_FALSE(WorkingDirectoryIsRoot());
181 ASSERT_EQ(0, chdir("/"));
182 ASSERT_TRUE(WorkingDirectoryIsRoot());
183 }
184
SANDBOX_TEST(Credentials,DropFileSystemAccessIsSafe)185 SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) {
186 Credentials creds;
187 CHECK(creds.DropAllCapabilities());
188 // Probably missing kernel support.
189 if (!creds.MoveToNewUserNS()) return;
190 CHECK(creds.DropFileSystemAccess());
191 CHECK(!DirectoryExists("/proc"));
192 CHECK(WorkingDirectoryIsRoot());
193 // We want the chroot to never have a subdirectory. A subdirectory
194 // could allow a chroot escape.
195 CHECK_NE(0, mkdir("/test", 0700));
196 }
197
198 // Check that after dropping filesystem access and dropping privileges
199 // it is not possible to regain capabilities.
SANDBOX_TEST(Credentials,CannotRegainPrivileges)200 SANDBOX_TEST(Credentials, CannotRegainPrivileges) {
201 Credentials creds;
202 CHECK(creds.DropAllCapabilities());
203 // Probably missing kernel support.
204 if (!creds.MoveToNewUserNS()) return;
205 CHECK(creds.DropFileSystemAccess());
206 CHECK(creds.DropAllCapabilities());
207
208 // The kernel should now prevent us from regaining capabilities because we
209 // are in a chroot.
210 CHECK(!creds.MoveToNewUserNS());
211 }
212
213 } // namespace.
214
215 } // namespace sandbox.
216