Message ID | 1465920443-22804-7-git-send-email-berrange@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 --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);
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