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(".", &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.
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