Преглед на файлове

filecopy: prevent files/dirs movement outside incoming directory during transfer

Otherwise, when the user moves directory, which is still in transfer,
somewhere else, it could allow malicious source domain to escape chroot
and place a file in arbitrary location.

It looks like bind mount is just enough - simple rename fails with
EXDEV, so tools are forced to perform copy+delete, which is enough to
keep unpacker process away from new file location.

One inconvenient detail is that we must clean the mount after transfer
finishes, so root perms cannot be dropped completely. We keep separate
process for only that reason.
Marek Marczykowski-Górecki преди 9 години
родител
ревизия
efc7d4d1f2
променени са 1 файла, в които са добавени 39 реда и са изтрити 6 реда
  1. 39 6
      qubes-rpc/qfile-unpacker.c

+ 39 - 6
qubes-rpc/qfile-unpacker.c

@@ -5,6 +5,9 @@
 #include <stdlib.h>
 #include <pwd.h>
 #include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <fcntl.h>
 #include <string.h>
 #include <unistd.h>
 #include <sys/fsuid.h>
@@ -34,8 +37,11 @@ int prepare_creds_return_uid(const char *username)
 int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unused__)))
 {
 	char *incoming_dir;
-	int uid;
+	int uid, ret;
+	pid_t pid;
 	const char *remote_domain;
+	char *procdir_path;
+	int procfs_fd;
 
 	uid = prepare_creds_return_uid("user");
 
@@ -50,9 +56,36 @@ int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unuse
 	mkdir(incoming_dir, 0700);
 	if (chdir(incoming_dir))
 		gui_fatal("Error chdir to %s", incoming_dir); 
-	if (chroot(incoming_dir)) //impossible
-		gui_fatal("Error chroot to %s", incoming_dir);
-	if (setuid(uid) < 0)
-		gui_fatal("Error changing permissions to '%s'", "user");
-	return do_unpack();
+
+	if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
+		gui_fatal("Failed to mount a directory %s", incoming_dir);
+
+	/* parse the input in unprivileged child process, parent will hold root
+	 * access to unmount incoming dir */
+	switch (pid=fork()) {
+		case -1:
+			gui_fatal("Failed to create new process");
+		case 0:
+			asprintf(&procdir_path, "/proc/%d/fd", getpid());
+			procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY);
+			if (procfs_fd < 0)
+				gui_fatal("Failed to open /proc");
+			free(procdir_path);
+			set_procfs_fd(procfs_fd);
+
+			if (chroot("."))
+				gui_fatal("Error chroot to %s", incoming_dir);
+			if (setuid(uid) < 0) {
+				/* no kdialog inside chroot */
+				perror("setuid");
+				exit(1);
+			}
+			return do_unpack();
+	}
+	if (waitpid(pid, &ret, 0) < 0) {
+		gui_fatal("Failed to wait for child process");
+	}
+	if (umount2(".", MNT_DETACH) < 0)
+		gui_fatal("Cannot umount incoming directory");
+	return ret;
 }