Fix crash on app list start page keyboard navigation with <4 apps.
[chromium-blink-merge.git] / sandbox / linux / services / credentials.cc
blobed21fd192bd0c7eda1ffaadcbf6583c26f18751d
1 // Copyright (c) 2013 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.
5 #include "sandbox/linux/services/credentials.h"
7 #include <errno.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <sys/capability.h>
11 #include <sys/syscall.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
16 #include "base/basictypes.h"
17 #include "base/bind.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
20 #include "base/logging.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/process/launch.h"
23 #include "base/template_util.h"
24 #include "base/third_party/valgrind/valgrind.h"
25 #include "sandbox/linux/services/namespace_utils.h"
26 #include "sandbox/linux/services/syscall_wrappers.h"
28 namespace sandbox {
30 namespace {
32 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
34 struct CapFreeDeleter {
35 inline void operator()(cap_t cap) const {
36 int ret = cap_free(cap);
37 CHECK_EQ(0, ret);
41 // Wrapper to manage libcap2's cap_t type.
42 typedef scoped_ptr<typeof(*((cap_t)0)), CapFreeDeleter> ScopedCap;
44 struct CapTextFreeDeleter {
45 inline void operator()(char* cap_text) const {
46 int ret = cap_free(cap_text);
47 CHECK_EQ(0, ret);
51 // Wrapper to manage the result from libcap2's cap_from_text().
52 typedef scoped_ptr<char, CapTextFreeDeleter> ScopedCapText;
54 // Checks that the set of RES-uids and the set of RES-gids have
55 // one element each and return that element in |resuid| and |resgid|
56 // respectively. It's ok to pass NULL as one or both of the ids.
57 bool GetRESIds(uid_t* resuid, gid_t* resgid) {
58 uid_t ruid, euid, suid;
59 gid_t rgid, egid, sgid;
60 PCHECK(getresuid(&ruid, &euid, &suid) == 0);
61 PCHECK(getresgid(&rgid, &egid, &sgid) == 0);
62 const bool uids_are_equal = (ruid == euid) && (ruid == suid);
63 const bool gids_are_equal = (rgid == egid) && (rgid == sgid);
64 if (!uids_are_equal || !gids_are_equal) return false;
65 if (resuid) *resuid = euid;
66 if (resgid) *resgid = egid;
67 return true;
70 const int kExitSuccess = 0;
72 int ChrootToSelfFdinfo(void*) {
73 RAW_CHECK(chroot("/proc/self/fdinfo/") == 0);
75 // CWD is essentially an implicit file descriptor, so be careful to not
76 // leave it behind.
77 RAW_CHECK(chdir("/") == 0);
78 _exit(kExitSuccess);
81 // chroot() to an empty dir that is "safe". To be safe, it must not contain
82 // any subdirectory (chroot-ing there would allow a chroot escape) and it must
83 // be impossible to create an empty directory there.
84 // We achieve this by doing the following:
85 // 1. We create a new process sharing file system information.
86 // 2. In the child, we chroot to /proc/self/fdinfo/
87 // This is already "safe", since fdinfo/ does not contain another directory and
88 // one cannot create another directory there.
89 // 3. The process dies
90 // After (3) happens, the directory is not available anymore in /proc.
91 bool ChrootToSafeEmptyDir() {
92 // We need to chroot to a fdinfo that is unique to a process and have that
93 // process die.
94 // 1. We don't want to simply fork() because duplicating the page tables is
95 // slow with a big address space.
96 // 2. We do not use a regular thread (that would unshare CLONE_FILES) because
97 // when we are in a PID namespace, we cannot easily get a handle to the
98 // /proc/tid directory for the thread (since /proc may not be aware of the
99 // PID namespace). With a process, we can just use /proc/self.
100 pid_t pid = -1;
101 char stack_buf[PTHREAD_STACK_MIN];
102 #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
103 defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
104 // The stack grows downward.
105 void* stack = stack_buf + sizeof(stack_buf);
106 #else
107 #error "Unsupported architecture"
108 #endif
109 pid = clone(ChrootToSelfFdinfo, stack,
110 CLONE_VM | CLONE_VFORK | CLONE_FS | SIGCHLD, nullptr, nullptr,
111 nullptr, nullptr);
112 PCHECK(pid != -1);
114 int status = -1;
115 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
117 return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
120 // CHECK() that an attempt to move to a new user namespace raised an expected
121 // errno.
122 void CheckCloneNewUserErrno(int error) {
123 // EPERM can happen if already in a chroot. EUSERS if too many nested
124 // namespaces are used. EINVAL for kernels that don't support the feature.
125 // Valgrind will ENOSYS unshare().
126 PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
127 error == ENOSYS);
130 } // namespace.
132 bool Credentials::DropAllCapabilities() {
133 ScopedCap cap(cap_init());
134 CHECK(cap);
135 PCHECK(0 == cap_set_proc(cap.get()));
136 CHECK(!HasAnyCapability());
137 // We never let this function fail.
138 return true;
141 bool Credentials::HasAnyCapability() {
142 ScopedCap current_cap(cap_get_proc());
143 CHECK(current_cap);
144 ScopedCap empty_cap(cap_init());
145 CHECK(empty_cap);
146 return cap_compare(current_cap.get(), empty_cap.get()) != 0;
149 scoped_ptr<std::string> Credentials::GetCurrentCapString() {
150 ScopedCap current_cap(cap_get_proc());
151 CHECK(current_cap);
152 ScopedCapText cap_text(cap_to_text(current_cap.get(), NULL));
153 CHECK(cap_text);
154 return scoped_ptr<std::string> (new std::string(cap_text.get()));
157 // static
158 bool Credentials::CanCreateProcessInNewUserNS() {
159 // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
160 // so always consider UserNS unsupported there.
161 if (IsRunningOnValgrind()) {
162 return false;
165 // This is roughly a fork().
166 const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0);
168 if (pid == -1) {
169 CheckCloneNewUserErrno(errno);
170 return false;
173 // The parent process could have had threads. In the child, these threads
174 // have disappeared. Make sure to not do anything in the child, as this is a
175 // fragile execution environment.
176 if (pid == 0) {
177 _exit(kExitSuccess);
180 // Always reap the child.
181 int status = -1;
182 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
183 CHECK(WIFEXITED(status));
184 CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
186 // clone(2) succeeded, we can use CLONE_NEWUSER.
187 return true;
190 bool Credentials::MoveToNewUserNS() {
191 uid_t uid;
192 gid_t gid;
193 if (!GetRESIds(&uid, &gid)) {
194 // If all the uids (or gids) are not equal to each other, the security
195 // model will most likely confuse the caller, abort.
196 DVLOG(1) << "uids or gids differ!";
197 return false;
199 int ret = unshare(CLONE_NEWUSER);
200 if (ret) {
201 const int unshare_errno = errno;
202 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
203 << "on this kernel.";
204 CheckCloneNewUserErrno(unshare_errno);
205 return false;
208 if (NamespaceUtils::KernelSupportsDenySetgroups()) {
209 PCHECK(NamespaceUtils::DenySetgroups());
212 // The current {r,e,s}{u,g}id is now an overflow id (c.f.
213 // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
214 DCHECK(GetRESIds(NULL, NULL));
215 const char kGidMapFile[] = "/proc/self/gid_map";
216 const char kUidMapFile[] = "/proc/self/uid_map";
217 PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
218 PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
219 DCHECK(GetRESIds(NULL, NULL));
220 return true;
223 bool Credentials::DropFileSystemAccess() {
224 CHECK(ChrootToSafeEmptyDir());
225 CHECK(!base::DirectoryExists(base::FilePath("/proc")));
226 // We never let this function fail.
227 return true;
230 } // namespace sandbox.