3 Commits
Author SHA1 Message Date
ekklesiaandClaude Opus 5 bf7b959893 feat: the room may use the church Wi-Fi and may not read its password
These are shared computers in rooms full of people, and in a stock Plasma
desktop "use the network" and "read the network's password" are much
closer together than they look.

Measured on Debian 13 with NetworkManager 1.52 and Plasma 6.3:

  * org.freedesktop.NetworkManager.settings.modify.own ships as "yes" for
    an active session — no prompt, no administrator. It covers any
    connection whose connection.permissions names that user, and a Wi-Fi
    network added from the desktop is created exactly that way. So a
    password a teacher types in is readable by the next person to sit
    down.
  * the applet offers "Show Network's QR Code" on every saved wireless
    connection, and the QR code contains the pre-shared key.
  * that action, and the "Configure…" beside it, are contextual actions in
    ConnectionItem.qml and do NOT consult
    authorizeControlModule("kcm_networkmanagement"). The Kiosk key gates
    the toolbar button only. Hiding buttons was never going to be the
    answer.

Every one of those paths ends at NetworkManager's GetSecrets, so that is
where this is answered. A polkit rule refuses settings.modify.own,
settings.modify.system, both wifi.share actions and the machine-wide
settings, for the classroom user by name.

Deliberately NOT refused: network-control, wifi.scan and the
enable/disable actions. None returns a secret, and they are what
connecting and seeing a network are made of. Automatic reconnection does
not go through polkit at all — NetworkManager does it as root — so nothing
here can stop the room coming back on the network after a reboot, which is
the property that matters most on a Sunday.

An administrator's account and root are untouched, and the override route
is the same one the sleep rule has.

classroom-wifi is the other half, and it is a requirement rather than a
convenience: it provisions the church Wi-Fi as a SYSTEM connection, with
no connection.permissions, psk-flags=0 and a 0600 root:root file. A
connection the machine owns is one nobody in the room can read. Verified
against real NetworkManager in the lab — the classroom account gets
"Permission denied" on the file, "--" from nmcli and "Insufficient
privileges" from GetSecrets, while autoconnect stays on.

There is deliberately no teacher-facing join dialog. Anything that lets
the room add a connection lets the room own it, and an owned connection is
a readable one; docs/networking.md says so rather than leaving it to be
rediscovered.

The rule is plain JavaScript, so the test evaluates it with polkit's
objects stubbed and asserts both halves: which actions come back NO, and
which are left alone. What it cannot test is the last mile — polkit
resolves a subject through logind and a container has no graphical seat
session, so every check there is denied for reasons unrelated to this
rule. That one is a physical check, and it is written down as one.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-20 21:04:07 -05:00
ekklesiaandClaude Opus 5 23dff0b7b4 feat: close the chord that let a teacher hibernate the room
Ctrl+Alt+Delete opened Plasma's session screen and a teacher hibernated a
classroom computer from it. A hibernated room cannot be recovered by
anybody standing in front of it, which on a Sunday morning is everybody.

logout=false was already set and did not stop it. That restriction
governs Plasma's own actions — what plasmashell draws in a menu or an
applet. The chord is a KGlobalAccel shortcut registered by ksmserver and
it asks D-Bus for the greeter without consulting a Plasma action at all.
A restriction on a menu cannot see a key.

Three layers, because each one alone has a way past it.

The key. kglobalshortcutsrc unbinds every ksmserver session action and
PowerDevil's Sleep, Hibernate, PowerDown and PowerOff, each entry marked
immutable. It is deliberately not in the xdg directory with the other
settings: kglobalacceld opens that file as a SimpleConfig, so it reads
one file in the teacher's home and no system directory at all. Measured —
with it in the cascade, kreadconfig6 reported the chord unbound while the
shortcut server went on answering Ctrl+Alt+Del. classroom-desktop-reset
copies the package's copy into place before the shortcut server starts,
the same way the panel is copied out of the package at every login.

The policy. A polkit rule refuses login1.suspend* and .hibernate* for the
classroom user and for nobody else. This is the layer that matters:
a key can be unbound and anything reaching logind can still ask.
Refusing here rather than masking sleep.target keeps a technician's own
account working, and is also what tells Plasma to stop drawing the
button — CanSuspend and CanHibernate come back no. Hybrid sleep and
suspend-then-hibernate have no action of their own in systemd 257; both
are authorised through hibernate, so denying it denies all three.

The greeter. Its D-Bus activation is shadowed with /bin/false, the same
mechanism already used for the old notification daemon. Layer one removes
the caller, this removes the callee.

Measured in the lab, from the package, after a reboot: the escape
reproduced first with the shadow removed — the greeter really does start
— and then refused, exited 1, with it back. The chord reports nothing
bound. systemctl suspend as the classroom user is denied. The maintenance
chord and the display chord both still answer, by key code, after the
shortcut file is replaced wholesale. A root process may still suspend,
hibernate, reboot and power off. 182 checks pass across a logout and back.

Screen locking stays disabled, the power button still does nothing, and
reboot and shut down are not newly denied to anybody.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-19 20:50:36 -05:00
ekklesiaandClaude Opus 5 34fd02e08f feat: give every room the broker, one scale, and its own panel back
Little Waders took the Plasma build and reported what a container
cannot. A disc player on a machine with no optical drive, a clock nobody
at the back of the room could read, a browser at half the size of the
panel around it, and Parent Call with no broker to call.

The broker is the same for every classroom, so it is a default now and
not something to fill in room by room. classroom-migrate brings an
existing room to the same place without touching its name, its username
or its password, and Parent Call distinguishes a broker it cannot reach
from one that was never set up: those send somebody to look at
completely different things.

The disc player's detection was right and ran too late — alongside the
session rather than before it, and the panel is built from the layout the
moment a session starts. It runs before the display manager now, and
again from udev when a drive is plugged in.

UI_SCALE had a ceiling of 1.5, which cannot express a 4K television; a
clock pinned at 15 points, which is the same size at every scale; and a
browser that was never told the number at all. All three are fixed and
the room doubles together at 200%.

This also brings 0.1.47's work into the repository, where it had only
ever existed in a build directory under /tmp: the hidden display dialog,
its authenticated writer, the polkit action, and the church's own
wallpaper. One piece of it is deliberately left out. The custom volume
display needed a shell package of our own, and with one selected
plasmashell builds no panel at all — measured twice, with that build's
own files, on a classroom that deletes its panel configuration at every
login by design.

The lab connected to the real church broker the moment the package
defaulted to it. Nothing was left behind, which was checked, but the
guard that should have stopped it had never worked: it used iptables,
which the lab image does not have, and probed with /dev/tcp under dash,
where that always fails. Both reported success.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-12 19:05:09 -05:00