diff --git a/CMakeLists.txt b/CMakeLists.txt index 478be4ea..93711fab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ endif() set (OUTPUT_DIR ${CMAKE_SOURCE_DIR}/out CACHE STRING "The output directory.") set (OBOS_PAGE_REPLACEMENT_ALGORITHM "Aging" CACHE STRING "(Kernel developers only) The page replacement algorithm used by the MM.") set (OBOS_DEV_PREFIX "/dev" CACHE STRING "Directory where the kernel places devices.") +set (OBOS_PERM_PREFIX "/sys/perm" CACHE STRING "Directory where the kernel checks for capabilities") set (OBOS_CLANG_SUFFIX "" CACHE STRING "Some Linux distributions add a suffix to various clang and LLVM commands, such as the version used. Specify that suffix, if needed") file (TO_CMAKE_PATH "${OUTPUT_DIR}" OUTPUT_DIR) @@ -249,6 +250,7 @@ configure_file(${CMAKE_SOURCE_DIR}/src/inc/dev_prefix.h.in ${CMAKE_SOURCE_DIR}/s add_compile_definitions( OBOS_ARCHITECTURE_HAS_ACPI=${OBOS_ARCHITECTURE_HAS_ACPI} OBOS_ARCHITECTURE_HAS_PCI=${OBOS_ARCHITECTURE_HAS_PCI} + OBOS_PERM_PREFIX="${OBOS_PERM_PREFIX}" ) if (OBOS_USE_OBOS_GCC AND OBOS_COMPILE_USER_UTILS) diff --git a/src/oboskrnl/CMakeLists.txt b/src/oboskrnl/CMakeLists.txt index 8e11352e..b86b74c3 100644 --- a/src/oboskrnl/CMakeLists.txt +++ b/src/oboskrnl/CMakeLists.txt @@ -22,7 +22,7 @@ list (APPEND oboskrnl_sources "init_proc.c" "vfs/create.c" "net/route.c" "net/eth.c" "net/ip.c" "net/arp.c" "power/event.c" "net/udp.c" "net/icmp.c" "net/tcp.c" "vfs/socket.c" "net/lo.c" - "kinit.c" "vfs/local_socket.c" + "kinit.c" "vfs/local_socket.c" "perm.c" ) add_executable(oboskrnl) diff --git a/src/oboskrnl/driver_interface/drv_sys.c b/src/oboskrnl/driver_interface/drv_sys.c index 2d99ee15..a14bf31c 100644 --- a/src/oboskrnl/driver_interface/drv_sys.c +++ b/src/oboskrnl/driver_interface/drv_sys.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -23,15 +24,11 @@ #include handle Sys_LoadDriver(const void* file, size_t szFile, obos_status* ustatus) -{ - if (Core_GetCurrentThread()->proc->euid != ROOT_UID) - { - obos_status status = OBOS_STATUS_ACCESS_DENIED; - if (ustatus) memcpy_k_to_usr(ustatus, &status, sizeof(obos_status)); - return HANDLE_INVALID; - } +{ + obos_status status = OBOS_CapabilityCheck("drv/load", false); + if (obos_is_error(status)) + return status; - obos_status status = OBOS_STATUS_SUCCESS; if (!szFile) { status = OBOS_STATUS_INVALID_ARGUMENT; @@ -62,19 +59,13 @@ handle Sys_LoadDriver(const void* file, size_t szFile, obos_status* ustatus) return ret; } -#define perm_check_impl(ret) do {\ - if (Core_GetCurrentThread()->proc->euid != ROOT_UID)\ - return (ret);\ -} while(0) -#define perm_check() perm_check_impl(OBOS_STATUS_ACCESS_DENIED) -#define perm_check_hnd() perm_check_impl(HANDLE_INVALID) - obos_status Sys_StartDriver(handle driver, handle* mainThread) { - perm_check(); + obos_status status = OBOS_CapabilityCheck("drv/start", false); + if (obos_is_error(status)) + return status; OBOS_LockHandleTable(OBOS_CurrentHandleTable()); - obos_status status = OBOS_STATUS_SUCCESS; handle_desc* drv = OBOS_HandleLookup(OBOS_CurrentHandleTable(), driver, HANDLE_TYPE_DRIVER_ID, false, &status); if (!drv) { @@ -105,10 +96,11 @@ obos_status Sys_StartDriver(handle driver, handle* mainThread) obos_status Sys_UnloadDriver(handle driver) { - perm_check(); + obos_status status = OBOS_CapabilityCheck("drv/unload", false); + if (obos_is_error(status)) + return status; OBOS_LockHandleTable(OBOS_CurrentHandleTable()); - obos_status status = OBOS_STATUS_SUCCESS; handle_desc* drv = OBOS_HandleLookup(OBOS_CurrentHandleTable(), driver, HANDLE_TYPE_DRIVER_ID, false, &status); if (!drv) { @@ -127,10 +119,11 @@ obos_status Sys_UnloadDriver(handle driver) obos_status Sys_PnpLoadDriversAt(handle dent, bool wait) { - perm_check(); + obos_status status = OBOS_CapabilityCheck("drv/load-pnp", false); + if (obos_is_error(status)) + return status; OBOS_LockHandleTable(OBOS_CurrentHandleTable()); - obos_status status = OBOS_STATUS_SUCCESS; handle_desc* dirent = OBOS_HandleLookup(OBOS_CurrentHandleTable(), dent, HANDLE_TYPE_DIRENT, false, &status); if (!dirent) { @@ -145,11 +138,13 @@ obos_status Sys_PnpLoadDriversAt(handle dent, bool wait) // Finds a loaded driver by its name, and returns a handle to it. handle Sys_FindDriverByName(const char* uname /* assumed to be 64-bytes at max */) { - perm_check_hnd(); + obos_status status = OBOS_CapabilityCheck("drv/open-name", false); + if (obos_is_error(status)) + return HANDLE_INVALID; char* name = nullptr; size_t sz_name = 0; - obos_status status = OBOSH_ReadUserString(uname, nullptr, &sz_name); + status = OBOSH_ReadUserString(uname, nullptr, &sz_name); if (obos_is_error(status)) return HANDLE_INVALID; if (sz_name >= 64) @@ -187,7 +182,9 @@ handle Sys_FindDriverByName(const char* uname /* assumed to be 64-bytes at max * // If 'curr' is HANDLE_INVALID, the first item in the list is returned. handle Sys_EnumerateLoadedDrivers(handle curr) { - perm_check_hnd(); + obos_status status = OBOS_CapabilityCheck("drv/enumerate", false); + if (obos_is_error(status)) + return HANDLE_INVALID; driver_id* id = nullptr; @@ -222,13 +219,14 @@ handle Sys_EnumerateLoadedDrivers(handle curr) // Queries the name of the driver in 'drv'. obos_status Sys_QueryDriverName(handle driver, char* namebuf, size_t *sznamebuf /* need not be over 64 */) { - perm_check(); + obos_status status = OBOS_CapabilityCheck("drv/query-name", false); + if (obos_is_error(status)) + return status; if (!sznamebuf) return OBOS_STATUS_INVALID_ARGUMENT; OBOS_LockHandleTable(OBOS_CurrentHandleTable()); - obos_status status = OBOS_STATUS_SUCCESS; handle_desc* drv = OBOS_HandleLookup(OBOS_CurrentHandleTable(), driver, HANDLE_TYPE_DRIVER_ID, false, &status); if (!drv) { diff --git a/src/oboskrnl/mm/mm_sys.c b/src/oboskrnl/mm/mm_sys.c index df4b6d0b..0c7ea672 100644 --- a/src/oboskrnl/mm/mm_sys.c +++ b/src/oboskrnl/mm/mm_sys.c @@ -4,10 +4,10 @@ * Copyright (c) 2024-2025 Omar Berrow */ -#include "mm/swap.h" #include #include #include +#include #include #include #include @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -81,6 +82,16 @@ void* Sys_VirtualMemoryAlloc(handle ctx, void* base, size_t size, struct vma_all prot &= ~OBOS_PROTECTION_CACHE_DISABLE; flags &= ~VMA_FLAGS_32BITPHYS; // userspace doesn't need this as much as kernel mode + if (flags & VMA_FLAGS_NON_PAGED) + { + obos_status status = OBOS_CapabilityCheck("mm/allocate-non-paged", false); + if (obos_is_error(status)) + { + if (pstatus) memcpy_k_to_usr(pstatus, &status, sizeof(status)); + return nullptr; + } + } + void* ret = Mm_VirtualMemoryAllocEx(vmm_ctx, base, size, prot, flags, file, args.offset, &status); if (pstatus) memcpy_k_to_usr(pstatus, &status, sizeof(status)); @@ -99,10 +110,12 @@ obos_status Sys_VirtualMemoryProtect(handle ctx, void* base, size_t size, prot_f } obos_status Sys_VirtualMemoryLock(handle ctx, void* base, size_t size) { + OBOS_UNUSED(ctx && base && size); return OBOS_STATUS_UNIMPLEMENTED; } obos_status Sys_VirtualMemoryUnlock(handle ctx, void* base, size_t size) { + OBOS_UNUSED(ctx && base && size); return OBOS_STATUS_UNIMPLEMENTED; } @@ -157,6 +170,10 @@ size_t Sys_GetUsedPhysicalMemoryCount() obos_status Sys_QueryPageInfo(handle ctx, void* base, page_info* info) { + obos_status status = OBOS_CapabilityCheck("mm/query-page-info", false); + if (obos_is_error(status)) + return status; + context* vmm_ctx = context_from_handle(ctx, false, 0, true); // Search the page in the RB-Tree. irql oldIrql = Core_SpinlockAcquire(&vmm_ctx->lock); @@ -198,9 +215,13 @@ obos_status Sys_MakeDiskSwap(const char* upath) obos_status Sys_SwitchSwap(const char* upath) { + obos_status status = OBOS_CapabilityCheck("mm/switch-swap", false); + if (obos_is_error(status)) + return status; + char* path = nullptr; size_t sz_path = 0; - obos_status status = OBOSH_ReadUserString(upath, nullptr, &sz_path); + status = OBOSH_ReadUserString(upath, nullptr, &sz_path); if (obos_is_error(status)) return status; path = ZeroAllocate(OBOS_KernelAllocator, sz_path+1, sizeof(char), nullptr); @@ -236,6 +257,26 @@ obos_status Sys_SwitchSwap(const char* upath) void Sys_SyncAnonPages() { + obos_status status = OBOS_CapabilityCheck("mm/sync-anon", true); + if (obos_is_error(status)) + return; Mm_PageWriterOperation = PAGE_WRITER_SYNC_ANON; Mm_WakePageWriter(true); } + +handle Sys_MmFork() +{ + // Zeroed in Mm_ConstructContext + context* ctx = Allocate(Mm_Allocator, sizeof(context), nullptr); + Mm_ConstructContext(ctx); + Mm_ForkContext(ctx, Core_GetCurrentThread()->proc->ctx); + ctx->workingSet.capacity = Core_GetCurrentThread()->proc->ctx->workingSet.capacity; + + OBOS_LockHandleTable(OBOS_CurrentHandleTable()); + handle_desc* desc = nullptr; + handle hnd = OBOS_HandleAllocate(OBOS_CurrentHandleTable(), HANDLE_TYPE_VMM_CONTEXT, &desc); + desc->un.vmm_context = ctx; + OBOS_UnlockHandleTable(OBOS_CurrentHandleTable()); + + return hnd; +} \ No newline at end of file diff --git a/src/oboskrnl/mm/mm_sys.h b/src/oboskrnl/mm/mm_sys.h index 0bc8ed95..80a9abec 100644 --- a/src/oboskrnl/mm/mm_sys.h +++ b/src/oboskrnl/mm/mm_sys.h @@ -38,3 +38,5 @@ obos_status Sys_QueryPageInfo(handle ctx, void* base, page_info* info); obos_status Sys_MakeDiskSwap(const char* path); obos_status Sys_SwitchSwap(const char* path); void Sys_SyncAnonPages(); + +handle Sys_MmFork(); \ No newline at end of file diff --git a/src/oboskrnl/perm.c b/src/oboskrnl/perm.c new file mode 100644 index 00000000..f1c3458e --- /dev/null +++ b/src/oboskrnl/perm.c @@ -0,0 +1,224 @@ +/* + * oboskrnl/perm.c + * + * Copyright (c) 2025 Omar Berrow + * + * Syscall permission checking utilities + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +dirent* Vfs_PermRoot; + +#define CHECK_ID(id) \ +do {\ + if (*id == '/' || *id == 0) return OBOS_STATUS_INVALID_ARGUMENT;\ +} while(0)\ + +void OBOS_CapabilityInitialize() +{ + Vfs_PermRoot = VfsH_DirentLookup(OBOS_PERM_PREFIX); + if (!Vfs_PermRoot) + OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "%s: Could not find directory at OBOS_PERM_PREFIX (%s) specified at build time.\n", __func__, OBOS_PERM_PREFIX); + // we chillin +} + +obos_status OBOS_CapabilityFetch(const char* id, capability* res, bool create) +{ + if (!Vfs_PermRoot) + return OBOS_STATUS_NOT_FOUND; + OBOS_ENSURE(res); + OBOS_ENSURE(id); + CHECK_ID(id); + dirent* ent = VfsH_DirentLookupFrom(id, Vfs_PermRoot); + if (!ent) + { + if (!create) + return OBOS_STATUS_NOT_FOUND; + capability def = {}; + def.allow_user = true; + def.allow_group = true; + def.owner = ROOT_UID; + def.group = ROOT_GID; + return OBOS_CapabilitySet(id, &def, true); + } + + if (!ent->vnode) + return OBOS_STATUS_INTERNAL_ERROR; + + res->owner = ent->vnode->uid; + res->group = ent->vnode->gid; + res->allow_user = ent->vnode->perm.owner_exec; + res->allow_group = ent->vnode->perm.group_exec; + res->allow_other = ent->vnode->perm.other_exec; + + if (res->allow_other) + OBOS_Warning("Allowing 'other' permissions on capability \"%s\"\n", id); + if (!res->allow_other && !res->allow_group && !res->allow_user) + OBOS_Warning("Capability \"%s\" is disabled.\n", id); + if (res->owner != ROOT_UID) + OBOS_Warning("Capability \"%s\" has weird ownership. Owned by %d:%d\n", id, res->owner, res->group); + + return OBOS_STATUS_SUCCESS; +} + +// TODO: Should this be replaced with strchr? +static size_t str_search(const char* str, char ch) +{ + size_t ret = strchr(str, ch); + for (; str[ret] == ch && str[ret] != 0; ret++) + ; + return ret; +} + +static obos_status create_parents(const char* path, dirent* root, dirent** last_parent, const char** last_token) +{ + size_t path_len = strlen(path); + if (!path_len) + return OBOS_STATUS_INVALID_ARGUMENT; + + for (; *path == '/'; path++, path_len--) + ; + const char* tok = path; + size_t tok_len = strchr(tok, '/'); + if (tok_len != path_len) + tok_len--; + while (tok[tok_len - 1] == '/') + tok_len--; + if (!tok_len) + return OBOS_STATUS_INVALID_ARGUMENT; + + obos_status status = OBOS_STATUS_SUCCESS; + file_perm mode = {}; + mode.owner_read = true; + mode.owner_write = true; + mode.owner_exec = true; + mode.group_read = true; + mode.group_write = false; + mode.group_exec = true; + mode.other_read = true; + mode.other_write = false; + mode.other_exec = true; + + while (1) + { + char* tok_nul = Vfs_Malloc(tok_len+1); + memcpy(tok_nul, tok, tok_len); + + dirent* new = VfsH_DirentLookupFrom(tok_nul, root); + if (new) + goto cont; + + cont: + *last_token = tok; + tok = tok + str_search(tok, '/'); + size_t currentPathLen = strlen(tok)-1; + if (tok[currentPathLen] != '/') + currentPathLen++; + while (tok[currentPathLen] == '/') + currentPathLen--; + tok_len = strchr(tok, '/'); + if (tok_len != currentPathLen) + tok_len--; + while (tok[tok_len - 1] == '/') + tok_len--; + if (!tok_len) + { + // Last token, do not create anything + *last_parent = root; + Vfs_Free(tok_nul); + break; + } + else + { + if (obos_is_error(status = Vfs_CreateNode(root, tok_nul, VNODE_TYPE_DIR, mode))) + return status; + root = VfsH_DirentLookupFrom(tok_nul, root); + Vfs_Free(tok_nul); + continue; + } + } + + return OBOS_STATUS_SUCCESS; +} + +obos_status OBOS_CapabilitySet(const char* id, const capability* perm, bool overwrite) +{ + CHECK_ID(id); + OBOS_ENSURE(Vfs_PermRoot); + OBOS_ENSURE(id); + OBOS_ENSURE(perm); + capability tmp = {}; + obos_status status = OBOS_CapabilityFetch(id, &tmp, false); + switch (status) { + case OBOS_STATUS_SUCCESS: + if (!overwrite) return OBOS_STATUS_ALREADY_INITIALIZED; + break; + case OBOS_STATUS_NOT_FOUND: + break; + default: return status; + } + + // We need to create the file. + dirent* parent = nullptr; + const char* name = nullptr; + status = create_parents(id, Vfs_PermRoot, &parent, &name); + if (obos_is_error(status)) + return status; + OBOS_ENSURE(parent); + file_perm mode = {}; + mode.owner_exec = perm->allow_user; + mode.group_exec = perm->allow_group; + mode.other_exec = perm->allow_other; + return Vfs_CreateNodeOwner(parent, name, VNODE_TYPE_REG, mode, perm->owner, perm->group); +} + +obos_status OBOS_CapabilityCheck(const char* id, bool def_other_allow) +{ + obos_status res = OBOS_CapabilityCheckAs(id, Core_GetCurrentThread()->proc->euid, Core_GetCurrentThread()->proc->egid, def_other_allow); + if (obos_is_success(res)) + return OBOS_STATUS_SUCCESS; + for (size_t i = 0; i < Core_GetCurrentThread()->proc->groups.nEntries; i++) + { + res = OBOS_CapabilityCheckAs(id, Core_GetCurrentThread()->proc->euid, Core_GetCurrentThread()->proc->groups.list[i], def_other_allow); + if (obos_is_success(res)) + return OBOS_STATUS_SUCCESS; + else if (res == OBOS_STATUS_ACCESS_DENIED) + continue; + else + return res; + } + return OBOS_STATUS_ACCESS_DENIED; +} + +obos_status OBOS_CapabilityCheckAs(const char* id, uid user, gid group, bool def_other_allow) +{ + capability res = {}; + obos_status status = OBOS_CapabilityFetch(id, &res, false); + if (obos_is_error(status)) + { + if (status == OBOS_STATUS_NOT_FOUND) + { + if (user == ROOT_UID || group == ROOT_GID) return OBOS_STATUS_SUCCESS; + return def_other_allow ? OBOS_STATUS_SUCCESS : OBOS_STATUS_ACCESS_DENIED; + } + return status; + } + if (res.allow_user && res.owner == user) + return OBOS_STATUS_SUCCESS; + if (res.allow_group && res.group == group) + return OBOS_STATUS_SUCCESS; + return res.allow_other ? OBOS_STATUS_SUCCESS : OBOS_STATUS_ACCESS_DENIED; +} \ No newline at end of file diff --git a/src/oboskrnl/perm.h b/src/oboskrnl/perm.h new file mode 100644 index 00000000..b6ff3981 --- /dev/null +++ b/src/oboskrnl/perm.h @@ -0,0 +1,46 @@ +/* + * oboskrnl/perm.h + * + * Copyright (c) 2025 Omar Berrow + * + * Syscall permission checking utilities + */ + +#pragma once + +#include +#include + +#ifndef OBOS_PERM_PREFIX +# define OBOS_PERM_PREFIX "/sys/perm/" +#endif + +extern struct dirent* Vfs_PermRoot; + +typedef struct capability { + uid owner; + gid group; + bool allow_user : 1; + bool allow_group : 1; + bool allow_other : 1; +} capability; + +void OBOS_CapabilityInitialize(); + +// NOTE: If a filesystem is unmounted, and remounted, any default values +// WILL be lost + +// NOTE: OBOS_CapabilityCheckAs treats a capability like the following, +// if it cannot be found +// capability cap = { +// .owner=ROOT_UID, +// .group=ROOT_GID, +// .allow_user=true, +// .allow_group=true, +// .allow_other=false +// }; + +obos_status OBOS_CapabilityFetch(const char* id, capability* res, bool create); +obos_status OBOS_CapabilitySet(const char* id, const capability* perm, bool overwrite); +obos_status OBOS_CapabilityCheck(const char* id, bool def_other_allow); +obos_status OBOS_CapabilityCheckAs(const char* id, uid user, gid group, bool def_other_allow); \ No newline at end of file diff --git a/src/oboskrnl/scheduler/process.c b/src/oboskrnl/scheduler/process.c index 36569a20..d238480b 100644 --- a/src/oboskrnl/scheduler/process.c +++ b/src/oboskrnl/scheduler/process.c @@ -92,6 +92,12 @@ OBOS_PAGEABLE_FUNCTION obos_status Core_ProcessStart(process* proc, thread* main proc->rgid = proc->parent->rgid; proc->egid = proc->parent->egid; proc->sgid = proc->parent->sgid; + proc->groups.nEntries = proc->parent->groups.nEntries; + proc->groups.list = memcpy( + Allocate(OBOS_KernelAllocator, proc->parent->groups.nEntries * sizeof(gid), nullptr), + proc->parent->groups.list, + proc->parent->groups.nEntries*sizeof(gid) + ); proc->refcount++; Core_SetProcessGroup(proc, proc->parent->pgrp->pgid); proc->waiting_threads = WAITABLE_HEADER_INITIALIZE(false, true); diff --git a/src/oboskrnl/scheduler/sched_sys.c b/src/oboskrnl/scheduler/sched_sys.c index 45d5a7d4..46f02b70 100644 --- a/src/oboskrnl/scheduler/sched_sys.c +++ b/src/oboskrnl/scheduler/sched_sys.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,10 @@ handle Sys_ThreadOpen(handle proc_hnd, uintptr_t tid) if (tid <= (Core_CpuCount + 1)) return HANDLE_INVALID; // you cannot open any CPU's idle thread + obos_status status = OBOS_CapabilityCheck("core/thread-open", true); + if (obos_is_error(status)) + return status; + process* parent = HANDLE_TYPE(proc_hnd) == HANDLE_TYPE_CURRENT ? Core_GetCurrentThread()->proc : nullptr; if (!parent) @@ -116,6 +121,12 @@ handle Sys_ThreadOpen(handle proc_hnd, uintptr_t tid) } parent = proc->un.process; OBOS_UnlockHandleTable(OBOS_CurrentHandleTable()); + if (parent->euid != Core_GetCurrentThread()->proc->euid) + { + obos_status status = OBOS_CapabilityCheck("core/thread-arbitrary-open", false); + if (obos_is_error(status)) + return status; + } } thread* thr = nullptr; @@ -141,6 +152,7 @@ handle Sys_ThreadOpen(handle proc_hnd, uintptr_t tid) return hnd; } + handle Sys_ThreadCreate(thread_priority priority, thread_affinity affinity, handle thread_context) { if (priority < 0 || priority > THREAD_PRIORITY_MAX_VALUE) @@ -208,6 +220,7 @@ handle Sys_ThreadCreate(thread_priority priority, thread_affinity affinity, hand }\ result__;\ }) + obos_status Sys_ThreadReady(handle thread) { struct thread* thr = thread_object_from_handle(thread, true, false); @@ -218,12 +231,14 @@ obos_status Sys_ThreadReady(handle thread) thr->kernelStack = Mm_AllocateKernelStack(thr->proc->ctx, nullptr); return CoreH_ThreadReady(thr); } + obos_status Sys_ThreadBlock(handle thread) { struct thread* thr = thread_object_from_handle(thread, true, true); OBOS_ASSERT(thr); return CoreH_ThreadBlock(thr, true); } + obos_status Sys_ThreadBoostPriority(handle thread, int reserved /* ignored as of now */) { OBOS_UNUSED(reserved); @@ -231,6 +246,7 @@ obos_status Sys_ThreadBoostPriority(handle thread, int reserved /* ignored as of OBOS_ASSERT(thr); return CoreH_ThreadBoostPriority(thr); } + obos_status Sys_ThreadPriority(handle thread_hnd, const thread_priority *new, thread_priority* old) { struct thread* thr = thread_object_from_handle(thread_hnd, true, true); @@ -239,8 +255,59 @@ obos_status Sys_ThreadPriority(handle thread_hnd, const thread_priority *new, th memcpy_k_to_usr(old, &thr->priority, sizeof(thr->priority)); if (new) { + obos_status status = OBOS_CapabilityCheck("core/thread-set-priority", true); + if (obos_is_error(status)) + return status; + thread_priority new_priority = 0; thread_priority old_priority = thr->priority; - memcpy_usr_to_k(&thr->priority, new, sizeof(thr->priority)); + memcpy_usr_to_k(&new_priority, new, sizeof(thr->priority)); + switch (new_priority) { + case THREAD_PRIORITY_IDLE: + { + obos_status status = OBOS_CapabilityCheck("core/thread-idle-priority", true); + if (obos_is_error(status)) + return status; + break; + } + case THREAD_PRIORITY_LOW: + { + obos_status status = OBOS_CapabilityCheck("core/thread-low-priority", true); + if (obos_is_error(status)) + return status; + break; + } + case THREAD_PRIORITY_NORMAL: + { + obos_status status = OBOS_CapabilityCheck("core/thread-normal-priority", true); + if (obos_is_error(status)) + return status; + break; + } + case THREAD_PRIORITY_HIGH: + { + obos_status status = OBOS_CapabilityCheck("core/thread-high-priority", true); + if (obos_is_error(status)) + return status; + break; + } + case THREAD_PRIORITY_URGENT: + { + obos_status status = OBOS_CapabilityCheck("core/thread-urgent-priority", false); + if (obos_is_error(status)) + return status; + break; + } + case THREAD_PRIORITY_REAL_TIME: + { + obos_status status = OBOS_CapabilityCheck("core/thread-realtime-priority", false); + if (obos_is_error(status)) + return status; + break; + } + default: return OBOS_STATUS_INVALID_ARGUMENT; + } + + thr->priority = new_priority; if (thr->priority != old_priority && thr->masterCPU) { CoreH_ThreadListRemove(&thr->masterCPU->priorityLists[old_priority].list, &thr->snode); @@ -249,6 +316,7 @@ obos_status Sys_ThreadPriority(handle thread_hnd, const thread_priority *new, th } return OBOS_STATUS_SUCCESS; } + obos_status Sys_ThreadAffinity(handle thread_hnd, const thread_affinity *new, thread_affinity* old) { struct thread* thr = thread_object_from_handle(thread_hnd, true, true); @@ -256,9 +324,36 @@ obos_status Sys_ThreadAffinity(handle thread_hnd, const thread_affinity *new, th if (old) memcpy_k_to_usr(old, &thr->affinity, sizeof(thr->affinity)); if (new) + { + obos_status status = OBOS_CapabilityCheck("core/thread-set-affinity", false); + if (obos_is_error(status)) + return status; + thread_affinity old = thr->affinity; memcpy_usr_to_k(&thr->affinity, new, sizeof(thr->affinity)); + thr->affinity &= ~Core_DefaultThreadAffinity; + if (!thr->affinity) + { + thr->affinity = old; + return OBOS_STATUS_INVALID_ARGUMENT; + } + if (thr->status != THREAD_STATUS_BLOCKED) + { + if (~thr->affinity & CoreH_CPUIdToAffinity(thr->masterCPU->id)) + { + irql oldIrql = Core_RaiseIrqlNoThread(IRQL_DISPATCH); + bool yield_after = thr->masterCPU == CoreS_GetCPULocalPtr(); + CoreH_ThreadBlock(thr, false); + CoreH_ThreadReady(thr); + if (yield_after) + Core_Yield(); + else + Core_LowerIrqlNoThread(oldIrql); + } + } + } return OBOS_STATUS_SUCCESS; } + // Can only be called once per thread-object, and must be called before readying a thread. obos_status Sys_ThreadSetOwner(handle thr_hnd, handle proc_hnd) { @@ -285,6 +380,13 @@ obos_status Sys_ThreadSetOwner(handle thr_hnd, handle proc_hnd) OBOS_UnlockHandleTable(OBOS_CurrentHandleTable()); } + if (proc_hnd != HANDLE_CURRENT) + { + obos_status status = OBOS_CapabilityCheck("core/set-thread-owner-arbitrary", false); + if (obos_is_error(status)) + return status; + } + thr->stackFreeUserdata = proc->ctx; thr->stackFree = CoreH_VMAStackFree; @@ -366,9 +468,18 @@ handle Sys_ProcessOpen(uintptr_t pid) OBOS_UNUSED(pid); if (pid > Core_NextPID) return HANDLE_INVALID; + obos_status status = OBOS_CapabilityCheck("core/process-open", true); + if (obos_is_error(status)) + return HANDLE_INVALID; process* proc = Core_LookupProc(pid); if (!proc) return HANDLE_INVALID; + if (proc->euid != Core_GetCurrentThread()->proc->euid) + { + obos_status status = OBOS_CapabilityCheck("core/process-open-arbitrary", false); + if (obos_is_error(status)) + return HANDLE_INVALID; + } proc->refcount++; OBOS_LockHandleTable(OBOS_CurrentHandleTable()); handle_desc* desc = nullptr; @@ -684,7 +795,7 @@ static bool check_unpriv_gid(gid gid) obos_status Sys_SetRESUid(uid ruid, uid euid, uid suid) { - if (Core_GetCurrentThread()->proc->euid != 0) + if (OBOS_CapabilityCheck("core/set-uid", false) == OBOS_STATUS_ACCESS_DENIED) { if ((ruid != -1) && !check_unpriv_uid(ruid)) return OBOS_STATUS_ACCESS_DENIED; @@ -710,7 +821,7 @@ obos_status Sys_SetRESUid(uid ruid, uid euid, uid suid) obos_status Sys_SetRESGid(gid rgid, gid egid, gid sgid) { - if (Core_GetCurrentThread()->proc->euid != 0) + if (OBOS_CapabilityCheck("core/set-gid", false) == OBOS_STATUS_ACCESS_DENIED) { if ((rgid != -1) && !check_unpriv_gid(rgid)) return OBOS_STATUS_ACCESS_DENIED; @@ -758,7 +869,7 @@ obos_status Sys_GetRESGid(gid* rgid, gid* egid, gid* sgid) obos_status Sys_SetUid(uid uid) { - if (Core_GetCurrentThread()->proc->euid == 0) + if (OBOS_CapabilityCheck("core/set-uid", false) == OBOS_STATUS_SUCCESS) { Core_GetCurrentThread()->proc->ruid = uid; Core_GetCurrentThread()->proc->euid = uid; @@ -770,7 +881,7 @@ obos_status Sys_SetUid(uid uid) obos_status Sys_SetGid(gid gid) { - if (Core_GetCurrentThread()->proc->euid == 0) + if (OBOS_CapabilityCheck("core/set-uid", false) == OBOS_STATUS_SUCCESS) { Core_GetCurrentThread()->proc->rgid = gid; Core_GetCurrentThread()->proc->egid = gid; diff --git a/src/oboskrnl/signal.c b/src/oboskrnl/signal.c index 3871a937..f1dbab84 100644 --- a/src/oboskrnl/signal.c +++ b/src/oboskrnl/signal.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,11 @@ obos_status OBOS_Kill(struct thread* as, struct thread* thr, int sigval) if (thr->proc->pid == 0) return OBOS_STATUS_INVALID_OPERATION; if (thr->proc->euid != as->proc->euid && as->proc->euid != 0) - return OBOS_STATUS_ACCESS_DENIED; + { + obos_status status = OBOS_CapabilityCheck("kill-arbitrary", true); + if (obos_is_error(status)) + return status; + } if (sigval == 0) return OBOS_STATUS_SUCCESS; // see man fork.2, "If sig is 0, then no signal is sent, but existence and permission checks are still performed" diff --git a/src/oboskrnl/syscall.c b/src/oboskrnl/syscall.c index da8a315b..deea4f1a 100644 --- a/src/oboskrnl/syscall.c +++ b/src/oboskrnl/syscall.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -49,10 +50,11 @@ #include -// TODO: Check permissions? obos_status Sys_PartProbeDrive(handle ent, bool check_checksum) { - obos_status status = OBOS_STATUS_SUCCESS; + obos_status status = OBOS_CapabilityCheck("fs/part-probe", false); + if (obos_is_error(status)) + return status; handle_desc* dent = OBOS_HandleLookup(OBOS_CurrentHandleTable(), ent, HANDLE_TYPE_DIRENT, false, &status); if (!dent) @@ -86,24 +88,6 @@ void Sys_LibCLog(const char* ustr) Free(OBOS_KernelAllocator, buf, str_len+1); } -static handle Sys_MmFork() -{ - // Zeroed in Mm_ConstructContext - context* ctx = Allocate(Mm_Allocator, sizeof(context), nullptr); - Mm_ConstructContext(ctx); - Mm_ForkContext(ctx, Core_GetCurrentThread()->proc->ctx); - ctx->workingSet.capacity = Core_GetCurrentThread()->proc->ctx->workingSet.capacity; - - OBOS_LockHandleTable(OBOS_CurrentHandleTable()); - handle_desc* desc = nullptr; - handle hnd = OBOS_HandleAllocate(OBOS_CurrentHandleTable(), HANDLE_TYPE_VMM_CONTEXT, &desc); - desc->un.vmm_context = ctx; - OBOS_UnlockHandleTable(OBOS_CurrentHandleTable()); - - return hnd; - -} - void Sys_ExitCurrentProcess(uint32_t exitCode) { Core_ExitCurrentProcess((exitCode & 0xff) << 8); @@ -322,24 +306,45 @@ obos_status Sys_ThreadGetStack(handle thr, void** stack_base, size_t *stack_size obos_status Sys_Shutdown() { - if (Core_GetCurrentThread()->proc->euid != 0) - return OBOS_STATUS_ACCESS_DENIED; + obos_status status = OBOS_CapabilityCheck("power/shutdown", false); + if (obos_is_error(status)) + { + if (status == OBOS_STATUS_ACCESS_DENIED) + return OBOS_STATUS_ACCESS_DENIED; + else + { + OBOS_Error("%s: OBOS_CapabilityCheck returned %d! It is safe to forcefully shutdown your PC\nThis could be a bug, report it\n", __func__, status); + return status; + } + } OBOS_Shutdown(); + OBOS_UNREACHABLE; } obos_status Sys_Reboot() { - if (Core_GetCurrentThread()->proc->euid != 0) - return OBOS_STATUS_ACCESS_DENIED; + obos_status status = OBOS_CapabilityCheck("power/reboot", false); + if (obos_is_error(status)) + { + if (status == OBOS_STATUS_ACCESS_DENIED) + return OBOS_STATUS_ACCESS_DENIED; + else + { + OBOS_Error("%s: OBOS_CapabilityCheck returned %d! It is safe to forcefully reboot your PC\nThis could be a bug, report it\n", __func__, status); + return status; + } + } OBOS_Reboot(); + OBOS_UNREACHABLE; } obos_status Sys_Suspend() { - if (Core_GetCurrentThread()->proc->euid != 0) - return OBOS_STATUS_ACCESS_DENIED; - OBOS_Suspend(); - OBOS_UNREACHABLE; + obos_status status = OBOS_CapabilityCheck("power/suspend", false); + if (obos_is_error(status)) + return status; + + return OBOS_Suspend(); } uintptr_t OBOS_SyscallTable[SYSCALL_END-SYSCALL_BEGIN] = { diff --git a/src/oboskrnl/vfs/fd_sys.c b/src/oboskrnl/vfs/fd_sys.c index 50ab5462..6e3d660c 100644 --- a/src/oboskrnl/vfs/fd_sys.c +++ b/src/oboskrnl/vfs/fd_sys.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -485,8 +486,11 @@ obos_status Sys_FdEOF(const handle desc) obos_status Sys_FdIoctl(handle desc, uintptr_t request, void* argp, size_t sz_argp) { + obos_status status = OBOS_CapabilityCheck("fs/ioctl", true); + if (obos_is_error(status)) + return status; + OBOS_LockHandleTable(OBOS_CurrentHandleTable()); - obos_status status = OBOS_STATUS_SUCCESS; handle_desc* fd = OBOS_HandleLookup(OBOS_CurrentHandleTable(), desc, HANDLE_TYPE_FD, false, &status); if (!fd) { @@ -1026,6 +1030,10 @@ void OBOS_OpenStandardFDs(handle_table* tbl) // makes new dirty pages. void Sys_Sync() { + obos_status status = OBOS_CapabilityCheck("fs/sync", true); + if (obos_is_error(status)) + return; + Mm_PageWriterOperation = PAGE_WRITER_SYNC_FILE; Mm_WakePageWriter(true); Mm_WakePageWriter(true); @@ -1044,9 +1052,13 @@ obos_status Sys_Mount(const char* uat, const char* uon) if (!uat || !uon) return OBOS_STATUS_INVALID_ARGUMENT; + obos_status status = OBOS_CapabilityCheck("fs/mount", false); + if (obos_is_error(status)) + return status; + char* at = nullptr; size_t sz_path = 0; - obos_status status = OBOSH_ReadUserString(uat, nullptr, &sz_path); + status = OBOSH_ReadUserString(uat, nullptr, &sz_path); if (obos_is_error(status)) return status; at = ZeroAllocate(OBOS_KernelAllocator, sz_path+1, sizeof(char), nullptr); @@ -1086,9 +1098,13 @@ obos_status Sys_Mount(const char* uat, const char* uon) obos_status Sys_Unmount(const char* uat) { + obos_status status = OBOS_CapabilityCheck("fs/unmount", false); + if (obos_is_error(status)) + return status; + char* at = nullptr; size_t sz_path = 0; - obos_status status = OBOSH_ReadUserString(uat, nullptr, &sz_path); + status = OBOSH_ReadUserString(uat, nullptr, &sz_path); if (obos_is_error(status)) return status; at = ZeroAllocate(OBOS_KernelAllocator, sz_path+1, sizeof(char), nullptr); @@ -1846,9 +1862,13 @@ obos_status Sys_SymLinkAt(const char* utarget, handle dirfd, const char* ulink) obos_status Sys_LinkAt(handle olddirfd, const char *utarget, handle newdirfd, const char *ulink, int flags) { + obos_status status = OBOS_CapabilityCheck("fs/hardlink", false); + if (obos_is_error(status)) + return status; + char* target = nullptr; size_t sz_path = 0; - obos_status status = OBOSH_ReadUserString(utarget, nullptr, &sz_path); + status = OBOSH_ReadUserString(utarget, nullptr, &sz_path); if (obos_is_error(status)) return status; target = ZeroAllocate(OBOS_KernelAllocator, sz_path+1, sizeof(char), nullptr); @@ -2837,6 +2857,10 @@ obos_status Sys_FChmodAt(handle dirfd, const char* upathname, int mode, int flag { obos_status status = 0; + status = OBOS_CapabilityCheck("fs/chmod", true); + if (obos_is_error(status)) + return status; + char* pathname = nullptr; size_t sz_path = 0; status = OBOSH_ReadUserString(upathname, nullptr, &sz_path); @@ -2958,7 +2982,9 @@ obos_status Sys_FChmodAt(handle dirfd, const char* upathname, int mode, int flag obos_status Sys_FChownAt(handle dirfd, const char *upathname, uid owner, gid group, int flags) { - obos_status status = 0; + obos_status status = OBOS_CapabilityCheck("fs/chown", false); + if (obos_is_error(status)) + return status; char* pathname = nullptr; size_t sz_path = 0; @@ -3049,12 +3075,6 @@ obos_status Sys_FChownAt(handle dirfd, const char *upathname, uid owner, gid gro have_target: OBOS_ENSURE(target); - if (Sys_GetUid() != ROOT_UID) - { - status = OBOS_STATUS_ACCESS_DENIED; - goto fail; - } - driver_header* header = Vfs_GetVnodeDriver(target); if (!header) { diff --git a/src/oboskrnl/vfs/init.c b/src/oboskrnl/vfs/init.c index a8976960..54805318 100644 --- a/src/oboskrnl/vfs/init.c +++ b/src/oboskrnl/vfs/init.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -111,6 +112,7 @@ void Vfs_Initialize() Vfs_DevRoot = VfsH_DirentLookup(OBOS_DEV_PREFIX); if (!Vfs_DevRoot) OBOS_Panic(OBOS_PANIC_FATAL_ERROR, "%s: Could not find directory at OBOS_DEV_PREFIX (%s) specified at build time.\n", __func__, OBOS_DEV_PREFIX); + OBOS_CapabilityInitialize(); if (root_partid) Free(OBOS_KernelAllocator, root_partid, strlen(root_partid)); if (root_uuid) diff --git a/src/oboskrnl/vfs/socket.c b/src/oboskrnl/vfs/socket.c index 6d255377..763bba2e 100644 --- a/src/oboskrnl/vfs/socket.c +++ b/src/oboskrnl/vfs/socket.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -271,6 +272,19 @@ static obos_status inet_socket(int type, int protocol, fd* out) if (protocol == IPPROTO_UDP && type != SOCK_DGRAM) return OBOS_STATUS_INVALID_ARGUMENT; + if (protocol == IPPROTO_TCP) + { + obos_status status = OBOS_CapabilityCheck("net/tcp", true); + if (obos_is_error(status)) + return status; + } + if (protocol == IPPROTO_UDP) + { + obos_status status = OBOS_CapabilityCheck("net/udp", true); + if (obos_is_error(status)) + return status; + } + if (!get_sock_ops(AF_INET, type, protocol)) return OBOS_STATUS_INVALID_ARGUMENT; @@ -306,9 +320,15 @@ obos_status Net_Socket(int domain, int type, int protocol, fd* out) obos_status status = OBOS_STATUS_UNIMPLEMENTED; switch (domain) { case AF_INET: + status = OBOS_CapabilityCheck("net/ipv4", true); + if (obos_is_error(status)) + return status; status = inet_socket(type, protocol, out); break; case AF_UNIX: + status = OBOS_CapabilityCheck("unix-socket", true); + if (obos_is_error(status)) + return status; status = local_socket(type, protocol, out); break; default: break;