Blob Blame History Raw
Index: qemu-kvm-0.10/qemu/Makefile
===================================================================
--- qemu-kvm-0.10.orig/qemu/Makefile
+++ qemu-kvm-0.10/qemu/Makefile
@@ -148,7 +148,7 @@ endif
 ifdef CONFIG_CURSES
 OBJS+=curses.o
 endif
-OBJS+=vnc.o d3des.o
+OBJS+=vnc.o acl.o d3des.o
 ifdef CONFIG_VNC_TLS
 OBJS+=vnc-tls.o vnc-auth-vencrypt.o
 endif
@@ -178,9 +178,11 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
 
 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
 
+acl.o: acl.h acl.c
+
 vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
 
-vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h
+vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
 
 vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
 
Index: qemu-kvm-0.10/qemu/acl.c
===================================================================
--- /dev/null
+++ qemu-kvm-0.10/qemu/acl.c
@@ -0,0 +1,185 @@
+/*
+ * QEMU access control list management
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "acl.h"
+
+#ifdef HAVE_FNMATCH_H
+#include <fnmatch.h>
+#endif
+
+
+static unsigned int nacls = 0;
+static qemu_acl **acls = NULL;
+
+
+
+qemu_acl *qemu_acl_find(const char *aclname)
+{
+    int i;
+    for (i = 0 ; i < nacls ; i++) {
+	if (strcmp(acls[i]->aclname, aclname) == 0)
+	    return acls[i];
+    }
+
+    return NULL;
+}
+
+qemu_acl *qemu_acl_init(const char *aclname)
+{
+    qemu_acl *acl;
+
+    acl = qemu_acl_find(aclname);
+    if (acl)
+	return acl;
+
+    acl = qemu_malloc(sizeof(*acl));
+    acl->aclname = qemu_strdup(aclname);
+    /* Deny by default, so there is no window of "open
+     * access" between QEMU starting, and the user setting
+     * up ACLs in the monitor */
+    acl->defaultDeny = 1;
+
+    acl->nentries = 0;
+    TAILQ_INIT(&acl->entries);
+
+    acls = qemu_realloc(acls, sizeof(*acls) * (nacls +1));
+    acls[nacls] = acl;
+    nacls++;
+
+    return acl;
+}
+
+int qemu_acl_party_is_allowed(qemu_acl *acl,
+			      const char *party)
+{
+    qemu_acl_entry *entry;
+
+    TAILQ_FOREACH(entry, &acl->entries, next) {
+#ifdef HAVE_FNMATCH_H
+	if (fnmatch(entry->match, party, 0) == 0)
+	    return entry->deny ? 0 : 1;
+#else
+	/* No fnmatch, so fallback to exact string matching
+	 * instead of allowing wildcards */
+	if (strcmp(entry->match, party) == 0)
+	    return entry->deny ? 0 : 1;
+#endif
+    }
+
+    return acl->defaultDeny ? 0 : 1;
+}
+
+
+void qemu_acl_reset(qemu_acl *acl)
+{
+    qemu_acl_entry *entry;
+
+    /* Put back to deny by default, so there is no window
+     * of "open access" while the user re-initializes the
+     * access control list */
+    acl->defaultDeny = 1;
+    TAILQ_FOREACH(entry, &acl->entries, next) {
+	TAILQ_REMOVE(&acl->entries, entry, next);
+	free(entry->match);
+	free(entry);
+    }
+    acl->nentries = 0;
+}
+
+
+int qemu_acl_append(qemu_acl *acl,
+		    int deny,
+		    const char *match)
+{
+    qemu_acl_entry *entry;
+
+    entry = qemu_malloc(sizeof(*entry));
+    entry->match = qemu_strdup(match);
+    entry->deny = deny;
+
+    TAILQ_INSERT_TAIL(&acl->entries, entry, next);
+    acl->nentries++;
+
+    return acl->nentries;
+}
+
+
+int qemu_acl_insert(qemu_acl *acl,
+		    int deny,
+		    const char *match,
+		    int index)
+{
+    qemu_acl_entry *entry;
+    qemu_acl_entry *tmp;
+    int i = 0;
+
+    if (index <= 0)
+	return -1;
+    if (index >= acl->nentries)
+	return qemu_acl_append(acl, deny, match);
+
+
+    entry = qemu_malloc(sizeof(*entry));
+    entry->match = qemu_strdup(match);
+    entry->deny = deny;
+
+    TAILQ_FOREACH(tmp, &acl->entries, next) {
+	i++;
+	if (i == index) {
+	    TAILQ_INSERT_BEFORE(tmp, entry, next);
+	    acl->nentries++;
+	    break;
+	}
+    }
+
+    return i;
+}
+
+int qemu_acl_remove(qemu_acl *acl,
+		    const char *match)
+{
+    qemu_acl_entry *entry;
+    int i = 0;
+
+    TAILQ_FOREACH(entry, &acl->entries, next) {
+	i++;
+	if (strcmp(entry->match, match) == 0) {
+	    TAILQ_REMOVE(&acl->entries, entry, next);
+	    return i;
+	}
+    }
+    return -1;
+}
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ * End:
+ */
Index: qemu-kvm-0.10/qemu/acl.h
===================================================================
--- /dev/null
+++ qemu-kvm-0.10/qemu/acl.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU access control list management
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __QEMU_ACL_H__
+#define __QEMU_ACL_H__
+
+#include "sys-queue.h"
+
+typedef struct qemu_acl_entry qemu_acl_entry;
+typedef struct qemu_acl qemu_acl;
+
+struct qemu_acl_entry {
+    char *match;
+    int deny;
+
+    TAILQ_ENTRY(qemu_acl_entry) next;
+};
+
+struct qemu_acl {
+    char *aclname;
+    unsigned int nentries;
+    TAILQ_HEAD(,qemu_acl_entry) entries;
+    int defaultDeny;
+};
+
+qemu_acl *qemu_acl_init(const char *aclname);
+
+qemu_acl *qemu_acl_find(const char *aclname);
+
+int qemu_acl_party_is_allowed(qemu_acl *acl,
+			      const char *party);
+
+void qemu_acl_reset(qemu_acl *acl);
+
+int qemu_acl_append(qemu_acl *acl,
+		    int deny,
+		    const char *match);
+int qemu_acl_insert(qemu_acl *acl,
+		    int deny,
+		    const char *match,
+		    int index);
+int qemu_acl_remove(qemu_acl *acl,
+		    const char *match);
+
+#endif /* __QEMU_ACL_H__ */
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ * End:
+ */
Index: qemu-kvm-0.10/qemu/configure
===================================================================
--- qemu-kvm-0.10.orig/qemu/configure
+++ qemu-kvm-0.10/qemu/configure
@@ -913,6 +913,21 @@ EOF
 fi
 
 ##########################################
