Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions bin/omarchy-windows-vm
Original file line number Diff line number Diff line change
Expand Up @@ -580,14 +580,17 @@ prepare_caller_mounts() {
# Privacy is an explicit preflight step for both already-pinned sources, not
# a side effect halfway through the two-mount transaction. Old umask-022
# installs are hardened together before either Docker-facing anchor changes.
chmod 0700 -- "/proc/$BASHPID/fd/$storage_fd" "/proc/$BASHPID/fd/$shared_fd" || {
# Symbolic, because a numeric mode leaves S_ISGID standing on a directory and
# the mode check below then rejects a source we were asked to secure.
chmod u=rwx,go=,a-s -- "/proc/$BASHPID/fd/$storage_fd" "/proc/$BASHPID/fd/$shared_fd" || {
exec {storage_fd}<&-
exec {shared_fd}<&-
return 1
}
storage_mode=$(stat -Lc '%a' "/proc/$BASHPID/fd/$storage_fd" 2>/dev/null) || storage_mode=""
shared_mode=$(stat -Lc '%a' "/proc/$BASHPID/fd/$shared_fd" 2>/dev/null) || shared_mode=""
if [[ $storage_mode != 700 || $shared_mode != 700 ]]; then
echo "omarchy-windows-vm: could not secure mount sources to mode 700 (storage=$storage_mode shared=$shared_mode)" >&2
exec {storage_fd}<&-
exec {shared_fd}<&-
return 1
Expand Down Expand Up @@ -1015,7 +1018,7 @@ prepare_user_mount_sources() {
echo "omarchy-windows-vm: storage and shared must be different directories" >&2
return 1
}
chmod 0700 -- "$storage" "$shared"
chmod u=rwx,go=,a-s -- "$storage" "$shared"
}

storage_space_path() {
Expand Down Expand Up @@ -1058,7 +1061,7 @@ write_credentials() {
local username="$1" password="$2" old_umask dir tmp
dir=$(dirname -- "$CREDENTIALS_FILE")
mkdir -p "$dir" || return 1
chmod 0700 "$dir" || return 1
chmod u=rwx,go=,a-s -- "$dir" || return 1
old_umask=$(umask)
umask 077
tmp=$(mktemp "$dir/.credentials.XXXXXX") || { umask "$old_umask"; return 1; }
Expand Down
23 changes: 23 additions & 0 deletions test/shell.d/windows-vm-compose-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,22 @@ resolve_caller
find "$CALLER_DATA_ROOT" -name 'rejected-*' -print -quit | grep -q . && fail "source was quarantined"
pass "invalid second source leaves paths and anchors untouched and leaks no FD"

# A set-ID bit on either source must not survive the privacy step. `chmod 0700`
# leaves S_ISGID standing on a directory, and the exact mode 700 the elevated
# path then demands would reject a source it had just been asked to secure.
reset_case
mkdir -p "$HOME/.windows" "$HOME/Windows"
chmod 2700 "$HOME/.windows"
chmod 6700 "$HOME/Windows"
prepare_user_mount_sources
[[ $(stat -Lc '%a' "$HOME/.windows") == 700 ]] || fail "setgid storage source was not cleared to 700"
[[ $(stat -Lc '%a' "$HOME/Windows") == 700 ]] || fail "setuid/setgid shared source was not cleared to 700"
chmod 2700 "$HOME/.windows"
write 4G 2 64G setgid pw UTC
resolve_caller
[[ $(stat -Lc '%a' "$EXPECTED_STORAGE") == 700 && $(stat -Lc '%a' "$EXPECTED_SHARED") == 700 ]] || fail "mount leaves are not private after a set-ID source"
pass "a set-ID bit on either source is cleared instead of failing the mount"

# Distinct caller-owned symlink targets are supported and remain links.
reset_case
external_storage="$TMPDIR/external-storage"
Expand Down Expand Up @@ -477,6 +493,13 @@ unset -f mv
! find "$credentials_dir" -name '.credentials.*' -print -quit | grep -q . || fail "failed credentials write left a temporary file"
pass "credentials are atomically replaced as a private regular file"

# The directory holding the plaintext password is hardened the same way, so a
# set-ID bit inherited from its parent cannot hand new credentials a group.
chmod 2755 "$credentials_dir"
write_credentials dave 'another-pw'
[[ $(stat -c '%a' "$credentials_dir") == 700 ]] || fail "credentials directory kept a set-ID bit"
pass "a set-ID bit on the credentials directory is cleared"

# Free-space accounting follows the real storage target.
reset_case
mkdir -p "$external_storage" "$HOME/Windows"
Expand Down