summaryrefslogtreecommitdiff
path: root/chromium/sandbox/linux/syscall_broker/broker_file_permission.h
blob: 3571b8a0f7e4e8854d9a17c67345502e14411109 (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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_

#include <bitset>
#include <cstdint>
#include <string>

#include "sandbox/sandbox_export.h"

namespace sandbox {
namespace syscall_broker {

// Recursive: allow everything under |path| (must be a dir).
enum class RecursionOption { kNonRecursive = 0, kRecursive };
// Temporary: file will be unlink'd after opening.
enum class PersistenceOption { kPermanent = 0, kTemporaryOnly };
enum class ReadPermission { kBlockRead = 0, kAllowRead };
enum class WritePermission { kBlockWrite = 0, kAllowWrite };
enum class CreatePermission { kBlockCreate = 0, kAllowCreate };
// Allow stat() for the path and all intermediate dirs.
enum class StatWithIntermediatesPermission {
  kBlockStatWithIntermediates = 0,
  kAllowStatWithIntermediates
};

// BrokerFilePermission defines a path for allowlisting.
// Pick the correct static factory method to create a permission.
// CheckOpen and CheckAccess are async signal safe.
// Construction and Destruction are not async signal safe.
// |path| is the path to be allowlisted.
class SANDBOX_EXPORT BrokerFilePermission {
 public:
  // Movable and copyable.
  BrokerFilePermission(BrokerFilePermission&&);
  BrokerFilePermission& operator=(BrokerFilePermission&&);
  BrokerFilePermission(const BrokerFilePermission&);
  BrokerFilePermission& operator=(const BrokerFilePermission&);

  ~BrokerFilePermission();

  static BrokerFilePermission ReadOnly(const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
        ReadPermission::kAllowRead, WritePermission::kBlockWrite,
        CreatePermission::kBlockCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission ReadOnlyRecursive(const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kRecursive, PersistenceOption::kPermanent,
        ReadPermission::kAllowRead, WritePermission::kBlockWrite,
        CreatePermission::kBlockCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission WriteOnly(const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
        ReadPermission::kBlockRead, WritePermission::kAllowWrite,
        CreatePermission::kBlockCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission ReadWrite(const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
        ReadPermission::kAllowRead, WritePermission::kAllowWrite,
        CreatePermission::kBlockCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission ReadWriteCreate(const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
        ReadPermission::kAllowRead, WritePermission::kAllowWrite,
        CreatePermission::kAllowCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission ReadWriteCreateRecursive(
      const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kRecursive, PersistenceOption::kPermanent,
        ReadPermission::kAllowRead, WritePermission::kAllowWrite,
        CreatePermission::kAllowCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  // Temporary files must always be newly created and do not confer rights to
  // use pre-existing files of the same name.
  static BrokerFilePermission ReadWriteCreateTemporary(
      const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kNonRecursive, PersistenceOption::kTemporaryOnly,
        ReadPermission::kAllowRead, WritePermission::kAllowWrite,
        CreatePermission::kAllowCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission ReadWriteCreateTemporaryRecursive(
      const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kRecursive, PersistenceOption::kTemporaryOnly,
        ReadPermission::kAllowRead, WritePermission::kAllowWrite,
        CreatePermission::kAllowCreate,
        StatWithIntermediatesPermission::kBlockStatWithIntermediates);
  }

  static BrokerFilePermission StatOnlyWithIntermediateDirs(
      const std::string& path) {
    return BrokerFilePermission(
        path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
        ReadPermission::kBlockRead, WritePermission::kBlockWrite,
        CreatePermission::kBlockCreate,
        StatWithIntermediatesPermission::kAllowStatWithIntermediates);
  }

  // Returns true if |requested_filename| is allowed to be accessed
  // by this permission as per access(2).
  // If |file_to_access| is not NULL, it is set to point to either
  // the |requested_filename| in the case of a recursive match,
  // or a pointer to the matched path in the allowlist if an absolute
  // match.
  // |mode| is per mode argument of access(2).
  // Async signal safe if |file_to_access| is NULL
  bool CheckAccess(const char* requested_filename,
                   int mode,
                   const char** file_to_access) const;

  // Returns true if |requested_filename| is allowed to be opened
  // by this permission.
  // If |file_to_open| is not NULL it is set to point to either
  // the |requested_filename| in the case of a recursive match,
  // or a pointer the matched path in the allowlist if an absolute
  // match.
  // If not NULL, |unlink_after_open| is set to point to true if the
  // caller is required to unlink the path after opening.
  // Async signal safe if |file_to_open| is NULL.
  bool CheckOpen(const char* requested_filename,
                 int flags,
                 const char** file_to_open,
                 bool* unlink_after_open) const;

  // Returns true if |requested_filename| is allowed to be stat'd
  // by this permission as per stat(2). Differs from CheckAccess()
  // in that if create permission is granted to a file, we permit
  // stat() on all of its leading components.
  // If |file_to_open| is not NULL, it is set to point to either
  // the |requested_filename| in the case of a recursive match,
  // or a pointer to the matched path in the allowlist if an absolute
  // match.
  // Async signal safe if |file_to_access| is NULL
  bool CheckStat(const char* requested_filename,
                 const char** file_to_access) const;

 private:
  friend class BrokerFilePermissionTester;

  enum BitPositions {
    kRecursiveBitPos = 0,
    kTemporaryOnlyBitPos,
    kAllowReadBitPos,
    kAllowWriteBitPos,
    kAllowCreateBitPos,
    kAllowStatWithIntermediatesBitPos,

    kMaxValueBitPos = kAllowStatWithIntermediatesBitPos
  };

  // NOTE: Validates the permission and dies if invalid!
  BrokerFilePermission(std::string path,
                       RecursionOption recurse_opt,
                       PersistenceOption persist_opt,
                       ReadPermission read_perm,
                       WritePermission write_perm,
                       CreatePermission create_perm,
                       StatWithIntermediatesPermission stat_perm);

  // Allows construction from the raw bitset.
  BrokerFilePermission(std::string path, uint64_t flags);

  const std::string& path() const { return path_; }

  // Returns a serialiazable version of |flags_|.
  uint64_t flags() const { return flags_.to_ullong(); }

  bool recursive() const { return flags_.test(kRecursiveBitPos); }

  bool temporary_only() const { return flags_.test(kTemporaryOnlyBitPos); }

  bool allow_read() const { return flags_.test(kAllowReadBitPos); }

  bool allow_write() const { return flags_.test(kAllowWriteBitPos); }

  bool allow_create() const { return flags_.test(kAllowCreateBitPos); }

  bool allow_stat_with_intermediates() const {
    return flags_.test(kAllowStatWithIntermediatesBitPos);
  }

  // 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
  static bool ValidatePath(const char* path);

  // MatchPath returns true if |requested_filename| is covered by this instance
  bool MatchPath(const char* requested_filename) const;

  // Helper routine for CheckAccess() and CheckStat(). Must be safe to call
  // from an async signal context.
  bool CheckAccessInternal(const char* requested_filename,
                           int mode,
                           const char** file_to_access) const;

  // Used in by BrokerFilePermissionTester for tests.
  static const char* GetErrorMessageForTests();

  void DieOnInvalidPermission();

  // These are not const as std::vector requires copy-assignment and this class
  // is stored in vectors. All methods are marked const so the compiler will
  // still enforce no changes outside of the constructor.
  std::string path_;
  std::bitset<kMaxValueBitPos + 1> flags_;
};

}  // namespace syscall_broker
}  // namespace sandbox

#endif  //  SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_