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