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 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 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. 53 TEST(Credentials, CreateAndDestroy) { 54 { 55 Credentials cred1; 56 (void) cred1; 57 } 58 scoped_ptr<Credentials> cred2(new Credentials); 59 } 60 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 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 99 SANDBOX_TEST(Credentials, DropAllCaps) { 100 Credentials creds; 101 CHECK(creds.DropAllCapabilities()); 102 CHECK(!creds.HasAnyCapability()); 103 } 104 105 SANDBOX_TEST(Credentials, GetCurrentCapString) { 106 Credentials creds; 107 CHECK(creds.DropAllCapabilities()); 108 const char kNoCapabilityText[] = "="; 109 CHECK(*creds.GetCurrentCapString() == kNoCapabilityText); 110 } 111 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 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 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 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. 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 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. 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