Commit 76655d6dece88bd00e190956e8e4285b682edcbb

Authored by aliguori
1 parent 1263b7d6

Support ACLs for controlling VNC access ("Daniel P. Berrange")

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>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6726 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
@@ -144,7 +144,7 @@ endif @@ -144,7 +144,7 @@ endif
144 ifdef CONFIG_CURSES 144 ifdef CONFIG_CURSES
145 OBJS+=curses.o 145 OBJS+=curses.o
146 endif 146 endif
147 -OBJS+=vnc.o d3des.o 147 +OBJS+=vnc.o acl.o d3des.o
148 ifdef CONFIG_VNC_TLS 148 ifdef CONFIG_VNC_TLS
149 OBJS+=vnc-tls.o vnc-auth-vencrypt.o 149 OBJS+=vnc-tls.o vnc-auth-vencrypt.o
150 endif 150 endif
@@ -174,9 +174,11 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h @@ -174,9 +174,11 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
174 174
175 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS) 175 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
176 176
  177 +acl.o: acl.h acl.c
  178 +
177 vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h 179 vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h
178 180
179 -vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h 181 +vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h
180 182
181 vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS) 183 vnc.o: CFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
182 184
acl.c 0 → 100644
  1 +/*
  2 + * QEMU access control list management
  3 + *
  4 + * Copyright (C) 2009 Red Hat, Inc
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +
  26 +#include "qemu-common.h"
  27 +#include "sysemu.h"
  28 +#include "acl.h"
  29 +
  30 +#ifdef HAVE_FNMATCH_H
  31 +#include <fnmatch.h>
  32 +#endif
  33 +
  34 +
  35 +static unsigned int nacls = 0;
  36 +static qemu_acl **acls = NULL;
  37 +
  38 +
  39 +
  40 +qemu_acl *qemu_acl_find(const char *aclname)
  41 +{
  42 + int i;
  43 + for (i = 0 ; i < nacls ; i++) {
  44 + if (strcmp(acls[i]->aclname, aclname) == 0)
  45 + return acls[i];
  46 + }
  47 +
  48 + return NULL;
  49 +}
  50 +
  51 +qemu_acl *qemu_acl_init(const char *aclname)
  52 +{
  53 + qemu_acl *acl;
  54 +
  55 + acl = qemu_acl_find(aclname);
  56 + if (acl)
  57 + return acl;
  58 +
  59 + acl = qemu_malloc(sizeof(*acl));
  60 + acl->aclname = qemu_strdup(aclname);
  61 + /* Deny by default, so there is no window of "open
  62 + * access" between QEMU starting, and the user setting
  63 + * up ACLs in the monitor */
  64 + acl->defaultDeny = 1;
  65 +
  66 + acl->nentries = 0;
  67 + TAILQ_INIT(&acl->entries);
  68 +
  69 + acls = qemu_realloc(acls, sizeof(*acls) * (nacls +1));
  70 + acls[nacls] = acl;
  71 + nacls++;
  72 +
  73 + return acl;
  74 +}
  75 +
  76 +int qemu_acl_party_is_allowed(qemu_acl *acl,
  77 + const char *party)
  78 +{
  79 + qemu_acl_entry *entry;
  80 +
  81 + TAILQ_FOREACH(entry, &acl->entries, next) {
  82 +#ifdef HAVE_FNMATCH_H
  83 + if (fnmatch(entry->match, party, 0) == 0)
  84 + return entry->deny ? 0 : 1;
  85 +#else
  86 + /* No fnmatch, so fallback to exact string matching
  87 + * instead of allowing wildcards */
  88 + if (strcmp(entry->match, party) == 0)
  89 + return entry->deny ? 0 : 1;
  90 +#endif
  91 + }
  92 +
  93 + return acl->defaultDeny ? 0 : 1;
  94 +}
  95 +
  96 +
  97 +void qemu_acl_reset(qemu_acl *acl)
  98 +{
  99 + qemu_acl_entry *entry;
  100 +
  101 + /* Put back to deny by default, so there is no window
  102 + * of "open access" while the user re-initializes the
  103 + * access control list */
  104 + acl->defaultDeny = 1;
  105 + TAILQ_FOREACH(entry, &acl->entries, next) {
  106 + TAILQ_REMOVE(&acl->entries, entry, next);
  107 + free(entry->match);
  108 + free(entry);
  109 + }
  110 + acl->nentries = 0;
  111 +}
  112 +
  113 +
  114 +int qemu_acl_append(qemu_acl *acl,
  115 + int deny,
  116 + const char *match)
  117 +{
  118 + qemu_acl_entry *entry;
  119 +
  120 + entry = qemu_malloc(sizeof(*entry));
  121 + entry->match = qemu_strdup(match);
  122 + entry->deny = deny;
  123 +
  124 + TAILQ_INSERT_TAIL(&acl->entries, entry, next);
  125 + acl->nentries++;
  126 +
  127 + return acl->nentries;
  128 +}
  129 +
  130 +
  131 +int qemu_acl_insert(qemu_acl *acl,
  132 + int deny,
  133 + const char *match,
  134 + int index)
  135 +{
  136 + qemu_acl_entry *entry;
  137 + qemu_acl_entry *tmp;
  138 + int i = 0;
  139 +
  140 + if (index <= 0)
  141 + return -1;
  142 + if (index >= acl->nentries)
  143 + return qemu_acl_append(acl, deny, match);
  144 +
  145 +
  146 + entry = qemu_malloc(sizeof(*entry));
  147 + entry->match = qemu_strdup(match);
  148 + entry->deny = deny;
  149 +
  150 + TAILQ_FOREACH(tmp, &acl->entries, next) {
  151 + i++;
  152 + if (i == index) {
  153 + TAILQ_INSERT_BEFORE(tmp, entry, next);
  154 + acl->nentries++;
  155 + break;
  156 + }
  157 + }
  158 +
  159 + return i;
  160 +}
  161 +
  162 +int qemu_acl_remove(qemu_acl *acl,
  163 + const char *match)
  164 +{
  165 + qemu_acl_entry *entry;
  166 + int i = 0;
  167 +
  168 + TAILQ_FOREACH(entry, &acl->entries, next) {
  169 + i++;
  170 + if (strcmp(entry->match, match) == 0) {
  171 + TAILQ_REMOVE(&acl->entries, entry, next);
  172 + return i;
  173 + }
  174 + }
  175 + return -1;
  176 +}
  177 +
  178 +
  179 +/*
  180 + * Local variables:
  181 + * c-indent-level: 4
  182 + * c-basic-offset: 4
  183 + * tab-width: 8
  184 + * End:
  185 + */
