summaryrefslogtreecommitdiffstats
path: root/security/sandbox/linux/SandboxUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/linux/SandboxUtil.cpp')
-rw-r--r--security/sandbox/linux/SandboxUtil.cpp106
1 files changed, 106 insertions, 0 deletions
diff --git a/security/sandbox/linux/SandboxUtil.cpp b/security/sandbox/linux/SandboxUtil.cpp
new file mode 100644
index 000000000..999329882
--- /dev/null
+++ b/security/sandbox/linux/SandboxUtil.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SandboxUtil.h"
+
+#include "LinuxCapabilities.h"
+#include "LinuxSched.h"
+#include "SandboxLogging.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace mozilla {
+
+bool
+IsSingleThreaded()
+{
+ // This detects the thread count indirectly. /proc/<pid>/task has a
+ // subdirectory for each thread in <pid>'s thread group, and the
+ // link count on the "task" directory follows Unix expectations: the
+ // link from its parent, the "." link from itself, and the ".." link
+ // from each subdirectory; thus, 2+N links for N threads.
+ struct stat sb;
+ if (stat("/proc/self/task", &sb) < 0) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Couldn't access /proc/self/task!");
+ return false;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(sb.st_nlink >= 3);
+ return sb.st_nlink == 3;
+}
+
+static bool
+WriteStringToFile(const char* aPath, const char* aStr, const size_t aLen)
+{
+ int fd = open(aPath, O_WRONLY);
+ if (fd < 0) {
+ return false;
+ }
+ ssize_t written = write(fd, aStr, aLen);
+ if (close(fd) != 0 || written != ssize_t(aLen)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+UnshareUserNamespace()
+{
+ // The uid and gid need to be retrieved before the unshare; see
+ // below.
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ char buf[80];
+
+ if (syscall(__NR_unshare, CLONE_NEWUSER) != 0) {
+ return false;
+ }
+
+ // As mentioned in the header, this function sets up uid/gid
+ // mappings that preserve the process's previous ids. Mapping the
+ // uid/gid to something is necessary in order to nest user
+ // namespaces (not used yet, but we'll need this in the future for
+ // pid namespace support), and leaving the ids unchanged is the
+ // least confusing option.
+ //
+ // In recent kernels (3.19, 3.18.2, 3.17.8), for security reasons,
+ // establishing gid mappings will fail unless the process first
+ // revokes its ability to call setgroups() by using a /proc node
+ // added in the same set of patches.
+ //
+ // Note that /proc/self points to the thread group leader, not the
+ // current thread. However, CLONE_NEWUSER can be unshared only in a
+ // single-threaded process, so those are equivalent if we reach this
+ // point.
+ int len = SprintfLiteral(buf, "%u %u 1\n", uid, uid);
+ if (len >= int(sizeof(buf)) || len < 0) {
+ return false;
+ }
+ if (!WriteStringToFile("/proc/self/uid_map", buf, size_t(len))) {
+ MOZ_CRASH("Failed to write /proc/self/uid_map");
+ }
+
+ Unused << WriteStringToFile("/proc/self/setgroups", "deny", 4);
+
+ len = SprintfLiteral(buf, "%u %u 1\n", gid, gid);
+ if (len >= int(sizeof(buf)) || len < 0) {
+ return false;
+ }
+ if (!WriteStringToFile("/proc/self/gid_map", buf, size_t(len))) {
+ MOZ_CRASH("Failed to write /proc/self/gid_map");
+ }
+ return true;
+}
+
+} // namespace mozilla