From 0115cce7508e4338445103d9e22721de48a1318b Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 28 Oct 2022 17:10:28 +0200 Subject: [PATCH] Kernel/VFS: Add file owners and file modes, and check those in system calls --- kernel/include/fs/VFS.h | 16 ++++- kernel/src/fs/VFS.cpp | 87 +++++++++++++++++++++++++++- kernel/src/fs/devices/Console.cpp | 2 + kernel/src/fs/devices/DeviceFS.cpp | 2 + kernel/src/fs/devices/Keyboard.cpp | 2 + kernel/src/fs/devices/NullDevice.cpp | 2 + kernel/src/fs/devices/Random.cpp | 2 + kernel/src/fs/devices/Serial.cpp | 2 + kernel/src/fs/devices/Uptime.cpp | 2 + kernel/src/fs/devices/Version.cpp | 2 + kernel/src/init/InitRD.cpp | 7 +++ kernel/src/sys/exec.cpp | 10 ++++ kernel/src/sys/stdio.cpp | 24 +++++++- kernel/src/thread/Scheduler.cpp | 2 + 14 files changed, 158 insertions(+), 4 deletions(-) diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index fbca4841..924b05ec 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include typedef long ssize_t; @@ -19,7 +20,7 @@ namespace VFS typedef ssize_t (*node_read)(Node*, size_t, size_t, char*); typedef ssize_t (*node_write)(Node*, size_t, size_t, const char*); typedef Node* (*node_finddir)(Node*, const char*); - typedef int (*node_mkdir)(Node*, const char*); + typedef int (*node_mkdir)(Node*, const char*, mode_t); typedef int (*node_block)(Node*); typedef Node* (*node_readdir)(Node*, long); @@ -38,6 +39,9 @@ namespace VFS node_block block_func; int tty = 0; Node* link; + int uid; + int gid; + mode_t mode; }; ssize_t read(Node* node, size_t offset, size_t length, char* buffer); @@ -45,6 +49,9 @@ namespace VFS int mkdir(const char* path, const char* name); int mkdir(const char* pathname); + int do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode); + int do_mkdir(const char* pathname, int uid, int gid, mode_t mode); + int would_block(Node* node); void mount_root(Node* root); @@ -61,4 +68,11 @@ namespace VFS Node* root(); Node* readdir(Node* dir, long offset); + + bool can_execute(Node* node, int uid, int gid); + bool can_read(Node* node, int uid, int gid); + bool can_write(Node* node, int uid, int gid); + + bool is_setuid(Node* node); + bool is_setgid(Node* node); } \ No newline at end of file diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 8efb5e48..ef0deae2 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -157,7 +157,43 @@ int VFS::mkdir(const char* path, const char* name) kwarnln("Already exists"); return -EEXIST; } - return node->mkdir_func(node, name); + return node->mkdir_func(node, name, 0755); +} + +int VFS::do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode) +{ + Node* node = resolve_path(path, vfs_root); + if (!node) + { + kwarnln("Attempting to mkdir in %s, which does not exist", path); + return -ENOENT; + } + if (node->type != VFS_DIRECTORY) + { + kwarnln("Attempting to mkdir in %s, which is not a directory!!", path); + return -ENOTDIR; + } + if (!node->mkdir_func) + { + kwarnln("Chosen node does not support mkdir()"); + return -ENOTSUP; + } + if (!node->find_func) + { + kwarnln("Chosen node does not support finddir()"); + return -ENOTSUP; + } + if (node->find_func(node, name) != nullptr) + { + kwarnln("Already exists"); + return -EEXIST; + } + if (!can_write(node, uid, gid)) + { + kwarnln("Not enough permissions"); + return -EACCES; + } + return node->mkdir_func(node, name, mode); } int VFS::mkdir(const char* pathname) @@ -178,6 +214,24 @@ int VFS::mkdir(const char* pathname) return result; } +int VFS::do_mkdir(const char* pathname, int uid, int gid, mode_t mode) +{ + char* bstr = strdup(pathname); + char* dstr = strdup(pathname); + + char* base = basename(bstr); + char* dir = dirname(dstr); + + kdbgln("mkdir(): creating %s in directory %s", base, dir); + + int result = do_mkdir(dir, base, uid, gid, mode); + + kfree(bstr); + kfree(dstr); + + return result; +} + bool VFS::exists(const char* pathname) { return resolve_path(pathname) != nullptr; @@ -208,4 +262,35 @@ VFS::Node* VFS::readdir(VFS::Node* dir, long offset) if (!dir) return 0; if (!dir->readdir_func) return 0; return dir->readdir_func(dir, offset); +} + +bool VFS::can_execute(VFS::Node* node, int uid, int gid) +{ + if (uid == node->uid) return node->mode & 0100; + if (gid == node->gid) return node->mode & 0010; + return node->mode & 0001; +} + +bool VFS::can_write(VFS::Node* node, int uid, int gid) +{ + if (uid == node->uid) return node->mode & 0200; + if (gid == node->gid) return node->mode & 0020; + return node->mode & 0002; +} + +bool VFS::can_read(VFS::Node* node, int uid, int gid) +{ + if (uid == node->uid) return node->mode & 0400; + if (gid == node->gid) return node->mode & 0040; + return node->mode & 0004; +} + +bool VFS::is_setuid(VFS::Node* node) +{ + return node->mode & 04000; +} + +bool VFS::is_setgid(VFS::Node* node) +{ + return node->mode & 02000; } \ No newline at end of file diff --git a/kernel/src/fs/devices/Console.cpp b/kernel/src/fs/devices/Console.cpp index d0157542..ad0d05c7 100644 --- a/kernel/src/fs/devices/Console.cpp +++ b/kernel/src/fs/devices/Console.cpp @@ -14,6 +14,8 @@ VFS::Node* ConsoleDevice::create_new(const char* devname) dev->type = VFS_DEVICE; dev->flags = 0; dev->tty = 1; + dev->uid = dev->gid = 0; + dev->mode = 0222; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/fs/devices/DeviceFS.cpp b/kernel/src/fs/devices/DeviceFS.cpp index a87d9149..ffe91078 100644 --- a/kernel/src/fs/devices/DeviceFS.cpp +++ b/kernel/src/fs/devices/DeviceFS.cpp @@ -25,6 +25,8 @@ VFS::Node* DeviceFS::get() devfs_root->type = VFS_DIRECTORY; devfs_root->find_func = DeviceFS::finddir; devfs_root->readdir_func = DeviceFS::readdir; + devfs_root->mode = 0755; + devfs_root->uid = devfs_root->gid = 0; strncpy(devfs_root->name, "dev", sizeof(devfs_root->name)); devfs_files[devfs_file_count++] = VersionDevice::create_new("version"); diff --git a/kernel/src/fs/devices/Keyboard.cpp b/kernel/src/fs/devices/Keyboard.cpp index f844d1b9..e3d0ed4f 100644 --- a/kernel/src/fs/devices/Keyboard.cpp +++ b/kernel/src/fs/devices/Keyboard.cpp @@ -26,6 +26,8 @@ VFS::Node* KeyboardDevice::create_new(const char* devname) dev->type = VFS_DEVICE; dev->flags = 0; dev->tty = 1; + dev->uid = dev->gid = 0; + dev->mode = 0444; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/fs/devices/NullDevice.cpp b/kernel/src/fs/devices/NullDevice.cpp index 962ee464..7fcecc8f 100644 --- a/kernel/src/fs/devices/NullDevice.cpp +++ b/kernel/src/fs/devices/NullDevice.cpp @@ -12,6 +12,8 @@ VFS::Node* NullDevice::create_new(const char* devname) dev->length = 0; dev->type = VFS_DEVICE; dev->flags = 0; + dev->uid = dev->gid = 0; + dev->mode = 0666; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/fs/devices/Random.cpp b/kernel/src/fs/devices/Random.cpp index ed309965..d7eeb6a4 100644 --- a/kernel/src/fs/devices/Random.cpp +++ b/kernel/src/fs/devices/Random.cpp @@ -14,6 +14,8 @@ VFS::Node* RandomDevice::create_new(const char* devname) dev->length = 0; dev->type = VFS_DEVICE; dev->flags = 0; + dev->uid = dev->gid = 0; + dev->mode = 0444; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/fs/devices/Serial.cpp b/kernel/src/fs/devices/Serial.cpp index 433478a6..1fe92824 100644 --- a/kernel/src/fs/devices/Serial.cpp +++ b/kernel/src/fs/devices/Serial.cpp @@ -13,6 +13,8 @@ VFS::Node* SerialDevice::create_new(const char* devname) dev->length = 0; dev->type = VFS_DEVICE; dev->flags = 0; + dev->uid = dev->gid = 0; + dev->mode = 0200; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/fs/devices/Uptime.cpp b/kernel/src/fs/devices/Uptime.cpp index f536a479..ab45cb42 100644 --- a/kernel/src/fs/devices/Uptime.cpp +++ b/kernel/src/fs/devices/Uptime.cpp @@ -12,6 +12,8 @@ VFS::Node* UptimeDevice::create_new(const char* devname) dev->length = 0; dev->type = VFS_DEVICE; dev->flags = 0; + dev->uid = dev->gid = 0; + dev->mode = 0444; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/fs/devices/Version.cpp b/kernel/src/fs/devices/Version.cpp index 86a4962a..c01c4aae 100644 --- a/kernel/src/fs/devices/Version.cpp +++ b/kernel/src/fs/devices/Version.cpp @@ -12,6 +12,8 @@ VFS::Node* VersionDevice::create_new(const char* devname) dev->length = strlen(moon_version()) + 5; dev->type = VFS_DEVICE; dev->flags = 0; + dev->uid = dev->gid = 0; + dev->mode = 0444; strncpy(dev->name, devname, sizeof(dev->name)); return dev; } diff --git a/kernel/src/init/InitRD.cpp b/kernel/src/init/InitRD.cpp index 0588653a..ddef39ac 100644 --- a/kernel/src/init/InitRD.cpp +++ b/kernel/src/init/InitRD.cpp @@ -225,6 +225,7 @@ int initrd_mkdir(VFS::Node* node, const char* name) // FIXME: Return proper erro new_node.mkdir_func = initrd_mkdir; new_node.length = 0; new_node.type = VFS_DIRECTORY; + new_node.uid = new_node.gid = 0; strncpy(new_node.name, name, sizeof(new_node.name)); InitRD::Directory dir; strncpy(dir.name, name, sizeof(dir.name)); @@ -282,6 +283,8 @@ static bool initrd_register_dir(InitRD::Directory& dir, uint64_t inode) node.mkdir_func = initrd_mkdir; node.readdir_func = initrd_read_dir; node.length = 0; + node.mode = 0755; + node.uid = node.gid = 0; strncpy(node.name, buffer, sizeof(node.name)); strncpy(dir.name, buffer, sizeof(dir.name)); @@ -341,6 +344,8 @@ static bool initrd_register_file(InitRD::File& f, uint64_t inode) node.read_func = initrd_read; node.length = f.size; node.type = VFS_FILE; + node.mode = 0555; + node.uid = node.gid = 0; strncpy(node.name, buffer, sizeof(node.name)); strncpy(f.name, buffer, sizeof(f.name)); @@ -383,6 +388,8 @@ static void initrd_initialize_root() initrd_root.length = 0; initrd_root.inode = 0; initrd_root.type |= VFS_DIRECTORY; + initrd_root.mode = 0755; + initrd_root.uid = initrd_root.gid = 0; InitRD::Directory& root = dirs[0]; total_dirs++; strncpy(initrd_root.name, "initrd", sizeof(initrd_root.name)); diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index c5a3bfbc..c110c661 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -97,6 +97,13 @@ void sys_execv(Context* context, const char* pathname, char** argv) return; } + if (!VFS::can_execute(program, Scheduler::current_task()->euid, Scheduler::current_task()->egid)) + { + kfree(kpathname); + context->rax = -EACCES; + return; + } + long memusage; if ((memusage = ELFLoader::check_elf_image(program)) < 0) { @@ -216,6 +223,9 @@ void sys_execv(Context* context, const char* pathname, char** argv) ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly // wrong. + if (VFS::is_setuid(program)) task->uid = program->uid; + if (VFS::is_setgid(program)) task->gid = program->gid; + strlcpy(task->name, kpathname, sizeof(task->name)); Scheduler::reset_task(task, image); diff --git a/kernel/src/sys/stdio.cpp b/kernel/src/sys/stdio.cpp index 8b0d0c84..28828a5a 100644 --- a/kernel/src/sys/stdio.cpp +++ b/kernel/src/sys/stdio.cpp @@ -207,6 +207,24 @@ void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXM return; } + if (can_read && !VFS::can_read(node, current_task->euid, current_task->egid)) + { + kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for reading", + current_task->euid, current_task->egid, kfilename, node->mode); + kfree(kfilename); + context->rax = -EACCES; + return; + } + + if (can_write && !VFS::can_write(node, current_task->euid, current_task->egid)) + { + kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for writing", + current_task->euid, current_task->egid, kfilename, node->mode); + kfree(kfilename); + context->rax = -EACCES; + return; + } + bool able_to_block = (flags & OPEN_NONBLOCK) == 0; bool close_on_exec = (flags & OPEN_CLOEXEC) > 0; @@ -304,7 +322,7 @@ void sys_close(Context* context, int fd) return; } -void sys_mkdir(Context* context, const char* filename) +void sys_mkdir(Context* context, const char* filename, mode_t mode) { char* kfilename = strdup_from_user(filename); if (!kfilename) @@ -313,7 +331,9 @@ void sys_mkdir(Context* context, const char* filename) return; } - int rc = VFS::mkdir(kfilename); + Task* current_task = Scheduler::current_task(); + + int rc = VFS::do_mkdir(kfilename, current_task->euid, current_task->egid, mode); kfree(kfilename); diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 4565786f..0db18fb9 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -115,6 +115,7 @@ void Scheduler::add_kernel_task(const char* taskname, void (*task)(void)) new_task->user_task = false; new_task->id = free_pid++; new_task->ppid = 0; + new_task->uid = new_task->euid = new_task->gid = new_task->egid = 0; new_task->regs.rip = (uint64_t)task; new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right? @@ -159,6 +160,7 @@ long Scheduler::load_user_task(const char* filename) memset(&new_task->regs, 0, sizeof(Context)); new_task->id = free_pid++; new_task->ppid = 0; + new_task->uid = new_task->euid = new_task->gid = new_task->egid = 0; if (!new_task->allocator.init()) { delete new_task;