Browse Source

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
Marek Marczykowski-Górecki 3 years ago
parent
commit
0f3e1ae8af

+ 2 - 0
debian/qubes-core-agent.install

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

+ 2 - 0
qubes-rpc/Makefile

@@ -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 \

+ 35 - 12
qubes-rpc/qfile-unpacker.c

@@ -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);
 

+ 33 - 0
qubes-rpc/qubes.RegisterBackupLocation

@@ -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 - 0
qubes-rpc/qubes.RestoreById

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

+ 11 - 13
qubes-rpc/tar2qfile.c

@@ -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");

+ 2 - 0
rpm_spec/core-agent.spec.in

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