PageRenderTime 61ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/sandbox/linux/suid/sandbox.c

https://gitlab.com/0072016/Facebook-SDK-
C | 483 lines | 342 code | 76 blank | 65 comment | 125 complexity | 0f79c706b40ec67f31e60977ee9bdc5f MD5 | raw file
  1. // Copyright (c) 2012 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. // https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox.md
  5. #include "sandbox/linux/suid/common/sandbox.h"
  6. #define _GNU_SOURCE
  7. #include <asm/unistd.h>
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #include <limits.h>
  11. #include <sched.h>
  12. #include <signal.h>
  13. #include <stdarg.h>
  14. #include <stdbool.h>
  15. #include <stddef.h>
  16. #include <stdint.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <sys/prctl.h>
  21. #include <sys/resource.h>
  22. #include <sys/socket.h>
  23. #include <sys/stat.h>
  24. #include <sys/time.h>
  25. #include <sys/types.h>
  26. #include <sys/vfs.h>
  27. #include <sys/wait.h>
  28. #include <unistd.h>
  29. #include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
  30. #include "sandbox/linux/suid/process_util.h"
  31. #if !defined(CLONE_NEWPID)
  32. #define CLONE_NEWPID 0x20000000
  33. #endif
  34. #if !defined(CLONE_NEWNET)
  35. #define CLONE_NEWNET 0x40000000
  36. #endif
  37. static bool DropRoot();
  38. #define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x)
  39. static void FatalError(const char* msg, ...)
  40. __attribute__((noreturn, format(printf, 1, 2)));
  41. static void FatalError(const char* msg, ...) {
  42. va_list ap;
  43. va_start(ap, msg);
  44. vfprintf(stderr, msg, ap);
  45. fprintf(stderr, ": %s\n", strerror(errno));
  46. fflush(stderr);
  47. va_end(ap);
  48. _exit(1);
  49. }
  50. static void ExitWithErrorSignalHandler(int signal) {
  51. const char msg[] = "\nThe setuid sandbox got signaled, exiting.\n";
  52. if (-1 == write(2, msg, sizeof(msg) - 1)) {
  53. // Do nothing.
  54. }
  55. _exit(1);
  56. }
  57. // We will chroot() to the helper's /proc/self directory. Anything there will
  58. // not exist anymore if we make sure to wait() for the helper.
  59. //
  60. // /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty
  61. // even if the helper survives as a zombie.
  62. //
  63. // There is very little reason to use fdinfo/ instead of fd/ but we are
  64. // paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/
  65. #define SAFE_DIR "/proc/self/fdinfo"
  66. #define SAFE_DIR2 "/proc/self/fd"
  67. static bool SpawnChrootHelper() {
  68. int sv[2];
  69. if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
  70. perror("socketpair");
  71. return false;
  72. }
  73. char* safedir = NULL;
  74. struct stat sdir_stat;
  75. if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) {
  76. safedir = SAFE_DIR;
  77. } else if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) {
  78. safedir = SAFE_DIR2;
  79. } else {
  80. fprintf(stderr, "Could not find %s\n", SAFE_DIR2);
  81. return false;
  82. }
  83. const pid_t pid = syscall(__NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
  84. if (pid == -1) {
  85. perror("clone");
  86. close(sv[0]);
  87. close(sv[1]);
  88. return false;
  89. }
  90. if (pid == 0) {
  91. // We share our files structure with an untrusted process. As a security in
  92. // depth measure, we make sure that we can't open anything by mistake.
  93. // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
  94. const struct rlimit nofile = {0, 0};
  95. if (setrlimit(RLIMIT_NOFILE, &nofile))
  96. FatalError("Setting RLIMIT_NOFILE");
  97. if (close(sv[1]))
  98. FatalError("close");
  99. // wait for message
  100. char msg;
  101. ssize_t bytes;
  102. do {
  103. bytes = read(sv[0], &msg, 1);
  104. } while (bytes == -1 && errno == EINTR);
  105. if (bytes == 0)
  106. _exit(0);
  107. if (bytes != 1)
  108. FatalError("read");
  109. // do chrooting
  110. if (msg != kMsgChrootMe)
  111. FatalError("Unknown message from sandboxed process");
  112. // sanity check
  113. if (chdir(safedir))
  114. FatalError("Cannot chdir into /proc/ directory");
  115. if (chroot(safedir))
  116. FatalError("Cannot chroot into /proc/ directory");
  117. if (chdir("/"))
  118. FatalError("Cannot chdir to / after chroot");
  119. const char reply = kMsgChrootSuccessful;
  120. do {
  121. bytes = write(sv[0], &reply, 1);
  122. } while (bytes == -1 && errno == EINTR);
  123. if (bytes != 1)
  124. FatalError("Writing reply");
  125. _exit(0);
  126. // We now become a zombie. /proc/self/fd(info) is now an empty dir and we
  127. // are chrooted there.
  128. // Our (unprivileged) parent should not even be able to open "." or "/"
  129. // since they would need to pass the ptrace() check. If our parent wait()
  130. // for us, our root directory will completely disappear.
  131. }
  132. if (close(sv[0])) {
  133. close(sv[1]);
  134. perror("close");
  135. return false;
  136. }
  137. // In the parent process, we install an environment variable containing the
  138. // number of the file descriptor.
  139. char desc_str[64];
  140. int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
  141. if (printed < 0 || printed >= (int)sizeof(desc_str)) {
  142. fprintf(stderr, "Failed to snprintf\n");
  143. return false;
  144. }
  145. if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
  146. perror("setenv");
  147. close(sv[1]);
  148. return false;
  149. }
  150. // We also install an environment variable containing the pid of the child
  151. char helper_pid_str[64];
  152. printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
  153. if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
  154. fprintf(stderr, "Failed to snprintf\n");
  155. return false;
  156. }
  157. if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
  158. perror("setenv");
  159. close(sv[1]);
  160. return false;
  161. }
  162. return true;
  163. }
  164. // Block until child_pid exits, then exit. Try to preserve the exit code.
  165. static void WaitForChildAndExit(pid_t child_pid) {
  166. int exit_code = -1;
  167. siginfo_t reaped_child_info;
  168. // Don't "Core" on SIGABRT. SIGABRT is sent by the Chrome OS session manager
  169. // when things are hanging.
  170. // Here, the current process is going to waitid() and _exit(), so there is no
  171. // point in generating a crash report. The child process is the one
  172. // blocking us.
  173. if (signal(SIGABRT, ExitWithErrorSignalHandler) == SIG_ERR) {
  174. FatalError("Failed to change signal handler");
  175. }
  176. int wait_ret =
  177. HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
  178. if (!wait_ret && reaped_child_info.si_pid == child_pid) {
  179. if (reaped_child_info.si_code == CLD_EXITED) {
  180. exit_code = reaped_child_info.si_status;
  181. } else {
  182. // Exit with code 0 if the child got signaled.
  183. exit_code = 0;
  184. }
  185. }
  186. _exit(exit_code);
  187. }
  188. static bool MoveToNewNamespaces() {
  189. // These are the sets of flags which we'll try, in order.
  190. const int kCloneExtraFlags[] = {CLONE_NEWPID | CLONE_NEWNET, CLONE_NEWPID, };
  191. // We need to close kZygoteIdFd before the child can continue. We use this
  192. // socketpair to tell the child when to continue;
  193. int sync_fds[2];
  194. if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
  195. FatalError("Failed to create a socketpair");
  196. }
  197. for (size_t i = 0; i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
  198. i++) {
  199. pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
  200. const int clone_errno = errno;
  201. if (pid > 0) {
  202. if (!DropRoot()) {
  203. FatalError("Could not drop privileges");
  204. } else {
  205. if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
  206. FatalError("Could not close socketpair");
  207. // The kZygoteIdFd needs to be closed in the parent before
  208. // Zygote gets started.
  209. if (close(kZygoteIdFd))
  210. FatalError("close");
  211. // Tell our child to continue
  212. if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
  213. FatalError("send");
  214. if (close(sync_fds[1]))
  215. FatalError("close");
  216. // We want to keep a full process tree and we don't want our childs to
  217. // be reparented to (the outer PID namespace) init. So we wait for it.
  218. WaitForChildAndExit(pid);
  219. }
  220. // NOTREACHED
  221. FatalError("Not reached");
  222. }
  223. if (pid == 0) {
  224. if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
  225. FatalError("Could not close socketpair");
  226. // Wait for the parent to confirm it closed kZygoteIdFd before we
  227. // continue
  228. char should_continue;
  229. if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
  230. FatalError("Read on socketpair");
  231. if (close(sync_fds[0]))
  232. FatalError("close");
  233. if (kCloneExtraFlags[i] & CLONE_NEWPID) {
  234. setenv(kSandboxPIDNSEnvironmentVarName, "", 1 /* overwrite */);
  235. } else {
  236. unsetenv(kSandboxPIDNSEnvironmentVarName);
  237. }
  238. if (kCloneExtraFlags[i] & CLONE_NEWNET) {
  239. setenv(kSandboxNETNSEnvironmentVarName, "", 1 /* overwrite */);
  240. } else {
  241. unsetenv(kSandboxNETNSEnvironmentVarName);
  242. }
  243. break;
  244. }
  245. // If EINVAL then the system doesn't support the requested flags, so
  246. // continue to try a different set.
  247. // On any other errno value the system *does* support these flags but
  248. // something went wrong, hence we bail with an error message rather then
  249. // provide less security.
  250. if (errno != EINVAL) {
  251. fprintf(stderr, "Failed to move to new namespace:");
  252. if (kCloneExtraFlags[i] & CLONE_NEWPID) {
  253. fprintf(stderr, " PID namespaces supported,");
  254. }
  255. if (kCloneExtraFlags[i] & CLONE_NEWNET) {
  256. fprintf(stderr, " Network namespace supported,");
  257. }
  258. fprintf(stderr, " but failed: errno = %s\n", strerror(clone_errno));
  259. return false;
  260. }
  261. }
  262. // If the system doesn't support NEWPID then we carry on anyway.
  263. return true;
  264. }
  265. static bool DropRoot() {
  266. if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
  267. perror("prctl(PR_SET_DUMPABLE)");
  268. return false;
  269. }
  270. if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
  271. perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
  272. return false;
  273. }
  274. gid_t rgid, egid, sgid;
  275. if (getresgid(&rgid, &egid, &sgid)) {
  276. perror("getresgid");
  277. return false;
  278. }
  279. if (setresgid(rgid, rgid, rgid)) {
  280. perror("setresgid");
  281. return false;
  282. }
  283. uid_t ruid, euid, suid;
  284. if (getresuid(&ruid, &euid, &suid)) {
  285. perror("getresuid");
  286. return false;
  287. }
  288. if (setresuid(ruid, ruid, ruid)) {
  289. perror("setresuid");
  290. return false;
  291. }
  292. return true;
  293. }
  294. static bool SetupChildEnvironment() {
  295. unsigned i;
  296. // ld.so may have cleared several environment variables because we are SUID.
  297. // However, the child process might need them so zygote_host_linux.cc saves a
  298. // copy in SANDBOX_$x. This is safe because we have dropped root by this
  299. // point, so we can only exec a binary with the permissions of the user who
  300. // ran us in the first place.
  301. for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
  302. const char* const envvar = kSUIDUnsafeEnvironmentVariables[i];
  303. char* const saved_envvar = SandboxSavedEnvironmentVariable(envvar);
  304. if (!saved_envvar)
  305. return false;
  306. const char* const value = getenv(saved_envvar);
  307. if (value) {
  308. setenv(envvar, value, 1 /* overwrite */);
  309. unsetenv(saved_envvar);
  310. }
  311. free(saved_envvar);
  312. }
  313. return true;
  314. }
  315. bool CheckAndExportApiVersion() {
  316. // Check the environment to see if a specific API version was requested.
  317. // assume version 0 if none.
  318. int api_number = -1;
  319. char* api_string = getenv(kSandboxEnvironmentApiRequest);
  320. if (!api_string) {
  321. api_number = 0;
  322. } else {
  323. errno = 0;
  324. char* endptr = NULL;
  325. long long_api_number = strtol(api_string, &endptr, 10);
  326. if (!endptr || *endptr || errno != 0 || long_api_number < INT_MIN ||
  327. long_api_number > INT_MAX) {
  328. return false;
  329. }
  330. api_number = long_api_number;
  331. }
  332. // Warn only for now.
  333. if (api_number != kSUIDSandboxApiNumber) {
  334. fprintf(
  335. stderr,
  336. "The setuid sandbox provides API version %d, "
  337. "but you need %d\n"
  338. "Please read "
  339. "https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox_development.md."
  340. "\n\n",
  341. kSUIDSandboxApiNumber,
  342. api_number);
  343. }
  344. // Export our version so that the sandboxed process can verify it did not
  345. // use an old sandbox.
  346. char version_string[64];
  347. snprintf(version_string, sizeof(version_string), "%d", kSUIDSandboxApiNumber);
  348. if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) {
  349. perror("setenv");
  350. return false;
  351. }
  352. return true;
  353. }
  354. int main(int argc, char** argv) {
  355. if (argc <= 1) {
  356. if (argc <= 0) {
  357. return 1;
  358. }
  359. fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
  360. return 1;
  361. }
  362. // Allow someone to query our API version
  363. if (argc == 2 && 0 == strcmp(argv[1], kSuidSandboxGetApiSwitch)) {
  364. printf("%d\n", kSUIDSandboxApiNumber);
  365. return 0;
  366. }
  367. // We cannot adjust /proc/pid/oom_adj for sandboxed renderers
  368. // because those files are owned by root. So we need a helper here.
  369. if (argc == 4 && (0 == strcmp(argv[1], kAdjustOOMScoreSwitch))) {
  370. char* endptr = NULL;
  371. long score;
  372. errno = 0;
  373. unsigned long pid_ul = strtoul(argv[2], &endptr, 10);
  374. if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
  375. return 1;
  376. pid_t pid = pid_ul;
  377. endptr = NULL;
  378. errno = 0;
  379. score = strtol(argv[3], &endptr, 10);
  380. if (score == LONG_MAX || score == LONG_MIN || !endptr || *endptr ||
  381. errno != 0) {
  382. return 1;
  383. }
  384. return AdjustOOMScore(pid, score);
  385. }
  386. // Protect the core setuid sandbox functionality with an API version
  387. if (!CheckAndExportApiVersion()) {
  388. return 1;
  389. }
  390. if (geteuid() != 0) {
  391. fprintf(stderr,
  392. "The setuid sandbox is not running as root. Common causes:\n"
  393. " * An unprivileged process using ptrace on it, like a debugger.\n"
  394. " * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n");
  395. }
  396. if (!MoveToNewNamespaces())
  397. return 1;
  398. if (!SpawnChrootHelper())
  399. return 1;
  400. if (!DropRoot())
  401. return 1;
  402. if (!SetupChildEnvironment())
  403. return 1;
  404. execv(argv[1], &argv[1]);
  405. FatalError("execv failed");
  406. return 1;
  407. }