kernel: Add support for supplementary groups (1/2)

Adds support for supplementary groups internally in the kernel.
No userspace support.
This commit is contained in:
apio 2023-11-22 18:49:40 +01:00
parent e97b61ef16
commit 3ad23eab21
Signed by: apio
GPG Key ID: B8A7D06E42258954
18 changed files with 118 additions and 70 deletions

View File

@ -35,9 +35,9 @@ Result<u64> ScriptLoader::load(AddressSpace* space)
auto& interpreter_path = m_interpreter_cmdline[0];
auto* current = Scheduler::current();
auto interpreter =
TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, current->current_directory, true));
if (!VFS::can_execute(interpreter, current->auth)) return err(EACCES);
auto interpreter = TRY(VFS::resolve_path(interpreter_path.chars(), current->auth, &current->extra_groups,
current->current_directory, true));
if (!VFS::can_execute(interpreter, current->auth, &current->extra_groups)) return err(EACCES);
auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1));
u64 entry = TRY(loader->load(space));

View File

@ -20,7 +20,7 @@ void InitRD::initialize()
static Result<void> vfs_create_dir_if_not_exists(const char* path, mode_t mode)
{
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {});
auto rc = VFS::create_directory(path, mode & (mode_t)~S_IFMT, Credentials {}, nullptr);
if (rc.has_error())
{
if (rc.error() == EEXIST) return {};
@ -37,7 +37,8 @@ Result<void> InitRD::populate_vfs()
{
if (entry.type == TarStream::EntryType::RegularFile)
{
auto file = TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}));
auto file =
TRY(VFS::create_file(entry.name.chars(), entry.mode & (mode_t)~S_IFMT, Credentials {}, nullptr));
file->write(entry.data(), 0, entry.size);
}
else if (entry.type == TarStream::EntryType::Directory)

View File

