1// Copyright 2014 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 <sched.h>
6#include <stdio.h>
7#include <string.h>
8#include <sys/socket.h>
9#include <sys/syscall.h>
10#include <sys/wait.h>
11#include <unistd.h>
12
13#include <vector>
14
15#include "base/files/scoped_file.h"
16#include "base/logging.h"
17#include "base/memory/scoped_vector.h"
18#include "base/posix/eintr_wrapper.h"
19#include "base/posix/unix_domain_socket_linux.h"
20#include "base/process/process_handle.h"
21#include "sandbox/linux/tests/unit_tests.h"
22
23// Additional tests for base's UnixDomainSocket to make sure it behaves
24// correctly in the presence of sandboxing functionality (e.g., receiving
25// PIDs across namespaces).
26
27namespace sandbox {
28
29namespace {
30
31const char kHello[] = "hello";
32
33// If the calling process isn't root, then try using unshare(CLONE_NEWUSER)
34// to fake it.
35void FakeRoot() {
36  // If we're already root, then allow test to proceed.
37  if (geteuid() == 0)
38    return;
39
40  // Otherwise hope the kernel supports unprivileged namespaces.
41  if (unshare(CLONE_NEWUSER) == 0)
42    return;
43
44  printf("Permission to use CLONE_NEWPID missing; skipping test.\n");
45  UnitTests::IgnoreThisTest();
46}
47
48void WaitForExit(pid_t pid) {
49  int status;
50  CHECK_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
51  CHECK(WIFEXITED(status));
52  CHECK_EQ(0, WEXITSTATUS(status));
53}
54
55base::ProcessId GetParentProcessId(base::ProcessId pid) {
56  // base::GetParentProcessId() is defined as taking a ProcessHandle instead of
57  // a ProcessId, even though it's a POSIX-only function and IDs and Handles
58  // are both simply pid_t on POSIX... :/
59  base::ProcessHandle handle;
60  CHECK(base::OpenProcessHandle(pid, &handle));
61  base::ProcessId ret = base::GetParentProcessId(pid);
62  base::CloseProcessHandle(handle);
63  return ret;
64}
65
66// SendHello sends a "hello" to socket fd, and then blocks until the recipient
67// acknowledges it by calling RecvHello.
68void SendHello(int fd) {
69  int pipe_fds[2];
70  CHECK_EQ(0, pipe(pipe_fds));
71  base::ScopedFD read_pipe(pipe_fds[0]);
72  base::ScopedFD write_pipe(pipe_fds[1]);
73
74  std::vector<int> send_fds;
75  send_fds.push_back(write_pipe.get());
76  CHECK(UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds));
77
78  write_pipe.reset();
79
80  // Block until receiver closes their end of the pipe.
81  char ch;
82  CHECK_EQ(0, HANDLE_EINTR(read(read_pipe.get(), &ch, 1)));
83}
84
85// RecvHello receives and acknowledges a "hello" on socket fd, and returns the
86// process ID of the sender in sender_pid.  Optionally, write_pipe can be used
87// to return a file descriptor, and the acknowledgement will be delayed until
88// the descriptor is closed.
89// (Implementation details: SendHello allocates a new pipe, sends us the writing
90// end alongside the "hello" message, and then blocks until we close the writing
91// end of the pipe.)
92void RecvHello(int fd,
93               base::ProcessId* sender_pid,
94               base::ScopedFD* write_pipe = NULL) {
95  // Extra receiving buffer space to make sure we really received only
96  // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
97  char buf[sizeof(kHello) + 1];
98  ScopedVector<base::ScopedFD> message_fds;
99  ssize_t n = UnixDomainSocket::RecvMsgWithPid(
100      fd, buf, sizeof(buf), &message_fds, sender_pid);
101  CHECK_EQ(sizeof(kHello), static_cast<size_t>(n));
102  CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
103  CHECK_EQ(1U, message_fds.size());
104  if (write_pipe)
105    write_pipe->swap(*message_fds[0]);
106}
107
108// Check that receiving PIDs works across a fork().
109SANDBOX_TEST(UnixDomainSocketTest, Fork) {
110  int fds[2];
111  CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
112  base::ScopedFD recv_sock(fds[0]);
113  base::ScopedFD send_sock(fds[1]);
114
115  CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
116
117  const pid_t pid = fork();
118  CHECK_NE(-1, pid);
119  if (pid == 0) {
120    // Child process.
121    recv_sock.reset();
122    SendHello(send_sock.get());
123    _exit(0);
124  }
125
126  // Parent process.
127  send_sock.reset();
128
129  base::ProcessId sender_pid;
130  RecvHello(recv_sock.get(), &sender_pid);
131  CHECK_EQ(pid, sender_pid);
132
133  WaitForExit(pid);
134}
135
136// Similar to Fork above, but forking the child into a new pid namespace.
137SANDBOX_TEST(UnixDomainSocketTest, Namespace) {
138  FakeRoot();
139
140  int fds[2];
141  CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
142  base::ScopedFD recv_sock(fds[0]);
143  base::ScopedFD send_sock(fds[1]);
144
145  CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
146
147  const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
148  CHECK_NE(-1, pid);
149  if (pid == 0) {
150    // Child process.
151    recv_sock.reset();
152
153    // Check that we think we're pid 1 in our new namespace.
154    CHECK_EQ(1, syscall(__NR_getpid));
155
156    SendHello(send_sock.get());
157    _exit(0);
158  }
159
160  // Parent process.
161  send_sock.reset();
162
163  base::ProcessId sender_pid;
164  RecvHello(recv_sock.get(), &sender_pid);
165  CHECK_EQ(pid, sender_pid);
166
167  WaitForExit(pid);
168}
169
170// Again similar to Fork, but now with nested PID namespaces.
171SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) {
172  FakeRoot();
173
174  int fds[2];
175  CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
176  base::ScopedFD recv_sock(fds[0]);
177  base::ScopedFD send_sock(fds[1]);
178
179  CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
180
181  const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
182  CHECK_NE(-1, pid);
183  if (pid == 0) {
184    // Child process.
185    recv_sock.reset();
186
187    const pid_t pid2 = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
188    CHECK_NE(-1, pid2);
189
190    if (pid2 != 0) {
191      // Wait for grandchild to run to completion; see comments below.
192      WaitForExit(pid2);
193
194      // Fallthrough once grandchild has sent its hello and exited.
195    }
196
197    // Check that we think we're pid 1.
198    CHECK_EQ(1, syscall(__NR_getpid));
199
200    SendHello(send_sock.get());
201    _exit(0);
202  }
203
204  // Parent process.
205  send_sock.reset();
206
207  // We have two messages to receive: first from the grand-child,
208  // then from the child.
209  for (unsigned iteration = 0; iteration < 2; ++iteration) {
210    base::ProcessId sender_pid;
211    base::ScopedFD pipe_fd;
212    RecvHello(recv_sock.get(), &sender_pid, &pipe_fd);
213
214    // We need our child and grandchild processes to both be alive for
215    // GetParentProcessId() to return a valid pid, hence the pipe trickery.
216    // (On the first iteration, grandchild is blocked reading from the pipe
217    // until we close it, and child is blocked waiting for grandchild to exit.)
218    switch (iteration) {
219      case 0:  // Grandchild's message
220        // Check that sender_pid refers to our grandchild by checking that pid
221        // (our child) is its parent.
222        CHECK_EQ(pid, GetParentProcessId(sender_pid));
223        break;
224      case 1:  // Child's message
225        CHECK_EQ(pid, sender_pid);
226        break;
227      default:
228        NOTREACHED();
229    }
230  }
231
232  WaitForExit(pid);
233}
234
235// Tests that GetPeerPid() returns 0 if the peer does not exist in caller's
236// namespace.
237SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) {
238  FakeRoot();
239
240  int fds[2];
241  CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
242  base::ScopedFD send_sock(fds[0]);
243  base::ScopedFD recv_sock(fds[1]);
244
245  CHECK(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
246
247  const pid_t pid = syscall(__NR_clone, CLONE_NEWPID | SIGCHLD, 0, 0, 0);
248  CHECK_NE(-1, pid);
249  if (pid == 0) {
250    // Child process.
251    send_sock.reset();
252
253    base::ProcessId sender_pid;
254    RecvHello(recv_sock.get(), &sender_pid);
255    CHECK_EQ(0, sender_pid);
256    _exit(0);
257  }
258
259  // Parent process.
260  recv_sock.reset();
261  SendHello(send_sock.get());
262  WaitForExit(pid);
263}
264
265}  // namespace
266
267}  // namespace sandbox
268