feat: add compositor capability
This commit is contained in:
1 parent
75e85f24ef
commit
95e8a739d4
10 files changed
+57
-27
No files matched your search
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 236
|
#define MONTAUK_BUILD_NUMBER 239
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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.
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ====
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in new issue
Block a user