@ -17,8 +17,9 @@ namespace VFS
static constexpr int MAX_SYMLINKS = 8;
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, SharedPtr<Inode> current_inode,
bool follow_last_symlink, int& symlinks_followed)
Result<SharedPtr<Inode>> resolve_path_impl(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
SharedPtr<Inode> current_inode, bool follow_last_symlink,
int& symlinks_followed)
{
if (symlinks_followed >= MAX_SYMLINKS) return err(ELOOP);
@ -31,7 +32,7 @@ namespace VFS
const char* section;
while (parser.next().try_set_value(section))
{
if (!can_execute(current_inode, auth)) return err(EACCES);
if (!can_execute(current_inode, auth, extra_groups)) return err(EACCES);
current_inode = TRY(current_inode->find(section));
if (current_inode->type() == VFS::InodeType::Symlink && (follow_last_symlink || parser.has_next()))
@ -45,7 +46,8 @@ namespace VFS
symlink_root = parent_inode;
symlinks_followed++;
current_inode = TRY(resolve_path_impl(link.chars(), auth, symlink_root, true, symlinks_followed));
current_inode =
TRY(resolve_path_impl(link.chars(), auth, extra_groups, symlink_root, true, symlinks_followed));
symlinks_followed--;
}
@ -55,8 +57,8 @@ namespace VFS
return current_inode;
}
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
bool follow_last_symlink)
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
SharedPtr<VFS::Inode> working_directory, bool follow_last_symlink)
{
SharedPtr<Inode> current_inode;
@ -66,17 +68,17 @@ namespace VFS
int symlinks_followed = 0;
return resolve_path_impl(path, auth, current_inode, follow_last_symlink, symlinks_followed);
return resolve_path_impl(path, auth, extra_groups, current_inode, follow_last_symlink, symlinks_followed);
}
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
SharedPtr<Inode> working_directory)
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
{
auto parent_path = TRY(PathParser::dirname(path));
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
if (!can_write(parent_inode, auth)) return err(EACCES);
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
auto child_name = TRY(PathParser::basename(path));
@ -86,13 +88,13 @@ namespace VFS
}
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
SharedPtr<Inode> working_directory)
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory)
{
auto parent_path = TRY(PathParser::dirname(path));
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
if (!can_write(parent_inode, auth)) return err(EACCES);
if (!can_write(parent_inode, auth, extra_groups)) return err(EACCES);
auto child_name = TRY(PathParser::basename(path));
@ -133,7 +135,8 @@ namespace VFS
return {};
}
bool can_execute(SharedPtr<Inode> inode, Credentials auth)
// FIXME: Check all three permissions even if the UID or GID match.
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
{
if (auth.euid == 0) return true;
@ -142,10 +145,19 @@ namespace VFS
if (metadata.uid == auth.euid) { return metadata.mode & S_IXUSR; }
if (metadata.gid == auth.egid) { return metadata.mode & S_IXGRP; }
if (extra_groups)
{
for (gid_t group : *extra_groups)
{
if (metadata.gid == group) return metadata.mode & S_IXGRP;
}
}
return metadata.mode & S_IXOTH;
}
bool can_write(SharedPtr<Inode> inode, Credentials auth)
// FIXME: Check all three permissions even if the UID or GID match.
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
{
if (auth.euid == 0) return true;
@ -154,10 +166,19 @@ namespace VFS
if (metadata.uid == auth.euid) { return metadata.mode & S_IWUSR; }
if (metadata.gid == auth.egid) { return metadata.mode & S_IWGRP; }
if (extra_groups)
{
for (gid_t group : *extra_groups)
{
if (metadata.gid == group) return metadata.mode & S_IWGRP;
}
}
return metadata.mode & S_IWOTH;
}
bool can_read(SharedPtr<Inode> inode, Credentials auth)
// FIXME: Check all three permissions even if the UID or GID match.
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups)
{
if (auth.euid == 0) return true;
@ -166,6 +187,14 @@ namespace VFS
if (metadata.uid == auth.euid) { return metadata.mode & S_IRUSR; }
if (metadata.gid == auth.egid) { return metadata.mode & S_IRGRP; }
if (extra_groups)
{
for (gid_t group : *extra_groups)
{
if (metadata.gid == group) return metadata.mode & S_IRGRP;
}
}
return metadata.mode & S_IROTH;
}
@ -203,7 +232,8 @@ namespace VFS
auto new_root_parent = TRY(PathParser::dirname(new_root));
auto new_root_path = TRY(PathParser::basename(new_root));
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, working_directory));
auto new_root_parent_inode =
TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, nullptr, working_directory));
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
if (new_root_inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
@ -215,7 +245,7 @@ namespace VFS
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, working_directory));
auto parent_inode = TRY(resolve_path(parent_path.chars(), Credentials {}, nullptr, working_directory));
auto inode = TRY(parent_inode->find(child.chars()));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
@ -236,7 +266,7 @@ namespace VFS
}
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
SharedPtr<VFS::Inode> working_directory)
const Vector<gid_t>* extra_groups, SharedPtr<VFS::Inode> working_directory)
{
auto parent_path = TRY(PathParser::dirname(path));
auto child = TRY(PathParser::basename(path));
@ -245,7 +275,7 @@ namespace VFS
kdbgln("vfs: Mounting filesystem on target %s", path);
#endif
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
auto inode = TRY(parent_inode->find(child.chars()));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
@ -260,7 +290,8 @@ namespace VFS
return {};
}
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
SharedPtr<VFS::Inode> working_directory)
{
auto parent_path = TRY(PathParser::dirname(path));
auto child = TRY(PathParser::basename(path));
@ -269,7 +300,7 @@ namespace VFS
kinfoln("vfs: Unmounting filesystem on target %s", path);
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
auto inode = TRY(parent_inode->find(child.chars()));
if (!inode->is_mountpoint()) return err(EINVAL);

View File

@ -306,21 +306,23 @@ namespace VFS
virtual ~DeviceInode() = default;
};
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth,
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
SharedPtr<VFS::Inode> working_directory = {},
bool follow_last_symlink = true);
Result<SharedPtr<Inode>> create_directory(const char* path, mode_t mode, Credentials auth,
const Vector<gid_t>* extra_groups,
SharedPtr<VFS::Inode> working_directory = {});
Result<SharedPtr<Inode>> create_file(const char* path, mode_t mode, Credentials auth,
const Vector<gid_t>* extra_groups,
SharedPtr<VFS::Inode> working_directory = {});
Result<void> validate_filename(StringView name);
bool can_execute(SharedPtr<Inode> inode, Credentials auth);
bool can_read(SharedPtr<Inode> inode, Credentials auth);
bool can_write(SharedPtr<Inode> inode, Credentials auth);
bool can_execute(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
bool can_read(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
bool can_write(SharedPtr<Inode> inode, Credentials auth, const Vector<gid_t>* extra_groups);
bool is_setuid(SharedPtr<Inode> inode);
bool is_setgid(SharedPtr<Inode> inode);
bool is_sticky(SharedPtr<Inode> inode);
@ -332,7 +334,8 @@ namespace VFS
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory);
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
SharedPtr<Inode> working_directory = {});
const Vector<gid_t>* extra_groups, SharedPtr<Inode> working_directory = {});
Result<void> umount(const char* path, Credentials auth, SharedPtr<Inode> working_directory = {});
Result<void> umount(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
SharedPtr<Inode> working_directory = {});
}

