[libvirt] [RFC PATCH V2] Add proxy FS support to libvirt

M. Mohan Kumar mohan at in.ibm.com
Wed Jan 18 17:11:15 UTC 2012


From: "M. Mohan Kumar" <mohan at in.ibm.com>

A new FS driver type 'proxy' is added to QEMU 9p server. This patch adds
support for using proxy FS driver from libvirt.

QEMU proxy FS driver uses socket for communicating between helper and qemu
proxy FS driver. Proxy helper (a stand alone binary part of qemu) is invoked
with one of the descriptors created using socketpair call and the share path.
Similarly QEMU is invoked with another descriptor created using the same
socketpair system call and with other required FS driver parameters.

Need for proxy FS driver
========================
Pass through security model in QEMU 9p server has following issues:
1) TOCTTOU vulnerability: Following symbolic links in the server could
provide access to files beyond 9p export path.

2) Running QEMU with root privilege could be a security issue (pass
through security model needs root privilege).

Proxy FS driver is implemented to solve these issues.

Proxy FS uses chroot + socket combination for securing the vulnerability
known with following symbolic links. Intention of adding a new filesystem
type is to allow qemu to run in non-root mode, but doing privileged
operations in a chroot environment using socket IO.

Proxy helper is invoked with root privileges and chroots into 9p export path.
QEMU proxy fs driver sends filesystem request to proxy helper and receives the
response from it.

Proxy helper is designed such a way that it needs only few capabilities related
to filesystem operations (such as CAP_DAC_OVERRIDE, CAP_FOWNER, etc) and all
other capabilities are dropped (CAP_SYS_CHROOT, etc)

Proxy patches
    http://permalink.gmane.org/gmane.comp.emulators.qemu/128735

Signed-off-by: M. Mohan Kumar <mohan at in.ibm.com>

---
Changes from previous version
* Remove the xml node for specifying the virtfs-proxy-helper, now it is
determined from qemu binary.

 docs/formatdomain.html.in    |    3 +-
 src/conf/domain_conf.c       |    3 +-
 src/conf/domain_conf.h       |    2 +-
 src/qemu/qemu_capabilities.c |    3 ++
 src/qemu/qemu_capabilities.h |    1 +
 src/qemu/qemu_command.c      |   71 ++++++++++++++++++++++++++++++++++++++++--
 src/qemu/qemu_command.h      |    3 +-
 tests/qemuhelptest.c         |    3 +-
 8 files changed, 81 insertions(+), 8 deletions(-)

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 18b7e22..e398779 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1336,7 +1336,8 @@
         This mode also has an optional
         sub-element <code>driver</code>, with an
         attribute <code>type='path'</code>
-        or <code>type='handle'</code> <span class="since">(since
+        or <code>type='handle'</code>
+        or <code>type='proxy'</code> <span class="since">(since
         0.9.7)</span>.
         </dd>
         <dt><code>type='template'</code></dt>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 0190a81..2d45324 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -250,7 +250,8 @@ VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
 VIR_ENUM_IMPL(virDomainFSDriverType, VIR_DOMAIN_FS_DRIVER_TYPE_LAST,
               "default",
               "path",
-              "handle")
+              "handle",
+              "proxy")
 
 VIR_ENUM_IMPL(virDomainFSAccessMode, VIR_DOMAIN_FS_ACCESSMODE_LAST,
               "passthrough",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 03aa5b6..3796da4 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -466,6 +466,7 @@ enum virDomainFSDriverType {
     VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT = 0,
     VIR_DOMAIN_FS_DRIVER_TYPE_PATH,
     VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE,
+    VIR_DOMAIN_FS_DRIVER_TYPE_PROXY,
 
     VIR_DOMAIN_FS_DRIVER_TYPE_LAST
 };
@@ -491,7 +492,6 @@ struct _virDomainFSDef {
     virDomainDeviceInfo info;
 };
 
-
 /* 5 different types of networking config */
 enum virDomainNetType {
     VIR_DOMAIN_NET_TYPE_USER,
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 43c7578..9c01a3b 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -144,6 +144,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
               "ich9-ahci",
               "no-acpi",
               "fsdev-readonly",
+              "fsdev-proxy",
     );
 
 struct qemu_feature_flags {
@@ -1083,6 +1084,8 @@ qemuCapsComputeCmdFlags(const char *help,
         qemuCapsSet(flags, QEMU_CAPS_FSDEV);
         if (strstr(fsdev, "readonly"))
             qemuCapsSet(flags, QEMU_CAPS_FSDEV_READONLY);
+        if (strstr(fsdev, "sock_fd"))
+            qemuCapsSet(flags, QEMU_CAPS_FSDEV_PROXY);
     }
     if (strstr(help, "-smbios type"))
         qemuCapsSet(flags, QEMU_CAPS_SMBIOS_TYPE);
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index c759baf..e129e35 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -117,6 +117,7 @@ enum qemuCapsFlags {
     QEMU_CAPS_ICH9_AHCI         = 77, /* -device ich9-ahci */
     QEMU_CAPS_NO_ACPI		= 78, /* -no-acpi */
     QEMU_CAPS_FSDEV_READONLY    =79, /* -fsdev readonly supported */
+    QEMU_CAPS_FSDEV_PROXY      = 80, /* -fsdev proxy supported */
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index f2e9cfa..0c79340 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -44,6 +44,7 @@
 #include <sys/utsname.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <libgen.h>
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -106,7 +107,8 @@ VIR_ENUM_DECL(qemuDomainFSDriver)
 VIR_ENUM_IMPL(qemuDomainFSDriver, VIR_DOMAIN_FS_DRIVER_TYPE_LAST,
               "local",
               "local",
-              "handle");
+              "handle",
+              "proxy");
 
 
 static void
@@ -2080,9 +2082,43 @@ error:
     return NULL;
 }
 
+/*
+ * Invokes the Proxy Helper with one of the socketpair as its parameter
+ *
+ */
+static int qemuInvokeProxyHelper(const char *emulator, int sock, const char *path)
+{
+#define HELPER "virtfs-proxy-helper"
+    int ret_val, status;
+    virCommandPtr cmd;
+    char *helper, *dname;
+
+    dname = dirname(strdup(emulator));
+    if (virAsprintf(&helper, "%s/%s", dname, HELPER) < 0) {
+        VIR_FREE(dname);
+        virReportOOMError();
+        return -1;
+    }
+
+    cmd = virCommandNewArgList(helper, NULL);
+    virCommandAddArg(cmd, "-f");
+    virCommandAddArgFormat(cmd, "%d", sock);
+    virCommandAddArg(cmd, "-p");
+    virCommandAddArgFormat(cmd, "%s", path);
+    virCommandTransferFD(cmd, sock);
+    virCommandDaemonize(cmd);
+    ret_val = virCommandRun(cmd, &status);
+    if (ret_val < 0)
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            _("%s can't execute"), helper);
+    virCommandFree(cmd);
+    VIR_FREE(helper);
+    VIR_FREE(dname);
+    return ret_val;
+}
 
 char *qemuBuildFSStr(virDomainFSDefPtr fs,
-                     virBitmapPtr qemuCaps ATTRIBUTE_UNUSED)
+                     virBitmapPtr qemuCaps ATTRIBUTE_UNUSED, int qemuSocket)
 {
     virBuffer opt = VIR_BUFFER_INITIALIZER;
     const char *driver = qemuDomainFSDriverTypeToString(fs->fsdriver);
@@ -2108,6 +2144,10 @@ char *qemuBuildFSStr(virDomainFSDefPtr fs,
         virBufferAddLit(&opt, ",security_model=none");
     }
     virBufferAsprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias);
