Merge remote-tracking branch 'origin/pr/184'

* origin/pr/184:
  Add services for paranoid backup restore mode
  qfile-unpacker: add option (-w) to wait for disk space before extracting
  tar2qfile: fix argument parser
  qfile-unpacker: add option for custom user and target directory
This commit is contained in:
Marek Marczykowski-Górecki 2020-08-07 03:01:25 +02:00
commit 0f3e1ae8af
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
7 changed files with 167 additions and 25 deletions

View File

@ -18,7 +18,9 @@ etc/qubes-rpc/qubes.InstallUpdatesGUI
etc/qubes-rpc/qubes.OpenInVM
etc/qubes-rpc/qubes.OpenURL
etc/qubes-rpc/qubes.PostInstall
etc/qubes-rpc/qubes.RegisterBackupLocation
etc/qubes-rpc/qubes.ResizeDisk
etc/qubes-rpc/qubes.RestoreById
etc/qubes-rpc/qubes.Restore
etc/qubes-rpc/qubes.SelectDirectory
etc/qubes-rpc/qubes.SelectFile

View File

@ -60,6 +60,8 @@ install:
qubes.WaitForSession \
qubes.DetachPciDevice \
qubes.Backup qubes.Restore \
qubes.RegisterBackupLocation \
qubes.RestoreById \
qubes.SelectFile qubes.SelectDirectory \
qubes.GetImageRGBA \
qubes.SetDateTime \

View File

@ -36,7 +36,7 @@ char *prepare_creds_return_dir(int uid)
return pwd->pw_dir;
}
int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unused__)))
int main(int argc, char ** argv)
{
char *home_dir;
char *incoming_dir_root;
@ -46,22 +46,45 @@ int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unuse
const char *remote_domain;
char *procdir_path;
int procfs_fd;
int i;
uid = getuid();
home_dir = prepare_creds_return_dir(uid);
if (argc >= 3) {
errno = 0;
uid = strtol(argv[1], NULL, 10);
if (errno)
gui_fatal("Invalid user ID argument");
home_dir = prepare_creds_return_dir(uid);
incoming_dir = argv[2];
} else {
uid = getuid();
home_dir = prepare_creds_return_dir(uid);
remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
if (!remote_domain) {
gui_fatal("Cannot get remote domain name");
}
remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
if (!remote_domain) {
gui_fatal("Cannot get remote domain name");
if (asprintf(&incoming_dir_root, "%s/%s", home_dir, INCOMING_DIR_NAME) < 0) {
gui_fatal("Error allocating memory");
}
mkdir(incoming_dir_root, 0700);
if (asprintf(&incoming_dir, "%s/%s", incoming_dir_root, remote_domain) < 0)
gui_fatal("Error allocating memory");
mkdir(incoming_dir, 0700);
}
if (asprintf(&incoming_dir_root, "%s/%s", home_dir, INCOMING_DIR_NAME) < 0) {
gui_fatal("Error allocating memory");
for (i = 3; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0)
set_verbose(1);
else if (strcmp(argv[i], "-w") == 0)
if (i+1 < argc && argv[i+1][0] != '-') {
set_wait_for_space(atoi(argv[i+1]));
i++;
} else
set_wait_for_space(1);
else
gui_fatal("Invalid option %s", argv[i]);
}
mkdir(incoming_dir_root, 0700);
if (asprintf(&incoming_dir, "%s/%s", incoming_dir_root, remote_domain) < 0)
gui_fatal("Error allocating memory");
mkdir(incoming_dir, 0700);
if (chdir(incoming_dir))
gui_fatal("Error chdir to %s", incoming_dir);

View 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
View 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

View File

@ -957,7 +957,6 @@ void tar_file_processor(int fd, struct filters *filters)
int main(int argc, char **argv)
{
int i;
char *entry;
int fd = -1;
int use_stdin = 1;
@ -967,18 +966,15 @@ int main(int argc, char **argv)
/* when extracting backup header, dom0 will terminate the transfer with
* EDQUOT just after getting qubes.xml */
set_ignore_quota_error(1);
for (i = 1; i < argc; i++) {
set_nonblock(0);
if (strcmp(argv[i], "-")==0) {
set_nonblock(0);
if (argc > 1) {
if (strcmp(argv[1], "-")==0) {
use_stdin = 1;
i++;
break;
} else {
// Parse tar file
use_stdin = 0;
entry = argv[i];
entry = argv[1];
#ifdef DEBUG
fprintf(stderr,"Parsing file %s\n",entry);
fprintf(stderr, "Parsing file %s\n",entry);
#endif
fd = open(entry, O_RDONLY);
@ -986,12 +982,14 @@ int main(int argc, char **argv)
fprintf(stderr,"Error opening file %s\n",entry);
exit(2);
}
i++;
break;
}
}
filters.filters_count = argc-i;
filters.filters = argv+i;
// Parse tar file
if (argc > 2)
filters.filters_count = argc-2;
else
filters.filters_count = 0;
filters.filters = argv+2;
filters.filters_matches = calloc(filters.filters_count, sizeof(int));
if (filters.filters_matches == NULL) {
perror("calloc");

View File

@ -590,6 +590,8 @@ rm -f %{name}-%{version}
%config(noreplace) /etc/qubes-rpc/qubes.DetachPciDevice
%config(noreplace) /etc/qubes-rpc/qubes.Backup
%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.SelectDirectory
%config(noreplace) /etc/qubes-rpc/qubes.GetImageRGBA