SandboxInfo.cpp (8181B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "SandboxInfo.h" 8 #include "SandboxLogging.h" 9 #include "LinuxSched.h" 10 11 #include <errno.h> 12 #include <stdlib.h> 13 #include <sys/prctl.h> 14 #include <sys/stat.h> 15 #include <sys/syscall.h> 16 #include <sys/wait.h> 17 #include <unistd.h> 18 19 #include "base/posix/eintr_wrapper.h" 20 #include "mozilla/Assertions.h" 21 #include "mozilla/SandboxSettings.h" 22 #include "sandbox/linux/system_headers/linux_seccomp.h" 23 #include "sandbox/linux/system_headers/linux_syscalls.h" 24 25 #ifdef MOZ_VALGRIND 26 # include <valgrind/valgrind.h> 27 #endif 28 29 // A note about assertions: in general, the worst thing this module 30 // should be able to do is disable sandboxing features, so release 31 // asserts or MOZ_CRASH should be avoided, even for seeming 32 // impossibilities like an unimplemented syscall returning success 33 // (which has happened: https://crbug.com/439795 ). 34 // 35 // MOZ_DIAGNOSTIC_ASSERT (debug builds, plus Nightly/Aurora non-debug) 36 // is probably the best choice for conditions that shouldn't be able 37 // to fail without the help of bugs in the kernel or system libraries. 38 // 39 // Regardless of assertion type, whatever condition caused it to fail 40 // should generally also disable the corresponding feature on builds 41 // that omit the assertion. 42 43 namespace mozilla { 44 45 static bool HasSeccompBPF() { 46 // Allow simulating the absence of seccomp-bpf support, for testing. 47 if (getenv("MOZ_FAKE_NO_SANDBOX")) { 48 return false; 49 } 50 51 // Valgrind and the sandbox don't interact well, probably because Valgrind 52 // does various system calls which aren't allowed, even if Firefox itself 53 // is playing by the rules. 54 #if defined(MOZ_VALGRIND) 55 if (RUNNING_ON_VALGRIND) { 56 return false; 57 } 58 #endif 59 60 // Determine whether seccomp-bpf is supported by trying to 61 // enable it with an invalid pointer for the filter. This will 62 // fail with EFAULT if supported and EINVAL if not, without 63 // changing the process's state. 64 65 int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr); 66 MOZ_DIAGNOSTIC_ASSERT(rv == -1, 67 "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER," 68 " nullptr) didn't fail"); 69 MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL); 70 return rv == -1 && errno == EFAULT; 71 } 72 73 static bool HasSeccompTSync() { 74 // Similar to above, but for thread-sync mode. See also Chromium's 75 // sandbox::SandboxBPF::SupportsSeccompThreadFilterSynchronization 76 if (getenv("MOZ_FAKE_NO_SECCOMP_TSYNC")) { 77 return false; 78 } 79 int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, 80 SECCOMP_FILTER_FLAG_TSYNC, nullptr); 81 MOZ_DIAGNOSTIC_ASSERT(rv == -1, 82 "seccomp(..., SECCOMP_FILTER_FLAG_TSYNC," 83 " nullptr) didn't fail"); 84 MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL || errno == ENOSYS); 85 return rv == -1 && errno == EFAULT; 86 } 87 88 static bool HasUserNamespaceSupport() { 89 // Note: the /proc/<pid>/ns/* files track setns(2) support, which in 90 // some cases (e.g., pid) significantly postdates kernel support for 91 // the namespace type, so in general this type of check could be a 92 // false negative. However, for user namespaces, any kernel new 93 // enough for the feature to be usable for us has setns support 94 // (v3.8), so this is okay. 95 // 96 // The non-user namespaces all default to "y" in init/Kconfig, but 97 // check them explicitly in case someone has a weird custom config. 98 static const char* const paths[] = { 99 "/proc/self/ns/user", 100 "/proc/self/ns/pid", 101 "/proc/self/ns/net", 102 "/proc/self/ns/ipc", 103 }; 104 for (size_t i = 0; i < std::size(paths); ++i) { 105 if (access(paths[i], F_OK) == -1) { 106 MOZ_ASSERT(errno == ENOENT); 107 return false; 108 } 109 } 110 return true; 111 } 112 113 static bool CanCreateUserNamespace() { 114 // Unfortunately, the only way to verify that this process can 115 // create a new user namespace is to actually create one; because 116 // this process's namespaces shouldn't be side-effected (yet), it's 117 // necessary to clone (and collect) a child process. See also 118 // Chromium's sandbox::Credentials::SupportsNewUserNS. 119 // 120 // This is somewhat more expensive than the other tests, so it's 121 // cached in the environment to prevent child processes from having 122 // to re-run the test. 123 // 124 // This is run at static initializer time, while single-threaded, so 125 // locking isn't needed to access the environment. 126 static const char kCacheEnvName[] = "MOZ_ASSUME_USER_NS"; 127 const char* cached = getenv(kCacheEnvName); 128 if (cached) { 129 return cached[0] > '0'; 130 } 131 132 // Bug 1434528: In addition to CLONE_NEWUSER, do something that uses 133 // the new capabilities (in this case, cloning another namespace) to 134 // detect AppArmor policies that allow CLONE_NEWUSER but don't allow 135 // doing anything useful with it. 136 // 137 // Bug 1884347: There's a new AppArmor feature which can result in 138 // unsharing NEWUSER and NEWPID (or NEWNET etc.) in one syscall 139 // being allowed, but further use of capabilities will be blocked 140 // afterwards. That may be a bug, but we need to handle it. 141 pid_t pid = syscall(__NR_clone, SIGCHLD | CLONE_NEWUSER, nullptr, nullptr, 142 nullptr, nullptr); 143 if (pid == 0) { 144 // The exact meaning of `unshare(CLONE_NEWPID)` is slightly 145 // counterintuitive but in this case it doesn't matter. This just 146 // needs to be some operation that attempts to use capabilities, 147 // to check if it's blocked by an LSM. 148 int rv = unshare(CLONE_NEWPID); 149 if (rv < 0) { 150 SANDBOX_LOG_ERRNO("CanCreateUserNamespace() unshare(CLONE_NEWPID)"); 151 } 152 153 // Exit with status 0 on success, 1 on failure. 154 _exit(rv == 0 ? 0 : 1); 155 } 156 if (pid == -1) { 157 SANDBOX_LOG_ERRNO("CanCreateUserNamespace() clone() failure"); 158 // Failure. 159 MOZ_ASSERT(errno == EINVAL || // unsupported 160 errno == EPERM || // root-only, or we're already chrooted 161 errno == EUSERS); // already at user namespace nesting limit 162 setenv(kCacheEnvName, "0", 1); 163 return false; 164 } 165 // Otherwise, in the parent and successful. 166 int wstatus; 167 bool waitpid_ok = HANDLE_EINTR(waitpid(pid, &wstatus, 0)) == pid; 168 MOZ_ASSERT(waitpid_ok); 169 if (!waitpid_ok) { 170 SANDBOX_LOG_ERRNO("CanCreateUserNamespace() waitpid(%d) failure", pid); 171 return false; 172 } 173 // Check for failures reported by the child process. 174 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { 175 if (!(WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 1)) { 176 SANDBOX_LOG( 177 "CanCreateUserNamespace() waitpid(%d) child process failure %08x", 178 pid, wstatus); 179 } 180 setenv(kCacheEnvName, "0", 1); 181 return false; 182 } 183 setenv(kCacheEnvName, "1", 1); 184 return true; 185 } 186 187 /* static */ 188 MOZ_RUNINIT const SandboxInfo SandboxInfo::sSingleton = SandboxInfo(); 189 190 SandboxInfo::SandboxInfo() { 191 int flags = 0; 192 static_assert(sizeof(flags) >= sizeof(Flags), "enum Flags fits in an int"); 193 194 if (HasSeccompBPF()) { 195 flags |= kHasSeccompBPF; 196 if (HasSeccompTSync()) { 197 flags |= kHasSeccompTSync; 198 } 199 } 200 201 if (HasUserNamespaceSupport()) { 202 flags |= kHasPrivilegedUserNamespaces; 203 if (CanCreateUserNamespace()) { 204 flags |= kHasUserNamespaces; 205 } 206 } 207 208 // We can't use mozilla::IsContentSandboxEnabled() here because a) 209 // libmozsandbox can't depend on libxul, and b) this is called in a static 210 // initializer before the prefences service is ready. 211 if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) { 212 flags |= kEnabledForContent; 213 } 214 if (getenv("MOZ_PERMISSIVE_CONTENT_SANDBOX")) { 215 flags |= kPermissive; 216 } 217 if (!getenv("MOZ_DISABLE_GMP_SANDBOX")) { 218 flags |= kEnabledForMedia; 219 } 220 if (getenv("MOZ_SANDBOX_LOGGING")) { 221 flags |= kVerbose; 222 } 223 if (getenv("MOZ_SANDBOX_LOGGING_FOR_TESTS")) { 224 flags |= kVerboseTests; 225 } 226 227 mFlags = static_cast<Flags>(flags); 228 } 229 230 } // namespace mozilla