From: "M. Mohan Kumar" <mohan@xxxxxxxxxx> 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@xxxxxxxxxx> --- Changes from V2 * Rebased on top of current libvirt git level Changes from previous version * Remove the xml node for specifying the virtfs-proxy-helper path, now it is determined from qemu binary path. docs/formatdomain.html.in | 2 + src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 2 +- src/qemu/qemu_capabilities.c | 5 ++- src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 71 ++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_command.h | 3 +- tests/qemuhelptest.c | 6 ++- 8 files changed, 83 insertions(+), 10 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f97c630..7242455 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1619,6 +1619,8 @@ while the value <code>immediate</code> means that a host writeback is immediately triggered for all pages touched during a guest file write operation <span class="since">(since 0.9.10)</span>. + or <code>type='proxy'</code> <span class="since">(since + 1.0)</span>. </dd> <dt><code>type='template'</code></dt> <dd> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 58603a3..70b145d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -273,7 +273,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 f4c43c6..6ea62a3 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -675,6 +675,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 }; @@ -713,7 +714,6 @@ struct _virDomainFSDef { unsigned long long space_soft_limit; /* in bytes */ }; - /* 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 85c49a2..d582549 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -144,7 +144,6 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "ich9-ahci", "no-acpi", "fsdev-readonly", - "virtio-blk-pci.scsi", /* 80 */ "blk-sg-io", "drive-copy-on-read", @@ -168,7 +167,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "nec-usb-xhci", "virtio-s390", "balloon-event", - + "fsdev-proxy", /* 100 */ ); struct qemu_feature_flags { @@ -1129,6 +1128,8 @@ qemuCapsComputeCmdFlags(const char *help, qemuCapsSet(flags, QEMU_CAPS_FSDEV_READONLY); if (strstr(fsdev, "writeout")) qemuCapsSet(flags, QEMU_CAPS_FSDEV_WRITEOUT); + 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 e8251dc..e48d623 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -135,6 +135,7 @@ enum qemuCapsFlags { QEMU_CAPS_NEC_USB_XHCI = 97, /* -device nec-usb-xhci */ QEMU_CAPS_VIRTIO_S390 = 98, /* -device virtio-*-s390 */ QEMU_CAPS_BALLOON_EVENT = 99, /* Async event for balloon changes */ + QEMU_CAPS_FSDEV_PROXY = 100, /* -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 6ad65a6..af689f8 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 @@ -114,7 +115,8 @@ VIR_ENUM_DECL(qemuDomainFSDriver) VIR_ENUM_IMPL(qemuDomainFSDriver, VIR_DOMAIN_FS_DRIVER_TYPE_LAST, "local", "local", - "handle"); + "handle", + "proxy"); static void @@ -2575,9 +2577,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) + virReportError(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); @@ -2626,6 +2662,10 @@ char *qemuBuildFSStr(virDomainFSDefPtr fs, } 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) { @@ -5081,10 +5121,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) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("proxy helper not supported")); + goto error; + } + /* create a socket pair */ + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) < 0) { + virReportError(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 3ccf4d7..5de7751 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(virDomainDefPtr def, diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 012ba26..6485c5f 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -677,7 +677,8 @@ mymain(void) QEMU_CAPS_FSDEV_WRITEOUT, QEMU_CAPS_SCSI_BLOCK, QEMU_CAPS_SCSI_CD, - QEMU_CAPS_IDE_CD); + QEMU_CAPS_IDE_CD, + QEMU_CAPS_FSDEV_PROXY); DO_TEST("qemu-1.1.0", 1001000, 0, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -754,7 +755,8 @@ mymain(void) QEMU_CAPS_IDE_CD, QEMU_CAPS_NO_USER_CONFIG, QEMU_CAPS_HDA_MICRO, - QEMU_CAPS_NEC_USB_XHCI); + QEMU_CAPS_NEC_USB_XHCI, + QEMU_CAPS_FSDEV_PROXY); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.7.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list