+# fnmatch() probe, used for ACL routines
+fnmatch="no"
+cat > $TMPC << EOF
+#include <fnmatch.h>
+int main(void)
+{
+    fnmatch("foo", "foo", 0);
+    return 0;
+}
+EOF
+if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2> /dev/null ; then
+   fnmatch="yes"
+fi
+
+##########################################
 # vde libraries probe
 if test "$vde" = "yes" ; then
   cat > $TMPC << EOF
@@ -1501,6 +1516,9 @@ if test "$vnc_sasl" = "yes" ; then
   echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
   echo "#define CONFIG_VNC_SASL 1" >> $config_h
 fi
+if test "$fnmatch" = "yes" ; then
+  echo "#define HAVE_FNMATCH_H 1" >> $config_h
+fi
 qemu_version=`head $source_path/VERSION`
 echo "VERSION=$qemu_version" >>$config_mak
 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
Index: qemu-kvm-0.10/qemu/monitor.c
===================================================================
--- qemu-kvm-0.10.orig/qemu/monitor.c
+++ qemu-kvm-0.10/qemu/monitor.c
@@ -39,6 +39,7 @@
 #include "qemu-timer.h"
 #include "migration.h"
 #include "kvm.h"
+#include "acl.h"
 
 #include "qemu-kvm.h"
 
@@ -1498,6 +1499,85 @@ static void do_info_balloon(void)
         term_printf("balloon: actual=%d\n", (int)(actual >> 20));
 }
 
