Keep Plymouth publishing working in dev mode
This commit is contained in:
+67
-29
@@ -76,6 +76,9 @@ run_root_transaction() {
|
|||||||
[[ -z $failure_reported ]] || return 0
|
[[ -z $failure_reported ]] || return 0
|
||||||
failure_reported=1
|
failure_reported=1
|
||||||
printf "omarchy-plymouth-set: refusing to publish: %s failed validation\n" "$failure_context" >&2
|
printf "omarchy-plymouth-set: refusing to publish: %s failed validation\n" "$failure_context" >&2
|
||||||
|
if [[ -n ${failure_hint:-} ]]; then
|
||||||
|
printf "omarchy-plymouth-set: %s\n" "$failure_hint" >&2
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
trap report_failure ERR
|
trap report_failure ERR
|
||||||
|
|
||||||
@@ -95,14 +98,65 @@ run_root_transaction() {
|
|||||||
canonical_source_root=$(realpath -e -- "$source_root")
|
canonical_source_root=$(realpath -e -- "$source_root")
|
||||||
[[ $canonical_source_root == "$source_root" ]]
|
[[ $canonical_source_root == "$source_root" ]]
|
||||||
|
|
||||||
# The one refusal a healthy machine can hit: omarchy dev link points
|
validate_trusted_directory() {
|
||||||
# OMARCHY_PATH at a checkout the desktop user can rewrite, and executable
|
local directory=$1 canonical uid directory_mode
|
||||||
# boot assets cannot come from there. Say so rather than failing mute.
|
|
||||||
|
failure_context="directory $directory"
|
||||||
|
canonical=$(realpath -e -- "$directory")
|
||||||
|
[[ $canonical == "$directory" && -d $directory && ! -L $directory ]]
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
failure_context="directory $directory (must be root-owned and not group- or world-writable)"
|
||||||
|
uid=$(stat -c %u -- "$directory")
|
||||||
|
directory_mode=$(stat -c %a -- "$directory")
|
||||||
|
(( uid == 0 ))
|
||||||
|
(( (8#$directory_mode & 0022) == 0 ))
|
||||||
|
[[ $directory == "/" ]] && break
|
||||||
|
directory=${directory%/*}
|
||||||
|
[[ -n $directory ]] || directory=/
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_trusted_configuration_file() {
|
||||||
|
local configuration=$1 canonical uid configuration_mode size
|
||||||
|
|
||||||
|
failure_context="root configuration $configuration"
|
||||||
|
[[ -f $configuration && ! -L $configuration ]]
|
||||||
|
canonical=$(realpath -e -- "$configuration")
|
||||||
|
[[ $canonical == "$configuration" ]]
|
||||||
|
validate_trusted_directory "${configuration%/*}"
|
||||||
|
uid=$(stat -c %u -- "$configuration")
|
||||||
|
configuration_mode=$(stat -c %a -- "$configuration")
|
||||||
|
size=$(stat -c %s -- "$configuration")
|
||||||
|
(( uid == 0 ))
|
||||||
|
(( (8#$configuration_mode & 0022) == 0 ))
|
||||||
|
(( size > 0 && size <= 4096 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
# A packaged tree must be root-owned. A development checkout is the one
|
||||||
|
# deliberate exception: omarchy dev link records its canonical path in a
|
||||||
|
# root-owned /etc/omarchy.conf. That is already an explicit decision to run
|
||||||
|
# privileged Omarchy commands from user-editable code in the checkout, so
|
||||||
|
# reading its packaged assets does not widen the development trust boundary.
|
||||||
|
development_source=false
|
||||||
source_root_uid=$(stat -c %u -- "$source_root")
|
source_root_uid=$(stat -c %u -- "$source_root")
|
||||||
if (( source_root_uid != 0 )); then
|
if (( source_root_uid != 0 )); then
|
||||||
printf "omarchy-plymouth-set: %s is not root-owned, so it cannot supply executable boot assets.\n" "$source_root" >&2
|
omarchy_conf=/etc/omarchy.conf
|
||||||
printf "omarchy-plymouth-set: this is expected on a development checkout; run omarchy dev unlink to publish from /usr/share/omarchy.\n" >&2
|
failure_context="$source_root is user-owned and $omarchy_conf must contain its trusted dev-link authorization; run omarchy dev link to authorize it"
|
||||||
exit 1
|
failure_hint="$source_root is user-owned; run omarchy dev link to authorize this development checkout, or omarchy dev unlink to use the packaged tree"
|
||||||
|
validate_trusted_configuration_file "$omarchy_conf"
|
||||||
|
|
||||||
|
quoted_source_root=$source_root
|
||||||
|
quoted_source_root=${quoted_source_root//\\/\\\\}
|
||||||
|
quoted_source_root=${quoted_source_root//\"/\\\"}
|
||||||
|
quoted_source_root=${quoted_source_root//\$/\\\$}
|
||||||
|
quoted_source_root=${quoted_source_root//\`/\\\`}
|
||||||
|
expected_config_line="export OMARCHY_PATH=\"$quoted_source_root\""
|
||||||
|
mapfile -t omarchy_config_lines <"$omarchy_conf"
|
||||||
|
(( ${#omarchy_config_lines[@]} == 1 ))
|
||||||
|
[[ ${omarchy_config_lines[0]} == "$expected_config_line" ]]
|
||||||
|
development_source=true
|
||||||
|
failure_hint=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $mode == "set" ]]; then
|
if [[ $mode == "set" ]]; then
|
||||||
@@ -126,25 +180,6 @@ run_root_transaction() {
|
|||||||
plymouth_default_assets=("${plymouth_theme_assets[@]}" logos/oma.png)
|
plymouth_default_assets=("${plymouth_theme_assets[@]}" logos/oma.png)
|
||||||
sddm_theme_assets=(Main.qml bullet.png entry-failed.png entry.png lock-failed.png lock.png logo.png)
|
sddm_theme_assets=(Main.qml bullet.png entry-failed.png entry.png lock-failed.png lock.png logo.png)
|
||||||
|
|
||||||
validate_trusted_directory() {
|
|
||||||
local directory=$1 canonical uid directory_mode
|
|
||||||
|
|
||||||
failure_context="directory $directory"
|
|
||||||
canonical=$(realpath -e -- "$directory")
|
|
||||||
[[ $canonical == "$directory" && -d $directory && ! -L $directory ]]
|
|
||||||
|
|
||||||
while :; do
|
|
||||||
failure_context="directory $directory (must be root-owned and not group- or world-writable)"
|
|
||||||
uid=$(stat -c %u -- "$directory")
|
|
||||||
directory_mode=$(stat -c %a -- "$directory")
|
|
||||||
(( uid == 0 ))
|
|
||||||
(( (8#$directory_mode & 0022) == 0 ))
|
|
||||||
[[ $directory == "/" ]] && break
|
|
||||||
directory=${directory%/*}
|
|
||||||
[[ -n $directory ]] || directory=/
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
validate_trusted_file() {
|
validate_trusted_file() {
|
||||||
local source=$1 canonical uid file_mode size
|
local source=$1 canonical uid file_mode size
|
||||||
|
|
||||||
@@ -152,13 +187,16 @@ run_root_transaction() {
|
|||||||
[[ -f $source && ! -L $source ]]
|
[[ -f $source && ! -L $source ]]
|
||||||
canonical=$(realpath -e -- "$source")
|
canonical=$(realpath -e -- "$source")
|
||||||
[[ $canonical == "$source" ]]
|
[[ $canonical == "$source" ]]
|
||||||
validate_trusted_directory "${source%/*}"
|
|
||||||
uid=$(stat -c %u -- "$source")
|
|
||||||
file_mode=$(stat -c %a -- "$source")
|
file_mode=$(stat -c %a -- "$source")
|
||||||
size=$(stat -c %s -- "$source")
|
size=$(stat -c %s -- "$source")
|
||||||
(( uid == 0 ))
|
|
||||||
(( (8#$file_mode & 0022) == 0 ))
|
|
||||||
(( size > 0 && size <= max_asset_size ))
|
(( size > 0 && size <= max_asset_size ))
|
||||||
|
|
||||||
|
if ! $development_source; then
|
||||||
|
validate_trusted_directory "${source%/*}"
|
||||||
|
uid=$(stat -c %u -- "$source")
|
||||||
|
(( uid == 0 ))
|
||||||
|
(( (8#$file_mode & 0022) == 0 ))
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_trusted_file() {
|
copy_trusted_file() {
|
||||||
|
|||||||
@@ -174,12 +174,14 @@ case "$1" in
|
|||||||
# simulation only, substitute trusted tools and map fixed system destinations
|
# simulation only, substitute trusted tools and map fixed system destinations
|
||||||
# under the disposable fake root.
|
# under the disposable fake root.
|
||||||
code=${code/PATH=\/usr\/bin:\/bin/PATH=$TEST_ROOT_TOOLS:\/usr\/bin:\/bin}
|
code=${code/PATH=\/usr\/bin:\/bin/PATH=$TEST_ROOT_TOOLS:\/usr\/bin:\/bin}
|
||||||
|
code=${code/omarchy_conf=\/etc\/omarchy.conf/omarchy_conf=$TEST_OMARCHY_CONF}
|
||||||
code=${code/theme_dir=\/usr\/share\/plymouth\/themes\/omarchy/theme_dir=$TEST_FAKE_ROOT\/usr\/share\/plymouth\/themes\/omarchy}
|
code=${code/theme_dir=\/usr\/share\/plymouth\/themes\/omarchy/theme_dir=$TEST_FAKE_ROOT\/usr\/share\/plymouth\/themes\/omarchy}
|
||||||
code=${code/sddm_dir=\/usr\/share\/sddm\/themes\/omarchy/sddm_dir=$TEST_FAKE_ROOT\/usr\/share\/sddm\/themes\/omarchy}
|
code=${code/sddm_dir=\/usr\/share\/sddm\/themes\/omarchy/sddm_dir=$TEST_FAKE_ROOT\/usr\/share\/sddm\/themes\/omarchy}
|
||||||
|
|
||||||
# Each rewrite above silently no-ops if the production text drifts, which
|
# Each rewrite above silently no-ops if the production text drifts, which
|
||||||
# would point this simulation at the real /usr/share. Refuse instead.
|
# would point this simulation at the real /usr/share. Refuse instead.
|
||||||
[[ $code == *"PATH=$TEST_ROOT_TOOLS:/usr/bin:/bin"* ]] || exit 94
|
[[ $code == *"PATH=$TEST_ROOT_TOOLS:/usr/bin:/bin"* ]] || exit 94
|
||||||
|
[[ $code == *"omarchy_conf=$TEST_OMARCHY_CONF"* ]] || exit 94
|
||||||
[[ $code == *"theme_dir=$TEST_FAKE_ROOT/usr/share/plymouth/themes/omarchy"* ]] || exit 94
|
[[ $code == *"theme_dir=$TEST_FAKE_ROOT/usr/share/plymouth/themes/omarchy"* ]] || exit 94
|
||||||
[[ $code == *"sddm_dir=$TEST_FAKE_ROOT/usr/share/sddm/themes/omarchy"* ]] || exit 94
|
[[ $code == *"sddm_dir=$TEST_FAKE_ROOT/usr/share/sddm/themes/omarchy"* ]] || exit 94
|
||||||
|
|
||||||
@@ -243,6 +245,7 @@ setup_run() {
|
|||||||
fake_root="$run_dir/root"
|
fake_root="$run_dir/root"
|
||||||
sudo_log="$run_dir/sudo.log"
|
sudo_log="$run_dir/sudo.log"
|
||||||
leak_log="$run_dir/leaked-stage-path.log"
|
leak_log="$run_dir/leaked-stage-path.log"
|
||||||
|
omarchy_conf="$run_dir/omarchy.conf"
|
||||||
theme="$fake_root/usr/share/plymouth/themes/omarchy"
|
theme="$fake_root/usr/share/plymouth/themes/omarchy"
|
||||||
sddm="$fake_root/usr/share/sddm/themes/omarchy"
|
sddm="$fake_root/usr/share/sddm/themes/omarchy"
|
||||||
|
|
||||||
@@ -295,6 +298,7 @@ run_set_colors() {
|
|||||||
TEST_FAKE_ROOT="$fake_root" \
|
TEST_FAKE_ROOT="$fake_root" \
|
||||||
TEST_STAGES="$stages" \
|
TEST_STAGES="$stages" \
|
||||||
TEST_ROOT_TOOLS="$root_tools" \
|
TEST_ROOT_TOOLS="$root_tools" \
|
||||||
|
TEST_OMARCHY_CONF="$omarchy_conf" \
|
||||||
TEST_SUDO_LOG="$sudo_log" \
|
TEST_SUDO_LOG="$sudo_log" \
|
||||||
TEST_LEAK_LOG="$leak_log" \
|
TEST_LEAK_LOG="$leak_log" \
|
||||||
"$@" \
|
"$@" \
|
||||||
@@ -442,21 +446,49 @@ assert_no_temporary_files "$fake_root"
|
|||||||
|
|
||||||
pass "root rejects packaged assets that a desktop process could rewrite"
|
pass "root rejects packaged assets that a desktop process could rewrite"
|
||||||
|
|
||||||
# omarchy dev link points OMARCHY_PATH at a checkout the desktop user owns, so
|
# A random user-owned OMARCHY_PATH remains untrusted. Only the exact canonical
|
||||||
# this refusal fires on a working machine, not only under attack. Every check in
|
# checkout recorded by root in /etc/omarchy.conf is the supported dev-link
|
||||||
# the privileged transaction is a bare assertion that aborts under set -e, so
|
# exception; an unrelated or stale authorization must not weaken the check.
|
||||||
# without a diagnostic the whole Plymouth menu would just close in silence.
|
|
||||||
setup_run
|
setup_run
|
||||||
output=$(run_set 022 env TEST_UNTRUSTED_SOURCE="$ROOT" 2>&1)
|
output=$(run_set 022 env TEST_UNTRUSTED_SOURCE="$ROOT" 2>&1)
|
||||||
status=$?
|
status=$?
|
||||||
|
|
||||||
(( status != 0 )) || fail "a user-owned OMARCHY_PATH is rejected"
|
(( status != 0 )) || fail "a user-owned OMARCHY_PATH is rejected"
|
||||||
[[ $output == *"is not root-owned"* ]] || fail "the refusal names the untrusted source tree" "$output"
|
[[ $output == *"user-owned"* ]] || fail "the refusal names the untrusted source tree" "$output"
|
||||||
[[ $output == *"omarchy dev unlink"* ]] || fail "the refusal names the way back to a trusted tree" "$output"
|
[[ $output == *"omarchy dev link"* ]] || fail "the refusal names how to authorize a development checkout" "$output"
|
||||||
[[ $(cat "$theme/bullet.png") == 'old plymouth bullet.png' ]] || fail "a user-owned OMARCHY_PATH leaves the live theme unchanged"
|
[[ $(cat "$theme/bullet.png") == 'old plymouth bullet.png' ]] || fail "a user-owned OMARCHY_PATH leaves the live theme unchanged"
|
||||||
assert_no_temporary_files "$fake_root"
|
assert_no_temporary_files "$fake_root"
|
||||||
|
|
||||||
pass "a development checkout is refused with an explanation instead of in silence"
|
setup_run
|
||||||
|
printf 'export OMARCHY_PATH="/some/other/checkout"\n' >"$omarchy_conf"
|
||||||
|
chmod 0644 "$omarchy_conf"
|
||||||
|
output=$(run_set 022 env TEST_UNTRUSTED_SOURCE="$ROOT" 2>&1)
|
||||||
|
status=$?
|
||||||
|
|
||||||
|
(( status != 0 )) || fail "a stale dev-link authorization is rejected"
|
||||||
|
[[ $(cat "$theme/bullet.png") == 'old plymouth bullet.png' ]] || fail "a stale dev-link authorization leaves the live theme unchanged"
|
||||||
|
assert_no_temporary_files "$fake_root"
|
||||||
|
|
||||||
|
setup_run
|
||||||
|
printf 'export OMARCHY_PATH="%s"\n' "$ROOT" >"$omarchy_conf"
|
||||||
|
chmod 0666 "$omarchy_conf"
|
||||||
|
output=$(run_set 022 env TEST_UNTRUSTED_SOURCE="$ROOT" 2>&1)
|
||||||
|
status=$?
|
||||||
|
|
||||||
|
(( status != 0 )) || fail "a writable dev-link authorization is rejected"
|
||||||
|
[[ $(cat "$theme/bullet.png") == 'old plymouth bullet.png' ]] || fail "a writable dev-link authorization leaves the live theme unchanged"
|
||||||
|
assert_no_temporary_files "$fake_root"
|
||||||
|
|
||||||
|
setup_run
|
||||||
|
printf 'export OMARCHY_PATH="%s"\n' "$ROOT" >"$omarchy_conf"
|
||||||
|
chmod 0644 "$omarchy_conf"
|
||||||
|
output=$(run_set 022 env TEST_UNTRUSTED_SOURCE="$ROOT" 2>&1)
|
||||||
|
status=$?
|
||||||
|
|
||||||
|
(( status == 0 )) || fail "the root-authorized development checkout can publish Plymouth assets" "$output"
|
||||||
|
cmp -s "$ROOT/default/plymouth/bullet.png" "$theme/bullet.png" || fail "the authorized development checkout supplies the packaged assets"
|
||||||
|
|
||||||
|
pass "only the checkout explicitly authorized by omarchy dev link may be user-owned"
|
||||||
|
|
||||||
# Root rejects both a symlinked parent and a group/world-writable parent before
|
# Root rejects both a symlinked parent and a group/world-writable parent before
|
||||||
# it creates a temporary file or touches the live destination.
|
# it creates a temporary file or touches the live destination.
|
||||||
@@ -509,6 +541,7 @@ output=$(
|
|||||||
TEST_FAKE_ROOT="$fake_root" \
|
TEST_FAKE_ROOT="$fake_root" \
|
||||||
TEST_STAGES="$stages" \
|
TEST_STAGES="$stages" \
|
||||||
TEST_ROOT_TOOLS="$root_tools" \
|
TEST_ROOT_TOOLS="$root_tools" \
|
||||||
|
TEST_OMARCHY_CONF="$omarchy_conf" \
|
||||||
TEST_SUDO_LOG="$sudo_log" \
|
TEST_SUDO_LOG="$sudo_log" \
|
||||||
TEST_LEAK_LOG="$leak_log" \
|
TEST_LEAK_LOG="$leak_log" \
|
||||||
/bin/bash "$ROOT/bin/omarchy-refresh-plymouth" 2>&1
|
/bin/bash "$ROOT/bin/omarchy-refresh-plymouth" 2>&1
|
||||||
|
|||||||
Reference in New Issue
Block a user