1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_SandboxBroker_h
#define mozilla_SandboxBroker_h
#include "mozilla/SandboxBrokerCommon.h"
#include "base/platform_thread.h"
#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsTHashMap.h"
#include "nsHashKeys.h"
#include "nsString.h"
namespace mozilla {
namespace ipc {
class FileDescriptor;
}
// This class implements a broker for filesystem operations requested
// by a sandboxed child process -- opening files and accessing their
// metadata. (This is necessary in order to restrict access by path;
// seccomp-bpf can filter only on argument register values, not
// parameters passed in memory like pathnames.)
//
// The broker currently runs on a thread in the parent process (with
// effective uid changed on B2G), which is for memory efficiency
// (compared to forking a process) and simplicity (compared to having
// a separate executable and serializing/deserializing the policy).
//
// See also ../SandboxBrokerClient.h for the corresponding client.
class SandboxBroker final : private SandboxBrokerCommon,
public PlatformThread::Delegate {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SandboxBroker)
enum Perms {
MAY_ACCESS = 1 << 0,
MAY_READ = 1 << 1,
MAY_WRITE = 1 << 2,
MAY_CREATE = 1 << 3,
// This flag is for testing policy changes -- when the client is
// used with the seccomp-bpf integration, an access to this file
// will invoke a crash dump with the context of the syscall.
// (This overrides all other flags.)
CRASH_INSTEAD = 1 << 4,
// Applies to everything below this path, including subdirs created
// at runtime
RECURSIVE = 1 << 5,
// Allow Unix-domain socket connections to a path
MAY_CONNECT = 1 << 6,
// This flag is for adding a deny rule, so that we can e.g., allow read
// access to ~/.config/ but still deny access to ~/.config/mozilla/.
// It will bypass other checks.
FORCE_DENY = 1 << 7,
};
// Bitwise operations on enum values return ints, so just use int in
// the hash table type (and below) to avoid cluttering code with casts.
typedef nsTHashMap<nsCStringHashKey, int> PathPermissionMap;
class Policy {
PathPermissionMap mMap;
public:
Policy();
Policy(const Policy& aOther);
~Policy();
// Add permissions from AddTree/AddDynamic rules to any rules that
// exist for their descendents, and remove any descendent rules
// made redundant by this process.
//
// Call this after adding rules and before using the policy to
// prevent the descendent rules from shadowing the ancestor rules
// and removing permissions that we expect the file to have.
void FixRecursivePermissions();
enum AddCondition {
AddIfExistsNow,
AddAlways,
};
// Typically, files that don't exist at policy creation time don't
// need to be whitelisted, but this allows adding entries for
// them if they'll exist later. See also the overload below.
void AddPath(int aPerms, const char* aPath, AddCondition aCond);
// A directory, and all files and directories under it, even those
// added after creation (the dir itself must exist).
void AddTree(int aPerms, const char* aPath);
// A directory, and all files and directories under it, even those
// added after creation (the dir itself may not exist).
void AddFutureDir(int aPerms, const char* aPath);
// All files in a directory with a given prefix; useful for devices.
void AddFilePrefix(int aPerms, const char* aDir, const char* aPrefix);
// Everything starting with the given path, even those files/dirs
// added after creation. The file or directory may or may not exist.
void AddPrefix(int aPerms, const char* aPath);
// Adds a file or dir (end with /) if it exists, and a prefix otherwhise.
void AddDynamic(int aPerms, const char* aPath);
// Adds permissions on all ancestors of a path. (This doesn't
// include the root directory, but if the path is given with a
// trailing slash it includes the path without the slash.)
void AddAncestors(const char* aPath, int aPerms = MAY_ACCESS);
// Default: add file if it exists when creating policy or if we're
// conferring permission to create it (log files, etc.).
void AddPath(int aPerms, const char* aPath) {
AddPath(aPerms, aPath,
(aPerms & MAY_CREATE) ? AddAlways : AddIfExistsNow);
}
int Lookup(const nsACString& aPath) const;
int Lookup(const char* aPath) const {
return Lookup(nsDependentCString(aPath));
}
bool IsEmpty() const { return mMap.Count() == 0; }
private:
// ValidatePath checks |path| and returns true if these conditions are met
// * Greater than 0 length
// * Is an absolute path
// * No trailing slash
// * No /../ path traversal
bool ValidatePath(const char* path) const;
void AddPrefixInternal(int aPerms, const nsACString& aPath);
void AddTreeInternal(int aPerms, const char* aPath);
};
// Constructing a broker involves creating a socketpair and a
// background thread to handle requests, so it can fail. If this
// returns nullptr, do not use the value of aClientFdOut.
static already_AddRefed<SandboxBroker> Create(
UniquePtr<const Policy> aPolicy, int aChildPid,
ipc::FileDescriptor& aClientFdOut);
// Allow for explicit termination in the gtests
void Terminate();
private:
PlatformThreadHandle mThread;
int mFileDesc;
const int mChildPid;
const UniquePtr<const Policy> mPolicy;
typedef nsTHashMap<nsCStringHashKey, nsCString> PathMap;
PathMap mSymlinkMap;
SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, int& aClientFd);
~SandboxBroker() override;
void ThreadMain(void) override;
void AuditPermissive(int aOp, int aFlags, uint64_t aId, int aPerms,
const char* aPath);
void AuditDenial(int aOp, int aFlags, uint64_t aId, int aPerms,
const char* aPath);
// Remap relative paths to absolute paths.
size_t ConvertRelativePath(char* aPath, size_t aBufSize, size_t aPathLen);
size_t RealPath(char* aPath, size_t aBufSize, size_t aPathLen);
nsCString ReverseSymlinks(const nsACString& aPath);
// Retrieves permissions for the path the original symlink sits in.
int SymlinkPermissions(const char* aPath, const size_t aPathLen);
// In SandboxBrokerRealPath.cpp
char* SymlinkPath(const Policy* aPolicy, const char* __restrict aPath,
char* __restrict aResolved, int* aPermission);
// Holding a UniquePtr should disallow copying, but to make that explicit:
SandboxBroker(const SandboxBroker&) = delete;
void operator=(const SandboxBroker&) = delete;
};
} // namespace mozilla
#endif // mozilla_SandboxBroker_h
|