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