summaryrefslogtreecommitdiffstats
path: root/security/sandbox/linux/broker/SandboxBroker.h
blob: ad3d4b7d49137d79d2c179be074a68844132087e (plain)
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
179
180
/* -*- 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/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:
  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 AddDir/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);
    // This adds all regular files (not directories) in the tree
    // rooted at the given path.
    void AddTree(int aPerms, const char* aPath);
    // A directory, and all files and directories under it, even those
    // added after creation (the dir itself must exist).
    void AddDir(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 AddDirInternal(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 UniquePtr<SandboxBroker> Create(UniquePtr<const Policy> aPolicy,
                                         int aChildPid,
                                         ipc::FileDescriptor& aClientFdOut);
  virtual ~SandboxBroker();

 private:
  PlatformThreadHandle mThread;
  int mFileDesc;
  const int mChildPid;
  const UniquePtr<const Policy> mPolicy;
#if defined(MOZ_CONTENT_TEMP_DIR)
  nsCString mTempPath;
  nsCString mContentTempPath;
#endif

  typedef nsTHashMap<nsCStringHashKey, nsCString> PathMap;
  PathMap mSymlinkMap;

  SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, int& aClientFd);
  void ThreadMain(void) override;
  void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath);
  void AuditDenial(int aOp, int aFlags, 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);
#if defined(MOZ_CONTENT_TEMP_DIR)
  // Remap references to /tmp and friends to the content process tempdir
  size_t RemapTempDirs(char* aPath, size_t aBufSize, size_t aPathLen);
#endif
  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