diff mbox

[v6,6/7] util: add QAuthZSimple object type for a simple access control list

Message ID 1465920443-22804-7-git-send-email-berrange@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel P. Berrangé June 14, 2016, 4:07 p.m. UTC
Add a QAuthZSimple object type that implements the QAuthZ
interface. This simple built-in implementation maintains
a trivial access control list with a sequence of match
rules and a final default policy. This replicates the
functionality currently provided by the qemu_acl module.

To create an instance of this object via the QMP monitor,
the syntax used would be

  {
    "execute": "object-add",
    "arguments": {
      "qom-type": "authz-simple",
      "id": "auth0",
      "parameters": {
        "rules": [
           { "match": "fred", "policy": "allow", "format": "exact" },
           { "match": "bob", "policy": "allow", "format": "exact" },
           { "match": "danb", "policy": "deny", "format": "glob" },
           { "match": "dan*", "policy": "allow", "format": "exact" },
        ],
        "policy": "deny"
      }
    }
  }

Or via the -object command line

  $QEMU \
     -object authz-simple,id=acl0,policy=deny,\
             rules.0.match=fred,rules.0.policy=allow,rules.0.format=exact,\
             rules.1.match=bob,rules.1.policy=allow,rules.1.format=exact,\
             rules.2.match=danb,rules.2.policy=deny,rules.2.format=glob,\
             rules.3.match=dan\*,rules.3.policy=allow,rules.3.format=exact

This sets up an authorization rule that allows 'fred',
'bob' and anyone whose name starts with 'dan', except
for 'danb'. Everyone unmatched is denied.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 Makefile                    |   2 +-
 include/qemu/authz-simple.h | 115 ++++++++++++++++
 qapi-schema.json            |   6 +-
 qapi/util.json              |  47 +++++++
 tests/.gitignore            |   1 +
 tests/Makefile.include      |   3 +
 tests/test-authz-simple.c   | 172 ++++++++++++++++++++++++
 util/Makefile.objs          |   1 +
 util/authz-simple.c         | 314 ++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 659 insertions(+), 2 deletions(-)
 create mode 100644 include/qemu/authz-simple.h
 create mode 100644 qapi/util.json
 create mode 100644 tests/test-authz-simple.c
 create mode 100644 util/authz-simple.c

Comments