acl.h 0 → 100644
  1 +/*
  2 + * QEMU access control list management
  3 + *
  4 + * Copyright (C) 2009 Red Hat, Inc
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#ifndef __QEMU_ACL_H__
  26 +#define __QEMU_ACL_H__
  27 +
  28 +#include "sys-queue.h"
  29 +
  30 +typedef struct qemu_acl_entry qemu_acl_entry;
  31 +typedef struct qemu_acl qemu_acl;
  32 +
  33 +struct qemu_acl_entry {
  34 + char *match;
  35 + int deny;
  36 +
  37 + TAILQ_ENTRY(qemu_acl_entry) next;
  38 +};
  39 +
  40 +struct qemu_acl {
  41 + char *aclname;
  42 + unsigned int nentries;
  43 + TAILQ_HEAD(,qemu_acl_entry) entries;
  44 + int defaultDeny;
  45 +};
  46 +
  47 +qemu_acl *qemu_acl_init(const char *aclname);
  48 +
  49 +qemu_acl *qemu_acl_find(const char *aclname);
  50 +
  51 +int qemu_acl_party_is_allowed(qemu_acl *acl,
  52 + const char *party);
  53 +
  54 +void qemu_acl_reset(qemu_acl *acl);
  55 +
  56 +int qemu_acl_append(qemu_acl *acl,
  57 + int deny,
  58 + const char *match);
  59 +int qemu_acl_insert(qemu_acl *acl,
  60 + int deny,
  61 + const char *match,
  62 + int index);
  63 +int qemu_acl_remove(qemu_acl *acl,
  64 + const char *match);
  65 +
  66 +#endif /* __QEMU_ACL_H__ */
  67 +
  68 +/*
  69 + * Local variables:
  70 + * c-indent-level: 4
  71 + * c-basic-offset: 4
  72 + * tab-width: 8
  73 + * End:
  74 + */