+static void do_acl(const char *command,
+		   const char *aclname,
+		   const char *match,
+		   int has_index,
+		   int index)
+{
+    qemu_acl *acl;
+
+    acl = qemu_acl_find(aclname);
+    if (!acl) {
+	term_printf("acl: unknown list '%s'\n", aclname);
+	return;
+    }
+
+    if (strcmp(command, "show") == 0) {
+	int i = 0;
+	qemu_acl_entry *entry;
+	term_printf("policy: %s\n",
+		    acl->defaultDeny ? "deny" : "allow");
+	TAILQ_FOREACH(entry, &acl->entries, next) {
+	    i++;
+	    term_printf("%d: %s %s\n", i,
+			entry->deny ? "deny" : "allow",
+			entry->match);
+	}
+    } else if (strcmp(command, "reset") == 0) {
+	qemu_acl_reset(acl);
+	term_printf("acl: removed all rules\n");
+    } else if (strcmp(command, "policy") == 0) {
+	if (!match) {
+	    term_printf("acl: missing policy parameter\n");
+	    return;
+	}
+
+	if (strcmp(match, "allow") == 0) {
+	    acl->defaultDeny = 0;
+	    term_printf("acl: policy set to 'allow'\n");
+	} else if (strcmp(match, "deny") == 0) {
+	    acl->defaultDeny = 1;
+	    term_printf("acl: policy set to 'deny'\n");
+	} else {
+	    term_printf("acl: unknown policy '%s', expected 'deny' or 'allow'\n", match);
+	}
+    } else if ((strcmp(command, "allow") == 0) ||
+	       (strcmp(command, "deny") == 0)) {
+	int deny = strcmp(command, "deny") == 0 ? 1 : 0;
+	int ret;
+
+	if (!match) {
+	    term_printf("acl: missing match parameter\n");
+	    return;
+	}
+
+	if (has_index)
+	    ret = qemu_acl_insert(acl, deny, match, index);
+	else
+	    ret = qemu_acl_append(acl, deny, match);
+	if (ret < 0)
+	    term_printf("acl: unable to add acl entry\n");
+	else
+	    term_printf("acl: added rule at position %d\n", ret);
+    } else if (strcmp(command, "remove") == 0) {
+	int ret;
+
+	if (!match) {
+	    term_printf("acl: missing match parameter\n");
+	    return;
+	}
+
+	ret = qemu_acl_remove(acl, match);
+	if (ret < 0)
+	    term_printf("acl: no matching acl entry\n");
+	else
+	    term_printf("acl: removed rule at position %d\n", ret);
+    } else {
+	term_printf("acl: unknown command '%s'\n", command);
+    }
+}
+
 /* Please update qemu-doc.texi when adding or changing commands */
 static const term_cmd_t term_cmds[] = {
     { "help|?", "s?", do_help,
@@ -1603,6 +1683,12 @@ static const term_cmd_t term_cmds[] = {
     { "set_link", "ss", do_set_link,
       "name [up|down]", "change the link status of a network adapter" },
     { "set_link", "ss", do_set_link, "name [up|down]" },
+    { "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
+                                "acl show vnc.username\n"
+                                "acl policy vnc.username deny\n"
+                                "acl allow vnc.username fred\n"
+                                "acl deny vnc.username bob\n"
+                                "acl reset vnc.username\n" },
     { "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" },
 #if defined(TARGET_I386) || defined(TARGET_X86_64)
     { "drive_add", "iss", drive_hot_add, "pcibus pcidevfn [file=file][,if=type][,bus=n]\n"
@@ -1611,6 +1697,7 @@ static const term_cmd_t term_cmds[] = {
                                         "[snapshot=on|off][,cache=on|off]",
                                         "add drive to PCI storage controller" },
 #endif
+
     { NULL, NULL, },
 };
 
@@ -2995,3 +3082,12 @@ int monitor_read_bdrv_key(BlockDriverSta
     }
     return -EPERM;
 }
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ * End:
+ */
Index: qemu-kvm-0.10/qemu/qemu-doc.texi
===================================================================
--- qemu-kvm-0.10.orig/qemu/qemu-doc.texi
+++ qemu-kvm-0.10/qemu/qemu-doc.texi
@@ -639,6 +639,19 @@ ensures a data encryption preventing com
 credentials. See the @ref{vnc_security} section for details on using
 SASL authentication.
 
+@item acl
+
+Turn on access control lists for checking of the x509 client certificate
+and SASL party. For x509 certs, the ACL check is made against the
+certificate's distinguished name. This is something that looks like
+@code{C=GB,O=ACME,L=Boston,CN=bob}. For SASL party, the ACL check is
+made against the username, which depending on the SASL plugin, may
+include a realm component, eg @code{bob} or @code{bob\@EXAMPLE.COM}.
+When the @option{acl} flag is set, the initial access list will be
+empty, with a @code{deny} policy. Thus no one will be allowed to
+use the VNC server until the ACLs have been loaded. This can be
+achieved using the @code{acl} monitor command.
+
 @end table
 
 @end table
@@ -1400,6 +1413,42 @@ Password: ********
 
 @end table
 
+@item acl @var{subcommand} @var{aclname} @var{match} @var{index}
+
+Manage access control lists for network services. There are currently
+two named access control lists, @var{vnc.x509dname} and @var{vnc.username}
+matching on the x509 client certificate distinguished name, and SASL
+username respectively.
+
+@table @option
+@item acl show <aclname>
+list all the match rules in the access control list, and the default
+policy
+@item acl policy <aclname> @code{allow|deny}
+set the default access control list policy, used in the event that
+none of the explicit rules match. The default policy at startup is
+always @code{deny}
+@item acl allow <aclname> <match> [<index>]
+add a match to the access control list, allowing access. The match will
+normally be an exact username or x509 distinguished name, but can
+optionally include wildcard globs. eg @code{*\@EXAMPLE.COM} to allow
+all users in the @code{EXAMPLE.COM} kerberos realm. The match will
+normally be appended to the end of the ACL, but can be inserted
+earlier in the list if the optional @code{index} parameter is supplied.
+@item acl deny <aclname> <match> [<index>]
+add a match to the access control list, denying access. The match will
+normally be an exact username or x509 distinguished name, but can
+optionally include wildcard globs. eg @code{*\@EXAMPLE.COM} to allow
+all users in the @code{EXAMPLE.COM} kerberos realm. The match will
+normally be appended to the end of the ACL, but can be inserted
+earlier in the list if the optional @code{index} parameter is supplied.
+@item acl remove <aclname> <match>
+remove the specified match rule from the access control list.
+@item acl reset <aclname>
+remove all matches from the access control list, and set the default
+policy back to @code{deny}.
+@end table
+
 @item screendump @var{filename}
 Save screen into PPM image @var{filename}.
 
Index: qemu-kvm-0.10/qemu/vnc-auth-sasl.c
===================================================================
--- qemu-kvm-0.10.orig/qemu/vnc-auth-sasl.c
+++ qemu-kvm-0.10/qemu/vnc-auth-sasl.c
@@ -120,22 +120,32 @@ static int vnc_auth_sasl_check_access(Vn
 {
     const void *val;
     int err;
+    int allow;
 
     err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
     if (err != SASL_OK) {
-	VNC_DEBUG("cannot query SASL username on connection %d (%s)\n",
+	VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
 		  err, sasl_errstring(err, NULL, NULL));
 	return -1;
     }
     if (val == NULL) {
-	VNC_DEBUG("no client username was found\n");
+	VNC_DEBUG("no client username was found, denying access\n");
 	return -1;
     }
     VNC_DEBUG("SASL client username %s\n", (const char *)val);
 
     vs->sasl.username = qemu_strdup((const char*)val);
 
-    return 0;
+    if (vs->vd->sasl.acl == NULL) {
+	VNC_DEBUG("no ACL activated, allowing access\n");
+	return 0;
+    }
+
+    allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
+
+    VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
+	      allow ? "allowed" : "denied");
+    return allow ? 0 : -1;
 }
 
 static int vnc_auth_sasl_check_ssf(VncState *vs)
Index: qemu-kvm-0.10/qemu/vnc-auth-sasl.h
===================================================================
--- qemu-kvm-0.10.orig/qemu/vnc-auth-sasl.h
+++ qemu-kvm-0.10/qemu/vnc-auth-sasl.h
@@ -30,6 +30,9 @@
 #include <sasl/sasl.h>
 
 typedef struct VncStateSASL VncStateSASL;
+typedef struct VncDisplaySASL VncDisplaySASL;
+
+#include "acl.h"
 
 struct VncStateSASL {
     sasl_conn_t *conn;
@@ -56,6 +59,10 @@ struct VncStateSASL {
     char *mechlist;
 };
 
+struct VncDisplaySASL {
+    qemu_acl *acl;
+};
+
 void vnc_sasl_client_cleanup(VncState *vs);
 
 long vnc_client_read_sasl(VncState *vs);
Index: qemu-kvm-0.10/qemu/vnc-tls.c
===================================================================
--- qemu-kvm-0.10.orig/qemu/vnc-tls.c
+++ qemu-kvm-0.10/qemu/vnc-tls.c
@@ -255,6 +255,25 @@ int vnc_tls_validate_certificate(struct 
 			  gnutls_strerror (ret));
 		return -1;
 	    }
+
+	    if (vs->vd->tls.x509verify) {
+		int allow;
+		if (!vs->vd->tls.acl) {
+		    VNC_DEBUG("no ACL activated, allowing access");
+		    gnutls_x509_crt_deinit (cert);
+		    continue;
+		}
+
+		allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
+						  vs->tls.dname);
+
+		VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
+			  vs->tls.dname, allow ? "allowed" : "denied");
+		if (!allow) {
+		    gnutls_x509_crt_deinit (cert);
+		    return -1;
+		}
+	    }
 	}
 
 	gnutls_x509_crt_deinit (cert);
