Example #1
0
int main(int argc, char **argv)
{
    if (argc <= 1) {
	fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
	return EXIT_FAILURE;
    }

    if (MoveToNewPIDNamespace())
	return EXIT_FAILURE;
    if (SpawnChrootHelper())
	return EXIT_FAILURE;
    if (DropRoot())
	return EXIT_FAILURE;
    if (SetupChildEnvironment())
	return EXIT_FAILURE;

    execv(argv[1], &argv[1]);
    _err(EXIT_FAILURE, "execv failed");

    return EXIT_FAILURE;
}
Example #2
0
int main(int argc, char** argv) {
  if (argc <= 1) {
    if (argc <= 0) {
      return 1;
    }

    fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
    return 1;
  }

  // Allow someone to query our API version
  if (argc == 2 && 0 == strcmp(argv[1], kSuidSandboxGetApiSwitch)) {
    printf("%ld\n", kSUIDSandboxApiNumber);
    return 0;
  }

  // We cannot adjust /proc/pid/oom_adj for sandboxed renderers
  // because those files are owned by root. So we need a helper here.
  if (argc == 4 && (0 == strcmp(argv[1], kAdjustOOMScoreSwitch))) {
    char* endptr = NULL;
    long score;
    errno = 0;
    unsigned long pid_ul = strtoul(argv[2], &endptr, 10);
    if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
      return 1;
    pid_t pid = pid_ul;
    endptr = NULL;
    errno = 0;
    score = strtol(argv[3], &endptr, 10);
    if (score == LONG_MAX || score == LONG_MIN || !endptr || *endptr ||
        errno != 0) {
      return 1;
    }
    return AdjustOOMScore(pid, score);
  }

  // Protect the core setuid sandbox functionality with an API version
  if (!CheckAndExportApiVersion()) {
    return 1;
  }

  if (geteuid() != 0) {
    fprintf(stderr,
            "The setuid sandbox is not running as root. Common causes:\n"
            "  * An unprivileged process using ptrace on it, like a debugger.\n"
            "  * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n");
  }

  if (!MoveToNewNamespaces())
    return 1;
  if (!SpawnChrootHelper())
    return 1;
  if (!DropRoot())
    return 1;
  if (!SetupChildEnvironment())
    return 1;

  execv(argv[1], &argv[1]);
  FatalError("execv failed");

  return 1;
}
Example #3
0
static bool MoveToNewNamespaces() {
  // These are the sets of flags which we'll try, in order.
  const int kCloneExtraFlags[] = {CLONE_NEWPID | CLONE_NEWNET, CLONE_NEWPID, };

  // We need to close kZygoteIdFd before the child can continue. We use this
  // socketpair to tell the child when to continue;
  int sync_fds[2];
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
    FatalError("Failed to create a socketpair");
  }

  for (size_t i = 0; i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
       i++) {
    pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
    const int clone_errno = errno;

    if (pid > 0) {
      if (!DropRoot()) {
        FatalError("Could not drop privileges");
      } else {
        if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
          FatalError("Could not close socketpair");
        // The kZygoteIdFd needs to be closed in the parent before
        // Zygote gets started.
        if (close(kZygoteIdFd))
          FatalError("close");
        // Tell our child to continue
        if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
          FatalError("send");
        if (close(sync_fds[1]))
          FatalError("close");
        // We want to keep a full process tree and we don't want our childs to
        // be reparented to (the outer PID namespace) init. So we wait for it.
        WaitForChildAndExit(pid);
      }
      // NOTREACHED
      FatalError("Not reached");
    }

    if (pid == 0) {
      if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
        FatalError("Could not close socketpair");

      // Wait for the parent to confirm it closed kZygoteIdFd before we
      // continue
      char should_continue;
      if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
        FatalError("Read on socketpair");
      if (close(sync_fds[0]))
        FatalError("close");

      if (kCloneExtraFlags[i] & CLONE_NEWPID) {
        setenv(kSandboxPIDNSEnvironmentVarName, "", 1 /* overwrite */);
      } else {
        unsetenv(kSandboxPIDNSEnvironmentVarName);
      }

      if (kCloneExtraFlags[i] & CLONE_NEWNET) {
        setenv(kSandboxNETNSEnvironmentVarName, "", 1 /* overwrite */);
      } else {
        unsetenv(kSandboxNETNSEnvironmentVarName);
      }

      break;
    }

    // If EINVAL then the system doesn't support the requested flags, so
    // continue to try a different set.
    // On any other errno value the system *does* support these flags but
    // something went wrong, hence we bail with an error message rather then
    // provide less security.
    if (errno != EINVAL) {
      fprintf(stderr, "Failed to move to new namespace:");
      if (kCloneExtraFlags[i] & CLONE_NEWPID) {
        fprintf(stderr, " PID namespaces supported,");
      }
      if (kCloneExtraFlags[i] & CLONE_NEWNET) {
        fprintf(stderr, " Network namespace supported,");
      }
      fprintf(stderr, " but failed: errno = %s\n", strerror(clone_errno));
      return false;
    }
  }

  // If the system doesn't support NEWPID then we carry on anyway.
  return true;
}
Example #4
0
int main(int argc, char **argv) {
  if (argc <= 1) {
    if (argc <= 0) {
      return 1;
    }

    fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
    return 1;
  }

  // Allow someone to query our API version
  if (argc == 2 && 0 == strcmp(argv[1], kSuidSandboxGetApiSwitch)) {
    printf("%ld\n", kSUIDSandboxApiNumber);
    return 0;
  }

  // In the SUID sandbox, if we succeed in calling MoveToNewNamespaces()
  // below, then the zygote and all the renderers are in an alternate PID
  // namespace and do not know their real PIDs. As such, they report the wrong
  // PIDs to the task manager.
  //
  // To fix this, when the zygote spawns a new renderer, it gives the renderer
  // a dummy socket, which has a unique inode number. Then it asks the sandbox
  // host to find the PID of the process holding that fd by searching /proc.
  //
  // Since the zygote and renderers are all spawned by this setuid executable,
  // their entries in /proc are owned by root and only readable by root. In
  // order to search /proc for the fd we want, this setuid executable has to
  // double as a helper and perform the search. The code block below does this
  // when you call it with --find-inode INODE_NUMBER.
  if (argc == 3 && (0 == strcmp(argv[1], kFindInodeSwitch))) {
    pid_t pid;
    char* endptr = NULL;
    errno = 0;
    ino_t inode = strtoull(argv[2], &endptr, 10);
    if (inode == ULLONG_MAX || !endptr || *endptr || errno != 0)
      return 1;
    if (!FindProcessHoldingSocket(&pid, inode))
      return 1;
    printf("%d\n", pid);
    return 0;
  }
  // Likewise, we cannot adjust /proc/pid/oom_adj for sandboxed renderers
  // because those files are owned by root. So we need another helper here.
  if (argc == 4 && (0 == strcmp(argv[1], kAdjustOOMScoreSwitch))) {
    char* endptr = NULL;
    long score;
    errno = 0;
    unsigned long pid_ul = strtoul(argv[2], &endptr, 10);
    if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
      return 1;
    pid_t pid = pid_ul;
    endptr = NULL;
    errno = 0;
    score = strtol(argv[3], &endptr, 10);
    if (score == LONG_MAX || score == LONG_MIN ||
        !endptr || *endptr || errno != 0)
      return 1;
    return AdjustOOMScore(pid, score);
  }
#if defined(OS_CHROMEOS)
  if (argc == 3 && (0 == strcmp(argv[1], kAdjustLowMemMarginSwitch))) {
    char* endptr = NULL;
    errno = 0;
    unsigned long margin_mb = strtoul(argv[2], &endptr, 10);
    if (!endptr || *endptr || errno != 0)
      return 1;
    return AdjustLowMemoryMargin(margin_mb);
  }
#endif

  // Protect the core setuid sandbox functionality with an API version
  if (!CheckAndExportApiVersion()) {
    return 1;
  }

  if (!MoveToNewNamespaces())
    return 1;
  if (!SpawnChrootHelper())
    return 1;
  if (!DropRoot())
    return 1;
  if (!SetupChildEnvironment())
    return 1;

  execv(argv[1], &argv[1]);
  FatalError("execv failed");

  return 1;
}
Example #5
0
static bool MoveToNewNamespaces() {
  // These are the sets of flags which we'll try, in order.
  const int kCloneExtraFlags[] = {
    CLONE_NEWPID | CLONE_NEWNET,
    CLONE_NEWPID,
  };

  // We need to close kZygoteIdFd before the child can continue. We use this
  // socketpair to tell the child when to continue;
  int sync_fds[2];
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
    FatalError("Failed to create a socketpair");
  }

  for (size_t i = 0;
       i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
       i++) {
    pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);

    if (pid > 0) {
      if (!DropRoot()) {
        FatalError("Could not drop privileges");
      } else {
        if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
          FatalError("Could not close socketpair");
        // The kZygoteIdFd needs to be closed in the parent before
        // Zygote gets started.
        if (close(kZygoteIdFd))
          FatalError("close");
        // Tell our child to continue
        if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
          FatalError("send");
        if (close(sync_fds[1]))
          FatalError("close");
        // We want to keep a full process tree and we don't want our childs to
        // be reparented to (the outer PID namespace) init. So we wait for it.
        WaitForChildAndExit(pid);
      }
      // NOTREACHED
      FatalError("Not reached");
    }

    if (pid == 0) {
      if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
        FatalError("Could not close socketpair");

      // Wait for the parent to confirm it closed kZygoteIdFd before we
      // continue
      char should_continue;
      if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
        FatalError("Read on socketpair");
      if (close(sync_fds[0]))
        FatalError("close");

      if (kCloneExtraFlags[i] & CLONE_NEWPID) {
        setenv("SBX_PID_NS", "", 1 /* overwrite */);
      } else {
        unsetenv("SBX_PID_NS");
      }

      if (kCloneExtraFlags[i] & CLONE_NEWNET) {
        setenv("SBX_NET_NS", "", 1 /* overwrite */);
      } else {
        unsetenv("SBX_NET_NS");
      }

      break;
    }

    if (errno != EINVAL) {
      perror("Failed to move to new PID namespace");
      return false;
    }
  }

  // If the system doesn't support NEWPID then we carry on anyway.
  return true;
}