From f6f9254eb473822051e83e4046b6d973efbc912a Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 8 Apr 2023 13:12:49 +0200 Subject: [PATCH] kernel: Add process and filesystem UIDs and GIDs --- kernel/src/InitRD.cpp | 5 ++- kernel/src/fs/VFS.cpp | 46 +++++++++++++++++--- kernel/src/fs/VFS.h | 28 +++++++++--- kernel/src/fs/tmpfs/FileSystem.cpp | 2 +- kernel/src/fs/tmpfs/FileSystem.h | 69 +++++++++++++++++++++++++++--- kernel/src/main.cpp | 2 +- kernel/src/sys/exec.cpp | 10 ++--- kernel/src/sys/mkdir.cpp | 6 ++- kernel/src/sys/mknod.cpp | 5 ++- kernel/src/sys/open.cpp | 9 ++-- kernel/src/thread/Scheduler.cpp | 3 ++ kernel/src/thread/Thread.h | 12 ++++++ 12 files changed, 165 insertions(+), 32 deletions(-) diff --git a/kernel/src/InitRD.cpp b/kernel/src/InitRD.cpp index ad8d9bdd..2fe7d038 100644 --- a/kernel/src/InitRD.cpp +++ b/kernel/src/InitRD.cpp @@ -4,6 +4,7 @@ #include "boot/bootboot.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" +#include "thread/Thread.h" #include #include @@ -19,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); + auto rc = VFS::create_directory(path, Credentials {}); if (rc.has_error()) { if (rc.error() == EEXIST) return {}; @@ -36,7 +37,7 @@ Result InitRD::populate_vfs() { if (entry.type == TarStream::EntryType::RegularFile) { - auto file = TRY(VFS::create_file(entry.name)); + auto file = TRY(VFS::create_file(entry.name, Credentials {})); file->write(entry.data(), 0, entry.size); file->chmod(entry.mode & (mode_t)~S_IFMT); } diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index c63b4eb9..1d6f251d 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -1,5 +1,7 @@ #include "fs/VFS.h" #include "Log.h" +#include "thread/Thread.h" +#include #include namespace VFS @@ -11,7 +13,7 @@ namespace VFS return *root_fs->root_inode(); } - Result> resolve_path(const char* path) + Result> resolve_path(const char* path, Credentials auth) { auto parser = TRY(PathParser::create(path)); @@ -20,17 +22,23 @@ namespace VFS // FIXME: Properly handle relative paths. const char* section; - while (parser.next().try_set_value(section)) { current_inode = TRY(current_inode->find(section)); } + while (parser.next().try_set_value(section)) + { + if (!can_execute(current_inode, auth)) return err(EACCES); + current_inode = TRY(current_inode->find(section)); + } return current_inode; } - Result> create_directory(const char* path) + Result> create_directory(const char* path, Credentials auth) { auto parser = TRY(PathParser::create(path)); auto parent_path = TRY(parser.dirname()); - auto parent_inode = TRY(resolve_path(parent_path.chars())); + auto parent_inode = TRY(resolve_path(parent_path.chars(), auth)); + + if (!can_write(parent_inode, auth)) return err(EACCES); auto child_name = TRY(parser.basename()); @@ -39,12 +47,14 @@ namespace VFS return parent_inode->create_subdirectory(child_name.chars()); } - Result> create_file(const char* path) + Result> create_file(const char* path, Credentials auth) { auto parser = TRY(PathParser::create(path)); auto parent_path = TRY(parser.dirname()); - auto parent_inode = TRY(resolve_path(parent_path.chars())); + auto parent_inode = TRY(resolve_path(parent_path.chars(), auth)); + + if (!can_write(parent_inode, auth)) return err(EACCES); auto child_name = TRY(parser.basename()); @@ -52,4 +62,28 @@ namespace VFS return parent_inode->create_file(child_name.chars()); } + + bool can_execute(SharedPtr inode, Credentials auth) + { + if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; } + if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; } + + return inode->mode() & S_IXOTH; + } + + bool can_write(SharedPtr inode, Credentials auth) + { + if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; } + if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; } + + return inode->mode() & S_IWOTH; + } + + bool can_read(SharedPtr inode, Credentials auth) + { + if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; } + if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; } + + return inode->mode() & S_IROTH; + } } diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index bb7dae26..c6b22a8d 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -3,6 +3,8 @@ #include #include +struct Credentials; + namespace VFS { enum class InodeType @@ -46,13 +48,25 @@ namespace VFS virtual bool blocking() const = 0; // Metadata accessors - virtual usize size() = 0; + virtual usize size() const = 0; - virtual mode_t mode() = 0; + virtual mode_t mode() const = 0; + + virtual u32 uid() const + { + return 0; + } + + virtual u32 gid() const + { + return 0; + } // Metadata changers virtual Result chmod(mode_t mode) = 0; + virtual Result chown(u32 uid, u32 gid) = 0; + // Generic VFS-related methods virtual FileSystem& fs() const = 0; @@ -156,10 +170,14 @@ namespace VFS extern SharedPtr root_fs; - Result> resolve_path(const char* path); + Result> resolve_path(const char* path, Credentials auth); - Result> create_directory(const char* path); - Result> create_file(const char* path); + Result> create_directory(const char* path, Credentials auth); + Result> create_file(const char* path, Credentials auth); + + bool can_execute(SharedPtr inode, Credentials auth); + bool can_read(SharedPtr inode, Credentials auth); + bool can_write(SharedPtr inode, Credentials auth); Inode& root_inode(); } diff --git a/kernel/src/fs/tmpfs/FileSystem.cpp b/kernel/src/fs/tmpfs/FileSystem.cpp index e745a847..7d1ece2f 100644 --- a/kernel/src/fs/tmpfs/FileSystem.cpp +++ b/kernel/src/fs/tmpfs/FileSystem.cpp @@ -144,7 +144,7 @@ namespace TmpFS return {}; } - usize FileInode::size() + usize FileInode::size() const { return m_data_buffer.size(); } diff --git a/kernel/src/fs/tmpfs/FileSystem.h b/kernel/src/fs/tmpfs/FileSystem.h index 82d5a496..0c92f929 100644 --- a/kernel/src/fs/tmpfs/FileSystem.h +++ b/kernel/src/fs/tmpfs/FileSystem.h @@ -66,19 +66,36 @@ namespace TmpFS Result truncate(usize size) override; - usize size() override; + usize size() const override; - mode_t mode() override + mode_t mode() const override { return m_mode; } + u32 uid() const override + { + return m_uid; + } + + u32 gid() const override + { + return m_gid; + } + Result chmod(mode_t mode) override { m_mode = mode; return {}; } + Result chown(u32 uid, u32 gid) override + { + m_uid = uid; + m_gid = gid; + return {}; + } + virtual ~FileInode() = default; private: @@ -86,6 +103,8 @@ namespace TmpFS Buffer m_data_buffer; usize m_inode_number; mode_t m_mode; + u32 m_uid { 0 }; + u32 m_gid { 0 }; }; class DeviceInode : public VFS::DeviceInode @@ -139,22 +158,39 @@ namespace TmpFS return m_device->blocking(); } - usize size() override + usize size() const override { return 0; } - mode_t mode() override + mode_t mode() const override { return m_mode; } + u32 uid() const override + { + return m_uid; + } + + u32 gid() const override + { + return m_gid; + } + Result chmod(mode_t mode) override { m_mode = mode; return {}; } + Result chown(u32 uid, u32 gid) override + { + m_uid = uid; + m_gid = gid; + return {}; + } + virtual ~DeviceInode() = default; private: @@ -162,6 +198,8 @@ namespace TmpFS SharedPtr m_device; usize m_inode_number; mode_t m_mode; + u32 m_uid { 0 }; + u32 m_gid { 0 }; }; class DirInode : public VFS::Inode @@ -207,22 +245,39 @@ namespace TmpFS return false; } - usize size() override + usize size() const override { return 0; } - mode_t mode() override + mode_t mode() const override { return m_mode; } + u32 uid() const override + { + return m_uid; + } + + u32 gid() const override + { + return m_gid; + } + Result chmod(mode_t mode) override { m_mode = mode; return {}; } + Result chown(u32 uid, u32 gid) override + { + m_uid = uid; + m_gid = gid; + return {}; + } + VFS::FileSystem& fs() const override { return *m_fs; @@ -249,6 +304,8 @@ namespace TmpFS VFS::FileSystem* m_fs; usize m_inode_number; mode_t m_mode; + u32 m_uid { 0 }; + u32 m_gid { 0 }; SharedPtr m_self; diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 843aad40..9454e537 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -54,7 +54,7 @@ Result init() TRY(DeviceRegistry::init()); InitRD::populate_vfs(); - auto init = TRY(VFS::resolve_path("/bin/init")); + auto init = TRY(VFS::resolve_path("/bin/init", Credentials {})); TRY(Scheduler::new_userspace_thread(init, "/bin/init")); Scheduler::new_kernel_thread(reap_thread, "[reap]").release_value(); diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 31684d7d..725002e5 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -37,17 +37,16 @@ Result sys_execve(Registers* regs, SyscallArgs args) auto argv = TRY(copy_string_vector_from_userspace(args[1])); auto envp = TRY(copy_string_vector_from_userspace(args[2])); + auto current = Scheduler::current(); + // FIXME: Make sure argv & envp are not too big. - auto inode = TRY(VFS::resolve_path(path.chars())); + auto inode = TRY(VFS::resolve_path(path.chars(), current->auth)); - // Not executable - if ((inode->mode() & S_IXUSR) != S_IXUSR) return err(EACCES); + if (!VFS::can_execute(inode, current->auth)) return err(EACCES); kinfoln("exec: attempting to replace current image with %s", path.chars()); - auto current = Scheduler::current(); - auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->directory); }); auto image = TRY(ThreadImage::try_load_from_elf(inode)); @@ -111,6 +110,7 @@ Result sys_fork(Registers* regs, SyscallArgs) thread->parent_id = current->id; thread->fp_data.save(); thread->name = current->name; + thread->auth = current->auth; for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; } diff --git a/kernel/src/sys/mkdir.cpp b/kernel/src/sys/mkdir.cpp index dd0a9b3e..6144e3ee 100644 --- a/kernel/src/sys/mkdir.cpp +++ b/kernel/src/sys/mkdir.cpp @@ -2,16 +2,20 @@ #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" +#include "thread/Scheduler.h" Result sys_mkdir(Registers*, SyscallArgs args) { auto path = TRY(MemoryManager::strdup_from_user(args[0])); mode_t mode = (mode_t)args[1]; + Thread* current = Scheduler::current(); + kinfoln("mkdir: attempting to create %s", path.chars()); - auto inode = TRY(VFS::create_directory(path.chars())); + auto inode = TRY(VFS::create_directory(path.chars(), current->auth)); inode->chmod(mode); + inode->chown(current->auth.euid, current->auth.egid); return 0; } diff --git a/kernel/src/sys/mknod.cpp b/kernel/src/sys/mknod.cpp index 31bf99f8..55c4df11 100644 --- a/kernel/src/sys/mknod.cpp +++ b/kernel/src/sys/mknod.cpp @@ -12,6 +12,8 @@ Result sys_mknod(Registers*, SyscallArgs args) mode_t mode = (mode_t)args[1]; dev_t dev = (dev_t)args[2]; + Thread* current = Scheduler::current(); + u32 maj = luna_dev_major(dev); u32 min = luna_dev_minor(dev); @@ -22,7 +24,8 @@ Result sys_mknod(Registers*, SyscallArgs args) auto dirname = TRY(parser.dirname()); auto basename = TRY(parser.basename()); - auto parent = TRY(VFS::resolve_path(dirname.chars())); + auto parent = TRY(VFS::resolve_path(dirname.chars(), current->auth)); + if (!VFS::can_write(parent, current->auth)) return err(EACCES); auto inode = TRY(parent->fs().create_device_inode(maj, min)); TRY(parent->add_entry(inode, basename.chars())); diff --git a/kernel/src/sys/open.cpp b/kernel/src/sys/open.cpp index 2854355e..c0d93550 100644 --- a/kernel/src/sys/open.cpp +++ b/kernel/src/sys/open.cpp @@ -25,13 +25,14 @@ Result sys_open(Registers*, SyscallArgs args) if ((flags & O_RDWR) == 0) { return err(EINVAL); } int error; - bool ok = VFS::resolve_path(path.chars()).try_set_value_or_error(inode, error); + bool ok = VFS::resolve_path(path.chars(), current->auth).try_set_value_or_error(inode, error); if (!ok) { if (error == ENOENT && (flags & O_CREAT)) { - inode = TRY(VFS::create_file(path.chars())); + inode = TRY(VFS::create_file(path.chars(), current->auth)); inode->chmod(mode); + inode->chown(current->auth.euid, current->auth.egid); } else return err(error); @@ -40,8 +41,8 @@ Result sys_open(Registers*, SyscallArgs args) return err(EEXIST); else { - if ((flags & O_RDONLY) && (inode->mode() & S_IRUSR) == 0) return err(EACCES); - if ((flags & O_WRONLY) && (inode->mode() & S_IWUSR) == 0) return err(EACCES); + 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 (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR); diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index afd5aadb..7f6089b4 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -72,6 +72,8 @@ namespace Scheduler thread->is_kernel = true; + thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 }; + g_threads.append(thread); kinfoln("Created kernel thread: id %lu with ip %#lx and sp %#lx", thread->id, thread->ip(), thread->sp()); @@ -114,6 +116,7 @@ namespace Scheduler thread->is_kernel = false; thread->name = name; thread->parent_id = 0; + thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 }; Vector args; auto name_string = TRY(String::from_cstring(name)); diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index cd34dd27..441e214d 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -38,6 +38,16 @@ struct FileDescriptor static constexpr int FD_MAX = 64; +struct Credentials +{ + u32 uid { 0 }; + u32 euid { 0 }; + u32 suid { 0 }; + u32 gid { 0 }; + u32 egid { 0 }; + u32 sgid { 0 }; +}; + struct Thread : public LinkedListNode { Registers regs; @@ -45,6 +55,8 @@ struct Thread : public LinkedListNode u64 id; u64 parent_id; + Credentials auth; + u64 ticks = 0; u64 ticks_in_user = 0; u64 ticks_in_kernel = 0;