feat: add permission consent prompts, drop permitted capability mask

This commit is contained in:
daniel committed 2026-09-30 08:32:48 +02:00
1 parent c54181103f
commit ae5e84592d
25 files changed
+1632 -94

No files matched your search

+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 239
#define MONTAUK_BUILD_NUMBER 251
-2
View File
@@ -144,7 +144,6 @@ namespace montauk::abi {
static int Sys_GetCaps(SpawnCapabilities* out) {
auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr || out == nullptr) return -1;
out->permitted = proc->permittedCaps;
out->effective = proc->effectiveCaps;
out->delegable = proc->delegableCaps;
return 0;
@@ -214,7 +213,6 @@ 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++;
+24 -16
View File
@@ -11,6 +11,7 @@
/* For common functions used by multiple syscall implementations*/
#include "Common.hpp"
#include <Sched/Consent.hpp>
/* Syscall impl. includes */
#include "Process.hpp" // SYS_EXIT, SYS_YIELD, SYS_SLEEP_MS, SYS_GETPID, SYS_WAITPID, SYS_SPAWN, SYS_GETARGS, SYS_PROCLIST, SYS_KILL
@@ -200,11 +201,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;
if (!Sched::Consent::Require(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;
!Sched::Consent::Require(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;
@@ -248,7 +249,7 @@ namespace montauk::abi {
Sys_GetTime((DateTime*)frame->arg1);
return 0;
case SYS_SETUNIXTIME:
if (!Sched::HasCapability(CAP_SET_TIME)) return SYS_ERR_PERMISSION;
if (!Sched::Consent::Require(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);
@@ -274,7 +275,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 (!Sched::Consent::Require(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:
@@ -330,7 +331,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 (!Sched::Consent::Require(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:
@@ -438,20 +439,20 @@ namespace montauk::abi {
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;
if (!Sched::Consent::Require(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 (!Sched::Consent::Require(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;
if (!Sched::Consent::Require(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;
if (!Sched::Consent::Require(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
return Sys_FsSync();
case SYS_FSFORMAT:
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
if (!Sched::Consent::Require(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:
@@ -517,6 +518,13 @@ namespace montauk::abi {
case SYS_GETCAPS:
if (!UserMemory::Writable<SpawnCapabilities>(frame->arg1)) return -1;
return Sys_GetCaps((SpawnCapabilities*)frame->arg1);
case SYS_CONSENT_REGISTER:
return Sched::Consent::Register();
case SYS_CONSENT_NEXT:
if (!UserMemory::Writable<ConsentRequest>(frame->arg1)) return -1;
return Sched::Consent::Next((ConsentRequest*)frame->arg1);
case SYS_CONSENT_REPLY:
return Sched::Consent::Reply(frame->arg1, (int)frame->arg2);
case SYS_THREAD_SPAWN:
return Sys_ThreadSpawn(frame->arg1, frame->arg2, frame->arg3);
case SYS_THREAD_EXIT:
@@ -560,7 +568,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 (!Sched::Consent::Require(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);
@@ -568,22 +576,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 (!Sched::Consent::Require(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;
if (!Sched::Consent::Require(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;
if (!Sched::Consent::Require(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 (!Sched::Consent::Require(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);
@@ -595,7 +603,7 @@ namespace montauk::abi {
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;
if (!Sched::Consent::Require(CAP_SET_TIME)) return SYS_ERR_PERMISSION;
return Sys_SetTZ((int32_t)frame->arg1);
case SYS_GETTZ:
return Sys_GetTZ();
+44 -9
View File
@@ -352,6 +352,13 @@ namespace montauk::abi {
/* Storage.hpp */
static constexpr uint64_t SYS_VOLINFO = 190; // (VolInfo*, maxCount) -> count, -1 bad pointer
/* Consent.hpp -- first-use consent prompts (docs/consent.md). Only the
session's consent agent (the desktop) may register, drain requests
and answer them. */
static constexpr uint64_t SYS_CONSENT_REGISTER = 191; // () -> 0, -13 lacks CAP_COMPOSITOR, -1 another agent is live
static constexpr uint64_t SYS_CONSENT_NEXT = 192; // (ConsentRequest*) -> 1 delivered, 0 none queued
static constexpr uint64_t SYS_CONSENT_REPLY = 193; // (requestId, CONSENT_ALLOW/DENY) -> 0, -1 stale id
/* 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;
@@ -396,26 +403,55 @@ namespace montauk::abi {
"login grants it to the desktop directly");
static constexpr int SYS_ERR_PERMISSION = -13;
// A process holds two independent sets: what it may use (effective) and
// what it may pass to children (delegable). A launcher can delegate what
// it never uses itself, so neither set bounds the other.
struct SpawnCapabilities {
uint64_t permitted;
uint64_t effective;
uint64_t delegable;
};
// Both of the child's sets must come out of the parent's delegable set.
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 &&
return ((child.effective | child.delegable) & ~CAP_ALL) == 0 &&
(child.effective & ~parentDelegable) == 0 &&
(child.delegable & ~parentDelegable) == 0;
}
static_assert(ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN, 0}, CAP_NETWORK_ADMIN));
{CAP_NETWORK_ADMIN, 0}, CAP_NETWORK_ADMIN));
static_assert(ValidCapabilityDelegation(
{0, CAP_NETWORK_ADMIN}, CAP_NETWORK_ADMIN));
static_assert(!ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN}, 0));
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN}, 0));
static_assert(!ValidCapabilityDelegation(
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN | CAP_SET_TIME, 0}, CAP_ALL));
{CAP_NETWORK_ADMIN | CAP_SET_TIME, 0}, CAP_NETWORK_ADMIN));
static_assert(!ValidCapabilityDelegation(
{0, CAP_NETWORK_ADMIN | CAP_SET_TIME}, CAP_NETWORK_ADMIN));
/* Capabilities a user can grant at first use through a consent prompt.
A consent grant never exceeds what the process's parent could have
delegated to it at spawn (its consent ceiling), is never delegable
onwards, and is issued only by the session's consent agent -- so it is
deferred delegation, not amplification. */
static constexpr uint64_t CAP_CONSENT_ELIGIBLE =
CAP_STORAGE_ADMIN | CAP_NETWORK_ADMIN | CAP_SET_TIME |
CAP_DISPLAY_ADMIN | CAP_LOG_READ;
static_assert((CAP_CONSENT_ELIGIBLE & ~CAP_ADMIN_SESSION) == 0,
"consent can only hand out authority an admin session holds");
static constexpr int CONSENT_DENY = 0;
static constexpr int CONSENT_ALLOW = 1;
// A request left unanswered this long is denied (and not remembered).
static constexpr uint64_t CONSENT_TIMEOUT_MS = 120000;
struct ConsentRequest {
uint64_t id;
uint64_t capability; // exactly one CAP_* bit
int32_t pid;
uint32_t imageProtected; // 1 when writing the image needs CAP_SYSTEM_IMAGE
char imagePath[256];
};
// Generic USB errors. Claims are restricted to interfaces without a
@@ -741,7 +777,6 @@ 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;
};