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"
10 #include <sys/capability.h>
11 #include <sys/syscall.h>
12 #include <sys/types.h>
16 #include "base/basictypes.h"
17 #include "base/bind.h"
18 #include "base/logging.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/template_util.h"
22 #include "base/third_party/valgrind/valgrind.h"
23 #include "base/threading/thread.h"
24 #include "sandbox/linux/services/syscall_wrappers.h"
28 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND
; }
30 struct CapFreeDeleter
{
31 inline void operator()(cap_t cap
) const {
32 int ret
= cap_free(cap
);
37 // Wrapper to manage libcap2's cap_t type.
38 typedef scoped_ptr
<typeof(*((cap_t
)0)), CapFreeDeleter
> ScopedCap
;
40 struct CapTextFreeDeleter
{
41 inline void operator()(char* cap_text
) const {
42 int ret
= cap_free(cap_text
);
47 // Wrapper to manage the result from libcap2's cap_from_text().
48 typedef scoped_ptr
<char, CapTextFreeDeleter
> ScopedCapText
;
51 inline void operator()(FILE* f
) const {
53 PCHECK(0 == fclose(f
));
57 // Don't use ScopedFILE in base since it doesn't check fclose().
58 // TODO(jln): fix base/.
59 typedef scoped_ptr
<FILE, FILECloser
> ScopedFILE
;
61 static_assert((base::is_same
<uid_t
, gid_t
>::value
),
62 "uid_t and gid_t should be the same type");
63 // generic_id_t can be used for either uid_t or gid_t.
64 typedef uid_t generic_id_t
;
66 // Write a uid or gid mapping from |id| to |id| in |map_file|.
67 bool WriteToIdMapFile(const char* map_file
, generic_id_t id
) {
68 ScopedFILE
f(fopen(map_file
, "w"));
70 const uid_t inside_id
= id
;
71 const uid_t outside_id
= id
;
72 int num
= fprintf(f
.get(), "%d %d 1\n", inside_id
, outside_id
);
73 if (num
< 0) return false;
74 // Manually call fflush() to catch permission failures.
75 int ret
= fflush(f
.get());
77 VLOG(1) << "Could not write to id map file";
83 // Checks that the set of RES-uids and the set of RES-gids have
84 // one element each and return that element in |resuid| and |resgid|
85 // respectively. It's ok to pass NULL as one or both of the ids.
86 bool GetRESIds(uid_t
* resuid
, gid_t
* resgid
) {
87 uid_t ruid
, euid
, suid
;
88 gid_t rgid
, egid
, sgid
;
89 PCHECK(getresuid(&ruid
, &euid
, &suid
) == 0);
90 PCHECK(getresgid(&rgid
, &egid
, &sgid
) == 0);
91 const bool uids_are_equal
= (ruid
== euid
) && (ruid
== suid
);
92 const bool gids_are_equal
= (rgid
== egid
) && (rgid
== sgid
);
93 if (!uids_are_equal
|| !gids_are_equal
) return false;
94 if (resuid
) *resuid
= euid
;
95 if (resgid
) *resgid
= egid
;
99 // chroot() and chdir() to /proc/<tid>/fdinfo.
100 void ChrootToThreadFdInfo(base::PlatformThreadId tid
, bool* result
) {
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/";
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());
113 PLOG(ERROR
) << "Could not chroot";
117 // CWD is essentially an implicit file descriptor, so be careful to not leave
119 PCHECK(0 == chdir("/"));
125 // 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
127 // be impossible to create an empty directory there.
128 // We achieve this by doing the following:
129 // 1. We create a new thread, which will create a new /proc/<tid>/ directory
130 // 2. We chroot to /proc/<tid>/fdinfo/
131 // This is already "safe", since fdinfo/ does not contain another directory and
132 // one cannot create another directory there.
133 // 3. The thread dies
134 // After (3) happens, the directory is not available anymore in /proc.
135 bool ChrootToSafeEmptyDir() {
136 base::Thread
chrooter("sandbox_chrooter");
137 if (!chrooter
.Start()) return false;
138 bool is_chrooted
= false;
139 chrooter
.message_loop()->PostTask(FROM_HERE
,
140 base::Bind(&ChrootToThreadFdInfo
, chrooter
.thread_id(), &is_chrooted
));
141 // Make sure our task has run before committing the return value.
146 // CHECK() that an attempt to move to a new user namespace raised an expected
148 void CheckCloneNewUserErrno(int error
) {
149 // 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.
151 // Valgrind will ENOSYS unshare().
152 PCHECK(error
== EPERM
|| error
== EUSERS
|| error
== EINVAL
||
160 bool Credentials::DropAllCapabilities() {
161 ScopedCap
cap(cap_init());
163 PCHECK(0 == cap_set_proc(cap
.get()));
164 CHECK(!HasAnyCapability());
165 // We never let this function fail.
169 bool Credentials::HasAnyCapability() {
170 ScopedCap
current_cap(cap_get_proc());
172 ScopedCap
empty_cap(cap_init());
174 return cap_compare(current_cap
.get(), empty_cap
.get()) != 0;
177 scoped_ptr
<std::string
> Credentials::GetCurrentCapString() {
178 ScopedCap
current_cap(cap_get_proc());
180 ScopedCapText
cap_text(cap_to_text(current_cap
.get(), NULL
));
182 return scoped_ptr
<std::string
> (new std::string(cap_text
.get()));
186 bool Credentials::SupportsNewUserNS() {
187 // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
188 // so always consider UserNS unsupported there.
189 if (IsRunningOnValgrind()) {
193 // This is roughly a fork().
194 const pid_t pid
= sys_clone(CLONE_NEWUSER
| SIGCHLD
, 0, 0, 0, 0);
197 CheckCloneNewUserErrno(errno
);
201 // The parent process could have had threads. In the child, these threads
202 // have disappeared. Make sure to not do anything in the child, as this is a
203 // fragile execution environment.
208 // Always reap the child.
210 PCHECK(0 == HANDLE_EINTR(waitid(P_PID
, pid
, &infop
, WEXITED
)));
212 // clone(2) succeeded, we can use CLONE_NEWUSER.
216 bool Credentials::MoveToNewUserNS() {
219 if (!GetRESIds(&uid
, &gid
)) {
220 // If all the uids (or gids) are not equal to each other, the security
221 // model will most likely confuse the caller, abort.
222 DVLOG(1) << "uids or gids differ!";
225 int ret
= unshare(CLONE_NEWUSER
);
227 const int unshare_errno
= errno
;
228 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
229 << "on this kernel.";
230 CheckCloneNewUserErrno(unshare_errno
);
234 // The current {r,e,s}{u,g}id is now an overflow id (c.f.
235 // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
236 DCHECK(GetRESIds(NULL
, NULL
));
237 const char kGidMapFile
[] = "/proc/self/gid_map";
238 const char kUidMapFile
[] = "/proc/self/uid_map";
239 CHECK(WriteToIdMapFile(kGidMapFile
, gid
));
240 CHECK(WriteToIdMapFile(kUidMapFile
, uid
));
241 DCHECK(GetRESIds(NULL
, NULL
));
245 bool Credentials::DropFileSystemAccess() {
246 return ChrootToSafeEmptyDir();
249 } // namespace sandbox.