configure
@@ -862,6 +862,21 @@ EOF @@ -862,6 +862,21 @@ EOF
862 fi 862 fi
863 863
864 ########################################## 864 ##########################################
  865 +# fnmatch() probe, used for ACL routines
  866 +fnmatch="no"
  867 +cat > $TMPC << EOF
  868 +#include <fnmatch.h>
  869 +int main(void)
  870 +{
  871 + fnmatch("foo", "foo", 0);
  872 + return 0;
  873 +}
  874 +EOF
  875 +if $cc $ARCH_CFLAGS -o $TMPE $TMPC > /dev/null 2> /dev/null ; then
  876 + fnmatch="yes"
  877 +fi
  878 +
  879 +##########################################
865 # vde libraries probe 880 # vde libraries probe
866 if test "$vde" = "yes" ; then 881 if test "$vde" = "yes" ; then
867 cat > $TMPC << EOF 882 cat > $TMPC << EOF
@@ -1421,6 +1436,9 @@ if test &quot;$vnc_sasl&quot; = &quot;yes&quot; ; then @@ -1421,6 +1436,9 @@ if test &quot;$vnc_sasl&quot; = &quot;yes&quot; ; then
1421 echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak 1436 echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
1422 echo "#define CONFIG_VNC_SASL 1" >> $config_h 1437 echo "#define CONFIG_VNC_SASL 1" >> $config_h
1423 fi 1438 fi
  1439 +if test "$fnmatch" = "yes" ; then
  1440 + echo "#define HAVE_FNMATCH_H 1" >> $config_h
  1441 +fi
1424 qemu_version=`head $source_path/VERSION` 1442 qemu_version=`head $source_path/VERSION`
1425 echo "VERSION=$qemu_version" >>$config_mak 1443 echo "VERSION=$qemu_version" >>$config_mak
1426 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h 1444 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
monitor.c
@@ -41,6 +41,7 @@ @@ -41,6 +41,7 @@
41 #include "qemu-timer.h" 41 #include "qemu-timer.h"
42 #include "migration.h" 42 #include "migration.h"
43 #include "kvm.h" 43 #include "kvm.h"
  44 +#include "acl.h"
44 45
45 //#define DEBUG 46 //#define DEBUG
46 //#define DEBUG_COMPLETION 47 //#define DEBUG_COMPLETION
@@ -1532,6 +1533,86 @@ static void do_info_balloon(Monitor *mon) @@ -1532,6 +1533,86 @@ static void do_info_balloon(Monitor *mon)
1532 monitor_printf(mon, "balloon: actual=%d\n", (int)(actual >> 20)); 1533 monitor_printf(mon, "balloon: actual=%d\n", (int)(actual >> 20));
1533 } 1534 }
1534 1535
  1536 +static void do_acl(Monitor *mon,
  1537 + const char *command,
  1538 + const char *aclname,
  1539 + const char *match,
  1540 + int has_index,
  1541 + int index)
  1542 +{
  1543 + qemu_acl *acl;
  1544 +
  1545 + acl = qemu_acl_find(aclname);
  1546 + if (!acl) {
  1547 + monitor_printf(mon, "acl: unknown list '%s'\n", aclname);
  1548 + return;
  1549 + }
  1550 +
  1551 + if (strcmp(command, "show") == 0) {
  1552 + int i = 0;
  1553 + qemu_acl_entry *entry;
  1554 + monitor_printf(mon, "policy: %s\n",
  1555 + acl->defaultDeny ? "deny" : "allow");
  1556 + TAILQ_FOREACH(entry, &acl->entries, next) {
  1557 + i++;
  1558 + monitor_printf(mon, "%d: %s %s\n", i,
  1559 + entry->deny ? "deny" : "allow",
  1560 + entry->match);
  1561 + }
  1562 + } else if (strcmp(command, "reset") == 0) {
  1563 + qemu_acl_reset(acl);
  1564 + monitor_printf(mon, "acl: removed all rules\n");
  1565 + } else if (strcmp(command, "policy") == 0) {
  1566 + if (!match) {
  1567 + monitor_printf(mon, "acl: missing policy parameter\n");
  1568 + return;
  1569 + }
  1570 +
  1571 + if (strcmp(match, "allow") == 0) {
  1572 + acl->defaultDeny = 0;
  1573 + monitor_printf(mon, "acl: policy set to 'allow'\n");
  1574 + } else if (strcmp(match, "deny") == 0) {
  1575 + acl->defaultDeny = 1;
  1576 + monitor_printf(mon, "acl: policy set to 'deny'\n");
  1577 + } else {
  1578 + monitor_printf(mon, "acl: unknown policy '%s', expected 'deny' or 'allow'\n", match);
  1579 + }
  1580 + } else if ((strcmp(command, "allow") == 0) ||
  1581 + (strcmp(command, "deny") == 0)) {
  1582 + int deny = strcmp(command, "deny") == 0 ? 1 : 0;
  1583 + int ret;
  1584 +
  1585 + if (!match) {
  1586 + monitor_printf(mon, "acl: missing match parameter\n");
  1587 + return;
  1588 + }
  1589 +
  1590 + if (has_index)
  1591 + ret = qemu_acl_insert(acl, deny, match, index);
  1592 + else
  1593 + ret = qemu_acl_append(acl, deny, match);
  1594 + if (ret < 0)
  1595 + monitor_printf(mon, "acl: unable to add acl entry\n");
  1596 + else
  1597 + monitor_printf(mon, "acl: added rule at position %d\n", ret);
  1598 + } else if (strcmp(command, "remove") == 0) {
  1599 + int ret;
  1600 +
  1601 + if (!match) {
  1602 + monitor_printf(mon, "acl: missing match parameter\n");
  1603 + return;
  1604 + }
  1605 +
  1606 + ret = qemu_acl_remove(acl, match);
  1607 + if (ret < 0)
  1608 + monitor_printf(mon, "acl: no matching acl entry\n");
  1609 + else
  1610 + monitor_printf(mon, "acl: removed rule at position %d\n", ret);
  1611 + } else {
  1612 + monitor_printf(mon, "acl: unknown command '%s'\n", command);
  1613 + }
  1614 +}
  1615 +
