You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
7.3 KiB
205 lines
7.3 KiB
// 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_
|