+
+    if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PROXY)
+        virBufferAsprintf(&opt, ",sock_fd=%d", qemuSocket);
+
     virBufferAsprintf(&opt, ",path=%s", fs->src);
 
     if (fs->readonly) {
@@ -4426,10 +4466,35 @@ qemuBuildCommandLine(virConnectPtr conn,
     if (qemuCapsGet(qemuCaps, QEMU_CAPS_FSDEV)) {
         for (i = 0 ; i < def->nfss ; i++) {
             char *optstr;
+            int sockets[2] = {-1, -1};
             virDomainFSDefPtr fs = def->fss[i];
 
+            /*
+             * If its a proxy FS, we need to create a socket pair
+             * and invoke proxy_helper
+             */
+            if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PROXY) {
+                if (qemuCapsGet(qemuCaps, QEMU_CAPS_FSDEV_PROXY) < 0) {
+                    qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                        _("proxy helper not supported"));
+                    goto error;
+                }
+                /* create a socket pair */
+                if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
+                    qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                        _("socketpair failed"));
+                    goto error;
+                }
+                virCommandTransferFD(cmd, sockets[1]);
+                if (qemuInvokeProxyHelper(def->emulator, sockets[0],
+                                    fs->src) < 0) {
+                    VIR_FORCE_CLOSE(sockets[0]);
+                    VIR_FORCE_CLOSE(sockets[1]);
+                    goto error;
+                }
+            }
             virCommandAddArg(cmd, "-fsdev");
-            if (!(optstr = qemuBuildFSStr(fs, qemuCaps)))
+            if (!(optstr = qemuBuildFSStr(fs, qemuCaps, sockets[1])))
                 goto error;
             virCommandAddArg(cmd, optstr);
             VIR_FREE(optstr);
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index de61cf3..8e4f335 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -87,7 +87,8 @@ char *qemuBuildDriveStr(virConnectPtr conn,
                         bool bootable,
                         virBitmapPtr qemuCaps);
 char *qemuBuildFSStr(virDomainFSDefPtr fs,
-                     virBitmapPtr qemuCaps);
+                     virBitmapPtr qemuCaps,
+                     int qemuSocket);
 
 /* Current, best practice */
 char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk,
diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c
index 60155e7..455a7e1 100644
--- a/tests/qemuhelptest.c
+++ b/tests/qemuhelptest.c
@@ -648,7 +648,8 @@ mymain(void)
             QEMU_CAPS_PCI_ROMBAR,
             QEMU_CAPS_ICH9_AHCI,
             QEMU_CAPS_NO_ACPI,
-            QEMU_CAPS_FSDEV_READONLY);
+            QEMU_CAPS_FSDEV_READONLY,
+            QEMU_CAPS_FSDEV_PROXY);
 
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
-- 
1.7.6




More information about the libvir-list mailing list