feat: add compositor capability

This commit is contained in:
daniel committed 2026-09-19 10:28:54 +02:00
1 parent 75e85f24ef
commit 95e8a739d4
10 files changed
+57 -27

No files matched your search

+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once #pragma once
#define MONTAUK_BUILD_NUMBER 236 #define MONTAUK_BUILD_NUMBER 239
+11 -1
View File
@@ -372,7 +372,14 @@ namespace montauk::abi {
whatever that image is granted at its next launch. Formatting a data whatever that image is granted at its next launch. Formatting a data
volume must not carry that authority with it. */ volume must not carry that authority with it. */
static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12; static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12;
static constexpr uint64_t CAP_ALL = (1ULL << 13) - 1; /* Map other processes' windows and deliver input events to them
(SYS_WINMAP, SYS_WINSENDEVENT): the desktop compositor's authority.
Anything holding it can read every window's pixels and click on any
window, including a consent prompt it is itself waiting on, so it is
never part of a session mask, never part of "all", and never
delegated by the compositor to what it launches. */
static constexpr uint64_t CAP_COMPOSITOR = 1ULL << 13;
static constexpr uint64_t CAP_ALL = (1ULL << 14) - 1;
static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND; static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND;
static constexpr uint64_t CAP_ADMIN_SESSION = static constexpr uint64_t CAP_ADMIN_SESSION =
CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN | CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN |
@@ -384,6 +391,9 @@ namespace montauk::abi {
static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0, static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0,
"an admin session must not imply authority to rewrite the " "an admin session must not imply authority to rewrite the "
"programs it launches; grant CAP_SYSTEM_IMAGE per binary"); "programs it launches; grant CAP_SYSTEM_IMAGE per binary");
static_assert((CAP_ADMIN_SESSION & CAP_COMPOSITOR) == 0,
"a session must not delegate the compositor's authority; "
"login grants it to the desktop directly");
static constexpr int SYS_ERR_PERMISSION = -13; static constexpr int SYS_ERR_PERMISSION = -13;
struct SpawnCapabilities { struct SpawnCapabilities {
+6
View File
@@ -45,7 +45,12 @@ namespace montauk::abi {
return WinServer::Enumerate(outArray, maxCount); return WinServer::Enumerate(outArray, maxCount);
} }
// Mapping another process's window and injecting input into it are the
// compositor's job alone. Left open, any app could read every window's
// pixels or click on any window -- including a prompt asking the user
// whether to grant that same app authority.
static uint64_t Sys_WinMap(int windowId) { static uint64_t Sys_WinMap(int windowId) {
if (!Sched::HasCapability(CAP_COMPOSITOR)) return 0;
auto* proc = Sched::GetCurrentProcessPtr(); auto* proc = Sched::GetCurrentProcessPtr();
if (proc == nullptr) return 0; if (proc == nullptr) return 0;
return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext); return WinServer::Map(windowId, proc->pid, proc->pml4Phys, proc->heapNext);
@@ -58,6 +63,7 @@ namespace montauk::abi {
} }
static int Sys_WinSendEvent(int windowId, const WinEvent* event) { static int Sys_WinSendEvent(int windowId, const WinEvent* event) {
if (!Sched::HasCapability(CAP_COMPOSITOR)) return SYS_ERR_PERMISSION;
if (event == nullptr) return -1; if (event == nullptr) return -1;
return WinServer::SendEvent(windowId, event); return WinServer::SendEvent(windowId, event);
} }
+10 -2
View File
@@ -404,11 +404,19 @@ $(BINDIR)/os/wallpapers/%: $(WPDIR)/%
cp $< $@ cp $< $@
# Build each system program from its source files. # Build each system program from its source files.
$(BINDIR)/os/%.elf: src/%/main.cpp link.ld GNUmakefile $(LIBC_LIB) # -MMD records the headers main.cpp pulled in, so editing a shared header
# (montauk/capabilities.h, Api/Syscall.hpp, ...) rebuilds every program that
# uses it. A missing depfile matches the empty rule below, which forces a
# rebuild; that is how programs built before this tracking existed catch up.
$(BINDIR)/os/%.elf: src/%/main.cpp link.ld GNUmakefile $(LIBC_LIB) obj/%/main.elf.d
mkdir -p $(BINDIR)/os obj/$* mkdir -p $(BINDIR)/os obj/$*
$(CXX) $(CXXFLAGS) -c src/$*/main.cpp -o obj/$*/main.o $(CXX) $(CXXFLAGS) -MMD -MP -MT $@ -MF obj/$*/main.elf.d -c src/$*/main.cpp -o obj/$*/main.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) obj/$*/main.o $(LIBC_LIB) -o $@ $(CXX) $(CXXFLAGS) $(LDFLAGS) obj/$*/main.o $(LIBC_LIB) -o $@
obj/%/main.elf.d: ;
.PRECIOUS: obj/%/main.elf.d
-include $(wildcard obj/*/main.elf.d)
# ============================================================================ # ============================================================================
# Native development kit (Montauk SDK): binutils built for MontaukOS # Native development kit (Montauk SDK): binutils built for MontaukOS
# plus a target sysroot (headers, libc.a, crt objects) under 0:/sdk. # plus a target sysroot (headers, libc.a, crt objects) under 0:/sdk.
+4 -21
View File
@@ -3,18 +3,7 @@
# #
# Capability names: process_admin, power_request, power_control, suspend, # Capability names: process_admin, power_request, power_control, suspend,
# storage_admin, raw_storage, network_admin, set_time, user_admin, # storage_admin, raw_storage, network_admin, set_time, user_admin,
# display_admin, device_admin, log_read, system_image, and "all". # display_admin, device_admin, log_read, system_image, compositor and "all".
#
# "all" is every capability except system_image, which must always be named
# explicitly: it is write access to 0:/os and 0:/apps, and since grants are
# keyed on binary path, holding it is equivalent to holding every capability
# the system can issue from the next launch onwards. Nothing is granted it.
#
# A grant is always clamped by the kernel to what the launching process may
# actually delegate, so "all" in a launcher entry means "whatever this session
# was given", not "root".
#
#
# ==== System services (started by init) ==== # ==== System services (started by init) ====
@@ -28,7 +17,7 @@ effective = ["set_time"]
[grant.login] [grant.login]
path = "0:/os/login.elf" path = "0:/os/login.elf"
permitted = ["all"] permitted = ["all", "compositor"]
effective = [ effective = [
"power_control", "storage_admin", "device_admin", "power_control", "storage_admin", "device_admin",
"user_admin", "process_admin", "log_read", "user_admin", "process_admin", "log_read",
@@ -36,7 +25,7 @@ effective = [
delegable = [ delegable = [
"power_request", "suspend", "process_admin", "storage_admin", "power_request", "suspend", "process_admin", "storage_admin",
"raw_storage", "network_admin", "set_time", "user_admin", "raw_storage", "network_admin", "set_time", "user_admin",
"display_admin", "device_admin", "log_read", "display_admin", "device_admin", "log_read", "compositor",
] ]
[grant.sshd] [grant.sshd]
@@ -48,7 +37,7 @@ path = "0:/os/desktop.elf"
effective = [ effective = [
"power_request", "suspend", "process_admin", "storage_admin", "power_request", "suspend", "process_admin", "storage_admin",
"raw_storage", "network_admin", "set_time", "user_admin", "raw_storage", "network_admin", "set_time", "user_admin",
"display_admin", "device_admin", "log_read", "display_admin", "device_admin", "log_read", "compositor",
] ]
delegable = [ delegable = [
"power_request", "suspend", "process_admin", "storage_admin", "power_request", "suspend", "process_admin", "storage_admin",
@@ -56,12 +45,6 @@ delegable = [
"display_admin", "device_admin", "log_read", "display_admin", "device_admin", "log_read",
] ]
# The console session launchers. terminal.elf needs no authority of its own;
# it exists to pass the session's authority to the shell it hosts. The shell
# in turn exercises only power_request and suspend, but delegates on the same
# terms as the desktop so that the tools below work from a console. Both are
# clamped to the launching session: a standard session narrows this to
# power_request and suspend, and an unprivileged one to nothing.
[grant.terminal] [grant.terminal]
path = "0:/apps/terminal/terminal.elf" path = "0:/apps/terminal/terminal.elf"
delegable = ["all"] delegable = ["all"]
+11 -1
View File
@@ -285,7 +285,14 @@ namespace montauk::abi {
whatever that image is granted at its next launch. Formatting a data whatever that image is granted at its next launch. Formatting a data
volume must not carry that authority with it. */ volume must not carry that authority with it. */
static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12; static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12;
static constexpr uint64_t CAP_ALL = (1ULL << 13) - 1; /* Map other processes' windows and deliver input events to them
(SYS_WINMAP, SYS_WINSENDEVENT): the desktop compositor's authority.
Anything holding it can read every window's pixels and click on any
window, including a consent prompt it is itself waiting on, so it is
never part of a session mask, never part of "all", and never
delegated by the compositor to what it launches. */
static constexpr uint64_t CAP_COMPOSITOR = 1ULL << 13;
static constexpr uint64_t CAP_ALL = (1ULL << 14) - 1;
static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND; static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND;
static constexpr uint64_t CAP_ADMIN_SESSION = static constexpr uint64_t CAP_ADMIN_SESSION =
CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN | CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN |
@@ -297,6 +304,9 @@ namespace montauk::abi {
static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0, static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0,
"an admin session must not imply authority to rewrite the " "an admin session must not imply authority to rewrite the "
"programs it launches; grant CAP_SYSTEM_IMAGE per binary"); "programs it launches; grant CAP_SYSTEM_IMAGE per binary");
static_assert((CAP_ADMIN_SESSION & CAP_COMPOSITOR) == 0,
"a session must not delegate the compositor's authority; "
"login grants it to the desktop directly");
static constexpr int SYS_ERR_PERMISSION = -13; static constexpr int SYS_ERR_PERMISSION = -13;
struct SpawnCapabilities { struct SpawnCapabilities {
+6 -1
View File
@@ -37,13 +37,18 @@ namespace caps {
{"device_admin", montauk::abi::CAP_DEVICE_ADMIN}, {"device_admin", montauk::abi::CAP_DEVICE_ADMIN},
{"log_read", montauk::abi::CAP_LOG_READ}, {"log_read", montauk::abi::CAP_LOG_READ},
{"system_image", montauk::abi::CAP_SYSTEM_IMAGE}, {"system_image", montauk::abi::CAP_SYSTEM_IMAGE},
{"compositor", montauk::abi::CAP_COMPOSITOR},
}; };
// Never implied by "all": each must be named explicitly in a grant.
inline constexpr uint64_t EXPLICIT_ONLY =
montauk::abi::CAP_SYSTEM_IMAGE | montauk::abi::CAP_COMPOSITOR;
inline uint64_t bit_for_name(const char* name) { inline uint64_t bit_for_name(const char* name) {
if (name == nullptr || name[0] == '\0') return 0; if (name == nullptr || name[0] == '\0') return 0;
// "all" means "everything this launcher may pass on". // "all" means "everything this launcher may pass on".
if (montauk::streq(name, "all")) if (montauk::streq(name, "all"))
return montauk::abi::CAP_ALL & ~montauk::abi::CAP_SYSTEM_IMAGE; return montauk::abi::CAP_ALL & ~EXPLICIT_ONLY;
for (const auto& entry : NAMES) { for (const auto& entry : NAMES) {
if (montauk::streq(entry.name, name)) return entry.bit; if (montauk::streq(entry.name, name)) return entry.bit;
} }
+1
View File
@@ -60,6 +60,7 @@ static const char* const kCapDescriptions[CAP_COUNT] = {
"Manage hardware devices (Bluetooth, radio)", "Manage hardware devices (Bluetooth, radio)",
"Read the kernel log", "Read the kernel log",
"Write to 0:/os and 0:/apps (never part of \"all\")", "Write to 0:/os and 0:/apps (never part of \"all\")",
"Read and send input to other windows (never part of \"all\")",
}; };
// ==== Grant table model ==== // ==== Grant table model ====
+4
View File
@@ -72,6 +72,10 @@ void launch_session(LoginState* ls) {
pid = montauk::spawn_with_caps("0:/apps/terminal/terminal.elf", pid = montauk::spawn_with_caps("0:/apps/terminal/terminal.elf",
console_args, ls->username, caps); console_args, ls->username, caps);
} else { } else {
// The desktop is also the compositor. That authority is not the
// session's to pass on, so it is added here and not delegable.
caps.permitted |= montauk::abi::CAP_COMPOSITOR;
caps.effective |= montauk::abi::CAP_COMPOSITOR;
pid = montauk::spawn_with_caps("0:/os/desktop.elf", ls->username, pid = montauk::spawn_with_caps("0:/os/desktop.elf", ls->username,
ls->username, caps); ls->username, caps);
} }
+3
View File
@@ -33,6 +33,9 @@ static void maybe_run_setup_session() {
// Matches launch_session(): a session delegates from what it holds, and // Matches launch_session(): a session delegates from what it holds, and
// the grant table decides which program receives which part of it. // the grant table decides which program receives which part of it.
caps.delegable = caps.permitted; caps.delegable = caps.permitted;
// Compositor authority for the desktop itself, never delegable onwards.
caps.permitted |= montauk::abi::CAP_COMPOSITOR;
caps.effective |= montauk::abi::CAP_COMPOSITOR;
int pid = montauk::spawn_with_caps("0:/os/desktop.elf", user, user, caps); int pid = montauk::spawn_with_caps("0:/os/desktop.elf", user, user, caps);
if (pid >= 0) { if (pid >= 0) {
montauk::waitpid(pid); montauk::waitpid(pid);