Marc-André Lureau June 28, 2016, 4:58 p.m. UTC | #1
On Tue, Jun 14, 2016 at 6:07 PM, Daniel P. Berrange <berrange@redhat.com> wrote:
> Add a QAuthZSimple object type that implements the QAuthZ
> interface. This simple built-in implementation maintains
> a trivial access control list with a sequence of match
> rules and a final default policy. This replicates the
> functionality currently provided by the qemu_acl module.
>
> To create an instance of this object via the QMP monitor,
> the syntax used would be
>
>   {
>     "execute": "object-add",
>     "arguments": {
>       "qom-type": "authz-simple",
>       "id": "auth0",
>       "parameters": {
>         "rules": [
>            { "match": "fred", "policy": "allow", "format": "exact" },
>            { "match": "bob", "policy": "allow", "format": "exact" },
>            { "match": "danb", "policy": "deny", "format": "glob" },
>            { "match": "dan*", "policy": "allow", "format": "exact" },
>         ],
>         "policy": "deny"
>       }
>     }
>   }
>
> Or via the -object command line
>
>   $QEMU \
>      -object authz-simple,id=acl0,policy=deny,\
>              rules.0.match=fred,rules.0.policy=allow,rules.0.format=exact,\
>              rules.1.match=bob,rules.1.policy=allow,rules.1.format=exact,\
>              rules.2.match=danb,rules.2.policy=deny,rules.2.format=glob,\
>              rules.3.match=dan\*,rules.3.policy=allow,rules.3.format=exact
>
> This sets up an authorization rule that allows 'fred',
> 'bob' and anyone whose name starts with 'dan', except
> for 'danb'. Everyone unmatched is denied.
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  Makefile                    |   2 +-
>  include/qemu/authz-simple.h | 115 ++++++++++++++++
>  qapi-schema.json            |   6 +-
>  qapi/util.json              |  47 +++++++
>  tests/.gitignore            |   1 +
>  tests/Makefile.include      |   3 +
>  tests/test-authz-simple.c   | 172 ++++++++++++++++++++++++
>  util/Makefile.objs          |   1 +
>  util/authz-simple.c         | 314 ++++++++++++++++++++++++++++++++++++++++++++
>  9 files changed, 659 insertions(+), 2 deletions(-)
>  create mode 100644 include/qemu/authz-simple.h
>  create mode 100644 qapi/util.json
>  create mode 100644 tests/test-authz-simple.c
>  create mode 100644 util/authz-simple.c
>
> diff --git a/Makefile b/Makefile
> index 421c390..e8010ed 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -292,7 +292,7 @@ qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
>                 $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
>                 $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
>                 $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
> -               $(SRC_PATH)/qapi/trace.json
> +               $(SRC_PATH)/qapi/trace.json $(SRC_PATH)/qapi/util.json
>
>  qapi-types.c qapi-types.h :\
>  $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> diff --git a/include/qemu/authz-simple.h b/include/qemu/authz-simple.h
> new file mode 100644
> index 0000000..30e78bd
> --- /dev/null
> +++ b/include/qemu/authz-simple.h
> @@ -0,0 +1,115 @@
> +/*
> + * QEMU simple authorization driver
> + *
> + * Copyright (c) 2016 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef QAUTHZ_SIMPLE_H__
> +#define QAUTHZ_SIMPLE_H__
> +
> +#include "qemu/authz.h"
> +
> +
> +#define TYPE_QAUTHZ_SIMPLE "authz-simple"
> +
> +#define QAUTHZ_SIMPLE_CLASS(klass) \
> +     OBJECT_CLASS_CHECK(QAuthZSimpleClass, (klass), \
> +                        TYPE_QAUTHZ_SIMPLE)
> +#define QAUTHZ_SIMPLE_GET_CLASS(obj) \
> +     OBJECT_GET_CLASS(QAuthZSimpleClass, (obj), \
> +                      TYPE_QAUTHZ_SIMPLE)
> +#define QAUTHZ_SIMPLE(obj) \
> +     INTERFACE_CHECK(QAuthZSimple, (obj), \
> +                     TYPE_QAUTHZ_SIMPLE)
> +
> +typedef struct QAuthZSimple QAuthZSimple;
> +typedef struct QAuthZSimpleClass QAuthZSimpleClass;
> +
> +
> +/**
> + * QAuthZSimple:
> + *
> + * This authorization driver provides a simple mechanism
> + * for granting access by matching user names against a
> + * list of globs. Each match rule has an associated policy
> + * and a catch all policy applies if no rule matches
> + *
> + * To create an instance of this class via QMP:
> + *
> + *  {
> + *    "execute": "object-add",
> + *    "arguments": {
> + *      "qom-type": "authz-simple",
> + *      "id": "auth0",
> + *      "parameters": {
> + *        "rules": [
> + *           { "match": "fred", "policy": "allow", "format": "exact" },
> + *           { "match": "bob", "policy": "allow", "format": "exact" },
> + *           { "match": "danb", "policy": "deny", "format": "exact" },
> + *           { "match": "dan*", "policy": "allow", "format": "glob" }
> + *        ],
> + *        "policy": "deny"
> + *      }
> + *    }
> + *  }
> + *
> + * Or via the CLI:
> + *
> + *   $QEMU                                                         \
> + *    -object authz-simple,id=acl0,policy=deny,                    \
> + *            match.0.name=fred,match.0.policy=allow,format=exact, \
> + *            match.1.name=bob,match.1.policy=allow,format=exact,  \
> + *            match.2.name=danb,match.2.policy=deny,format=exact,  \
> + *            match.3.name=dan\*,match.3.policy=allow,format=exact
> + *
> + */
> +struct QAuthZSimple {
> +    QAuthZ parent_obj;
> +
> +    QAuthZSimplePolicy policy;
> +    QAuthZSimpleRuleList *rules;
> +};
> +
> +
> +struct QAuthZSimpleClass {
> +    QAuthZClass parent_class;
> +};
> +
> +
> +QAuthZSimple *qauthz_simple_new(const char *id,
> +                                QAuthZSimplePolicy policy,
> +                                Error **errp);
> +
> +ssize_t qauthz_simple_append_rule(QAuthZSimple *auth,
> +                                  const char *match,
> +                                  QAuthZSimplePolicy policy,
> +                                  QAuthZSimpleFormat format,
> +                                  Error **errp);
> +
> +ssize_t qauthz_simple_insert_rule(QAuthZSimple *auth,
> +                                  const char *match,
> +                                  QAuthZSimplePolicy policy,
> +                                  QAuthZSimpleFormat format,
> +                                  size_t index,
> +                                  Error **errp);
> +
> +ssize_t qauthz_simple_delete_rule(QAuthZSimple *auth,
> +                                  const char *match);
> +
> +
> +#endif /* QAUTHZ_SIMPLE_H__ */
> +
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 48c3a6f..e46b4e3 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -5,6 +5,9 @@
>  # QAPI common definitions
>  { 'include': 'qapi/common.json' }
>
> +# QAPI util definitions
> +{ 'include': 'qapi/util.json' }
> +
>  # QAPI crypto definitions
>  { 'include': 'qapi/crypto.json' }
>
> @@ -3729,7 +3732,8 @@
>  # Since 2.5
>  ##
>  { 'struct': 'DummyForceArrays',
> -  'data': { 'unused': ['X86CPUFeatureWordInfo'] } }
> +  'data': { 'unused1': ['X86CPUFeatureWordInfo'],
> +            'unused2': ['QAuthZSimpleRule'] } }
>
>
>  ##
> diff --git a/qapi/util.json b/qapi/util.json
> new file mode 100644
> index 0000000..fec43a8
> --- /dev/null
> +++ b/qapi/util.json
> @@ -0,0 +1,47 @@
> +# -*- Mode: Python -*-
> +#
> +# QAPI util definitions
> +
> +##
> +# QAuthZSimplePolicy:
> +#
> +# The authorization policy result
> +#
> +# @deny: deny access
> +# @allow: allow access
> +#
> +# Since: 2.6

now 2.7 (and the rest of the file)

> +##
> +{ 'enum': 'QAuthZSimplePolicy',
> +  'prefix': 'QAUTHZ_SIMPLE_POLICY',
> +  'data': ['deny', 'allow']}
> +
> +##
> +# QAuthZSimpleFormat:
> +#
> +# The authorization policy result
> +#
> +# @exact: an exact string match
> +# @glob: string with ? and * shell wildcard support
> +#
> +# Since: 2.6
> +##
> +{ 'enum': 'QAuthZSimpleFormat',
> +  'prefix': 'QAUTHZ_SIMPLE_FORMAT',
> +  'data': ['exact', 'glob']}
> +
> +##
> +# QAuthZSimpleRule:
> +#
> +# A single authorization rule.
> +#
> +# @match: a glob to match against a user identity
> +# @policy: the result to return if @match evaluates to true
> +# @format: (optional) the format ofthe @match rule (default 'exact')

"of the" (missing space)

