credentials_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/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 21namespace sandbox { 22 23namespace { 24 25bool 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 32bool 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(".", ¤t)); 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. 52TEST(Credentials, CreateAndDestroy) { 53 { 54 Credentials cred1; 55 (void) cred1; 56 } 57 scoped_ptr<Credentials> cred2(new Credentials); 58} 59 60TEST(Credentials, HasOpenDirectory) { 61 Credentials creds; 62 // No open directory should exist at startup. 63 EXPECT_FALSE(creds.HasOpenDirectory(-1)); 64 { 65 // Have a "/dev" file descriptor around. 66 int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY); 67 base::ScopedFD dev_fd_closer(dev_fd); 68 EXPECT_TRUE(creds.HasOpenDirectory(-1)); 69 } 70 EXPECT_FALSE(creds.HasOpenDirectory(-1)); 71} 72 73TEST(Credentials, HasOpenDirectoryWithFD) { 74 Credentials creds; 75 76 int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY); 77 base::ScopedFD proc_fd_closer(proc_fd); 78 ASSERT_LE(0, proc_fd); 79 80 // Don't pass |proc_fd|, an open directory (proc_fd) should 81 // be detected. 82 EXPECT_TRUE(creds.HasOpenDirectory(-1)); 83 // Pass |proc_fd| and no open directory should be detected. 84 EXPECT_FALSE(creds.HasOpenDirectory(proc_fd)); 85 86 { 87 // Have a "/dev" file descriptor around. 88 int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY); 89 base::ScopedFD dev_fd_closer(dev_fd); 90 EXPECT_TRUE(creds.HasOpenDirectory(proc_fd)); 91 } 92 93 // The "/dev" file descriptor should now be closed, |proc_fd| is the only 94 // directory file descriptor open. 95 EXPECT_FALSE(creds.HasOpenDirectory(proc_fd)); 96} 97 98SANDBOX_TEST(Credentials, DropAllCaps) { 99 Credentials creds; 100 CHECK(creds.DropAllCapabilities()); 101 CHECK(!creds.HasAnyCapability()); 102} 103 104SANDBOX_TEST(Credentials, GetCurrentCapString) { 105 Credentials creds; 106 CHECK(creds.DropAllCapabilities()); 107 const char kNoCapabilityText[] = "="; 108 CHECK(*creds.GetCurrentCapString() == kNoCapabilityText); 109} 110 111SANDBOX_TEST(Credentials, MoveToNewUserNS) { 112 Credentials creds; 113 creds.DropAllCapabilities(); 114 bool moved_to_new_ns = creds.MoveToNewUserNS(); 115 fprintf(stdout, 116 "Unprivileged CLONE_NEWUSER supported: %s\n", 117 moved_to_new_ns ? "true." : "false."); 118 fflush(stdout); 119 if (!moved_to_new_ns) { 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 130SANDBOX_TEST(Credentials, SupportsUserNS) { 131 Credentials creds; 132 creds.DropAllCapabilities(); 133 bool user_ns_supported = Credentials::SupportsNewUserNS(); 134 bool moved_to_new_ns = creds.MoveToNewUserNS(); 135 CHECK_EQ(user_ns_supported, moved_to_new_ns); 136} 137 138SANDBOX_TEST(Credentials, UidIsPreserved) { 139 Credentials creds; 140 creds.DropAllCapabilities(); 141 uid_t old_ruid, old_euid, old_suid; 142 gid_t old_rgid, old_egid, old_sgid; 143 PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid)); 144 PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid)); 145 // Probably missing kernel support. 146 if (!creds.MoveToNewUserNS()) return; 147 uid_t new_ruid, new_euid, new_suid; 148 PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid)); 149 CHECK(old_ruid == new_ruid); 150 CHECK(old_euid == new_euid); 151 CHECK(old_suid == new_suid); 152 153 gid_t new_rgid, new_egid, new_sgid; 154 PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid)); 155 CHECK(old_rgid == new_rgid); 156 CHECK(old_egid == new_egid); 157 CHECK(old_sgid == new_sgid); 158} 159 160bool NewUserNSCycle(Credentials* creds) { 161 DCHECK(creds); 162 if (!creds->MoveToNewUserNS() || 163 !creds->HasAnyCapability() || 164 !creds->DropAllCapabilities() || 165 creds->HasAnyCapability()) { 166 return false; 167 } 168 return true; 169} 170 171SANDBOX_TEST(Credentials, NestedUserNS) { 172 Credentials creds; 173 CHECK(creds.DropAllCapabilities()); 174 // Probably missing kernel support. 175 if (!creds.MoveToNewUserNS()) return; 176 creds.DropAllCapabilities(); 177 // As of 3.12, the kernel has a limit of 32. See create_user_ns(). 178 const int kNestLevel = 10; 179 for (int i = 0; i < kNestLevel; ++i) { 180 CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration " 181 << i << "."; 182 } 183} 184 185// Test the WorkingDirectoryIsRoot() helper. 186TEST(Credentials, CanDetectRoot) { 187 ASSERT_EQ(0, chdir("/proc/")); 188 ASSERT_FALSE(WorkingDirectoryIsRoot()); 189 ASSERT_EQ(0, chdir("/")); 190 ASSERT_TRUE(WorkingDirectoryIsRoot()); 191} 192 193SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) { 194 Credentials creds; 195 CHECK(creds.DropAllCapabilities()); 196 // Probably missing kernel support. 197 if (!creds.MoveToNewUserNS()) return; 198 CHECK(creds.DropFileSystemAccess()); 199 CHECK(!DirectoryExists("/proc")); 200 CHECK(WorkingDirectoryIsRoot()); 201 // We want the chroot to never have a subdirectory. A subdirectory 202 // could allow a chroot escape. 203 CHECK_NE(0, mkdir("/test", 0700)); 204} 205 206// Check that after dropping filesystem access and dropping privileges 207// it is not possible to regain capabilities. 208SANDBOX_TEST(Credentials, CannotRegainPrivileges) { 209 Credentials creds; 210 CHECK(creds.DropAllCapabilities()); 211 // Probably missing kernel support. 212 if (!creds.MoveToNewUserNS()) return; 213 CHECK(creds.DropFileSystemAccess()); 214 CHECK(creds.DropAllCapabilities()); 215 216 // The kernel should now prevent us from regaining capabilities because we 217 // are in a chroot. 218 CHECK(!Credentials::SupportsNewUserNS()); 219 CHECK(!creds.MoveToNewUserNS()); 220} 221 222} // namespace. 223 224} // namespace sandbox. 225