diff --git a/kernel/src/binfmt/Script.cpp b/kernel/src/binfmt/Script.cpp index f9c5f3d8..fe56af83 100644 --- a/kernel/src/binfmt/Script.cpp +++ b/kernel/src/binfmt/Script.cpp @@ -35,9 +35,9 @@ Result 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, ¤t->extra_groups, + current->current_directory, true)); + if (!VFS::can_execute(interpreter, current->auth, ¤t->extra_groups)) return err(EACCES); auto loader = TRY(BinaryFormat::create_loader(interpreter, m_recursion_level + 1)); u64 entry = TRY(loader->load(space)); diff --git a/kernel/src/fs/InitRD.cpp b/kernel/src/fs/InitRD.cpp index 026ad603..0cf712c5 100644 --- a/kernel/src/fs/InitRD.cpp +++ b/kernel/src/fs/InitRD.cpp @@ -20,7 +20,7 @@ void InitRD::initialize() static Result 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 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) diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index fd5c67ae..f809ec20 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -17,8 +17,9 @@ namespace VFS static constexpr int MAX_SYMLINKS = 8; - Result> resolve_path_impl(const char* path, Credentials auth, SharedPtr current_inode, - bool follow_last_symlink, int& symlinks_followed) + Result> resolve_path_impl(const char* path, Credentials auth, const Vector* extra_groups, + SharedPtr 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> resolve_path(const char* path, Credentials auth, SharedPtr working_directory, - bool follow_last_symlink) + Result> resolve_path(const char* path, Credentials auth, const Vector* extra_groups, + SharedPtr working_directory, bool follow_last_symlink) { SharedPtr 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> create_directory(const char* path, mode_t mode, Credentials auth, - SharedPtr working_directory) + const Vector* extra_groups, SharedPtr 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> create_file(const char* path, mode_t mode, Credentials auth, - SharedPtr working_directory) + const Vector* extra_groups, SharedPtr 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, Credentials auth) + // FIXME: Check all three permissions even if the UID or GID match. + bool can_execute(SharedPtr inode, Credentials auth, const Vector* 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, Credentials auth) + // FIXME: Check all three permissions even if the UID or GID match. + bool can_write(SharedPtr inode, Credentials auth, const Vector* 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, Credentials auth) + // FIXME: Check all three permissions even if the UID or GID match. + bool can_read(SharedPtr inode, Credentials auth, const Vector* 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 mount(const char* path, SharedPtr fs, Credentials auth, - SharedPtr working_directory) + const Vector* extra_groups, SharedPtr 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 umount(const char* path, Credentials auth, SharedPtr working_directory) + Result umount(const char* path, Credentials auth, const Vector* extra_groups, + SharedPtr 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); diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index ad852240..96349292 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -306,21 +306,23 @@ namespace VFS virtual ~DeviceInode() = default; }; - Result> resolve_path(const char* path, Credentials auth, + Result> resolve_path(const char* path, Credentials auth, const Vector* extra_groups, SharedPtr working_directory = {}, bool follow_last_symlink = true); Result> create_directory(const char* path, mode_t mode, Credentials auth, + const Vector* extra_groups, SharedPtr working_directory = {}); Result> create_file(const char* path, mode_t mode, Credentials auth, + const Vector* extra_groups, SharedPtr working_directory = {}); Result validate_filename(StringView name); - bool can_execute(SharedPtr inode, Credentials auth); - bool can_read(SharedPtr inode, Credentials auth); - bool can_write(SharedPtr inode, Credentials auth); + bool can_execute(SharedPtr inode, Credentials auth, const Vector* extra_groups); + bool can_read(SharedPtr inode, Credentials auth, const Vector* extra_groups); + bool can_write(SharedPtr inode, Credentials auth, const Vector* extra_groups); bool is_setuid(SharedPtr inode); bool is_setgid(SharedPtr inode); bool is_sticky(SharedPtr inode); @@ -332,7 +334,8 @@ namespace VFS Result mount_root(SharedPtr fs); Result pivot_root(const char* new_root, const char* put_old, SharedPtr working_directory); Result mount(const char* path, SharedPtr fs, Credentials auth, - SharedPtr working_directory = {}); + const Vector* extra_groups, SharedPtr working_directory = {}); - Result umount(const char* path, Credentials auth, SharedPtr working_directory = {}); + Result umount(const char* path, Credentials auth, const Vector* extra_groups, + SharedPtr working_directory = {}); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 19838856..76de5daa 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -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"); diff --git a/kernel/src/net/UnixSocket.cpp b/kernel/src/net/UnixSocket.cpp index d44fc1fe..bb514d2a 100644 --- a/kernel/src/net/UnixSocket.cpp +++ b/kernel/src/net/UnixSocket.cpp @@ -52,14 +52,14 @@ Result UnixSocket::recv(u8* buf, usize length, int) const return m_data.dequeue_data(buf, length); } -static Result bind_socket_to_fs(const char* path, Credentials auth, SharedPtr working_directory, - SharedPtr socket) +static Result bind_socket_to_fs(const char* path, Credentials auth, const Vector* extra_groups, + SharedPtr working_directory, SharedPtr 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 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 { this }); + auto rc = bind_socket_to_fs(path.chars(), current->auth, ¤t->extra_groups, current->current_directory, + SharedPtr { this }); if (rc.has_error()) { if (rc.error() == EEXIST) return err(EADDRINUSE); @@ -123,10 +124,11 @@ Result 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, ¤t->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, ¤t->extra_groups)) return err(EACCES); auto socket = (SharedPtr)inode; if (socket->m_state != State::Listening) return err(ECONNREFUSED); diff --git a/kernel/src/sys/chdir.cpp b/kernel/src/sys/chdir.cpp index 7523a889..70baeda5 100644 --- a/kernel/src/sys/chdir.cpp +++ b/kernel/src/sys/chdir.cpp @@ -14,10 +14,10 @@ Result sys_chdir(Registers*, SyscallArgs args) if (PathParser::is_absolute(path.view())) { - SharedPtr inode = TRY(VFS::resolve_path(path.chars(), current->auth)); + SharedPtr inode = TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->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, ¤t->extra_groups)) return err(EACCES); inode->add_handle(); if (current->current_directory) current->current_directory->remove_handle(); @@ -29,10 +29,11 @@ Result sys_chdir(Registers*, SyscallArgs args) } else { - SharedPtr inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory)); + SharedPtr inode = + TRY(VFS::resolve_path(path.chars(), current->auth, ¤t->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, ¤t->extra_groups)) return err(EACCES); auto old_wdir = current->current_directory_path.view(); diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 534e5aaa..fa81d67c 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -68,9 +68,10 @@ Result 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, ¤t->extra_groups, current->current_directory)); - if (!VFS::can_execute(inode, current->auth)) return err(EACCES); + if (!VFS::can_execute(inode, current->auth, ¤t->extra_groups)) return err(EACCES); #ifdef EXEC_DEBUG kdbgln("exec: attempting to replace current image with %s", path.chars()); @@ -159,6 +160,8 @@ Result 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(¤t->regs, regs, sizeof(*regs)); @@ -183,6 +186,7 @@ Result 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); diff --git a/kernel/src/sys/file.cpp b/kernel/src/sys/file.cpp index 4ba20d5d..8a768b46 100644 --- a/kernel/src/sys/file.cpp +++ b/kernel/src/sys/file.cpp @@ -259,9 +259,10 @@ Result 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, ¤t->extra_groups, current->current_directory)); - if (!VFS::can_write(inode, current->auth)) return err(EACCES); + if (!VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES); TRY(inode->truncate(length)); @@ -308,8 +309,8 @@ Result 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, ¤t->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) diff --git a/kernel/src/sys/link.cpp b/kernel/src/sys/link.cpp index e7e36057..909aa460 100644 --- a/kernel/src/sys/link.cpp +++ b/kernel/src/sys/link.cpp @@ -23,7 +23,7 @@ Result 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, ¤t->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 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, ¤t->extra_groups)) return err(EACCES); auto child_name = TRY(PathParser::basename(linkpath.view())); @@ -115,7 +115,7 @@ Result 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, ¤t->extra_groups)) return err(EACCES); auto child_name = TRY(PathParser::basename(newpath.view())); diff --git a/kernel/src/sys/mkdir.cpp b/kernel/src/sys/mkdir.cpp index 69a6da36..a07c3e17 100644 --- a/kernel/src/sys/mkdir.cpp +++ b/kernel/src/sys/mkdir.cpp @@ -13,8 +13,8 @@ Result 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, ¤t->extra_groups, + current->current_directory)); auto metadata = inode->metadata(); metadata.uid = current->auth.euid; metadata.gid = current->auth.egid; diff --git a/kernel/src/sys/mount.cpp b/kernel/src/sys/mount.cpp index 84a6ba39..fe14ca74 100644 --- a/kernel/src/sys/mount.cpp +++ b/kernel/src/sys/mount.cpp @@ -19,7 +19,8 @@ Result sys_mount(Registers*, SyscallArgs args) if (current->auth.euid != 0) return err(EPERM); auto get_source = [current, &source]() -> Result> { - auto inode = TRY(VFS::resolve_path(source.chars(), current->auth, current->current_directory)); + auto inode = + TRY(VFS::resolve_path(source.chars(), current->auth, ¤t->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 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, ¤t->extra_groups, current->current_directory)); return 0; } @@ -53,7 +54,7 @@ Result 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, ¤t->extra_groups, current->current_directory)); return 0; } diff --git a/kernel/src/sys/open.cpp b/kernel/src/sys/open.cpp index 0f0d03f2..c4ec95d0 100644 --- a/kernel/src/sys/open.cpp +++ b/kernel/src/sys/open.cpp @@ -44,7 +44,8 @@ Result 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, ¤t->extra_groups, + parent_inode)); // FIXME: Pass these in create_file(). auto metadata = inode->metadata(); metadata.uid = current->auth.euid; @@ -58,8 +59,8 @@ Result 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, ¤t->extra_groups)) return err(EACCES); + if ((flags & O_WRONLY) && !VFS::can_write(inode, current->auth, ¤t->extra_groups)) return err(EACCES); } inode = TRY(inode->open()); diff --git a/kernel/src/sys/stat.cpp b/kernel/src/sys/stat.cpp index 8a2806a7..1573d7b5 100644 --- a/kernel/src/sys/stat.cpp +++ b/kernel/src/sys/stat.cpp @@ -81,9 +81,9 @@ Result 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, ¤t->extra_groups)) return err(EACCES); + if ((amode & W_OK) && !VFS::can_write(inode, creds, ¤t->extra_groups)) return err(EACCES); + if ((amode & X_OK) && !VFS::can_execute(inode, creds, ¤t->extra_groups)) return err(EACCES); // Either all checks succeeded, or amode == F_OK and the file exists, since resolve_atfile() would have failed // otherwise. diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index fcad36f7..9ac0aeca 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -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 args; auto name_string = TRY(String::from_cstring(name)); diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 8afa6812..a10218f9 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -64,10 +64,11 @@ Result> 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> 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) diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index 0e9190ee..ada8ad90 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -54,6 +54,7 @@ struct Thread : public LinkedListNode pid_t sid { 0 }; Credentials auth; + Vector extra_groups; u64 user_ticks_self = 0; u64 kernel_ticks_self = 0; diff --git a/libluna/include/luna/Vector.h b/libluna/include/luna/Vector.h index b3a8e991..28a578c5 100644 --- a/libluna/include/luna/Vector.h +++ b/libluna/include/luna/Vector.h @@ -252,7 +252,7 @@ template class Vector Result 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);