View File

@ -65,8 +65,8 @@ void oom_thread()
mark_critical(BinaryFormat::init(), "Failed to register initial binary formats");
mark_critical(FSRegistry::init(), "Failed to register initial file systems");
auto init =
mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}), "Can't find init in the initial ramfs!");
auto init = mark_critical(VFS::resolve_path("/bin/preinit", Credentials {}, nullptr),
"Can't find init in the initial ramfs!");
auto init_thread = mark_critical(Scheduler::create_init_process(init, "/bin/preinit"),
"Failed to create PID 1 process for init");

View File

@ -52,14 +52,14 @@ Result<usize> UnixSocket::recv(u8* buf, usize length, int) const
return m_data.dequeue_data(buf, length);
}
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory,
SharedPtr<UnixSocket> socket)
static Result<void> bind_socket_to_fs(const char* path, Credentials auth, const Vector<gid_t>* extra_groups,
SharedPtr<VFS::Inode> working_directory, SharedPtr<UnixSocket> socket)
{
auto parent_path = TRY(PathParser::dirname(path));
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, working_directory));
auto parent_inode = TRY(VFS::resolve_path(parent_path.chars(), auth, extra_groups, working_directory));
if (!VFS::can_write(parent_inode, auth)) return err(EACCES);
if (!VFS::can_write(parent_inode, auth, extra_groups)) return err(EACCES);
auto child_name = TRY(PathParser::basename(path));
@ -91,7 +91,8 @@ Result<void> UnixSocket::bind(struct sockaddr* addr, socklen_t addrlen)
m_metadata.uid = current->auth.euid;
m_metadata.gid = current->auth.egid;
auto rc = bind_socket_to_fs(path.chars(), current->auth, current->current_directory, SharedPtr<Socket> { this });
auto rc = bind_socket_to_fs(path.chars(), current->auth, &current->extra_groups, current->current_directory,
SharedPtr<Socket> { this });
if (rc.has_error())
{
if (rc.error() == EEXIST) return err(EADDRINUSE);
@ -123,10 +124,11 @@ Result<void> UnixSocket::connect(Registers* regs, int flags, struct sockaddr* ad
auto* current = Scheduler::current();
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
auto inode =
TRY(VFS::resolve_path(path.chars(), current->auth, &current->extra_groups, current->current_directory));
if (inode->type() != VFS::InodeType::Socket)
return err(ENOTSOCK); // FIXME: POSIX doesn't say what error to return here?
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
if (!VFS::can_write(inode, current->auth, &current->extra_groups)) return err(EACCES);
auto socket = (SharedPtr<UnixSocket>)inode;
if (socket->m_state != State::Listening) return err(ECONNREFUSED);

View File

@ -14,10 +14,10 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
if (PathParser::is_absolute(path.view()))
{
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth));
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, &current->extra_groups));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
if (!VFS::can_execute(inode, current->auth, &current->extra_groups)) return err(EACCES);
inode->add_handle();
if (current->current_directory) current->current_directory->remove_handle();
@ -29,10 +29,11 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
}
else
{
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
SharedPtr<VFS::Inode> inode =
TRY(VFS::resolve_path(path.chars(), current->auth, &current->extra_groups, current->current_directory));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
if (!VFS::can_execute(inode, current->auth, &current->extra_groups)) return err(EACCES);
auto old_wdir = current->current_directory_path.view();

View File

@ -68,9 +68,10 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
TRY(check_pledge(current, Promise::p_exec));
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
auto inode =
TRY(VFS::resolve_path(path.chars(), current->auth, &current->extra_groups, current->current_directory));
if (!VFS::can_execute(inode, current->auth)) return err(EACCES);
if (!VFS::can_execute(inode, current->auth, &current->extra_groups)) return err(EACCES);
#ifdef EXEC_DEBUG
kdbgln("exec: attempting to replace current image with %s", path.chars());
@ -159,6 +160,8 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
TRY(check_pledge(current, Promise::p_proc));
auto extra_groups = TRY(current->extra_groups.shallow_copy());
auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); });
memcpy(&current->regs, regs, sizeof(*regs));
@ -183,6 +186,7 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
thread->controlling_terminal = current->controlling_terminal;
thread->pgid = current->pgid;
thread->sid = current->sid;
thread->extra_groups = move(extra_groups);
thread->virtual_clock.set_resolution(1'000'000);
thread->profiling_clock.set_resolution(1'000'000);

