Source/WebCore/ChangeLog

 12018-09-03 Patrick Griffis <pgriffis@igalia.com>
 2
 3 [GTK][WPE] Implement subprocess sandboxing
 4 https://bugs.webkit.org/show_bug.cgi?id=188568
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Link against libseccomp.
 9
 10 * PlatformGTK.cmake:
 11
1122018-08-31 Zalan Bujtas <zalan@apple.com>
213
314 [LFC] Add margin box verification back now that Display::Box has non-computed horizontal margin.

Source/WebKit/ChangeLog

 12018-09-03 Patrick Griffis <pgriffis@igalia.com>
 2
 3 [GTK][WPE] Implement subprocess sandboxing
 4 https://bugs.webkit.org/show_bug.cgi?id=188568
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 This implements sandboxing of the WebKitWebProcess, WebKitNetworkProcess,
 9 and WebKitStorageProcess.
 10
 11 The sandbox is opt-in at runtime as it is a behavior change.
 12 See webkit_web_context_set_sandbox_enabled() and the
 13 WEBKIT_SANDBOX_ENABLED env var for developers.
 14
 15 This is Linux specific using Namespaces, Seccomp, and a DBus proxy service.
 16 This introduces three new dependencies:
 17
 18 - bwrap executable
 19 - libseccomp library
 20 - flatpak-dbus-proxy executable
 21
 22 The use of flatpak-dbus-proxy will ideally be replaced once upstream DBus
 23 gains the same filtering abilities which is a work in progress.
 24
 25 Currently the sandbox is not completed and there are a few large holes:
 26
 27 - Pulseaudio: The Pipewire project will ideally solve this.
 28 - DRI device access: No immediate solutions planned.
 29 - Webcam device access: Pipewire+GStreamer will also solve this.
 30 - Webprocess network access: Will require GStreamer changes.
 31 - DConf access: Is being worked on upstream, no ETA.
 32 - X11 access: Wayland solves this.
 33
 34 That is not an exhaustive list but are the noteworthy ones. Filesystem access
 35 is still an evolving list as problems are found as is specific DBus name access.
 36
 37 * UIProcess/API/glib/WebKitWebContext.cpp:
 38 (webkit_web_context_set_sandbox_enabled):
 39 (webkit_web_context_get_sandbox_enabled):
 40 * UIProcess/API/gtk/WebKitWebContext.h:
 41 * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
 42 * UIProcess/API/wpe/WebKitWebContext.h:
 43 * UIProcess/Launcher/ProcessLauncher.h:
 44 * UIProcess/Launcher/glib/ProcessLauncherGLib.cpp:
 45 (WebKit::DBusProxy::setSocket):
 46 (WebKit::DBusProxy::isRunning const):
 47 (WebKit::DBusProxy::proxyPath const):
 48 (WebKit::DBusProxy::path const):
 49 (WebKit::DBusProxy::appendPermissions):
 50 (WebKit::DBusProxy::launch):
 51 (WebKit::DBusProxy::makeProxySocket):
 52 (WebKit::dbusAddressToPath):
 53 (WebKit::bindIfExists):
 54 (WebKit::bindDBusSession):
 55 (WebKit::bindDBusSystem):
 56 (WebKit::bindX11):
 57 (WebKit::bindDconf):
 58 (WebKit::bindWayland):
 59 (WebKit::bindPulse):
 60 (WebKit::bindFonts):
 61 (WebKit::bindGtkData):
 62 (WebKit::bindA11y):
 63 (WebKit::bindPathVar):
 64 (WebKit::bindGStreamerData):
 65 (WebKit::bindOpenGL):
 66 (WebKit::bindV4l):
 67 (WebKit::memfd_create):
 68 (WebKit::setupSeccomp):
 69 (WebKit::ensureDirectoryExists):
 70 (WebKit::getBwrapArgs):
 71 (WebKit::isInsideFlatpak):
 72 (WebKit::getFlatpakArgs):
 73 (WebKit::ProcessLauncher::launchProcess):
 74 (WebKit::ProcessLauncher::terminateProcess):
 75 * UIProcess/Network/NetworkProcessProxy.cpp:
 76 (WebKit::NetworkProcessProxy::getLaunchOptions):
 77 * UIProcess/Storage/StorageProcessProxy.cpp:
 78 (WebKit::StorageProcessProxy::getLaunchOptions):
 79 * UIProcess/WebProcessPool.cpp:
 80 (WebKit::WebProcessPool::setSandboxEnabled):
 81 * UIProcess/WebProcessPool.h:
 82 * UIProcess/WebProcessProxy.cpp:
 83 (WebKit::WebProcessProxy::getLaunchOptions):
 84 * UIProcess/WebsiteData/WebsiteDataStore.cpp:
 85 (WebKit::WebsiteDataStore::resolveDirectoriesIfNecessary):
 86 * UIProcess/WebsiteData/WebsiteDataStore.h:
 87 (WebKit::WebsiteDataStore::resolvedNetworkCacheDirectory const):
 88 (WebKit::WebsiteDataStore::resolvedLocalStorageDirectory const):
 89
1902018-08-31 Antti Koivisto <antti@apple.com>
291
392 Replace OptionSet |= and -= operators with add() and remove() functions

Source/WebCore/PlatformGTK.cmake