1535 /* Please update qemu-doc.texi when adding or changing commands */ 1616 /* Please update qemu-doc.texi when adding or changing commands */
1536 static const mon_cmd_t mon_cmds[] = { 1617 static const mon_cmd_t mon_cmds[] = {
1537 { "help|?", "s?", help_cmd, 1618 { "help|?", "s?", help_cmd,
@@ -1636,6 +1717,12 @@ static const mon_cmd_t mon_cmds[] = { @@ -1636,6 +1717,12 @@ static const mon_cmd_t mon_cmds[] = {
1636 "target", "request VM to change it's memory allocation (in MB)" }, 1717 "target", "request VM to change it's memory allocation (in MB)" },
1637 { "set_link", "ss", do_set_link, 1718 { "set_link", "ss", do_set_link,
1638 "name [up|down]", "change the link status of a network adapter" }, 1719 "name [up|down]", "change the link status of a network adapter" },
  1720 + { "acl", "sss?i?", do_acl, "<command> <aclname> [<match>] [<index>]\n",
  1721 + "acl show vnc.username\n"
  1722 + "acl policy vnc.username deny\n"
  1723 + "acl allow vnc.username fred\n"
  1724 + "acl deny vnc.username bob\n"
  1725 + "acl reset vnc.username\n" },
1639 { NULL, NULL, }, 1726 { NULL, NULL, },
1640 }; 1727 };
1641 1728
@@ -2961,6 +3048,15 @@ static void monitor_event(void *opaque, int event) @@ -2961,6 +3048,15 @@ static void monitor_event(void *opaque, int event)
2961 } 3048 }
2962 } 3049 }
2963 3050
  3051 +
  3052 +/*
  3053 + * Local variables:
  3054 + * c-indent-level: 4
  3055 + * c-basic-offset: 4
  3056 + * tab-width: 8
  3057 + * End:
  3058 + */
  3059 +
