blob: 7771ae1521d017db68ef6c75c87ad1c45921538d [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
#include <bitset>
#include <cstdint>
#include <string>
#include "sandbox/sandbox_export.h"
namespace sandbox {
namespace syscall_broker {
// Recursive: allow everything under |path| (must be a dir).
enum class RecursionOption { kNonRecursive = 0, kRecursive };
// Temporary: file will be unlink'd after opening.
enum class PersistenceOption { kPermanent = 0, kTemporaryOnly };
enum class ReadPermission { kBlockRead = 0, kAllowRead };
enum class WritePermission { kBlockWrite = 0, kAllowWrite };
enum class CreatePermission { kBlockCreate = 0, kAllowCreate };
// Allow stat() for the path and all intermediate dirs.
enum class StatWithIntermediatesPermission {
kBlockStatWithIntermediates = 0,
kAllowStatWithIntermediates
};
enum class InotifyAddWatchWithIntermediatesPermission {
kBlockInotifyAddWatchWithIntermediates = 0,
kAllowInotifyAddWatchWithIntermediates
};
// BrokerFilePermission defines a path for allowlisting.
// Pick the correct static factory method to create a permission.
// CheckOpen and CheckAccess are async signal safe.
// Construction and Destruction are not async signal safe.
// |path| is the path to be allowlisted.
class SANDBOX_EXPORT BrokerFilePermission {
public:
// Movable and copyable.
BrokerFilePermission(BrokerFilePermission&&);
BrokerFilePermission& operator=(BrokerFilePermission&&);
BrokerFilePermission(const BrokerFilePermission&);
BrokerFilePermission& operator=(const BrokerFilePermission&);
~BrokerFilePermission();
static BrokerFilePermission ReadOnly(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kBlockWrite,
CreatePermission::kBlockCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission ReadOnlyRecursive(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kBlockWrite,
CreatePermission::kBlockCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission WriteOnly(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kBlockRead, WritePermission::kAllowWrite,
CreatePermission::kBlockCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission ReadWrite(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kBlockCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission ReadWriteCreate(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kAllowCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission ReadWriteCreateRecursive(
const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kAllowCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission AllPermissions(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kAllowCreate,
StatWithIntermediatesPermission::kAllowStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kAllowInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission AllPermissionsRecursive(const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kRecursive, PersistenceOption::kPermanent,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kAllowCreate,
StatWithIntermediatesPermission::kAllowStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kAllowInotifyAddWatchWithIntermediates);
}
// Temporary files must always be newly created and do not confer rights to
// use pre-existing files of the same name.
static BrokerFilePermission ReadWriteCreateTemporary(
const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kTemporaryOnly,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kAllowCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission ReadWriteCreateTemporaryRecursive(
const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kRecursive, PersistenceOption::kTemporaryOnly,
ReadPermission::kAllowRead, WritePermission::kAllowWrite,
CreatePermission::kAllowCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission StatOnlyWithIntermediateDirs(
const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kBlockRead, WritePermission::kBlockWrite,
CreatePermission::kBlockCreate,
StatWithIntermediatesPermission::kAllowStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kBlockInotifyAddWatchWithIntermediates);
}
static BrokerFilePermission InotifyAddWatchWithIntermediateDirs(
const std::string& path) {
return BrokerFilePermission(
path, RecursionOption::kNonRecursive, PersistenceOption::kPermanent,
ReadPermission::kBlockRead, WritePermission::kBlockWrite,
CreatePermission::kBlockCreate,
StatWithIntermediatesPermission::kBlockStatWithIntermediates,
InotifyAddWatchWithIntermediatesPermission::
kAllowInotifyAddWatchWithIntermediates);
}
// Returns nullptr if |requested_filename| is NOT allowed to be accessed
// by this permission with the given |mode| as per access(2).
//
// Otherwise, the returned C-string is either |requested_filename| in the
// case of a recursive match, or a pointer to the matched path in the
// allowlist if an absolute match.
//
// Async signal safe.
[[nodiscard]] const char* CheckAccess(const char* requested_filename,
int mode) const;
// Returns {nullptr, _} if |requested_filename| is NOT allowed to be opened
// by this permission.
//
// Otherwise, the returned C-string is either |requested_filename| in the
// case of a recursive match, or a pointer to the matched path in the
// allowlist if an absolute match. And, the returned boolean indicates whether
// or not the caller is required to unlink the path after opening.
//
// Async signal safe.
[[nodiscard]] std::pair<const char*, bool> CheckOpen(
const char* requested_filename,
int flags) const;
// Returns nullptr if |requested_filename| is NOT allowed to be stat'd
// by this permission as per stat(2). Differs from CheckAccess()
// in that if create permission is granted to a file, we permit
// stat() on all of its leading components.
//
// Otherwise, the returned C-string is either |requested_filename| in the
// case of a recursive match, or a pointer to the matched path in the
// allowlist if an absolute match.
//
// Async signal safe.
[[nodiscard]] const char* CheckStatWithIntermediates(
const char* requested_filename) const;
// Returns nullptr if |requested_filename| is NOT allowed by this
// permission to be added to an inotify instance's watch list by
// inotify_add_watch(2), with the specific |mask|. Differs from CheckAccess()
// in that if inotify_add_watch permission is granted to a file, we permit
// inotify_add_watch() on all of its leading components.
//
// Otherwise, the returned C-string is either |requested_filename| in the
// case of a recursive match, or a pointer to the matched path in the
// allowlist if an absolute match.
//
// Async signal safe.
[[nodiscard]] const char* CheckInotifyAddWatchWithIntermediates(
const char* requested_filename,
uint32_t mask) const;
private:
friend class BrokerFilePermissionTester;
enum BitPositions {
kRecursiveBitPos = 0,
kTemporaryOnlyBitPos,
kAllowReadBitPos,
kAllowWriteBitPos,
kAllowCreateBitPos,
kAllowStatWithIntermediatesBitPos,
kAllowInotifyAddWatchWithIntermediates,
kMaxValueBitPos = kAllowInotifyAddWatchWithIntermediates
};
// NOTE: Validates the permission and dies if invalid!
BrokerFilePermission(std::string path,
RecursionOption recurse_opt,
PersistenceOption persist_opt,
ReadPermission read_perm,
WritePermission write_perm,
CreatePermission create_perm,
StatWithIntermediatesPermission stat_perm,
InotifyAddWatchWithIntermediatesPermission inotify_perm);
// Allows construction from the raw bitset.
BrokerFilePermission(std::string path, uint64_t flags);
const std::string& path() const { return path_; }
// Returns a serialiazable version of |flags_|.
uint64_t flags() const { return flags_.to_ullong(); }
bool recursive() const { return flags_.test(kRecursiveBitPos); }
bool temporary_only() const { return flags_.test(kTemporaryOnlyBitPos); }
bool allow_read() const { return flags_.test(kAllowReadBitPos); }
bool allow_write() const { return flags_.test(kAllowWriteBitPos); }
bool allow_create() const { return flags_.test(kAllowCreateBitPos); }
bool allow_stat_with_intermediates() const {
return flags_.test(kAllowStatWithIntermediatesBitPos);
}
bool allow_inotify_add_watch_with_intermediates() const {
return flags_.test(kAllowInotifyAddWatchWithIntermediates);
}
// ValidatePath checks |path| and returns true if these conditions are met
// * Greater than 0 length
// * Is an absolute path
// * No trailing slash
// * No /../ path traversal
[[nodiscard]] static bool ValidatePath(const char* path);
// MatchPath returns true if |requested_filename| is covered by this instance
[[nodiscard]] bool MatchPath(const char* requested_filename) const;
// Helper routine for CheckAccess() and CheckStat(). Must be safe to call
// from an async signal context.
[[nodiscard]] const char* CheckAccessInternal(const char* requested_filename,
int mode) const;
// Helper routine for CheckStatWithIntermediates() and
// CheckInotifyAddWatchWithIntermediates() to return true if one of the
// following is true:
// 1. |requested_filename| matches a leading directory of |path_|.
// 2. |can_match_full_path| is true and |path_| == |requested_filename|.
[[nodiscard]] bool CheckIntermediates(const char* requested_filename,
bool can_match_full_path) const;
// Used in by BrokerFilePermissionTester for tests.
static const char* GetErrorMessageForTests();
void DieOnInvalidPermission();
// These are not const to support the move constructor. All methods are marked
// const so the compiler will still enforce no changes outside of the
// constructor.
std::string path_;
std::bitset<kMaxValueBitPos + 1> flags_;
};
} // namespace syscall_broker
} // namespace sandbox
#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_