feat: implement kernel capability model

This commit is contained in:
daniel committed 2026-08-29 16:53:04 +02:00
1 parent 9051b8a16e
commit e7646bbbdb
47 files changed
+1507 -264

No files matched your search

+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 155
#define MONTAUK_BUILD_NUMBER 168
+14
View File
@@ -17,8 +17,14 @@
#include <Ipc/Ipc.hpp>
#include <Timekeeping/Time.hpp>
#include "Path.hpp"
#include <Fs/ProtectedPaths.hpp>
namespace montauk::abi {
static bool CanModifyFilePath(const char* resolved) {
uint64_t required = Fs::RequiredFileWriteCapability(resolved);
return required == 0 || Sched::HasCapability(required);
}
static int Sys_Open(const char* path) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
@@ -108,12 +114,14 @@ namespace montauk::abi {
static int Sys_FCreate(const char* path) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
return Ipc::CreateFileHandle(resolved);
}
static int Sys_FDelete(const char* path) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
return Fs::Vfs::VfsDelete(resolved);
}
@@ -138,6 +146,9 @@ namespace montauk::abi {
bool useCurrent) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
// Timestamps are file state like any other: a protected path must not
// be mutable through a side door that skips the write check.
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
if (useCurrent) {
int64_t now = Timekeeping::GetUnixTimestamp();
atime = now;
@@ -149,6 +160,7 @@ namespace montauk::abi {
static int Sys_FMkdir(const char* path) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
return Fs::Vfs::VfsMkdir(resolved);
}
@@ -157,6 +169,8 @@ namespace montauk::abi {
char resolvedNew[256];
if (!ResolveProcessPath(oldPath, resolvedOld, sizeof(resolvedOld))) return -1;
if (!ResolveProcessPath(newPath, resolvedNew, sizeof(resolvedNew))) return -1;
if (!CanModifyFilePath(resolvedOld) || !CanModifyFilePath(resolvedNew))
return SYS_ERR_PERMISSION;
return Fs::Vfs::VfsRename(resolvedOld, resolvedNew);
}
+4 -3
View File
@@ -5,6 +5,7 @@
*/
#pragma once
#include <Memory/UserRange.hpp>
#include <cstdint>
#include <Sched/Scheduler.hpp>
#include <Memory/Paging.hpp>
@@ -155,7 +156,7 @@ namespace montauk::abi {
// user TLB entry can otherwise corrupt the frame's next owner.
while (released != nullptr) {
HeapAlloc* next = released->next;
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, released->va,
Memory::UnmapAndFreeUserRange(proc->pml4Phys, released->va,
released->numPages);
Sched::ReleaseUserHeapRange(slot, released->va,
released->numPages * 0x1000ULL);
@@ -218,7 +219,7 @@ namespace montauk::abi {
resident++;
g_heapLocks[slot].Release();
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, addr, pages);
Memory::UnmapAndFreeUserRange(proc->pml4Phys, addr, pages);
Sched::ReleaseUserHeapRange(slot, addr, size);
g_heapLocks[slot].Acquire();
Sched::g_allocatedPages[slot] -= resident;
@@ -308,7 +309,7 @@ namespace montauk::abi {
}
}
g_heapLocks[slot].Release();
Ipc::ShootdownUserRange(proc->pml4Phys, addr, (uint32_t)pages);
Memory::ShootdownUserRange(proc->pml4Phys, addr, (uint32_t)pages);
return 0;
}
+1 -1
View File
@@ -23,7 +23,7 @@ namespace montauk::abi {
for (int i = 0; ver[i]; i++) outInfo->osVersion[i] = ver[i];
outInfo->osVersion[5] = '\0';
outInfo->apiVersion = 10;
outInfo->apiVersion = 11;
outInfo->maxProcesses = Sched::MaxProcesses;
outInfo->buildNumber = MONTAUK_BUILD_NUMBER;
}
+33 -6
View File
@@ -15,12 +15,15 @@ namespace montauk::abi {
static constexpr uint32_t RedirOutputStreamCapacity = 64 * 1024;
static int Sys_SpawnRedir(const char* path, const char* args) {
static int Sys_SpawnRedirInternal(
const char* path, const char* args,
const SpawnCapabilities* capabilities) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
int parentSlot = Ipc::CurrentSlot();
int childPid = Sched::Spawn(resolved, args, false);
int childPid = Sched::Spawn(resolved, args, false, nullptr, 0,
capabilities);
if (childPid < 0) return -1;
auto* child = Sched::GetProcessByPid(childPid);
@@ -83,28 +86,50 @@ namespace montauk::abi {
return childPid;
}
static int Sys_SpawnRedir(const char* path, const char* args) {
return Sys_SpawnRedirInternal(path, args, nullptr);
}
static int Sys_SpawnRedirCaps(const char* path, const char* args,
const SpawnCapabilities* requested) {
auto* parent = Sched::GetCurrentProcessPtr();
if (parent == nullptr || requested == nullptr) return -1;
SpawnCapabilities copy = *requested;
if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) {
return SYS_ERR_PERMISSION;
}
return Sys_SpawnRedirInternal(path, args, &copy);
}
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
return SYS_ERR_PERMISSION;
Ipc::HandleSnapshot snapshot;
Ipc::Stream* stream = GetRedirOutStream(child, snapshot);
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
if (!child->redirected || stream == nullptr) return -1;
return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true);
}
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
return SYS_ERR_PERMISSION;
Ipc::HandleSnapshot snapshot;
Ipc::Stream* stream = GetRedirInStream(child, snapshot);
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
if (!child->redirected || stream == nullptr) return -1;
return WriteAllToStream(stream, (const uint8_t*)data, len);
}
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
if (key == nullptr) return -1;
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
return SYS_ERR_PERMISSION;
Ipc::HandleSnapshot snapshot;
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child, snapshot);
if (child == nullptr || !child->redirected || mailbox == nullptr) return -1;
if (!child->redirected || mailbox == nullptr) return -1;
for (;;) {
uint64_t observedWake = Sched::ObserveObjectWake(mailbox);
@@ -120,7 +145,9 @@ namespace montauk::abi {
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
auto* child = Sched::GetProcessByPid(childPid);
if (child == nullptr || !child->redirected) return -1;
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
return SYS_ERR_PERMISSION;
if (!child->redirected) return -1;
child->termCols = cols;
child->termRows = rows;
return 0;
+3 -2
View File
@@ -4,6 +4,7 @@
* Copyright (c) 2026 Daniel Hammer
*/
#include <Memory/UserRange.hpp>
#include <cstdint>
#include <Sched/Scheduler.hpp>
#include <Sched/ElfLoader.hpp>
@@ -133,7 +134,7 @@ namespace montauk::abi {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc != nullptr) {
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
Memory::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
(libEnd - libBase) / 0x1000ULL);
}
@@ -202,7 +203,7 @@ namespace montauk::abi {
uint64_t libBase = GetLibSlotBase(i);
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
Memory::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
(libEnd - libBase) / 0x1000ULL);
g_libTable[slot][i].inUse = false;
+5
View File
@@ -31,6 +31,11 @@ namespace montauk::abi {
// action; any other value records it as the pending action. Returns the
// pending action for queries, or 0 when recording one.
static int64_t Sys_PowerRequest(int action) {
// Polled by the session leader every second; deliberately silent and
// non-destructive, so it neither floods the log nor races login for
// the request it is about to hand over by exiting.
if (action == POWER_REQ_PEEK) return (int64_t)g_pendingPowerAction;
if (action == POWER_REQ_QUERY) {
int pending = g_pendingPowerAction;
g_pendingPowerAction = POWER_REQ_QUERY;
+50
View File
@@ -86,6 +86,39 @@ namespace montauk::abi {
return childPid;
}
static int Sys_SpawnCaps(const char* path, const char* args,
const char* user, const SpawnCapabilities* requested) {
auto* parent = Sched::GetCurrentProcessPtr();
if (parent == nullptr || requested == nullptr) return -1;
// Snapshot all security-sensitive userspace inputs before evaluating
// them. This prevents another thread from changing a mask or owner
// name between validation and process creation.
SpawnCapabilities copy = *requested;
char childUser[32];
const char* userOverride = nullptr;
if (user != nullptr) {
if (!Sched::HasCapability(CAP_USER_ADMIN))
return SYS_ERR_PERMISSION;
int i = 0;
for (; i < 31 && user[i]; i++) childUser[i] = user[i];
childUser[i] = '\0';
userOverride = childUser;
}
// Authority may only diminish down the process tree. In particular,
// possessing a capability is insufficient to pass it: the parent must
// also hold it in its delegable set.
if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) {
return SYS_ERR_PERMISSION;
}
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
return Sched::Spawn(resolved, args, true, nullptr, 0, &copy,
userOverride);
}
// Copy the absolute path this process was spawned from (argv[0]).
static int Sys_GetExecPath(char* buf, uint64_t maxLen) {
auto* proc = Sched::GetCurrentProcessPtr();
@@ -150,12 +183,29 @@ namespace montauk::abi {
}
buf[count].heapUsed = Sched::g_allocatedPages[i] * 0x1000;
buf[count].cpuTimeMs = proc->cpuTimeMs;
buf[count].permittedCaps = proc->permittedCaps;
buf[count].effectiveCaps = proc->effectiveCaps;
buf[count].delegableCaps = proc->delegableCaps;
count++;
}
return count;
}
static int Sys_Kill(int pid) {
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) {
int ancestor = pid;
bool descendant = false;
for (int depth = 0; depth < Sched::MaxProcesses; depth++) {
auto* target = Sched::GetProcessByPid(ancestor);
if (target == nullptr || target->parentPid < 0) break;
if (target->parentPid == Sched::GetCurrentPid()) {
descendant = true;
break;
}
ancestor = target->parentPid;
}
if (!descendant) return SYS_ERR_PERMISSION;
}
return Sched::KillProcess(pid);
}
+58 -1
View File
@@ -150,6 +150,16 @@ namespace montauk::abi {
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
return (int64_t)Sys_Spawn((const char*)frame->arg1,
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
case SYS_SPAWN_CAPS:
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
if (frame->arg3 != 0 && !UserMemory::String(frame->arg3, 32)) return -1;
if (!UserMemory::Readable<SpawnCapabilities>(frame->arg4)) return -1;
return (int64_t)Sys_SpawnCaps(
(const char*)frame->arg1,
frame->arg2 ? (const char*)frame->arg2 : nullptr,
frame->arg3 ? (const char*)frame->arg3 : nullptr,
(const SpawnCapabilities*)frame->arg4);
case SYS_SPAWN_ENV:
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
@@ -188,8 +198,11 @@ namespace montauk::abi {
(uint64_t)frame->arg2 * sizeof(DisplayModeInfo), true)) return -1;
return Sys_DisplayModes((DisplayModeInfo*)frame->arg1, (int)frame->arg2);
case SYS_DISPLAYSETMODE:
if (!Sched::HasCapability(CAP_DISPLAY_ADMIN)) return SYS_ERR_PERMISSION;
return Sys_DisplaySetMode((int)frame->arg1);
case SYS_DISPLAYBRIGHTNESS:
if ((int64_t)frame->arg1 >= 0 &&
!Sched::HasCapability(CAP_DISPLAY_ADMIN)) return SYS_ERR_PERMISSION;
return Sys_DisplayBrightness((int)frame->arg1);
case SYS_GETEXECPATH:
if (!UserMemory::Range(frame->arg2 ? frame->arg1 : frame->arg1, frame->arg2, true)) return -1;
@@ -210,18 +223,30 @@ namespace montauk::abi {
if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1;
return (int64_t)Sys_GetArgs((char*)frame->arg1, frame->arg2);
case SYS_RESET:
if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION;
Sys_Reset();
return 0;
case SYS_SHUTDOWN:
if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION;
Sys_Shutdown();
return 0;
case SYS_POWER_REQUEST:
if (frame->arg1 != POWER_REQ_QUERY &&
frame->arg1 != POWER_REQ_SHUTDOWN &&
frame->arg1 != POWER_REQ_REBOOT &&
frame->arg1 != POWER_REQ_PEEK) return -1;
if (frame->arg1 == POWER_REQ_QUERY) {
if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION;
} else if (!Sched::HasCapability(CAP_POWER_REQUEST)) {
return SYS_ERR_PERMISSION;
}
return Sys_PowerRequest((int)frame->arg1);
case SYS_GETTIME:
if (!UserMemory::Writable<DateTime>(frame->arg1)) return -1;
Sys_GetTime((DateTime*)frame->arg1);
return 0;
case SYS_SETUNIXTIME:
if (!Sched::HasCapability(CAP_SET_TIME)) return SYS_ERR_PERMISSION;
return Sys_SetUnixTime((int64_t)frame->arg1);
case SYS_SOCKET:
return (int64_t)Sys_Socket((int)frame->arg1);
@@ -247,6 +272,7 @@ namespace montauk::abi {
Sys_GetNetCfg((NetCfg*)frame->arg1);
return 0;
case SYS_SETNETCFG:
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Readable<NetCfg>(frame->arg1)) return -1;
return (int64_t)Sys_SetNetCfg((const NetCfg*)frame->arg1);
case SYS_NETSTATUS:
@@ -302,6 +328,7 @@ namespace montauk::abi {
if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1;
return Sys_GetRandom((uint8_t*)frame->arg1, frame->arg2);
case SYS_LOG:
if (!Sched::HasCapability(CAP_LOG_READ)) return SYS_ERR_PERMISSION;
if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1;
return Kt::ReadKernelLogBuffer((char*)frame->arg1, frame->arg2);
case SYS_MOUSESTATE:
@@ -316,6 +343,14 @@ namespace montauk::abi {
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
return (int64_t)Sys_SpawnRedir((const char*)frame->arg1,
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
case SYS_SPAWN_REDIR_CAPS:
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
if (!UserMemory::Readable<SpawnCapabilities>(frame->arg3)) return -1;
return (int64_t)Sys_SpawnRedirCaps(
(const char*)frame->arg1,
frame->arg2 ? (const char*)frame->arg2 : nullptr,
(const SpawnCapabilities*)frame->arg3);
case SYS_CHILDIO_READ:
if ((int64_t)frame->arg3 < 0) return -1;
if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return -1;
@@ -363,6 +398,7 @@ namespace montauk::abi {
case SYS_SETSESSION:
return (int64_t)Sys_SetSession();
case SYS_KILLSESSION:
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) return SYS_ERR_PERMISSION;
return (int64_t)Sys_KillSession((int)frame->arg1);
case SYS_DEVLIST:
if ((int64_t)frame->arg2 < 0) return -1;
@@ -388,21 +424,28 @@ namespace montauk::abi {
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(PartInfo), true)) return -1;
return (int64_t)Sys_PartList((PartInfo*)frame->arg1, (int)frame->arg2);
case SYS_DISKREAD:
if (!Sched::HasCapability(CAP_RAW_STORAGE)) return SYS_ERR_PERMISSION;
return (int64_t)Sys_DiskRead((int)frame->arg1, frame->arg2,
(uint32_t)frame->arg3, (void*)frame->arg4);
case SYS_DISKWRITE:
if (!Sched::HasCapability(CAP_RAW_STORAGE)) return SYS_ERR_PERMISSION;
return (int64_t)Sys_DiskWrite((int)frame->arg1, frame->arg2,
(uint32_t)frame->arg3, (const void*)frame->arg4);
case SYS_GPTINIT:
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
return (int64_t)Sys_GptInit((int)frame->arg1);
case SYS_GPTADD:
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Readable<GptAddParams>(frame->arg1)) return -1;
return (int64_t)Sys_GptAdd((const GptAddParams*)frame->arg1);
case SYS_FSMOUNT:
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
return (int64_t)Sys_FsMount((int)frame->arg1, (int)frame->arg2);
case SYS_FS_SYNC:
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
return Sys_FsSync();
case SYS_FSFORMAT:
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Readable<FsFormatParams>(frame->arg1)) return -1;
return (int64_t)Sys_FsFormat((const FsFormatParams*)frame->arg1);
case SYS_AUDIOOPEN:
@@ -428,6 +471,7 @@ namespace montauk::abi {
return Sys_UsbList((UsbInterfaceInfo*)frame->arg1, (int)maxCount);
}
case SYS_USB_CLAIM:
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
if (frame->arg1 == 0 || frame->arg1 > 16 || frame->arg2 > 255)
return USB_ERR_INVALID;
return Sys_UsbClaim((uint8_t)frame->arg1, (uint8_t)frame->arg2);
@@ -469,16 +513,20 @@ namespace montauk::abi {
case SYS_THREAD_SELF:
return Sys_ThreadSelf();
case SYS_BTSCAN:
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
if ((int64_t)frame->arg2 < 0) return -1;
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtScanResult), true)) return -1;
return Sys_BtScan((BtScanResult*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3);
case SYS_BTCONNECT:
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
return Sys_BtConnect((const uint8_t*)frame->arg1);
case SYS_BTDISCONNECT:
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
return Sys_BtDisconnect((const uint8_t*)frame->arg1);
case SYS_BTSETADDR:
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
return Sys_BtSetAddr((const uint8_t*)frame->arg1);
case SYS_BTBONDS:
@@ -486,6 +534,7 @@ namespace montauk::abi {
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtBondInfo), true)) return -1;
return Sys_BtBonds((BtBondInfo*)frame->arg1, (int)frame->arg2);
case SYS_BTFORGET:
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
return Sys_BtForget((const uint8_t*)frame->arg1);
case SYS_BTLIST:
@@ -496,6 +545,7 @@ namespace montauk::abi {
if (!UserMemory::Writable<BtAdapterInfo>(frame->arg1)) return -1;
return Sys_BtInfo((BtAdapterInfo*)frame->arg1);
case SYS_WIFI_SCAN:
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
if ((int64_t)frame->arg2 < 0) return -1;
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1;
return Sys_WifiScan((WifiNetwork*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3);
@@ -503,18 +553,22 @@ namespace montauk::abi {
if (!UserMemory::Writable<WifiInfo>(frame->arg1)) return -1;
return Sys_WifiInfo((WifiInfo*)frame->arg1);
case SYS_WIFI_CONNECT:
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::String(frame->arg1, 64)) return -1;
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1;
return Sys_WifiConnect((const char*)frame->arg1, (const char*)frame->arg2);
case SYS_WIFI_DISCONNECT:
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
return Sys_WifiDisconnect();
case SYS_WIFI_SCAN_START:
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
return Sys_WifiScanStart((uint32_t)frame->arg1);
case SYS_WIFI_RESULTS:
if ((int64_t)frame->arg2 < 0) return -1;
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1;
return Sys_WifiResults((WifiNetwork*)frame->arg1, (int)frame->arg2);
case SYS_WIFI_CONNECT_ASYNC:
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::String(frame->arg1, 64)) return -1;
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1;
return Sys_WifiConnectAsync((const char*)frame->arg1, (const char*)frame->arg2);
@@ -523,12 +577,15 @@ namespace montauk::abi {
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(NetIfInfo), true)) return -1;
return Sys_NetIfs((NetIfInfo*)frame->arg1, (int)frame->arg2);
case SYS_SUSPEND:
if (!Sched::HasCapability(CAP_SUSPEND)) return SYS_ERR_PERMISSION;
return Sys_Suspend();
case SYS_SETTZ:
if (!Sched::HasCapability(CAP_SET_TIME)) return SYS_ERR_PERMISSION;
return Sys_SetTZ((int32_t)frame->arg1);
case SYS_GETTZ:
return Sys_GetTZ();
case SYS_SETUSER:
if (!Sched::HasCapability(CAP_USER_ADMIN)) return SYS_ERR_PERMISSION;
if (!UserMemory::String(frame->arg2, kMaxUserNameBytes)) return -1;
return Sys_SetUser((int)frame->arg1, (const char*)frame->arg2);
case SYS_GETUSER:
@@ -670,7 +727,7 @@ namespace montauk::abi {
Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR="
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", "
<< (SYS_USB_BULK_IN_READ + 1) << " syscall slots)";
<< (SYS_SPAWN_REDIR_CAPS + 1) << " syscall slots)";
}
}
+67
View File
@@ -335,6 +335,63 @@ namespace montauk::abi {
static constexpr uint64_t SYS_USB_BULK_IN_START = 182; // (handle, transferBytes, buffers)
static constexpr uint64_t SYS_USB_BULK_IN_STOP = 183; // (handle)
static constexpr uint64_t SYS_USB_BULK_IN_READ = 184; // (handle, data, len) -> bytes
static constexpr uint64_t SYS_SPAWN_CAPS = 185;
static constexpr uint64_t SYS_SPAWN_REDIR_CAPS = 186;
/* Kernel-owned process capabilities. User identities may namespace
per-user resources, but never participate in authorization decisions. */
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
static constexpr uint64_t CAP_POWER_REQUEST = 1ULL << 1;
static constexpr uint64_t CAP_POWER_CONTROL = 1ULL << 2;
static constexpr uint64_t CAP_SUSPEND = 1ULL << 3;
static constexpr uint64_t CAP_STORAGE_ADMIN = 1ULL << 4;
static constexpr uint64_t CAP_RAW_STORAGE = 1ULL << 5;
static constexpr uint64_t CAP_NETWORK_ADMIN = 1ULL << 6;
static constexpr uint64_t CAP_SET_TIME = 1ULL << 7;
static constexpr uint64_t CAP_USER_ADMIN = 1ULL << 8;
static constexpr uint64_t CAP_DISPLAY_ADMIN = 1ULL << 9;
static constexpr uint64_t CAP_DEVICE_ADMIN = 1ULL << 10;
static constexpr uint64_t CAP_LOG_READ = 1ULL << 11;
/* Write to the program images the system boots and runs (0:/os,
0:/apps). Deliberately separate from CAP_STORAGE_ADMIN: grants are
keyed on binary path, so writing an image is equivalent to acquiring
whatever that image is granted at its next launch. Formatting a data
volume must not carry that authority with it. */
static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12;
static constexpr uint64_t CAP_ALL = (1ULL << 13) - 1;
static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND;
static constexpr uint64_t CAP_ADMIN_SESSION =
CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN |
CAP_RAW_STORAGE | CAP_NETWORK_ADMIN | CAP_SET_TIME | CAP_USER_ADMIN |
CAP_DISPLAY_ADMIN | CAP_DEVICE_ADMIN | CAP_LOG_READ;
static_assert((CAP_STANDARD_SESSION & ~CAP_ADMIN_SESSION) == 0);
static_assert((CAP_ADMIN_SESSION & CAP_POWER_CONTROL) == 0,
"final power control belongs only to the session supervisor");
static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0,
"an admin session must not imply authority to rewrite the "
"programs it launches; grant CAP_SYSTEM_IMAGE per binary");
static constexpr int SYS_ERR_PERMISSION = -13;
struct SpawnCapabilities {
uint64_t permitted;
uint64_t effective;
uint64_t delegable;
};
constexpr bool ValidCapabilityDelegation(const SpawnCapabilities& child,
uint64_t parentDelegable) {
return (child.permitted & ~CAP_ALL) == 0 &&
(child.effective & ~child.permitted) == 0 &&
(child.delegable & ~child.permitted) == 0 &&
(child.permitted & ~parentDelegable) == 0 &&
(child.delegable & ~parentDelegable) == 0;
}
static_assert(ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN, 0}, CAP_NETWORK_ADMIN));
static_assert(!ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN}, 0));
static_assert(!ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN | CAP_SET_TIME, 0}, CAP_ALL));
// Generic USB errors. Claims are restricted to interfaces without a
@@ -351,10 +408,17 @@ namespace montauk::abi {
// Graceful power-off request actions (SYS_POWER_REQUEST). The desktop posts
// a pending action and exits; login.elf reads it, runs the shutdown stages,
// then issues the matching SYS_SHUTDOWN / SYS_RESET.
//
// A request can also be posted from inside the session -- the shell's
// shutdown builtin does. login only looks at it once the session leader
// exits, so the leader has to notice and stand down: POWER_REQ_PEEK is the
// non-destructive read it polls with. Only login consumes (QUERY), so a
// leader that peeks cannot swallow the request it is meant to act on.
enum PowerRequestAction : int {
POWER_REQ_QUERY = 0, // read-and-clear the pending action
POWER_REQ_SHUTDOWN = 1,
POWER_REQ_REBOOT = 2,
POWER_REQ_PEEK = 3, // read the pending action without clearing it
};
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
@@ -631,6 +695,9 @@ namespace montauk::abi {
char name[64];
uint64_t heapUsed; // Distance from UserHeapBase to high-water mark
uint64_t cpuTimeMs; // accumulated scheduler runtime
uint64_t permittedCaps;
uint64_t effectiveCaps;
uint64_t delegableCaps;
};
// Bluetooth scan result (returned by SYS_BTSCAN)