aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/files/safe_fd.h
blob: 3c77362331e78d625209cd8e69db12df3cfed0f0 (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
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This provides an API for performing typical filesystem related tasks while
// guaranteeing certain security properties are maintained. Specifically, checks
// are performed to disallow symbolic links, and exotic file objects. The goal
// behind these checks is to thwart attacks that rely on confusing system
// services to perform unintended file operations like ownership changes or
// copy-as-root attack primitives. To accomplish this these operations are
// written to avoid susceptibility to TOCTOU (time-of-check-time-of-use)
// attacks.

// To use this API start with the root path and work from there. For example:
// SafeFD fd(SafeDirFD::Root().MakeFile(PATH).first);
// if (!fd.is_valid()) {
//   LOG(ERROR) << "Failed to open " << PATH;
//   return false;
// }
// if (fd.WriteString(CONTENTS) != SafeFD::kNoError) {
//   LOG(ERROR) << "Failed to write to " << PATH;
//   return false;
// }
// auto read_result = fd.ReadString();
// if (!read_result.second != SafeFD::kNoError) {
//   LOG(ERROR) << "Failed to read from " << PATH;
//   return false;
// }

#ifndef LIBBRILLO_BRILLO_FILES_SAFE_FD_H_
#define LIBBRILLO_BRILLO_FILES_SAFE_FD_H_

#include <fcntl.h>

#include <string>
#include <utility>
#include <vector>

#include <base/files/file_path.h>
#include <base/files/scoped_file.h>
#include <base/optional.h>
#include <base/synchronization/lock.h>
#include <brillo/brillo_export.h>

namespace brillo {

class SafeFDTest;

class SafeFD {
 public:
  enum class Error {
    kNoError = 0,
    kBadArgument,
    kNotInitialized,  // Invalid operation on a SafeFD that was not initialized.
    kIOError,         // Check errno for specific cause.
    kDoesNotExist,    // The specified path does not exist.
    kSymlinkDetected,
    kBoundaryDetected,  // Detected a file system boundary during recursion.
    kWrongType,         // (e.g. got a directory and expected a file)
    kWrongUID,
    kWrongGID,
    kWrongPermissions,
    kExceededMaximum,  // The maximum allowed read size was reached.
  };

  // Returns true if |err| denotes a failed operation.
  BRILLO_EXPORT static bool IsError(SafeFD::Error err);

  typedef std::pair<SafeFD, Error> SafeFDResult;

  // 100 MiB
  BRILLO_EXPORT static constexpr size_t kDefaultMaxRead = 100 << 20;
  BRILLO_EXPORT static constexpr size_t kDefaultMaxPathDepth = 256;
  // User read and write only.
  BRILLO_EXPORT static constexpr size_t kDefaultFilePermissions = 0640;
  // User read, write, and execute. Group read and execute.
  BRILLO_EXPORT static constexpr size_t kDefaultDirPermissions = 0750;

  // Get a SafeFD to the root path.
  BRILLO_EXPORT static SafeFDResult Root() WARN_UNUSED_RESULT;
  BRILLO_EXPORT static void SetRootPathForTesting(const char* new_root_path);

  // Constructs an invalid fd;
  BRILLO_EXPORT SafeFD() = default;

  // Move-based constructor and assignment.
  BRILLO_EXPORT SafeFD(SafeFD&&) = default;
  BRILLO_EXPORT SafeFD& operator=(SafeFD&&) = default;

  // Return the fd number.
  BRILLO_EXPORT int get() const WARN_UNUSED_RESULT;

  // Check the validity of the file descriptor.
  BRILLO_EXPORT bool is_valid() const WARN_UNUSED_RESULT;

  // Close the scoped file if one was open.
  BRILLO_EXPORT void reset();

  // Wrap |fd| with a SafeFD which will close the fd when this goes out of
  // scope. This closes the original fd if one was open.
  // This is named "Unsafe" because the recommended way to get a SafeFD
  // instance is opening one from SafeFD::Root().
  BRILLO_EXPORT void UnsafeReset(int fd);

  // Writes |size| bytes from |data| into a file and returns kNoError on
  // success. Note the file will be truncated to the size of the content.
  //
  // Parameters
  //  data - The buffer to write to the file.
  //  size - The number of bytes to write.
  BRILLO_EXPORT Error Write(const char* data, size_t size) WARN_UNUSED_RESULT;

  // Read the contents of the file and return it as a string.
  //
  // Parameters
  //  size - The max number of bytes to read.
  BRILLO_EXPORT std::pair<std::vector<char>, Error> ReadContents(
      size_t max_size = kDefaultMaxRead) WARN_UNUSED_RESULT;

  // Reads exactly |size| bytes into |data|.
  //
  // Parameters
  //  data - The buffer to read the file into.
  //  size - The number of bytes to read.
  BRILLO_EXPORT Error Read(char* data, size_t size) WARN_UNUSED_RESULT;

  // Open an existing file relative to this directory.
  //
  // Parameters
  //  path - The path to open relative to the current directory.
  BRILLO_EXPORT SafeFDResult OpenExistingFile(const base::FilePath& path,
                                              int flags = O_RDWR | O_CLOEXEC)
      WARN_UNUSED_RESULT;

  // Open an existing directory relative to this directory.
  //
  // Parameters
  //  path - The path to open relative to the current directory.
  BRILLO_EXPORT SafeFDResult OpenExistingDir(const base::FilePath& path,
                                             int flags = O_RDONLY | O_CLOEXEC)
      WARN_UNUSED_RESULT;

  // Open a file relative to this directory creating the parent directories and
  // file if they don't already exist.
  BRILLO_EXPORT SafeFDResult
  MakeFile(const base::FilePath& path,
           mode_t permissions = kDefaultFilePermissions,
           uid_t uid = getuid(),
           gid_t gid = getgid(),
           int flags = O_RDWR | O_CLOEXEC) WARN_UNUSED_RESULT;

  // Create the directories in the relative path with the given ownership and
  // permissions and return a file descriptor to the result.
  BRILLO_EXPORT SafeFDResult
  MakeDir(const base::FilePath& path,
          mode_t permissions = kDefaultDirPermissions,
          uid_t uid = getuid(),
          gid_t gid = getgid(),
          int flags = O_RDONLY | O_CLOEXEC) WARN_UNUSED_RESULT;

  // Hard link |fd| in the directory represented by |this| with the specified
  // name |filename|. This requires CAP_DAC_READ_SEARCH.
  //
  // Parameters
  //  data - The buffer to write to the file.
  //  size - The number of bytes to write.
  BRILLO_EXPORT Error Link(const SafeFD& source_dir,
                           const std::string& source_name,
                           const std::string& destination_name)
      WARN_UNUSED_RESULT;

  // Deletes the child path named |name|.
  //
  // Parameters
  //  name - the name of the filesystem object to delete.
  BRILLO_EXPORT Error Unlink(const std::string& name) WARN_UNUSED_RESULT;

  // Deletes a child directory. It will return kBoundaryDetected if a file
  // system boundary is reached during recursion.
  //
  // Parameters
  //  name - the name of the directory to delete.
  //  recursive - if true also unlink child paths.
  //  max_depth - limit on recursion depth to prevent fd exhaustion and stack
  //    overflows.
  //  keep_going - in recursive case continue deleting even in the face of
  //    errors. If all entries cannot be deleted, the last error encountered
  //    during recursion is returned.
  BRILLO_EXPORT Error Rmdir(const std::string& name,
                            bool recursive = false,
                            size_t max_depth = kDefaultMaxPathDepth,
                            bool keep_going = true) WARN_UNUSED_RESULT;

 private:
  BRILLO_EXPORT static const char* RootPath;

  base::ScopedFD fd_;

  DISALLOW_COPY_AND_ASSIGN(SafeFD);
};

}  // namespace brillo

#endif  // LIBBRILLO_BRILLO_FILES_SAFE_FD_H_