Add services for paranoid backup restore mode
Add a pair of services: 1. qubes.RegisterBackupLocation - called by dom0, registers what backup location (including both file and command options) can be accessed. Registered location gets an ID returned to the caller. The location (and its ID) is valid as long as the service call remains open. 2. qubes.RestoreById - called by restoring DispVM to retrieve the backup content. The service expects location ID as an argument, and then list of files/directories (separated with spaces) on the first line of stdin. This is very similar to qubes.Restore service, with exception for the archive location control. QubesOS/qubes-issues#5310
This commit is contained in:
parent
7c261f45da
commit
8066129445
2
debian/qubes-core-agent.install
vendored
2
debian/qubes-core-agent.install
vendored
@ -18,7 +18,9 @@ etc/qubes-rpc/qubes.InstallUpdatesGUI
|
|||||||
etc/qubes-rpc/qubes.OpenInVM
|
etc/qubes-rpc/qubes.OpenInVM
|
||||||
etc/qubes-rpc/qubes.OpenURL
|
etc/qubes-rpc/qubes.OpenURL
|
||||||
etc/qubes-rpc/qubes.PostInstall
|
etc/qubes-rpc/qubes.PostInstall
|
||||||
|
etc/qubes-rpc/qubes.RegisterBackupLocation
|
||||||
etc/qubes-rpc/qubes.ResizeDisk
|
etc/qubes-rpc/qubes.ResizeDisk
|
||||||
|
etc/qubes-rpc/qubes.RestoreById
|
||||||
etc/qubes-rpc/qubes.Restore
|
etc/qubes-rpc/qubes.Restore
|
||||||
etc/qubes-rpc/qubes.SelectDirectory
|
etc/qubes-rpc/qubes.SelectDirectory
|
||||||
etc/qubes-rpc/qubes.SelectFile
|
etc/qubes-rpc/qubes.SelectFile
|
||||||
|
@ -60,6 +60,8 @@ install:
|
|||||||
qubes.WaitForSession \
|
qubes.WaitForSession \
|
||||||
qubes.DetachPciDevice \
|
qubes.DetachPciDevice \
|
||||||
qubes.Backup qubes.Restore \
|
qubes.Backup qubes.Restore \
|
||||||
|
qubes.RegisterBackupLocation \
|
||||||
|
qubes.RestoreById \
|
||||||
qubes.SelectFile qubes.SelectDirectory \
|
qubes.SelectFile qubes.SelectDirectory \
|
||||||
qubes.GetImageRGBA \
|
qubes.GetImageRGBA \
|
||||||
qubes.SetDateTime \
|
qubes.SetDateTime \
|
||||||
|
33
qubes-rpc/qubes.RegisterBackupLocation
Normal file
33
qubes-rpc/qubes.RegisterBackupLocation
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Register backup location (path or a command) to be retrieved with qubes
|
||||||
|
# .RestoreById service.
|
||||||
|
# Registered location is only valid as long as this service call stays open
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REGISTRY_DIR="$XDG_RUNTIME_DIR/qubes-backup-location"
|
||||||
|
|
||||||
|
if ! [ -d "$REGISTRY_DIR" ]; then
|
||||||
|
mkdir -p "$REGISTRY_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r backup_location
|
||||||
|
|
||||||
|
REGISTRY_FILE=$(mktemp "$REGISTRY_DIR/XXXXXXXX")
|
||||||
|
|
||||||
|
PID=$$
|
||||||
|
# this isn't perfetct, as comm field could contain spaces, but we do control
|
||||||
|
# this value and we know it doesn't
|
||||||
|
START_TIME=$(cut -f 22 -d ' ' /proc/$PID/stat)
|
||||||
|
# add process id at the beginning to help verifying if it's still running;
|
||||||
|
# record starttime too, to detect PID reuse
|
||||||
|
printf "%d %d\n%s\n" "$PID" "$START_TIME" "$backup_location" >"$REGISTRY_FILE"
|
||||||
|
# output registered ID to the user
|
||||||
|
basename "$REGISTRY_FILE"
|
||||||
|
# close stdout
|
||||||
|
exec >&-
|
||||||
|
# wait for stdin to close
|
||||||
|
cat >/dev/null
|
||||||
|
# and cleanup
|
||||||
|
rm -f "$REGISTRY_FILE"
|
82
qubes-rpc/qubes.RestoreById
Executable file
82
qubes-rpc/qubes.RestoreById
Executable file
@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REGISTRY_DIR="$XDG_RUNTIME_DIR/qubes-backup-location"
|
||||||
|
|
||||||
|
backup_location_id="$1"
|
||||||
|
|
||||||
|
if [ -z "$backup_location_id" ]; then
|
||||||
|
echo "Missing backup location ID argument" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -e "$REGISTRY_DIR/$backup_location_id" ]; then
|
||||||
|
echo "Invalid location ID" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -r check_pid check_starttime
|
||||||
|
read -r backup_location
|
||||||
|
break
|
||||||
|
done < "$REGISTRY_DIR/$backup_location_id"
|
||||||
|
|
||||||
|
if ! [ -e "/proc/$check_pid" ]; then
|
||||||
|
echo "Invalid location ID" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pid_starttime=$(cut -f 22 -d ' ' "/proc/$check_pid/stat")
|
||||||
|
if [ "$check_starttime" != "$pid_starttime" ]; then
|
||||||
|
echo "Invalid location ID" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# now $backup_location is verified to be still valid
|
||||||
|
|
||||||
|
echo Starting Restorecopy >&2
|
||||||
|
read -r untrusted_paths
|
||||||
|
echo "Backup location: $backup_location" >&2
|
||||||
|
echo "Paths: $untrusted_paths" >&2
|
||||||
|
if [ -f "$backup_location" ] ; then
|
||||||
|
echo "Performing restore from backup file $backup_location" >&2
|
||||||
|
TARGET="$backup_location"
|
||||||
|
echo "Copying $TARGET to STDOUT" >&2
|
||||||
|
# tar2qfile always use argv[1] for input path and the rest for selecting
|
||||||
|
# paths to extract - no other options are supported, so passing
|
||||||
|
# untrusted_paths directly is fine
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
/usr/lib/qubes/tar2qfile "$TARGET" $untrusted_paths
|
||||||
|
else
|
||||||
|
echo "Checking if arguments is matching a command" >&2
|
||||||
|
COMMAND=$(echo "$backup_location" | cut -d ' ' -f 1)
|
||||||
|
if command -v "$COMMAND" >/dev/null; then
|
||||||
|
tmpdir=$(mktemp -d)
|
||||||
|
mkfifo "$tmpdir/backup-data"
|
||||||
|
echo "Redirecting $backup_location to STDOUT" >&2
|
||||||
|
# Parsing args to handle quotes correctly
|
||||||
|
# Dangerous method if args are uncontrolled
|
||||||
|
eval "set -- $backup_location"
|
||||||
|
# Use named pipe to pass original stdin to tar2file
|
||||||
|
"$@" > "$tmpdir/backup-data" < /dev/null &
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
# tar2qfile always use argv[1] for input path and the rest for selecting
|
||||||
|
# paths to extract - no other options are supported, so passing
|
||||||
|
# untrusted_paths directly is fine
|
||||||
|
/usr/lib/qubes/tar2qfile "$tmpdir/backup-data" $untrusted_paths
|
||||||
|
# Restoration may be terminated earlier because of selected files. This
|
||||||
|
# will be seen as EPIPE to the retrieving process, which may cause retcode
|
||||||
|
# other than 0 in some cases - which would be incorrectly treated as backup
|
||||||
|
# restore error. So instead of that, use tar2qfile exit code (and have dom0
|
||||||
|
# detect if anything wrong with actual data)
|
||||||
|
retcode=$?
|
||||||
|
wait $!
|
||||||
|
rm "$tmpdir/backup-data"
|
||||||
|
rmdir "$tmpdir"
|
||||||
|
exit "$retcode"
|
||||||
|
else
|
||||||
|
echo "Invalid command $COMMAND" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
@ -590,6 +590,8 @@ rm -f %{name}-%{version}
|
|||||||
%config(noreplace) /etc/qubes-rpc/qubes.DetachPciDevice
|
%config(noreplace) /etc/qubes-rpc/qubes.DetachPciDevice
|
||||||
%config(noreplace) /etc/qubes-rpc/qubes.Backup
|
%config(noreplace) /etc/qubes-rpc/qubes.Backup
|
||||||
%config(noreplace) /etc/qubes-rpc/qubes.Restore
|
%config(noreplace) /etc/qubes-rpc/qubes.Restore
|
||||||
|
%config(noreplace) /etc/qubes-rpc/qubes.RegisterBackupLocation
|
||||||
|
%config(noreplace) /etc/qubes-rpc/qubes.RestoreById
|
||||||
%config(noreplace) /etc/qubes-rpc/qubes.SelectFile
|
%config(noreplace) /etc/qubes-rpc/qubes.SelectFile
|
||||||
%config(noreplace) /etc/qubes-rpc/qubes.SelectDirectory
|
%config(noreplace) /etc/qubes-rpc/qubes.SelectDirectory
|
||||||
%config(noreplace) /etc/qubes-rpc/qubes.GetImageRGBA
|
%config(noreplace) /etc/qubes-rpc/qubes.GetImageRGBA
|
||||||
|
Loading…
Reference in New Issue
Block a user