155d86984SDaniel P. Berrangé /*
255d86984SDaniel P. Berrangé * QEMU access control list file authorization driver
355d86984SDaniel P. Berrangé *
455d86984SDaniel P. Berrangé * Copyright (c) 2018 Red Hat, Inc.
555d86984SDaniel P. Berrangé *
655d86984SDaniel P. Berrangé * This library is free software; you can redistribute it and/or
755d86984SDaniel P. Berrangé * modify it under the terms of the GNU Lesser General Public
855d86984SDaniel P. Berrangé * License as published by the Free Software Foundation; either
9036a80cdSChetan Pant * version 2.1 of the License, or (at your option) any later version.
1055d86984SDaniel P. Berrangé *
1155d86984SDaniel P. Berrangé * This library is distributed in the hope that it will be useful,
1255d86984SDaniel P. Berrangé * but WITHOUT ANY WARRANTY; without even the implied warranty of
1355d86984SDaniel P. Berrangé * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1455d86984SDaniel P. Berrangé * Lesser General Public License for more details.
1555d86984SDaniel P. Berrangé *
1655d86984SDaniel P. Berrangé * You should have received a copy of the GNU Lesser General Public
1755d86984SDaniel P. Berrangé * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1855d86984SDaniel P. Berrangé *
1955d86984SDaniel P. Berrangé */
2055d86984SDaniel P. Berrangé
2155d86984SDaniel P. Berrangé #include "qemu/osdep.h"
2255d86984SDaniel P. Berrangé #include "authz/listfile.h"
2345b1f68cSMarkus Armbruster #include "trace.h"
2455d86984SDaniel P. Berrangé #include "qemu/error-report.h"
2555d86984SDaniel P. Berrangé #include "qemu/main-loop.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
2755d86984SDaniel P. Berrangé #include "qemu/sockets.h"
2855d86984SDaniel P. Berrangé #include "qemu/filemonitor.h"
2955d86984SDaniel P. Berrangé #include "qom/object_interfaces.h"
3055d86984SDaniel P. Berrangé #include "qapi/qapi-visit-authz.h"
31407bc4bfSDaniel P. Berrangé #include "qobject/qjson.h"
32407bc4bfSDaniel P. Berrangé #include "qobject/qobject.h"
3355d86984SDaniel P. Berrangé #include "qapi/qobject-input-visitor.h"
3455d86984SDaniel P. Berrangé
3555d86984SDaniel P. Berrangé
3655d86984SDaniel P. Berrangé static bool
qauthz_list_file_is_allowed(QAuthZ * authz,const char * identity,Error ** errp)3755d86984SDaniel P. Berrangé qauthz_list_file_is_allowed(QAuthZ *authz,
3855d86984SDaniel P. Berrangé const char *identity,
3955d86984SDaniel P. Berrangé Error **errp)
4055d86984SDaniel P. Berrangé {
4155d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz);
4255d86984SDaniel P. Berrangé if (fauthz->list) {
4355d86984SDaniel P. Berrangé return qauthz_is_allowed(fauthz->list, identity, errp);
4455d86984SDaniel P. Berrangé }
4555d86984SDaniel P. Berrangé
4655d86984SDaniel P. Berrangé return false;
4755d86984SDaniel P. Berrangé }
4855d86984SDaniel P. Berrangé
4955d86984SDaniel P. Berrangé
5055d86984SDaniel P. Berrangé static QAuthZ *
qauthz_list_file_load(QAuthZListFile * fauthz,Error ** errp)5155d86984SDaniel P. Berrangé qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
5255d86984SDaniel P. Berrangé {
5355d86984SDaniel P. Berrangé GError *err = NULL;
5455d86984SDaniel P. Berrangé gchar *content = NULL;
5555d86984SDaniel P. Berrangé gsize len;
5655d86984SDaniel P. Berrangé QObject *obj = NULL;
5755d86984SDaniel P. Berrangé QDict *pdict;
5855d86984SDaniel P. Berrangé Visitor *v = NULL;
5955d86984SDaniel P. Berrangé QAuthZ *ret = NULL;
6055d86984SDaniel P. Berrangé
6155d86984SDaniel P. Berrangé trace_qauthz_list_file_load(fauthz, fauthz->filename);
6255d86984SDaniel P. Berrangé if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
6355d86984SDaniel P. Berrangé error_setg(errp, "Unable to read '%s': %s",
6455d86984SDaniel P. Berrangé fauthz->filename, err->message);
6555d86984SDaniel P. Berrangé goto cleanup;
6655d86984SDaniel P. Berrangé }
6755d86984SDaniel P. Berrangé
6855d86984SDaniel P. Berrangé obj = qobject_from_json(content, errp);
6955d86984SDaniel P. Berrangé if (!obj) {
7055d86984SDaniel P. Berrangé goto cleanup;
7155d86984SDaniel P. Berrangé }
7255d86984SDaniel P. Berrangé
7355d86984SDaniel P. Berrangé pdict = qobject_to(QDict, obj);
7455d86984SDaniel P. Berrangé if (!pdict) {
758e26ae7bSMarkus Armbruster error_setg(errp, "File '%s' must contain a JSON object",
768e26ae7bSMarkus Armbruster fauthz->filename);
7755d86984SDaniel P. Berrangé goto cleanup;
7855d86984SDaniel P. Berrangé }
7955d86984SDaniel P. Berrangé
8055d86984SDaniel P. Berrangé v = qobject_input_visitor_new(obj);
8155d86984SDaniel P. Berrangé
8255d86984SDaniel P. Berrangé ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
8355d86984SDaniel P. Berrangé NULL, pdict, v, errp);
8455d86984SDaniel P. Berrangé
8555d86984SDaniel P. Berrangé cleanup:
8655d86984SDaniel P. Berrangé visit_free(v);
8755d86984SDaniel P. Berrangé qobject_unref(obj);
8855d86984SDaniel P. Berrangé if (err) {
8955d86984SDaniel P. Berrangé g_error_free(err);
9055d86984SDaniel P. Berrangé }
9155d86984SDaniel P. Berrangé g_free(content);
9255d86984SDaniel P. Berrangé return ret;
9355d86984SDaniel P. Berrangé }
9455d86984SDaniel P. Berrangé
9555d86984SDaniel P. Berrangé
9655d86984SDaniel P. Berrangé static void
qauthz_list_file_event(int64_t wd G_GNUC_UNUSED,QFileMonitorEvent ev G_GNUC_UNUSED,const char * name G_GNUC_UNUSED,void * opaque)97b4682a63SDaniel P. Berrangé qauthz_list_file_event(int64_t wd G_GNUC_UNUSED,
9855d86984SDaniel P. Berrangé QFileMonitorEvent ev G_GNUC_UNUSED,
9955d86984SDaniel P. Berrangé const char *name G_GNUC_UNUSED,
10055d86984SDaniel P. Berrangé void *opaque)
10155d86984SDaniel P. Berrangé {
10255d86984SDaniel P. Berrangé QAuthZListFile *fauthz = opaque;
10355d86984SDaniel P. Berrangé Error *err = NULL;
10455d86984SDaniel P. Berrangé
10555d86984SDaniel P. Berrangé if (ev != QFILE_MONITOR_EVENT_MODIFIED &&
10655d86984SDaniel P. Berrangé ev != QFILE_MONITOR_EVENT_CREATED) {
10755d86984SDaniel P. Berrangé return;
10855d86984SDaniel P. Berrangé }
10955d86984SDaniel P. Berrangé
11055d86984SDaniel P. Berrangé object_unref(OBJECT(fauthz->list));
11155d86984SDaniel P. Berrangé fauthz->list = qauthz_list_file_load(fauthz, &err);
11255d86984SDaniel P. Berrangé trace_qauthz_list_file_refresh(fauthz,
11355d86984SDaniel P. Berrangé fauthz->filename, fauthz->list ? 1 : 0);
11455d86984SDaniel P. Berrangé if (!fauthz->list) {
11555d86984SDaniel P. Berrangé error_report_err(err);
11655d86984SDaniel P. Berrangé }
11755d86984SDaniel P. Berrangé }
11855d86984SDaniel P. Berrangé
11955d86984SDaniel P. Berrangé static void
qauthz_list_file_complete(UserCreatable * uc,Error ** errp)12055d86984SDaniel P. Berrangé qauthz_list_file_complete(UserCreatable *uc, Error **errp)
12155d86984SDaniel P. Berrangé {
12255d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc);
12355d86984SDaniel P. Berrangé gchar *dir = NULL, *file = NULL;
12455d86984SDaniel P. Berrangé
12514b39485SEduardo Habkost if (!fauthz->filename) {
12614b39485SEduardo Habkost error_setg(errp, "filename not provided");
12714b39485SEduardo Habkost return;
12814b39485SEduardo Habkost }
12914b39485SEduardo Habkost
13055d86984SDaniel P. Berrangé fauthz->list = qauthz_list_file_load(fauthz, errp);
1312b37e9f8SMarkus Armbruster if (!fauthz->list) {
1322b37e9f8SMarkus Armbruster return;
1332b37e9f8SMarkus Armbruster }
13455d86984SDaniel P. Berrangé
13555d86984SDaniel P. Berrangé if (!fauthz->refresh) {
13655d86984SDaniel P. Berrangé return;
13755d86984SDaniel P. Berrangé }
13855d86984SDaniel P. Berrangé
13955d86984SDaniel P. Berrangé fauthz->file_monitor = qemu_file_monitor_new(errp);
14055d86984SDaniel P. Berrangé if (!fauthz->file_monitor) {
14155d86984SDaniel P. Berrangé return;
14255d86984SDaniel P. Berrangé }
14355d86984SDaniel P. Berrangé
14455d86984SDaniel P. Berrangé dir = g_path_get_dirname(fauthz->filename);
14555d86984SDaniel P. Berrangé if (g_str_equal(dir, ".")) {
14655d86984SDaniel P. Berrangé error_setg(errp, "Filename must be an absolute path");
14755d86984SDaniel P. Berrangé goto cleanup;
14855d86984SDaniel P. Berrangé }
14955d86984SDaniel P. Berrangé file = g_path_get_basename(fauthz->filename);
15055d86984SDaniel P. Berrangé if (g_str_equal(file, ".")) {
15155d86984SDaniel P. Berrangé error_setg(errp, "Path has no trailing filename component");
15255d86984SDaniel P. Berrangé goto cleanup;
15355d86984SDaniel P. Berrangé }
15455d86984SDaniel P. Berrangé
15555d86984SDaniel P. Berrangé fauthz->file_watch = qemu_file_monitor_add_watch(
15655d86984SDaniel P. Berrangé fauthz->file_monitor, dir, file,
15755d86984SDaniel P. Berrangé qauthz_list_file_event, fauthz, errp);
15855d86984SDaniel P. Berrangé if (fauthz->file_watch < 0) {
15955d86984SDaniel P. Berrangé goto cleanup;
16055d86984SDaniel P. Berrangé }
16155d86984SDaniel P. Berrangé
16255d86984SDaniel P. Berrangé cleanup:
16355d86984SDaniel P. Berrangé g_free(file);
16455d86984SDaniel P. Berrangé g_free(dir);
16555d86984SDaniel P. Berrangé }
16655d86984SDaniel P. Berrangé
16755d86984SDaniel P. Berrangé
16855d86984SDaniel P. Berrangé static void
qauthz_list_file_prop_set_filename(Object * obj,const char * value,Error ** errp G_GNUC_UNUSED)16955d86984SDaniel P. Berrangé qauthz_list_file_prop_set_filename(Object *obj,
17055d86984SDaniel P. Berrangé const char *value,
17155d86984SDaniel P. Berrangé Error **errp G_GNUC_UNUSED)
17255d86984SDaniel P. Berrangé {
17355d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
17455d86984SDaniel P. Berrangé
17555d86984SDaniel P. Berrangé g_free(fauthz->filename);
17655d86984SDaniel P. Berrangé fauthz->filename = g_strdup(value);
17755d86984SDaniel P. Berrangé }
17855d86984SDaniel P. Berrangé
17955d86984SDaniel P. Berrangé
18055d86984SDaniel P. Berrangé static char *
qauthz_list_file_prop_get_filename(Object * obj,Error ** errp G_GNUC_UNUSED)18155d86984SDaniel P. Berrangé qauthz_list_file_prop_get_filename(Object *obj,
18255d86984SDaniel P. Berrangé Error **errp G_GNUC_UNUSED)
18355d86984SDaniel P. Berrangé {
18455d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
18555d86984SDaniel P. Berrangé
18655d86984SDaniel P. Berrangé return g_strdup(fauthz->filename);
18755d86984SDaniel P. Berrangé }
18855d86984SDaniel P. Berrangé
18955d86984SDaniel P. Berrangé
19055d86984SDaniel P. Berrangé static void
qauthz_list_file_prop_set_refresh(Object * obj,bool value,Error ** errp G_GNUC_UNUSED)19155d86984SDaniel P. Berrangé qauthz_list_file_prop_set_refresh(Object *obj,
19255d86984SDaniel P. Berrangé bool value,
19355d86984SDaniel P. Berrangé Error **errp G_GNUC_UNUSED)
19455d86984SDaniel P. Berrangé {
19555d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
19655d86984SDaniel P. Berrangé
19755d86984SDaniel P. Berrangé fauthz->refresh = value;
19855d86984SDaniel P. Berrangé }
19955d86984SDaniel P. Berrangé
20055d86984SDaniel P. Berrangé
20155d86984SDaniel P. Berrangé static bool
qauthz_list_file_prop_get_refresh(Object * obj,Error ** errp G_GNUC_UNUSED)20255d86984SDaniel P. Berrangé qauthz_list_file_prop_get_refresh(Object *obj,
20355d86984SDaniel P. Berrangé Error **errp G_GNUC_UNUSED)
20455d86984SDaniel P. Berrangé {
20555d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
20655d86984SDaniel P. Berrangé
20755d86984SDaniel P. Berrangé return fauthz->refresh;
20855d86984SDaniel P. Berrangé }
20955d86984SDaniel P. Berrangé
21055d86984SDaniel P. Berrangé
21155d86984SDaniel P. Berrangé static void
qauthz_list_file_finalize(Object * obj)21255d86984SDaniel P. Berrangé qauthz_list_file_finalize(Object *obj)
21355d86984SDaniel P. Berrangé {
21455d86984SDaniel P. Berrangé QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
21555d86984SDaniel P. Berrangé
21655d86984SDaniel P. Berrangé object_unref(OBJECT(fauthz->list));
21755d86984SDaniel P. Berrangé g_free(fauthz->filename);
21855d86984SDaniel P. Berrangé qemu_file_monitor_free(fauthz->file_monitor);
21955d86984SDaniel P. Berrangé }
22055d86984SDaniel P. Berrangé
22155d86984SDaniel P. Berrangé
22255d86984SDaniel P. Berrangé static void
qauthz_list_file_class_init(ObjectClass * oc,const void * data)22312d1a768SPhilippe Mathieu-Daudé qauthz_list_file_class_init(ObjectClass *oc, const void *data)
22455d86984SDaniel P. Berrangé {
22555d86984SDaniel P. Berrangé UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
22655d86984SDaniel P. Berrangé QAuthZClass *authz = QAUTHZ_CLASS(oc);
22755d86984SDaniel P. Berrangé
22855d86984SDaniel P. Berrangé ucc->complete = qauthz_list_file_complete;
22955d86984SDaniel P. Berrangé
23055d86984SDaniel P. Berrangé object_class_property_add_str(oc, "filename",
23155d86984SDaniel P. Berrangé qauthz_list_file_prop_get_filename,
232d2623129SMarkus Armbruster qauthz_list_file_prop_set_filename);
23355d86984SDaniel P. Berrangé object_class_property_add_bool(oc, "refresh",
23455d86984SDaniel P. Berrangé qauthz_list_file_prop_get_refresh,
235d2623129SMarkus Armbruster qauthz_list_file_prop_set_refresh);
23655d86984SDaniel P. Berrangé
23755d86984SDaniel P. Berrangé authz->is_allowed = qauthz_list_file_is_allowed;
23855d86984SDaniel P. Berrangé }
23955d86984SDaniel P. Berrangé
24055d86984SDaniel P. Berrangé
24155d86984SDaniel P. Berrangé static void
qauthz_list_file_init(Object * obj)24255d86984SDaniel P. Berrangé qauthz_list_file_init(Object *obj)
24355d86984SDaniel P. Berrangé {
24455d86984SDaniel P. Berrangé QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj);
24555d86984SDaniel P. Berrangé
24655d86984SDaniel P. Berrangé authz->file_watch = -1;
24755d86984SDaniel P. Berrangé #ifdef CONFIG_INOTIFY1
248834e8bf1SJafar Abdi authz->refresh = true;
24955d86984SDaniel P. Berrangé #endif
25055d86984SDaniel P. Berrangé }
25155d86984SDaniel P. Berrangé
25255d86984SDaniel P. Berrangé
qauthz_list_file_new(const char * id,const char * filename,bool refresh,Error ** errp)25355d86984SDaniel P. Berrangé QAuthZListFile *qauthz_list_file_new(const char *id,
25455d86984SDaniel P. Berrangé const char *filename,
25555d86984SDaniel P. Berrangé bool refresh,
25655d86984SDaniel P. Berrangé Error **errp)
25755d86984SDaniel P. Berrangé {
25855d86984SDaniel P. Berrangé return QAUTHZ_LIST_FILE(
25955d86984SDaniel P. Berrangé object_new_with_props(TYPE_QAUTHZ_LIST_FILE,
26055d86984SDaniel P. Berrangé object_get_objects_root(),
26155d86984SDaniel P. Berrangé id, errp,
26255d86984SDaniel P. Berrangé "filename", filename,
26355d86984SDaniel P. Berrangé "refresh", refresh ? "yes" : "no",
26455d86984SDaniel P. Berrangé NULL));
26555d86984SDaniel P. Berrangé }
26655d86984SDaniel P. Berrangé
26755d86984SDaniel P. Berrangé
26855d86984SDaniel P. Berrangé static const TypeInfo qauthz_list_file_info = {
26955d86984SDaniel P. Berrangé .parent = TYPE_QAUTHZ,
27055d86984SDaniel P. Berrangé .name = TYPE_QAUTHZ_LIST_FILE,
27155d86984SDaniel P. Berrangé .instance_init = qauthz_list_file_init,
27255d86984SDaniel P. Berrangé .instance_size = sizeof(QAuthZListFile),
27355d86984SDaniel P. Berrangé .instance_finalize = qauthz_list_file_finalize,
27455d86984SDaniel P. Berrangé .class_init = qauthz_list_file_class_init,
275*2cd09e47SPhilippe Mathieu-Daudé .interfaces = (const InterfaceInfo[]) {
27655d86984SDaniel P. Berrangé { TYPE_USER_CREATABLE },
27755d86984SDaniel P. Berrangé { }
27855d86984SDaniel P. Berrangé }
27955d86984SDaniel P. Berrangé };
28055d86984SDaniel P. Berrangé
28155d86984SDaniel P. Berrangé
28255d86984SDaniel P. Berrangé static void
qauthz_list_file_register_types(void)28355d86984SDaniel P. Berrangé qauthz_list_file_register_types(void)
28455d86984SDaniel P. Berrangé {
28555d86984SDaniel P. Berrangé type_register_static(&qauthz_list_file_info);
28655d86984SDaniel P. Berrangé }
28755d86984SDaniel P. Berrangé
28855d86984SDaniel P. Berrangé
28955d86984SDaniel P. Berrangé type_init(qauthz_list_file_register_types);
290