Index: qemu-kvm-0.10/qemu/vnc-tls.h
===================================================================
--- qemu-kvm-0.10.orig/qemu/vnc-tls.h
+++ qemu-kvm-0.10/qemu/vnc-tls.h
@@ -31,6 +31,8 @@
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
 
+#include "acl.h"
+
 enum {
     VNC_WIREMODE_CLEAR,
     VNC_WIREMODE_TLS,
@@ -42,6 +44,7 @@ typedef struct VncStateTLS VncStateTLS;
 /* Server state */
 struct VncDisplayTLS {
     int x509verify; /* Non-zero if server requests & validates client cert */
+    qemu_acl *acl;
 
     /* Paths to x509 certs/keys */
     char *x509cacert;
Index: qemu-kvm-0.10/qemu/vnc.c
===================================================================
--- qemu-kvm-0.10.orig/qemu/vnc.c
+++ qemu-kvm-0.10/qemu/vnc.c
@@ -28,6 +28,7 @@
 #include "sysemu.h"
 #include "qemu_socket.h"
 #include "qemu-timer.h"
+#include "acl.h"
 
 #define VNC_REFRESH_INTERVAL (1000 / 30)
 
@@ -2082,6 +2083,7 @@ int vnc_display_open(DisplayState *ds, c
     int sasl = 0;
     int saslErr;
 #endif
+    int acl = 0;
 
     if (!vnc_display)
         return -1;
@@ -2138,9 +2140,28 @@ int vnc_display_open(DisplayState *ds, c
 		return -1;
 	    }
 #endif
+	} else if (strncmp(options, "acl", 3) == 0) {
+	    acl = 1;
 	}
     }
 
+#ifdef CONFIG_VNC_TLS
+    if (acl && x509 && vs->tls.x509verify) {
+	if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
+	    fprintf(stderr, "Failed to create x509 dname ACL\n");
+	    exit(1);
+	}
+    }
+#endif
+#ifdef CONFIG_VNC_SASL
+    if (acl && sasl) {
+	if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) {
+	    fprintf(stderr, "Failed to create username ACL\n");
+	    exit(1);
+	}
+    }
+#endif
+
     /*
      * Combinations we support here:
      *
Index: qemu-kvm-0.10/qemu/vnc.h
===================================================================
--- qemu-kvm-0.10.orig/qemu/vnc.h
+++ qemu-kvm-0.10/qemu/vnc.h
@@ -98,6 +98,9 @@ struct VncDisplay
     int subauth; /* Used by VeNCrypt */
     VncDisplayTLS tls;
 #endif
+#ifdef CONFIG_VNC_SASL
+    VncDisplaySASL sasl;
+#endif
 };
 
 struct VncState