• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(".", &current));
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