View File

@ -259,9 +259,10 @@ Result<u64> sys_truncate(Registers*, SyscallArgs args)
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_wpath));
auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory));
auto inode =
TRY(VFS::resolve_path(path.chars(), current->auth, &current->extra_groups, current->current_directory));
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
if (!VFS::can_write(inode, current->auth, &current->extra_groups)) return err(EACCES);
TRY(inode->truncate(length));
@ -308,8 +309,8 @@ Result<u64> sys_utimensat(Registers*, SyscallArgs args)
if (allow_write_access)
{
if (!VFS::can_write(inode, current->auth) && current->auth.euid != inode->metadata().uid &&
current->auth.euid != 0)
if (!VFS::can_write(inode, current->auth, &current->extra_groups) &&
current->auth.euid != inode->metadata().uid && current->auth.euid != 0)
return err(EACCES);
}
else if (current->auth.euid != inode->metadata().uid && current->auth.euid != 0)

View File

@ -23,7 +23,7 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
kinfoln("unlinkat: remove %s from directory %s, dirfd is %d", basename.chars(), dirname.chars(), dirfd);
auto inode = TRY(current->resolve_atfile(dirfd, dirname, false, false));
if (!VFS::can_write(inode, current->auth)) return err(EACCES);
if (!VFS::can_write(inode, current->auth, &current->extra_groups)) return err(EACCES);
auto child = TRY(inode->find(basename.chars()));
if (flags == AT_REMOVEDIR && child->type() != VFS::InodeType::Directory) return err(ENOTDIR);
@ -52,7 +52,7 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
if (!VFS::can_write(parent_inode, current->auth, &current->extra_groups)) return err(EACCES);
auto child_name = TRY(PathParser::basename(linkpath.view()));
@ -115,7 +115,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
if (target->fs() != parent_inode->fs()) return err(EXDEV);
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
if (!VFS::can_write(parent_inode, current->auth, &current->extra_groups)) return err(EACCES);
auto child_name = TRY(PathParser::basename(newpath.view()));

View File

@ -13,8 +13,8 @@ Result<u64> sys_mkdir(Registers*, SyscallArgs args)
Thread* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_cpath));
auto inode =
TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current->auth, current->current_directory));
auto inode = TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current->auth, &current->extra_groups,
current->current_directory));
auto metadata = inode->metadata();
metadata.uid = current->auth.euid;
metadata.gid = current->auth.egid;

View File

