xref: /aosp_15_r20/external/libbrillo/brillo/files/safe_fd.h (revision 1a96fba65179ea7d3f56207137718607415c5953)
1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // This provides an API for performing typical filesystem related tasks while
6 // guaranteeing certain security properties are maintained. Specifically, checks
7 // are performed to disallow symbolic links, and exotic file objects. The goal
8 // behind these checks is to thwart attacks that rely on confusing system
9 // services to perform unintended file operations like ownership changes or
10 // copy-as-root attack primitives. To accomplish this these operations are
11 // written to avoid susceptibility to TOCTOU (time-of-check-time-of-use)
12 // attacks.
13 
14 // To use this API start with the root path and work from there. For example:
15 // SafeFD fd(SafeDirFD::Root().MakeFile(PATH).first);
16 // if (!fd.is_valid()) {
17 //   LOG(ERROR) << "Failed to open " << PATH;
18 //   return false;
19 // }
20 // if (fd.WriteString(CONTENTS) != SafeFD::kNoError) {
21 //   LOG(ERROR) << "Failed to write to " << PATH;
22 //   return false;
23 // }
24 // auto read_result = fd.ReadString();
25 // if (!read_result.second != SafeFD::kNoError) {
26 //   LOG(ERROR) << "Failed to read from " << PATH;
27 //   return false;
28 // }
29 
30 #ifndef LIBBRILLO_BRILLO_FILES_SAFE_FD_H_
31 #define LIBBRILLO_BRILLO_FILES_SAFE_FD_H_
32 
33 #include <fcntl.h>
34 
35 #include <string>
36 #include <utility>
37 #include <vector>
38 
39 #include <base/files/file_path.h>
40 #include <base/files/scoped_file.h>
41 #include <base/optional.h>
42 #include <base/synchronization/lock.h>
43 #include <brillo/brillo_export.h>
44 
45 namespace brillo {
46 
47 class SafeFDTest;
48 
49 class SafeFD {
50  public:
51   enum class Error {
52     kNoError = 0,
53     kBadArgument,
54     kNotInitialized,  // Invalid operation on a SafeFD that was not initialized.
55     kIOError,         // Check errno for specific cause.
56     kDoesNotExist,    // The specified path does not exist.
57     kSymlinkDetected,
58     kBoundaryDetected,  // Detected a file system boundary during recursion.
59     kWrongType,         // (e.g. got a directory and expected a file)
60     kWrongUID,
61     kWrongGID,
62     kWrongPermissions,
63     kExceededMaximum,  // The maximum allowed read size was reached.
64   };
65 
66   // Returns true if |err| denotes a failed operation.
67   BRILLO_EXPORT static bool IsError(SafeFD::Error err);
68 
69   typedef std::pair<SafeFD, Error> SafeFDResult;
70 
71   // 100 MiB
72   BRILLO_EXPORT static constexpr size_t kDefaultMaxRead = 100 << 20;
73   BRILLO_EXPORT static constexpr size_t kDefaultMaxPathDepth = 256;
74   // User read and write only.
75   BRILLO_EXPORT static constexpr size_t kDefaultFilePermissions = 0640;
76   // User read, write, and execute. Group read and execute.
77   BRILLO_EXPORT static constexpr size_t kDefaultDirPermissions = 0750;
78 
79   // Get a SafeFD to the root path.
80   BRILLO_EXPORT static SafeFDResult Root() WARN_UNUSED_RESULT;
81   BRILLO_EXPORT static void SetRootPathForTesting(const char* new_root_path);
82 
83   // Constructs an invalid fd;
84   BRILLO_EXPORT SafeFD() = default;
85 
86   // Move-based constructor and assignment.
87   BRILLO_EXPORT SafeFD(SafeFD&&) = default;
88   BRILLO_EXPORT SafeFD& operator=(SafeFD&&) = default;
89 
90   // Return the fd number.
91   BRILLO_EXPORT int get() const WARN_UNUSED_RESULT;
92 
93   // Check the validity of the file descriptor.
94   BRILLO_EXPORT bool is_valid() const WARN_UNUSED_RESULT;
95 
96   // Close the scoped file if one was open.
97   BRILLO_EXPORT void reset();
98 
99   // Wrap |fd| with a SafeFD which will close the fd when this goes out of
100   // scope. This closes the original fd if one was open.
101   // This is named "Unsafe" because the recommended way to get a SafeFD
102   // instance is opening one from SafeFD::Root().
103   BRILLO_EXPORT void UnsafeReset(int fd);
104 
105   // Writes |size| bytes from |data| into a file and returns kNoError on
106   // success. Note the file will be truncated to the size of the content.
107   //
108   // Parameters
109   //  data - The buffer to write to the file.
110   //  size - The number of bytes to write.
111   BRILLO_EXPORT Error Write(const char* data, size_t size) WARN_UNUSED_RESULT;
112 
113   // Read the contents of the file and return it as a string.
114   //
115   // Parameters
116   //  size - The max number of bytes to read.
117   BRILLO_EXPORT std::pair<std::vector<char>, Error> ReadContents(
118       size_t max_size = kDefaultMaxRead) WARN_UNUSED_RESULT;
119 
120   // Reads exactly |size| bytes into |data|.
121   //
122   // Parameters
123   //  data - The buffer to read the file into.
124   //  size - The number of bytes to read.
125   BRILLO_EXPORT Error Read(char* data, size_t size) WARN_UNUSED_RESULT;
126 
127   // Open an existing file relative to this directory.
128   //
129   // Parameters
130   //  path - The path to open relative to the current directory.
131   BRILLO_EXPORT SafeFDResult OpenExistingFile(const base::FilePath& path,
132                                               int flags = O_RDWR | O_CLOEXEC)
133       WARN_UNUSED_RESULT;
134 
135   // Open an existing directory relative to this directory.
136   //
137   // Parameters
138   //  path - The path to open relative to the current directory.
139   BRILLO_EXPORT SafeFDResult OpenExistingDir(const base::FilePath& path,
140                                              int flags = O_RDONLY | O_CLOEXEC)
141       WARN_UNUSED_RESULT;
142 
143   // Open a file relative to this directory creating the parent directories and
144   // file if they don't already exist.
145   BRILLO_EXPORT SafeFDResult
146   MakeFile(const base::FilePath& path,
147            mode_t permissions = kDefaultFilePermissions,
148            uid_t uid = getuid(),
149            gid_t gid = getgid(),
150            int flags = O_RDWR | O_CLOEXEC) WARN_UNUSED_RESULT;
151 
152   // Create the directories in the relative path with the given ownership and
153   // permissions and return a file descriptor to the result.
154   BRILLO_EXPORT SafeFDResult
155   MakeDir(const base::FilePath& path,
156           mode_t permissions = kDefaultDirPermissions,
157           uid_t uid = getuid(),
158           gid_t gid = getgid(),
159           int flags = O_RDONLY | O_CLOEXEC) WARN_UNUSED_RESULT;
160 
161   // Hard link |fd| in the directory represented by |this| with the specified
162   // name |filename|. This requires CAP_DAC_READ_SEARCH.
163   //
164   // Parameters
165   //  data - The buffer to write to the file.
166   //  size - The number of bytes to write.
167   BRILLO_EXPORT Error Link(const SafeFD& source_dir,
168                            const std::string& source_name,
169                            const std::string& destination_name)
170       WARN_UNUSED_RESULT;
171 
172   // Deletes the child path named |name|.
173   //
174   // Parameters
175   //  name - the name of the filesystem object to delete.
176   BRILLO_EXPORT Error Unlink(const std::string& name) WARN_UNUSED_RESULT;
177 
178   // Deletes a child directory. It will return kBoundaryDetected if a file
179   // system boundary is reached during recursion.
180   //
181   // Parameters
182   //  name - the name of the directory to delete.
183   //  recursive - if true also unlink child paths.
184   //  max_depth - limit on recursion depth to prevent fd exhaustion and stack
185   //    overflows.
186   //  keep_going - in recursive case continue deleting even in the face of
187   //    errors. If all entries cannot be deleted, the last error encountered
188   //    during recursion is returned.
189   BRILLO_EXPORT Error Rmdir(const std::string& name,
190                             bool recursive = false,
191                             size_t max_depth = kDefaultMaxPathDepth,
192                             bool keep_going = true) WARN_UNUSED_RESULT;
193 
194  private:
195   BRILLO_EXPORT static const char* RootPath;
196 
197   base::ScopedFD fd_;
198 
199   DISALLOW_COPY_AND_ASSIGN(SafeFD);
200 };
201 
202 }  // namespace brillo
203 
204 #endif  // LIBBRILLO_BRILLO_FILES_SAFE_FD_H_
205