diff --git a/headers/os/drivers/fs_interface.h b/headers/os/drivers/fs_interface.h index 96541ffde4..a3d6c5a9a2 100644 --- a/headers/os/drivers/fs_interface.h +++ b/headers/os/drivers/fs_interface.h @@ -331,8 +331,11 @@ extern status_t unremove_vnode(fs_volume* volume, ino_t vnodeID); extern status_t get_vnode_removed(fs_volume* volume, ino_t vnodeID, bool* _removed); extern fs_volume* volume_for_vnode(fs_vnode* vnode); + extern status_t check_access_permissions(int accessMode, mode_t mode, gid_t nodeGroupID, uid_t nodeUserID); +extern status_t check_write_stat_permissions(gid_t nodeGroupID, uid_t nodeUserID, + mode_t nodeMode, uint32 mask, const struct stat* stat); extern status_t read_pages(int fd, off_t pos, const struct iovec* vecs, size_t count, size_t* _numBytes); diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index 36753bb049..6b3925463d 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -950,6 +950,7 @@ #define get_vnode_removed fssh_get_vnode_removed #define volume_for_vnode fssh_volume_for_vnode #define check_access_permissions fssh_check_access_permissions +#define check_write_stat_permissions fssh_check_write_stat_permissions #define read_pages fssh_read_pages #define write_pages fssh_write_pages #define read_file_io_vec_pages fssh_read_file_io_vec_pages diff --git a/headers/private/fs_shell/fssh_fs_interface.h b/headers/private/fs_shell/fssh_fs_interface.h index 81e4ee3d27..163673665d 100644 --- a/headers/private/fs_shell/fssh_fs_interface.h +++ b/headers/private/fs_shell/fssh_fs_interface.h @@ -364,6 +364,9 @@ extern fssh_fs_volume* fssh_volume_for_vnode(fssh_fs_vnode *vnode); extern fssh_status_t fssh_check_access_permissions(int accessMode, fssh_mode_t mode, fssh_gid_t nodeGroupID, fssh_uid_t nodeUserID); +extern fssh_status_t fssh_check_write_stat_permissions(fssh_gid_t nodeGroupID, + fssh_uid_t nodeUserID, fssh_mode_t nodeMode, uint32_t mask, + const struct fssh_stat* stat); extern fssh_status_t fssh_read_pages(int fd, fssh_off_t pos, const struct fssh_iovec *vecs, fssh_size_t count, diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 96dc705eff..50bba8fe84 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -4007,6 +4007,53 @@ check_access_permissions(int accessMode, mode_t mode, gid_t nodeGroupID, } +extern "C" status_t +check_write_stat_permissions(gid_t nodeGroupID, uid_t nodeUserID, mode_t nodeMode, + uint32 mask, const struct stat* stat) +{ + uid_t uid = geteuid(); + + // root has all permissions + if (uid == 0) + return B_OK; + + const bool hasWriteAccess = check_access_permissions(W_OK, + nodeMode, nodeGroupID, nodeUserID) == B_OK; + + if ((mask & B_STAT_SIZE) != 0) { + if (!hasWriteAccess) + return B_NOT_ALLOWED; + } + + if ((mask & B_STAT_UID) != 0) { + if (nodeUserID == uid && stat->st_uid == uid) { + // No change. + } else + return B_NOT_ALLOWED; + } + + if ((mask & B_STAT_GID) != 0) { + if (nodeUserID != uid) + return B_NOT_ALLOWED; + + if (!is_in_group(thread_get_current_thread()->team, stat->st_gid)) + return B_NOT_ALLOWED; + } + + if ((mask & B_STAT_MODE) != 0) { + if (nodeUserID != uid) + return B_NOT_ALLOWED; + } + + if ((mask & (B_STAT_CREATION_TIME | B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME)) != 0) { + if (!hasWriteAccess && nodeUserID != uid) + return B_NOT_ALLOWED; + } + + return B_OK; +} + + #if 0 extern "C" status_t read_pages(int fd, off_t pos, const iovec* vecs, size_t count, diff --git a/src/tools/fs_shell/vfs.cpp b/src/tools/fs_shell/vfs.cpp index 388e976215..57863bc027 100644 --- a/src/tools/fs_shell/vfs.cpp +++ b/src/tools/fs_shell/vfs.cpp @@ -24,6 +24,7 @@ #include "fssh_fs_volume.h" #include "fssh_kernel_export.h" #include "fssh_module.h" +#include "fssh_node_monitor.h" #include "fssh_stat.h" #include "fssh_stdio.h" #include "fssh_string.h" @@ -2167,6 +2168,54 @@ fssh_check_access_permissions(int accessMode, fssh_mode_t mode, } +extern "C" fssh_status_t +fssh_check_write_stat_permissions(fssh_gid_t nodeGroupID, fssh_uid_t nodeUserID, + fssh_mode_t nodeMode, uint32_t mask, const struct fssh_stat* stat) +{ + uid_t uid = fssh_geteuid(); + + // root has all permissions + if (uid == 0) + return FSSH_B_OK; + + const bool hasWriteAccess = fssh_check_access_permissions(FSSH_W_OK, + nodeMode, nodeGroupID, nodeUserID) == FSSH_B_OK; + + if ((mask & FSSH_B_STAT_SIZE) != 0) { + if (!hasWriteAccess) + return FSSH_B_NOT_ALLOWED; + } + + if ((mask & FSSH_B_STAT_UID) != 0) { + if (nodeUserID == uid && stat->fssh_st_uid == uid) { + // No change. + } else + return FSSH_B_NOT_ALLOWED; + } + + if ((mask & FSSH_B_STAT_GID) != 0) { + if (nodeUserID != uid) + return FSSH_B_NOT_ALLOWED; + + if (fssh_getegid() != stat->fssh_st_gid) + return FSSH_B_NOT_ALLOWED; + } + + if ((mask & FSSH_B_STAT_MODE) != 0) { + if (nodeUserID != uid) + return FSSH_B_NOT_ALLOWED; + } + + if ((mask & (FSSH_B_STAT_CREATION_TIME | FSSH_B_STAT_MODIFICATION_TIME + | FSSH_B_STAT_CHANGE_TIME)) != 0) { + if (!hasWriteAccess && nodeUserID != uid) + return FSSH_B_NOT_ALLOWED; + } + + return FSSH_B_OK; +} + + //! Works directly on the host's file system extern "C" fssh_status_t fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,