more fixes, add argon2 patch

This commit is contained in:
Anderson Rosenberg 2025-09-01 17:42:15 -04:00
parent c28c503296
commit 0830eb4e45
No known key found for this signature in database
GPG key ID: 7ACF448C0590AB9C
11 changed files with 150 additions and 86 deletions

2
build.sh Normal file → Executable file
View file

@ -2,5 +2,7 @@
set -euo pipefail set -euo pipefail
# Build Shufflecake # Build Shufflecake
echo "[INFO]: Building qubes-sflc..."
cd qubes-sflc cd qubes-sflc
./build.sh ./build.sh
echo "[INFO]: Build complete."

View file

@ -10,10 +10,10 @@ depends() {
install() { install() {
# Interactive script # Interactive script
inst_simple "$moddir/scripts/reliant-initramfs.sh" "/usr/local/share/scripts/reliant-initramfs.sh" inst_script "$moddir/scripts/reliant-initramfs.sh" "/usr/local/share/scripts/reliant-initramfs.sh"
# Main service # Main service
inst_simple "$moddir/reliant.service" "/etc/systemd/system/reliant.service" inst "$moddir/reliant.service" "/etc/systemd/system/reliant.service"
systemctl --root="$initdir" enable reliant.service systemctl --root="$initdir" enable reliant.service
# Shufflecake # Shufflecake
@ -28,10 +28,12 @@ install() {
# TODO: Do we need udev rules to --setro newly attached devices? # TODO: Do we need udev rules to --setro newly attached devices?
# Reliant # Reliant
for file in "/usr/local/share/scripts/reliant-*" "/usr/local/sbin/reliant-*" "/usr/local/bin/surgeon-*"; do inst_script /usr/local/share/scripts/reliant-common.sh
inst_binary "$file" inst_script /usr/local/sbin/reliant-security
done inst_script /usr/local/sbin/surgeon-suture
inst_simple "/etc/reliant.conf" inst_script /usr/local/sbin/reliant-mount
inst_script /usr/local/sbin/reliant-hash
inst /etc/reliant.conf
# Other binaries # Other binaries
inst_multiple dmesg lsblk blockdev mount dd wc sed sleep md5sum find modprobe cut grep mkdir rm tail inst_multiple dmesg lsblk blockdev mount dd wc sed sleep md5sum find modprobe cut grep mkdir rm tail

View file

@ -6,7 +6,7 @@ export RELIANT_INITRAMFS=$RELIANT_TRUE
# Upon encountering issues, prompt the user for the next course of action # Upon encountering issues, prompt the user for the next course of action
reliant_prompt_user() { reliant_prompt_user() {
while true; do while true; do
REPLY=$(plymouth ask-question --prompt="System might be compromised. Proceed? [Y(es)/N(o)/S(kip)]: ") REPLY=$(plymouth ask-question --prompt="System might be compromised. Proceed? [Y(es)/N(o)/S(kip)]")
case $REPLY in case $REPLY in
[Yy]*) [Yy]*)
# User confirmed the system is safe to boot # User confirmed the system is safe to boot
@ -29,16 +29,16 @@ reliant_prompt_user() {
exit 0 ;; exit 0 ;;
*) *)
plymouth display-message --text="Invalid response. [Y(es)/N(o)/S(kip)]: " plymouth display-message --text="Invalid response."
sleep 1 sleep 1
plymouth hide-message --text="Invalid response. [Y(es)/N(o)/S(kip)]: " plymouth hide-message --text="Invalid response."
;; ;;
esac esac
done done
} }
# Ensures the canary files have been wiped by previous shutdown # Ensures the canary files have been wiped by previous shutdown
reliant_initramfs_check_canaries() { reliant_security_initramfs_check_canaries() {
# /rootfs.canary # /rootfs.canary
if [ -f /rootfs.canary ]; then if [ -f /rootfs.canary ]; then
reliant_error "reliant_initramfs_check_canaries: /rootfs.canary exists!" reliant_error "reliant_initramfs_check_canaries: /rootfs.canary exists!"
@ -73,14 +73,25 @@ reliant_initramfs_check_canaries() {
# Verifies that the initramfs cannot leak information via non-volatile media, logs or network # Verifies that the initramfs cannot leak information via non-volatile media, logs or network
reliant_security_check_initramfs() { reliant_security_check_initramfs() {
# Run the common checks # Run the common checks first
reliant-security /sysroot || return $RELIANT_SECURITY_FAIL status_common=$RELIANT_OK
reliant-security /sysroot || status_common=$?
# No need to report, already done
# Check canaries # Check canaries
reliant_security_initramfs_check_canaries || return $RELIANT_SECURITY_FAIL status_canaries=$RELIANT_OK
reliant_security_initramfs_check_canaries || status_canaries=$?
echo "[CANARIES]: $(reliant_err2str $status_canaries)"
# Pass # Calculate and return the verdict
return 0 echo -n "[VERDICT]: "
if [ $status_common -eq $RELIANT_OK ] && [ $status_canaries -eq $RELIANT_OK ]; then
echo "SAFE TO BOOT"
return $RELIANT_OK
else
echo "POTENTIALLY COMPROMISED"
return $RELIANT_SECURITY_FAIL
fi
} }
# Main function # Main function

View file

@ -22,6 +22,7 @@ fi
: "${RELIANT_SKIP_CHECKSUM:=}" : "${RELIANT_SKIP_CHECKSUM:=}"
: "${RELIANT_SPARSE_SAMPLES:=512}" : "${RELIANT_SPARSE_SAMPLES:=512}"
: "${RELIANT_BOOTSTRAP_QUBE:=bootstrap}" : "${RELIANT_BOOTSTRAP_QUBE:=bootstrap}"
: "${RELIANT_KERNEL_VERSION:=$(qvm-run --pass-io "$RELIANT_BOOTSTRAP_QUBE" 'uname -r')}"
# Validate configuration values # Validate configuration values
if [ -z "$RELIANT_SECURE_DEVICE" ]; then if [ -z "$RELIANT_SECURE_DEVICE" ]; then
@ -86,6 +87,10 @@ reliant_install_dracut() {
reliant_install_file "$1" "$RELIANT_DRACUT_DIR/$2" "$3" "$4" "$5" reliant_install_file "$1" "$RELIANT_DRACUT_DIR/$2" "$3" "$4" "$5"
} }
# Run the build script inside of the bootstrap qube
echo "[INFO]: Building $RELIANT_BOOTSTRAP_QUBE:$RELIANT_SYSTEM_ROOT for kernel $RELIANT_KERNEL_VERSION..."
qvm-run --pass-io "$RELIANT_BOOTSTRAP_QUBE" "sh -c 'cd $RELIANT_SYSTEM_ROOT && ./build.sh'"
# Begin the installation process # Begin the installation process
echo "[INFO]: Installing reliant-system from $RELIANT_BOOTSTRAP_QUBE:$RELIANT_SYSTEM_ROOT..." echo "[INFO]: Installing reliant-system from $RELIANT_BOOTSTRAP_QUBE:$RELIANT_SYSTEM_ROOT..."
@ -118,7 +123,7 @@ reliant_install_dracut dracut/99reliant/scripts/reliant-initramfs.sh scripts/rel
reliant_install_dracut dracut/99reliant/patches/create-snapshot.sh patches/create-snapshot.sh 0755 root root reliant_install_dracut dracut/99reliant/patches/create-snapshot.sh patches/create-snapshot.sh 0755 root root
# reliant-system/qubes-sflc # reliant-system/qubes-sflc
reliant_install_file qubes-sflc/dm-sflc.ko "/usr/lib/modules/$(uname -r)/extra" 0644 root root reliant_install_file qubes-sflc/dm-sflc.ko "/usr/lib/modules/$RELIANT_KERNEL_VERSION/extra/dm-sflc.ko" 0644 root root
reliant_install_sbin qubes-sflc/shufflecake shufflecake 0744 root root reliant_install_sbin qubes-sflc/shufflecake shufflecake 0744 root root
echo "[INFO]: Successfully copied files to dom0." echo "[INFO]: Successfully copied files to dom0."
@ -158,11 +163,11 @@ systemctl enable shufflecake-close.service
surgeon-dissect -t varlibqubes surgeon-dissect -t varlibqubes
reliant-snapshot-rw reliant-snapshot-rw
# reliant-system/qubes-sflc
depmod -a "$RELIANT_KERNEL_VERSION"
# reliant-system/dracut # reliant-system/dracut
dracut --force --regenerate-all dracut --force --regenerate-all
# reliant-system/qubes-sflc
depmod
# Report successful installation # Report successful installation
echo "[INFO]: Installation complete. Reboot to enter protected mode." echo "[INFO]: Installation complete. Reboot to enter protected mode."

View file

@ -2,9 +2,29 @@
set -euo pipefail set -euo pipefail
# Download the submodules # Download the submodules
git submodule init --update --recursive echo "[INFO]: Updating git submodules..."
git submodule update --init --recursive
# Apply the Argon2 memory parameter patch
echo "[INFO]: Applying Argon2 memory patch to improve KDF resilience..."
# Change the working directory
OLD_PWD=$PWD
cd shufflecake-c
# Apply the patch
CRYPTO_H=shufflecake-userland/include/utils/crypto.h
git restore $CRYPTO_H
patch $CRYPTO_H "$OLD_PWD/crypto.h.patch"
# Return back to qubes-sflc
cd "$OLD_PWD"
# Notify user of a breaking change
echo "[WARN]: Your new Argon2 memory parameter is 2097152 KiB, INCOMPATIBLE with default Shufflecake."
# Ensure Docker is running # Ensure Docker is running
echo "[INFO]: Starting the Docker container..."
sudo systemctl start docker sudo systemctl start docker
# Build and execute the container # Build and execute the container
@ -12,5 +32,9 @@ sudo docker build -t qubes-sflc .
sudo docker run --rm -v /usr/lib/modules:/usr/lib/modules:ro -v $PWD/shufflecake-c:/root/shufflecake-c qubes-sflc sudo docker run --rm -v /usr/lib/modules:/usr/lib/modules:ro -v $PWD/shufflecake-c:/root/shufflecake-c qubes-sflc
# Copy artifacts # Copy artifacts
cp shufflecake-c/dm-sflc.ko $PWD/dm-sflc.ko echo "[INFO]: Copying build artifacts..."
cp shufflecake-c/shufflecake $PWD/shufflecake cp shufflecake-c/dm-sflc.ko "$PWD/dm-sflc.ko"
cp shufflecake-c/shufflecake "$PWD/shufflecake"
# Restore the original crypto.h to avoid interference with future updates
git restore "shufflecake-c/$CRYPTO_H"

13
qubes-sflc/crypto.h.patch Normal file
View file

@ -0,0 +1,13 @@
diff --git a/shufflecake-userland/include/utils/crypto.h b/shufflecake-userland/include/utils/crypto.h
index 8366134..303b2ae 100644
--- a/shufflecake-userland/include/utils/crypto.h
+++ b/shufflecake-userland/include/utils/crypto.h
@@ -67,7 +67,7 @@
// Argon memory parameter
// We assume machines with at least 128 MiB available RAM, so 2^17 kiB
-#define SFLC_ARGON_M (1 << 17) /* kibibytes */
+#define SFLC_ARGON_M 2097152 /* kibibytes */
// Argon iterations count
// We aim for 1-2 seconds on a low-end laptop or mobile (it's a one-time operation)

View file

@ -23,8 +23,9 @@ modprobe dm-sflc
# Prompt the user for their password # Prompt the user for their password
while true; do while true; do
if plymouth ask-for-password --prompt="Auxillary password" | shufflecake open "$SECURE_DEVICE" &>/dev/null; then if plymouth ask-for-password --prompt="Auxillary password" | shufflecake open "$RELIANT_SECURE_DEVICE" &>/dev/null; then
plymouth display-message --text="Success" plymouth display-message --text="Success"
break
else else
plymouth display-message --text="Invalid password" plymouth display-message --text="Invalid password"
sleep 2 sleep 2

View file

@ -22,7 +22,7 @@ fi
name=$1 name=$1
echo -n "Destroying links... " echo -n "Destroying links... "
for appvm in /run/shufflecake/$name/appvms/*; do for appvm in "/run/shufflecake/$name/appvms/"*; do
qube="${appvm##*/}" qube="${appvm##*/}"
# Shutdown # Shutdown
@ -31,8 +31,8 @@ for appvm in /run/shufflecake/$name/appvms/*; do
# Remove directory # Remove directory
directory="/var/lib/qubes/appvms/$qube" directory="/var/lib/qubes/appvms/$qube"
if [ -d $directory ]; then if [ -d "$directory" ]; then
rm -rf $directory rm -rf "$directory"
fi fi
done done
echo "Done." echo "Done."

View file

@ -15,6 +15,11 @@ fi
# No more variable checks needed # No more variable checks needed
set -u set -u
# Require superuser permissions
if [ "$EUID" -ne 0 ]; then
reliant_fail "must be superuser"
fi
# Kernel command line check # Kernel command line check
reliant_security_check_cmdline() { reliant_security_check_cmdline() {
# Check if /proc/cmdline exists # Check if /proc/cmdline exists
@ -94,7 +99,7 @@ reliant_security_check_network() {
# Persistent, real filesystems must be readonly # Persistent, real filesystems must be readonly
reliant_security_check_filesystems() { reliant_security_check_filesystems() {
# Check each mountpoint # Check each mountpoint
mount | while IFS= read -r line; do mount | while read -r line; do
# Determine the filesystem type # Determine the filesystem type
filesystem_type=$(echo "$line" | awk '{ print $5 }' ) filesystem_type=$(echo "$line" | awk '{ print $5 }' )
@ -115,7 +120,7 @@ reliant_security_check_filesystems() {
esac esac
done done
# Pass from subshell # Pass status from subshell
return $? return $?
} }
@ -139,13 +144,17 @@ reliant_security_check_overlay() {
fi fi
# Find the mountpoint # Find the mountpoint
mount | while IFS= read -r line; do mount | {
# Determine the mountpoint found=$RELIANT_FALSE
while read -r line; do
# Filter by mountpoint
filesystem_mountpoint=$(echo "$line" | awk '{ print $3 }') filesystem_mountpoint=$(echo "$line" | awk '{ print $3 }')
# Skip if not matching
if [ "$filesystem_mountpoint" != "$1" ]; then continue; fi if [ "$filesystem_mountpoint" != "$1" ]; then continue; fi
# Filter by filesystem type
filesystem_type=$(echo "$line" | awk '{ print $5 }' )
if [ "$filesystem_type" != "overlay" ]; then continue; fi
# Determine the filesystem flags # Determine the filesystem flags
filesystem_flags=$(echo "$line" | awk '{ print $6 }') filesystem_flags=$(echo "$line" | awk '{ print $6 }')
@ -155,8 +164,8 @@ reliant_security_check_overlay() {
IFS=',' IFS=','
for flag in $filesystem_flags; do for flag in $filesystem_flags; do
case $flag in case $flag in
'upperdir=/run/systemd/overlay-sysroot/upper') upper_found=0 ;; 'upperdir=/run/systemd/overlay-sysroot/upper') upper_found=$RELIANT_TRUE ;;
'workdir=/run/systemd/overlay-sysroot/work' ) work_found=0 ;; 'workdir=/run/systemd/overlay-sysroot/work' ) work_found=$RELIANT_TRUE ;;
esac esac
done done
@ -171,36 +180,28 @@ reliant_security_check_overlay() {
fi fi
# Indicate that the mountpoint has been found # Indicate that the mountpoint has been found
exit 255 found=$RELIANT_TRUE
break
done done
status=$?
# Check the subshell return code # Check if the mountpoint has been found within the loop
case "$status" in if [ $found -ne $RELIANT_TRUE ]; then
0) reliant_error "reliant_security_check_overlay: mountpoint for overlay root on $1 not found"
# Not found exit $RELIANT_SECURITY_FAIL
reliant_error "reliant_security_check_overlay: mountpoint $1 not found" else
return $RELIANT_FAIL exit $RELIANT_OK
;; fi
}
255) # Forward from subshell
# Found return $?
return $RELIANT_OK
;;
*)
# Other issues
reliant_error "reliant_security_check_overlay: mountpoint $1 verification failed"
return $status
;;
esac
} }
# Checksum verification # Checksum verification
reliant_security_check_devices() { reliant_security_check_devices() {
IFS=$'\n' IFS=$'\n'
for device in $(lsblk -rno name); do for device_path in $(lsblk -rnpo name); do
device_path="/dev/$device" device="${device_path##*/}"
# Check if it is a valid block device # Check if it is a valid block device
if [ ! -b "$device_path" ]; then return $RELIANT_BUG; fi if [ ! -b "$device_path" ]; then return $RELIANT_BUG; fi
@ -214,6 +215,7 @@ reliant_security_check_devices() {
# User-specified devices will be excluded # User-specified devices will be excluded
should_skip=$RELIANT_FALSE should_skip=$RELIANT_FALSE
IFS=' '
for skip_device in $RELIANT_SKIP_CHECKSUM; do for skip_device in $RELIANT_SKIP_CHECKSUM; do
if [ "$skip_device" = "$device_path" ]; then if [ "$skip_device" = "$device_path" ]; then
should_skip=$RELIANT_TRUE should_skip=$RELIANT_TRUE
@ -232,7 +234,7 @@ reliant_security_check_devices() {
# Inside initramfs, use Plymouth, otherwise, use read # Inside initramfs, use Plymouth, otherwise, use read
prompt="$device_path matches $hash? [Y/N]" prompt="$device_path matches $hash? [Y/N]"
if [ $RELIANT_INITRAMFS -eq $RELIANT_TRUE ]; then if [ "$RELIANT_INITRAMFS" -eq $RELIANT_TRUE ]; then
REPLY=$(plymouth ask-question --prompt="$prompt") REPLY=$(plymouth ask-question --prompt="$prompt")
else else
read -r -p "$prompt: " read -r -p "$prompt: "
@ -244,6 +246,9 @@ reliant_security_check_devices() {
return $RELIANT_SECURITY_FAIL return $RELIANT_SECURITY_FAIL
fi fi
done done
# Forward from subshell
return $?
} }
# Main function # Main function

View file

@ -106,7 +106,7 @@ def parse_qubes_xml(target_pools, qubes_xml):
for domain in root.find('domains'): for domain in root.find('domains'):
volume_config = domain.find('volume-config') volume_config = domain.find('volume-config')
if volume_config is None: if volume_config is None:
continue volume_config = []
# Detect sys-net and sys-whonix # Detect sys-net and sys-whonix
# TODO: use reliant.conf RELIANT_RW_DOMAINS # TODO: use reliant.conf RELIANT_RW_DOMAINS
@ -118,8 +118,6 @@ def parse_qubes_xml(target_pools, qubes_xml):
# Check VM type # Check VM type
if domain.get('class') == 'AdminVM': if domain.get('class') == 'AdminVM':
# dom0
# Disable update checking # Disable update checking
features = domain.find('features') features = domain.find('features')
for feature in features: for feature in features:
@ -127,6 +125,9 @@ def parse_qubes_xml(target_pools, qubes_xml):
features.remove(feature) features.remove(feature)
ElementTree.SubElement(features, 'feature', name='config.default.qubes-update-check').text = '' ElementTree.SubElement(features, 'feature', name='config.default.qubes-update-check').text = ''
# dom0 does not have volumes, so add it immediately
domains['varlibqubes'].append(domain)
else: else:
# Not dom0, qube ID must be randomized # Not dom0, qube ID must be randomized
qid = random.randint(1, qubes.config.max_qid) qid = random.randint(1, qubes.config.max_qid)