> +#
> +# Since: 2.6
> +##
> +{ 'struct': 'QAuthZSimpleRule',
> +  'data': {'match': 'str',
> +           'policy': 'QAuthZSimplePolicy',
> +           '*format': 'QAuthZSimpleFormat'}}
> diff --git a/tests/.gitignore b/tests/.gitignore
> index 840ea39..d90d738 100644
> --- a/tests/.gitignore
> +++ b/tests/.gitignore
> @@ -10,6 +10,7 @@ check-qom-proplist
>  qht-bench
>  rcutorture
>  test-aio
> +test-authz-simple
>  test-base64
>  test-bitops
>  test-blockjob-txn
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index 7d63d16..ff1026a 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -109,6 +109,7 @@ check-unit-y += tests/test-crypto-xts$(EXESUF)
>  check-unit-y += tests/test-crypto-block$(EXESUF)
>  gcov-files-test-logging-y = tests/test-logging.c
>  check-unit-y += tests/test-logging$(EXESUF)
> +check-unit-y += tests/test-authz-simple$(EXESUF)
>
>  check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
>
> @@ -460,6 +461,8 @@ tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
>         $(test-util-obj-y)
>  tests/test-base64$(EXESUF): tests/test-base64.o \
>         libqemuutil.a libqemustub.a
> +tests/test-authz-simple$(EXESUF): tests/test-authz-simple.o \
> +       libqemuutil.a libqemustub.a $(util-qom-obj-y) $(qom-obj-y)
>
>  tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y)
>
> diff --git a/tests/test-authz-simple.c b/tests/test-authz-simple.c
> new file mode 100644
> index 0000000..e9aea6d
> --- /dev/null
> +++ b/tests/test-authz-simple.c
> @@ -0,0 +1,172 @@
> +/*
> + * QEMU simple authorization object
> + *
> + * Copyright (c) 2016 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include <glib.h>
> +
> +#include "qemu/authz-simple.h"
> +
> +static void test_authz_default_deny(void)
> +{
> +    QAuthZSimple *auth = qauthz_simple_new("auth0",
> +                                           QAUTHZ_SIMPLE_POLICY_DENY,
> +                                           &error_abort);
> +
> +    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
> +
> +    object_unparent(OBJECT(auth));
> +}
> +
> +static void test_authz_default_allow(void)
> +{
> +    QAuthZSimple *auth = qauthz_simple_new("auth0",
> +                                           QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                                           &error_abort);
> +
> +    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
> +
> +    object_unparent(OBJECT(auth));
> +}
> +
> +static void test_authz_explicit_deny(void)
> +{
> +    QAuthZSimple *auth = qauthz_simple_new("auth0",
> +                                           QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                                           &error_abort);
> +
> +    qauthz_simple_append_rule(auth, "fred", QAUTHZ_SIMPLE_POLICY_DENY,
> +                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
> +
> +    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
> +
> +    object_unparent(OBJECT(auth));
> +}
> +
> +static void test_authz_explicit_allow(void)
> +{
> +    QAuthZSimple *auth = qauthz_simple_new("auth0",
> +                                           QAUTHZ_SIMPLE_POLICY_DENY,
> +                                           &error_abort);
> +
> +    qauthz_simple_append_rule(auth, "fred", QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
> +
> +    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
> +
> +    object_unparent(OBJECT(auth));
> +}
> +
> +
> +static void test_authz_complex(void)
> +{
> +#ifndef CONFIG_FNMATCH
> +    Error *local_err = NULL;
> +#endif
> +    QAuthZSimple *auth = qauthz_simple_new("auth0",
> +                                           QAUTHZ_SIMPLE_POLICY_DENY,
> +                                           &error_abort);
> +
> +    qauthz_simple_append_rule(auth, "fred", QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
> +    qauthz_simple_append_rule(auth, "bob", QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
> +    qauthz_simple_append_rule(auth, "dan", QAUTHZ_SIMPLE_POLICY_DENY,
> +                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
> +#ifdef CONFIG_FNMATCH
> +    qauthz_simple_append_rule(auth, "dan*", QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                              QAUTHZ_SIMPLE_FORMAT_GLOB, &error_abort);
> +
> +    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
> +    g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort));
> +    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
> +    g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort));
> +#else
> +    g_assert(qauthz_simple_append_rule(auth, "dan*",
> +                                       QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                                       QAUTHZ_SIMPLE_FORMAT_GLOB,
> +                                       &local_err) < 0);
> +    g_assert(local_err != NULL);
> +    error_free(local_err);
> +#endif
> +
> +    object_unparent(OBJECT(auth));
> +}
> +
> +static void test_authz_add_remove(void)
> +{
> +    QAuthZSimple *auth = qauthz_simple_new("auth0",
> +                                           QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                                           &error_abort);
> +
> +    g_assert_cmpint(qauthz_simple_append_rule(auth, "fred",
> +                                              QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
> +                                              &error_abort),
> +                    ==, 0);
> +    g_assert_cmpint(qauthz_simple_append_rule(auth, "bob",
> +                                              QAUTHZ_SIMPLE_POLICY_ALLOW,
> +                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
> +                                              &error_abort),
> +                    ==, 1);
> +    g_assert_cmpint(qauthz_simple_append_rule(auth, "dan",
> +                                              QAUTHZ_SIMPLE_POLICY_DENY,
> +                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
> +                                              &error_abort),
> +                    ==, 2);
> +    g_assert_cmpint(qauthz_simple_append_rule(auth, "frank",
> +                                              QAUTHZ_SIMPLE_POLICY_DENY,
> +                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
> +                                              &error_abort),
> +                    ==, 3);
> +
> +    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
> +
> +    g_assert_cmpint(qauthz_simple_delete_rule(auth, "dan"),
> +                    ==, 2);
> +
> +    g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
> +
> +    g_assert_cmpint(qauthz_simple_insert_rule(auth, "dan",
> +                                              QAUTHZ_SIMPLE_POLICY_DENY,
> +                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
> +                                              2,
> +                                              &error_abort),
> +                    ==, 2);
> +
> +    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
> +
> +    object_unparent(OBJECT(auth));
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +
> +    module_call_init(MODULE_INIT_QOM);
> +
> +    g_test_add_func("/auth/simple/default/deny", test_authz_default_deny);
> +    g_test_add_func("/auth/simple/default/allow", test_authz_default_allow);
> +    g_test_add_func("/auth/simple/explicit/deny", test_authz_explicit_deny);
> +    g_test_add_func("/auth/simple/explicit/allow", test_authz_explicit_allow);
> +    g_test_add_func("/auth/simple/complex", test_authz_complex);
> +    g_test_add_func("/auth/simple/add-remove", test_authz_add_remove);
> +
> +    return g_test_run();
> +}
> diff --git a/util/Makefile.objs b/util/Makefile.objs
> index 0d83583..c0f89de 100644
> --- a/util/Makefile.objs
> +++ b/util/Makefile.objs
> @@ -36,3 +36,4 @@ util-obj-y += qdist.o
>  util-obj-y += qht.o
>
>  util-qom-obj-y += authz.o
> +util-qom-obj-y += authz-simple.o
> diff --git a/util/authz-simple.c b/util/authz-simple.c
> new file mode 100644
> index 0000000..a26177f
> --- /dev/null
> +++ b/util/authz-simple.c
> @@ -0,0 +1,314 @@
> +/*
> + * QEMU simple authorization driver
> + *
> + * Copyright (c) 2016 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/authz-simple.h"
> +#include "qom/object_interfaces.h"
> +#include "qapi-visit.h"
> +
> +#ifdef CONFIG_FNMATCH
> +#include <fnmatch.h>
> +#endif
> +
> +static bool qauthz_simple_is_allowed(QAuthZ *authz,
> +                                     const char *identity,
> +                                     Error **errp)
> +{
> +    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
> +    QAuthZSimpleRuleList *rules = sauthz->rules;
> +
> +    while (rules) {
> +        QAuthZSimpleRule *rule = rules->value;
> +        QAuthZSimpleFormat format = rule->has_format ? rule->format :
> +            QAUTHZ_SIMPLE_FORMAT_EXACT;
> +
> +        switch (format) {
> +        case QAUTHZ_SIMPLE_FORMAT_EXACT:
> +            if (strcmp(rule->match, identity) == 0) {
> +                return rule->policy == QAUTHZ_SIMPLE_POLICY_ALLOW;
> +            }
> +            break;
> +#ifdef CONFIG_FNMATCH
> +        case QAUTHZ_SIMPLE_FORMAT_GLOB:
> +            if (fnmatch(rule->match, identity, 0) == 0) {
> +                return rule->policy == QAUTHZ_SIMPLE_POLICY_ALLOW;
> +            }
> +            break;
> +#else
> +            return false;
> +#endif
> +        default:
> +            return false;
> +        }
> +        rules = rules->next;
> +    }
> +
> +    return sauthz->policy == QAUTHZ_SIMPLE_POLICY_ALLOW;
> +}
> +
> +
> +static void
> +qauthz_simple_prop_set_policy(Object *obj,
> +                              int value,
> +                              Error **errp G_GNUC_UNUSED)
> +{
> +    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
> +
> +    sauthz->policy = value;
> +}
> +
> +
> +static int
> +qauthz_simple_prop_get_policy(Object *obj,
> +                              Error **errp G_GNUC_UNUSED)
> +{
> +    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
> +
> +    return sauthz->policy;
> +}
> +
> +
> +static void
> +qauthz_simple_prop_get_rules(Object *obj, Visitor *v, const char *name,
> +                             void *opaque, Error **errp)
> +{
> +    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
> +
> +    visit_type_QAuthZSimpleRuleList(v, name, &sauthz->rules, errp);
> +}
> +
> +static void
> +qauthz_simple_prop_set_rules(Object *obj, Visitor *v, const char *name,
> +                             void *opaque, Error **errp)
> +{
> +    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
> +    QAuthZSimpleRuleList *oldrules;
> +#ifndef CONFIG_FNMATCH
> +    QAuthZSimpleRuleList *rules;
> +#endif
> +
> +    oldrules = sauthz->rules;
> +    visit_type_QAuthZSimpleRuleList(v, name, &sauthz->rules, errp);
> +
> +#ifndef CONFIG_FNMATCH
> +    rules = sauthz->rules;
> +    while (rules) {
> +        QAuthZSimpleRule *rule = rules->value;
> +        if (rule->has_format &&
> +            rule->format == QAUTHZ_SIMPLE_FORMAT_GLOB) {
> +            error_setg(errp, "Glob format not supported on this platform");
> +            qapi_free_QAuthZSimpleRuleList(sauthz->rules);
> +            sauthz->rules = oldrules;
> +            return;
> +        }
> +    }
> +#endif
> +
> +    qapi_free_QAuthZSimpleRuleList(oldrules);
> +}
> +
> +
> +static void
> +qauthz_simple_complete(UserCreatable *uc, Error **errp)
> +{
> +}
> +
> +
> +static void
> +qauthz_simple_finalize(Object *obj)
> +{
> +    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
> +
> +    qapi_free_QAuthZSimpleRuleList(sauthz->rules);
> +}
> +
> +
> +static void
> +qauthz_simple_class_init(ObjectClass *oc, void *data)
> +{
> +    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
> +    QAuthZClass *authz = QAUTHZ_CLASS(oc);
> +
> +    ucc->complete = qauthz_simple_complete;
> +    authz->is_allowed = qauthz_simple_is_allowed;
> +
> +    object_class_property_add_enum(oc, "policy",
> +                                   "QAuthZSimplePolicy",
> +                                   QAuthZSimplePolicy_lookup,
> +                                   qauthz_simple_prop_get_policy,
> +                                   qauthz_simple_prop_set_policy,
> +                                   NULL);
> +
> +    object_class_property_add(oc, "rules", "QAuthZSimpleRule",
> +                              qauthz_simple_prop_get_rules,
> +                              qauthz_simple_prop_set_rules,
> +                              NULL, NULL, NULL);
> +}
> +
> +
> +QAuthZSimple *qauthz_simple_new(const char *id,
> +                                QAuthZSimplePolicy policy,
> +                                Error **errp)
> +{
> +    return QAUTHZ_SIMPLE(
> +        object_new_with_props(TYPE_QAUTHZ_SIMPLE,
> +                              object_get_objects_root(),
> +                              id, errp,
> +                              "policy", QAuthZSimplePolicy_lookup[policy],
> +                              NULL));
> +}
> +
> +
> +ssize_t qauthz_simple_append_rule(QAuthZSimple *auth,
> +                                  const char *match,
> +                                  QAuthZSimplePolicy policy,
> +                                  QAuthZSimpleFormat format,
> +                                  Error **errp)
> +{
> +    QAuthZSimpleRule *rule;
> +    QAuthZSimpleRuleList *rules, *tmp;
> +    size_t i = 0;
> +
> +#ifndef CONFIG_FNMATCH
> +    if (format == QAUTHZ_SIMPLE_FORMAT_GLOB) {
> +        error_setg(errp, "Glob format not supported on this platform");
> +        return -1;
> +    }
> +#endif
> +
> +    rule = g_new0(QAuthZSimpleRule, 1);
> +    rule->policy = policy;
> +    rule->match = g_strdup(match);
> +    rule->format = format;
> +    rule->has_format = true;
> +
> +    tmp = g_new0(QAuthZSimpleRuleList, 1);
> +    tmp->value = rule;
> +
> +    rules = auth->rules;
> +    if (rules) {
> +        while (rules->next) {
> +            i++;
> +            rules = rules->next;
> +        }
> +        rules->next = tmp;
> +        return i + 1;
> +    } else {
> +        auth->rules = tmp;
> +        return 0;
> +    }
> +}
> +
> +
> +ssize_t qauthz_simple_insert_rule(QAuthZSimple *auth,
> +                                  const char *match,
> +                                  QAuthZSimplePolicy policy,
> +                                  QAuthZSimpleFormat format,
> +                                  size_t index,
> +                                  Error **errp)
> +{
> +    QAuthZSimpleRule *rule;
> +    QAuthZSimpleRuleList *rules, *tmp;
> +    size_t i = 0;
> +
> +#ifndef CONFIG_FNMATCH
> +    if (format == QAUTHZ_SIMPLE_FORMAT_GLOB) {
> +        error_setg(errp, "Glob format not supported on this platform");
> +        return -1;
> +    }
> +#endif
> +
> +    rule = g_new0(QAuthZSimpleRule, 1);
> +    rule->policy = policy;
> +    rule->match = g_strdup(match);
> +    rule->format = format;
> +    rule->has_format = true;
> +
> +    tmp = g_new0(QAuthZSimpleRuleList, 1);
> +    tmp->value = rule;
> +
> +    rules = auth->rules;
> +    if (rules && index > 0) {
> +        while (rules->next && i < (index - 1)) {
> +            i++;
> +            rules = rules->next;
> +        }
> +        tmp->next = rules->next;
> +        rules->next = tmp;
> +        return i + 1;
> +    } else {
> +        tmp->next = auth->rules;
> +        auth->rules = tmp;
> +        return 0;
> +    }
> +}
> +
> +
> +ssize_t qauthz_simple_delete_rule(QAuthZSimple *auth, const char *match)
> +{
> +    QAuthZSimpleRule *rule;
> +    QAuthZSimpleRuleList *rules, *prev;
> +    size_t i = 0;
> +
> +    prev = NULL;
> +    rules = auth->rules;
> +    while (rules) {
> +        rule = rules->value;
> +        if (g_str_equal(rule->match, match)) {
> +            if (prev) {
> +                prev->next = rules->next;
> +            } else {
> +                auth->rules = rules->next;
> +            }
> +            rules->next = NULL;
> +            qapi_free_QAuthZSimpleRuleList(rules);
> +            return i;
> +        }
> +        prev = rules;
> +        rules = rules->next;
> +        i++;
> +    }
> +
> +    return -1;
> +}
> +
> +
> +static const TypeInfo qauthz_simple_info = {
> +    .parent = TYPE_QAUTHZ,
> +    .name = TYPE_QAUTHZ_SIMPLE,
> +    .instance_size = sizeof(QAuthZSimple),
> +    .instance_finalize = qauthz_simple_finalize,
> +    .class_size = sizeof(QAuthZSimpleClass),
> +    .class_init = qauthz_simple_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_USER_CREATABLE },
> +        { }
> +    }
> +};
> +
> +
> +static void
> +qauthz_simple_register_types(void)
> +{
> +    type_register_static(&qauthz_simple_info);
> +}
> +
> +
> +type_init(qauthz_simple_register_types);
> --
> 2.5.5
>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 421c390..e8010ed 100644
--- a/Makefile
+++ b/Makefile
@@ -292,7 +292,7 @@  qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
                $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
                $(SRC_PATH)/qapi/crypto.json $(SRC_PATH)/qapi/rocker.json \
-               $(SRC_PATH)/qapi/trace.json
+               $(SRC_PATH)/qapi/trace.json $(SRC_PATH)/qapi/util.json
 
 qapi-types.c qapi-types.h :\
 $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
diff --git a/include/qemu/authz-simple.h b/include/qemu/authz-simple.h
new file mode 100644
index 0000000..30e78bd
--- /dev/null
+++ b/include/qemu/authz-simple.h
@@ -0,0 +1,115 @@ 
+/*
+ * QEMU simple authorization driver
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_SIMPLE_H__
+#define QAUTHZ_SIMPLE_H__
+
+#include "qemu/authz.h"
+
+
+#define TYPE_QAUTHZ_SIMPLE "authz-simple"
+
+#define QAUTHZ_SIMPLE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(QAuthZSimpleClass, (klass), \
+                        TYPE_QAUTHZ_SIMPLE)
+#define QAUTHZ_SIMPLE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(QAuthZSimpleClass, (obj), \
+                      TYPE_QAUTHZ_SIMPLE)
+#define QAUTHZ_SIMPLE(obj) \
+     INTERFACE_CHECK(QAuthZSimple, (obj), \
+                     TYPE_QAUTHZ_SIMPLE)
+
+typedef struct QAuthZSimple QAuthZSimple;
+typedef struct QAuthZSimpleClass QAuthZSimpleClass;
+
+
+/**
+ * QAuthZSimple:
+ *
+ * This authorization driver provides a simple mechanism
+ * for granting access by matching user names against a
+ * list of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ *  {
+ *    "execute": "object-add",
+ *    "arguments": {
+ *      "qom-type": "authz-simple",
+ *      "id": "auth0",
+ *      "parameters": {
+ *        "rules": [
+ *           { "match": "fred", "policy": "allow", "format": "exact" },
+ *           { "match": "bob", "policy": "allow", "format": "exact" },
+ *           { "match": "danb", "policy": "deny", "format": "exact" },
+ *           { "match": "dan*", "policy": "allow", "format": "glob" }
+ *        ],
+ *        "policy": "deny"
+ *      }
+ *    }
+ *  }
+ *
+ * Or via the CLI:
+ *
+ *   $QEMU                                                         \
+ *    -object authz-simple,id=acl0,policy=deny,                    \
+ *            match.0.name=fred,match.0.policy=allow,format=exact, \
+ *            match.1.name=bob,match.1.policy=allow,format=exact,  \
+ *            match.2.name=danb,match.2.policy=deny,format=exact,  \
+ *            match.3.name=dan\*,match.3.policy=allow,format=exact
+ *
+ */
+struct QAuthZSimple {
+    QAuthZ parent_obj;
+
+    QAuthZSimplePolicy policy;
+    QAuthZSimpleRuleList *rules;
+};
+
+
+struct QAuthZSimpleClass {
+    QAuthZClass parent_class;
+};
+
+
+QAuthZSimple *qauthz_simple_new(const char *id,
+                                QAuthZSimplePolicy policy,
+                                Error **errp);
+
+ssize_t qauthz_simple_append_rule(QAuthZSimple *auth,
+                                  const char *match,
+                                  QAuthZSimplePolicy policy,
+                                  QAuthZSimpleFormat format,
+                                  Error **errp);
+
+ssize_t qauthz_simple_insert_rule(QAuthZSimple *auth,
+                                  const char *match,
+                                  QAuthZSimplePolicy policy,
+                                  QAuthZSimpleFormat format,
+                                  size_t index,
+                                  Error **errp);
+
+ssize_t qauthz_simple_delete_rule(QAuthZSimple *auth,
+                                  const char *match);
+
+
+#endif /* QAUTHZ_SIMPLE_H__ */
+
diff --git a/qapi-schema.json b/qapi-schema.json
index 48c3a6f..e46b4e3 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -5,6 +5,9 @@ 
 # QAPI common definitions
 { 'include': 'qapi/common.json' }
 
