Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Side by Side Diff: sandbox/linux/services/credentials.cc

Issue 853583002: Convert DropFileSystemAccess to use ForkWithFlags. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Use RAW_CHECK in the child. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | sandbox/linux/services/credentials_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sandbox/linux/services/credentials.h" 5 #include "sandbox/linux/services/credentials.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <signal.h> 8 #include <signal.h>
9 #include <stdio.h> 9 #include <stdio.h>
10 #include <sys/capability.h> 10 #include <sys/capability.h>
11 #include <sys/syscall.h> 11 #include <sys/syscall.h>
12 #include <sys/types.h> 12 #include <sys/types.h>
13 #include <sys/wait.h> 13 #include <sys/wait.h>
14 #include <unistd.h> 14 #include <unistd.h>
15 15
16 #include "base/basictypes.h" 16 #include "base/basictypes.h"
17 #include "base/bind.h" 17 #include "base/bind.h"
18 #include "base/files/file_path.h"
19 #include "base/files/file_util.h"
18 #include "base/logging.h" 20 #include "base/logging.h"
19 #include "base/posix/eintr_wrapper.h" 21 #include "base/posix/eintr_wrapper.h"
20 #include "base/strings/string_number_conversions.h" 22 #include "base/process/process.h"
21 #include "base/template_util.h" 23 #include "base/template_util.h"
22 #include "base/third_party/valgrind/valgrind.h" 24 #include "base/third_party/valgrind/valgrind.h"
23 #include "base/threading/thread.h"
24 #include "sandbox/linux/services/syscall_wrappers.h" 25 #include "sandbox/linux/services/syscall_wrappers.h"
25 26
26 namespace { 27 namespace {
27 28
28 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } 29 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
29 30
30 struct CapFreeDeleter { 31 struct CapFreeDeleter {
31 inline void operator()(cap_t cap) const { 32 inline void operator()(cap_t cap) const {
32 int ret = cap_free(cap); 33 int ret = cap_free(cap);
33 CHECK_EQ(0, ret); 34 CHECK_EQ(0, ret);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
89 PCHECK(getresuid(&ruid, &euid, &suid) == 0); 90 PCHECK(getresuid(&ruid, &euid, &suid) == 0);
90 PCHECK(getresgid(&rgid, &egid, &sgid) == 0); 91 PCHECK(getresgid(&rgid, &egid, &sgid) == 0);
91 const bool uids_are_equal = (ruid == euid) && (ruid == suid); 92 const bool uids_are_equal = (ruid == euid) && (ruid == suid);
92 const bool gids_are_equal = (rgid == egid) && (rgid == sgid); 93 const bool gids_are_equal = (rgid == egid) && (rgid == sgid);
93 if (!uids_are_equal || !gids_are_equal) return false; 94 if (!uids_are_equal || !gids_are_equal) return false;
94 if (resuid) *resuid = euid; 95 if (resuid) *resuid = euid;
95 if (resgid) *resgid = egid; 96 if (resgid) *resgid = egid;
96 return true; 97 return true;
97 } 98 }
98 99
99 // chroot() and chdir() to /proc/<tid>/fdinfo.
100 void ChrootToThreadFdInfo(base::PlatformThreadId tid, bool* result) {
101 DCHECK(result);
102 *result = false;
103
104 static_assert((base::is_same<base::PlatformThreadId, int>::value),
105 "platform thread id should be an int");
106 const std::string current_thread_fdinfo = "/proc/" +
107 base::IntToString(tid) + "/fdinfo/";
108
109 // Make extra sure that /proc/<tid>/fdinfo is unique to the thread.
110 CHECK(0 == unshare(CLONE_FILES));
111 int chroot_ret = chroot(current_thread_fdinfo.c_str());
112 if (chroot_ret) {
113 PLOG(ERROR) << "Could not chroot";
114 return;
115 }
116
117 // CWD is essentially an implicit file descriptor, so be careful to not leave
118 // it behind.
119 PCHECK(0 == chdir("/"));
120
121 *result = true;
122 return;
123 }
124
125 // chroot() to an empty dir that is "safe". To be safe, it must not contain 100 // chroot() to an empty dir that is "safe". To be safe, it must not contain
126 // any subdirectory (chroot-ing there would allow a chroot escape) and it must 101 // any subdirectory (chroot-ing there would allow a chroot escape) and it must
127 // be impossible to create an empty directory there. 102 // be impossible to create an empty directory there.
128 // We achieve this by doing the following: 103 // We achieve this by doing the following:
129 // 1. We create a new thread, which will create a new /proc/<tid>/ directory 104 // 1. We create a new process sharing file system information.
130 // 2. We chroot to /proc/<tid>/fdinfo/ 105 // 2. In the child, we chroot to /proc/self/fdinfo/
131 // This is already "safe", since fdinfo/ does not contain another directory and 106 // This is already "safe", since fdinfo/ does not contain another directory and
132 // one cannot create another directory there. 107 // one cannot create another directory there.
133 // 3. The thread dies 108 // 3. The process dies
134 // After (3) happens, the directory is not available anymore in /proc. 109 // After (3) happens, the directory is not available anymore in /proc.
135 bool ChrootToSafeEmptyDir() { 110 bool ChrootToSafeEmptyDir() {
136 base::Thread chrooter("sandbox_chrooter"); 111 // We do not use a thread because when we are in a PID namespace, we cannot
137 if (!chrooter.Start()) return false; 112 // easily get a handle to the /proc/tid directory for the thread (since /proc
138 bool is_chrooted = false; 113 // may not be aware of the PID namespace). With a process, we can just use
139 chrooter.message_loop()->PostTask(FROM_HERE, 114 // /proc/self.
140 base::Bind(&ChrootToThreadFdInfo, chrooter.thread_id(), &is_chrooted)); 115 pid_t pid = base::ForkWithFlags(SIGCHLD | CLONE_FS, nullptr, nullptr);
jln (very slow on Chromium) 2015/01/15 19:29:44 This will be quite slow (we need to duplicate all
rickyz (no longer on Chrome) 2015/01/15 22:19:05 Acknowledged. If processes being expensive ends up
141 // Make sure our task has run before committing the return value. 116 PCHECK(pid != -1);
142 chrooter.Stop(); 117 if (pid == 0) {
143 return is_chrooted; 118 // Make extra sure that /proc/self/fdinfo is unique to the process.
119 RAW_CHECK(unshare(CLONE_FILES) == 0);
jln (very slow on Chromium) 2015/01/15 19:29:44 I don't think that this is required given that it'
rickyz (no longer on Chrome) 2015/01/15 22:19:05 Good point, removed.
120 RAW_CHECK(chroot("/proc/self/fdinfo") == 0);
121
122 // CWD is essentially an implicit file descriptor, so be careful to not
123 // leave it behind.
124 RAW_CHECK(chdir("/") == 0);
125 _exit(0);
126 }
127
128 int status;
129 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
130
131 return status == 0;
144 } 132 }
145 133
146 // CHECK() that an attempt to move to a new user namespace raised an expected 134 // CHECK() that an attempt to move to a new user namespace raised an expected
147 // errno. 135 // errno.
148 void CheckCloneNewUserErrno(int error) { 136 void CheckCloneNewUserErrno(int error) {
149 // EPERM can happen if already in a chroot. EUSERS if too many nested 137 // EPERM can happen if already in a chroot. EUSERS if too many nested
150 // namespaces are used. EINVAL for kernels that don't support the feature. 138 // namespaces are used. EINVAL for kernels that don't support the feature.
151 // Valgrind will ENOSYS unshare(). 139 // Valgrind will ENOSYS unshare().
152 PCHECK(error == EPERM || error == EUSERS || error == EINVAL || 140 PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
153 error == ENOSYS); 141 error == ENOSYS);
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 DCHECK(GetRESIds(NULL, NULL)); 224 DCHECK(GetRESIds(NULL, NULL));
237 const char kGidMapFile[] = "/proc/self/gid_map"; 225 const char kGidMapFile[] = "/proc/self/gid_map";
238 const char kUidMapFile[] = "/proc/self/uid_map"; 226 const char kUidMapFile[] = "/proc/self/uid_map";
239 CHECK(WriteToIdMapFile(kGidMapFile, gid)); 227 CHECK(WriteToIdMapFile(kGidMapFile, gid));
240 CHECK(WriteToIdMapFile(kUidMapFile, uid)); 228 CHECK(WriteToIdMapFile(kUidMapFile, uid));
241 DCHECK(GetRESIds(NULL, NULL)); 229 DCHECK(GetRESIds(NULL, NULL));
242 return true; 230 return true;
243 } 231 }
244 232
245 bool Credentials::DropFileSystemAccess() { 233 bool Credentials::DropFileSystemAccess() {
246 return ChrootToSafeEmptyDir(); 234 return ChrootToSafeEmptyDir() &&
jln (very slow on Chromium) 2015/01/15 19:29:44 If ChrootToSafeEmptyDir() succeeds, it's critical
rickyz (no longer on Chrome) 2015/01/15 22:19:05 Done.
235 !base::DirectoryExists(base::FilePath("/proc"));
247 } 236 }
248 237
249 } // namespace sandbox. 238 } // namespace sandbox.
OLDNEW
« no previous file with comments | « no previous file | sandbox/linux/services/credentials_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698