@@list(APPEND WebCore_LIBRARIES
106106 ${GLIB_GMODULE_LIBRARIES}
107107 ${GLIB_GOBJECT_LIBRARIES}
108108 ${GLIB_LIBRARIES}
 109 ${LIBSECCOMP_LIBRARIES}
109110 ${LIBSECRET_LIBRARIES}
110111 ${LIBSOUP_LIBRARIES}
111112 ${LIBTASN1_LIBRARIES}

@@list(APPEND WebCore_SYSTEM_INCLUDE_DIRECTORIES
125126 ${ENCHANT_INCLUDE_DIRS}
126127 ${GIO_UNIX_INCLUDE_DIRS}
127128 ${GLIB_INCLUDE_DIRS}
 129 ${LIBSECCOMP_INCLUDE_DIRS}
128130 ${LIBSECRET_INCLUDE_DIRS}
129131 ${LIBSOUP_INCLUDE_DIRS}
130132 ${LIBTASN1_INCLUDE_DIRS}

Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp

@@void webkit_web_context_register_uri_scheme(WebKitWebContext* context, const cha
11471147 context->priv->processPool->registerSchemeForCustomProtocol(String::fromUTF8(scheme));
11481148}
11491149
 1150/**
 1151 * webkit_web_context_set_sandbox_enabled:
 1152 * @context: a #WebKitWebContext
 1153 * @enabled: if %TRUE enable sandboxing
 1154 *
 1155 * Set whether WebKit subprocesses will be sandboxed, limiting access to the system.
 1156 *
 1157 * This method **must be called before any web process has been created**,
 1158 * as early as possible in your application. Calling it later is a fatal error.
 1159 *
 1160 * This is only implemented on Linux and is a no-op otherwise.
 1161 *
 1162 * Since: 2.24
 1163 */
 1164void webkit_web_context_set_sandbox_enabled(WebKitWebContext* context, gboolean enabled)
 1165{
 1166 g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
 1167
 1168 context->priv->processPool->setSandboxEnabled(enabled);
 1169}
 1170
 1171/**
 1172 * webkit_web_context_get_sandbox_enabled:
 1173 * @context: a #WebKitWebContext
 1174 *
 1175 * Get whether sandboxing is currently enabled.
 1176 *
 1177 * Returns: %TRUE if sandboxing is enabled, or %FALSE otherwise.
 1178 *
 1179 * Since: 2.24
 1180 */
 1181gboolean webkit_web_context_get_sandbox_enabled(WebKitWebContext* context)
 1182{
 1183 g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE);
 1184
 1185 return context->priv->processPool->sandboxEnabled();
 1186}
 1187
11501188/**
11511189 * webkit_web_context_get_spell_checking_enabled:
11521190 * @context: a #WebKitWebContext

Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h

@@webkit_web_context_register_uri_scheme (WebKitWebContext
247247 gpointer user_data,
248248 GDestroyNotify user_data_destroy_func);
249249
 250WEBKIT_API void
 251webkit_web_context_set_sandbox_enabled (WebKitWebContext *context,
 252 gboolean enabled);
 253
 254WEBKIT_API gboolean
 255webkit_web_context_get_sandbox_enabled (WebKitWebContext *context);
 256
250257WEBKIT_API gboolean
251258webkit_web_context_get_spell_checking_enabled (WebKitWebContext *context);
252259

Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt

@@webkit_web_context_get_security_manager
5151webkit_web_context_set_additional_plugins_directory
5252webkit_web_context_get_plugins
5353webkit_web_context_get_plugins_finish
 54webkit_web_context_get_sandbox_enabled
 55webkit_web_context_set_sandbox_enabled
5456webkit_web_context_get_spell_checking_enabled
5557webkit_web_context_set_spell_checking_enabled
5658webkit_web_context_get_spell_checking_languages

Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h

@@webkit_web_context_set_process_model (WebKitWebContext
296296WEBKIT_API WebKitProcessModel
297297webkit_web_context_get_process_model (WebKitWebContext *context);
298298
 299WEBKIT_API void
 300webkit_web_context_set_sandbox_enabled (WebKitWebContext *context,
 301 gboolean enabled);
 302
 303WEBKIT_API gboolean
 304webkit_web_context_get_sandbox_enabled (WebKitWebContext *context);
 305
299306WEBKIT_API void
300307webkit_web_context_initialize_notification_permissions
301308 (WebKitWebContext *context,

Source/WebKit/UIProcess/Launcher/ProcessLauncher.h

@@public:
6868 bool shouldMakeProcessLaunchFailForTesting { false };
6969 CString customWebContentServiceBundleIdentifier;
7070
 71#if PLATFORM(GTK) || PLATFORM(WPE)
 72 Vector<String> extraSandboxPaths;
 73 bool sandboxEnabled { false };
 74#endif
 75
7176#if ENABLE(DEVELOPER_MODE) && (PLATFORM(GTK) || PLATFORM(WPE))
7277 String processCmdPrefix;
7378#endif

Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp

4444#include <wpe/wpe.h>
4545#endif
4646
 47#if ENABLE(BUBBLEWRAP_SANDBOX)
 48#include <seccomp.h>
 49#include <sys/ioctl.h>
 50#endif
 51
4752namespace WebKit {
4853using namespace WebCore;
4954

@@static void childSetupFunction(gpointer userData)
5358 close(socket);
5459}
5560
 61#if ENABLE(BUBBLEWRAP_SANDBOX)
 62
 63class DBusProxy {
 64public:
 65 void setSocket(const char* dbusSocket, const char* dbusPath)
 66 {
 67 m_socket = dbusSocket;
 68 m_path = dbusPath;
 69 };
 70
 71 bool isRunning() const { return m_isRunning; };
 72 CString proxyPath() const { return m_proxyPath; };
 73 CString path() const { return m_path; };
 74
 75 void appendPermissions(std::initializer_list<CString> permissions)
 76 {
 77 ASSERT_WITH_SECURITY_IMPLICATION(!m_isRunning);
 78
 79 Vector<CString> newPermissions(permissions);
 80 m_permissions.appendVector(newPermissions);
 81 };
 82
 83 void launch()
 84 {
 85 if (m_isRunning)
 86 return;
 87
 88 GUniquePtr<char> proxySocket(makeProxySocket());
 89 ASSERT_WITH_SECURITY_IMPLICATION(proxySocket.get());
 90
 91 m_proxyPath = proxySocket.get();
 92 const char* runDir = g_get_user_runtime_dir();
 93 GUniquePtr<char> appRunDir(g_build_filename(runDir, g_get_prgname(), nullptr));
 94
 95 Vector<CString> args = {
 96 BWRAP_EXECUTABLE,
 97 "--die-with-parent",
 98 "--unshare-all",
 99 "--ro-bind", DBUS_PROXY_EXECUTABLE, DBUS_PROXY_EXECUTABLE,
 100 "--ro-bind", "/usr/lib", "/usr/lib",
 101 "--ro-bind-try", "/lib", "/lib",
 102 "--ro-bind-try", "/lib64", "/lib64",
 103 "--dev", "/dev",
 104 "--proc", "/proc",
 105 "--dir", "/run",
 106 "--symlink", "../run", "/var/run",
 107 "--symlink", "../tmp", "/var/tmp",
 108 "--tmpfs", "/tmp",
 109 "--unsetenv", "TMPDIR",
 110 "--bind-try", m_path, m_path,
 111 "--bind", appRunDir.get(), appRunDir.get(),
 112
 113 DBUS_PROXY_EXECUTABLE,
 114 m_socket, m_proxyPath,
 115 "--filter",
 116 };
 117
 118 if (!g_strcmp0(g_getenv("WEBKIT_DBUS_LOG"), "1"))
 119 args.append("--log");
 120
 121 args.appendVector(m_permissions);
 122
 123 int nargs = args.size() + 1;
 124 int i = 0;
 125 char** argv = g_newa(char*, nargs);
 126 for (const auto& arg : args)
 127 argv[i++] = const_cast<char*>(arg.data());
 128 argv[i] = nullptr;
 129
 130 GUniqueOutPtr<GError> error;
 131 GRefPtr<GSubprocess> process(g_subprocess_newv(argv, G_SUBPROCESS_FLAGS_NONE, &error.outPtr()));
 132 if (error.get()) {
 133 g_error("Failed to start dbus proxy: %s", error.get()->message);
 134 return;
 135 }
 136
 137 m_isRunning = true;
 138 };
 139
 140private:
 141 static char* makeProxySocket()
 142 {
 143 const char* runDir = g_get_user_runtime_dir();
 144 GUniquePtr<char> appRunDir(g_build_filename(runDir, g_get_prgname(), nullptr));
 145
 146 if (g_mkdir_with_parents(appRunDir.get(), 0700) == -1) {
 147 g_error("Failed to mkdir for dbus proxy (%s): %s", appRunDir.get(), g_strerror(errno));
 148 return nullptr;
 149 }
 150
 151 char* proxySocketTemplate = g_build_filename(appRunDir.get(), "dbus-proxy-XXXXXX", nullptr);
 152 int fd;
 153 if ((fd = g_mkstemp(proxySocketTemplate)) == -1) {
 154 g_free(proxySocketTemplate);
 155 g_error("Failed to make socket file for dbus proxy: %s", g_strerror(errno));
 156 return nullptr;
 157 }
 158
 159 close(fd);
 160 return proxySocketTemplate;
 161 };
 162
 163 CString m_socket;
 164 CString m_path;
 165 CString m_proxyPath;
 166 bool m_isRunning = false;
 167 Vector<CString> m_permissions = { };
 168};
 169
 170// From flatpak-run.c (LGPLv2.1+)
 171static char* dbusAddressToPath(const char* address, bool abstract = false)
 172{
 173 const char* path;
 174 const char* path_end;
 175
 176 if (!address)
 177 return nullptr;
 178
 179 if (!g_str_has_prefix(address, "unix:"))
 180 return nullptr;
 181
 182 path = strstr(address, abstract ? "abstract=" : "path=");
 183 if (!path)
 184 return nullptr;
 185
 186 path += strlen(abstract ? "abstract=" : "path=");
 187 path_end = path;
 188 while (*path_end && *path_end != ',')
 189 path_end++;
 190
 191 return g_strndup(path, path_end - path);
 192} // End LGPLv2.1+
 193
 194enum class BindFlags {
 195 ReadOnly,
 196 ReadWrite,
 197 Device,
 198};
 199
 200static void bindIfExists(Vector<CString>& args, const char* path, BindFlags bindFlags = BindFlags::ReadOnly)
 201{
 202 if (!path)
 203 return;
 204
 205 const char* type;
 206 if (bindFlags == BindFlags::Device)
 207 type = "--dev-bind-try";
 208 else if (bindFlags == BindFlags::ReadOnly)
 209 type = "--ro-bind-try";
 210 else
 211 type = "--bind-try";
 212 args.appendVector(Vector<CString>({ type, path, path }));
 213}
 214
 215static void bindDBusSession(Vector<CString>& args, DBusProxy& proxy)
 216{
 217 if (!proxy.isRunning()) {
 218 const char* sessionAddress = g_getenv("DBUS_SESSION_BUS_ADDRESS");
 219 if (!sessionAddress)
 220 return;
 221
 222 GUniquePtr<char> sessionPath(dbusAddressToPath(sessionAddress));
 223 if (!sessionPath.get())
 224 return;
 225
 226 proxy.setSocket(sessionAddress, sessionPath.get());
 227 proxy.launch();
 228 }
 229
 230 args.appendVector(Vector<CString>({
 231 "--bind", proxy.proxyPath(), proxy.path(),
 232 }));
 233}
 234
 235#if 0
 236static void bindDBusSystem(Vector<CString>& args)
 237{
 238 const char* systemAddress = g_getenv("DBUS_SYSTEM_BUS_ADDRESS");
 239 if (!systemAddress)
 240 systemAddress = "unix:path=/var/run/dbus/system_bus_socket";
 241
 242 GUniquePtr<char> systemPath(dbusAddressToPath(systemAddress));
 243 if (!systemPath.get())
 244 return;
 245
 246 DBusProxy proxy(systemAddress, systemPath.get());
 247 args.appendVector(Vector<CString>({
 248 "--bind", proxy.proxyPath(), systemPath.get(),
 249 }));
 250}
 251#endif
 252
 253static void bindX11(Vector<CString>& args)
 254{
 255 const char* display = g_getenv("DISPLAY");
 256 if (!display || display[0] != ':' || !g_ascii_isdigit(const_cast<char*>(display)[1]))
 257 display = ":0";
 258 GUniquePtr<char> x11File(g_strdup_printf("/tmp/.X11-unix/X%s", display + 1));
 259 bindIfExists(args, x11File.get(), BindFlags::ReadWrite);
 260
 261 const char* xauth = g_getenv("XAUTHORITY");
 262 if (!xauth) {
 263 const char* homeDir = g_get_home_dir();
 264 GUniquePtr<char> xauthFile(g_build_filename(homeDir, ".Xauthority", nullptr));
 265 bindIfExists(args, xauthFile.get());
 266 } else
 267 bindIfExists(args, xauth);
 268}
 269
 270static void bindDconf(Vector<CString>& args)
 271{
 272 const char* runtimeDir = g_get_user_runtime_dir();
 273 GUniquePtr<char> dconfRuntimeDir(g_build_filename(runtimeDir, "dconf", nullptr));
 274 args.appendVector(Vector<CString>({ "--bind", dconfRuntimeDir.get(), dconfRuntimeDir.get() }));
 275
 276 const char* dconfDir = g_getenv("DCONF_USER_CONFIG_DIR");
 277 if (dconfDir)
 278 bindIfExists(args, dconfDir);
 279 else {
 280 const char* configDir = g_get_user_config_dir();
 281 GUniquePtr<char> dconfConfigDir(g_build_filename(configDir, "dconf", nullptr));
 282 bindIfExists(args, dconfConfigDir.get(), BindFlags::ReadWrite);
 283 }
 284}
 285
 286static void bindWayland(Vector<CString>& args)
 287{
 288 const char* display = g_getenv("WAYLAND_DISPLAY");
 289 if (!display)
 290 display = "wayland-0";
 291
 292 const char* runtimeDir = g_get_user_runtime_dir();
 293 GUniquePtr<char> waylandRuntimeFile(g_build_filename(runtimeDir, display, nullptr));
 294 bindIfExists(args, waylandRuntimeFile.get(), BindFlags::ReadWrite);
 295}
 296
 297static void bindPulse(Vector<CString>& args)
 298{
 299 // FIXME: The server can be defined in config files we'd have to parse.
 300 // They can also be set as X11 props but that is getting a bit ridiculous.
 301 const char* pulseServer = g_getenv("PULSE_SERVER");
 302 if (pulseServer) {
 303 if (g_str_has_prefix(pulseServer, "unix:"))
 304 bindIfExists(args, pulseServer + 5, BindFlags::ReadWrite);
 305 // else it uses tcp
 306 } else {
 307 const char* runtimeDir = g_get_user_runtime_dir();
 308 GUniquePtr<char> pulseRuntimeDir(g_build_filename(runtimeDir, "pulse", nullptr));
 309 bindIfExists(args, pulseRuntimeDir.get(), BindFlags::ReadWrite);
 310 }
 311
 312 const char* pulseConfig = g_getenv("PULSE_CLIENTCONFIG");
 313 if (pulseConfig)
 314 bindIfExists(args, pulseConfig);
 315
 316 const char* configDir = g_get_user_config_dir();
 317 GUniquePtr<char> pulseConfigDir(g_build_filename(configDir, "pulse", nullptr));
 318 bindIfExists(args, pulseConfigDir.get());
 319
 320 const char* homeDir = g_get_home_dir();
 321 GUniquePtr<char> pulseHomeConfigDir(g_build_filename(homeDir, ".pulse", nullptr));
 322 GUniquePtr<char> asoundHomeConfigDir(g_build_filename(homeDir, ".asoundrc", nullptr));
 323 bindIfExists(args, pulseHomeConfigDir.get());
 324 bindIfExists(args, asoundHomeConfigDir.get());
 325
 326 // This is the ultimate fallback to raw ALSA
 327 bindIfExists(args, "/dev/snd", BindFlags::Device);
 328}
 329
 330static void bindFonts(Vector<CString>& args)
 331{
 332 const char* configDir = g_get_user_config_dir();
 333 const char* homeDir = g_get_home_dir();
 334 const char* dataDir = g_get_user_data_dir();
 335 const char* cacheDir = g_get_user_cache_dir();
 336
 337 // Configs can include custom dirs but then we have to parse them...
 338 GUniquePtr<char> fontConfig(g_build_filename(configDir, "fontconfig", nullptr));
 339 GUniquePtr<char> fontCache(g_build_filename(cacheDir, "fontconfig", nullptr));
 340 GUniquePtr<char> fontHomeConfig(g_build_filename(homeDir, ".fonts.conf", nullptr));
 341 GUniquePtr<char> fontHomeConfigDir(g_build_filename(configDir, ".fonts.conf.d", nullptr));
 342 GUniquePtr<char> fontData(g_build_filename(dataDir, "fonts", nullptr));
 343 GUniquePtr<char> fontHomeData(g_build_filename(homeDir, ".fonts", nullptr));
 344 bindIfExists(args, fontConfig.get());
 345 bindIfExists(args, fontCache.get(), BindFlags::ReadWrite);
 346 bindIfExists(args, fontHomeConfig.get());
 347 bindIfExists(args, fontHomeConfigDir.get());
 348 bindIfExists(args, fontData.get());
 349 bindIfExists(args, fontHomeData.get());
 350}
 351
 352#if PLATFORM(GTK)
 353static void bindGtkData(Vector<CString>& args)
 354{
 355 const char* configDir = g_get_user_config_dir();
 356 const char* dataDir = g_get_user_data_dir();
 357 const char* homeDir = g_get_home_dir();
 358
 359 GUniquePtr<char> gtkConfig(g_build_filename(configDir, "gtk-3.0", nullptr));
 360 GUniquePtr<char> themeData(g_build_filename(dataDir, "themes", nullptr));
 361 GUniquePtr<char> themeHomeData(g_build_filename(homeDir, ".themes", nullptr));
 362 GUniquePtr<char> iconHomeData(g_build_filename(homeDir, ".icons", nullptr));
 363 bindIfExists(args, gtkConfig.get());
 364 bindIfExists(args, themeData.get());
 365 bindIfExists(args, themeHomeData.get());
 366 bindIfExists(args, iconHomeData.get());
 367}
 368
 369static void bindA11y(Vector<CString>& args)
 370{
 371 static NeverDestroyed<DBusProxy> staticProxy;
 372 DBusProxy proxy = staticProxy.get();
 373
 374 if (!proxy.isRunning()) {
 375 // FIXME: Avoid blocking IO... (It is at least a one-time cost)
 376 GRefPtr<GDBusConnection> sessionBus(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr));
 377 if (!sessionBus.get())
 378 return;
 379
 380 GRefPtr<GDBusMessage> msg(g_dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus", "GetAddress"));
 381 g_dbus_message_set_body(msg.get(), g_variant_new("()"));
 382 GRefPtr<GDBusMessage> reply(g_dbus_connection_send_message_with_reply_sync(
 383 sessionBus.get(), msg.get(),
 384 G_DBUS_SEND_MESSAGE_FLAGS_NONE,
 385 30000,
 386 nullptr,
 387 nullptr,
 388 nullptr));
 389
 390 GUniqueOutPtr<GError> error;
 391 GUniqueOutPtr<char> a11yAddress;
 392 if (reply.get()) {
 393 if (g_dbus_message_to_gerror(reply.get(), &error.outPtr())) {
 394 if (!g_error_matches(error.get(), G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
 395 g_warning("Can't find a11y bus: %s", error.get()->message);
 396 } else
 397 g_variant_get(g_dbus_message_get_body(reply.get()), "(s)", &a11yAddress.outPtr());
 398 }
 399
 400 if (!a11yAddress.get())
 401 return;
 402
 403 GUniquePtr<char> a11yPath(dbusAddressToPath(a11yAddress.get(), true));
 404 if (!a11yAddress.get())
 405 return;
 406
 407 proxy.setSocket(a11yAddress.get(), a11yPath.get());
 408 proxy.appendPermissions({
 409 "--sloppy-names",
 410 "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Embed@/org/a11y/atspi/accessible/root",
 411 "--call=org.a11y.atspi.Registry=org.a11y.atspi.Socket.Unembed@/org/a11y/atspi/accessible/root",
 412 "--call=org.a11y.atspi.Registry=org.a11y.atspi.Registry.GetRegisteredEvents@/org/a11y/atspi/registry",
 413 "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetKeystrokeListeners@/org/a11y/atspi/registry/deviceeventcontroller",
 414 "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.GetDeviceEventListeners@/org/a11y/atspi/registry/deviceeventcontroller",
 415 "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersSync@/org/a11y/atspi/registry/deviceeventcontroller",
 416 "--call=org.a11y.atspi.Registry=org.a11y.atspi.DeviceEventController.NotifyListenersAsync@/org/a11y/atspi/registry/deviceeventcontroller",
 417 });
 418 proxy.launch();
 419 }
 420
 421 args.appendVector(Vector<CString>({
 422 "--bind", proxy.proxyPath(), proxy.path(),
 423 }));
 424}
 425#endif
 426
 427static bool bindPathVar(Vector<CString>& args, const char* varname)
 428{
 429 const char* pathValue = g_getenv(varname);
 430 if (!pathValue)
 431 return false;
 432
 433 GUniquePtr<char*> splitPaths(g_strsplit(pathValue, ":", -1));
 434 for (size_t i; splitPaths.get()[i]; ++i)
 435 bindIfExists(args, splitPaths.get()[i]);
 436
 437 return true;
 438}
 439
 440static void bindGStreamerData(Vector<CString>& args)
 441{
 442 if (!bindPathVar(args, "GST_PLUGIN_PATH_1_0"))
 443 bindPathVar(args, "GST_PLUGIN_PATH");
 444
 445 if (!bindPathVar(args, "GST_PLUGIN_SYSTEM_PATH_1_0")) {
 446 if (!bindPathVar(args, "GST_PLUGIN_SYSTEM_PATH")) {
 447 GUniquePtr<char> gstData(g_build_filename(g_get_user_data_dir(), "gstreamer-1.0", nullptr));
 448 bindIfExists(args, gstData.get());
 449 }
 450 }
 451
 452 GUniquePtr<char> gstCache(g_build_filename(g_get_user_cache_dir(), "gstreamer-1.0", nullptr));
 453 bindIfExists(args, gstCache.get(), BindFlags::ReadWrite);
 454
 455 // /usr/lib is already added so this is only requried for other dirs
 456 const char* scannerPath = g_getenv("GST_PLUGIN_SCANNER") ?: "/usr/libexec/gstreamer-1.0/gst-plugin-scanner";
 457 const char* helperPath = g_getenv("GST_INSTALL_PLUGINS_HELPER ") ?: "/usr/libexec/gst-install-plugins-helper";
 458
 459 bindIfExists(args, scannerPath);
 460 bindIfExists(args, helperPath);
 461}
 462
 463static void bindOpenGL(Vector<CString>& args)
 464{
 465 args.appendVector(Vector<CString>({
 466 "--dev-bind-try", "/dev/dri", "/dev/dri",
 467 // Mali
 468 "--dev-bind-try", "/dev/mali", "/dev/mali",
 469 "--dev-bind-try", "/dev/mali0", "/dev/mali0",
 470 "--dev-bind-try", "/dev/umplock", "/dev/umplock",
 471 // Nvidia
 472 "--dev-bind-try", "/dev/nvidiactl", "/dev/nvidiactl",
 473 "--dev-bind-try", "/dev/nvidia0", "/dev/nvidia0",
 474 "--dev-bind-try", "/dev/nvidia", "/dev/nvidia",
 475 // Adreno
 476 "--dev-bind-try", "/dev/kgsl-3d0", "/dev/kgsl-3d0",
 477 "--dev-bind-try", "/dev/ion", "/dev/ion",
 478#if PLATFORM(WPE)
 479 "--dev-bind-try", "/dev/fb0", "/dev/fb0",
 480 "--dev-bind-try", "/dev/fb1", "/dev/fb1",
 481#endif
 482 }));
 483}
 484
 485static void bindV4l(Vector<CString>& args)
 486{
 487 args.appendVector(Vector<CString>({
 488 "--dev-bind-try", "/dev/v4l", "/dev/v4l",
 489 // Not pretty but a stop-gap for pipewire anyway
 490 "--dev-bind-try", "/dev/video0", "/dev/video0",
 491 "--dev-bind-try", "/dev/video1", "/dev/video1",
 492 }));
 493}
 494
 495static int memfd_create(const char* name, unsigned flags)
 496{
 497 return syscall(__NR_memfd_create, name, flags);
 498}
 499
 500static int setupSeccomp()
 501{
 502 // NOTE: This is shared code (flatpak-run.c - LGPLv2.1+)
 503 // There are today a number of different Linux container
 504 // implementations. That will likely continue for long into the
 505 // future. But we can still try to share code, and it's important
 506 // to do so because it affects what library and application writers
 507 // can do, and we should support code portability between different
 508 // container tools.
 509 //
 510 // This syscall blacklist is copied from linux-user-chroot, which was in turn
 511 // clearly influenced by the Sandstorm.io blacklist.
 512 //
 513 // If you make any changes here, I suggest sending the changes along
 514 // to other sandbox maintainers. Using the libseccomp list is also
 515 // an appropriate venue:
 516 // https://groups.google.com/forum/#!topic/libseccomp
 517 //
 518 // A non-exhaustive list of links to container tooling that might
 519 // want to share this blacklist:
 520 //
 521 // https://github.com/sandstorm-io/sandstorm
 522 // in src/sandstorm/supervisor.c++
 523 // http://cgit.freedesktop.org/xdg-app/xdg-app/
 524 // in common/flatpak-run.c
 525 // https://git.gnome.org/browse/linux-user-chroot
 526 // in src/setup-seccomp.c
 527 struct scmp_arg_cmp clone_arg = SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER);
 528 struct scmp_arg_cmp tty_arg = SCMP_A1(SCMP_CMP_EQ, (int)TIOCSTI);
 529 struct {
 530 int scall;
 531 struct scmp_arg_cmp* arg;
 532 } syscall_blacklist[] = {
 533 // Block dmesg
 534 { SCMP_SYS(syslog), nullptr },
 535 // Useless old syscall
 536 { SCMP_SYS(uselib), nullptr },
 537 // Don't allow disabling accounting
 538 { SCMP_SYS(acct), nullptr },
 539 // 16-bit code is unnecessary in the sandbox, and modify_ldt is a
 540 // historic source of interesting information leaks.
 541 { SCMP_SYS(modify_ldt), nullptr },
 542 // Don't allow reading current quota use
 543 { SCMP_SYS(quotactl), nullptr },
 544
 545 // Don't allow access to the kernel keyring
 546 { SCMP_SYS(add_key), nullptr },
 547 { SCMP_SYS(keyctl), nullptr },
 548 { SCMP_SYS(request_key), nullptr },
 549
 550 // Scary VM/NUMA ops
 551 { SCMP_SYS(move_pages), nullptr },
 552 { SCMP_SYS(mbind), nullptr },
 553 { SCMP_SYS(get_mempolicy), nullptr },
 554 { SCMP_SYS(set_mempolicy), nullptr },
 555 { SCMP_SYS(migrate_pages), nullptr },
 556
 557 // Don't allow subnamespace setups:
 558 { SCMP_SYS(unshare), nullptr },
 559 { SCMP_SYS(mount), nullptr },
 560 { SCMP_SYS(pivot_root), nullptr },
 561 { SCMP_SYS(clone), &clone_arg },
 562
 563 // Don't allow faking input to the controlling tty (CVE-2017-5226)
 564 { SCMP_SYS(ioctl), &tty_arg },
 565
 566 // Profiling operations; we expect these to be done by tools from outside
 567 // the sandbox. In particular perf has been the source of many CVEs.
 568 { SCMP_SYS(perf_event_open), nullptr },
 569 // Don't allow you to switch to bsd emulation or whatnot
 570 { SCMP_SYS(personality), nullptr },
 571 { SCMP_SYS(ptrace), nullptr }
 572 };
 573
 574 scmp_filter_ctx seccomp = seccomp_init(SCMP_ACT_ALLOW);
 575 if (!seccomp)
 576 return -1;
 577
 578 for (auto& rule : syscall_blacklist) {
 579 int scall = rule.scall;
 580 int r;
 581 if (rule.arg)
 582 r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), scall, 1, rule.arg);
 583 else
 584 r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), scall, 0);
 585 if (r == -EFAULT) {
 586 g_warning("Failed to add seccomp rule");
 587 seccomp_release(seccomp);
 588 return -1;
 589 }
 590 }
 591
 592 int tmpfd = memfd_create("seccomp-bpf", 0);
 593 if (tmpfd == -1) {
 594 g_warning("Failed to create memfd: %s", g_strerror(errno));
 595 seccomp_release(seccomp);
 596 return -1;
 597 }
 598
 599 if (seccomp_export_bpf(seccomp, tmpfd)) {
 600 g_warning("Failed to export seccomp bpf");
 601 seccomp_release(seccomp);
 602 close(tmpfd);
 603 return -1;
 604 }
 605
 606 lseek(tmpfd, 0, SEEK_SET);
 607 seccomp_release(seccomp);
 608 return tmpfd;
 609}
 610
 611static void ensureDirectoryExists(const String& path)
 612{
 613 if (path.isEmpty())
 614 return;
 615
 616 // FIXME: Blocking
 617 if (g_mkdir_with_parents(path.utf8().data(), 0700) == -1)
 618 g_warning("Could not create directory \"%s\": %s", path.utf8().data(), g_strerror(errno));
 619}
 620
 621static std::tuple<Vector<CString>, int> getBwrapArgs(ProcessLauncher::ProcessType processType, const Vector<String>& extraPaths)
 622{
 623 // It is impossible to know what access arbitrary plugins need and since it is for legacy
 624 // reasons lets just leave it unsandboxed.
 625 if (processType == ProcessLauncher::ProcessType::Plugin64
 626 || processType == ProcessLauncher::ProcessType::Plugin32)
 627 return {{ }, -1};
 628
 629 // NOTE: This is not a great solution but we just assume that applications create this directory
 630 // ahead of time if they require it
 631 GUniquePtr<char> configDir(g_build_filename(g_get_user_config_dir(), g_get_prgname(), nullptr));
 632
 633 Vector<CString> sandboxArgs = {
 634 BWRAP_EXECUTABLE,
 635 "--die-with-parent",
 636 "--unshare-pid",
 637 "--unshare-uts",
 638
 639 // We assume /etc has safe permissions
 640 // At a later point we can start masking privacy-concerning files
 641 "--ro-bind", "/etc", "/etc",
 642 "--dev", "/dev",
 643 "--proc", "/proc",
 644 "--tmpfs", "/tmp",
 645 "--unsetenv", "TMPDIR",
 646 "--dir", "/run",
 647 "--symlink", "../run", "/var/run",
 648 "--symlink", "../tmp", "/var/tmp",
 649 "--ro-bind", "/sys/block", "/sys/block",
 650 "--ro-bind", "/sys/bus", "/sys/bus",
 651 "--ro-bind", "/sys/class", "/sys/class",
 652 "--ro-bind", "/sys/dev", "/sys/dev",
 653 "--ro-bind", "/sys/devices", "/sys/devices",
 654
 655 "--ro-bind-try", "/usr/share", "/usr/share",
 656 "--ro-bind-try", "/usr/local/share", "/usr/local/share",
 657
 658 // We only grant access to the libdirs webkit is built with and
 659 // guess system libdirs. This will always have some edge cases
 660 "--ro-bind-try", "/lib", "/lib",
 661 "--ro-bind-try", "/usr/lib", "/usr/lib",
 662 "--ro-bind-try", "/usr/local/lib", "/usr/local/lib",
 663 "--ro-bind-try", LIBDIR, LIBDIR,
 664 "--ro-bind-try", "/lib64", "/lib64",
 665 "--ro-bind-try", "/usr/lib64", "/usr/lib64",
 666 "--ro-bind-try", "/usr/local/lib64", "/usr/local/lib64",
 667 "--ro-bind-try", PKGLIBEXECDIR, PKGLIBEXECDIR,
 668
 669 "--bind-try", configDir.get(), configDir.get(),
 670 };
 671 // We would have to parse ld config files for more info
 672 bindPathVar(sandboxArgs, "LD_LIBRARY_PATH");
 673
 674 if (processType == ProcessLauncher::ProcessType::Network) {
 675 static NeverDestroyed<DBusProxy> staticProxy;
 676 DBusProxy proxy = staticProxy.get();
 677
 678 // FIXME: The network process is used for `file://` URIs and
 679 // we would have to pass through most paths for this to work
 680
 681 // FIXME: pkcs11? (~/.config/pkcs11)
 682
 683 // glib-networking can use dbus for proxy information
 684 // FIXME: Find and add the permissions it needs for this (pacrunner?)
 685 bindDBusSession(sandboxArgs, proxy);
 686 }
 687
 688 // NOTE: This has network access for HLS via GStreamer
 689 if (processType == ProcessLauncher::ProcessType::Web) {
 690 static NeverDestroyed<DBusProxy> staticProxy;
 691 DBusProxy proxy = staticProxy.get();
 692
 693 if (!proxy.isRunning()) {
 694 proxy.appendPermissions({
 695 "--talk=ca.desrt.dconf", // Used by GTK on Wayland
 696 "--talk=org.freedesktop.ScreenSaver", // GStreamer inhibits, FIXME: Use xdg-desktop-portal
 697 "--talk=org.freedesktop.portal.Desktop",
 698 // GStreamers plugin install helper
 699 "--call=org.freedesktop.PackageKit=org.freedesktop.PackageKit.Modify2.InstallGStreamerResources@/org/freedesktop/PackageKit"
 700 // "--talk=org.freedesktop.secrets" // FIXME: WebKitGTK uses for HTTP Auth
 701 });
 702 }
 703
 704 bindX11(sandboxArgs);
 705 bindWayland(sandboxArgs);
 706 bindDBusSession(sandboxArgs, proxy);
 707
 708 // FIXME: This needs to be restricted, upstream is working on it
 709 bindDconf(sandboxArgs);
 710 // FIXME: We should move to Pipewire as soon as viable, Pulse
 711 // doesn't restrict clients atm.
 712 bindPulse(sandboxArgs);
 713 bindFonts(sandboxArgs);
 714 bindGStreamerData(sandboxArgs);
 715 bindOpenGL(sandboxArgs);
 716 bindV4l(sandboxArgs);
 717#if PLATFORM(GTK)
 718 bindA11y(sandboxArgs);
 719 bindGtkData(sandboxArgs);
 720#endif
 721 } else {
 722 // Only X11 users need this for XShm which is only the Web process
 723 sandboxArgs.append("--unshare-ipc");
 724 }
 725
 726 // Only process without any network access for now
 727 if (processType == ProcessLauncher::ProcessType::Storage)
 728 sandboxArgs.append("--unshare-net");
 729
 730#if ENABLE(DEVELOPER_MODE)
 731 const char* execDirectory = g_getenv("WEBKIT_EXEC_PATH");
 732 if (execDirectory) {
 733 String parentDir = FileSystem::directoryName(FileSystem::stringFromFileSystemRepresentation(execDirectory));
 734 bindIfExists(sandboxArgs, parentDir.utf8().data());
 735 }
 736
 737 CString executablePath = getCurrentExecutablePath();
 738 if (!executablePath.isNull()) {
 739 // Our executable is `/foo/bar/bin/Process`, we want `/foo/bar` as a usable prefix
 740 String parentDir = FileSystem::directoryName(FileSystem::directoryName(FileSystem::stringFromFileSystemRepresentation(executablePath.data())));
 741 bindIfExists(sandboxArgs, parentDir.utf8().data());
 742 }
 743#endif
 744
 745 int seccompFd = setupSeccomp();
 746 if (seccompFd != -1) {
 747 GUniquePtr<char> fdStr(g_strdup_printf("%d", seccompFd));
 748 sandboxArgs.appendVector(Vector<CString>({ "--seccomp", fdStr.get() }));
 749 }
 750
 751 for (const String& path : extraPaths) {
 752 ensureDirectoryExists(path);
 753 if (path.isEmpty())
 754 g_warning("Empty path passed to sandbox, this is probably a bug");
 755 else {
 756 const char* cpath = path.utf8().data();
 757 sandboxArgs.appendVector(Vector<CString>({ "--bind", cpath, cpath }));
 758 }
 759 }
 760
 761 return {sandboxArgs, seccompFd};
 762}
 763
 764#endif
 765
 766#if OS(LINUX)
 767static bool isInsideFlatpak()
 768{
 769 static int ret = -1;
 770 if (ret != -1)
 771 return ret;
 772
 773 GUniquePtr<GKeyFile> infoFile(g_key_file_new());
 774 if (!g_key_file_load_from_file(infoFile.get(), "/.flatpak-info", G_KEY_FILE_NONE, nullptr)) {
 775 ret = false;
 776 return ret;
 777 }
 778
 779 // If we are in a `flatpak build` session we cannot launch ourselves since we aren't installed
 780 ret = !g_key_file_get_boolean(infoFile.get(), "Instance", "build", nullptr);
 781 return ret;
 782}
 783
 784static Vector<CString> getFlatpakArgs(ProcessLauncher::ProcessType processType)
 785{
 786 // When we are running inside of flatpak's sandbox we do not have permissions to
 787 // use the same sandbox we do outside but flatpak offers to create new sandboxes
 788 // for us using flatpak-spawn.
 789 //
 790 // This is extremely bare-bones atm though as the Spawn interface does not expose
 791 // much outside of `--sandbox` (no permissions) and `--no-network`. We need to
 792 // add some permissions in between those for this to provide meaningful security.
 793 Vector<CString> sandboxArgs = {
 794 "/usr/bin/flatpak-spawn",
 795 };
 796
 797 if (processType == ProcessLauncher::ProcessType::Storage)
 798 sandboxArgs.append("--no-network");
 799
 800 return sandboxArgs;
 801}
 802#endif
 803
56804void ProcessLauncher::launchProcess()
57805{
58806 GPid pid = 0;

@@void ProcessLauncher::launchProcess()
120868 }
121869#endif
122870
 871#if OS(LINUX)
 872 const char* sandboxEnv = g_getenv("WEBKIT_SANDBOX_ENABLED");
 873 bool sandboxEnabled = m_launchOptions.sandboxEnabled;
 874 int seccompFd = -1;
 875
 876 if (sandboxEnv)
 877 sandboxEnabled = !strcmp(sandboxEnv, "1");
 878
 879 Vector<CString> sandboxArgs = { };
 880 if (sandboxEnabled) {
 881 if (isInsideFlatpak())
 882 sandboxArgs = getFlatpakArgs(m_launchOptions.processType);
 883#if ENABLE(BUBBLEWRAP_SANDBOX)
 884 else {
 885 auto ret = getBwrapArgs(m_launchOptions.processType, m_launchOptions.extraSandboxPaths);
 886 sandboxArgs = std::get<0>(ret);
 887 seccompFd = std::get<1>(ret);
 888 }
 889#endif
 890 }
 891 nargs += sandboxArgs.size();
 892#endif
 893
123894 char** argv = g_newa(char*, nargs);
124895 unsigned i = 0;
125896#if ENABLE(DEVELOPER_MODE)

@@void ProcessLauncher::launchProcess()
127898 for (auto& arg : prefixArgs)
128899 argv[i++] = const_cast<char*>(arg.data());
129900#endif
 901
 902#if OS(LINUX)
 903 for (auto& arg : sandboxArgs)
 904 argv[i++] = const_cast<char*>(arg.data());
 905#endif
 906
130907 argv[i++] = const_cast<char*>(realExecutablePath.data());
131908 argv[i++] = processIdentifier.get();
132909 argv[i++] = webkitSocket.get();

@@void ProcessLauncher::launchProcess()
151928 if (!setCloseOnExec(socketPair.client))
152929 RELEASE_ASSERT_NOT_REACHED();
153930
 931#if OS(LINUX)
 932 if (seccompFd != -1)
 933 close(seccompFd);
 934#endif
 935
154936 close(socketPair.client);
155937 m_processIdentifier = pid;
156938

@@void ProcessLauncher::launchProcess()
162944
163945void ProcessLauncher::terminateProcess()
164946{
 947
 948
165949 if (m_isLaunching) {
166950 invalidate();
167951 return;

Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp

@@void NetworkProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launc
106106 processPool().setShouldMakeNextNetworkProcessLaunchFailForTesting(false);
107107 launchOptions.shouldMakeProcessLaunchFailForTesting = true;
108108 }
 109
 110#if PLATFORM(GTK) || PLATFORM(WPE)
 111 WebsiteDataStore& store = processPool().websiteDataStore()->websiteDataStore();
 112 store.resolveDirectoriesIfNecessary();
 113 launchOptions.extraSandboxPaths.append(store.resolvedNetworkCacheDirectory());
 114
 115 launchOptions.sandboxEnabled = m_processPool.sandboxEnabled();
 116#endif
109117}
110118
111119void NetworkProcessProxy::connectionWillOpen(IPC::Connection& connection)

Source/WebKit/UIProcess/Storage/StorageProcessProxy.cpp

@@void StorageProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launc
8686{
8787 launchOptions.processType = ProcessLauncher::ProcessType::Storage;
8888 ChildProcessProxy::getLaunchOptions(launchOptions);
 89
 90#if PLATFORM(GTK) || PLATFORM(WPE)
 91 launchOptions.sandboxEnabled = m_processPool.sandboxEnabled();
 92
 93 WebsiteDataStore& store = m_processPool.websiteDataStore()->websiteDataStore();
 94 store.resolveDirectoriesIfNecessary();
 95 launchOptions.extraSandboxPaths.append(store.resolvedIndexedDatabaseDirectory());
 96#endif
8997}
9098
9199void StorageProcessProxy::processWillShutDown(IPC::Connection& connection)

Source/WebKit/UIProcess/WebProcessPool.cpp

@@void WebProcessPool::sendDisplayConfigurationChangedMessageForTesting()
22972297#endif
22982298}
22992299
 2300#if PLATFORM(GTK) || PLATFORM(WPE)
 2301void WebProcessPool::setSandboxEnabled(bool enabled)
 2302{
 2303 if (m_processes.size())
 2304 g_error("Sandboxing cannot be changed after subprocesses were spawned.");
 2305
 2306 m_sandboxEnabled = enabled;
 2307};
 2308#endif
 2309
23002310} // namespace WebKit

Source/WebKit/UIProcess/WebProcessPool.h

@@public:
464464
465465 void sendDisplayConfigurationChangedMessageForTesting();
466466
 467#if PLATFORM(GTK) || PLATFORM(WPE)
 468 void setSandboxEnabled(bool enabled);
 469 bool sandboxEnabled() const { return m_sandboxEnabled; };
 470#endif
 471
467472private:
468473 void platformInitialize();
469474

@@private:
708713
709714 HashMap<WebCore::SecurityOriginData, Vector<SuspendedPageProxy*>> m_suspendedPages;
710715 HashMap<String, RefPtr<WebProcessProxy>> m_swappedProcessesPerRegistrableDomain;
 716
 717#if PLATFORM(GTK) || PLATFORM(WPE)
 718 bool m_sandboxEnabled { false };
 719#endif
711720};
712721
713722template<typename T>

Source/WebKit/UIProcess/WebProcessProxy.cpp

@@void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOpt
195195 processPool().setShouldMakeNextWebProcessLaunchFailForTesting(false);
196196 launchOptions.shouldMakeProcessLaunchFailForTesting = true;
197197 }
 198
 199#if PLATFORM(GTK) || PLATFORM(WPE)
 200 launchOptions.sandboxEnabled = m_processPool->sandboxEnabled();
 201
 202 websiteDataStore().resolveDirectoriesIfNecessary();
 203 launchOptions.extraSandboxPaths.append(websiteDataStore().resolvedNetworkCacheDirectory());
 204 launchOptions.extraSandboxPaths.append(websiteDataStore().resolvedApplicationCacheDirectory());
 205#endif
198206}
199207
200208void WebProcessProxy::connectionWillOpen(IPC::Connection& connection)

Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp

@@void WebsiteDataStore::resolveDirectoriesIfNecessary()
178178 m_resolvedConfiguration.javaScriptConfigurationDirectory = resolvePathForSandboxExtension(m_configuration.javaScriptConfigurationDirectory);
179179 if (!m_configuration.cacheStorageDirectory.isEmpty() && m_resolvedConfiguration.cacheStorageDirectory.isEmpty())
180180 m_resolvedConfiguration.cacheStorageDirectory = resolvePathForSandboxExtension(m_configuration.cacheStorageDirectory);
 181 if (!m_configuration.networkCacheDirectory.isEmpty())
 182 m_resolvedConfiguration.networkCacheDirectory = resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration.networkCacheDirectory);
 183 if (!m_configuration.localStorageDirectory.isEmpty())
 184 m_resolvedConfiguration.localStorageDirectory = resolveAndCreateReadWriteDirectoryForSandboxExtension(m_configuration.localStorageDirectory);
181185
182186 // Resolve directories for file paths.
183187 if (!m_configuration.cookieStorageFile.isEmpty()) {

Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h

@@public:
154154 const String& resolvedIndexedDatabaseDirectory() const { return m_resolvedConfiguration.indexedDBDatabaseDirectory; }
155155 const String& resolvedServiceWorkerRegistrationDirectory() const { return m_resolvedConfiguration.serviceWorkerRegistrationDirectory; }
156156 const String& resolvedResourceLoadStatisticsDirectory() const { return m_resolvedConfiguration.resourceLoadStatisticsDirectory; }
 157 const String& resolvedNetworkCacheDirectory() const { return m_resolvedConfiguration.networkCacheDirectory; }
 158 const String& resolvedLocalStorageDirectory() const { return m_resolvedConfiguration.localStorageDirectory; }
 159
157160
158161 StorageManager* storageManager() { return m_storageManager.get(); }
159162

Source/cmake/FindLibseccomp.cmake

 1# - Try to find libseccomp
 2# Once done, this will define
 3#
 4# LIBSECCOMP_FOUND - system has libseccomp
 5# LIBSECCOMP_INCLUDE_DIRS - the libseccomp include drectories
 6# LIBSECCOMP_LIBRARIES - link these to use libseccomp
 7#
 8# Copyright (C) 2018 Igalia S.L.
 9#
 10# Redistribution and use in source and binary forms, with or without
 11# modification, are permitted provided that the following conditions
 12# are met:
 13# 1. Redistributions of source code must retain the above copyright
 14# notice, this list of conditions and the following disclaimer.
 15# 2. Redistributions in binary form must reproduce the above copyright
 16# notice, this list of conditions and the following disclaimer in the
 17# documentation and/or other materials provided with the distribution.
 18#
 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
 20# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 21# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 22# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
 23# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 24# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 25# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 26# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 27# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 28# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 29# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30
 31find_package(PkgConfig)
 32pkg_check_modules(PC_LIBSECCOMP libseccomp)
 33
 34find_path(LIBSECCOMP_INCLUDE_DIRS
 35 NAMES seccomp.h
 36 HINTS ${PC_LIBSECCOMP_INCLUDEDIR}
 37)
 38
 39find_library(LIBSECCOMP_LIBRARIES
 40 NAMES seccomp
 41 HINTS ${PC_LIBSECCOMP_LIBDIR}
 42)
 43
 44include(FindPackageHandleStandardArgs)
 45find_package_handle_standard_args(LIBSECCOMP
 46 REQUIRED_VARS LIBSECCOMP_LIBRARIES
 47 FOUND_VAR LIBSECCOMP_FOUND
 48 VERSION_VAR PC_LIBSECCOMP_VERSION)
 49
 50mark_as_advanced(
 51 LIBSECCOMP_INCLUDE_DIRS
 52 LIBSECCOMP_LIBRARIES
 53)

Source/cmake/OptionsGTK.cmake

@@else ()
118118 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_RESOURCE_USAGE PRIVATE OFF)
119119endif ()
120120
 121if (CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT EXISTS "/.flatpak-info")
 122 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_BUBBLEWRAP_SANDBOX PUBLIC ON)
 123else ()
 124 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_BUBBLEWRAP_SANDBOX PRIVATE OFF)
 125endif ()
 126
121127# Public options shared with other WebKit ports. Do not add any options here
122128# without approval from a GTK+ reviewer. There must be strong reason to support
123129# changing the value of the option.

@@if (ENABLE_ACCELERATED_2D_CANVAS)
201207 endif ()
202208endif ()
203209
 210if (ENABLE_BUBBLEWRAP_SANDBOX)
 211 find_program(BWRAP_EXECUTABLE bwrap)
 212 if (NOT BWRAP_EXECUTABLE)
 213 message(FATAL_ERROR "bwrap executable is needed for ENABLE_BUBBLEWRAP_SANDBOX")
 214 endif ()
 215 add_definitions(-DBWRAP_EXECUTABLE="${BWRAP_EXECUTABLE}")
 216
 217 execute_process(
 218 COMMAND "${BWRAP_EXECUTABLE}" --version
 219 RESULT_VARIABLE BWRAP_RET
 220 OUTPUT_VARIABLE BWRAP_OUTPUT
 221 )
 222 if (BWRAP_RET)
 223 message(FATAL_ERROR "Failed to run ${BWRAP_EXECUTABLE}")
 224 endif ()
 225 string(REGEX MATCH "([0-9]+.[0-9]+.[0-9]+)" BWRAP_VERSION "${BWRAP_OUTPUT}")
 226 if (NOT "${BWRAP_VERSION}" VERSION_GREATER_EQUAL "0.3.1")
 227 message(FATAL_ERROR "bwrap must be >= 0.3.1 but ${BWRAP_VERSION} found")
 228 endif ()
 229
 230 find_package(Libseccomp)
 231 if (NOT LIBSECCOMP_FOUND)
 232 message(FATAL_ERROR "libseccomp is needed for ENABLE_BUBBLEWRAP_SANDBOX")
 233 endif ()
 234
 235 find_program(DBUS_PROXY_EXECUTABLE flatpak-dbus-proxy
 236 PATHS /usr/libexec /usr/lib /usr/lib/flatpak
 237 )
 238 if (NOT DBUS_PROXY_EXECUTABLE)
 239 message(FATAL_ERROR "flatpak-dbus-proxy not found and is needed for ENABLE_BUBBLEWRAP_SANDBOX")
 240 endif ()
 241 add_definitions(-DDBUS_PROXY_EXECUTABLE="${DBUS_PROXY_EXECUTABLE}")
 242endif ()
 243
204244if (USE_LIBSECRET)
205245 find_package(Libsecret)
206246 if (NOT LIBSECRET_FOUND)

Source/cmake/WebKitFeatures.cmake

@@macro(WEBKIT_OPTION_BEGIN)
8585 WEBKIT_OPTION_DEFINE(ENABLE_ASYNC_SCROLLING "Enable asynchronouse scrolling" PRIVATE OFF)
8686 WEBKIT_OPTION_DEFINE(ENABLE_ATTACHMENT_ELEMENT "Toggle attachment element support" PRIVATE OFF)
8787 WEBKIT_OPTION_DEFINE(ENABLE_AVF_CAPTIONS "Toggle AVFoundation caption support" PRIVATE OFF)
 88 WEBKIT_OPTION_DEFINE(ENABLE_BUBBLEWRAP_SANDBOX "Toggle bubblewrap sandboxing support" PRIVATE OFF)
8889 WEBKIT_OPTION_DEFINE(ENABLE_CACHE_PARTITIONING "Toggle cache partitioning support" PRIVATE OFF)
8990 WEBKIT_OPTION_DEFINE(ENABLE_CHANNEL_MESSAGING "Toggle MessageChannel and MessagePort support" PRIVATE ON)
9091 WEBKIT_OPTION_DEFINE(ENABLE_CONTENT_FILTERING "Toggle content filtering support" PRIVATE OFF)

ChangeLog

 12018-09-03 Patrick Griffis <pgriffis@igalia.com>
 2
 3 [GTK][WPE] Implement subprocess sandboxing
 4 https://bugs.webkit.org/show_bug.cgi?id=188568
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Add ENABLE_BUBBLEWRAP_SANDBOX option for sandboxing.
 9
 10 * Source/cmake/FindLibseccomp.cmake: Added.
 11 * Source/cmake/OptionsGTK.cmake:
 12 * Source/cmake/WebKitFeatures.cmake:
 13
1142018-08-30 Don Olmstead <don.olmstead@sony.com>
215
316 [CMake] Replace AVFoundationSupport.py using CMake