Harden kiosk shell: hashed password, verified updates, working watchdog #1

Merged
leonhard.suckau merged 3 commits from fix/kiosk-hardening into main 2026-07-29 11:12:06 +00:00

Fixes the findings collected in NOTWENDIGE_AENDERUNGEN.md, plus three
errors that kept the tree from compiling at all.

Security:

  • Store the password as an argon2 hash instead of plain text. A value
    that is not a PHC string comes from an older version: it is compared
    once and then replaced by its hash, so existing installs keep working.
  • An unset password no longer unlocks the kiosk for everybody. main.rs
    sends the user to the settings instead, which insist on a password.
  • Pick the update asset by file name and verify it against the
    SHA256SUMS asset of the same release before self_replace installs it.
    create-release.yml publishes that file now. A missing or mismatching
    checksum refuses the update and logs why.

Bugs:

  • Focus the client application through EnumWindows and its process id.
    FindWindowA was given the executable's file name, but matches against
    the window title, so the watchdog never actually raised anything.
  • download_latest_release was handed the directory of the running exe,
    so File::create always failed and no update was ever written. The
    self-replace dependency was declared but unused.
  • Drop the duplicated user name from the NTUSER.DAT path.
  • Report a client application that fails to start instead of aborting
    without a console: shown in the settings window and appended to
    winkioskshell.log next to the configuration.

Watchdog:

  • Keep one System and refresh only what is used, instead of rebuilding
    it with CPU, memory and disk data every second.
  • Move the focus call out of the process loop; it ran for every process
    that was not on the kill list.
  • Make the kill list configurable, defaulting to the previous values.

Build:

  • Import HWND from Win32::Foundation.
  • whoami::username() returns a Result since whoami 2.
  • Compile a single ui/ui.slint entry point. Compiling both windows
    separately made the second call overwrite the first, leaving
    SettingsWindow out of the generated module.
  • Track Cargo.lock, as recommended for executables. Resolving fresh
    versions on every CI run is what broke the build on whoami 2.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Fixes the findings collected in NOTWENDIGE_AENDERUNGEN.md, plus three errors that kept the tree from compiling at all. Security: - Store the password as an argon2 hash instead of plain text. A value that is not a PHC string comes from an older version: it is compared once and then replaced by its hash, so existing installs keep working. - An unset password no longer unlocks the kiosk for everybody. main.rs sends the user to the settings instead, which insist on a password. - Pick the update asset by file name and verify it against the SHA256SUMS asset of the same release before self_replace installs it. create-release.yml publishes that file now. A missing or mismatching checksum refuses the update and logs why. Bugs: - Focus the client application through EnumWindows and its process id. FindWindowA was given the executable's file name, but matches against the window title, so the watchdog never actually raised anything. - download_latest_release was handed the directory of the running exe, so File::create always failed and no update was ever written. The self-replace dependency was declared but unused. - Drop the duplicated user name from the NTUSER.DAT path. - Report a client application that fails to start instead of aborting without a console: shown in the settings window and appended to winkioskshell.log next to the configuration. Watchdog: - Keep one System and refresh only what is used, instead of rebuilding it with CPU, memory and disk data every second. - Move the focus call out of the process loop; it ran for every process that was not on the kill list. - Make the kill list configurable, defaulting to the previous values. Build: - Import HWND from Win32::Foundation. - whoami::username() returns a Result since whoami 2. - Compile a single ui/ui.slint entry point. Compiling both windows separately made the second call overwrite the first, leaving SettingsWindow out of the generated module. - Track Cargo.lock, as recommended for executables. Resolving fresh versions on every CI run is what broke the build on whoami 2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Harden kiosk shell: hashed password, verified updates, working watchdog
Some checks failed
Rust / build (pull_request) Has been cancelled
e5d79ba5d1
Fixes the findings collected in NOTWENDIGE_AENDERUNGEN.md, plus three
errors that kept the tree from compiling at all.

