8571d06737
- Build KVM (basic build, no tools yet) - Set ppc in ExcludeArch. This is temporary, just to fix one issue at a time. ppc users (IBM ? ;-)) please wait a little bit.
782 lines
23 KiB
Diff
782 lines
23 KiB
Diff
This patch introduces a generic internal API for access control lists
|
|
to be used by network servers in QEMU. It adds support for checking
|
|
these ACL in the VNC server, in two places. The first ACL is for the
|
|
SASL authentication mechanism, checking the SASL username. This ACL
|
|
is called 'vnc.username'. The second is for the TLS authentication
|
|
mechanism, when x509 client certificates are turned on, checking against
|
|
the Distinguished Name of the client. This ACL is called 'vnc.x509dname'
|
|
|
|
The internal API provides for an ACL with the following characteristics
|
|
|
|
- A unique name, eg vnc.username, and vnc.x509dname.
|
|
- A default policy, allow or deny
|
|
- An ordered series of match rules, with allow or deny policy
|
|
|
|
If none of the match rules apply, then the default policy is
|
|
used.
|
|
|
|
There is a monitor API to manipulate the ACLs, which I'll describe via
|
|
examples
|
|
|
|
(qemu) acl show vnc.username
|
|
policy: allow
|
|
(qemu) acl policy vnc.username denya
|
|
acl: policy set to 'deny'
|
|
(qemu) acl allow vnc.username fred
|
|
acl: added rule at position 1
|
|
(qemu) acl allow vnc.username bob
|
|
acl: added rule at position 2
|
|
(qemu) acl allow vnc.username joe 1
|
|
acl: added rule at position 1
|
|
(qemu) acl show vnc.username
|
|
policy: deny
|
|
0: allow fred
|
|
1: allow joe
|
|
2: allow bob
|
|
|
|
|
|
(qemu) acl show vnc.x509dname
|
|
policy: allow
|
|
(qemu) acl policy vnc.x509dname deny
|
|
acl: policy set to 'deny'
|
|
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=London,CN=*
|
|
acl: added rule at position 1
|
|
(qemu) acl allow vnc.x509dname C=GB,O=ACME,L=Boston,CN=bob
|
|
acl: added rule at position 2
|
|
(qemu) acl show vnc.x509dname
|
|
policy: deny
|
|
0: allow C=GB,O=ACME,L=London,CN=*
|
|
1: allow C=GB,O=ACME,L=Boston,CN=bob
|
|
|
|
By default the VNC server will not use any ACLs, allowing access to
|
|
the server if the user successfully authenticates. To enable use of
|
|
ACLs to restrict user access, the ',acl' flag should be given when
|
|
starting QEMU. The initial ACL activated will be a 'deny all' policy
|
|
and should be customized using monitor commands.
|
|
|
|
eg enable SASL auth and ACLs
|
|
|
|
qemu .... -vnc localhost:1,sasl,acl
|
|
|
|
The next patch will provide a way to load a pre-defined ACL when
|
|
starting up
|
|
|
|
|
|
Makefile | 6 +
|
|
b/acl.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
b/acl.h | 74 ++++++++++++++++++++++
|
|
configure | 18 +++++
|
|
monitor.c | 95 ++++++++++++++++++++++++++++
|
|
qemu-doc.texi | 49 ++++++++++++++
|
|
vnc-auth-sasl.c | 16 +++-
|
|
vnc-auth-sasl.h | 7 ++
|
|
vnc-tls.c | 19 +++++
|
|
vnc-tls.h | 3
|
|
vnc.c | 21 ++++++
|
|
vnc.h | 3
|
|
12 files changed, 491 insertions(+), 5 deletions(-)
|
|
|
|
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
Index: kvm-84.git-snapshot-20090303/qemu/Makefile
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/Makefile
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/acl.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/acl.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/configure
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/configure
|
|
+++ kvm-84.git-snapshot-20090303/qemu/configure
|
|
@@ -897,6 +897,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
|
|
@@ -1485,6 +1500,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: kvm-84.git-snapshot-20090303/qemu/monitor.c
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/monitor.c
|
|
+++ kvm-84.git-snapshot-20090303/qemu/monitor.c
|
|
@@ -39,6 +39,7 @@
|
|
#include "qemu-timer.h"
|
|
#include "migration.h"
|
|
#include "kvm.h"
|
|
+#include "acl.h"
|
|
|
|
#include "qemu-kvm.h"
|
|
|
|
@@ -1452,6 +1453,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,
|
|
@@ -1557,6 +1637,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"
|
|
@@ -2927,3 +3013,12 @@ void monitor_readline(const char *prompt
|
|
monitor_hd[i]->focus = old_focus[i];
|
|
}
|
|
}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Local variables:
|
|
+ * c-indent-level: 4
|
|
+ * c-basic-offset: 4
|
|
+ * tab-width: 8
|
|
+ * End:
|
|
+ */
|
|
Index: kvm-84.git-snapshot-20090303/qemu/qemu-doc.texi
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/qemu-doc.texi
|
|
+++ kvm-84.git-snapshot-20090303/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
|
|
@@ -1389,6 +1402,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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.c
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/vnc-auth-sasl.c
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-auth-sasl.h
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/vnc-auth-sasl.h
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-tls.c
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/vnc-tls.c
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc-tls.h
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/vnc-tls.h
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.c
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/vnc.c
|
|
+++ kvm-84.git-snapshot-20090303/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: kvm-84.git-snapshot-20090303/qemu/vnc.h
|
|
===================================================================
|
|
--- kvm-84.git-snapshot-20090303.orig/qemu/vnc.h
|
|
+++ kvm-84.git-snapshot-20090303/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
|