diff mbox

[V2,03/12] hw/9pfs: File system helper process for qemu 9p proxy FS

Message ID 1321358265-10924-4-git-send-email-mohan@in.ibm.com
State New
Headers show

Commit Message

Mohan Kumar M Nov. 15, 2011, 11:57 a.m. UTC
Provide root privilege access to QEMU 9p proxy filesystem using socket
communication.

Proxy helper is started by root user as:
~ # virtfs-proxy-helper -f|--fd <socket descriptor> -p|--path <path-to-share>

Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
---
 Makefile                    |    3 +
 configure                   |   19 +++
 fsdev/virtfs-proxy-helper.c |  271 +++++++++++++++++++++++++++++++++++++++++++
 hw/9pfs/virtio-9p-proxy.h   |   10 ++
 4 files changed, 303 insertions(+), 0 deletions(-)
 create mode 100644 fsdev/virtfs-proxy-helper.c

Comments

Stefan Hajnoczi Nov. 15, 2011, 2:03 p.m. UTC | #1
On Tue, Nov 15, 2011 at 11:57 AM, M. Mohan Kumar <mohan@in.ibm.com> wrote:
> diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
> new file mode 100644
> index 0000000..69daf7c
> --- /dev/null
> +++ b/fsdev/virtfs-proxy-helper.c
> @@ -0,0 +1,271 @@
> +/*
> + * Helper for QEMU Proxy FS Driver
> + * Copyright IBM, Corp. 2011
> + *
> + * Authors:
> + * M. Mohan Kumar <mohan@in.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +#include <stdio.h>
> +#include <sys/socket.h>
> +#include <string.h>
> +#include <sys/un.h>
> +#include <limits.h>
> +#include <signal.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <sys/resource.h>
> +#include <sys/stat.h>
> +#include <getopt.h>
> +#include <unistd.h>
> +#include <syslog.h>
> +#include <sys/prctl.h>
> +#include <sys/capability.h>
> +#include <sys/fsuid.h>
> +#include <stdarg.h>
> +#include "bswap.h"

Where is "bswap.h" used and why above <sys/socket.h>?

> +#include <sys/socket.h>
> +#include "qemu-common.h"
> +#include "virtio-9p-marshal.h"
> +#include "hw/9pfs/virtio-9p-proxy.h"
> +
> +#define PROGNAME "virtfs-proxy-helper"
> +
> +static struct option helper_opts[] = {
> +    {"fd", required_argument, NULL, 'f'},
> +    {"path", required_argument, NULL, 'p'},
> +    {"nodaemon", no_argument, NULL, 'n'},
> +};
> +
> +int is_daemon;

static?

Also, please use the bool type from <stdbool.h>, it makes it easier
for readers who don't have to guess how the variable works (might be a
bitfield or reference count too).

> +static int socket_read(int sockfd, void *buff, ssize_t size)
> +{
> +    int retval;
> +
> +    do {
> +        retval = read(sockfd, buff, size);
> +    } while (retval < 0 && errno == EINTR);
> +    if (retval != size) {
> +        return -EIO;
> +    }

Shouldn't this loop until size bytes have been read?

> +    return retval;
> +}
> +
> +static int socket_write(int sockfd, void *buff, ssize_t size)
> +{
> +    int retval;
> +
> +    do {
> +        retval = write(sockfd, buff, size);
> +    } while (retval < 0 && errno == EINTR);
> +    if (retval != size) {
> +        return -EIO;

We could pass the actual -errno here if retval < 0.

> +    }
> +    return retval;
> +}
> +
> +static int read_request(int sockfd, struct iovec *iovec)
> +{
> +    int retval;
> +    ProxyHeader header;
> +
> +    /* read the header */
> +    retval = socket_read(sockfd, iovec->iov_base, sizeof(header));
> +    if (retval != sizeof(header)) {
> +        return -EIO;
> +    }
> +    /* unmarshal header */
> +    proxy_unmarshal(iovec, 1, 0, "dd", &header.type, &header.size);
> +    /* read the request */
> +    retval = socket_read(sockfd, iovec->iov_base + sizeof(header), header.size);
> +    if (retval != header.size) {
> +        return -EIO;
> +    }
> +    return header.type;
> +}

