1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SandboxBrokerPolicyFactory.h"
8 #include "SandboxInfo.h"
9 #include "SandboxLogging.h"
11 #include "base/shared_memory.h"
12 #include "mozilla/Array.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "mozilla/Omnijar.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/SandboxLaunch.h"
17 #include "mozilla/SandboxSettings.h"
18 #include "mozilla/StaticPrefs_security.h"
19 #include "mozilla/StaticMutex.h"
20 #include "mozilla/UniquePtr.h"
21 #include "mozilla/UniquePtrExtensions.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsPrintfCString.h"
25 #include "nsThreadUtils.h"
26 #include "nsXULAppAPI.h"
27 #include "nsDirectoryServiceDefs.h"
28 #include "nsAppDirectoryServiceDefs.h"
29 #include "SpecialSystemDirectory.h"
30 #include "nsReadableUtils.h"
31 #include "nsIFileStreams.h"
32 #include "nsILineInputStream.h"
39 # include "cutils/properties.h"
43 # include "mozilla/WidgetUtilsGtk.h"
49 #include <sys/sysmacros.h>
50 #include <sys/types.h>
58 static const int rdonly
= SandboxBroker::MAY_READ
;
59 static const int wronly
= SandboxBroker::MAY_WRITE
;
60 static const int rdwr
= rdonly
| wronly
;
61 static const int rdwrcr
= rdwr
| SandboxBroker::MAY_CREATE
;
62 static const int access
= SandboxBroker::MAY_ACCESS
;
63 static const int deny
= SandboxBroker::FORCE_DENY
;
66 using CacheE
= std::pair
<nsCString
, int>;
67 using FileCacheT
= nsTArray
<CacheE
>;
69 static void AddDriPaths(SandboxBroker::Policy
* aPolicy
) {
70 // Bug 1401666: Mesa driver loader part 2: Mesa <= 12 using libudev
71 // Used by libdrm, which is used by Mesa, and
72 // Intel(R) Media Driver for VAAPI.
73 if (auto dir
= opendir("/dev/dri")) {
74 while (auto entry
= readdir(dir
)) {
75 if (entry
->d_name
[0] != '.') {
76 nsPrintfCString
devPath("/dev/dri/%s", entry
->d_name
);
78 if (stat(devPath
.get(), &sb
) == 0 && S_ISCHR(sb
.st_mode
)) {
79 // For both the DRI node and its parent (the physical
80 // device), allow reading the "uevent" file.
81 static const Array
<nsCString
, 2> kSuffixes
= {""_ns
, "/device"_ns
};
82 nsPrintfCString
prefix("/sys/dev/char/%u:%u", major(sb
.st_rdev
),
84 for (const auto& suffix
: kSuffixes
) {
85 nsCString
sysPath(prefix
+ suffix
);
87 // libudev will expand the symlink but not do full
88 // canonicalization, so it will leave in ".." path
89 // components that will be realpath()ed in the
90 // broker. To match this, allow the canonical paths.
91 UniqueFreePtr
<char[]> realSysPath(realpath(sysPath
.get(), nullptr));
93 // https://gitlab.freedesktop.org/mesa/drm/-/commit/3988580e4c0f4b3647a0c6af138a3825453fe6e0
94 // > term = strrchr(real_path, '/');
95 // > if (term && strncmp(term, "/virtio", 7) == 0)
97 char* term
= strrchr(realSysPath
.get(), '/');
98 if (term
&& strncmp(term
, "/virtio", 7) == 0) {
102 aPolicy
->AddFilePrefix(rdonly
, realSysPath
.get(), "");
103 // Allowing stat-ing and readlink-ing the parent dirs
104 nsPrintfCString
basePath("%s/", realSysPath
.get());
105 aPolicy
->AddAncestors(basePath
.get(), rdonly
);
109 // https://gitlab.freedesktop.org/mesa/drm/-/commit/a02900133b32dd4a7d6da4966f455ab337e80dfc
110 // > strncpy(path, device_path, PATH_MAX);
111 // > strncat(path, "/subsystem", PATH_MAX);
113 // > if (readlink(path, link, PATH_MAX) < 0)
115 nsCString
subsystemPath(prefix
+ "/device/subsystem"_ns
);
116 aPolicy
->AddPath(rdonly
, subsystemPath
.get());
117 aPolicy
->AddAncestors(subsystemPath
.get(), rdonly
);
124 // https://gitlab.freedesktop.org/mesa/mesa/-/commit/04bdbbcab3c4862bf3f54ce60fcc1d2007776f80
125 aPolicy
->AddPath(rdonly
, "/usr/share/drirc.d");
127 // https://dri.freedesktop.org/wiki/ConfigurationInfrastructure/
128 aPolicy
->AddPath(rdonly
, "/etc/drirc");
130 nsCOMPtr
<nsIFile
> drirc
;
132 GetSpecialSystemDirectory(Unix_HomeDirectory
, getter_AddRefs(drirc
));
133 if (NS_SUCCEEDED(rv
)) {
134 rv
= drirc
->AppendNative(".drirc"_ns
);
135 if (NS_SUCCEEDED(rv
)) {
136 nsAutoCString tmpPath
;
137 rv
= drirc
->GetNativePath(tmpPath
);
138 if (NS_SUCCEEDED(rv
)) {
139 aPolicy
->AddPath(rdonly
, tmpPath
.get());
145 static void JoinPathIfRelative(const nsACString
& aCwd
, const nsACString
& inPath
,
146 nsACString
& outPath
) {
147 if (inPath
.Length() < 1) {
148 outPath
.Assign(aCwd
);
149 SANDBOX_LOG("Unjoinable path: %s", PromiseFlatCString(aCwd
).get());
152 const char* startChar
= inPath
.BeginReading();
153 if (*startChar
!= '/') {
154 // Relative path, copy basepath in front
155 outPath
.Assign(aCwd
);
157 outPath
.Append(inPath
);
159 // Absolute path, it's ok like this
160 outPath
.Assign(inPath
);
164 static void CachePathsFromFile(FileCacheT
& aCache
, const nsACString
& aPath
);
166 static void CachePathsFromFileInternal(FileCacheT
& aCache
,
167 const nsACString
& aCwd
,
168 const nsACString
& aPath
) {
170 nsCOMPtr
<nsIFile
> ldconfig(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
174 rv
= ldconfig
->InitWithNativePath(aPath
);
175 if (NS_WARN_IF(NS_FAILED(rv
))) {
178 nsCOMPtr
<nsIFileInputStream
> fileStream(
179 do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID
, &rv
));
180 if (NS_WARN_IF(NS_FAILED(rv
))) {
183 rv
= fileStream
->Init(ldconfig
, -1, -1, 0);
184 if (NS_WARN_IF(NS_FAILED(rv
))) {
187 nsCOMPtr
<nsILineInputStream
> lineStream(do_QueryInterface(fileStream
, &rv
));
188 if (NS_WARN_IF(NS_FAILED(rv
))) {
195 rv
= lineStream
->ReadLine(line
, &more
);
199 // Cut off any comments at the end of the line, also catches lines
200 // that are entirely a comment
201 int32_t hash
= line
.FindChar('#');
203 line
= Substring(line
, 0, hash
);
205 // Simplify our following parsing by trimming whitespace
206 line
.CompressWhitespace(true, true);
207 if (line
.IsEmpty()) {
208 // Skip comment lines
211 // Check for any included files and recursively process
212 nsACString::const_iterator start
, end
, token_end
;
214 line
.BeginReading(start
);
215 line
.EndReading(end
);
218 if (FindInReadable("include "_ns
, start
, token_end
)) {
219 nsAutoCString
includes(Substring(token_end
, end
));
220 for (const nsACString
& includeGlob
: includes
.Split(' ')) {
221 // Glob path might be relative, so add cwd if so.
222 nsAutoCString includeFile
;
223 JoinPathIfRelative(aCwd
, includeGlob
, includeFile
);
225 if (!glob(PromiseFlatCString(includeFile
).get(), GLOB_NOSORT
, nullptr,
227 for (size_t fileIdx
= 0; fileIdx
< globbuf
.gl_pathc
; fileIdx
++) {
228 nsAutoCString
filePath(globbuf
.gl_pathv
[fileIdx
]);
229 CachePathsFromFile(aCache
, filePath
);
236 // Cut off anything behind an = sign, used by dirname=TYPE directives
237 int32_t equals
= line
.FindChar('=');
239 line
= Substring(line
, 0, equals
);
241 char* resolvedPath
= realpath(line
.get(), nullptr);
243 aCache
.AppendElement(std::make_pair(nsCString(resolvedPath
), rdonly
));
249 static void CachePathsFromFile(FileCacheT
& aCache
, const nsACString
& aPath
) {
250 // Find the new base path where that file sits in.
252 nsCOMPtr
<nsIFile
> includeFile(
253 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
257 rv
= includeFile
->InitWithNativePath(aPath
);
258 if (NS_WARN_IF(NS_FAILED(rv
))) {
261 if (SandboxInfo::Get().Test(SandboxInfo::kVerbose
)) {
262 SANDBOX_LOG("Adding paths from %s to policy.",
263 PromiseFlatCString(aPath
).get());
266 // Find the parent dir where this file sits in.
267 nsCOMPtr
<nsIFile
> parentDir
;
268 rv
= includeFile
->GetParent(getter_AddRefs(parentDir
));
269 if (NS_WARN_IF(NS_FAILED(rv
))) {
272 nsAutoCString parentPath
;
273 rv
= parentDir
->GetNativePath(parentPath
);
274 if (NS_WARN_IF(NS_FAILED(rv
))) {
277 if (SandboxInfo::Get().Test(SandboxInfo::kVerbose
)) {
278 SANDBOX_LOG("Parent path is %s", PromiseFlatCString(parentPath
).get());
280 CachePathsFromFileInternal(aCache
, parentPath
, aPath
);
283 static void AddLdconfigPaths(SandboxBroker::Policy
* aPolicy
) {
284 static StaticMutex sMutex
;
285 StaticMutexAutoLock
lock(sMutex
);
287 static FileCacheT ldConfigCache
{};
288 static bool ldConfigCachePopulated
= false;
289 if (!ldConfigCachePopulated
) {
290 CachePathsFromFile(ldConfigCache
, "/etc/ld.so.conf"_ns
);
291 ldConfigCachePopulated
= true;
293 ldConfigCache
.Clear();
294 MOZ_ASSERT(ldConfigCache
.IsEmpty(), "ldconfig cache should be empty");
297 for (const CacheE
& e
: ldConfigCache
) {
298 aPolicy
->AddDir(e
.second
, e
.first
.get());
302 static void AddLdLibraryEnvPaths(SandboxBroker::Policy
* aPolicy
) {
303 nsAutoCString
LdLibraryEnv(PR_GetEnv("LD_LIBRARY_PATH"));
304 // The items in LD_LIBRARY_PATH can be separated by either colons or
305 // semicolons, according to the ld.so(8) man page, and empirically it
306 // seems to be allowed to mix them (i.e., a:b;c is a list with 3 elements).
307 // There is no support for escaping the delimiters, fortunately (for us).
308 LdLibraryEnv
.ReplaceChar(';', ':');
309 for (const nsACString
& libPath
: LdLibraryEnv
.Split(':')) {
310 char* resolvedPath
= realpath(PromiseFlatCString(libPath
).get(), nullptr);
312 aPolicy
->AddDir(rdonly
, resolvedPath
);
318 static void AddSharedMemoryPaths(SandboxBroker::Policy
* aPolicy
, pid_t aPid
) {
319 std::string
shmPath("/dev/shm");
320 if (base::SharedMemory::AppendPosixShmPrefix(&shmPath
, aPid
)) {
321 aPolicy
->AddPrefix(rdwrcr
, shmPath
.c_str());
325 static void AddMemoryReporting(SandboxBroker::Policy
* aPolicy
, pid_t aPid
) {
326 // Bug 1198552: memory reporting.
327 // Bug 1647957: memory reporting.
328 aPolicy
->AddPath(rdonly
, nsPrintfCString("/proc/%d/statm", aPid
).get());
329 aPolicy
->AddPath(rdonly
, nsPrintfCString("/proc/%d/smaps", aPid
).get());
332 static void AddDynamicPathList(SandboxBroker::Policy
* policy
,
333 const char* aPathListPref
, int perms
) {
334 nsAutoCString pathList
;
335 nsresult rv
= Preferences::GetCString(aPathListPref
, pathList
);
336 if (NS_SUCCEEDED(rv
)) {
337 for (const nsACString
& path
: pathList
.Split(',')) {
338 nsCString
trimPath(path
);
339 trimPath
.Trim(" ", true, true);
340 policy
->AddDynamic(perms
, trimPath
.get());
345 static void AddX11Dependencies(SandboxBroker::Policy
* policy
) {
346 // Allow Primus to contact the Bumblebee daemon to manage GPU
347 // switching on NVIDIA Optimus systems.
348 const char* bumblebeeSocket
= PR_GetEnv("BUMBLEBEE_SOCKET");
349 if (bumblebeeSocket
== nullptr) {
350 bumblebeeSocket
= "/var/run/bumblebee.socket";
352 policy
->AddPath(SandboxBroker::MAY_CONNECT
, bumblebeeSocket
);
354 #if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
355 // Allow local X11 connections, for several purposes:
357 // * for content processes to use WebGL when the browser is in headless
358 // mode, by opening the X display if/when needed
360 // * if Primus or VirtualGL is used, to contact the secondary X server
361 static const bool kIsX11
=
362 !mozilla::widget::GdkIsWaylandDisplay() && PR_GetEnv("DISPLAY");
364 policy
->AddPrefix(SandboxBroker::MAY_CONNECT
, "/tmp/.X11-unix/X");
365 if (auto* const xauth
= PR_GetEnv("XAUTHORITY")) {
366 policy
->AddPath(rdonly
, xauth
);
367 } else if (auto* const home
= PR_GetEnv("HOME")) {
368 // This follows the logic in libXau: append "/.Xauthority",
369 // even if $HOME ends in a slash, except in the special case
370 // where HOME=/ because POSIX allows implementations to treat
371 // an initial double slash specially.
372 nsAutoCString
xauth(home
);
373 if (xauth
!= "/"_ns
) {
376 xauth
.AppendLiteral(".Xauthority");
377 policy
->AddPath(rdonly
, xauth
.get());
383 static void AddGLDependencies(SandboxBroker::Policy
* policy
) {
385 policy
->AddDir(rdwr
, "/dev/dri");
386 policy
->AddFilePrefix(rdwr
, "/dev", "nvidia");
391 // /etc and /usr/share (glvnd, libdrm, drirc, ...?)
392 policy
->AddDir(rdonly
, "/etc");
393 policy
->AddDir(rdonly
, "/usr/share");
394 policy
->AddDir(rdonly
, "/usr/local/share");
396 // Snap puts the usual /usr/share things in a different place, and
397 // we'll fail to load the library if we don't have (at least) the
399 if (const char* snapDesktopDir
= PR_GetEnv("SNAP_DESKTOP_RUNTIME")) {
400 nsAutoCString
snapDesktopShare(snapDesktopDir
);
401 snapDesktopShare
.AppendLiteral("/usr/share");
402 policy
->AddDir(rdonly
, snapDesktopShare
.get());
405 // Note: This function doesn't do anything about Mesa's shader
406 // cache, because the details can vary by process type, including
407 // whether caching is enabled.
409 // This also doesn't include permissions for connecting to a display
410 // server, because headless GL (e.g., Mesa GBM) may not need it.
413 void SandboxBrokerPolicyFactory::InitContentPolicy() {
414 const bool headless
=
415 StaticPrefs::security_sandbox_content_headless_AtStartup();
417 // Policy entries that are the same in every process go here, and
418 // are cached over the lifetime of the factory.
419 SandboxBroker::Policy
* policy
= new SandboxBroker::Policy
;
422 // Bug 1575985: WASM library sandbox needs RW access to /dev/null
423 policy
->AddPath(rdwr
, "/dev/null");
426 AddGLDependencies(policy
);
427 AddX11Dependencies(policy
);
431 policy
->AddPath(rdonly
, "/dev/urandom");
432 policy
->AddPath(rdonly
, "/dev/random");
433 policy
->AddPath(rdonly
, "/proc/sys/crypto/fips_enabled");
434 policy
->AddPath(rdonly
, "/proc/cpuinfo");
435 policy
->AddPath(rdonly
, "/proc/meminfo");
436 policy
->AddDir(rdonly
, "/sys/devices/cpu");
437 policy
->AddDir(rdonly
, "/sys/devices/system/cpu");
438 policy
->AddDir(rdonly
, "/lib");
439 policy
->AddDir(rdonly
, "/lib64");
440 policy
->AddDir(rdonly
, "/usr/lib");
441 policy
->AddDir(rdonly
, "/usr/lib32");
442 policy
->AddDir(rdonly
, "/usr/lib64");
443 policy
->AddDir(rdonly
, "/etc");
444 policy
->AddDir(rdonly
, "/usr/share");
445 policy
->AddDir(rdonly
, "/usr/local/share");
446 // Various places where fonts reside
447 policy
->AddDir(rdonly
, "/usr/X11R6/lib/X11/fonts");
448 policy
->AddDir(rdonly
, "/nix/store");
449 // https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/blob/e434e680d22260f277f4a30ec4660ed32b591d16/files/fontconfig-flatpak.conf
450 policy
->AddDir(rdonly
, "/run/host/fonts");
451 policy
->AddDir(rdonly
, "/run/host/user-fonts");
452 policy
->AddDir(rdonly
, "/run/host/local-fonts");
453 policy
->AddDir(rdonly
, "/var/cache/fontconfig");
455 AddLdconfigPaths(policy
);
456 AddLdLibraryEnvPaths(policy
);
459 // Bug 1385715: NVIDIA PRIME support
460 policy
->AddPath(rdonly
, "/proc/modules");
463 // XDG directories might be non existent according to specs:
464 // https://specifications.freedesktop.org/basedir-spec/0.8/ar01s04.html
466 // > If, when attempting to write a file, the destination directory is
467 // > non-existent an attempt should be made to create it with permission 0700.
469 // For that we use AddPath(, SandboxBroker::Policy::AddCondition::AddAlways).
471 // Allow access to XDG_CONFIG_HOME and XDG_CONFIG_DIRS
472 nsAutoCString
xdgConfigHome(PR_GetEnv("XDG_CONFIG_HOME"));
473 if (!xdgConfigHome
.IsEmpty()) { // AddPath will fail on empty strings
474 policy
->AddFutureDir(rdonly
, xdgConfigHome
.get());
477 nsAutoCString
xdgConfigDirs(PR_GetEnv("XDG_CONFIG_DIRS"));
478 for (const auto& path
: xdgConfigDirs
.Split(':')) {
479 if (!path
.IsEmpty()) { // AddPath will fail on empty strings
480 policy
->AddFutureDir(rdonly
, PromiseFlatCString(path
).get());
484 // Allow fonts subdir in XDG_DATA_HOME
485 nsAutoCString
xdgDataHome(PR_GetEnv("XDG_DATA_HOME"));
486 if (!xdgDataHome
.IsEmpty()) {
487 nsAutoCString
fontPath(xdgDataHome
);
488 fontPath
.Append("/fonts");
489 policy
->AddFutureDir(rdonly
, PromiseFlatCString(fontPath
).get());
492 // Any font subdirs in XDG_DATA_DIRS
493 nsAutoCString
xdgDataDirs(PR_GetEnv("XDG_DATA_DIRS"));
494 for (const auto& path
: xdgDataDirs
.Split(':')) {
495 nsAutoCString
fontPath(path
);
496 fontPath
.Append("/fonts");
497 policy
->AddFutureDir(rdonly
, PromiseFlatCString(fontPath
).get());
500 // Extra configuration/cache dirs in the homedir that we want to allow read
502 std::vector
<const char*> extraConfDirsAllow
= {
508 // Fallback if XDG_CONFIG_HOME isn't set
509 if (xdgConfigHome
.IsEmpty()) {
510 extraConfDirsAllow
.emplace_back(".config");
513 nsCOMPtr
<nsIFile
> homeDir
;
515 GetSpecialSystemDirectory(Unix_HomeDirectory
, getter_AddRefs(homeDir
));
516 if (NS_SUCCEEDED(rv
)) {
517 nsCOMPtr
<nsIFile
> confDir
;
519 for (const auto& dir
: extraConfDirsAllow
) {
520 rv
= homeDir
->Clone(getter_AddRefs(confDir
));
521 if (NS_SUCCEEDED(rv
)) {
522 rv
= confDir
->AppendRelativeNativePath(nsDependentCString(dir
));
523 if (NS_SUCCEEDED(rv
)) {
524 nsAutoCString tmpPath
;
525 rv
= confDir
->GetNativePath(tmpPath
);
526 if (NS_SUCCEEDED(rv
)) {
527 policy
->AddDir(rdonly
, tmpPath
.get());
533 // ~/.config/mozilla/ needs to be manually blocked, because the previous
534 // loop will allow for ~/.config/ access.
536 // If $XDG_CONFIG_HOME is set, we need to account for it.
537 // FIXME: Bug 1722272: Maybe this should just be handled with
538 // GetSpecialSystemDirectory(Unix_XDG_ConfigHome) ?
539 nsCOMPtr
<nsIFile
> confDirOrXDGConfigHomeDir
;
540 if (!xdgConfigHome
.IsEmpty()) {
541 rv
= NS_NewNativeLocalFile(xdgConfigHome
, true,
542 getter_AddRefs(confDirOrXDGConfigHomeDir
));
543 // confDirOrXDGConfigHomeDir = nsIFile($XDG_CONFIG_HOME)
545 rv
= homeDir
->Clone(getter_AddRefs(confDirOrXDGConfigHomeDir
));
546 if (NS_SUCCEEDED(rv
)) {
547 // since we will use that later, we dont need to care about trailing
549 rv
= confDirOrXDGConfigHomeDir
->AppendNative(".config"_ns
);
550 // confDirOrXDGConfigHomeDir = nsIFile($HOME/.config/)
554 if (NS_SUCCEEDED(rv
)) {
555 rv
= confDirOrXDGConfigHomeDir
->AppendNative("mozilla"_ns
);
556 if (NS_SUCCEEDED(rv
)) {
557 nsAutoCString tmpPath
;
558 rv
= confDirOrXDGConfigHomeDir
->GetNativePath(tmpPath
);
559 if (NS_SUCCEEDED(rv
)) {
560 policy
->AddFutureDir(deny
, tmpPath
.get());
566 // ~/.local/share (for themes)
567 rv
= homeDir
->Clone(getter_AddRefs(confDir
));
568 if (NS_SUCCEEDED(rv
)) {
569 rv
= confDir
->AppendNative(".local"_ns
);
570 if (NS_SUCCEEDED(rv
)) {
571 rv
= confDir
->AppendNative("share"_ns
);
573 if (NS_SUCCEEDED(rv
)) {
574 nsAutoCString tmpPath
;
575 rv
= confDir
->GetNativePath(tmpPath
);
576 if (NS_SUCCEEDED(rv
)) {
577 policy
->AddDir(rdonly
, tmpPath
.get());
582 // ~/.fonts.conf (Fontconfig)
583 rv
= homeDir
->Clone(getter_AddRefs(confDir
));
584 if (NS_SUCCEEDED(rv
)) {
585 rv
= confDir
->AppendNative(".fonts.conf"_ns
);
586 if (NS_SUCCEEDED(rv
)) {
587 nsAutoCString tmpPath
;
588 rv
= confDir
->GetNativePath(tmpPath
);
589 if (NS_SUCCEEDED(rv
)) {
590 policy
->AddPath(rdonly
, tmpPath
.get());
596 rv
= homeDir
->Clone(getter_AddRefs(confDir
));
597 if (NS_SUCCEEDED(rv
)) {
598 rv
= confDir
->AppendNative(".pangorc"_ns
);
599 if (NS_SUCCEEDED(rv
)) {
600 nsAutoCString tmpPath
;
601 rv
= confDir
->GetNativePath(tmpPath
);
602 if (NS_SUCCEEDED(rv
)) {
603 policy
->AddPath(rdonly
, tmpPath
.get());
609 // Firefox binary dir.
610 // Note that unlike the previous cases, we use NS_GetSpecialDirectory
611 // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
612 // system, which may not be the case for some tests. For querying for the
613 // location of XPCOM things, we can use it anyway.
614 nsCOMPtr
<nsIFile
> ffDir
;
615 rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(ffDir
));
616 if (NS_SUCCEEDED(rv
)) {
617 nsAutoCString tmpPath
;
618 rv
= ffDir
->GetNativePath(tmpPath
);
619 if (NS_SUCCEEDED(rv
)) {
620 policy
->AddDir(rdonly
, tmpPath
.get());
624 if (!mozilla::IsPackagedBuild()) {
625 // If this is not a packaged build the resources are likely symlinks to
626 // outside the binary dir. Therefore in non-release builds we allow reads
627 // from the whole repository. MOZ_DEVELOPER_REPO_DIR is set by mach run.
628 const char* developer_repo_dir
= PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
629 if (developer_repo_dir
) {
630 policy
->AddDir(rdonly
, developer_repo_dir
);
635 char* bloatLog
= PR_GetEnv("XPCOM_MEM_BLOAT_LOG");
636 // XPCOM_MEM_BLOAT_LOG has the format
637 // /tmp/tmpd0YzFZ.mozrunner/runtests_leaks.log
638 // but stores into /tmp/tmpd0YzFZ.mozrunner/runtests_leaks_tab_pid3411.log
639 // So cut the .log part and whitelist the prefix.
640 if (bloatLog
!= nullptr) {
641 size_t bloatLen
= strlen(bloatLog
);
643 nsAutoCString
bloatStr(bloatLog
);
644 bloatStr
.Truncate(bloatLen
- 4);
645 policy
->AddPrefix(rdwrcr
, bloatStr
.get());
651 AddX11Dependencies(policy
);
654 // Bug 1732580: when packaged as a strictly confined snap, may need
655 // read-access to configuration files under $SNAP/.
656 const char* snap
= PR_GetEnv("SNAP");
658 // When running as a snap, the directory pointed to by $SNAP is guaranteed
659 // to exist before the app is launched, but unit tests need to create it
660 // dynamically, hence the use of AddFutureDir().
661 policy
->AddDir(rdonly
, snap
);
664 // Read any extra paths that will get write permissions,
665 // configured by the user or distro
666 AddDynamicPathList(policy
, "security.sandbox.content.write_path_whitelist",
669 // Whitelisted for reading by the user/distro
670 AddDynamicPathList(policy
, "security.sandbox.content.read_path_whitelist",
673 #if defined(MOZ_CONTENT_TEMP_DIR)
674 // Add write permissions on the content process specific temporary dir.
675 nsCOMPtr
<nsIFile
> tmpDir
;
676 rv
= NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR
,
677 getter_AddRefs(tmpDir
));
678 if (NS_SUCCEEDED(rv
)) {
679 nsAutoCString tmpPath
;
680 rv
= tmpDir
->GetNativePath(tmpPath
);
681 if (NS_SUCCEEDED(rv
)) {
682 policy
->AddDir(rdwrcr
, tmpPath
.get());
687 // userContent.css and the extensions dir sit in the profile, which is
689 nsCOMPtr
<nsIFile
> profileDir
;
690 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
691 getter_AddRefs(profileDir
));
692 if (NS_SUCCEEDED(rv
)) {
693 nsCOMPtr
<nsIFile
> workDir
;
694 rv
= profileDir
->Clone(getter_AddRefs(workDir
));
695 if (NS_SUCCEEDED(rv
)) {
696 rv
= workDir
->AppendNative("chrome"_ns
);
697 if (NS_SUCCEEDED(rv
)) {
698 nsAutoCString tmpPath
;
699 rv
= workDir
->GetNativePath(tmpPath
);
700 if (NS_SUCCEEDED(rv
)) {
701 policy
->AddDir(rdonly
, tmpPath
.get());
705 rv
= profileDir
->Clone(getter_AddRefs(workDir
));
706 if (NS_SUCCEEDED(rv
)) {
707 rv
= workDir
->AppendNative("extensions"_ns
);
708 if (NS_SUCCEEDED(rv
)) {
709 nsAutoCString tmpPath
;
710 rv
= workDir
->GetNativePath(tmpPath
);
711 if (NS_SUCCEEDED(rv
)) {
713 rv
= workDir
->Exists(&exists
);
714 if (NS_SUCCEEDED(rv
)) {
716 policy
->AddPrefix(rdonly
, tmpPath
.get());
717 policy
->AddPath(rdonly
, tmpPath
.get());
719 policy
->AddDir(rdonly
, tmpPath
.get());
727 const int level
= GetEffectiveContentSandboxLevel();
728 bool allowPulse
= false;
729 bool allowAlsa
= false;
731 #ifdef MOZ_PULSEAUDIO
740 // Bug 1309098: ALSA support
741 policy
->AddDir(rdwr
, "/dev/snd");
745 policy
->AddDir(rdwrcr
, "/dev/shm");
748 #ifdef MOZ_WIDGET_GTK
749 if (const auto userDir
= g_get_user_runtime_dir()) {
750 // Bug 1321134: DConf's single bit of shared memory
751 // The leaf filename is "user" by default, but is configurable.
752 nsPrintfCString
shmPath("%s/dconf/", userDir
);
753 policy
->AddPrefix(rdwrcr
, shmPath
.get());
754 policy
->AddAncestors(shmPath
.get());
756 // PulseAudio, if it can't get server info from X11, will break
757 // unless it can open this directory (or create it, but in our use
758 // case we know it already exists). See bug 1335329.
759 nsPrintfCString
pulsePath("%s/pulse", userDir
);
760 policy
->AddPath(rdonly
, pulsePath
.get());
763 #endif // MOZ_WIDGET_GTK
766 // PulseAudio also needs access to read the $XAUTHORITY file (see
767 // bug 1384986 comment #1), but that's already allowed for hybrid
768 // GPU drivers (see above).
769 policy
->AddPath(rdonly
, "/var/lib/dbus/machine-id");
772 // Bug 1434711 - AMDGPU-PRO crashes if it can't read it's marketing ids
773 // and various other things
774 if (!headless
&& HasAtiDrivers()) {
775 policy
->AddDir(rdonly
, "/opt/amdgpu/share");
776 policy
->AddPath(rdonly
, "/sys/module/amdgpu");
779 mCommonContentPolicy
.reset(policy
);
782 UniquePtr
<SandboxBroker::Policy
> SandboxBrokerPolicyFactory::GetContentPolicy(
783 int aPid
, bool aFileProcess
) {
784 // Policy entries that vary per-process (because they depend on the
785 // pid or content subtype) are added here.
787 MOZ_ASSERT(NS_IsMainThread());
789 const int level
= GetEffectiveContentSandboxLevel();
790 // The file broker is used at level 2 and up.
792 // Level 1 has been removed.
793 MOZ_ASSERT(level
== 0);
797 std::call_once(mContentInited
, [this] { InitContentPolicy(); });
798 MOZ_ASSERT(mCommonContentPolicy
);
799 UniquePtr
<SandboxBroker::Policy
> policy(
800 new SandboxBroker::Policy(*mCommonContentPolicy
));
802 // No read blocking at level 2 and below.
803 // file:// processes also get global read permissions
804 if (level
<= 2 || aFileProcess
) {
805 policy
->AddDir(rdonly
, "/");
806 // Any other read-only rules will be removed as redundant by
807 // Policy::FixRecursivePermissions, so there's no need to
808 // early-return here.
811 // Access to /dev/shm is restricted to a per-process prefix to
812 // prevent interfering with other processes or with services outside
813 // the browser (e.g., PulseAudio).
814 AddSharedMemoryPaths(policy
.get(), aPid
);
816 // Bug 1198550: the profiler's replacement for dl_iterate_phdr
817 policy
->AddPath(rdonly
, nsPrintfCString("/proc/%d/maps", aPid
).get());
819 // Bug 1736040: CPU use telemetry
820 policy
->AddPath(rdonly
, nsPrintfCString("/proc/%d/stat", aPid
).get());
822 // Bug 1198552: memory reporting.
823 AddMemoryReporting(policy
.get(), aPid
);
825 // Bug 1384804, notably comment 15
826 // Used by libnuma, included by x265/ffmpeg, who falls back
827 // to get_mempolicy if this fails
828 policy
->AddPath(rdonly
, nsPrintfCString("/proc/%d/status", aPid
).get());
830 // Finalize the policy.
831 policy
->FixRecursivePermissions();
835 /* static */ UniquePtr
<SandboxBroker::Policy
>
836 SandboxBrokerPolicyFactory::GetRDDPolicy(int aPid
) {
837 auto policy
= MakeUnique
<SandboxBroker::Policy
>();
839 AddSharedMemoryPaths(policy
.get(), aPid
);
841 policy
->AddPath(rdonly
, "/dev/urandom");
842 // FIXME (bug 1662321): we should fix nsSystemInfo so that every
843 // child process doesn't need to re-read these files to get the info
844 // the parent process already has.
845 policy
->AddPath(rdonly
, "/proc/cpuinfo");
846 policy
->AddPath(rdonly
,
847 "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
848 policy
->AddPath(rdonly
, "/sys/devices/system/cpu/cpu0/cache/index2/size");
849 policy
->AddPath(rdonly
, "/sys/devices/system/cpu/cpu0/cache/index3/size");
850 policy
->AddDir(rdonly
, "/sys/devices/cpu");
851 policy
->AddDir(rdonly
, "/sys/devices/system/cpu");
852 policy
->AddDir(rdonly
, "/sys/devices/system/node");
853 policy
->AddDir(rdonly
, "/lib");
854 policy
->AddDir(rdonly
, "/lib64");
855 policy
->AddDir(rdonly
, "/usr/lib");
856 policy
->AddDir(rdonly
, "/usr/lib32");
857 policy
->AddDir(rdonly
, "/usr/lib64");
858 policy
->AddDir(rdonly
, "/run/opengl-driver/lib");
859 policy
->AddDir(rdonly
, "/nix/store");
861 // Bug 1647957: memory reporting.
862 AddMemoryReporting(policy
.get(), aPid
);
864 // Firefox binary dir.
865 // Note that unlike the previous cases, we use NS_GetSpecialDirectory
866 // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
867 // system, which may not be the case for some tests. For querying for the
868 // location of XPCOM things, we can use it anyway.
869 nsCOMPtr
<nsIFile
> ffDir
;
870 nsresult rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(ffDir
));
871 if (NS_SUCCEEDED(rv
)) {
872 nsAutoCString tmpPath
;
873 rv
= ffDir
->GetNativePath(tmpPath
);
874 if (NS_SUCCEEDED(rv
)) {
875 policy
->AddDir(rdonly
, tmpPath
.get());
879 if (!mozilla::IsPackagedBuild()) {
880 // If this is not a packaged build the resources are likely symlinks to
881 // outside the binary dir. Therefore in non-release builds we allow reads
882 // from the whole repository. MOZ_DEVELOPER_REPO_DIR is set by mach run.
883 const char* developer_repo_dir
= PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
884 if (developer_repo_dir
) {
885 policy
->AddDir(rdonly
, developer_repo_dir
);
889 // VA-API needs GPU access and GL context creation (but not display
890 // server access, as of bug 1769499).
891 AddGLDependencies(policy
.get());
893 // FFmpeg and GPU drivers may need general-case library loading
894 AddLdconfigPaths(policy
.get());
895 AddLdLibraryEnvPaths(policy
.get());
897 if (policy
->IsEmpty()) {
903 /* static */ UniquePtr
<SandboxBroker::Policy
>
904 SandboxBrokerPolicyFactory::GetSocketProcessPolicy(int aPid
) {
905 auto policy
= MakeUnique
<SandboxBroker::Policy
>();
907 policy
->AddPath(rdonly
, "/dev/urandom");
908 policy
->AddPath(rdonly
, "/dev/random");
909 policy
->AddPath(rdonly
, "/proc/sys/crypto/fips_enabled");
910 policy
->AddPath(rdonly
, "/proc/cpuinfo");
911 policy
->AddPath(rdonly
, "/proc/meminfo");
912 policy
->AddDir(rdonly
, "/sys/devices/cpu");
913 policy
->AddDir(rdonly
, "/sys/devices/system/cpu");
914 policy
->AddDir(rdonly
, "/lib");
915 policy
->AddDir(rdonly
, "/lib64");
916 policy
->AddDir(rdonly
, "/usr/lib");
917 policy
->AddDir(rdonly
, "/usr/lib32");
918 policy
->AddDir(rdonly
, "/usr/lib64");
919 policy
->AddDir(rdonly
, "/usr/share");
920 policy
->AddDir(rdonly
, "/usr/local/share");
921 policy
->AddDir(rdonly
, "/etc");
923 // glibc will try to stat64("/") while populating nsswitch database
924 // https://sourceware.org/git/?p=glibc.git;a=blob;f=nss/nss_database.c;h=cf0306adc47f12d9bc761ab1b013629f4482b7e6;hb=9826b03b747b841f5fc6de2054bf1ef3f5c4bdf3#l396
925 // denying will make getaddrinfo() return ENONAME
926 policy
->AddDir(access
, "/");
928 AddLdconfigPaths(policy
.get());
930 // Socket process sandbox needs to allow shmem in order to support
931 // profiling. See Bug 1626385.
932 AddSharedMemoryPaths(policy
.get(), aPid
);
934 // Bug 1647957: memory reporting.
935 AddMemoryReporting(policy
.get(), aPid
);
937 // Firefox binary dir.
938 // Note that unlike the previous cases, we use NS_GetSpecialDirectory
939 // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
940 // system, which may not be the case for some tests. For querying for the
941 // location of XPCOM things, we can use it anyway.
942 nsCOMPtr
<nsIFile
> ffDir
;
943 nsresult rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(ffDir
));
944 if (NS_SUCCEEDED(rv
)) {
945 nsAutoCString tmpPath
;
946 rv
= ffDir
->GetNativePath(tmpPath
);
947 if (NS_SUCCEEDED(rv
)) {
948 policy
->AddDir(rdonly
, tmpPath
.get());
952 if (policy
->IsEmpty()) {
958 /* static */ UniquePtr
<SandboxBroker::Policy
>
959 SandboxBrokerPolicyFactory::GetUtilityProcessPolicy(int aPid
) {
960 auto policy
= MakeUnique
<SandboxBroker::Policy
>();
962 policy
->AddPath(rdonly
, "/dev/urandom");
963 policy
->AddPath(rdonly
, "/proc/cpuinfo");
964 policy
->AddPath(rdonly
, "/proc/meminfo");
965 policy
->AddPath(rdonly
, nsPrintfCString("/proc/%d/exe", aPid
).get());
966 policy
->AddDir(rdonly
, "/sys/devices/cpu");
967 policy
->AddDir(rdonly
, "/sys/devices/system/cpu");
968 policy
->AddDir(rdonly
, "/lib");
969 policy
->AddDir(rdonly
, "/lib64");
970 policy
->AddDir(rdonly
, "/usr/lib");
971 policy
->AddDir(rdonly
, "/usr/lib32");
972 policy
->AddDir(rdonly
, "/usr/lib64");
973 policy
->AddDir(rdonly
, "/usr/share");
974 policy
->AddDir(rdonly
, "/usr/local/share");
975 policy
->AddDir(rdonly
, "/etc");
977 // glibc will try to stat64("/") while populating nsswitch database
978 // https://sourceware.org/git/?p=glibc.git;a=blob;f=nss/nss_database.c;h=cf0306adc47f12d9bc761ab1b013629f4482b7e6;hb=9826b03b747b841f5fc6de2054bf1ef3f5c4bdf3#l396
979 // denying will make getaddrinfo() return ENONAME
980 policy
->AddDir(access
, "/");
982 AddLdconfigPaths(policy
.get());
984 // Utility process sandbox needs to allow shmem in order to support
985 // profiling. See Bug 1626385.
986 AddSharedMemoryPaths(policy
.get(), aPid
);
988 // Bug 1647957: memory reporting.
989 AddMemoryReporting(policy
.get(), aPid
);
991 // Firefox binary dir.
992 // Note that unlike the previous cases, we use NS_GetSpecialDirectory
993 // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
994 // system, which may not be the case for some tests. For querying for the
995 // location of XPCOM things, we can use it anyway.
996 nsCOMPtr
<nsIFile
> ffDir
;
997 nsresult rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(ffDir
));
998 if (NS_SUCCEEDED(rv
)) {
999 nsAutoCString tmpPath
;
1000 rv
= ffDir
->GetNativePath(tmpPath
);
1001 if (NS_SUCCEEDED(rv
)) {
1002 policy
->AddDir(rdonly
, tmpPath
.get());
1006 if (policy
->IsEmpty()) {
1012 } // namespace mozilla