broker_process_unittest.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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/broker_process.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <sys/resource.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <sys/wait.h>
13#include <unistd.h>
14
15#include <algorithm>
16#include <string>
17#include <vector>
18
19#include "base/basictypes.h"
20#include "base/bind.h"
21#include "base/file_util.h"
22#include "base/files/scoped_file.h"
23#include "base/logging.h"
24#include "base/memory/scoped_ptr.h"
25#include "base/posix/eintr_wrapper.h"
26#include "base/posix/unix_domain_socket_linux.h"
27#include "sandbox/linux/tests/test_utils.h"
28#include "sandbox/linux/tests/unit_tests.h"
29#include "testing/gtest/include/gtest/gtest.h"
30
31namespace sandbox {
32
33class BrokerProcessTestHelper {
34 public:
35  static int get_ipc_socketpair(const BrokerProcess* broker) {
36    return broker->ipc_socketpair_;
37  }
38};
39
40namespace {
41
42// Creates and open a temporary file on creation and closes
43// and removes it on destruction.
44// Unlike base/ helpers, this does not require JNI on Android.
45class ScopedTemporaryFile {
46 public:
47  ScopedTemporaryFile()
48      : fd_(-1) {
49#if defined(OS_ANDROID)
50    static const char file_template[] = "/data/local/tmp/ScopedTempFileXXXXXX";
51#else
52    static const char file_template[] = "/tmp/ScopedTempFileXXXXXX";
53#endif  // defined(OS_ANDROID)
54    COMPILE_ASSERT(sizeof(full_file_name_) >= sizeof(file_template),
55                   full_file_name_is_large_enough);
56    memcpy(full_file_name_, file_template, sizeof(file_template));
57    fd_ = mkstemp(full_file_name_);
58    CHECK_LE(0, fd_);
59  }
60  ~ScopedTemporaryFile() {
61    CHECK_EQ(0, unlink(full_file_name_));
62    CHECK_EQ(0, IGNORE_EINTR(close(fd_)));
63  }
64
65  int fd() const { return fd_; }
66  const char* full_file_name() const { return full_file_name_; }
67
68 private:
69  int fd_;
70  char full_file_name_[128];
71  DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
72};
73
74bool NoOpCallback() { return true; }
75
76}  // namespace
77
78TEST(BrokerProcess, CreateAndDestroy) {
79  std::vector<std::string> read_whitelist;
80  read_whitelist.push_back("/proc/cpuinfo");
81
82  scoped_ptr<BrokerProcess> open_broker(
83      new BrokerProcess(EPERM, read_whitelist, std::vector<std::string>()));
84  ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
85
86  ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
87  // Destroy the broker and check it has exited properly.
88  open_broker.reset();
89  ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
90}
91
92TEST(BrokerProcess, TestOpenAccessNull) {
93  const std::vector<std::string> empty;
94  BrokerProcess open_broker(EPERM, empty, empty);
95  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
96
97  int fd = open_broker.Open(NULL, O_RDONLY);
98  ASSERT_EQ(fd, -EFAULT);
99
100  int ret = open_broker.Access(NULL, F_OK);
101  ASSERT_EQ(ret, -EFAULT);
102}
103
104void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
105  const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1";
106  // We can't debug the init process, and shouldn't be able to access
107  // its auxv file.
108  const char kR_WhiteListedButDenied[] = "/proc/1/auxv";
109  const char kW_WhiteListed[] = "/proc/DOESNOTEXIST2";
110  const char kRW_WhiteListed[] = "/proc/DOESNOTEXIST3";
111  const char k_NotWhitelisted[] = "/proc/DOESNOTEXIST4";
112
113  std::vector<std::string> read_whitelist;
114  read_whitelist.push_back(kR_WhiteListed);
115  read_whitelist.push_back(kR_WhiteListedButDenied);
116  read_whitelist.push_back(kRW_WhiteListed);
117
118  std::vector<std::string> write_whitelist;
119  write_whitelist.push_back(kW_WhiteListed);
120  write_whitelist.push_back(kRW_WhiteListed);
121
122  BrokerProcess open_broker(denied_errno,
123                            read_whitelist,
124                            write_whitelist,
125                            fast_check_in_client);
126  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
127
128  int fd = -1;
129  fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
130  ASSERT_EQ(fd, -ENOENT);
131  fd = open_broker.Open(kR_WhiteListed, O_WRONLY);
132  ASSERT_EQ(fd, -denied_errno);
133  fd = open_broker.Open(kR_WhiteListed, O_RDWR);
134  ASSERT_EQ(fd, -denied_errno);
135  int ret = -1;
136  ret = open_broker.Access(kR_WhiteListed, F_OK);
137  ASSERT_EQ(ret, -ENOENT);
138  ret = open_broker.Access(kR_WhiteListed, R_OK);
139  ASSERT_EQ(ret, -ENOENT);
140  ret = open_broker.Access(kR_WhiteListed, W_OK);
141  ASSERT_EQ(ret, -denied_errno);
142  ret = open_broker.Access(kR_WhiteListed, R_OK | W_OK);
143  ASSERT_EQ(ret, -denied_errno);
144  ret = open_broker.Access(kR_WhiteListed, X_OK);
145  ASSERT_EQ(ret, -denied_errno);
146  ret = open_broker.Access(kR_WhiteListed, R_OK | X_OK);
147  ASSERT_EQ(ret, -denied_errno);
148
149  // Android sometimes runs tests as root.
150  // This part of the test requires a process that doesn't have
151  // CAP_DAC_OVERRIDE. We check against a root euid as a proxy for that.
152  if (geteuid()) {
153    fd = open_broker.Open(kR_WhiteListedButDenied, O_RDONLY);
154    // The broker process will allow this, but the normal permission system
155    // won't.
156    ASSERT_EQ(fd, -EACCES);
157    fd = open_broker.Open(kR_WhiteListedButDenied, O_WRONLY);
158    ASSERT_EQ(fd, -denied_errno);
159    fd = open_broker.Open(kR_WhiteListedButDenied, O_RDWR);
160    ASSERT_EQ(fd, -denied_errno);
161    ret = open_broker.Access(kR_WhiteListedButDenied, F_OK);
162    // The normal permission system will let us check that the file exists.
163    ASSERT_EQ(ret, 0);
164    ret = open_broker.Access(kR_WhiteListedButDenied, R_OK);
165    ASSERT_EQ(ret, -EACCES);
166    ret = open_broker.Access(kR_WhiteListedButDenied, W_OK);
167    ASSERT_EQ(ret, -denied_errno);
168    ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | W_OK);
169    ASSERT_EQ(ret, -denied_errno);
170    ret = open_broker.Access(kR_WhiteListedButDenied, X_OK);
171    ASSERT_EQ(ret, -denied_errno);
172    ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | X_OK);
173    ASSERT_EQ(ret, -denied_errno);
174  }
175
176  fd = open_broker.Open(kW_WhiteListed, O_RDONLY);
177  ASSERT_EQ(fd, -denied_errno);
178  fd = open_broker.Open(kW_WhiteListed, O_WRONLY);
179  ASSERT_EQ(fd, -ENOENT);
180  fd = open_broker.Open(kW_WhiteListed, O_RDWR);
181  ASSERT_EQ(fd, -denied_errno);
182  ret = open_broker.Access(kW_WhiteListed, F_OK);
183  ASSERT_EQ(ret, -ENOENT);
184  ret = open_broker.Access(kW_WhiteListed, R_OK);
185  ASSERT_EQ(ret, -denied_errno);
186  ret = open_broker.Access(kW_WhiteListed, W_OK);
187  ASSERT_EQ(ret, -ENOENT);
188  ret = open_broker.Access(kW_WhiteListed, R_OK | W_OK);
189  ASSERT_EQ(ret, -denied_errno);
190  ret = open_broker.Access(kW_WhiteListed, X_OK);
191  ASSERT_EQ(ret, -denied_errno);
192  ret = open_broker.Access(kW_WhiteListed, R_OK | X_OK);
193  ASSERT_EQ(ret, -denied_errno);
194
195  fd = open_broker.Open(kRW_WhiteListed, O_RDONLY);
196  ASSERT_EQ(fd, -ENOENT);
197  fd = open_broker.Open(kRW_WhiteListed, O_WRONLY);
198  ASSERT_EQ(fd, -ENOENT);
199  fd = open_broker.Open(kRW_WhiteListed, O_RDWR);
200  ASSERT_EQ(fd, -ENOENT);
201  ret = open_broker.Access(kRW_WhiteListed, F_OK);
202  ASSERT_EQ(ret, -ENOENT);
203  ret = open_broker.Access(kRW_WhiteListed, R_OK);
204  ASSERT_EQ(ret, -ENOENT);
205  ret = open_broker.Access(kRW_WhiteListed, W_OK);
206  ASSERT_EQ(ret, -ENOENT);
207  ret = open_broker.Access(kRW_WhiteListed, R_OK | W_OK);
208  ASSERT_EQ(ret, -ENOENT);
209  ret = open_broker.Access(kRW_WhiteListed, X_OK);
210  ASSERT_EQ(ret, -denied_errno);
211  ret = open_broker.Access(kRW_WhiteListed, R_OK | X_OK);
212  ASSERT_EQ(ret, -denied_errno);
213
214  fd = open_broker.Open(k_NotWhitelisted, O_RDONLY);
215  ASSERT_EQ(fd, -denied_errno);
216  fd = open_broker.Open(k_NotWhitelisted, O_WRONLY);
217  ASSERT_EQ(fd, -denied_errno);
218  fd = open_broker.Open(k_NotWhitelisted, O_RDWR);
219  ASSERT_EQ(fd, -denied_errno);
220  ret = open_broker.Access(k_NotWhitelisted, F_OK);
221  ASSERT_EQ(ret, -denied_errno);
222  ret = open_broker.Access(k_NotWhitelisted, R_OK);
223  ASSERT_EQ(ret, -denied_errno);
224  ret = open_broker.Access(k_NotWhitelisted, W_OK);
225  ASSERT_EQ(ret, -denied_errno);
226  ret = open_broker.Access(k_NotWhitelisted, R_OK | W_OK);
227  ASSERT_EQ(ret, -denied_errno);
228  ret = open_broker.Access(k_NotWhitelisted, X_OK);
229  ASSERT_EQ(ret, -denied_errno);
230  ret = open_broker.Access(k_NotWhitelisted, R_OK | X_OK);
231  ASSERT_EQ(ret, -denied_errno);
232
233  // We have some extra sanity check for clearly wrong values.
234  fd = open_broker.Open(kRW_WhiteListed, O_RDONLY | O_WRONLY | O_RDWR);
235  ASSERT_EQ(fd, -denied_errno);
236
237  // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
238  // is denied.
239  fd = open_broker.Open(kRW_WhiteListed, O_RDWR | O_CREAT);
240  ASSERT_EQ(fd, -denied_errno);
241}
242
243// Run the same thing twice. The second time, we make sure that no security
244// check is performed on the client.
245TEST(BrokerProcess, OpenFilePermsWithClientCheck) {
246  TestOpenFilePerms(true /* fast_check_in_client */, EPERM);
247  // Don't do anything here, so that ASSERT works in the subfunction as
248  // expected.
249}
250
251TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) {
252  TestOpenFilePerms(false /* fast_check_in_client */, EPERM);
253  // Don't do anything here, so that ASSERT works in the subfunction as
254  // expected.
255}
256
257// Run the same twice again, but with ENOENT instead of EPERM.
258TEST(BrokerProcess, OpenFilePermsWithClientCheckNoEnt) {
259  TestOpenFilePerms(true /* fast_check_in_client */, ENOENT);
260  // Don't do anything here, so that ASSERT works in the subfunction as
261  // expected.
262}
263
264TEST(BrokerProcess, OpenOpenFilePermsNoClientCheckNoEnt) {
265  TestOpenFilePerms(false /* fast_check_in_client */, ENOENT);
266  // Don't do anything here, so that ASSERT works in the subfunction as
267  // expected.
268}
269
270void TestOpenCpuinfo(bool fast_check_in_client) {
271  const char kFileCpuInfo[] = "/proc/cpuinfo";
272  std::vector<std::string> read_whitelist;
273  read_whitelist.push_back(kFileCpuInfo);
274
275  scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(
276      EPERM, read_whitelist, std::vector<std::string>(), fast_check_in_client));
277  ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
278
279  int fd = -1;
280  fd = open_broker->Open(kFileCpuInfo, O_RDWR);
281  base::ScopedFD fd_closer(fd);
282  ASSERT_EQ(fd, -EPERM);
283
284  // Check we can read /proc/cpuinfo.
285  int can_access = open_broker->Access(kFileCpuInfo, R_OK);
286  ASSERT_EQ(can_access, 0);
287  can_access = open_broker->Access(kFileCpuInfo, W_OK);
288  ASSERT_EQ(can_access, -EPERM);
289  // Check we can not write /proc/cpuinfo.
290
291  // Open cpuinfo via the broker.
292  int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
293  base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
294  ASSERT_GE(cpuinfo_fd, 0);
295  char buf[3];
296  memset(buf, 0, sizeof(buf));
297  int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
298  ASSERT_GT(read_len1, 0);
299
300  // Open cpuinfo directly.
301  int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
302  base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
303  ASSERT_GE(cpuinfo_fd2, 0);
304  char buf2[3];
305  memset(buf2, 1, sizeof(buf2));
306  int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
307  ASSERT_GT(read_len1, 0);
308
309  // The following is not guaranteed true, but will be in practice.
310  ASSERT_EQ(read_len1, read_len2);
311  // Compare the cpuinfo as returned by the broker with the one we opened
312  // ourselves.
313  ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
314
315  ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
316  open_broker.reset();
317  ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
318}
319
320// Run the same thing twice. The second time, we make sure that no security
321// check is performed on the client.
322TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
323  TestOpenCpuinfo(true /* fast_check_in_client */);
324  // Don't do anything here, so that ASSERT works in the subfunction as
325  // expected.
326}
327
328TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
329  TestOpenCpuinfo(false /* fast_check_in_client */);
330  // Don't do anything here, so that ASSERT works in the subfunction as
331  // expected.
332}
333
334TEST(BrokerProcess, OpenFileRW) {
335  ScopedTemporaryFile tempfile;
336  const char* tempfile_name = tempfile.full_file_name();
337
338  std::vector<std::string> whitelist;
339  whitelist.push_back(tempfile_name);
340
341  BrokerProcess open_broker(EPERM, whitelist, whitelist);
342  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
343
344  // Check we can access that file with read or write.
345  int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
346  ASSERT_EQ(can_access, 0);
347
348  int tempfile2 = -1;
349  tempfile2 = open_broker.Open(tempfile_name, O_RDWR);
350  ASSERT_GE(tempfile2, 0);
351
352  // Write to the descriptor opened by the broker.
353  char test_text[] = "TESTTESTTEST";
354  ssize_t len = write(tempfile2, test_text, sizeof(test_text));
355  ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
356
357  // Read back from the original file descriptor what we wrote through
358  // the descriptor provided by the broker.
359  char buf[1024];
360  len = read(tempfile.fd(), buf, sizeof(buf));
361
362  ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
363  ASSERT_EQ(memcmp(test_text, buf, sizeof(test_text)), 0);
364
365  ASSERT_EQ(close(tempfile2), 0);
366}
367
368// SANDBOX_TEST because the process could die with a SIGPIPE
369// and we want this to happen in a subprocess.
370SANDBOX_TEST(BrokerProcess, BrokerDied) {
371  std::vector<std::string> read_whitelist;
372  read_whitelist.push_back("/proc/cpuinfo");
373
374  BrokerProcess open_broker(EPERM,
375                            read_whitelist,
376                            std::vector<std::string>(),
377                            true /* fast_check_in_client */,
378                            true /* quiet_failures_for_tests */);
379  SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
380  const pid_t broker_pid = open_broker.broker_pid();
381  SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
382
383  // Now we check that the broker has been signaled, but do not reap it.
384  siginfo_t process_info;
385  SANDBOX_ASSERT(HANDLE_EINTR(waitid(
386                     P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
387                 0);
388  SANDBOX_ASSERT(broker_pid == process_info.si_pid);
389  SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
390  SANDBOX_ASSERT(SIGKILL == process_info.si_status);
391
392  // Check that doing Open with a dead broker won't SIGPIPE us.
393  SANDBOX_ASSERT(open_broker.Open("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
394  SANDBOX_ASSERT(open_broker.Access("/proc/cpuinfo", O_RDONLY) == -ENOMEM);
395}
396
397void TestOpenComplexFlags(bool fast_check_in_client) {
398  const char kCpuInfo[] = "/proc/cpuinfo";
399  std::vector<std::string> whitelist;
400  whitelist.push_back(kCpuInfo);
401
402  BrokerProcess open_broker(EPERM,
403                            whitelist,
404                            whitelist,
405                            fast_check_in_client);
406  ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
407  // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
408  int fd = -1;
409  int ret = 0;
410  fd = open_broker.Open(kCpuInfo, O_RDONLY);
411  ASSERT_GE(fd, 0);
412  ret = fcntl(fd, F_GETFL);
413  ASSERT_NE(-1, ret);
414  // The descriptor shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
415  ASSERT_EQ(0, ret & (O_CLOEXEC | O_NONBLOCK));
416  ASSERT_EQ(0, close(fd));
417
418  fd = open_broker.Open(kCpuInfo, O_RDONLY | O_CLOEXEC);
419  ASSERT_GE(fd, 0);
420  ret = fcntl(fd, F_GETFD);
421  ASSERT_NE(-1, ret);
422  // Important: use F_GETFD, not F_GETFL. The O_CLOEXEC flag in F_GETFL
423  // is actually not used by the kernel.
424  ASSERT_TRUE(FD_CLOEXEC & ret);
425  ASSERT_EQ(0, close(fd));
426
427  fd = open_broker.Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
428  ASSERT_GE(fd, 0);
429  ret = fcntl(fd, F_GETFL);
430  ASSERT_NE(-1, ret);
431  ASSERT_TRUE(O_NONBLOCK & ret);
432  ASSERT_EQ(0, close(fd));
433}
434
435TEST(BrokerProcess, OpenComplexFlagsWithClientCheck) {
436  TestOpenComplexFlags(true /* fast_check_in_client */);
437  // Don't do anything here, so that ASSERT works in the subfunction as
438  // expected.
439}
440
441TEST(BrokerProcess, OpenComplexFlagsNoClientCheck) {
442  TestOpenComplexFlags(false /* fast_check_in_client */);
443  // Don't do anything here, so that ASSERT works in the subfunction as
444  // expected.
445}
446
447// We need to allow noise because the broker will log when it receives our
448// bogus IPCs.
449SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, RecvMsgDescriptorLeak) {
450  // Find the four lowest available file descriptors.
451  int available_fds[4];
452  SANDBOX_ASSERT(0 == pipe(available_fds));
453  SANDBOX_ASSERT(0 == pipe(available_fds + 2));
454
455  // Save one FD to send to the broker later, and close the others.
456  base::ScopedFD message_fd(available_fds[0]);
457  for (size_t i = 1; i < arraysize(available_fds); i++) {
458    SANDBOX_ASSERT(0 == IGNORE_EINTR(close(available_fds[i])));
459  }
460
461  // Lower our file descriptor limit to just allow three more file descriptors
462  // to be allocated.  (N.B., RLIMIT_NOFILE doesn't limit the number of file
463  // descriptors a process can have: it only limits the highest value that can
464  // be assigned to newly-created descriptors allocated by the process.)
465  const rlim_t fd_limit =
466      1 + *std::max_element(available_fds,
467                            available_fds + arraysize(available_fds));
468
469  // Valgrind doesn't allow changing the hard descriptor limit, so we only
470  // change the soft descriptor limit here.
471  struct rlimit rlim;
472  SANDBOX_ASSERT(0 == getrlimit(RLIMIT_NOFILE, &rlim));
473  SANDBOX_ASSERT(fd_limit <= rlim.rlim_cur);
474  rlim.rlim_cur = fd_limit;
475  SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
476
477  static const char kCpuInfo[] = "/proc/cpuinfo";
478  std::vector<std::string> read_whitelist;
479  read_whitelist.push_back(kCpuInfo);
480
481  BrokerProcess open_broker(EPERM, read_whitelist, std::vector<std::string>());
482  SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
483
484  const int ipc_fd = BrokerProcessTestHelper::get_ipc_socketpair(&open_broker);
485  SANDBOX_ASSERT(ipc_fd >= 0);
486
487  static const char kBogus[] = "not a pickle";
488  std::vector<int> fds;
489  fds.push_back(message_fd.get());
490
491  // The broker process should only have a couple spare file descriptors
492  // available, but for good measure we send it fd_limit bogus IPCs anyway.
493  for (rlim_t i = 0; i < fd_limit; ++i) {
494    SANDBOX_ASSERT(
495        UnixDomainSocket::SendMsg(ipc_fd, kBogus, sizeof(kBogus), fds));
496  }
497
498  const int fd = open_broker.Open(kCpuInfo, O_RDONLY);
499  SANDBOX_ASSERT(fd >= 0);
500  SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
501}
502
503}  // namespace sandbox
504