Size checks are missing and we're trusting what the client sends!

> +
> +static void usage(char *prog)
> +{
> +    fprintf(stderr, "usage: %s\n"
> +            " -p|--path <path> 9p path to export\n"
> +            " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
> +            " [-n|--nodaemon] Run as a normal program\n",
> +            basename(prog));
> +}
> +
> +static int process_requests(int sock)
> +{
> +    int type;
> +    struct iovec iovec;
> +
> +    iovec.iov_base = g_malloc(BUFF_SZ);
> +    iovec.iov_len = BUFF_SZ;
> +    while (1) {
> +        type = read_request(sock, &iovec);
> +        if (type <= 0) {
> +            goto error;
> +        }
> +    }
> +    (void)socket_write;
> +error:
> +    g_free(iovec.iov_base);
> +    return -1;
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    int sock;
> +    char rpath[PATH_MAX];
> +    struct stat stbuf;
> +    int c, option_index;
> +
> +    is_daemon = 1;
> +    rpath[0] = '\0';
> +    sock = -1;
> +    while (1) {
> +        option_index = 0;
> +        c = getopt_long(argc, argv, "p:nh?f:", helper_opts,
> +                        &option_index);
> +        if (c == -1) {
> +            break;
> +        }
> +        switch (c) {
> +        case 'p':
> +            strcpy(rpath, optarg);

Buffer overflow.  The whole thing would be simpler like this:

const char *rpath = "";
[...]
    case 'p':
        rpath = optarg;
        break;

> +            break;
> +        case 'n':
> +            is_daemon = 0;
> +            break;
> +        case 'f':
> +            sock = atoi(optarg);
> +            break;
> +        case '?':
> +        case 'h':
> +        default:
> +            usage(argv[0]);
> +            return -1;

The convention is for programs to exit with 1 (EXIT_FAILURE) on error.

> +            break;
> +        }
> +    }
> +
> +    /* Parameter validation */
> +    if (sock == -1 || rpath[0] == '\0') {
> +        fprintf(stderr, "socket descriptor or path not specified\n");
> +        usage(argv[0]);
> +        return -1;
> +    }
> +
> +    if (lstat(rpath, &stbuf) < 0) {
> +        fprintf(stderr, "invalid path \"%s\" specified?\n", rpath);

sterror() would provide further details on what went wrong.

> +        return -1;
> +    }
> +
> +    if (!S_ISDIR(stbuf.st_mode)) {
> +        fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
> +        return -1;
> +    }
> +
> +    if (is_daemon) {
> +        if (daemon(0, 0) < 0) {
> +            fprintf(stderr, "daemon call failed\n");
> +            return -1;
> +        }
> +        openlog(PROGNAME, LOG_PID, LOG_DAEMON);
> +    }
> +
> +    do_log(LOG_INFO, "Started");
> +
> +    if (chroot(rpath) < 0) {
> +        do_perror("chroot");
> +        goto error;
> +    }
> +    umask(0);
> +
> +    if (init_capabilities() < 0) {
> +        goto error;
> +    }
> +
> +    process_requests(sock);
> +error:
> +    do_log(LOG_INFO, "Done");
> +    closelog();
> +    return 0;
> +}
> diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h
> index f5e1a02..120e940 100644
> --- a/hw/9pfs/virtio-9p-proxy.h
> +++ b/hw/9pfs/virtio-9p-proxy.h
> @@ -3,6 +3,16 @@
>
>  #define BUFF_SZ (4 * 1024)
>
> +#define proxy_unmarshal(in_sg, in_elem, offset, fmt, args...) \
> +    v9fs_unmarshal(in_sg, in_elem, offset, 0 /* convert */, fmt, ##args)
> +#define proxy_marshal(out_sg, out_elem, offset, fmt, args...) \
> +    v9fs_marshal(out_sg, out_elem, offset, 0 /* convert */, fmt, ##args)
> +
> +union MsgControl {
> +    struct cmsghdr cmsg;
> +    char control[CMSG_SPACE(sizeof(int))];
> +};

This union isn't used in this patch.

Stefan
Mohan Kumar M Nov. 16, 2011, 8:51 a.m. UTC | #2
Stefan Hajnoczi wrote:
> On Tue, Nov 15, 2011 at 11:57 AM, M. Mohan Kumar<mohan@in.ibm.com>  wrote:
>    
>> diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
>> new file mode 100644
>> index 0000000..69daf7c
>> --- /dev/null
>> +++ b/fsdev/virtfs-proxy-helper.c
>> @@ -0,0 +1,271 @@
>> +/*
>> + * Helper for QEMU Proxy FS Driver
>> + * Copyright IBM, Corp. 2011
>> + *
>> + * Authors:
>> + * M. Mohan Kumar<mohan@in.ibm.com>
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2. See
>> + * the COPYING file in the top-level directory.
>> + */
>> +#include<stdio.h>
>> +#include<sys/socket.h>
>> +#include<string.h>
>> +#include<sys/un.h>
>> +#include<limits.h>
>> +#include<signal.h>
>> +#include<errno.h>
>> +#include<stdlib.h>
>> +#include<sys/resource.h>
>> +#include<sys/stat.h>
>> +#include<getopt.h>
>> +#include<unistd.h>
>> +#include<syslog.h>
>> +#include<sys/prctl.h>
>> +#include<sys/capability.h>
>> +#include<sys/fsuid.h>
>> +#include<stdarg.h>
>> +#include "bswap.h"
>>      
> Where is "bswap.h" used and why above<sys/socket.h>?
>    
I will fix it
>    
>> +#include<sys/socket.h>
>> +#include "qemu-common.h"
>> +#include "virtio-9p-marshal.h"
>> +#include "hw/9pfs/virtio-9p-proxy.h"
>> +
>> +#define PROGNAME "virtfs-proxy-helper"
>> +
>> +static struct option helper_opts[] = {
>> +    {"fd", required_argument, NULL, 'f'},
>> +    {"path", required_argument, NULL, 'p'},
>> +    {"nodaemon", no_argument, NULL, 'n'},
>> +};
>> +
>> +int is_daemon;
>>      
> static?
>
> Also, please use the bool type from<stdbool.h>, it makes it easier
> for readers who don't have to guess how the variable works (might be a
> bitfield or reference count too).
>
>    
I will fix it.
>> +static int socket_read(int sockfd, void *buff, ssize_t size)
>> +{
>> +    int retval;
>> +
>> +    do {
>> +        retval = read(sockfd, buff, size);
>> +    } while (retval<  0&&  errno == EINTR);
>> +    if (retval != size) {
>> +        return -EIO;
>> +    }
>>      
> Shouldn't this loop until size bytes have been read?
>
>    
Ok, I will fix this.
>> +    return retval;
>> +}
>> +
>> +static int socket_write(int sockfd, void *buff, ssize_t size)
>> +{
>> +    int retval;
>> +
>> +    do {
>> +        retval = write(sockfd, buff, size);
>> +    } while (retval<  0&&  errno == EINTR);
>> +    if (retval != size) {
>> +        return -EIO;
>>      
> We could pass the actual -errno here if retval<  0.
>
>    
Socket errors are treated fatal and the reason for failures are not used
by the code. When ever there is socket error, helper exits.


>> +    }
>> +    return retval;
>> +}
>> +
>> +static int read_request(int sockfd, struct iovec *iovec)
>> +{
>> +    int retval;
>> +    ProxyHeader header;
>> +
>> +    /* read the header */
>> +    retval = socket_read(sockfd, iovec->iov_base, sizeof(header));
>> +    if (retval != sizeof(header)) {
>> +        return -EIO;
>> +    }
>> +    /* unmarshal header */
>> +    proxy_unmarshal(iovec, 1, 0, "dd",&header.type,&header.size);
>> +    /* read the request */
>> +    retval = socket_read(sockfd, iovec->iov_base + sizeof(header), header.size);
>> +    if (retval != header.size) {
>> +        return -EIO;
>> +    }
>> +    return header.type;
>> +}
>>      
> Size checks are missing and we're trusting what the client sends!
>    
Could you please elaborate?
>    
>> +
>> +static void usage(char *prog)
>> +{
>> +    fprintf(stderr, "usage: %s\n"
>> +            " -p|--path<path>  9p path to export\n"
>> +            " {-f|--fd<socket-descriptor>} socket file descriptor to be used\n"
>> +            " [-n|--nodaemon] Run as a normal program\n",
>> +            basename(prog));
>> +}
>> +
>> +static int process_requests(int sock)
>> +{
>> +    int type;
>> +    struct iovec iovec;
>> +
>> +    iovec.iov_base = g_malloc(BUFF_SZ);
>> +    iovec.iov_len = BUFF_SZ;
>> +    while (1) {
>> +        type = read_request(sock,&iovec);
>> +        if (type<= 0) {
>> +            goto error;
>> +        }
>> +    }
>> +    (void)socket_write;
>> +error:
>> +    g_free(iovec.iov_base);
>> +    return -1;
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    int sock;
>> +    char rpath[PATH_MAX];
>> +    struct stat stbuf;
>> +    int c, option_index;
>> +
>> +    is_daemon = 1;
>> +    rpath[0] = '\0';
>> +    sock = -1;
>> +    while (1) {
>> +        option_index = 0;
>> +        c = getopt_long(argc, argv, "p:nh?f:", helper_opts,
>> +&option_index);
>> +        if (c == -1) {
>> +            break;
>> +        }
>> +        switch (c) {
>> +        case 'p':
>> +            strcpy(rpath, optarg);
>>      
> Buffer overflow.  The whole thing would be simpler like this:
>
> const char *rpath = "";
> [...]
>      case 'p':
>          rpath = optarg;
>          break;
>
>    
I will fix it
>> +            break;
>> +        case 'n':
>> +            is_daemon = 0;
>> +            break;
>> +        case 'f':
>> +            sock = atoi(optarg);
>> +            break;
>> +        case '?':
>> +        case 'h':
>> +        default:
>> +            usage(argv[0]);
>> +            return -1;
>>      
> The convention is for programs to exit with 1 (EXIT_FAILURE) on error.
>
>    
I will fix it.
>> +            break;
>> +        }
>> +    }
>> +
>> +    /* Parameter validation */
>> +    if (sock == -1 || rpath[0] == '\0') {
>> +        fprintf(stderr, "socket descriptor or path not specified\n");
>> +        usage(argv[0]);
>> +        return -1;
>> +    }
>> +
>> +    if (lstat(rpath,&stbuf)<  0) {
>> +        fprintf(stderr, "invalid path \"%s\" specified?\n", rpath);
>>      
> sterror() would provide further details on what went wrong.
>    
Ok
>    
>> +        return -1;
>> +    }
>> +
>> +    if (!S_ISDIR(stbuf.st_mode)) {
>> +        fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
>> +        return -1;
>> +    }
>> +
>> +    if (is_daemon) {
>> +        if (daemon(0, 0)<  0) {
>> +            fprintf(stderr, "daemon call failed\n");
>> +            return -1;
>> +        }
>> +        openlog(PROGNAME, LOG_PID, LOG_DAEMON);
>> +    }
>> +
>> +    do_log(LOG_INFO, "Started");
>> +
>> +    if (chroot(rpath)<  0) {
>> +        do_perror("chroot");
>> +        goto error;
>> +    }
>> +    umask(0);
>> +
>> +    if (init_capabilities()<  0) {
>> +        goto error;
>> +    }
>> +
>> +    process_requests(sock);
>> +error:
>> +    do_log(LOG_INFO, "Done");
>> +    closelog();
>> +    return 0;
>> +}
>> diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h
>> index f5e1a02..120e940 100644
>> --- a/hw/9pfs/virtio-9p-proxy.h
>> +++ b/hw/9pfs/virtio-9p-proxy.h
>> @@ -3,6 +3,16 @@
>>
>>   #define BUFF_SZ (4 * 1024)
>>
>> +#define proxy_unmarshal(in_sg, in_elem, offset, fmt, args...) \
>> +    v9fs_unmarshal(in_sg, in_elem, offset, 0 /* convert */, fmt, ##args)
>> +#define proxy_marshal(out_sg, out_elem, offset, fmt, args...) \
>> +    v9fs_marshal(out_sg, out_elem, offset, 0 /* convert */, fmt, ##args)
>> +
>> +union MsgControl {
>> +    struct cmsghdr cmsg;
>> +    char control[CMSG_SPACE(sizeof(int))];
>> +};
>>      
> This union isn't used in this patch.
>
>    
I will fix it.
Stefan Hajnoczi Nov. 16, 2011, 10:23 a.m. UTC | #3
On Wed, Nov 16, 2011 at 8:51 AM, M. Mohan Kumar <mohan@in.ibm.com> wrote:
> Stefan Hajnoczi wrote:
>>> +static int socket_write(int sockfd, void *buff, ssize_t size)
>>> +{
>>> +    int retval;
>>> +
>>> +    do {
>>> +        retval = write(sockfd, buff, size);
>>> +    } while (retval<  0&&  errno == EINTR);
>>> +    if (retval != size) {
>>> +        return -EIO;
>>>
>>
>> We could pass the actual -errno here if retval<  0.
>>
>>
>
> Socket errors are treated fatal and the reason for failures are not used
> by the code. When ever there is socket error, helper exits.

Exiting may be fine but errno or strerror(errno) needs to be printed
on exit.  Otherwise we need to strace it to figure out what happened -
and the error exit may be hard to reproduce, so this is not
attractive.  Therefore it makes sense to return the full -errno and
print it out when exiting.

>>> +static int read_request(int sockfd, struct iovec *iovec)
>>> +{
>>> +    int retval;
>>> +    ProxyHeader header;
>>> +
>>> +    /* read the header */
>>> +    retval = socket_read(sockfd, iovec->iov_base, sizeof(header));
>>> +    if (retval != sizeof(header)) {
>>> +        return -EIO;
>>> +    }
>>> +    /* unmarshal header */
>>> +    proxy_unmarshal(iovec, 1, 0, "dd",&header.type,&header.size);
>>> +    /* read the request */
>>> +    retval = socket_read(sockfd, iovec->iov_base + sizeof(header),
>>> header.size);
>>> +    if (retval != header.size) {
>>> +        return -EIO;
>>> +    }
>>> +    return header.type;
>>> +}
>>>
>>
>> Size checks are missing and we're trusting what the client sends!
>>
>
> Could you please elaborate?

This function allows the client to overflow the iovec buffer.  Check
what happens when header.size > 4096.

I'm alarmed by this because there are 9 more patches to review and
this is just the foundation of the helper.  If the helper isn't secure
then isolating it from QEMU is useless since a malicious QEMU can take
over the helper.  Input needs to be validated.

Stefan
diff mbox

Patch

diff --git a/Makefile b/Makefile
index ba8d738..19b481a 100644
--- a/Makefile
+++ b/Makefile
@@ -153,6 +153,9 @@  qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
 qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
 qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)
 
+fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o
+fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
+
 qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $@")
 
diff --git a/configure b/configure
index a6cf6d6..a2b55e8 100755
--- a/configure
+++ b/configure
@@ -1873,6 +1873,22 @@  else
 fi
 
 ##########################################
+# libcap probe
+
+if test "$cap" != "no" ; then
+  cat > $TMPC <<EOF
+#include <stdio.h>
+#include <sys/capability.h>
+int main(void) { cap_t caps; caps = cap_init(); }
+EOF
+  if compile_prog "" "-lcap" ; then
+    cap=yes
+  else
+    cap=no
+  fi
+fi
+
+##########################################
 # pthread probe
 PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2"
 
@@ -2662,6 +2678,9 @@  confdir=$sysconfdir$confsuffix
 tools=
 if test "$softmmu" = yes ; then
   tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
+  if [ "$cap" = "yes" -a "$linux" = "yes" ] ; then
+      tools="$tools fsdev/virtfs-proxy-helper\$(EXESUF)"
+  fi
   if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
       tools="qemu-nbd\$(EXESUF) $tools"
     if [ "$guest_agent" = "yes" ]; then
diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
new file mode 100644
index 0000000..69daf7c
--- /dev/null
+++ b/fsdev/virtfs-proxy-helper.c
@@ -0,0 +1,271 @@ 
+/*
+ * Helper for QEMU Proxy FS Driver
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * M. Mohan Kumar <mohan@in.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+#include <stdio.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <sys/un.h>
+#include <limits.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <sys/fsuid.h>
+#include <stdarg.h>
+#include "bswap.h"
+#include <sys/socket.h>
+#include "qemu-common.h"
+#include "virtio-9p-marshal.h"
+#include "hw/9pfs/virtio-9p-proxy.h"
+
+#define PROGNAME "virtfs-proxy-helper"
+
+static struct option helper_opts[] = {
+    {"fd", required_argument, NULL, 'f'},
+    {"path", required_argument, NULL, 'p'},
+    {"nodaemon", no_argument, NULL, 'n'},
+};
+
+int is_daemon;
+
+static void do_perror(const char *string)
+{
+    if (is_daemon) {
+        syslog(LOG_CRIT, "%s:%s", string, strerror(errno));
+    } else {
+        fprintf(stderr, "%s:%s\n", string, strerror(errno));
+    }
+}
+
+static void do_log(int level, const char *string)
+{
+    if (is_daemon) {
+        syslog(level, "%s", string);
+    } else {
+        fprintf(stderr, "%s\n", string);
+    }
+}
+
+static int cap_set(void)
+{
+    int retval;
+    cap_t caps;
+    cap_value_t cap_list[10];
+
+    /* helper needs following capbabilities only */
+    cap_list[0] = CAP_CHOWN;
+    cap_list[1] = CAP_DAC_OVERRIDE;
+    cap_list[2] = CAP_DAC_READ_SEARCH;
+    cap_list[3] = CAP_FOWNER;
+    cap_list[4] = CAP_FSETID;
+    cap_list[5] = CAP_SETGID;
+    cap_list[6] = CAP_MKNOD;
+    cap_list[7] = CAP_SETUID;
+
+    caps = cap_init();
+    if (caps == NULL) {
+        do_perror("cap_init");
+        return -1;
+    }
+    retval = cap_set_flag(caps, CAP_PERMITTED, 8, cap_list, CAP_SET);
+    if (retval < 0) {
+        do_perror("cap_set_flag");
+        goto error;
+    }
+    retval = cap_set_proc(caps);
+    if (retval < 0) {
+        do_perror("cap_set_proc");
+    }
+    retval = cap_set_flag(caps, CAP_EFFECTIVE, 8, cap_list, CAP_SET);
+    if (retval < 0) {
+        do_perror("cap_set_flag");
+        goto error;
+    }
+    retval = cap_set_proc(caps);
+    if (retval < 0) {
+        do_perror("cap_set_proc");
+    }
+
+error:
+    cap_free(caps);
+    return retval;
+}
+
+static int init_capabilities(void)
+{
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+        do_perror("prctl");
+        return -1;
+    }
+    if (cap_set() < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int socket_read(int sockfd, void *buff, ssize_t size)
+{
+    int retval;
+
+    do {
+        retval = read(sockfd, buff, size);
+    } while (retval < 0 && errno == EINTR);
+    if (retval != size) {
+        return -EIO;
+    }
+    return retval;
+}
+
+static int socket_write(int sockfd, void *buff, ssize_t size)
+{
+    int retval;
+
+    do {
+        retval = write(sockfd, buff, size);
+    } while (retval < 0 && errno == EINTR);
+    if (retval != size) {
+        return -EIO;
+    }
+    return retval;
+}
+
+static int read_request(int sockfd, struct iovec *iovec)
+{
+    int retval;
+    ProxyHeader header;
+
+    /* read the header */
+    retval = socket_read(sockfd, iovec->iov_base, sizeof(header));
+    if (retval != sizeof(header)) {
+        return -EIO;
+    }
+    /* unmarshal header */
+    proxy_unmarshal(iovec, 1, 0, "dd", &header.type, &header.size);
+    /* read the request */
+    retval = socket_read(sockfd, iovec->iov_base + sizeof(header), header.size);
+    if (retval != header.size) {
+        return -EIO;
+    }
+    return header.type;
+}
+
+static void usage(char *prog)
+{
+    fprintf(stderr, "usage: %s\n"
+            " -p|--path <path> 9p path to export\n"
+            " {-f|--fd <socket-descriptor>} socket file descriptor to be used\n"
+            " [-n|--nodaemon] Run as a normal program\n",
+            basename(prog));
+}
+
+static int process_requests(int sock)
+{
+    int type;
+    struct iovec iovec;
+
+    iovec.iov_base = g_malloc(BUFF_SZ);
+    iovec.iov_len = BUFF_SZ;
+    while (1) {
+        type = read_request(sock, &iovec);
+        if (type <= 0) {
+            goto error;
+        }
+    }
+    (void)socket_write;
+error:
+    g_free(iovec.iov_base);
+    return -1;
+}
+
+int main(int argc, char **argv)
+{
+    int sock;
+    char rpath[PATH_MAX];
+    struct stat stbuf;
+    int c, option_index;
+
+    is_daemon = 1;
+    rpath[0] = '\0';
+    sock = -1;
+    while (1) {
+        option_index = 0;
+        c = getopt_long(argc, argv, "p:nh?f:", helper_opts,
+                        &option_index);
+        if (c == -1) {
+            break;
+        }
+        switch (c) {
+        case 'p':
+            strcpy(rpath, optarg);
+            break;
+        case 'n':
+            is_daemon = 0;
+            break;
+        case 'f':
+            sock = atoi(optarg);
+            break;
+        case '?':
+        case 'h':
+        default:
+            usage(argv[0]);
+            return -1;
+            break;
+        }
+    }
+
+    /* Parameter validation */
+    if (sock == -1 || rpath[0] == '\0') {
+        fprintf(stderr, "socket descriptor or path not specified\n");
+        usage(argv[0]);
+        return -1;
+    }
+
+    if (lstat(rpath, &stbuf) < 0) {
+        fprintf(stderr, "invalid path \"%s\" specified?\n", rpath);
+        return -1;
+    }
+
+    if (!S_ISDIR(stbuf.st_mode)) {
+        fprintf(stderr, "specified path \"%s\" is not directory\n", rpath);
+        return -1;
+    }
+
+    if (is_daemon) {
+        if (daemon(0, 0) < 0) {
+            fprintf(stderr, "daemon call failed\n");
+            return -1;
+        }
+        openlog(PROGNAME, LOG_PID, LOG_DAEMON);
+    }
+
+    do_log(LOG_INFO, "Started");
+
+    if (chroot(rpath) < 0) {
+        do_perror("chroot");
+        goto error;
+    }
+    umask(0);
+
+    if (init_capabilities() < 0) {
+        goto error;
+    }
+
+    process_requests(sock);
+error:
+    do_log(LOG_INFO, "Done");
+    closelog();
+    return 0;
+}
diff --git a/hw/9pfs/virtio-9p-proxy.h b/hw/9pfs/virtio-9p-proxy.h
index f5e1a02..120e940 100644
--- a/hw/9pfs/virtio-9p-proxy.h
+++ b/hw/9pfs/virtio-9p-proxy.h
@@ -3,6 +3,16 @@ 
 
 #define BUFF_SZ (4 * 1024)
 
+#define proxy_unmarshal(in_sg, in_elem, offset, fmt, args...) \
+    v9fs_unmarshal(in_sg, in_elem, offset, 0 /* convert */, fmt, ##args)
+#define proxy_marshal(out_sg, out_elem, offset, fmt, args...) \
+    v9fs_marshal(out_sg, out_elem, offset, 0 /* convert */, fmt, ##args)
+
+union MsgControl {
+    struct cmsghdr cmsg;
+    char control[CMSG_SPACE(sizeof(int))];
+};
+
 typedef struct {
     int type;
     int size;