fuse permission fixes

This commit is contained in:
Scott E. Graves 2024-10-21 11:52:21 -05:00
parent bd25904371
commit 841d57cf13
4 changed files with 149 additions and 35 deletions

View File

@ -170,6 +170,9 @@ renterd
richtext richtext
rocksdb_library rocksdb_library
rpcrt4 rpcrt4
s_igid
s_isvtx
s_iuid
sddl_revision_1 sddl_revision_1
secp256k1 secp256k1
secur32 secur32

View File

@ -67,6 +67,11 @@ auto fuse_drive::chmod_impl(std::string api_path, mode_t mode,
auto fuse_drive::chmod_impl(std::string api_path, mode_t mode) -> api_error { auto fuse_drive::chmod_impl(std::string api_path, mode_t mode) -> api_error {
#endif // FUSE_USE_VERSION >= 30 #endif // FUSE_USE_VERSION >= 30
return check_and_perform(api_path, X_OK, [&](api_meta_map &) -> api_error { return check_and_perform(api_path, X_OK, [&](api_meta_map &) -> api_error {
if ((mode & (S_IGID | S_IUID | S_ISVTX) != 0) &&
(get_effective_uid() != 0)) {
return api_error::permission_denied;
}
return provider_.set_item_meta(api_path, META_MODE, std::to_string(mode)); return provider_.set_item_meta(api_path, META_MODE, std::to_string(mode));
}); });
} }
@ -86,23 +91,24 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
if (get_effective_uid() != 0) { if (get_effective_uid() != 0) {
return api_error::permission_denied; return api_error::permission_denied;
} }
meta[META_UID] = std::to_string(uid); meta[META_UID] = std::to_string(uid);
} }
if (gid != static_cast<gid_t>(-1)) { if (gid != static_cast<gid_t>(-1)) {
if (get_effective_uid() != 0) { if (get_effective_uid() != 0 &&
if (not utils::is_uid_member_of_group(get_effective_uid(), gid)) { not utils::is_uid_member_of_group(get_effective_uid(), gid)) {
return api_error::permission_denied; return api_error::permission_denied;
} }
}
meta[META_GID] = std::to_string(gid); meta[META_GID] = std::to_string(gid);
} }
if (not meta.empty()) { if (meta.empty()) {
return provider_.set_item_meta(api_path, meta); return api_error::success;
} }
return api_error::success; return provider_.set_item_meta(api_path, meta);
}); });
} }

View File