Security:
- Store the password as an argon2 hash instead of plain text. A value
  that is not a PHC string comes from an older version: it is compared
  once and then replaced by its hash, so existing installs keep working.
- An unset password no longer unlocks the kiosk for everybody. main.rs
  sends the user to the settings instead, which insist on a password.
- Pick the update asset by file name and verify it against the
  SHA256SUMS asset of the same release before self_replace installs it.
  create-release.yml publishes that file now. A missing or mismatching
  checksum refuses the update and logs why.

Bugs:
- Focus the client application through EnumWindows and its process id.
  FindWindowA was given the executable's file name, but matches against
  the window title, so the watchdog never actually raised anything.
- download_latest_release was handed the directory of the running exe,
  so File::create always failed and no update was ever written. The
  self-replace dependency was declared but unused.
- Drop the duplicated user name from the NTUSER.DAT path.
- Report a client application that fails to start instead of aborting
  without a console: shown in the settings window and appended to
  winkioskshell.log next to the configuration.

Watchdog:
- Keep one System and refresh only what is used, instead of rebuilding
  it with CPU, memory and disk data every second.
- Move the focus call out of the process loop; it ran for every process
  that was not on the kill list.
- Make the kill list configurable, defaulting to the previous values.

Build:
- Import HWND from Win32::Foundation.
- whoami::username() returns a Result since whoami 2.
- Compile a single ui/ui.slint entry point. Compiling both windows
  separately made the second call overwrite the first, leaving
  SettingsWindow out of the generated module.
- Track Cargo.lock, as recommended for executables. Resolving fresh
  versions on every CI run is what broke the build on whoami 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Move CI and updates from GitHub to Forgejo
All checks were successful
test / test (pull_request) Successful in 10m24s
48e85b2fc7
The GitHub workflows never ran: origin is git.bg-ak.de, and their
runs-on: windows-latest matches no runner here. ngenSermon2Usb and
ngenAudioRecord already solve this; this follows their pattern.

- .github/workflows -> .forgejo/workflows, runs-on: docker in the
  ghcr.io/rust-cross/cargo-xwin image. Windows builds cross-compile to
  x86_64-pc-windows-msvc; no Windows machine is involved.
- Check out manually instead of actions/checkout: the container has no
  node, so JS actions fail with `exec: "node": not found`.
- rustup update stable, because the image ships a Rust older than the
  1.92 Slint 1.17 requires.
- Do not apt-get install llvm. Debian only ships versioned names
  (llvm-lib-21) while cc-rs looks for the bare one, which fails the
  build with `failed to find tool "llvm-lib"`. The image provides them.
- Build with --locked, so a run checks the committed Cargo.lock instead
  of silently resolving new versions. That is how the tree last broke.
- Publish the release through the Forgejo API with SHA256SUMS beside the
  executable, deleting a same-named asset before each of three upload
  attempts so a rerun stays idempotent.
- Point the self-update at the same forge. Forgejo answers
  releases/latest with the tag_name and assets[].name /
  browser_download_url fields release.rs already reads, so only the base
  URL changes.
- Move the README badge and the repository field off GitHub, and
  document the cargo xwin invocation.

Verified locally: both build steps run, and the sha256sum line the
workflow writes parses back to the digest of the executable it names.
The release API calls need PACKAGES_TOKEN and are untested here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fail the build when the icon cannot be embedded
All checks were successful
test / test (pull_request) Successful in 11m4s
ae98b5d9ec
`let _ = embed_resource::compile(...)` swallowed both a failed and a
never-attempted resource compilation, so a Windows executable without an
icon would ship with nothing turning red. The CI log made this visible:
cc-rs reports `Compiler family detection failed` for clang-cl, falls back,
and llvm-rc still does the work — but nothing in the build would have said
so had it not.

Checking manifest_required() rejects both cases. The icon is embedded
either way: .rsrc measures 9488 bytes against 9158 in icon.ico.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
leonhard.suckau deleted branch fix/kiosk-hardening 2026-07-29 11:12:06 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
leonhard.suckau/winkioskshell!1
No description provided.