WebKit Bugzilla
Attachment 349776 Details for
Bug 188568
: [GTK][WPE] Implement subprocess sandboxing
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
[GTK][WPE] Implement subprocess sandboxing
bug-188568-20180914135217.patch (text/plain), 56.45 KB, created by
Patrick Griffis
on 2018-09-14 10:52:18 PDT
(
hide
)
Description:
[GTK][WPE] Implement subprocess sandboxing
Filename:
MIME Type:
Creator:
Patrick Griffis
Created:
2018-09-14 10:52:18 PDT
Size:
56.45 KB
patch
obsolete
>Subversion Revision: 235943 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 2237346682a217619f49a06f75bad8db5df8ef3e..93b8e475e925655c11bcc7f82d05bb06bbad0c06 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,14 @@ >+2018-09-14 Patrick Griffis <pgriffis@igalia.com> >+ >+ [GTK][WPE] Implement subprocess sandboxing >+ https://bugs.webkit.org/show_bug.cgi?id=188568 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Link against libseccomp. >+ >+ * PlatformGTK.cmake: >+ > 2018-09-12 Ali Juma <ajuma@chromium.org> > > [IntersectionObserver] Implement rootMargin expansion >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 1acac4b8bd955419ef37303ac34e6dda391214fb..c8675b9015b5a97b81f813b3f4eeb8ec19b871b8 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,92 @@ >+2018-09-14 Patrick Griffis <pgriffis@igalia.com> >+ >+ [GTK][WPE] Implement subprocess sandboxing >+ https://bugs.webkit.org/show_bug.cgi?id=188568 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This implements sandboxing of the WebKitWebProcess, WebKitNetworkProcess, >+ and WebKitStorageProcess. >+ >+ The sandbox is opt-in at runtime as it is a behavior change. >+ See webkit_web_context_set_sandbox_enabled() and the >+ WEBKIT_SANDBOX_ENABLED env var for developers. >+ >+ This is Linux specific using Namespaces, Seccomp, and a DBus proxy service. >+ This introduces three new dependencies: >+ >+ - bwrap executable >+ - libseccomp library >+ - xdg-dbus-proxy executable >+ >+ The use of xdg-dbus-proxy will ideally be replaced once upstream DBus >+ gains the same filtering abilities which is a work in progress. >+ >+ Currently the sandbox is not completed and there are a few large holes: >+ >+ - Pulseaudio: The Pipewire project will ideally solve this. >+ - DRI device access: No immediate solutions planned. >+ - Webcam device access: Pipewire+GStreamer will also solve this. >+ - Webprocess network access: Will require GStreamer changes. >+ - DConf access: Is being worked on upstream, no ETA. >+ - X11 access: Wayland solves this. >+ >+ That is not an exhaustive list but are the noteworthy ones. Filesystem access >+ is still an evolving list as problems are found as is specific DBus name access. >+ >+ * PlatformGTK.cmake: >+ * PlatformWPE.cmake: >+ * UIProcess/API/glib/WebKitWebContext.cpp: >+ (webkit_web_context_set_sandbox_enabled): >+ (webkit_web_context_get_sandbox_enabled): >+ * UIProcess/API/gtk/WebKitWebContext.h: >+ * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt: >+ * UIProcess/API/wpe/WebKitWebContext.h: >+ * UIProcess/Launcher/ProcessLauncher.h: >+ * UIProcess/Launcher/glib/ProcessLauncherGLib.cpp: >+ (WebKit::memfd_create): >+ (WebKit::argsToFd): >+ (WebKit::DBusProxy::~DBusProxy): >+ (WebKit::DBusProxy::setSocket): >+ (WebKit::DBusProxy::isRunning const): >+ (WebKit::DBusProxy::proxyPath const): >+ (WebKit::DBusProxy::path const): >+ (WebKit::DBusProxy::appendPermissions): >+ (WebKit::DBusProxy::launch): >+ (WebKit::DBusProxy::makeProxySocket): >+ (WebKit::dbusAddressToPath): >+ (WebKit::bindIfExists): >+ (WebKit::bindDBusSession): >+ (WebKit::bindX11): >+ (WebKit::bindDconf): >+ (WebKit::bindWayland): >+ (WebKit::bindPulse): >+ (WebKit::bindFonts): >+ (WebKit::bindGtkData): >+ (WebKit::bindA11y): >+ (WebKit::bindPathVar): >+ (WebKit::bindGStreamerData): >+ (WebKit::bindOpenGL): >+ (WebKit::bindV4l): >+ (WebKit::setupSeccomp): >+ (WebKit::ensureDirectoryExists): >+ (WebKit::getBwrapArgs): >+ (WebKit::isInsideFlatpak): >+ (WebKit::getFlatpakArgs): >+ (WebKit::ProcessLauncher::launchProcess): >+ * UIProcess/Network/NetworkProcessProxy.cpp: >+ (WebKit::NetworkProcessProxy::getLaunchOptions): >+ * UIProcess/Storage/StorageProcessProxy.cpp: >+ (WebKit::StorageProcessProxy::getLaunchOptions): >+ * UIProcess/WebProcessPool.h: >+ * UIProcess/WebProcessProxy.cpp: >+ (WebKit::WebProcessProxy::getLaunchOptions): >+ * UIProcess/WebsiteData/WebsiteDataStore.cpp: >+ (WebKit::WebsiteDataStore::resolveDirectoriesIfNecessary): >+ * UIProcess/WebsiteData/WebsiteDataStore.h: >+ (WebKit::WebsiteDataStore::resolvedNetworkCacheDirectory const): >+ (WebKit::WebsiteDataStore::resolvedLocalStorageDirectory const): >+ > 2018-09-12 Fujii Hironori <Hironori.Fujii@sony.com> > > [Win][Clang] error: non-constant-expression cannot be narrowed from type 'int' to 'SHORT' >diff --git a/Source/WebCore/PlatformGTK.cmake b/Source/WebCore/PlatformGTK.cmake >index e14ed4adcec092cd8830745f432c737c1e40dfd8..4eb05cd06db3daa6684c8a8f32de341f6d1e0e85 100644 >--- a/Source/WebCore/PlatformGTK.cmake >+++ b/Source/WebCore/PlatformGTK.cmake >@@ -106,6 +106,7 @@ list(APPEND WebCore_LIBRARIES > ${GLIB_GMODULE_LIBRARIES} > ${GLIB_GOBJECT_LIBRARIES} > ${GLIB_LIBRARIES} >+ ${LIBSECCOMP_LIBRARIES} > ${LIBSECRET_LIBRARIES} > ${LIBSOUP_LIBRARIES} > ${LIBTASN1_LIBRARIES} >@@ -125,6 +126,7 @@ list(APPEND WebCore_SYSTEM_INCLUDE_DIRECTORIES > ${ENCHANT_INCLUDE_DIRS} > ${GIO_UNIX_INCLUDE_DIRS} > ${GLIB_INCLUDE_DIRS} >+ ${LIBSECCOMP_INCLUDE_DIRS} > ${LIBSECRET_INCLUDE_DIRS} > ${LIBSOUP_INCLUDE_DIRS} > ${LIBTASN1_INCLUDE_DIRS} >diff --git a/Source/WebKit/PlatformGTK.cmake b/Source/WebKit/PlatformGTK.cmake >index e36e4c351055f85418e9e3b172ef257a8b9dfa73..1264e70c37eb89ad05840ec5e9dcec4389d3e3e0 100644 >--- a/Source/WebKit/PlatformGTK.cmake >+++ b/Source/WebKit/PlatformGTK.cmake >@@ -20,6 +20,7 @@ add_definitions(-DWEBKIT_DOM_USE_UNSTABLE_API) > > add_definitions(-DPKGLIBEXECDIR="${LIBEXEC_INSTALL_DIR}") > add_definitions(-DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") >+add_definitions(-DDATADIR="${CMAKE_INSTALL_FULL_DATADIR}") > add_definitions(-DLIBDIR="${LIB_INSTALL_DIR}") > > if (NOT DEVELOPER_MODE AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") >diff --git a/Source/WebKit/PlatformWPE.cmake b/Source/WebKit/PlatformWPE.cmake >index 0f4f02448eade00ee7a05b24bf911a235d0ec9d3..724f5b24721b633d1fb4560649fdeb304d7a7f0f 100644 >--- a/Source/WebKit/PlatformWPE.cmake >+++ b/Source/WebKit/PlatformWPE.cmake >@@ -17,6 +17,7 @@ add_definitions(-DWEBKIT2_COMPILATION) > > add_definitions(-DPKGLIBDIR="${LIB_INSTALL_DIR}/wpe-webkit-${WPE_API_VERSION}") > add_definitions(-DPKGLIBEXECDIR="${LIBEXEC_INSTALL_DIR}") >+add_definitions(-DDATADIR="${CMAKE_INSTALL_FULL_DATADIR}") > add_definitions(-DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") > > if (NOT DEVELOPER_MODE AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") >diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp >index 3f52625e67b1e71a44f641ea92b8536ea0149b2d..498cfadd361611f0e5bccd78d1381fd0a4389312 100644 >--- a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp >+++ b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp >@@ -1147,6 +1147,50 @@ void webkit_web_context_register_uri_scheme(WebKitWebContext* context, const cha > context->priv->processPool->registerSchemeForCustomProtocol(String::fromUTF8(scheme)); > } > >+/** >+ * webkit_web_context_set_sandbox_enabled: >+ * @context: a #WebKitWebContext >+ * @enabled: if %TRUE enable sandboxing >+ * >+ * Set whether WebKit subprocesses will be sandboxed, limiting access to the system. >+ * >+ * This method **must be called before any web process has been created**, >+ * as early as possible in your application. Calling it later is a fatal error. >+ * >+ * This is only implemented on Linux and is a no-op otherwise. >+ * >+ * If you use `$XDG_CONFIG_HOME/g_get_prgname()` in your #WebKitUserContentManager >+ * you must ensure it exists before subprocess are created. >+ * >+ * Since: 2.24 >+ */ >+void webkit_web_context_set_sandbox_enabled(WebKitWebContext* context, gboolean enabled) >+{ >+ g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context)); >+ >+ if (context->priv->processPool->processes().size()) >+ g_error("Sandboxing cannot be changed after subprocesses were spawned."); >+ >+ context->priv->processPool->setSandboxEnabled(enabled); >+} >+ >+/** >+ * webkit_web_context_get_sandbox_enabled: >+ * @context: a #WebKitWebContext >+ * >+ * Get whether sandboxing is currently enabled. >+ * >+ * Returns: %TRUE if sandboxing is enabled, or %FALSE otherwise. >+ * >+ * Since: 2.24 >+ */ >+gboolean webkit_web_context_get_sandbox_enabled(WebKitWebContext* context) >+{ >+ g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE); >+ >+ return context->priv->processPool->sandboxEnabled(); >+} >+ > /** > * webkit_web_context_get_spell_checking_enabled: > * @context: a #WebKitWebContext >diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h b/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h >index 3149a329c27821ac4448f5dc24d0b0395229d4ad..bbdeac93316df2102b6e8c5cbb8e7b78f57f8939 100644 >--- a/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h >+++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h >@@ -247,6 +247,13 @@ webkit_web_context_register_uri_scheme (WebKitWebContext > gpointer user_data, > GDestroyNotify user_data_destroy_func); > >+WEBKIT_API void >+webkit_web_context_set_sandbox_enabled (WebKitWebContext *context, >+ gboolean enabled); >+ >+WEBKIT_API gboolean >+webkit_web_context_get_sandbox_enabled (WebKitWebContext *context); >+ > WEBKIT_API gboolean > webkit_web_context_get_spell_checking_enabled (WebKitWebContext *context); > >diff --git a/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt b/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt >index 55605db4043f134a946b2f46bfedc8fc0e19627d..936e1e5c3a9a7147f5e10214862f70e8144ff3eb 100644 >--- a/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt >+++ b/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt >@@ -51,6 +51,8 @@ webkit_web_context_get_security_manager > webkit_web_context_set_additional_plugins_directory > webkit_web_context_get_plugins > webkit_web_context_get_plugins_finish >+webkit_web_context_get_sandbox_enabled >+webkit_web_context_set_sandbox_enabled > webkit_web_context_get_spell_checking_enabled > webkit_web_context_set_spell_checking_enabled > webkit_web_context_get_spell_checking_languages >diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h b/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h >index 8cf646f4ede340e5276cee7da84fcd9308938764..0eb8f367de574793bb9b58c5e400440c10b79845 100644 >--- a/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h >+++ b/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h >@@ -296,6 +296,13 @@ webkit_web_context_set_process_model (WebKitWebContext > WEBKIT_API WebKitProcessModel > webkit_web_context_get_process_model (WebKitWebContext *context); > >+WEBKIT_API void >+webkit_web_context_set_sandbox_enabled (WebKitWebContext *context, >+ gboolean enabled); >+ >+WEBKIT_API gboolean >+webkit_web_context_get_sandbox_enabled (WebKitWebContext *context); >+ > WEBKIT_API void > webkit_web_context_initialize_notification_permissions > (WebKitWebContext *context, >diff --git a/Source/WebKit/UIProcess/Launcher/ProcessLauncher.h b/Source/WebKit/UIProcess/Launcher/ProcessLauncher.h >index 5c4bb3b9e11641d987811da6679465b6e31840a5..e6f55d3ac212daeca1478f8687bc84dc1d823603 100644 >--- a/Source/WebKit/UIProcess/Launcher/ProcessLauncher.h >+++ b/Source/WebKit/UIProcess/Launcher/ProcessLauncher.h >@@ -68,6 +68,11 @@ public: > bool shouldMakeProcessLaunchFailForTesting { false }; > CString customWebContentServiceBundleIdentifier; > >+#if PLATFORM(GTK) || PLATFORM(WPE) >+ Vector<String> extraSandboxPaths; >+ bool sandboxEnabled { false }; >+#endif >+ > #if ENABLE(DEVELOPER_MODE) && (PLATFORM(GTK) || PLATFORM(WPE)) > String processCmdPrefix; > #endif >diff --git a/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp b/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp >index 52916971526da9935e3ff8ba653e733a2aa25a9c..369de5732672e8d0f098390ae3f919d5475a3abf 100644 >--- a/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp >+++ b/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp >@@ -28,6 +28,7 @@ > #include "ProcessLauncher.h" > > #include "Connection.h" >+#include "PlatformDisplay.h" > #include "ProcessExecutablePath.h" > #include <WebCore/FileSystem.h> > #include <errno.h> >@@ -44,6 +45,11 @@ > #include <wpe/wpe.h> > #endif > >+#if ENABLE(BUBBLEWRAP_SANDBOX) >+#include <seccomp.h> >+#include <sys/ioctl.h> >+#endif >+ > namespace WebKit { > using namespace WebCore; > >@@ -53,6 +59,804 @@ static void childSetupFunction(gpointer userData) > close(socket); > } > >+#if ENABLE(BUBBLEWRAP_SANDBOX) >+ >+static int memfd_create(const char* name, unsigned flags) >+{ >+ return syscall(__NR_memfd_create, name, flags); >+} >+ >+#define MFD_ALLOW_SEALING 2U >+ >+static int >+argsToFd(const Vector<CString>& args, const char *name) >+{ >+ GString* buffer = g_string_new(nullptr); >+ >+ for (const auto& arg : args) >+ g_string_append_len(buffer, arg.data(), arg.length() + 1); // Include NUL >+ >+ GRefPtr<GBytes> bytes(g_string_free_to_bytes(buffer)); >+ >+ int memfd = memfd_create(name, MFD_ALLOW_SEALING); >+ if (memfd == -1) >+ g_error("memfd_create failed: %s", g_strerror(errno)); >+ >+ size_t size; >+ ssize_t ret; >+ gconstpointer data = g_bytes_get_data(bytes.get(), &size); >+ >+ ret = write(memfd, data, size); >+ if (ret < 0) >+ g_error("Writing args to memfd failed: %s", g_strerror(errno)); >+ >+ if ((size_t)ret != size) >+ g_error("Failed to write all args to memfd"); >+ >+ if (lseek(memfd, 0, SEEK_SET) == -1) >+ g_error("lseek failed: %s", g_strerror(errno)); >+ >+ if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == -1) >+ g_error("Failed to seal memfd: %s", g_strerror(errno)); >+ >+ return memfd; >+} >+ >+class DBusProxy { >+public: >+ ~DBusProxy() >+ { >+ if (m_isRunning) >+ kill(m_pid, SIGKILL); >+ } >+ >+ void setSocket(const char* dbusSocket, const char* dbusPath) >+ { >+ m_socket = dbusSocket; >+ m_path = dbusPath; >+ }; >+ >+ bool isRunning() const { return m_isRunning; }; >+ CString proxyPath() const { return m_proxyPath; }; >+ CString path() const { return m_path; }; >+ >+ void appendPermissions(std::initializer_list<CString> permissions) >+ { >+ RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!m_isRunning); >+ >+ Vector<CString> newPermissions(permissions); >+ m_permissions.appendVector(newPermissions); >+ }; >+ >+ void launch() >+ { >+ if (m_isRunning) >+ return; >+ >+ const char* runDir = g_get_user_runtime_dir(); >+ GUniquePtr<char> appRunDir(g_build_filename(runDir, g_get_prgname(), nullptr)); >+ >+ GUniquePtr<char> proxySocket(makeProxySocket(appRunDir.get())); >+ RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(proxySocket.get()); >+ m_proxyPath = proxySocket.get(); >+ >+ Vector<CString> bwrapArgs = { >+ "--die-with-parent", >+ "--unshare-all", >+ "--ro-bind", DBUS_PROXY_EXECUTABLE, DBUS_PROXY_EXECUTABLE, >+ "--ro-bind", "/usr/lib", "/usr/lib", >+ "--ro-bind-try", "/lib", "/lib", >+ "--ro-bind-try", "/lib64", "/lib64", >+ "--dev", "/dev", >+ "--proc", "/proc", >+ "--dir", "/run", >+ "--symlink", "../run", "/var/run", >+ "--symlink", "../tmp", "/var/tmp", >+ "--tmpfs", "/tmp", >+ "--unsetenv", "TMPDIR", >+ "--bind-try", m_path, m_path, >+ "--bind", appRunDir.get(), appRunDir.get(), >+ }; >+ >+ Vector<CString> proxyArgs = { >+ m_socket, m_proxyPath, >+ "--filter", >+ }; >+ >+ if (!g_strcmp0(g_getenv("WEBKIT_ENABLE_DBUS_PROXY_LOGGING"), "1")) >+ proxyArgs.append("--log"); >+ >+ proxyArgs.appendVector(m_permissions); >+ >+ int bwrapFd = argsToFd(bwrapArgs, "bwrap-args"); >+ GUniquePtr<char> bwrapFdStr(g_strdup_printf("%d", bwrapFd)); >+ >+ int proxyFd = argsToFd(proxyArgs, "dbus-proxy"); >+ GUniquePtr<char> proxyArgsStr(g_strdup_printf("--args=%d", proxyFd)); >+ >+ Vector<CString> args = { >+ BWRAP_EXECUTABLE, >+ "--args", bwrapFdStr.get(), >+ "--", >+ DBUS_PROXY_EXECUTABLE, >+ proxyArgsStr.get(), >+ }; >+ >+ int nargs = args.size() + 1; >+ int i = 0; >+ char** argv = g_newa(char*, nargs); >+ for (const auto& arg : args) >+ argv[i++] = const_cast<char*>(arg.data()); >+ argv[i] = nullptr; >+ >+ GUniqueOutPtr<GError> error; >+ if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_LEAVE_DESCRIPTORS_OPEN, NULL, NULL, &m_pid, &error.outPtr())) { >+ g_error("Failed to start dbus proxy: %s", error.get()->message); >+ return; >+ } >+ >+ close(bwrapFd); >+ close(proxyFd); >+ m_isRunning = true; >+ }; >+ >+private: >+ static char* makeProxySocket(const char *appRunDir) >+ { >+ if (g_mkdir_with_parents(appRunDir, 0700) == -1) { >+ g_error("Failed to mkdir for dbus proxy (%s): %s", appRunDir, g_strerror(errno)); >+ return nullptr; >+ } >+ >+ char* proxySocketTemplate = g_build_filename(appRunDir, "dbus-proxy-XXXXXX", nullptr); >+ int fd; >+ if ((fd = g_mkstemp(proxySocketTemplate)) == -1) { >+ g_free(proxySocketTemplate); >+ g_error("Failed to make socket file for dbus proxy: %s", g_strerror(errno)); >+ return nullptr; >+ } >+ >+ close(fd); >+ return proxySocketTemplate; >+ }; >+ >+ CString m_socket; >+ CString m_path; >+ CString m_proxyPath; >+ bool m_isRunning = false; >+ GPid m_pid; >+ Vector<CString> m_permissions = { }; >+}; >+ >+static char* dbusAddressToPath(const char* address, bool abstract = false) >+{ >+ const char* path; >+ const char* path_end; >+ >+ if (!address) >+ return nullptr; >+ >+ if (!g_str_has_prefix(address, "unix:")) >+ return nullptr; >+ >+ path = strstr(address, abstract ? "abstract=" : "path="); >+ if (!path) >+ return nullptr; >+ >+ path += strlen(abstract ? "abstract=" : "path="); >+ path_end = path; >+ while (*path_end && *path_end != ',') >+ path_end++; >+ >+ return g_strndup(path, path_end - path); >+} >+ >+enum class BindFlags { >+ ReadOnly, >+ ReadWrite, >+ Device, >+}; >+ >+static void bindIfExists(Vector<CString>& args, const char* path, BindFlags bindFlags = BindFlags::ReadOnly) >+{ >+ if (!path) >+ return; >+ >+ const char* type; >+ if (bindFlags == BindFlags::Device) >+ type = "--dev-bind-try"; >+ else if (bindFlags == BindFlags::ReadOnly) >+ type = "--ro-bind-try"; >+ else >+ type = "--bind-try"; >+ args.appendVector(Vector<CString>({ type, path, path })); >+} >+ >+static void bindDBusSession(Vector<CString>& args, DBusProxy& proxy) >+{ >+ if (!proxy.isRunning()) { >+ const char* sessionAddress = g_getenv("DBUS_SESSION_BUS_ADDRESS"); >+ if (!sessionAddress) >+ return; >+ >+ GUniquePtr<char> sessionPath(dbusAddressToPath(sessionAddress)); >+ if (!sessionPath.get()) >+ return; >+ >+ proxy.setSocket(sessionAddress, sessionPath.get()); >+ proxy.launch(); >+ } >+ >+ args.appendVector(Vector<CString>({ >+ "--bind", proxy.proxyPath(), proxy.path(), >+ })); >+} >+ >+static void bindX11(Vector<CString>& args) >+{ >+ const char* display = g_getenv("DISPLAY"); >+ if (!display || display[0] != ':' || !g_ascii_isdigit(const_cast<char*>(display)[1])) >+ display = ":0"; >+ GUniquePtr<char> x11File(g_strdup_printf("/tmp/.X11-unix/X%s", display + 1)); >+ bindIfExists(args, x11File.get(), BindFlags::ReadWrite); >+ >+ const char* xauth = g_getenv("XAUTHORITY"); >+ if (!xauth) { >+ const char* homeDir = g_get_home_dir(); >+ GUniquePtr<char> xauthFile(g_build_filename(homeDir, ".Xauthority", nullptr)); >+ bindIfExists(args, xauthFile.get()); >+ } else >+ bindIfExists(args, xauth); >+} >+ >+static void bindDconf(Vector<CString>& args) >+{ >+ const char* runtimeDir = g_get_user_runtime_dir(); >+ GUniquePtr<char> dconfRuntimeDir(g_build_filename(runtimeDir, "dconf", nullptr)); >+ args.appendVector(Vector<CString>({ "--bind", dconfRuntimeDir.get(), dconfRuntimeDir.get() })); >+ >+ const char* dconfDir = g_getenv("DCONF_USER_CONFIG_DIR"); >+ if (dconfDir) >+ bindIfExists(args, dconfDir); >+ else { >+ const char* configDir = g_get_user_config_dir(); >+ GUniquePtr<char> dconfConfigDir(g_build_filename(configDir, "dconf", nullptr)); >+ bindIfExists(args, dconfConfigDir.get(), BindFlags::ReadWrite); >+ } >+} >+ >+static void bindWayland(Vector<CString>& args) >+{ >+ const char* display = g_getenv("WAYLAND_DISPLAY"); >+ if (!display) >+ display = "wayland-0"; >+ >+ const char* runtimeDir = g_get_user_runtime_dir(); >+ GUniquePtr<char> waylandRuntimeFile(g_build_filename(runtimeDir, display, nullptr)); >+ bindIfExists(args, waylandRuntimeFile.get(), BindFlags::ReadWrite); >+} >+ >+static void bindPulse(Vector<CString>& args) >+{ >+ // FIXME: The server can be defined in config files we'd have to parse. >+ // They can also be set as X11 props but that is getting a bit ridiculous. >+ const char* pulseServer = g_getenv("PULSE_SERVER"); >+ if (pulseServer) { >+ if (g_str_has_prefix(pulseServer, "unix:")) >+ bindIfExists(args, pulseServer + 5, BindFlags::ReadWrite); >+ // else it uses tcp >+ } else { >+ const char* runtimeDir = g_get_user_runtime_dir(); >+ GUniquePtr<char> pulseRuntimeDir(g_build_filename(runtimeDir, "pulse", nullptr)); >+ bindIfExists(args, pulseRuntimeDir.get(), BindFlags::ReadWrite); >+ } >+ >+ const char* pulseConfig = g_getenv("PULSE_CLIENTCONFIG"); >+ if (pulseConfig) >+ bindIfExists(args, pulseConfig); >+ >+ const char* configDir = g_get_user_config_dir(); >+ GUniquePtr<char> pulseConfigDir(g_build_filename(configDir, "pulse", nullptr)); >+ bindIfExists(args, pulseConfigDir.get()); >+ >+ const char* homeDir = g_get_home_dir(); >+ GUniquePtr<char> pulseHomeConfigDir(g_build_filename(homeDir, ".pulse", nullptr)); >+ GUniquePtr<char> asoundHomeConfigDir(g_build_filename(homeDir, ".asoundrc", nullptr)); >+ bindIfExists(args, pulseHomeConfigDir.get()); >+ bindIfExists(args, asoundHomeConfigDir.get()); >+ >+ // This is the ultimate fallback to raw ALSA >+ bindIfExists(args, "/dev/snd", BindFlags::Device); >+} >+ >+static void bindFonts(Vector<CString>& args) >+{ >+ const char* configDir = g_get_user_config_dir(); >+ const char* homeDir = g_get_home_dir(); >+ const char* dataDir = g_get_user_data_dir(); >+ const char* cacheDir = g_get_user_cache_dir(); >+ >+ // Configs can include custom dirs but then we have to parse them... >+ GUniquePtr<char> fontConfig(g_build_filename(configDir, "fontconfig", nullptr)); >+ GUniquePtr<char> fontCache(g_build_filename(cacheDir, "fontconfig", nullptr)); >+ GUniquePtr<char> fontHomeConfig(g_build_filename(homeDir, ".fonts.conf", nullptr)); >+ GUniquePtr<char> fontHomeConfigDir(g_build_filename(configDir, ".fonts.conf.d", nullptr)); >+ GUniquePtr<char> fontData(g_build_filename(dataDir, "fonts", nullptr)); >+ GUniquePtr<char> fontHomeData(g_build_filename(homeDir, ".fonts", nullptr)); >+ bindIfExists(args, fontConfig.get()); >+ bindIfExists(args, fontCache.get(), BindFlags::ReadWrite); >+ bindIfExists(args, fontHomeConfig.get()); >+ bindIfExists(args, fontHomeConfigDir.get()); >+ bindIfExists(args, fontData.get()); >+ bindIfExists(args, fontHomeData.get()); >+} >+ >+#if PLATFORM(GTK) >+static void bindGtkData(Vector<CString>& args) >+{ >+ const char* configDir = g_get_user_config_dir(); >+ const char* dataDir = g_get_user_data_dir(); >+ const char* homeDir = g_get_home_dir(); >+ >+ GUniquePtr<char> gtkConfig(g_build_filename(configDir, "gtk-3.0", nullptr)); >+ GUniquePtr<char> themeData(g_build_filename(dataDir, "themes", nullptr)); >+ GUniquePtr<char> themeHomeData(g_build_filename(homeDir, ".themes", nullptr)); >+ GUniquePtr<char> iconHomeData(g_build_filename(homeDir, ".icons", nullptr)); >+ bindIfExists(args, gtkConfig.get()); >+ bindIfExists(args, themeData.get()); >+ bindIfExists(args, themeHomeData.get()); >+ bindIfExists(args, iconHomeData.get()); >+} >+ >+static void bindA11y(Vector<CString>& args) >+{ >+ static DBusProxy proxy; >+ >+ if (!proxy.isRunning()) { >+ // FIXME: Avoid blocking IO... (It is at least a one-time cost) >+ GRefPtr<GDBusConnection> sessionBus(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr)); >+ if (!sessionBus.get()) >+ return; >+ >+ GRefPtr<GDBusMessage> msg(g_dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus", "GetAddress")); >+ g_dbus_message_set_body(msg.get(), g_variant_new("()")); >+ GRefPtr<GDBusMessage> reply(g_dbus_connection_send_message_with_reply_sync( >+ sessionBus.get(), msg.get(), >+ G_DBUS_SEND_MESSAGE_FLAGS_NONE, >+ 30000, >+ nullptr, >+ nullptr, >+ nullptr)); >+ >+ GUniqueOutPtr<GError> error; >+ GUniqueOutPtr<char> a11yAddress; >+ if (reply.get()) { >+ if (g_dbus_message_to_gerror(reply.get(), &error.outPtr())) { >+ if (!g_error_matches(error.get(), G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) >+ g_warning("Can't find a11y bus: %s", error.get()->message); >+ } else >+ g_variant_get(g_dbus_message_get_body(reply.get()), "(s)", &a11yAddress.outPtr()); >+ } >+ >+ if (!a11yAddress.get()) >+ return; >+ >+ GUniquePtr<char> a11yPath(dbusAddressToPath(a11yAddress.get(), true)); >+ if (!a11yAddress.get()) >+ return; >+ >+ proxy.setSocket(a11yAddress.get(), a11yPath.get()); >+ proxy.appendPermissions({ >+ "--sloppy-names", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Embed@/org/a11y/atspi/accessible/root", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Unembed@/org/a11y/atspi/accessible/root", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.Registry.GetRegisteredEvents@/org/a11y/atspi/registry", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetKeystrokeListeners@/org/a11y/atspi/registry/deviceeventcontroller", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetDeviceEventListeners@/org/a11y/atspi/registry/deviceeventcontroller", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersSync@/org/a11y/atspi/registry/deviceeventcontroller", >+ "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersAsync@/org/a11y/atspi/registry/deviceeventcontroller", >+ }); >+ proxy.launch(); >+ } >+ >+ args.appendVector(Vector<CString>({ >+ "--bind", proxy.proxyPath(), proxy.path(), >+ })); >+} >+#endif >+ >+static bool bindPathVar(Vector<CString>& args, const char* varname) >+{ >+ const char* pathValue = g_getenv(varname); >+ if (!pathValue) >+ return false; >+ >+ GUniquePtr<char*> splitPaths(g_strsplit(pathValue, ":", -1)); >+ for (size_t i; splitPaths.get()[i]; ++i) >+ bindIfExists(args, splitPaths.get()[i]); >+ >+ return true; >+} >+ >+static void bindGStreamerData(Vector<CString>& args) >+{ >+ if (!bindPathVar(args, "GST_PLUGIN_PATH_1_0")) >+ bindPathVar(args, "GST_PLUGIN_PATH"); >+ >+ if (!bindPathVar(args, "GST_PLUGIN_SYSTEM_PATH_1_0")) { >+ if (!bindPathVar(args, "GST_PLUGIN_SYSTEM_PATH")) { >+ GUniquePtr<char> gstData(g_build_filename(g_get_user_data_dir(), "gstreamer-1.0", nullptr)); >+ bindIfExists(args, gstData.get()); >+ } >+ } >+ >+ GUniquePtr<char> gstCache(g_build_filename(g_get_user_cache_dir(), "gstreamer-1.0", nullptr)); >+ bindIfExists(args, gstCache.get(), BindFlags::ReadWrite); >+ >+ // /usr/lib is already added so this is only requried for other dirs >+ const char* scannerPath = g_getenv("GST_PLUGIN_SCANNER") ?: "/usr/libexec/gstreamer-1.0/gst-plugin-scanner"; >+ const char* helperPath = g_getenv("GST_INSTALL_PLUGINS_HELPER ") ?: "/usr/libexec/gst-install-plugins-helper"; >+ >+ bindIfExists(args, scannerPath); >+ bindIfExists(args, helperPath); >+} >+ >+static void bindOpenGL(Vector<CString>& args) >+{ >+ args.appendVector(Vector<CString>({ >+ "--dev-bind-try", "/dev/dri", "/dev/dri", >+ // Mali >+ "--dev-bind-try", "/dev/mali", "/dev/mali", >+ "--dev-bind-try", "/dev/mali0", "/dev/mali0", >+ "--dev-bind-try", "/dev/umplock", "/dev/umplock", >+ // Nvidia >+ "--dev-bind-try", "/dev/nvidiactl", "/dev/nvidiactl", >+ "--dev-bind-try", "/dev/nvidia0", "/dev/nvidia0", >+ "--dev-bind-try", "/dev/nvidia", "/dev/nvidia", >+ // Adreno >+ "--dev-bind-try", "/dev/kgsl-3d0", "/dev/kgsl-3d0", >+ "--dev-bind-try", "/dev/ion", "/dev/ion", >+#if PLATFORM(WPE) >+ "--dev-bind-try", "/dev/fb0", "/dev/fb0", >+ "--dev-bind-try", "/dev/fb1", "/dev/fb1", >+#endif >+ })); >+} >+ >+static void bindV4l(Vector<CString>& args) >+{ >+ args.appendVector(Vector<CString>({ >+ "--dev-bind-try", "/dev/v4l", "/dev/v4l", >+ // Not pretty but a stop-gap for pipewire anyway >+ "--dev-bind-try", "/dev/video0", "/dev/video0", >+ "--dev-bind-try", "/dev/video1", "/dev/video1", >+ })); >+} >+ >+static int setupSeccomp() >+{ >+ // NOTE: This is shared code (flatpak-run.c - LGPLv2.1+) >+ // There are today a number of different Linux container >+ // implementations. That will likely continue for long into the >+ // future. But we can still try to share code, and it's important >+ // to do so because it affects what library and application writers >+ // can do, and we should support code portability between different >+ // container tools. >+ // >+ // This syscall blacklist is copied from linux-user-chroot, which was in turn >+ // clearly influenced by the Sandstorm.io blacklist. >+ // >+ // If you make any changes here, I suggest sending the changes along >+ // to other sandbox maintainers. Using the libseccomp list is also >+ // an appropriate venue: >+ // https://groups.google.com/forum/#!topic/libseccomp >+ // >+ // A non-exhaustive list of links to container tooling that might >+ // want to share this blacklist: >+ // >+ // https://github.com/sandstorm-io/sandstorm >+ // in src/sandstorm/supervisor.c++ >+ // http://cgit.freedesktop.org/xdg-app/xdg-app/ >+ // in common/flatpak-run.c >+ // https://git.gnome.org/browse/linux-user-chroot >+ // in src/setup-seccomp.c >+ struct scmp_arg_cmp clone_arg = SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER); >+ struct scmp_arg_cmp tty_arg = SCMP_A1(SCMP_CMP_EQ, (int)TIOCSTI); >+ struct { >+ int scall; >+ struct scmp_arg_cmp* arg; >+ } syscall_blacklist[] = { >+ // Block dmesg >+ { SCMP_SYS(syslog), nullptr }, >+ // Useless old syscall >+ { SCMP_SYS(uselib), nullptr }, >+ // Don't allow disabling accounting >+ { SCMP_SYS(acct), nullptr }, >+ // 16-bit code is unnecessary in the sandbox, and modify_ldt is a >+ // historic source of interesting information leaks. >+ { SCMP_SYS(modify_ldt), nullptr }, >+ // Don't allow reading current quota use >+ { SCMP_SYS(quotactl), nullptr }, >+ >+ // Don't allow access to the kernel keyring >+ { SCMP_SYS(add_key), nullptr }, >+ { SCMP_SYS(keyctl), nullptr }, >+ { SCMP_SYS(request_key), nullptr }, >+ >+ // Scary VM/NUMA ops >+ { SCMP_SYS(move_pages), nullptr }, >+ { SCMP_SYS(mbind), nullptr }, >+ { SCMP_SYS(get_mempolicy), nullptr }, >+ { SCMP_SYS(set_mempolicy), nullptr }, >+ { SCMP_SYS(migrate_pages), nullptr }, >+ >+ // Don't allow subnamespace setups: >+ { SCMP_SYS(unshare), nullptr }, >+ { SCMP_SYS(mount), nullptr }, >+ { SCMP_SYS(pivot_root), nullptr }, >+ { SCMP_SYS(clone), &clone_arg }, >+ >+ // Don't allow faking input to the controlling tty (CVE-2017-5226) >+ { SCMP_SYS(ioctl), &tty_arg }, >+ >+ // Profiling operations; we expect these to be done by tools from outside >+ // the sandbox. In particular perf has been the source of many CVEs. >+ { SCMP_SYS(perf_event_open), nullptr }, >+ // Don't allow you to switch to bsd emulation or whatnot >+ { SCMP_SYS(personality), nullptr }, >+ { SCMP_SYS(ptrace), nullptr } >+ }; >+ >+ scmp_filter_ctx seccomp = seccomp_init(SCMP_ACT_ALLOW); >+ if (!seccomp) { >+ g_error("Failed to init seccomp"); >+ return -1; >+ } >+ >+ for (auto& rule : syscall_blacklist) { >+ int scall = rule.scall; >+ int r; >+ if (rule.arg) >+ r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), scall, 1, rule.arg); >+ else >+ r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), scall, 0); >+ if (r == -EFAULT) { >+ g_error("Failed to add seccomp rule"); >+ seccomp_release(seccomp); >+ return -1; >+ } >+ } >+ >+ int tmpfd = memfd_create("seccomp-bpf", 0); >+ if (tmpfd == -1) { >+ g_error("Failed to create memfd: %s", g_strerror(errno)); >+ seccomp_release(seccomp); >+ return -1; >+ } >+ >+ if (seccomp_export_bpf(seccomp, tmpfd)) { >+ g_error("Failed to export seccomp bpf"); >+ seccomp_release(seccomp); >+ close(tmpfd); >+ return -1; >+ } >+ >+ if (lseek(tmpfd, 0, SEEK_SET) < 0) >+ g_error("lseek failed: %s", g_strerror(errno)); >+ >+ seccomp_release(seccomp); >+ return tmpfd; >+} >+ >+static void ensureDirectoryExists(const String& path) >+{ >+ if (path.isEmpty()) >+ return; >+ >+ // FIXME: Blocking >+ if (g_mkdir_with_parents(path.utf8().data(), 0700) == -1 && errno != ENOTDIR) >+ g_warning("Could not create directory \"%s\": %s", path.utf8().data(), g_strerror(errno)); >+} >+ >+static DBusProxy webProcessDBusProxy; >+ >+static std::tuple<Vector<CString>, int, int> getBwrapArgs(ProcessLauncher::ProcessType processType, const Vector<String>& extraPaths) >+{ >+ // It is impossible to know what access arbitrary plugins need and since it is for legacy >+ // reasons lets just leave it unsandboxed. >+ if (processType == ProcessLauncher::ProcessType::Plugin64 >+ || processType == ProcessLauncher::ProcessType::Plugin32) >+ return {{ }, -1, -1}; >+ >+ // NOTE: This is not a great solution but we just assume that applications create this directory >+ // ahead of time if they require it >+ GUniquePtr<char> configDir(g_build_filename(g_get_user_config_dir(), g_get_prgname(), nullptr)); >+ >+ Vector<CString> sandboxArgs = { >+ "--die-with-parent", >+ "--unshare-pid", >+ "--unshare-uts", >+ >+ // We assume /etc has safe permissions >+ // At a later point we can start masking privacy-concerning files >+ "--ro-bind", "/etc", "/etc", >+ "--dev", "/dev", >+ "--proc", "/proc", >+ "--tmpfs", "/tmp", >+ "--unsetenv", "TMPDIR", >+ "--dir", "/run", >+ "--symlink", "../run", "/var/run", >+ "--symlink", "../tmp", "/var/tmp", >+ "--ro-bind", "/sys/block", "/sys/block", >+ "--ro-bind", "/sys/bus", "/sys/bus", >+ "--ro-bind", "/sys/class", "/sys/class", >+ "--ro-bind", "/sys/dev", "/sys/dev", >+ "--ro-bind", "/sys/devices", "/sys/devices", >+ >+ "--ro-bind-try", "/usr/share", "/usr/share", >+ "--ro-bind-try", "/usr/local/share", "/usr/local/share", >+ "--ro-bind-try", DATADIR, DATADIR, >+ >+ // We only grant access to the libdirs webkit is built with and >+ // guess system libdirs. This will always have some edge cases >+ "--ro-bind-try", "/lib", "/lib", >+ "--ro-bind-try", "/usr/lib", "/usr/lib", >+ "--ro-bind-try", "/usr/local/lib", "/usr/local/lib", >+ "--ro-bind-try", LIBDIR, LIBDIR, >+ "--ro-bind-try", "/lib64", "/lib64", >+ "--ro-bind-try", "/usr/lib64", "/usr/lib64", >+ "--ro-bind-try", "/usr/local/lib64", "/usr/local/lib64", >+ >+ "--ro-bind-try", PKGLIBEXECDIR, PKGLIBEXECDIR, >+ >+ "--setenv", "GTK_USE_PORTAL", "1", >+ >+ "--bind-try", configDir.get(), configDir.get(), >+ }; >+ // We would have to parse ld config files for more info >+ bindPathVar(sandboxArgs, "LD_LIBRARY_PATH"); >+ >+ if (processType == ProcessLauncher::ProcessType::Network) { >+ static DBusProxy proxy; >+ >+ // FIXME: The network process is used for `file://` URIs and >+ // we would have to pass through most paths for this to work >+ >+ // glib-networking can use dbus for proxy information >+ // FIXME: Find and add the permissions it needs for this (pacrunner) >+ bindDBusSession(sandboxArgs, proxy); >+ } >+ >+ // NOTE: This has network access for HLS via GStreamer >+ if (processType == ProcessLauncher::ProcessType::Web) { >+ >+ >+ if (!webProcessDBusProxy.isRunning()) { >+ webProcessDBusProxy.appendPermissions({ >+ "--talk=ca.desrt.dconf", // Used by GTK on Wayland >+ "--talk=org.freedesktop.ScreenSaver", // GStreamer inhibits, FIXME: Use xdg-desktop-portal >+ // xdg-desktop-portal used by GTK >+ "--talk=org.freedesktop.portal.Desktop", >+ // GStreamers plugin install helper >+ "--call=org.freedesktop.PackageKit=org.freedesktop.PackageKit.Modify2.InstallGStreamerResources@/org/freedesktop/PackageKit" >+ // "--talk=org.freedesktop.secrets" // FIXME: WebKitGTK uses for HTTP Auth >+ }); >+ } >+ >+ // If Wayland in use don't grant X11 >+#if PLATFORM(WAYLAND) && USE(EGL) >+ if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::Wayland) { >+ bindWayland(sandboxArgs); >+ sandboxArgs.append("--unshare-ipc"); >+ } else >+#endif >+ bindX11(sandboxArgs); >+ >+ bindDBusSession(sandboxArgs, webProcessDBusProxy); >+ // FIXME: This needs to be restricted, upstream is working on it >+ bindDconf(sandboxArgs); >+ // FIXME: We should move to Pipewire as soon as viable, Pulse doesn't restrict clients atm >+ bindPulse(sandboxArgs); >+ bindFonts(sandboxArgs); >+ bindGStreamerData(sandboxArgs); >+ bindOpenGL(sandboxArgs); >+ // FIXME: This is also fixed by Pipewire once in use >+ bindV4l(sandboxArgs); >+#if PLATFORM(GTK) >+ bindA11y(sandboxArgs); >+ bindGtkData(sandboxArgs); >+#endif >+ } else { >+ // Only X11 users need this for XShm which is only the Web process >+ sandboxArgs.append("--unshare-ipc"); >+ } >+ >+ // Only process without any network access for now >+ if (processType == ProcessLauncher::ProcessType::Storage) >+ sandboxArgs.append("--unshare-net"); >+ >+#if ENABLE(DEVELOPER_MODE) >+ const char* execDirectory = g_getenv("WEBKIT_EXEC_PATH"); >+ if (execDirectory) { >+ String parentDir = FileSystem::directoryName(FileSystem::stringFromFileSystemRepresentation(execDirectory)); >+ bindIfExists(sandboxArgs, parentDir.utf8().data()); >+ } >+ >+ CString executablePath = getCurrentExecutablePath(); >+ if (!executablePath.isNull()) { >+ // Our executable is `/foo/bar/bin/Process`, we want `/foo/bar` as a usable prefix >+ String parentDir = FileSystem::directoryName(FileSystem::directoryName(FileSystem::stringFromFileSystemRepresentation(executablePath.data()))); >+ bindIfExists(sandboxArgs, parentDir.utf8().data()); >+ } >+#endif >+ >+ int seccompFd = setupSeccomp(); >+ GUniquePtr<char> fdStr(g_strdup_printf("%d", seccompFd)); >+ sandboxArgs.appendVector(Vector<CString>({ "--seccomp", fdStr.get() })); >+ >+ for (const String& path : extraPaths) { >+ ensureDirectoryExists(path); >+ if (path.isEmpty()) >+ g_warning("Empty path passed to sandbox, this is probably a bug"); >+ else { >+ const char* cpath = path.utf8().data(); >+ sandboxArgs.appendVector(Vector<CString>({ "--bind", cpath, cpath })); >+ } >+ } >+ >+ int bwrapFd = argsToFd(sandboxArgs, "bwrap"); >+ GUniquePtr<char> bwrapFdStr(g_strdup_printf("%d", bwrapFd)); >+ >+ Vector<CString> bwrapArgs = { >+ BWRAP_EXECUTABLE, >+ "--args", >+ bwrapFdStr.get(), >+ "--", >+ }; >+ >+ return {bwrapArgs, seccompFd, bwrapFd}; >+} >+ >+#endif >+ >+#if OS(LINUX) >+static bool isInsideFlatpak() >+{ >+ static int ret = -1; >+ if (ret != -1) >+ return ret; >+ >+ GUniquePtr<GKeyFile> infoFile(g_key_file_new()); >+ if (!g_key_file_load_from_file(infoFile.get(), "/.flatpak-info", G_KEY_FILE_NONE, nullptr)) { >+ ret = false; >+ return ret; >+ } >+ >+ // If we are in a `flatpak build` session we cannot launch ourselves since we aren't installed >+ ret = !g_key_file_get_boolean(infoFile.get(), "Instance", "build", nullptr); >+ return ret; >+} >+ >+static Vector<CString> getFlatpakArgs(ProcessLauncher::ProcessType processType) >+{ >+ // When we are running inside of flatpak's sandbox we do not have permissions to >+ // use the same sandbox we do outside but flatpak offers to create new sandboxes >+ // for us using flatpak-spawn. >+ // >+ // This is extremely bare-bones atm though as the Spawn interface does not expose >+ // much outside of `--sandbox` (no permissions) and `--no-network`. We need to >+ // add some permissions in between those for this to provide meaningful security. >+ Vector<CString> sandboxArgs = { >+ "/usr/bin/flatpak-spawn", >+ }; >+ >+ if (processType == ProcessLauncher::ProcessType::Storage) >+ sandboxArgs.append("--no-network"); >+ >+ return sandboxArgs; >+} >+#endif >+ > void ProcessLauncher::launchProcess() > { > GPid pid = 0; >@@ -120,6 +924,31 @@ void ProcessLauncher::launchProcess() > } > #endif > >+#if OS(LINUX) >+ const char* sandboxEnv = g_getenv("WEBKIT_SANDBOX_ENABLED"); >+ bool sandboxEnabled = m_launchOptions.sandboxEnabled; >+ int seccompFd = -1; >+ int bwrapFd = -1; >+ >+ if (sandboxEnv) >+ sandboxEnabled = !strcmp(sandboxEnv, "1"); >+ >+ Vector<CString> sandboxArgs = { }; >+ if (sandboxEnabled) { >+ if (isInsideFlatpak()) >+ sandboxArgs = getFlatpakArgs(m_launchOptions.processType); >+#if ENABLE(BUBBLEWRAP_SANDBOX) >+ else { >+ auto ret = getBwrapArgs(m_launchOptions.processType, m_launchOptions.extraSandboxPaths); >+ sandboxArgs = std::get<0>(ret); >+ seccompFd = std::get<1>(ret); >+ bwrapFd = std::get<2>(ret); >+ } >+#endif >+ } >+ nargs += sandboxArgs.size(); >+#endif >+ > char** argv = g_newa(char*, nargs); > unsigned i = 0; > #if ENABLE(DEVELOPER_MODE) >@@ -127,6 +956,12 @@ void ProcessLauncher::launchProcess() > for (auto& arg : prefixArgs) > argv[i++] = const_cast<char*>(arg.data()); > #endif >+ >+#if OS(LINUX) >+ for (auto& arg : sandboxArgs) >+ argv[i++] = const_cast<char*>(arg.data()); >+#endif >+ > argv[i++] = const_cast<char*>(realExecutablePath.data()); > argv[i++] = processIdentifier.get(); > argv[i++] = webkitSocket.get(); >@@ -151,6 +986,13 @@ void ProcessLauncher::launchProcess() > if (!setCloseOnExec(socketPair.client)) > RELEASE_ASSERT_NOT_REACHED(); > >+#if OS(LINUX) >+ if (seccompFd != -1) >+ close(seccompFd); >+ if (bwrapFd != -1) >+ close(bwrapFd); >+#endif >+ > close(socketPair.client); > m_processIdentifier = pid; > >diff --git a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp >index 61b604d9deac13ed6dee8fee366d42977531986b..cb59009334ada3189e2561b0c5c6eaa28a8b5994 100644 >--- a/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp >+++ b/Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp >@@ -106,6 +106,14 @@ void NetworkProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launc > processPool().setShouldMakeNextNetworkProcessLaunchFailForTesting(false); > launchOptions.shouldMakeProcessLaunchFailForTesting = true; > } >+ >+#if PLATFORM(GTK) || PLATFORM(WPE) >+ WebsiteDataStore& store = processPool().websiteDataStore()->websiteDataStore(); >+ store.resolveDirectoriesIfNecessary(); >+ launchOptions.extraSandboxPaths.append(store.resolvedNetworkCacheDirectory()); >+ >+ launchOptions.sandboxEnabled = m_processPool.sandboxEnabled(); >+#endif > } > > void NetworkProcessProxy::connectionWillOpen(IPC::Connection& connection) >diff --git a/Source/WebKit/UIProcess/Storage/StorageProcessProxy.cpp b/Source/WebKit/UIProcess/Storage/StorageProcessProxy.cpp >index 3a131b4d60d8aa07e87fb5e4c614f30a7d42b574..5306322e0ff20f020c1c2b6fbf3357811ac47dec 100644 >--- a/Source/WebKit/UIProcess/Storage/StorageProcessProxy.cpp >+++ b/Source/WebKit/UIProcess/Storage/StorageProcessProxy.cpp >@@ -86,6 +86,14 @@ void StorageProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launc > { > launchOptions.processType = ProcessLauncher::ProcessType::Storage; > ChildProcessProxy::getLaunchOptions(launchOptions); >+ >+#if PLATFORM(GTK) || PLATFORM(WPE) >+ launchOptions.sandboxEnabled = m_processPool.sandboxEnabled(); >+ >+ WebsiteDataStore& store = m_processPool.websiteDataStore()->websiteDataStore(); >+ store.resolveDirectoriesIfNecessary(); >+ launchOptions.extraSandboxPaths.append(store.resolvedIndexedDatabaseDirectory()); >+#endif > } > > void StorageProcessProxy::processWillShutDown(IPC::Connection& connection) >diff --git a/Source/WebKit/UIProcess/WebProcessPool.h b/Source/WebKit/UIProcess/WebProcessPool.h >index a93998ea1fa088884c0a341dde4b591733293cf5..f1b2bc6d20c91466ce569747adb3031cbd6013a1 100644 >--- a/Source/WebKit/UIProcess/WebProcessPool.h >+++ b/Source/WebKit/UIProcess/WebProcessPool.h >@@ -461,6 +461,11 @@ public: > > void sendDisplayConfigurationChangedMessageForTesting(); > >+#if PLATFORM(GTK) || PLATFORM(WPE) >+ void setSandboxEnabled(bool enabled) { m_sandboxEnabled = enabled; }; >+ bool sandboxEnabled() const { return m_sandboxEnabled; }; >+#endif >+ > private: > void platformInitialize(); > >@@ -705,6 +710,10 @@ private: > > HashMap<WebCore::SecurityOriginData, Vector<SuspendedPageProxy*>> m_suspendedPages; > HashMap<String, RefPtr<WebProcessProxy>> m_swappedProcessesPerRegistrableDomain; >+ >+#if PLATFORM(GTK) || PLATFORM(WPE) >+ bool m_sandboxEnabled { false }; >+#endif > }; > > template<typename T> >diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp >index e4f47d930a7bed4c1b621dff9db3198d80cf4ce8..bb44eb0b7dccd4de6eaed3391063a15322ce643c 100644 >--- a/Source/WebKit/UIProcess/WebProcessProxy.cpp >+++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp >@@ -195,6 +195,24 @@ void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOpt > processPool().setShouldMakeNextWebProcessLaunchFailForTesting(false); > launchOptions.shouldMakeProcessLaunchFailForTesting = true; > } >+ >+#if PLATFORM(GTK) || PLATFORM(WPE) >+ launchOptions.sandboxEnabled = m_processPool->sandboxEnabled(); >+ >+ websiteDataStore().resolveDirectoriesIfNecessary(); >+ launchOptions.extraSandboxPaths.append(websiteDataStore().resolvedNetworkCacheDirectory()); >+ launchOptions.extraSandboxPaths.append(websiteDataStore().resolvedApplicationCacheDirectory()); >+ >+#if PLATFORM(WAYLAND) && USE(EGL) >+ if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::Wayland) { >+ String displayName = WaylandCompositor::singleton().displayName(); >+ String runtimeDir(g_get_user_runtime_dir()); >+ String waylandSocket = FileSystem::pathByAppendingComponent(runtimeDir, displayName); >+ launchOptions.extraSandboxPaths.append(waylandSocket); >+ } >+#endif >+ >+#endif > } > > void WebProcessProxy::connectionWillOpen(IPC::Connection& connection) >diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp >index a1372b1d7dd8be1b6dffc8be1a959707f70dff97..e4fdd0d40722a05d336e198858a256dc0a678d10 100644 >--- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp >+++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp >@@ -178,6 +178,10 @@ void WebsiteDataStore::resolveDirectoriesIfNecessary() > m_resolvedConfiguration.javaScriptConfigurationDirectory = resolvePathForSandboxExtension(m_configuration.javaScriptConfigurationDirectory); > if (!m_configuration.cacheStorageDirectory.isEmpty() && m_resolvedConfiguration.cacheStorageDirectory.isEmpty()) > m_resolvedConfiguration.cacheStorageDirectory = resolvePathForSandboxExtension(m_configuration.cacheStorageDirectory); >+ if (!m_configuration.networkCacheDirectory.isEmpty()) >+ m_resolvedConfiguration.networkCacheDirectory = resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration.networkCacheDirectory); >+ if (!m_configuration.localStorageDirectory.isEmpty()) >+ m_resolvedConfiguration.localStorageDirectory = resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration.localStorageDirectory); > > // Resolve directories for file paths. > if (!m_configuration.cookieStorageFile.isEmpty()) { >diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h >index a80ef16cebc97c479f3706bd07056a477278cda2..d3b8acae946a51154cca7f9a8da33f5192a2ae54 100644 >--- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h >+++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h >@@ -154,6 +154,9 @@ public: > const String& resolvedIndexedDatabaseDirectory() const { return m_resolvedConfiguration.indexedDBDatabaseDirectory; } > const String& resolvedServiceWorkerRegistrationDirectory() const { return m_resolvedConfiguration.serviceWorkerRegistrationDirectory; } > const String& resolvedResourceLoadStatisticsDirectory() const { return m_resolvedConfiguration.resourceLoadStatisticsDirectory; } >+ const String& resolvedNetworkCacheDirectory() const { return m_resolvedConfiguration.networkCacheDirectory; } >+ const String& resolvedLocalStorageDirectory() const { return m_resolvedConfiguration.localStorageDirectory; } >+ > > StorageManager* storageManager() { return m_storageManager.get(); } > >diff --git a/Source/cmake/FindLibseccomp.cmake b/Source/cmake/FindLibseccomp.cmake >new file mode 100644 >index 0000000000000000000000000000000000000000..e6a22803dda02581ff972ce8aeceac0bd09d1a84 >--- /dev/null >+++ b/Source/cmake/FindLibseccomp.cmake >@@ -0,0 +1,53 @@ >+# - Try to find libseccomp >+# Once done, this will define >+# >+# LIBSECCOMP_FOUND - system has libseccomp >+# LIBSECCOMP_INCLUDE_DIRS - the libseccomp include drectories >+# LIBSECCOMP_LIBRARIES - link these to use libseccomp >+# >+# Copyright (C) 2018 Igalia S.L. >+# >+# Redistribution and use in source and binary forms, with or without >+# modification, are permitted provided that the following conditions >+# are met: >+# 1. Redistributions of source code must retain the above copyright >+# notice, this list of conditions and the following disclaimer. >+# 2. Redistributions in binary form must reproduce the above copyright >+# notice, this list of conditions and the following disclaimer in the >+# documentation and/or other materials provided with the distribution. >+# >+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS >+# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, >+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS >+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; >+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, >+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR >+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF >+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ >+find_package(PkgConfig) >+pkg_check_modules(PC_LIBSECCOMP libseccomp) >+ >+find_path(LIBSECCOMP_INCLUDE_DIRS >+ NAMES seccomp.h >+ HINTS ${PC_LIBSECCOMP_INCLUDEDIR} >+) >+ >+find_library(LIBSECCOMP_LIBRARIES >+ NAMES seccomp >+ HINTS ${PC_LIBSECCOMP_LIBDIR} >+) >+ >+include(FindPackageHandleStandardArgs) >+find_package_handle_standard_args(LIBSECCOMP >+ REQUIRED_VARS LIBSECCOMP_LIBRARIES >+ FOUND_VAR LIBSECCOMP_FOUND >+ VERSION_VAR PC_LIBSECCOMP_VERSION) >+ >+mark_as_advanced( >+ LIBSECCOMP_INCLUDE_DIRS >+ LIBSECCOMP_LIBRARIES >+) >\ No newline at end of file >diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake >index 08f6c8372747e68790abf9eb8078a568b8ecfa06..4fe7c2c86e2247bab97d13b5f46c9a4bc69cdbdf 100644 >--- a/Source/cmake/OptionsGTK.cmake >+++ b/Source/cmake/OptionsGTK.cmake >@@ -118,6 +118,12 @@ else () > WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_RESOURCE_USAGE PRIVATE OFF) > endif () > >+if (CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT EXISTS "/.flatpak-info") >+ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_BUBBLEWRAP_SANDBOX PUBLIC ON) >+else () >+ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_BUBBLEWRAP_SANDBOX PRIVATE OFF) >+endif () >+ > # Public options shared with other WebKit ports. Do not add any options here > # without approval from a GTK+ reviewer. There must be strong reason to support > # changing the value of the option. >@@ -201,6 +207,38 @@ if (ENABLE_ACCELERATED_2D_CANVAS) > endif () > endif () > >+if (ENABLE_BUBBLEWRAP_SANDBOX) >+ find_program(BWRAP_EXECUTABLE bwrap) >+ if (NOT BWRAP_EXECUTABLE) >+ message(FATAL_ERROR "bwrap executable is needed for ENABLE_BUBBLEWRAP_SANDBOX") >+ endif () >+ add_definitions(-DBWRAP_EXECUTABLE="${BWRAP_EXECUTABLE}") >+ >+ execute_process( >+ COMMAND "${BWRAP_EXECUTABLE}" --version >+ RESULT_VARIABLE BWRAP_RET >+ OUTPUT_VARIABLE BWRAP_OUTPUT >+ ) >+ if (BWRAP_RET) >+ message(FATAL_ERROR "Failed to run ${BWRAP_EXECUTABLE}") >+ endif () >+ string(REGEX MATCH "([0-9]+.[0-9]+.[0-9]+)" BWRAP_VERSION "${BWRAP_OUTPUT}") >+ if (NOT "${BWRAP_VERSION}" VERSION_GREATER_EQUAL "0.3.1") >+ message(FATAL_ERROR "bwrap must be >= 0.3.1 but ${BWRAP_VERSION} found") >+ endif () >+ >+ find_package(Libseccomp) >+ if (NOT LIBSECCOMP_FOUND) >+ message(FATAL_ERROR "libseccomp is needed for ENABLE_BUBBLEWRAP_SANDBOX") >+ endif () >+ >+ find_program(DBUS_PROXY_EXECUTABLE xdg-dbus-proxy) >+ if (NOT DBUS_PROXY_EXECUTABLE) >+ message(FATAL_ERROR "xdg-dbus-proxy not found and is needed for ENABLE_BUBBLEWRAP_SANDBOX") >+ endif () >+ add_definitions(-DDBUS_PROXY_EXECUTABLE="${DBUS_PROXY_EXECUTABLE}") >+endif () >+ > if (USE_LIBSECRET) > find_package(Libsecret) > if (NOT LIBSECRET_FOUND) >diff --git a/Source/cmake/WebKitFeatures.cmake b/Source/cmake/WebKitFeatures.cmake >index 116e0d96974f12edde0605c1e7cf6d238bf3fa00..5ef4ce327397e7d56371dad93672d99189bcaf42 100644 >--- a/Source/cmake/WebKitFeatures.cmake >+++ b/Source/cmake/WebKitFeatures.cmake >@@ -85,6 +85,7 @@ macro(WEBKIT_OPTION_BEGIN) > WEBKIT_OPTION_DEFINE(ENABLE_ASYNC_SCROLLING "Enable asynchronouse scrolling" PRIVATE OFF) > WEBKIT_OPTION_DEFINE(ENABLE_ATTACHMENT_ELEMENT "Toggle attachment element support" PRIVATE OFF) > WEBKIT_OPTION_DEFINE(ENABLE_AVF_CAPTIONS "Toggle AVFoundation caption support" PRIVATE OFF) >+ WEBKIT_OPTION_DEFINE(ENABLE_BUBBLEWRAP_SANDBOX "Toggle bubblewrap sandboxing support" PRIVATE OFF) > WEBKIT_OPTION_DEFINE(ENABLE_CACHE_PARTITIONING "Toggle cache partitioning support" PRIVATE OFF) > WEBKIT_OPTION_DEFINE(ENABLE_CHANNEL_MESSAGING "Toggle MessageChannel and MessagePort support" PRIVATE ON) > WEBKIT_OPTION_DEFINE(ENABLE_CONTENT_FILTERING "Toggle content filtering support" PRIVATE OFF) >diff --git a/ChangeLog b/ChangeLog >index 72ab7f574cff32e5b5d6f4890bbcb7ea42361f11..9e310639f8560703e0bb3447f4d8f8b25e386b39 100644 >--- a/ChangeLog >+++ b/ChangeLog >@@ -1,3 +1,16 @@ >+2018-09-14 Patrick Griffis <pgriffis@igalia.com> >+ >+ [GTK][WPE] Implement subprocess sandboxing >+ https://bugs.webkit.org/show_bug.cgi?id=188568 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add ENABLE_BUBBLEWRAP_SANDBOX option for sandboxing. >+ >+ * Source/cmake/FindLibseccomp.cmake: Added. >+ * Source/cmake/OptionsGTK.cmake: >+ * Source/cmake/WebKitFeatures.cmake: >+ > 2018-09-12 Dan Bernstein <mitz@apple.com> > > Removed a couple of empty directories left behind after r235892.
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 188568
:
347098
|
347101
|
347158
|
347367
|
347637
|
347745
|
348657
|
348658
|
348659
|
348781
|
349776
|
349885
|
349888
|
350383
|
350385
|
350637
|
350638
|
350654
|
350655
|
350657
|
350781
|
351231
|
351370
|
352043