qemu/authz/listfile.c
<<
>>
Prefs
   1/*
   2 * QEMU access control list file authorization driver
   3 *
   4 * Copyright (c) 2018 Red Hat, Inc.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2.1 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "authz/listfile.h"
  23#include "trace.h"
  24#include "qemu/error-report.h"
  25#include "qemu/main-loop.h"
  26#include "qemu/module.h"
  27#include "qemu/sockets.h"
  28#include "qemu/filemonitor.h"
  29#include "qom/object_interfaces.h"
  30#include "qapi/qapi-visit-authz.h"
  31#include "qapi/qmp/qjson.h"
  32#include "qapi/qmp/qobject.h"
  33#include "qapi/qmp/qerror.h"
  34#include "qapi/qobject-input-visitor.h"
  35
  36
  37static bool
  38qauthz_list_file_is_allowed(QAuthZ *authz,
  39                            const char *identity,
  40                            Error **errp)
  41{
  42    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz);
  43    if (fauthz->list) {
  44        return qauthz_is_allowed(fauthz->list, identity, errp);
  45    }
  46
  47    return false;
  48}
  49
  50
  51static QAuthZ *
  52qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
  53{
  54    GError *err = NULL;
  55    gchar *content = NULL;
  56    gsize len;
  57    QObject *obj = NULL;
  58    QDict *pdict;
  59    Visitor *v = NULL;
  60    QAuthZ *ret = NULL;
  61
  62    trace_qauthz_list_file_load(fauthz, fauthz->filename);
  63    if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
  64        error_setg(errp, "Unable to read '%s': %s",
  65                   fauthz->filename, err->message);
  66        goto cleanup;
  67    }
  68
  69    obj = qobject_from_json(content, errp);
  70    if (!obj) {
  71        goto cleanup;
  72    }
  73
  74    pdict = qobject_to(QDict, obj);
  75    if (!pdict) {
  76        error_setg(errp, "File '%s' must contain a JSON object",
  77                   fauthz->filename);
  78        goto cleanup;
  79    }
  80
  81    v = qobject_input_visitor_new(obj);
  82
  83    ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
  84                                            NULL, pdict, v, errp);
  85
  86 cleanup:
  87    visit_free(v);
  88    qobject_unref(obj);
  89    if (err) {
  90        g_error_free(err);
  91    }
  92    g_free(content);
  93    return ret;
  94}
  95
  96
  97static void
  98qauthz_list_file_event(int64_t wd G_GNUC_UNUSED,
  99                       QFileMonitorEvent ev G_GNUC_UNUSED,
 100                       const char *name G_GNUC_UNUSED,
 101                       void *opaque)
 102{
 103    QAuthZListFile *fauthz = opaque;
 104    Error *err = NULL;
 105
 106    if (ev != QFILE_MONITOR_EVENT_MODIFIED &&
 107        ev != QFILE_MONITOR_EVENT_CREATED) {
 108        return;
 109    }
 110
 111    object_unref(OBJECT(fauthz->list));
 112    fauthz->list = qauthz_list_file_load(fauthz, &err);
 113    trace_qauthz_list_file_refresh(fauthz,
 114                                   fauthz->filename, fauthz->list ? 1 : 0);
 115    if (!fauthz->list) {
 116        error_report_err(err);
 117    }
 118}
 119
 120static void
 121qauthz_list_file_complete(UserCreatable *uc, Error **errp)
 122{
 123    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc);
 124    gchar *dir = NULL, *file = NULL;
 125
 126    if (!fauthz->filename) {
 127        error_setg(errp, "filename not provided");
 128        return;
 129    }
 130
 131    fauthz->list = qauthz_list_file_load(fauthz, errp);
 132    if (!fauthz->list) {
 133        return;
 134    }
 135
 136    if (!fauthz->refresh) {
 137        return;
 138    }
 139
 140    fauthz->file_monitor = qemu_file_monitor_new(errp);
 141    if (!fauthz->file_monitor) {
 142        return;
 143    }
 144
 145    dir = g_path_get_dirname(fauthz->filename);
 146    if (g_str_equal(dir, ".")) {
 147        error_setg(errp, "Filename must be an absolute path");
 148        goto cleanup;
 149    }
 150    file = g_path_get_basename(fauthz->filename);
 151    if (g_str_equal(file, ".")) {
 152        error_setg(errp, "Path has no trailing filename component");
 153        goto cleanup;
 154    }
 155
 156    fauthz->file_watch = qemu_file_monitor_add_watch(
 157        fauthz->file_monitor, dir, file,
 158        qauthz_list_file_event, fauthz, errp);
 159    if (fauthz->file_watch < 0) {
 160        goto cleanup;
 161    }
 162
 163 cleanup:
 164    g_free(file);
 165    g_free(dir);
 166}
 167
 168
 169static void
 170qauthz_list_file_prop_set_filename(Object *obj,
 171                                   const char *value,
 172                                   Error **errp G_GNUC_UNUSED)
 173{
 174    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
 175
 176    g_free(fauthz->filename);
 177    fauthz->filename = g_strdup(value);
 178}
 179
 180
 181static char *
 182qauthz_list_file_prop_get_filename(Object *obj,
 183                                   Error **errp G_GNUC_UNUSED)
 184{
 185    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
 186
 187    return g_strdup(fauthz->filename);
 188}
 189
 190
 191static void
 192qauthz_list_file_prop_set_refresh(Object *obj,
 193                                  bool value,
 194                                  Error **errp G_GNUC_UNUSED)
 195{
 196    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
 197
 198    fauthz->refresh = value;
 199}
 200
 201
 202static bool
 203qauthz_list_file_prop_get_refresh(Object *obj,
 204                                  Error **errp G_GNUC_UNUSED)
 205{
 206    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
 207
 208    return fauthz->refresh;
 209}
 210
 211
 212static void
 213qauthz_list_file_finalize(Object *obj)
 214{
 215    QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
 216
 217    object_unref(OBJECT(fauthz->list));
 218    g_free(fauthz->filename);
 219    qemu_file_monitor_free(fauthz->file_monitor);
 220}
 221
 222
 223static void
 224qauthz_list_file_class_init(ObjectClass *oc, void *data)
 225{
 226    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
 227    QAuthZClass *authz = QAUTHZ_CLASS(oc);
 228
 229    ucc->complete = qauthz_list_file_complete;
 230
 231    object_class_property_add_str(oc, "filename",
 232                                  qauthz_list_file_prop_get_filename,
 233                                  qauthz_list_file_prop_set_filename);
 234    object_class_property_add_bool(oc, "refresh",
 235                                   qauthz_list_file_prop_get_refresh,
 236                                   qauthz_list_file_prop_set_refresh);
 237
 238    authz->is_allowed = qauthz_list_file_is_allowed;
 239}
 240
 241
 242static void
 243qauthz_list_file_init(Object *obj)
 244{
 245    QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj);
 246
 247    authz->file_watch = -1;
 248#ifdef CONFIG_INOTIFY1
 249    authz->refresh = true;
 250#endif
 251}
 252
 253
 254QAuthZListFile *qauthz_list_file_new(const char *id,
 255                                     const char *filename,
 256                                     bool refresh,
 257                                     Error **errp)
 258{
 259    return QAUTHZ_LIST_FILE(
 260        object_new_with_props(TYPE_QAUTHZ_LIST_FILE,
 261                              object_get_objects_root(),
 262                              id, errp,
 263                              "filename", filename,
 264                              "refresh", refresh ? "yes" : "no",
 265                              NULL));
 266}
 267
 268
 269static const TypeInfo qauthz_list_file_info = {
 270    .parent = TYPE_QAUTHZ,
 271    .name = TYPE_QAUTHZ_LIST_FILE,
 272    .instance_init = qauthz_list_file_init,
 273    .instance_size = sizeof(QAuthZListFile),
 274    .instance_finalize = qauthz_list_file_finalize,
 275    .class_init = qauthz_list_file_class_init,
 276    .interfaces = (InterfaceInfo[]) {
 277        { TYPE_USER_CREATABLE },
 278        { }
 279    }
 280};
 281
 282
 283static void
 284qauthz_list_file_register_types(void)
 285{
 286    type_register_static(&qauthz_list_file_info);
 287}
 288
 289
 290type_init(qauthz_list_file_register_types);
 291