2964 void monitor_init(CharDriverState *chr, int flags) 3060 void monitor_init(CharDriverState *chr, int flags)
2965 { 3061 {
2966 static int is_first_init = 1; 3062 static int is_first_init = 1;
qemu-doc.texi
@@ -631,6 +631,19 @@ ensures a data encryption preventing compromise of authentication @@ -631,6 +631,19 @@ ensures a data encryption preventing compromise of authentication
631 credentials. See the @ref{vnc_security} section for details on using 631 credentials. See the @ref{vnc_security} section for details on using
632 SASL authentication. 632 SASL authentication.
633 633
  634 +@item acl
  635 +
  636 +Turn on access control lists for checking of the x509 client certificate
  637 +and SASL party. For x509 certs, the ACL check is made against the
  638 +certificate's distinguished name. This is something that looks like
  639 +@code{C=GB,O=ACME,L=Boston,CN=bob}. For SASL party, the ACL check is
  640 +made against the username, which depending on the SASL plugin, may
  641 +include a realm component, eg @code{bob} or @code{bob\@EXAMPLE.COM}.
  642 +When the @option{acl} flag is set, the initial access list will be
  643 +empty, with a @code{deny} policy. Thus no one will be allowed to
  644 +use the VNC server until the ACLs have been loaded. This can be
  645 +achieved using the @code{acl} monitor command.
  646 +
634 @end table 647 @end table
635 648
636 @end table 649 @end table
@@ -1392,6 +1405,42 @@ Password: ******** @@ -1392,6 +1405,42 @@ Password: ********
1392 1405
1393 @end table 1406 @end table
1394 1407
  1408 +@item acl @var{subcommand} @var{aclname} @var{match} @var{index}
  1409 +
  1410 +Manage access control lists for network services. There are currently
  1411 +two named access control lists, @var{vnc.x509dname} and @var{vnc.username}
  1412 +matching on the x509 client certificate distinguished name, and SASL
  1413 +username respectively.
  1414 +
  1415 +@table @option
  1416 +@item acl show <aclname>
  1417 +list all the match rules in the access control list, and the default
  1418 +policy
  1419 +@item acl policy <aclname> @code{allow|deny}
  1420 +set the default access control list policy, used in the event that
  1421 +none of the explicit rules match. The default policy at startup is
  1422 +always @code{deny}
  1423 +@item acl allow <aclname> <match> [<index>]
  1424 +add a match to the access control list, allowing access. The match will
  1425 +normally be an exact username or x509 distinguished name, but can
  1426 +optionally include wildcard globs. eg @code{*\@EXAMPLE.COM} to allow
  1427 +all users in the @code{EXAMPLE.COM} kerberos realm. The match will
  1428 +normally be appended to the end of the ACL, but can be inserted
  1429 +earlier in the list if the optional @code{index} parameter is supplied.
  1430 +@item acl deny <aclname> <match> [<index>]
  1431 +add a match to the access control list, denying access. The match will
  1432 +normally be an exact username or x509 distinguished name, but can
  1433 +optionally include wildcard globs. eg @code{*\@EXAMPLE.COM} to allow
  1434 +all users in the @code{EXAMPLE.COM} kerberos realm. The match will
  1435 +normally be appended to the end of the ACL, but can be inserted
  1436 +earlier in the list if the optional @code{index} parameter is supplied.
  1437 +@item acl remove <aclname> <match>
  1438 +remove the specified match rule from the access control list.
  1439 +@item acl reset <aclname>
  1440 +remove all matches from the access control list, and set the default
  1441 +policy back to @code{deny}.
  1442 +@end table
  1443 +
1395 @item screendump @var{filename} 1444 @item screendump @var{filename}
1396 Save screen into PPM image @var{filename}. 1445 Save screen into PPM image @var{filename}.
1397 1446
vnc-auth-sasl.c
@@ -120,22 +120,32 @@ static int vnc_auth_sasl_check_access(VncState *vs) @@ -120,22 +120,32 @@ static int vnc_auth_sasl_check_access(VncState *vs)
120 { 120 {
121 const void *val; 121 const void *val;
122 int err; 122 int err;
  123 + int allow;
123 124
124 err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); 125 err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
125 if (err != SASL_OK) { 126 if (err != SASL_OK) {
126 - VNC_DEBUG("cannot query SASL username on connection %d (%s)\n", 127 + VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
127 err, sasl_errstring(err, NULL, NULL)); 128 err, sasl_errstring(err, NULL, NULL));
128 return -1; 129 return -1;
129 } 130 }
130 if (val == NULL) { 131 if (val == NULL) {
131 - VNC_DEBUG("no client username was found\n"); 132 + VNC_DEBUG("no client username was found, denying access\n");
132 return -1; 133 return -1;
133 } 134 }
134 VNC_DEBUG("SASL client username %s\n", (const char *)val); 135 VNC_DEBUG("SASL client username %s\n", (const char *)val);
135 136
136 vs->sasl.username = qemu_strdup((const char*)val); 137 vs->sasl.username = qemu_strdup((const char*)val);
137 138
138 - return 0; 139 + if (vs->vd->sasl.acl == NULL) {
  140 + VNC_DEBUG("no ACL activated, allowing access\n");
  141 + return 0;
  142 + }
  143 +
  144 + allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
  145 +
  146 + VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
  147 + allow ? "allowed" : "denied");
  148 + return allow ? 0 : -1;
139 } 149 }
140 150
141 static int vnc_auth_sasl_check_ssf(VncState *vs) 151 static int vnc_auth_sasl_check_ssf(VncState *vs)
vnc-auth-sasl.h
@@ -30,6 +30,9 @@ @@ -30,6 +30,9 @@
30 #include <sasl/sasl.h> 30 #include <sasl/sasl.h>
31 31
32 typedef struct VncStateSASL VncStateSASL; 32 typedef struct VncStateSASL VncStateSASL;
  33 +typedef struct VncDisplaySASL VncDisplaySASL;
  34 +
  35 +#include "acl.h"