+# QAPI util definitions
+{ 'include': 'qapi/util.json' }
+
 # QAPI crypto definitions
 { 'include': 'qapi/crypto.json' }
 
@@ -3729,7 +3732,8 @@ 
 # Since 2.5
 ##
 { 'struct': 'DummyForceArrays',
-  'data': { 'unused': ['X86CPUFeatureWordInfo'] } }
+  'data': { 'unused1': ['X86CPUFeatureWordInfo'],
+            'unused2': ['QAuthZSimpleRule'] } }
 
 
 ##
diff --git a/qapi/util.json b/qapi/util.json
new file mode 100644
index 0000000..fec43a8
--- /dev/null
+++ b/qapi/util.json
@@ -0,0 +1,47 @@ 
+# -*- Mode: Python -*-
+#
+# QAPI util definitions
+
+##
+# QAuthZSimplePolicy:
+#
+# The authorization policy result
+#
+# @deny: deny access
+# @allow: allow access
+#
+# Since: 2.6
+##
+{ 'enum': 'QAuthZSimplePolicy',
+  'prefix': 'QAUTHZ_SIMPLE_POLICY',
+  'data': ['deny', 'allow']}
+
+##
+# QAuthZSimpleFormat:
+#
+# The authorization policy result
+#
+# @exact: an exact string match
+# @glob: string with ? and * shell wildcard support
+#
+# Since: 2.6
+##
+{ 'enum': 'QAuthZSimpleFormat',
+  'prefix': 'QAUTHZ_SIMPLE_FORMAT',
+  'data': ['exact', 'glob']}
+
+##
+# QAuthZSimpleRule:
+#
+# A single authorization rule.
+#
+# @match: a glob to match against a user identity
+# @policy: the result to return if @match evaluates to true
+# @format: (optional) the format ofthe @match rule (default 'exact')
+#
+# Since: 2.6
+##
+{ 'struct': 'QAuthZSimpleRule',
+  'data': {'match': 'str',
+           'policy': 'QAuthZSimplePolicy',
+           '*format': 'QAuthZSimpleFormat'}}
diff --git a/tests/.gitignore b/tests/.gitignore
index 840ea39..d90d738 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -10,6 +10,7 @@  check-qom-proplist
 qht-bench
 rcutorture
 test-aio
