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