33 36
34 struct VncStateSASL { 37 struct VncStateSASL {
35 sasl_conn_t *conn; 38 sasl_conn_t *conn;
@@ -56,6 +59,10 @@ struct VncStateSASL { @@ -56,6 +59,10 @@ struct VncStateSASL {
56 char *mechlist; 59 char *mechlist;
57 }; 60 };
58 61
  62 +struct VncDisplaySASL {
  63 + qemu_acl *acl;
  64 +};
  65 +
59 void vnc_sasl_client_cleanup(VncState *vs); 66 void vnc_sasl_client_cleanup(VncState *vs);
60 67
61 long vnc_client_read_sasl(VncState *vs); 68 long vnc_client_read_sasl(VncState *vs);
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 #include "sysemu.h" 28 #include "sysemu.h"
29 #include "qemu_socket.h" 29 #include "qemu_socket.h"
30 #include "qemu-timer.h" 30 #include "qemu-timer.h"
  31 +#include "acl.h"
31 32
32 #define VNC_REFRESH_INTERVAL (1000 / 30) 33 #define VNC_REFRESH_INTERVAL (1000 / 30)
33 34
@@ -2083,6 +2084,7 @@ int vnc_display_open(DisplayState *ds, const char *display) @@ -2083,6 +2084,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
2083 int sasl = 0; 2084 int sasl = 0;
2084 int saslErr; 2085 int saslErr;
2085 #endif 2086 #endif
  2087 + int acl = 0;
2086 2088
2087 if (!vnc_display) 2089 if (!vnc_display)
2088 return -1; 2090 return -1;
@@ -2139,9 +2141,28 @@ int vnc_display_open(DisplayState *ds, const char *display) @@ -2139,9 +2141,28 @@ int vnc_display_open(DisplayState *ds, const char *display)
2139 return -1; 2141 return -1;
2140 } 2142 }
2141 #endif 2143 #endif
  2144 + } else if (strncmp(options, "acl", 3) == 0) {
  2145 + acl = 1;
2142 } 2146 }
2143 } 2147 }
2144 2148
  2149 +#ifdef CONFIG_VNC_TLS
  2150 + if (acl && x509 && vs->tls.x509verify) {
  2151 + if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
  2152 + fprintf(stderr, "Failed to create x509 dname ACL\n");
  2153 + exit(1);
  2154 + }
  2155 + }
  2156 +#endif
  2157 +#ifdef CONFIG_VNC_SASL
  2158 + if (acl && sasl) {
  2159 + if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) {
  2160 + fprintf(stderr, "Failed to create username ACL\n");
  2161 + exit(1);
  2162 + }
  2163 + }
  2164 +#endif
  2165 +
2145 /* 2166 /*
2146 * Combinations we support here: 2167 * Combinations we support here:
2147 * 2168 *
@@ -99,6 +99,9 @@ struct VncDisplay @@ -99,6 +99,9 @@ struct VncDisplay
99 int subauth; /* Used by VeNCrypt */ 99 int subauth; /* Used by VeNCrypt */
100 VncDisplayTLS tls; 100 VncDisplayTLS tls;
101 #endif 101 #endif
  102 +#ifdef CONFIG_VNC_SASL
  103 + VncDisplaySASL sasl;
  104 +#endif
102 }; 105 };
103 106
104 struct VncState 107 struct VncState