28199fb30a5294526a622ee4a7bb58a845b4f1cf
[platform/framework/web/crosswalk.git] / src / sandbox / linux / services / credentials_unittest.cc
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/files/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"
20
21 namespace sandbox {
22
23 namespace {
24
25 bool DirectoryExists(const char* path) {
26   struct stat dir;
27   errno = 0;
28   int ret = stat(path, &dir);
29   return -1 != ret || ENOENT != errno;
30 }
31
32 bool WorkingDirectoryIsRoot() {
33   char current_dir[PATH_MAX];
34   char* cwd = getcwd(current_dir, sizeof(current_dir));
35   PCHECK(cwd);
36   if (strcmp("/", cwd)) return false;
37
38   // The current directory is the root. Add a few paranoid checks.
39   struct stat current;
40   CHECK_EQ(0, stat(".", &current));
41   struct stat parrent;
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);
48   return true;
49 }
50
51 // Give dynamic tools a simple thing to test.
52 TEST(Credentials, CreateAndDestroy) {
53   {
54     Credentials cred1;
55     (void) cred1;
56   }
57   scoped_ptr<Credentials> cred2(new Credentials);
58 }
59
60 TEST(Credentials, CountOpenFds) {
61   base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
62   ASSERT_TRUE(proc_fd.is_valid());
63   Credentials creds;
64   int fd_count = creds.CountOpenFds(proc_fd.get());
65   int fd = open("/dev/null", O_RDONLY);
66   ASSERT_LE(0, fd);
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()));
70 }
71
72 TEST(Credentials, HasOpenDirectory) {
73   Credentials creds;
74   // No open directory should exist at startup.
75   EXPECT_FALSE(creds.HasOpenDirectory(-1));
76   {
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));
81   }
82   EXPECT_FALSE(creds.HasOpenDirectory(-1));
83 }
84
85 TEST(Credentials, HasOpenDirectoryWithFD) {
86   Credentials creds;
87
88   int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
89   base::ScopedFD proc_fd_closer(proc_fd);
90   ASSERT_LE(0, proc_fd);
91
92   // Don't pass |proc_fd|, an open directory (proc_fd) should
93   // be detected.
94   EXPECT_TRUE(creds.HasOpenDirectory(-1));
95   // Pass |proc_fd| and no open directory should be detected.
96   EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
97
98   {
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));
103   }
104
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));
108 }
109
110 SANDBOX_TEST(Credentials, DropAllCaps) {
111   Credentials creds;
112   CHECK(creds.DropAllCapabilities());
113   CHECK(!creds.HasAnyCapability());
114 }
115
116 SANDBOX_TEST(Credentials, GetCurrentCapString) {
117   Credentials creds;
118   CHECK(creds.DropAllCapabilities());
119   const char kNoCapabilityText[] = "=";
120   CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
121 }
122
123 SANDBOX_TEST(Credentials, MoveToNewUserNS) {
124   Credentials creds;
125   creds.DropAllCapabilities();
126   bool moved_to_new_ns = creds.MoveToNewUserNS();
127   fprintf(stdout,
128           "Unprivileged CLONE_NEWUSER supported: %s\n",
129           moved_to_new_ns ? "true." : "false.");
130   fflush(stdout);
131   if (!moved_to_new_ns) {
132     fprintf(stdout, "This kernel does not support unprivileged namespaces. "
133             "USERNS tests will succeed without running.\n");
134     fflush(stdout);
135     return;
136   }
137   CHECK(creds.HasAnyCapability());
138   creds.DropAllCapabilities();
139   CHECK(!creds.HasAnyCapability());
140 }
141
142 SANDBOX_TEST(Credentials, SupportsUserNS) {
143   Credentials creds;
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);
148 }
149
150 SANDBOX_TEST(Credentials, UidIsPreserved) {
151   Credentials creds;
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);
164
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);
170 }
171
172 bool NewUserNSCycle(Credentials* creds) {
173   DCHECK(creds);
174   if (!creds->MoveToNewUserNS() ||
175       !creds->HasAnyCapability() ||
176       !creds->DropAllCapabilities() ||
177       creds->HasAnyCapability()) {
178     return false;
179   }
180   return true;
181 }
182
183 SANDBOX_TEST(Credentials, NestedUserNS) {
184   Credentials creds;
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 "
193                                   << i << ".";
194   }
195 }
196
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());
203 }
204
205 SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(DropFileSystemAccessIsSafe)) {
206   Credentials creds;
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));
216 }
217
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)) {
221   Credentials creds;
222   CHECK(creds.DropAllCapabilities());
223   // Probably missing kernel support.
224   if (!creds.MoveToNewUserNS()) return;
225   CHECK(creds.DropFileSystemAccess());
226   CHECK(creds.DropAllCapabilities());
227
228   // The kernel should now prevent us from regaining capabilities because we
229   // are in a chroot.
230   CHECK(!Credentials::SupportsNewUserNS());
231   CHECK(!creds.MoveToNewUserNS());
232 }
233
234 }  // namespace.
235
236 }  // namespace sandbox.