ddf19c
From c7ae38df696e4be432fd418c670dcea892b910a7 Mon Sep 17 00:00:00 2001
ddf19c
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
ddf19c
Date: Mon, 27 Jan 2020 19:01:27 +0100
ddf19c
Subject: [PATCH 056/116] virtiofsd: sandbox mount namespace
ddf19c
MIME-Version: 1.0
ddf19c
Content-Type: text/plain; charset=UTF-8
ddf19c
Content-Transfer-Encoding: 8bit
ddf19c
ddf19c
RH-Author: Dr. David Alan Gilbert <dgilbert@redhat.com>
ddf19c
Message-id: <20200127190227.40942-53-dgilbert@redhat.com>
ddf19c
Patchwork-id: 93504
ddf19c
O-Subject: [RHEL-AV-8.2 qemu-kvm PATCH 052/112] virtiofsd: sandbox mount namespace
ddf19c
Bugzilla: 1694164
ddf19c
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
ddf19c
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
ddf19c
RH-Acked-by: Sergio Lopez Pascual <slp@redhat.com>
ddf19c
ddf19c
From: Stefan Hajnoczi <stefanha@redhat.com>
ddf19c
ddf19c
Use a mount namespace with the shared directory tree mounted at "/" and
ddf19c
no other mounts.
ddf19c
ddf19c
This prevents symlink escape attacks because symlink targets are
ddf19c
resolved only against the shared directory and cannot go outside it.
ddf19c
ddf19c
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
ddf19c
Signed-off-by: Peng Tao <tao.peng@linux.alibaba.com>
ddf19c
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
ddf19c
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
ddf19c
(cherry picked from commit 5baa3b8e95064c2434bd9e2f312edd5e9ae275dc)
ddf19c
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
ddf19c
---
ddf19c
 tools/virtiofsd/passthrough_ll.c | 89 ++++++++++++++++++++++++++++++++++++++++
ddf19c
 1 file changed, 89 insertions(+)
ddf19c
ddf19c
diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
ddf19c
index e2e2211..0570453 100644
ddf19c
--- a/tools/virtiofsd/passthrough_ll.c
ddf19c
+++ b/tools/virtiofsd/passthrough_ll.c
ddf19c
@@ -50,6 +50,7 @@
ddf19c
 #include <stdlib.h>
ddf19c
 #include <string.h>
ddf19c
 #include <sys/file.h>
ddf19c
+#include <sys/mount.h>
ddf19c
 #include <sys/syscall.h>
ddf19c
 #include <sys/xattr.h>
ddf19c
 #include <unistd.h>
ddf19c
@@ -1943,6 +1944,58 @@ static void print_capabilities(void)
ddf19c
     printf("}\n");
ddf19c
 }
ddf19c
 
ddf19c
+/* This magic is based on lxc's lxc_pivot_root() */
ddf19c
+static void setup_pivot_root(const char *source)
ddf19c
+{
ddf19c
+    int oldroot;
ddf19c
+    int newroot;
ddf19c
+
ddf19c
+    oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
ddf19c
+    if (oldroot < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "open(/): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    newroot = open(source, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
ddf19c
+    if (newroot < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "open(%s): %m\n", source);
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (fchdir(newroot) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (syscall(__NR_pivot_root, ".", ".") < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "pivot_root(., .): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (fchdir(oldroot) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "fchdir(oldroot): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (mount("", ".", "", MS_SLAVE | MS_REC, NULL) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "mount(., MS_SLAVE | MS_REC): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (umount2(".", MNT_DETACH) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "umount2(., MNT_DETACH): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (fchdir(newroot) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    close(newroot);
ddf19c
+    close(oldroot);
ddf19c
+}
ddf19c
+
ddf19c
 static void setup_proc_self_fd(struct lo_data *lo)
ddf19c
 {
ddf19c
     lo->proc_self_fd = open("/proc/self/fd", O_PATH);
ddf19c
@@ -1952,6 +2005,39 @@ static void setup_proc_self_fd(struct lo_data *lo)
ddf19c
     }
ddf19c
 }
ddf19c
 
ddf19c
+/*
ddf19c
+ * Make the source directory our root so symlinks cannot escape and no other
ddf19c
+ * files are accessible.
ddf19c
+ */
ddf19c
+static void setup_mount_namespace(const char *source)
ddf19c
+{
ddf19c
+    if (unshare(CLONE_NEWNS) != 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNS): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_PRIVATE): %m\n");
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    if (mount(source, source, NULL, MS_BIND, NULL) < 0) {
ddf19c
+        fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source);
ddf19c
+        exit(1);
ddf19c
+    }
ddf19c
+
ddf19c
+    setup_pivot_root(source);
ddf19c
+}
ddf19c
+
ddf19c
+/*
ddf19c
+ * Lock down this process to prevent access to other processes or files outside
ddf19c
+ * source directory.  This reduces the impact of arbitrary code execution bugs.
ddf19c
+ */
ddf19c
+static void setup_sandbox(struct lo_data *lo)
ddf19c
+{
ddf19c
+    setup_mount_namespace(lo->source);
ddf19c
+}
ddf19c
+
ddf19c
 int main(int argc, char *argv[])
ddf19c
 {
ddf19c
     struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
ddf19c
@@ -2052,6 +2138,7 @@ int main(int argc, char *argv[])
ddf19c
     }
ddf19c
 
ddf19c
     lo.root.fd = open(lo.source, O_PATH);
ddf19c
+
ddf19c
     if (lo.root.fd == -1) {
ddf19c
         fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source);
ddf19c
         exit(1);
ddf19c
@@ -2075,6 +2162,8 @@ int main(int argc, char *argv[])
ddf19c
     /* Must be after daemonize to get the right /proc/self/fd */
ddf19c
     setup_proc_self_fd(&lo);
ddf19c
 
ddf19c
+    setup_sandbox(&lo);
ddf19c
+
ddf19c
     /* Block until ctrl+c or fusermount -u */
ddf19c
     ret = virtio_loop(se);
ddf19c
 
ddf19c
-- 
ddf19c
1.8.3.1
ddf19c