@ -19,7 +19,8 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
if (current->auth.euid != 0) return err(EPERM);
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
auto inode = TRY(VFS::resolve_path(source.chars(), current->auth, current->current_directory));
auto inode =
TRY(VFS::resolve_path(source.chars(), current->auth, &current->extra_groups, current->current_directory));
if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK);
dev_t device_id = inode->metadata().devid;
return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id)));
@ -40,7 +41,7 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
fs = TRY(factory(device));
}
TRY(VFS::mount(target.chars(), fs, current->auth, current->current_directory));
TRY(VFS::mount(target.chars(), fs, current->auth, &current->extra_groups, current->current_directory));
return 0;
}
@ -53,7 +54,7 @@ Result<u64> sys_umount(Registers*, SyscallArgs args)
TRY(check_pledge(current, Promise::p_mount));
if (current->auth.euid != 0) return err(EPERM);
TRY(VFS::umount(target.chars(), current->auth, current->current_directory));
TRY(VFS::umount(target.chars(), current->auth, &current->extra_groups, current->current_directory));
return 0;
}

View File

@ -44,7 +44,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
{
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
{
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current->auth, parent_inode));
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current->auth, &current->extra_groups,
parent_inode));
// FIXME: Pass these in create_file().
auto metadata = inode->metadata();
metadata.uid = current->auth.euid;
@ -58,8 +59,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
return err(EEXIST);
else
{
if ((flags & O_RDONLY) && !VFS::can_read(inode, current->auth)) return err(EACCES);
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth)) return err(EACCES);
if ((flags & O_RDONLY) && !VFS::can_read(inode, current->auth, &current->extra_groups)) return err(EACCES);
if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth, &current->extra_groups)) return err(EACCES);
}
inode = TRY(inode->open());

View File

@ -81,9 +81,9 @@ Result<u64> sys_faccessat(Registers*, SyscallArgs args)
auto inode = TRY(current->resolve_atfile(dirfd, path, false, true));
if ((amode & R_OK) && !VFS::can_read(inode, creds)) return err(EACCES);
if ((amode & W_OK) && !VFS::can_write(inode, creds)) return err(EACCES);
if ((amode & X_OK) && !VFS::can_execute(inode, creds)) return err(EACCES);
if ((amode & R_OK) && !VFS::can_read(inode, creds, &current->extra_groups)) return err(EACCES);
if ((amode & W_OK) && !VFS::can_write(inode, creds, &current->extra_groups)) return err(EACCES);
if ((amode & X_OK) && !VFS::can_execute(inode, creds, &current->extra_groups)) return err(EACCES);
// Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed
// otherwise.

View File

@ -156,6 +156,7 @@ namespace Scheduler
thread->pgid = 1;
thread->cmdline = name;
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
thread->extra_groups = {};
Vector<String> args;
auto name_string = TRY(String::from_cstring(name));

View File

@ -64,10 +64,11 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
if (parent_inode) *parent_inode = this->current_directory;
if (PathParser::is_absolute(path.view()))
return VFS::resolve_path(path.chars(), this->auth, {}, follow_last_symlink);
return VFS::resolve_path(path.chars(), this->auth, &this->extra_groups, {}, follow_last_symlink);
if (dirfd == AT_FDCWD)
return VFS::resolve_path(path.chars(), this->auth, this->current_directory, follow_last_symlink);
return VFS::resolve_path(path.chars(), this->auth, &this->extra_groups, this->current_directory,
follow_last_symlink);
auto descriptor = TRY(resolve_fd(dirfd));
@ -75,7 +76,7 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
if (path.is_empty() && allow_empty_path) return descriptor->inode();
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode(), follow_last_symlink);
return VFS::resolve_path(path.chars(), this->auth, &this->extra_groups, descriptor->inode(), follow_last_symlink);
}
[[noreturn]] void Thread::exit_and_signal_parent(int _status)

View File

@ -54,6 +54,7 @@ struct Thread : public LinkedListNode<Thread>
pid_t sid { 0 };
Credentials auth;
Vector<gid_t> extra_groups;
u64 user_ticks_self = 0;
u64 kernel_ticks_self = 0;

View File

@ -252,7 +252,7 @@ template <typename T> class Vector
Result<void> resize(usize new_capacity)
{
if (new_capacity < m_capacity) return {};
if (new_capacity <= m_capacity) return {};
const usize new_byte_capacity = new_capacity * sizeof(T);