@ -32,16 +32,22 @@
#include "providers/encrypt/encrypt_provider.hpp" #include "providers/encrypt/encrypt_provider.hpp"
#include "providers/s3/s3_provider.hpp" #include "providers/s3/s3_provider.hpp"
#include "providers/sia/sia_provider.hpp" #include "providers/sia/sia_provider.hpp"
#include "types/repertory.hpp"
#include "utils/event_capture.hpp"
#include "utils/file_utils.hpp" #include "utils/file_utils.hpp"
#include "utils/path.hpp" #include "utils/path.hpp"
#include "utils/utils.hpp"
#if !defined(ACCESSPERMS) #if !defined(ACCESSPERMS)
#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO) /* 0777 */ #define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO) /* 0777 */
#endif #endif
namespace repertory { namespace {
inline constexpr const auto SLEEP_SECONDS{1.5s}; std::atomic<std::size_t> idx{0U};
constexpr const auto SLEEP_SECONDS{1.5s};
} // namespace
namespace repertory {
template <typename provider_t> class fuse_test : public ::testing::Test { template <typename provider_t> class fuse_test : public ::testing::Test {
public: public:
static std::string cfg_directory; static std::string cfg_directory;
@ -147,7 +153,10 @@ protected:
public: public:
static auto create_file_and_test(std::string name) -> std::string { static auto create_file_and_test(std::string name) -> std::string {
auto file_path = utils::path::combine(mount_location, {name}); auto file_path =
utils::path::combine(mount_location, {
name + std::to_string(++idx),
});
auto fd = auto fd =
open(file_path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP); open(file_path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP);
@ -166,6 +175,19 @@ public:
return file_path; return file_path;
} }
static auto create_root_file(std::string name) -> std::string {
auto file_path = create_file_and_test(name);
auto api_path =
utils::path::create_api_path(utils::path::strip_to_filename(file_path));
provider->set_item_meta(api_path, {
{META_UID, "0"},
{META_GID, "0"},
});
return file_path;
}
static void execute_mount(auto &&drive_args) { static void execute_mount(auto &&drive_args) {
auto mount_cmd = "./repertory -dd \"" + config->get_data_directory() + auto mount_cmd = "./repertory -dd \"" + config->get_data_directory() +
"\"" + " " + utils::string::join(drive_args, ' '); "\"" + " " + utils::string::join(drive_args, ' ');
@ -200,6 +222,18 @@ public:
EXPECT_FALSE(utils::file::directory(file_path).exists()); EXPECT_FALSE(utils::file::directory(file_path).exists());
EXPECT_FALSE(utils::file::file(file_path).exists()); EXPECT_FALSE(utils::file::file(file_path).exists());
} }
static void unlink_root_file(const std::string &file_path) {
auto api_path =
utils::path::create_api_path(utils::path::strip_to_filename(file_path));
provider->set_item_meta(api_path, {
{META_UID, std::to_string(getuid())},
{META_GID, std::to_string(getgid())},
});
unlink_file_and_test(file_path);
}
}; };
template <typename provider_t> template <typename provider_t>

View File

@ -19,22 +19,10 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#if 0
#if !defined(_WIN32) #if !defined(_WIN32)
#include "fixtures/fuse_fixture.hpp" #include "fixtures/fuse_fixture.hpp"
// #include "app_config.hpp"
// #include "comm/curl/curl_comm.hpp"
// #include "drives/fuse/fuse_drive.hpp"
// #include "platform/platform.hpp"
// #include "providers/s3/s3_provider.hpp"
// #include "providers/sia/sia_provider.hpp"
// #include "types/repertory.hpp"
// #include "utils/event_capture.hpp"
// #include "utils/file_utils.hpp"
// #include "utils/utils.hpp"
namespace repertory { namespace repertory {
// static void rmdir_and_test(const std::string &directory_path) { // static void rmdir_and_test(const std::string &directory_path) {
// std::cout << __FUNCTION__ << std::endl; // std::cout << __FUNCTION__ << std::endl;
@ -529,7 +517,7 @@ namespace repertory {
TYPED_TEST_CASE(fuse_test, fuse_provider_types); TYPED_TEST_CASE(fuse_test, fuse_provider_types);
TYPED_TEST(fuse_test, chmod) { TYPED_TEST(fuse_test, can_chmod_if_owner) {
auto file_path = this->create_file_and_test("chmod_test"); auto file_path = this->create_file_and_test("chmod_test");
EXPECT_EQ(0, chmod(file_path.c_str(), S_IRUSR | S_IWUSR)); EXPECT_EQ(0, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
std::this_thread::sleep_for(SLEEP_SECONDS); std::this_thread::sleep_for(SLEEP_SECONDS);
@ -538,27 +526,110 @@ TYPED_TEST(fuse_test, chmod) {
stat64(file_path.c_str(), &unix_st); stat64(file_path.c_str(), &unix_st);
EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR), EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR),
ACCESSPERMS & unix_st.st_mode); ACCESSPERMS & unix_st.st_mode);
this->unlink_file_and_test(file_path); this->unlink_file_and_test(file_path);
} }
TYPED_TEST(fuse_test, chown) { TYPED_TEST(fuse_test, can_not_chmod_if_not_owner) {
auto file_path = this->create_root_file("chmod_test");
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
EXPECT_EQ(EPERM, errno);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setgid_if_not_root) {
auto file_path = this->create_file_and_test("chown_test");
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_IGID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setuid_if_not_root) {
auto file_path = this->create_file_and_test("chown_test");
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_IUID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_set_sticky_if_not_root) {
auto file_path = this->create_file_and_test("chown_test");
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISVTX));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_chown_group_if_owner_and_a_member_of_the_group) {
auto file_path = this->create_file_and_test("chown_test"); auto file_path = this->create_file_and_test("chown_test");
EXPECT_EQ(0, chown(file_path.c_str(), static_cast<uid_t>(-1), 0));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st{}; struct stat64 unix_st{};
stat64(file_path.c_str(), &unix_st); EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(0U, unix_st.st_gid);
EXPECT_EQ(0, chown(file_path.c_str(), 0, static_cast<gid_t>(-1))); EXPECT_EQ(0, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
std::this_thread::sleep_for(SLEEP_SECONDS); std::this_thread::sleep_for(SLEEP_SECONDS);
stat64(file_path.c_str(), &unix_st); struct stat64 unix_st2{};
EXPECT_EQ(0U, unix_st.st_gid); stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(getgid(), unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test,
can_not_chown_group_if_owner_but_not_a_member_of_the_group) {
auto file_path = this->create_file_and_test("chown_test");
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), 0));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_group_if_not_the_owner) {
auto file_path = this->create_root_file("chown_test");
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_user_if_not_root) {
auto file_path = this->create_file_and_test("chown_test");
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), 0, static_cast<gid_t>(-1)));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path); this->unlink_file_and_test(file_path);
} }
} // namespace repertory } // namespace repertory
#endif // !defined(_WIN32) #endif // !defined(_WIN32)
#endif // 0