+test-authz-simple
 test-base64
 test-bitops
 test-blockjob-txn
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 7d63d16..ff1026a 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -109,6 +109,7 @@  check-unit-y += tests/test-crypto-xts$(EXESUF)
 check-unit-y += tests/test-crypto-block$(EXESUF)
 gcov-files-test-logging-y = tests/test-logging.c
 check-unit-y += tests/test-logging$(EXESUF)
+check-unit-y += tests/test-authz-simple$(EXESUF)
 
 check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 
@@ -460,6 +461,8 @@  tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
 	$(test-util-obj-y)
 tests/test-base64$(EXESUF): tests/test-base64.o \
 	libqemuutil.a libqemustub.a
+tests/test-authz-simple$(EXESUF): tests/test-authz-simple.o \
+	libqemuutil.a libqemustub.a $(util-qom-obj-y) $(qom-obj-y)
 
 tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y)
 
diff --git a/tests/test-authz-simple.c b/tests/test-authz-simple.c
new file mode 100644
index 0000000..e9aea6d
--- /dev/null
+++ b/tests/test-authz-simple.c
@@ -0,0 +1,172 @@ 
+/*
+ * QEMU simple authorization object
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include <glib.h>
+
+#include "qemu/authz-simple.h"
+
+static void test_authz_default_deny(void)
+{
+    QAuthZSimple *auth = qauthz_simple_new("auth0",
+                                           QAUTHZ_SIMPLE_POLICY_DENY,
+                                           &error_abort);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_default_allow(void)
+{
+    QAuthZSimple *auth = qauthz_simple_new("auth0",
+                                           QAUTHZ_SIMPLE_POLICY_ALLOW,
+                                           &error_abort);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_deny(void)
+{
+    QAuthZSimple *auth = qauthz_simple_new("auth0",
+                                           QAUTHZ_SIMPLE_POLICY_ALLOW,
+                                           &error_abort);
+
+    qauthz_simple_append_rule(auth, "fred", QAUTHZ_SIMPLE_POLICY_DENY,
+                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_explicit_allow(void)
+{
+    QAuthZSimple *auth = qauthz_simple_new("auth0",
+                                           QAUTHZ_SIMPLE_POLICY_DENY,
+                                           &error_abort);
+
+    qauthz_simple_append_rule(auth, "fred", QAUTHZ_SIMPLE_POLICY_ALLOW,
+                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+
+static void test_authz_complex(void)
+{
+#ifndef CONFIG_FNMATCH
+    Error *local_err = NULL;
+#endif
+    QAuthZSimple *auth = qauthz_simple_new("auth0",
+                                           QAUTHZ_SIMPLE_POLICY_DENY,
+                                           &error_abort);
+
+    qauthz_simple_append_rule(auth, "fred", QAUTHZ_SIMPLE_POLICY_ALLOW,
+                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
+    qauthz_simple_append_rule(auth, "bob", QAUTHZ_SIMPLE_POLICY_ALLOW,
+                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
+    qauthz_simple_append_rule(auth, "dan", QAUTHZ_SIMPLE_POLICY_DENY,
+                              QAUTHZ_SIMPLE_FORMAT_EXACT, &error_abort);
+#ifdef CONFIG_FNMATCH
+    qauthz_simple_append_rule(auth, "dan*", QAUTHZ_SIMPLE_POLICY_ALLOW,
+                              QAUTHZ_SIMPLE_FORMAT_GLOB, &error_abort);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort));
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort));
+#else
+    g_assert(qauthz_simple_append_rule(auth, "dan*",
+                                       QAUTHZ_SIMPLE_POLICY_ALLOW,
+                                       QAUTHZ_SIMPLE_FORMAT_GLOB,
+                                       &local_err) < 0);
+    g_assert(local_err != NULL);
+    error_free(local_err);
+#endif
+
+    object_unparent(OBJECT(auth));
+}
+
+static void test_authz_add_remove(void)
+{
+    QAuthZSimple *auth = qauthz_simple_new("auth0",
+                                           QAUTHZ_SIMPLE_POLICY_ALLOW,
+                                           &error_abort);
+
+    g_assert_cmpint(qauthz_simple_append_rule(auth, "fred",
+                                              QAUTHZ_SIMPLE_POLICY_ALLOW,
+                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
+                                              &error_abort),
+                    ==, 0);
+    g_assert_cmpint(qauthz_simple_append_rule(auth, "bob",
+                                              QAUTHZ_SIMPLE_POLICY_ALLOW,
+                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
+                                              &error_abort),
+                    ==, 1);
+    g_assert_cmpint(qauthz_simple_append_rule(auth, "dan",
+                                              QAUTHZ_SIMPLE_POLICY_DENY,
+                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
+                                              &error_abort),
+                    ==, 2);
+    g_assert_cmpint(qauthz_simple_append_rule(auth, "frank",
+                                              QAUTHZ_SIMPLE_POLICY_DENY,
+                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
+                                              &error_abort),
+                    ==, 3);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+    g_assert_cmpint(qauthz_simple_delete_rule(auth, "dan"),
+                    ==, 2);
+
+    g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+    g_assert_cmpint(qauthz_simple_insert_rule(auth, "dan",
+                                              QAUTHZ_SIMPLE_POLICY_DENY,
+                                              QAUTHZ_SIMPLE_FORMAT_EXACT,
+                                              2,
+                                              &error_abort),
+                    ==, 2);
+
+    g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort));
+
+    object_unparent(OBJECT(auth));
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    module_call_init(MODULE_INIT_QOM);
+
+    g_test_add_func("/auth/simple/default/deny", test_authz_default_deny);
+    g_test_add_func("/auth/simple/default/allow", test_authz_default_allow);
+    g_test_add_func("/auth/simple/explicit/deny", test_authz_explicit_deny);
+    g_test_add_func("/auth/simple/explicit/allow", test_authz_explicit_allow);
+    g_test_add_func("/auth/simple/complex", test_authz_complex);
+    g_test_add_func("/auth/simple/add-remove", test_authz_add_remove);
+
+    return g_test_run();
+}
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 0d83583..c0f89de 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -36,3 +36,4 @@  util-obj-y += qdist.o
 util-obj-y += qht.o
 
 util-qom-obj-y += authz.o
+util-qom-obj-y += authz-simple.o
diff --git a/util/authz-simple.c b/util/authz-simple.c
new file mode 100644
index 0000000..a26177f
--- /dev/null
+++ b/util/authz-simple.c
@@ -0,0 +1,314 @@ 
+/*
+ * QEMU simple authorization driver
+ *
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/authz-simple.h"
+#include "qom/object_interfaces.h"
+#include "qapi-visit.h"
+
+#ifdef CONFIG_FNMATCH
+#include <fnmatch.h>
+#endif
+
+static bool qauthz_simple_is_allowed(QAuthZ *authz,
+                                     const char *identity,
+                                     Error **errp)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
+    QAuthZSimpleRuleList *rules = sauthz->rules;
+
+    while (rules) {
+        QAuthZSimpleRule *rule = rules->value;
+        QAuthZSimpleFormat format = rule->has_format ? rule->format :
+            QAUTHZ_SIMPLE_FORMAT_EXACT;
+
+        switch (format) {
+        case QAUTHZ_SIMPLE_FORMAT_EXACT:
+            if (strcmp(rule->match, identity) == 0) {
+                return rule->policy == QAUTHZ_SIMPLE_POLICY_ALLOW;
+            }
+            break;
+#ifdef CONFIG_FNMATCH
+        case QAUTHZ_SIMPLE_FORMAT_GLOB:
+            if (fnmatch(rule->match, identity, 0) == 0) {
+                return rule->policy == QAUTHZ_SIMPLE_POLICY_ALLOW;
+            }
+            break;
+#else
+            return false;
+#endif
+        default:
+            return false;
+        }
+        rules = rules->next;
+    }
+
+    return sauthz->policy == QAUTHZ_SIMPLE_POLICY_ALLOW;
+}
+
+
+static void
+qauthz_simple_prop_set_policy(Object *obj,
+                              int value,
+                              Error **errp G_GNUC_UNUSED)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    sauthz->policy = value;
+}
+
+
+static int
+qauthz_simple_prop_get_policy(Object *obj,
+                              Error **errp G_GNUC_UNUSED)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    return sauthz->policy;
+}
+
+
+static void
+qauthz_simple_prop_get_rules(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    visit_type_QAuthZSimpleRuleList(v, name, &sauthz->rules, errp);
+}
+
+static void
+qauthz_simple_prop_set_rules(Object *obj, Visitor *v, const char *name,
+                             void *opaque, Error **errp)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+    QAuthZSimpleRuleList *oldrules;
+#ifndef CONFIG_FNMATCH
+    QAuthZSimpleRuleList *rules;
+#endif
+
+    oldrules = sauthz->rules;
+    visit_type_QAuthZSimpleRuleList(v, name, &sauthz->rules, errp);
+
+#ifndef CONFIG_FNMATCH
+    rules = sauthz->rules;
+    while (rules) {
+        QAuthZSimpleRule *rule = rules->value;
+        if (rule->has_format &&
+            rule->format == QAUTHZ_SIMPLE_FORMAT_GLOB) {
+            error_setg(errp, "Glob format not supported on this platform");
+            qapi_free_QAuthZSimpleRuleList(sauthz->rules);
+            sauthz->rules = oldrules;
+            return;
+        }
+    }
+#endif
+
+    qapi_free_QAuthZSimpleRuleList(oldrules);
+}
+
+
+static void
+qauthz_simple_complete(UserCreatable *uc, Error **errp)
+{
+}
+
+
+static void
+qauthz_simple_finalize(Object *obj)
+{
+    QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
+
+    qapi_free_QAuthZSimpleRuleList(sauthz->rules);
+}
+
+
+static void
+qauthz_simple_class_init(ObjectClass *oc, void *data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+    ucc->complete = qauthz_simple_complete;
+    authz->is_allowed = qauthz_simple_is_allowed;
+
+    object_class_property_add_enum(oc, "policy",
+                                   "QAuthZSimplePolicy",
+                                   QAuthZSimplePolicy_lookup,
+                                   qauthz_simple_prop_get_policy,
+                                   qauthz_simple_prop_set_policy,
+                                   NULL);
+
+    object_class_property_add(oc, "rules", "QAuthZSimpleRule",
+                              qauthz_simple_prop_get_rules,
+                              qauthz_simple_prop_set_rules,
+                              NULL, NULL, NULL);
+}
+
+
+QAuthZSimple *qauthz_simple_new(const char *id,
+                                QAuthZSimplePolicy policy,
+                                Error **errp)
+{
+    return QAUTHZ_SIMPLE(
+        object_new_with_props(TYPE_QAUTHZ_SIMPLE,
+                              object_get_objects_root(),
+                              id, errp,
+                              "policy", QAuthZSimplePolicy_lookup[policy],
+                              NULL));
+}
+
+
+ssize_t qauthz_simple_append_rule(QAuthZSimple *auth,
+                                  const char *match,
+                                  QAuthZSimplePolicy policy,
+                                  QAuthZSimpleFormat format,
+                                  Error **errp)
+{
+    QAuthZSimpleRule *rule;
+    QAuthZSimpleRuleList *rules, *tmp;
+    size_t i = 0;
+
+#ifndef CONFIG_FNMATCH
+    if (format == QAUTHZ_SIMPLE_FORMAT_GLOB) {
+        error_setg(errp, "Glob format not supported on this platform");
+        return -1;
+    }
+#endif
+
+    rule = g_new0(QAuthZSimpleRule, 1);
+    rule->policy = policy;
+    rule->match = g_strdup(match);
+    rule->format = format;
+    rule->has_format = true;
+
+    tmp = g_new0(QAuthZSimpleRuleList, 1);
+    tmp->value = rule;
+
+    rules = auth->rules;
+    if (rules) {
+        while (rules->next) {
+            i++;
+            rules = rules->next;
+        }
+        rules->next = tmp;
+        return i + 1;
+    } else {
+        auth->rules = tmp;
+        return 0;
+    }
+}
+
+
+ssize_t qauthz_simple_insert_rule(QAuthZSimple *auth,
+                                  const char *match,
+                                  QAuthZSimplePolicy policy,
+                                  QAuthZSimpleFormat format,
+                                  size_t index,
+                                  Error **errp)
+{
+    QAuthZSimpleRule *rule;
+    QAuthZSimpleRuleList *rules, *tmp;
+    size_t i = 0;
+
+#ifndef CONFIG_FNMATCH
+    if (format == QAUTHZ_SIMPLE_FORMAT_GLOB) {
+        error_setg(errp, "Glob format not supported on this platform");
+        return -1;
+    }
+#endif
+
+    rule = g_new0(QAuthZSimpleRule, 1);
+    rule->policy = policy;
+    rule->match = g_strdup(match);
+    rule->format = format;
+    rule->has_format = true;
+
+    tmp = g_new0(QAuthZSimpleRuleList, 1);
+    tmp->value = rule;
+
+    rules = auth->rules;
+    if (rules && index > 0) {
+        while (rules->next && i < (index - 1)) {
+            i++;
+            rules = rules->next;
+        }
+        tmp->next = rules->next;
+        rules->next = tmp;
+        return i + 1;
+    } else {
+        tmp->next = auth->rules;
+        auth->rules = tmp;
+        return 0;
+    }
+}
+
+
+ssize_t qauthz_simple_delete_rule(QAuthZSimple *auth, const char *match)
+{
+    QAuthZSimpleRule *rule;
+    QAuthZSimpleRuleList *rules, *prev;
+    size_t i = 0;
+
+    prev = NULL;
+    rules = auth->rules;
+    while (rules) {
+        rule = rules->value;
+        if (g_str_equal(rule->match, match)) {
+            if (prev) {
+                prev->next = rules->next;
+            } else {
+                auth->rules = rules->next;
+            }
+            rules->next = NULL;
+            qapi_free_QAuthZSimpleRuleList(rules);
+            return i;
+        }
+        prev = rules;
+        rules = rules->next;
+        i++;
+    }
+
+    return -1;
+}
+
+
+static const TypeInfo qauthz_simple_info = {
+    .parent = TYPE_QAUTHZ,
+    .name = TYPE_QAUTHZ_SIMPLE,
+    .instance_size = sizeof(QAuthZSimple),
+    .instance_finalize = qauthz_simple_finalize,
+    .class_size = sizeof(QAuthZSimpleClass),
+    .class_init = qauthz_simple_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+
+static void
+qauthz_simple_register_types(void)
+{
+    type_register_static(&qauthz_simple_info);
+}
+
+
+type_init(qauthz_simple_register_types);