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 144 ifdef CONFIG_CURSES
145 145 OBJS+=curses.o
146 146 endif
147   -OBJS+=vnc.o d3des.o
  147 +OBJS+=vnc.o acl.o d3des.o
148 148 ifdef CONFIG_VNC_TLS
149 149 OBJS+=vnc-tls.o vnc-auth-vencrypt.o
150 150 endif
... ... @@ -174,9 +174,11 @@ sdl.o: sdl.c keymaps.h sdl_keysym.h
174 174  
175 175 sdl.o audio/sdlaudio.o: CFLAGS += $(SDL_CFLAGS)
176 176  
  177 +acl.o: acl.h acl.c
  178 +
177 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 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 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 880 # vde libraries probe
866 881 if test "$vde" = "yes" ; then
867 882 cat > $TMPC << EOF
... ... @@ -1421,6 +1436,9 @@ if test &quot;$vnc_sasl&quot; = &quot;yes&quot; ; then
1421 1436 echo "CONFIG_VNC_SASL_LIBS=$vnc_sasl_libs" >> $config_mak
1422 1437 echo "#define CONFIG_VNC_SASL 1" >> $config_h
1423 1438 fi
  1439 +if test "$fnmatch" = "yes" ; then
  1440 + echo "#define HAVE_FNMATCH_H 1" >> $config_h
  1441 +fi
1424 1442 qemu_version=`head $source_path/VERSION`
1425 1443 echo "VERSION=$qemu_version" >>$config_mak
1426 1444 echo "#define QEMU_VERSION \"$qemu_version\"" >> $config_h
... ...
monitor.c
... ... @@ -41,6 +41,7 @@
41 41 #include "qemu-timer.h"
42 42 #include "migration.h"
43 43 #include "kvm.h"
  44 +#include "acl.h"
44 45  
45 46 //#define DEBUG
46 47 //#define DEBUG_COMPLETION
... ... @@ -1532,6 +1533,86 @@ static void do_info_balloon(Monitor *mon)
1532 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 1616 /* Please update qemu-doc.texi when adding or changing commands */
1536 1617 static const mon_cmd_t mon_cmds[] = {
1537 1618 { "help|?", "s?", help_cmd,
... ... @@ -1636,6 +1717,12 @@ static const mon_cmd_t mon_cmds[] = {
1636 1717 "target", "request VM to change it's memory allocation (in MB)" },
1637 1718 { "set_link", "ss", do_set_link,
1638 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 1726 { NULL, NULL, },
1640 1727 };
1641 1728  
... ... @@ -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 3060 void monitor_init(CharDriverState *chr, int flags)
2965 3061 {
2966 3062 static int is_first_init = 1;
... ...
qemu-doc.texi
... ... @@ -631,6 +631,19 @@ ensures a data encryption preventing compromise of authentication
631 631 credentials. See the @ref{vnc_security} section for details on using
632 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 647 @end table
635 648  
636 649 @end table
... ... @@ -1392,6 +1405,42 @@ Password: ********
1392 1405  
1393 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 1444 @item screendump @var{filename}
1396 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 120 {
121 121 const void *val;
122 122 int err;
  123 + int allow;
123 124  
124 125 err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
125 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 128 err, sasl_errstring(err, NULL, NULL));
128 129 return -1;
129 130 }
130 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 133 return -1;
133 134 }
134 135 VNC_DEBUG("SASL client username %s\n", (const char *)val);
135 136  
136 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 151 static int vnc_auth_sasl_check_ssf(VncState *vs)
... ...
vnc-auth-sasl.h
... ... @@ -30,6 +30,9 @@
30 30 #include <sasl/sasl.h>
31 31  
32 32 typedef struct VncStateSASL VncStateSASL;
  33 +typedef struct VncDisplaySASL VncDisplaySASL;
  34 +
  35 +#include "acl.h"
33 36  
34 37 struct VncStateSASL {
35 38 sasl_conn_t *conn;
... ... @@ -56,6 +59,10 @@ struct VncStateSASL {
56 59 char *mechlist;
57 60 };
58 61  
  62 +struct VncDisplaySASL {
  63 + qemu_acl *acl;
  64 +};
  65 +
59 66 void vnc_sasl_client_cleanup(VncState *vs);
60 67  
61 68 long vnc_client_read_sasl(VncState *vs);
... ...
... ... @@ -28,6 +28,7 @@
28 28 #include "sysemu.h"
29 29 #include "qemu_socket.h"
30 30 #include "qemu-timer.h"
  31 +#include "acl.h"
31 32  
32 33 #define VNC_REFRESH_INTERVAL (1000 / 30)
33 34  
... ... @@ -2083,6 +2084,7 @@ int vnc_display_open(DisplayState *ds, const char *display)
2083 2084 int sasl = 0;
2084 2085 int saslErr;
2085 2086 #endif
  2087 + int acl = 0;
2086 2088  
2087 2089 if (!vnc_display)
2088 2090 return -1;
... ... @@ -2139,9 +2141,28 @@ int vnc_display_open(DisplayState *ds, const char *display)
2139 2141 return -1;
2140 2142 }
2141 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 2167 * Combinations we support here:
2147 2168 *
... ...
... ... @@ -99,6 +99,9 @@ struct VncDisplay
99 99 int subauth; /* Used by VeNCrypt */
100 100 VncDisplayTLS tls;
101 101 #endif
  102 +#ifdef CONFIG_VNC_SASL
  103 + VncDisplaySASL sasl;
  104 +#endif
102 105 };
103 106  
104 107 struct VncState
... ...