kernel/fs: Introduce check_write_stat_permissions utility method.
Like check_access_permissions() but for write_stat() hook instead of access(). Basic logic copied from BFS, but with the UID/GID logic rewritten to better conform to behavior on other OSes: notably, we allow chown() requests that keep the UID the same to go through, same as Linux seems to, which should fix #19666. We now also validate that the GID is one of the current team's groups.
This commit is contained in:
@@ -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,
|
extern status_t get_vnode_removed(fs_volume* volume, ino_t vnodeID,
|
||||||
bool* _removed);
|
bool* _removed);
|
||||||
extern fs_volume* volume_for_vnode(fs_vnode* vnode);
|
extern fs_volume* volume_for_vnode(fs_vnode* vnode);
|
||||||
|
|
||||||
extern status_t check_access_permissions(int accessMode, mode_t mode,
|
extern status_t check_access_permissions(int accessMode, mode_t mode,
|
||||||
gid_t nodeGroupID, uid_t nodeUserID);
|
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,
|
extern status_t read_pages(int fd, off_t pos, const struct iovec* vecs,
|
||||||
size_t count, size_t* _numBytes);
|
size_t count, size_t* _numBytes);
|
||||||
|
|||||||
@@ -950,6 +950,7 @@
|
|||||||
#define get_vnode_removed fssh_get_vnode_removed
|
#define get_vnode_removed fssh_get_vnode_removed
|
||||||
#define volume_for_vnode fssh_volume_for_vnode
|
#define volume_for_vnode fssh_volume_for_vnode
|
||||||
#define check_access_permissions fssh_check_access_permissions
|
#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 read_pages fssh_read_pages
|
||||||
#define write_pages fssh_write_pages
|
#define write_pages fssh_write_pages
|
||||||
#define read_file_io_vec_pages fssh_read_file_io_vec_pages
|
#define read_file_io_vec_pages fssh_read_file_io_vec_pages
|
||||||
|
|||||||
@@ -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,
|
extern fssh_status_t fssh_check_access_permissions(int accessMode,
|
||||||
fssh_mode_t mode, fssh_gid_t nodeGroupID,
|
fssh_mode_t mode, fssh_gid_t nodeGroupID,
|
||||||
fssh_uid_t nodeUserID);
|
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,
|
extern fssh_status_t fssh_read_pages(int fd, fssh_off_t pos,
|
||||||
const struct fssh_iovec *vecs, fssh_size_t count,
|
const struct fssh_iovec *vecs, fssh_size_t count,
|
||||||
|
|||||||
@@ -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
|
#if 0
|
||||||
extern "C" status_t
|
extern "C" status_t
|
||||||
read_pages(int fd, off_t pos, const iovec* vecs, size_t count,
|
read_pages(int fd, off_t pos, const iovec* vecs, size_t count,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "fssh_fs_volume.h"
|
#include "fssh_fs_volume.h"
|
||||||
#include "fssh_kernel_export.h"
|
#include "fssh_kernel_export.h"
|
||||||
#include "fssh_module.h"
|
#include "fssh_module.h"
|
||||||
|
#include "fssh_node_monitor.h"
|
||||||
#include "fssh_stat.h"
|
#include "fssh_stat.h"
|
||||||
#include "fssh_stdio.h"
|
#include "fssh_stdio.h"
|
||||||
#include "fssh_string.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
|
//! Works directly on the host's file system
|
||||||
extern "C" fssh_status_t
|
extern "C" fssh_status_t
|
||||||
fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
|
fssh_read_pages(int fd, fssh_off_t pos, const fssh_iovec *vecs,
|
||||||
|
|||||||
Reference in New Issue
Block a user