From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529079450483365.16716383883204; Fri, 15 Jun 2018 09:17:30 -0700 (PDT) Received: from localhost ([::1]:47915 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrPl-0004JW-IR for importer@patchew.org; Fri, 15 Jun 2018 12:17:29 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56435) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrd-00010I-Lq for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:16 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqra-0000U9-VH for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:13 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:58222 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqra-0000To-CO for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:10 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E7967F68AB; Fri, 15 Jun 2018 15:42:09 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id E01C010FFE7F; Fri, 15 Jun 2018 15:42:08 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:53 +0100 Message-Id: <20180615154203.11347-2-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:09 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:09 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 01/11] util: add helper APIs for dealing with inotify in portable manner X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" The inotify userspace API for reading events is quite horrible, so it is useful to wrap it in a more friendly API to avoid duplicating code across many users in QEMU. Wrapping it also allows introduction of a platform portability layer, so that we can add impls for non-Linux based equivalents in future. Signed-off-by: Daniel P. Berrang=C3=A9 --- MAINTAINERS | 6 + include/qemu/filemonitor.h | 117 ++++++++++++++ util/Makefile.objs | 1 + util/filemonitor.c | 315 +++++++++++++++++++++++++++++++++++++ util/trace-events | 9 ++ 5 files changed, 448 insertions(+) create mode 100644 include/qemu/filemonitor.h create mode 100644 util/filemonitor.c diff --git a/MAINTAINERS b/MAINTAINERS index 8a94517e9e..4a4f7c514b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1771,6 +1771,12 @@ F: include/qemu/sockets.h F: util/qemu-sockets.c F: qapi/sockets.json =20 +File monitor +M: Daniel P. Berrange +S: Odd fixes +F: util/filemonitor.c +F: include/qemu/filemonitor.h + Throttling infrastructure M: Alberto Garcia S: Supported diff --git a/include/qemu/filemonitor.h b/include/qemu/filemonitor.h new file mode 100644 index 0000000000..1326272f0a --- /dev/null +++ b/include/qemu/filemonitor.h @@ -0,0 +1,117 @@ +/* + * QEMU file monitor helper + * + * Copyright (c) 2018 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 . + * + */ + +#ifndef QEMU_FILE_MONITOR_H +#define QEMU_FILE_MONITOR_H + +#include "qemu-common.h" + + +typedef struct QFileMonitor QFileMonitor; + +typedef enum { + /* File has been created in a dir */ + QFILE_MONITOR_EVENT_CREATED, + /* File has been modified in a dir */ + QFILE_MONITOR_EVENT_MODIFIED, + /* File has been deleted in a dir */ + QFILE_MONITOR_EVENT_DELETED, + /* Dir is no longer being monitored (due to deletion) */ + QFILE_MONITOR_EVENT_IGNORED, +} QFileMonitorEvent; + + +/** + * QFileMonitorHandler: + * @id: id from qemu_file_monitor_add_watch() + * @event: the file change that occurred + * @filename: the name of the file affected + * @opaque: opaque data provided to qemu_file_monitor_add_watch() + * + * Invoked whenever a file changes. If @event is + * QFILE_MONITOR_EVENT_IGNORED, @filename will be + * empty. + * + */ +typedef void (*QFileMonitorHandler)(int id, + QFileMonitorEvent event, + const char *filename, + void *opaque); + +/** + * qemu_file_monitor_get_instance: + * @errp: pointer to a NULL-initialized error object + * + * Acquire a handle to the shared file monitoring object. + * + * This object does locking internally to enable it to be + * safe to use from multiple threads + * + * If the platform does not support file monitoring, an + * error will be reported. Likewise if file monitoring + * is supported, but cannot be initialized + * + * Currently this is implemented on Linux platforms with + * the inotify subsystem. + * + * Returns: the shared monitoring object, or NULL on error + */ +QFileMonitor *qemu_file_monitor_get_instance(Error **errp); + +/** + * qemu_file_monitor_add_watch: + * @mon: the file monitor context + * @dirpath: the directory whose contents to watch + * @filename: optional filename to filter on + * @cb: the function to invoke when @dirpath has changes + * @opaque: data to pass to @cb + * @errp: pointer to a NULL-initialized error object + * + * Register to receive notifications of changes + * in the directory @dirpath. All files in the + * directory will be monitored. If the caller is + * only interested in one specific file, @filename + * can be used to filter events. + * + * Returns: a positive integer watch ID, or -1 on error + */ +int qemu_file_monitor_add_watch(QFileMonitor *mon, + const char *dirpath, + const char *filename, + QFileMonitorHandler cb, + void *opaque, + Error **errp); + +/** + * qemu_file_monitor_remove_watch: + * @mon: the file monitor context + * @dirpath: the directory whose contents to unwatch + * @id: id of the watch to remove + * + * Removes the file monitoring watch @id, associated + * with the directory @dirpath. This must never be + * called from a QFileMonitorHandler callback, or a + * deadlock will result. + */ +void qemu_file_monitor_remove_watch(QFileMonitor *mon, + const char *dirpath, + int id); + +#endif /* QEMU_FILE_MONITOR_H */ diff --git a/util/Makefile.objs b/util/Makefile.objs index e1c3fed4dc..769d295a27 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -48,4 +48,5 @@ util-obj-y +=3D range.o util-obj-y +=3D stats64.o util-obj-y +=3D systemd.o util-obj-y +=3D iova-tree.o +util-obj-y +=3D filemonitor.o util-obj-$(CONFIG_LINUX) +=3D vfio-helpers.o diff --git a/util/filemonitor.c b/util/filemonitor.c new file mode 100644 index 0000000000..67d7aedbe0 --- /dev/null +++ b/util/filemonitor.c @@ -0,0 +1,315 @@ +/* + * QEMU file_monitor helper + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" +#include "qemu/filemonitor.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "trace.h" + +struct QFileMonitor { + QemuMutex lock; + int fd; + + GHashTable *dirs; /* dirname =3D> QFileMonitorDir */ + GHashTable *idmap; /* inotify ID =3D> dirname */ +}; + + +typedef struct { + int id; /* watch ID */ + char *filename; /* optional filter */ + QFileMonitorHandler cb; + void *opaque; +} QFileMonitorWatch; + + +typedef struct { + char *path; + int id; /* inotify ID */ + int nextid; /* watch ID counter */ + gsize nwatches; + QFileMonitorWatch *watches; +} QFileMonitorDir; + + +#ifdef CONFIG_INOTIFY1 +#include + +static void qemu_file_monitor_watch(void *arg) +{ + QFileMonitor *mon =3D arg; + char buf[4096] + __attribute__ ((aligned(__alignof__(struct inotify_event)))); + int used =3D 0; + int len =3D read(mon->fd, buf, sizeof(buf)); + + qemu_mutex_lock(&mon->lock); + + if (len < 0) { + if (errno !=3D EAGAIN) { + error_report("Failure monitoring inotify FD, disabling events"= ); + goto cleanup; + } + + /* no more events right now */ + goto cleanup; + } + + /* Loop over all events in the buffer */ + while (used < len) { + struct inotify_event *ev =3D + (struct inotify_event *)(buf + used); + const char *name =3D ev->len ? ev->name : ""; + QFileMonitorDir *dir =3D g_hash_table_lookup(mon->idmap, + GINT_TO_POINTER(ev->wd)= ); + uint32_t iev =3D ev->mask & + (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | + IN_MOVED_TO | IN_MOVED_FROM); + int qev; + gsize i; + + used +=3D sizeof(struct inotify_event) + ev->len; + + if (!dir) { + continue; + } + + /* + * During a rename operation, the old name gets + * IN_MOVED_FROM and the new name gets IN_MOVED_TO. + * To simplify life for callers, we turn these into + * DELETED and CREATED events + */ + switch (iev) { + case IN_CREATE: + case IN_MOVED_TO: + qev =3D QFILE_MONITOR_EVENT_CREATED; + break; + case IN_MODIFY: + qev =3D QFILE_MONITOR_EVENT_MODIFIED; + break; + case IN_DELETE: + case IN_MOVED_FROM: + qev =3D QFILE_MONITOR_EVENT_DELETED; + break; + case IN_IGNORED: + qev =3D QFILE_MONITOR_EVENT_IGNORED; + break; + default: + g_assert_not_reached(); + } + + trace_qemu_file_monitor_event(mon, dir->path, name, ev->mask, dir-= >id); + for (i =3D 0; i < dir->nwatches; i++) { + QFileMonitorWatch *watch =3D &dir->watches[i]; + + if (watch->filename =3D=3D NULL || + (name && g_str_equal(watch->filename, name))) { + trace_qemu_file_monitor_dispatch(mon, dir->path, name, + qev, watch->cb, + watch->opaque, watch->id); + watch->cb(watch->id, qev, name, watch->opaque); + } + } + } + + cleanup: + qemu_mutex_unlock(&mon->lock); +} + +static void +qemu_file_monitor_dir_free(void *data) +{ + QFileMonitorDir *dir =3D data; + + g_free(dir->watches); + g_free(dir); +} + +#endif + +static QFileMonitor * +qemu_file_monitor_new(Error **errp) +{ +#ifdef CONFIG_INOTIFY1 + int fd; + QFileMonitor *mon; + + fd =3D inotify_init1(IN_NONBLOCK); + if (fd < 0) { + error_setg_errno(errp, errno, + "Unable to initialize inotify"); + return NULL; + } + + mon =3D g_new0(QFileMonitor, 1); + qemu_mutex_init(&mon->lock); + mon->fd =3D fd; + + mon->dirs =3D g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + qemu_file_monitor_dir_free); + mon->idmap =3D g_hash_table_new(g_direct_hash, g_direct_equal); + + trace_qemu_file_monitor_new(mon, mon->fd); + + return mon; +#else + error_setg(errp, "File monitoring not available on this platform"); + return NULL; +#endif +} + + +QFileMonitor *qemu_file_monitor_get_instance(Error **errp) +{ + static QFileMonitor *global; + + if (!global) { + global =3D qemu_file_monitor_new(errp); + } + + return global; +} + + +#ifdef CONFIG_INOTIFY1 +int +qemu_file_monitor_add_watch(QFileMonitor *mon, + const char *dirpath, + const char *filename, + QFileMonitorHandler cb, + void *opaque, + Error **errp) +{ + QFileMonitorDir *dir; + int ret =3D -1; + + qemu_mutex_lock(&mon->lock); + dir =3D g_hash_table_lookup(mon->dirs, dirpath); + if (!dir) { + int rv =3D inotify_add_watch(mon->fd, dirpath, + IN_CREATE | IN_DELETE | IN_MODIFY | + IN_MOVED_TO | IN_MOVED_FROM); + + if (rv < 0) { + error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath); + goto cleanup; + } + + trace_qemu_file_monitor_enable_watch(mon, dirpath, rv); + + dir =3D g_new0(QFileMonitorDir, 1); + dir->path =3D g_strdup(dirpath); + dir->id =3D rv; + + g_hash_table_insert(mon->dirs, dir->path, dir); + g_hash_table_insert(mon->idmap, GINT_TO_POINTER(rv), dir); + + if (g_hash_table_size(mon->dirs) =3D=3D 1) { + qemu_set_fd_handler(mon->fd, qemu_file_monitor_watch, NULL, mo= n); + } + } + + dir->watches =3D g_renew(QFileMonitorWatch, dir->watches, dir->nwatche= s + 1); + + dir->watches[dir->nwatches].id =3D ++dir->nextid; + dir->watches[dir->nwatches].filename =3D filename ? g_strdup(filename)= : NULL; + dir->watches[dir->nwatches].cb =3D cb; + dir->watches[dir->nwatches].opaque =3D opaque; + dir->nwatches++; + + trace_qemu_file_monitor_add_watch(mon, dirpath, + filename ? filename : "", + cb, opaque, + dir->watches[dir->nwatches - 1].id); + + ret =3D 0; + + cleanup: + qemu_mutex_unlock(&mon->lock); + return ret; +} + + +void qemu_file_monitor_remove_watch(QFileMonitor *mon, + const char *dirpath, + int id) +{ + QFileMonitorDir *dir; + gsize i; + + qemu_mutex_lock(&mon->lock); + + trace_qemu_file_monitor_remove_watch(mon, dirpath, id); + + dir =3D g_hash_table_lookup(mon->dirs, dirpath); + if (!dir) { + goto cleanup; + } + + for (i =3D 0; i < dir->nwatches; i++) { + if (dir->watches[i].id =3D=3D id) { + if (i < (dir->nwatches - 1)) { + memmove(dir->watches + i, + dir->watches + i + 1, + sizeof(QFileMonitorWatch) * + (dir->nwatches - (i + 1))); + dir->watches =3D g_renew(QFileMonitorWatch, dir->watches, + dir->nwatches - 1); + dir->nwatches--; + } + break; + } + } + + if (dir->nwatches =3D=3D 0) { + inotify_rm_watch(mon->fd, dir->id); + trace_qemu_file_monitor_disable_watch(mon, dir->path, dir->id); + + g_hash_table_remove(mon->idmap, GINT_TO_POINTER(dir->id)); + g_hash_table_remove(mon->dirs, dir->path); + } + + cleanup: + qemu_mutex_lock(&mon->lock); +} + +#else +int +qemu_file_monitor_add_watch(QFileMonitor *mon, + const char *dirpath, + const char *filename, + QFileMonitorHandler cb, + void *opaque, + Error **errp) +{ + error_setg(errp, "File monitoring not available on this platform"); + return -1; +} + +void qemu_file_monitor_remove_watch(QFileMonitor *mon, + const char *dirpath, + int id) +{ +} +#endif + diff --git a/util/trace-events b/util/trace-events index 4822434c89..1a429f5525 100644 --- a/util/trace-events +++ b/util/trace-events @@ -21,6 +21,15 @@ buffer_move_empty(const char *buf, size_t len, const cha= r *from) "%s: %zd bytes buffer_move(const char *buf, size_t len, const char *from) "%s: %zd bytes = from %s" buffer_free(const char *buf, size_t len) "%s: capacity %zd" =20 +# util/filemonitor.c +qemu_file_monitor_add_watch(void *mon, const char *dirpath, const char *fi= lename, void *cb, void *opaque, int id) "File monitor %p add watch dir=3D'%= s' file=3D'%s' cb=3D%p opaque=3D%p id=3D%u" +qemu_file_monitor_remove_watch(void *mon, const char *dirpath, int id) "Fi= le monitor %p remove watch dir=3D'%s' id=3D%u" +qemu_file_monitor_new(void *mon, int fd) "File monitor %p created fd=3D%d" +qemu_file_monitor_enable_watch(void *mon, const char *dirpath, int id) "Fi= le monitor %p enable watch dir=3D'%s' id=3D%u" +qemu_file_monitor_disable_watch(void *mon, const char *dirpath, int id) "F= le monitor %p disable watch dir=3D'%s' id=3D%u" +qemu_file_monitor_event(void *mon, const char *dirpath, const char *filena= me, int mask, unsigned int id) "File monitor %p event dir=3D'%s' file=3D'%s= ' mask=3D0x%x id=3D%u" +qemu_file_monitor_dispatch(void *mon, const char *dirpath, const char *fil= ename, int ev, void *cb, void *opaque, unsigned int id) "File monitor %p di= spatch dir=3D'%s' file=3D'%s' ev=3D%d cb=3D%p opaque=3D%p id=3D%u" + # util/qemu-coroutine.c qemu_aio_coroutine_enter(void *ctx, void *from, void *to, void *opaque) "c= tx %p from %p to %p opaque %p" qemu_coroutine_yield(void *from, void *to) "from %p to %p" --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529079679185437.7955583331534; Fri, 15 Jun 2018 09:21:19 -0700 (PDT) Received: from localhost ([::1]:47960 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrTO-0007Y2-Vb for importer@patchew.org; Fri, 15 Jun 2018 12:21:15 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56421) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrd-0000zn-4u for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrb-0000UW-LE for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:13 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:52944 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrb-0000UH-Fb for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:11 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0B6D1818A6A9; Fri, 15 Jun 2018 15:42:11 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1DE1010FD2AF; Fri, 15 Jun 2018 15:42:10 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:54 +0100 Message-Id: <20180615154203.11347-3-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 15 Jun 2018 15:42:11 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 15 Jun 2018 15:42:11 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 02/11] qom: don't require user creatable objects to be registered X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" When an object is in turn owned by another user object, it is not desirable to expose this in the QOM object hierarchy, as it is just an internal implementation detail, we should be free to change without exposure. Signed-off-by: Daniel P. Berrang=C3=A9 --- qom/object.c | 12 ++++++++---- qom/object_interfaces.c | 16 ++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/qom/object.c b/qom/object.c index 4609e34a6a..560ed0e219 100644 --- a/qom/object.c +++ b/qom/object.c @@ -546,15 +546,19 @@ Object *object_new_with_propv(const char *typename, goto error; } =20 - object_property_add_child(parent, id, obj, &local_err); - if (local_err) { - goto error; + if (id !=3D NULL) { + object_property_add_child(parent, id, obj, &local_err); + if (local_err) { + goto error; + } } =20 if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) { user_creatable_complete(obj, &local_err); if (local_err) { - object_unparent(obj); + if (id !=3D NULL) { + object_unparent(obj); + } goto error; } } diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 72b97a8bed..e0f12c0a9b 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -83,16 +83,20 @@ Object *user_creatable_add_type(const char *type, const= char *id, goto out; } =20 - object_property_add_child(object_get_objects_root(), - id, obj, &local_err); - if (local_err) { - goto out; + if (id !=3D NULL) { + object_property_add_child(object_get_objects_root(), + id, obj, &local_err); + if (local_err) { + goto out; + } } =20 user_creatable_complete(obj, &local_err); if (local_err) { - object_property_del(object_get_objects_root(), - id, &error_abort); + if (id !=3D NULL) { + object_property_del(object_get_objects_root(), + id, &error_abort); + } goto out; } out: --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529079403560865.6081295829784; Fri, 15 Jun 2018 09:16:43 -0700 (PDT) Received: from localhost ([::1]:47914 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrOx-0003eW-Dm for importer@patchew.org; Fri, 15 Jun 2018 12:16:39 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56432) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrd-00010B-Ia for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrc-0000W6-Mv for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:13 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:58228 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrc-0000VD-IV for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:12 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2514087A81; Fri, 15 Jun 2018 15:42:12 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 37CCD10FD2AF; Fri, 15 Jun 2018 15:42:11 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:55 +0100 Message-Id: <20180615154203.11347-4-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:12 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:12 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 03/11] hw/usb: don't set IN_ISDIR for inotify watch in MTP driver X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" IN_ISDIR is not a bit that one can request when registering a watch with inotify_add_watch. Rather it is a bit that is set automatically when reading events from the kernel. Signed-off-by: Daniel P. Berrang=C3=A9 --- hw/usb/dev-mtp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 1ded7ac9a3..80f88e40fa 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -638,8 +638,7 @@ static void usb_mtp_inotify_cleanup(MTPState *s) =20 static int usb_mtp_add_watch(int inotifyfd, char *path) { - uint32_t mask =3D IN_CREATE | IN_DELETE | IN_MODIFY | - IN_ISDIR; + uint32_t mask =3D IN_CREATE | IN_DELETE | IN_MODIFY; =20 return inotify_add_watch(inotifyfd, path, mask); } --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529078229039517.2547652415248; Fri, 15 Jun 2018 08:57:09 -0700 (PDT) Received: from localhost ([::1]:47763 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTr64-0004XP-BO for importer@patchew.org; Fri, 15 Jun 2018 11:57:08 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56457) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqre-00011V-Vl for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrd-0000Xj-R7 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:15 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:57098 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrd-0000X8-MN for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:13 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3EB5540122CE; Fri, 15 Jun 2018 15:42:13 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 50CDF10FD2B5; Fri, 15 Jun 2018 15:42:12 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:56 +0100 Message-Id: <20180615154203.11347-5-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 15 Jun 2018 15:42:13 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 15 Jun 2018 15:42:13 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 04/11] hw/usb: fix const-ness for string params in MTP driver X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Various functions accepting 'char *' string parameters were missing 'const' qualifiers. Signed-off-by: Daniel P. Berrang=C3=A9 --- hw/usb/dev-mtp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 80f88e40fa..d5f570fb56 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -368,7 +368,7 @@ static const USBDesc desc =3D { /* -----------------------------------------------------------------------= */ =20 static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, - MTPObject *parent, char *name) + MTPObject *parent, const char *name) { MTPObject *o =3D g_new0(MTPObject, 1); =20 @@ -450,7 +450,7 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, ui= nt32_t handle) } =20 static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, - char *name) + const char *name) { MTPObject *child =3D usb_mtp_object_alloc(s, s->next_handle++, o, name); @@ -469,7 +469,7 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObj= ect *o, } =20 static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, - char *name, int len) + const char *name, int len) { MTPObject *iter; =20 @@ -636,7 +636,7 @@ static void usb_mtp_inotify_cleanup(MTPState *s) } } =20 -static int usb_mtp_add_watch(int inotifyfd, char *path) +static int usb_mtp_add_watch(int inotifyfd, const char *path) { uint32_t mask =3D IN_CREATE | IN_DELETE | IN_MODIFY; =20 --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529079599941790.9481468287873; Fri, 15 Jun 2018 09:19:59 -0700 (PDT) Received: from localhost ([::1]:47948 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrSB-0006Qo-3d for importer@patchew.org; Fri, 15 Jun 2018 12:19:59 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56496) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrg-00013b-W9 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqre-0000Ym-VT for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:17 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:58232 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqre-0000Y7-PW for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:14 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 57E7687A81; Fri, 15 Jun 2018 15:42:14 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6A8E610FD2AF; Fri, 15 Jun 2018 15:42:13 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:57 +0100 Message-Id: <20180615154203.11347-6-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:14 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:14 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 05/11] hw/usb: switch MTP to use new inotify APIs X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" The internal inotify APIs allow alot of conditional statements to be cleared out, and provide a simpler callback for handling events. Signed-off-by: Daniel P. Berrang=C3=A9 --- hw/usb/dev-mtp.c | 250 ++++++++++++++++---------------------------- hw/usb/trace-events | 2 +- 2 files changed, 93 insertions(+), 159 deletions(-) diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index d5f570fb56..68309ce66d 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -15,13 +15,11 @@ #include =20 #include -#ifdef CONFIG_INOTIFY1 -#include -#include "qemu/main-loop.h" -#endif + =20 #include "qemu-common.h" #include "qemu/iov.h" +#include "qemu/filemonitor.h" #include "trace.h" #include "hw/usb.h" #include "desc.h" @@ -123,7 +121,6 @@ enum { EP_EVENT, }; =20 -#ifdef CONFIG_INOTIFY1 typedef struct MTPMonEntry MTPMonEntry; =20 struct MTPMonEntry { @@ -132,7 +129,6 @@ struct MTPMonEntry { =20 QTAILQ_ENTRY(MTPMonEntry) next; }; -#endif =20 struct MTPControl { uint16_t code; @@ -158,10 +154,8 @@ struct MTPObject { char *name; char *path; struct stat stat; -#ifdef CONFIG_INOTIFY1 - /* inotify watch cookie */ + /* file monitor watch cookie */ int watchfd; -#endif MTPObject *parent; uint32_t nchildren; QLIST_HEAD(, MTPObject) children; @@ -184,11 +178,8 @@ struct MTPState { bool readonly; =20 QTAILQ_HEAD(, MTPObject) objects; -#ifdef CONFIG_INOTIFY1 - /* inotify descriptor */ - int inotifyfd; + QFileMonitor *file_monitor; QTAILQ_HEAD(events, MTPMonEntry) events; -#endif /* Responder is expecting a write operation */ bool write_pending; struct { @@ -473,6 +464,10 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject= *parent, { MTPObject *iter; =20 + if (len =3D=3D -1) { + len =3D strlen(name); + } + QLIST_FOREACH(iter, &parent->children, list) { if (strncmp(iter->name, name, len) =3D=3D 0) { return iter; @@ -482,7 +477,6 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject = *parent, return NULL; } =20 -#ifdef CONFIG_INOTIFY1 static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) { MTPObject *iter; @@ -496,158 +490,98 @@ static MTPObject *usb_mtp_object_lookup_wd(MTPState = *s, int wd) return NULL; } =20 -static void inotify_watchfn(void *arg) +static void file_monitor_event(int wd, + QFileMonitorEvent ev, + const char *name, + void *opaque) { - MTPState *s =3D arg; - ssize_t bytes; - /* From the man page: atleast one event can be read */ - int pos; - char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; - - for (;;) { - bytes =3D read(s->inotifyfd, buf, sizeof(buf)); - pos =3D 0; - - if (bytes <=3D 0) { - /* Better luck next time */ + MTPState *s =3D opaque; + int watchfd =3D 0; + MTPObject *parent =3D usb_mtp_object_lookup_wd(s, wd); + MTPMonEntry *entry =3D NULL; + MTPObject *o; + + if (!parent) { + return; + } + + switch (ev) { + case QFILE_MONITOR_EVENT_CREATED: + if (usb_mtp_object_lookup_name(parent, name, -1)) { + /* Duplicate create event */ return; } + entry =3D g_new0(MTPMonEntry, 1); + entry->handle =3D s->next_handle; + entry->event =3D EVT_OBJ_ADDED; + o =3D usb_mtp_add_child(s, parent, name); + if (!o) { + g_free(entry); + return; + } + o->watchfd =3D watchfd; + trace_usb_mtp_file_monitor_event(s->dev.addr, name, "Obj Added"); + break; =20 + case QFILE_MONITOR_EVENT_DELETED: /* - * TODO: Ignore initiator initiated events. - * For now we are good because the store is RO + * The kernel issues a IN_IGNORED event + * when a dir containing a watchpoint is + * deleted, so we don't have to delete the + * watchpoint */ - while (bytes > 0) { - char *p =3D buf + pos; - struct inotify_event *event =3D (struct inotify_event *)p; - int watchfd =3D 0; - uint32_t mask =3D event->mask & (IN_CREATE | IN_DELETE | - IN_MODIFY | IN_IGNORED); - MTPObject *parent =3D usb_mtp_object_lookup_wd(s, event->wd); - MTPMonEntry *entry =3D NULL; - MTPObject *o; - - pos =3D pos + sizeof(struct inotify_event) + event->len; - bytes =3D bytes - pos; - - if (!parent) { - continue; - } - - switch (mask) { - case IN_CREATE: - if (usb_mtp_object_lookup_name - (parent, event->name, event->len)) { - /* Duplicate create event */ - continue; - } - entry =3D g_new0(MTPMonEntry, 1); - entry->handle =3D s->next_handle; - entry->event =3D EVT_OBJ_ADDED; - o =3D usb_mtp_add_child(s, parent, event->name); - if (!o) { - g_free(entry); - continue; - } - o->watchfd =3D watchfd; - trace_usb_mtp_inotify_event(s->dev.addr, event->name, - event->mask, "Obj Added"); - break; - - case IN_DELETE: - /* - * The kernel issues a IN_IGNORED event - * when a dir containing a watchpoint is - * deleted, so we don't have to delete the - * watchpoint - */ - o =3D usb_mtp_object_lookup_name(parent, event->name, even= t->len); - if (!o) { - continue; - } - entry =3D g_new0(MTPMonEntry, 1); - entry->handle =3D o->handle; - entry->event =3D EVT_OBJ_REMOVED; - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj Deleted"); - usb_mtp_object_free(s, o); - break; - - case IN_MODIFY: - o =3D usb_mtp_object_lookup_name(parent, event->name, even= t->len); - if (!o) { - continue; - } - entry =3D g_new0(MTPMonEntry, 1); - entry->handle =3D o->handle; - entry->event =3D EVT_OBJ_INFO_CHANGED; - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj Modified"); - break; - - case IN_IGNORED: - trace_usb_mtp_inotify_event(s->dev.addr, parent->path, - event->mask, "Obj parent dir ignored= "); - break; - - default: - fprintf(stderr, "usb-mtp: failed to parse inotify event\n"= ); - continue; - } + o =3D usb_mtp_object_lookup_name(parent, name, -1); + if (!o) { + return; + } + entry =3D g_new0(MTPMonEntry, 1); + entry->handle =3D o->handle; + entry->event =3D EVT_OBJ_REMOVED; + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Delete= d"); + usb_mtp_object_free(s, o); + break; =20 - if (entry) { - QTAILQ_INSERT_HEAD(&s->events, entry, next); - } + case QFILE_MONITOR_EVENT_MODIFIED: + o =3D usb_mtp_object_lookup_name(parent, name, -1); + if (!o) { + return; } - } -} + entry =3D g_new0(MTPMonEntry, 1); + entry->handle =3D o->handle; + entry->event =3D EVT_OBJ_INFO_CHANGED; + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Modifi= ed"); + break; =20 -static int usb_mtp_inotify_init(MTPState *s) -{ - int fd; + case QFILE_MONITOR_EVENT_IGNORED: + trace_usb_mtp_file_monitor_event(s->dev.addr, parent->path, + "Obj parent dir ignored"); + break; =20 - fd =3D inotify_init1(IN_NONBLOCK); - if (fd =3D=3D -1) { - return 1; + default: + g_assert_not_reached(); } =20 - QTAILQ_INIT(&s->events); - s->inotifyfd =3D fd; - - qemu_set_fd_handler(fd, inotify_watchfn, NULL, s); - - return 0; + if (entry) { + QTAILQ_INSERT_HEAD(&s->events, entry, next); + } } =20 -static void usb_mtp_inotify_cleanup(MTPState *s) +static void usb_mtp_file_monitor_cleanup(MTPState *s) { MTPMonEntry *e, *p; =20 - if (!s->inotifyfd) { - return; - } - - qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); - close(s->inotifyfd); - QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { QTAILQ_REMOVE(&s->events, e, next); g_free(e); } } =20 -static int usb_mtp_add_watch(int inotifyfd, const char *path) -{ - uint32_t mask =3D IN_CREATE | IN_DELETE | IN_MODIFY; - - return inotify_add_watch(inotifyfd, path, mask); -} -#endif =20 static void usb_mtp_object_readdir(MTPState *s, MTPObject *o) { struct dirent *entry; DIR *dir; + Error *err =3D NULL; =20 if (o->have_children) { return; @@ -658,16 +592,19 @@ static void usb_mtp_object_readdir(MTPState *s, MTPOb= ject *o) if (!dir) { return; } -#ifdef CONFIG_INOTIFY1 - int watchfd =3D usb_mtp_add_watch(s->inotifyfd, o->path); + + int watchfd =3D qemu_file_monitor_add_watch(s->file_monitor, o->path, = NULL, + file_monitor_event, s, &err); if (watchfd =3D=3D -1) { - fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path); + fprintf(stderr, "usb-mtp: failed to add watch for %s: %s\n", o->pa= th, + error_get_pretty(err)); + error_free(err); } else { - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - 0, "Watch Added"); + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, + "Watch Added"); o->watchfd =3D watchfd; } -#endif + while ((entry =3D readdir(dir)) !=3D NULL) { usb_mtp_add_child(s, o, entry->d_name); } @@ -1175,13 +1112,11 @@ enum { /* Assumes that children, if any, have been already freed */ static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) { -#ifndef CONFIG_INOTIFY1 assert(o->nchildren =3D=3D 0); QTAILQ_REMOVE(&s->objects, o, next); g_free(o->name); g_free(o->path); g_free(o); -#endif } =20 static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) @@ -1280,6 +1215,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *= c) MTPData *data_in =3D NULL; MTPObject *o =3D NULL; uint32_t nres =3D 0, res0 =3D 0; + Error *err =3D NULL; =20 /* sanity checks */ if (c->code >=3D CMD_CLOSE_SESSION && s->session =3D=3D 0) { @@ -1307,19 +1243,21 @@ static void usb_mtp_command(MTPState *s, MTPControl= *c) trace_usb_mtp_op_open_session(s->dev.addr); s->session =3D c->argv[0]; usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root); -#ifdef CONFIG_INOTIFY1 - if (usb_mtp_inotify_init(s)) { - fprintf(stderr, "usb-mtp: file monitoring init failed\n"); + + s->file_monitor =3D qemu_file_monitor_get_instance(&err); + if (err) { + fprintf(stderr, "usb-mtp: file monitoring init failed: %s\n", + error_get_pretty(err)); + error_free(err); + } else { + QTAILQ_INIT(&s->events); } -#endif break; case CMD_CLOSE_SESSION: trace_usb_mtp_op_close_session(s->dev.addr); s->session =3D 0; s->next_handle =3D 0; -#ifdef CONFIG_INOTIFY1 - usb_mtp_inotify_cleanup(s); -#endif + usb_mtp_file_monitor_cleanup(s); usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); assert(QTAILQ_EMPTY(&s->objects)); break; @@ -1532,9 +1470,7 @@ static void usb_mtp_handle_reset(USBDevice *dev) =20 trace_usb_mtp_reset(s->dev.addr); =20 -#ifdef CONFIG_INOTIFY1 - usb_mtp_inotify_cleanup(s); -#endif + usb_mtp_file_monitor_cleanup(s); usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); s->session =3D 0; usb_mtp_data_free(s->data_in); @@ -1899,7 +1835,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPa= cket *p) } break; case EP_EVENT: -#ifdef CONFIG_INOTIFY1 if (!QTAILQ_EMPTY(&s->events)) { struct MTPMonEntry *e =3D QTAILQ_LAST(&s->events, events); uint32_t handle; @@ -1923,7 +1858,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPa= cket *p) g_free(e); return; } -#endif p->status =3D USB_RET_NAK; return; default: diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 2c18770ca5..99b1e8b8ce 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -237,7 +237,7 @@ usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, com= mand code 0x%x" usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, = handle 0x%x, path %s" usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, h= andle 0x%x, path %s" usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, han= dle 0x%x, path %s" -usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char= *s) "dev %d, path %s mask 0x%x event %s" +usb_mtp_file_monitor_event(int dev, const char *path, const char *s) "dev = %d, path %s event %s" =20 # hw/usb/host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529080047366201.74101668202275; Fri, 15 Jun 2018 09:27:27 -0700 (PDT) Received: from localhost ([::1]:48007 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrZK-0004UQ-Rp for importer@patchew.org; Fri, 15 Jun 2018 12:27:22 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56511) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrh-00014h-Tr for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrg-0000a4-2i for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:17 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:44464 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrf-0000Zi-T2 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:16 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 738084057CDD; Fri, 15 Jun 2018 15:42:15 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 842F210FD2AF; Fri, 15 Jun 2018 15:42:14 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:58 +0100 Message-Id: <20180615154203.11347-7-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 15 Jun 2018 15:42:15 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 15 Jun 2018 15:42:15 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 06/11] authz: add QAuthZ object as an authorization base class X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Daniel P. Berrange" The current qemu_acl module provides a simple access control list facility inside QEMU, which is used via a set of monitor commands acl_show, acl_policy, acl_add, acl_remove & acl_reset. Note there is no ability to create ACLs - the network services (eg VNC server) were expected to create ACLs that they want to check. There is also no way to define ACLs on the command line, nor potentially integrate with external authorization systems like polkit, pam, ldap lookup, etc. The QAuthZ object defines a minimal abstract QOM class that can be subclassed for creating different authorization providers. Signed-off-by: Daniel P. Berrange --- MAINTAINERS | 7 +++ Makefile | 10 ++-- Makefile.objs | 6 +++ Makefile.target | 2 + authz/Makefile.objs | 1 + authz/base.c | 82 +++++++++++++++++++++++++++++++ authz/trace-events | 4 ++ include/authz/base.h | 112 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 authz/Makefile.objs create mode 100644 authz/base.c create mode 100644 authz/trace-events create mode 100644 include/authz/base.h diff --git a/MAINTAINERS b/MAINTAINERS index 4a4f7c514b..367b14b84d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1762,6 +1762,13 @@ F: io/ F: include/io/ F: tests/test-io-* =20 +User authorization +M: Daniel P. Berrange +S: Maintained +F: authz/ +F: include/authz/ +F: tests/test-authz-* + Sockets M: Daniel P. Berrange M: Gerd Hoffmann diff --git a/Makefile b/Makefile index e46f2b625a..ea51b10311 100644 --- a/Makefile +++ b/Makefile @@ -413,6 +413,7 @@ endif =20 dummy :=3D $(call unnest-vars,, \ stub-obj-y \ + authz-obj-y \ chardev-obj-y \ util-obj-y \ qga-obj-y \ @@ -474,6 +475,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PAT= H)/scripts/hxtool SUBDIR_RULES=3D$(patsubst %,subdir-%, $(TARGET_LIST)) SOFTMMU_SUBDIR_RULES=3D$(filter %-softmmu,$(SUBDIR_RULES)) =20 +$(SOFTMMU_SUBDIR_RULES): $(authz-obj-y) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) $(SOFTMMU_SUBDIR_RULES): $(io-obj-y) @@ -536,9 +538,9 @@ COMMON_LDADDS =3D libqemuutil.a =20 qemu-img.o: qemu-img-cmds.h =20 -qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $= (qom-obj-y) $(COMMON_LDADDS) -qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $= (qom-obj-y) $(COMMON_LDADDS) -qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(q= om-obj-y) $(COMMON_LDADDS) +qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y= ) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y= ) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) = $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) =20 qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) =20 @@ -547,7 +549,7 @@ qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $= (COMMON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-m= arshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): LIBS +=3D -lcap =20 -scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-= obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-o= bj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) ifdef CONFIG_MPATH scsi/qemu-pr-helper$(EXESUF): LIBS +=3D -ludev -lmultipath -lmpathpersist endif diff --git a/Makefile.objs b/Makefile.objs index 7a9828da28..42cdb1c1c3 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -62,6 +62,11 @@ util-obj-y +=3D qapi/qapi-introspect.o =20 chardev-obj-y =3D chardev/ =20 +####################################################################### +# authz-obj-y is code used by both qemu system emulation and qemu-img + +authz-obj-y =3D authz/ + ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img =20 @@ -199,6 +204,7 @@ trace-events-subdirs =3D trace-events-subdirs +=3D accel/kvm trace-events-subdirs +=3D accel/tcg trace-events-subdirs +=3D audio +trace-events-subdirs +=3D authz trace-events-subdirs +=3D block trace-events-subdirs +=3D chardev trace-events-subdirs +=3D crypto diff --git a/Makefile.target b/Makefile.target index dad2cf8778..0499ef4014 100644 --- a/Makefile.target +++ b/Makefile.target @@ -166,6 +166,7 @@ include $(SRC_PATH)/Makefile.objs dummy :=3D $(call unnest-vars,,target-obj-y) target-obj-y-save :=3D $(target-obj-y) dummy :=3D $(call unnest-vars,.., \ + authz-obj-y \ block-obj-y \ block-obj-m \ chardev-obj-y \ @@ -179,6 +180,7 @@ target-obj-y :=3D $(target-obj-y-save) all-obj-y +=3D $(common-obj-y) all-obj-y +=3D $(target-obj-y) all-obj-y +=3D $(qom-obj-y) +all-obj-$(CONFIG_SOFTMMU) +=3D $(authz-obj-y) all-obj-$(CONFIG_SOFTMMU) +=3D $(block-obj-y) $(chardev-obj-y) all-obj-$(CONFIG_USER_ONLY) +=3D $(crypto-aes-obj-y) all-obj-$(CONFIG_SOFTMMU) +=3D $(crypto-obj-y) diff --git a/authz/Makefile.objs b/authz/Makefile.objs new file mode 100644 index 0000000000..12597c9528 --- /dev/null +++ b/authz/Makefile.objs @@ -0,0 +1 @@ +authz-obj-y +=3D base.o diff --git a/authz/base.c b/authz/base.c new file mode 100644 index 0000000000..5b15811384 --- /dev/null +++ b/authz/base.c @@ -0,0 +1,82 @@ +/* + * QEMU authorization framework base class + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" +#include "authz/base.h" +#include "authz/trace.h" + +bool qauthz_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZClass *cls =3D QAUTHZ_GET_CLASS(authz); + bool allowed; + + allowed =3D cls->is_allowed(authz, identity, errp); + trace_qauthz_is_allowed(authz, identity, allowed); + + return allowed; +} + + +bool qauthz_is_allowed_by_id(const char *authzid, + const char *identity, + Error **errp) +{ + QAuthZ *authz; + Object *obj; + Object *container; + + container =3D object_get_objects_root(); + obj =3D object_resolve_path_component(container, + authzid); + if (!obj) { + error_setg(errp, "Cannot find QAuthZ object ID %s", + authzid); + return 0; + } + + if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) { + error_setg(errp, "Object '%s' is not a QAuthZ subclass", + authzid); + return 0; + } + + authz =3D QAUTHZ(obj); + + return qauthz_is_allowed(authz, identity, errp); +} + + +static const TypeInfo authz_info =3D { + .parent =3D TYPE_OBJECT, + .name =3D TYPE_QAUTHZ, + .instance_size =3D sizeof(QAuthZ), + .class_size =3D sizeof(QAuthZClass), + .abstract =3D true, +}; + +static void qauthz_register_types(void) +{ + type_register_static(&authz_info); +} + +type_init(qauthz_register_types) + diff --git a/authz/trace-events b/authz/trace-events new file mode 100644 index 0000000000..481c90f511 --- /dev/null +++ b/authz/trace-events @@ -0,0 +1,4 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# authz/base.c +qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ = %p check identity=3D%s allowed=3D%d" diff --git a/include/authz/base.h b/include/authz/base.h new file mode 100644 index 0000000000..77dcd54c4c --- /dev/null +++ b/include/authz/base.h @@ -0,0 +1,112 @@ +/* + * QEMU authorization framework base class + * + * Copyright (c) 2018 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 . + * + */ + +#ifndef QAUTHZ_BASE_H__ +#define QAUTHZ_BASE_H__ + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qom/object.h" + + +#define TYPE_QAUTHZ "authz" + +#define QAUTHZ_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZClass, (klass), \ + TYPE_QAUTHZ) +#define QAUTHZ_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZClass, (obj), \ + TYPE_QAUTHZ) +#define QAUTHZ(obj) \ + INTERFACE_CHECK(QAuthZ, (obj), \ + TYPE_QAUTHZ) + +typedef struct QAuthZ QAuthZ; +typedef struct QAuthZClass QAuthZClass; + +/** + * QAuthZ: + * + * The QAuthZ class defines an API contract to be used + * for providing an authorization driver for services + * with user identities. + */ + +struct QAuthZ { + Object parent_obj; +}; + + +struct QAuthZClass { + ObjectClass parent_class; + + bool (*is_allowed)(QAuthZ *authz, + const char *identity, + Error **errp); +}; + + +/** + * qauthz_is_allowed: + * @authz: the authorization object + * @identity: the user identity to authorize + * @errp: pointer to a NULL initialized error object + * + * Check if a user @identity is authorized. If an error + * occurs this method will return false to indicate + * denial, as well as setting @errp to contain the details. + * Callers are recommended to treat the denial and error + * scenarios identically. Specifically the error info in + * @errp should never be fed back to the user being + * authorized, it is merely for benefit of administrator + * debugging. + * + * Returns: true if @identity is authorized, false if denied or if + * an error occurred. + */ +bool qauthz_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp); + + +/** + * qauthz_is_allowed_by_id: + * @authzid: ID of the authorization object + * @identity: the user identity to authorize + * @errp: pointer to a NULL initialized error object + * + * Check if a user @identity is authorized. If an error + * occurs this method will return false to indicate + * denial, as well as setting @errp to contain the details. + * Callers are recommended to treat the denial and error + * scenarios identically. Specifically the error info in + * @errp should never be fed back to the user being + * authorized, it is merely for benefit of administrator + * debugging. + * + * Returns: true if @identity is authorized, false if denied or if + * an error occurred. + */ +bool qauthz_is_allowed_by_id(const char *authzid, + const char *identity, + Error **errp); + +#endif /* QAUTHZ_BASE_H__ */ + --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529080209455648.9701896116919; Fri, 15 Jun 2018 09:30:09 -0700 (PDT) Received: from localhost ([::1]:48029 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrbv-0006xH-NC for importer@patchew.org; Fri, 15 Jun 2018 12:30:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56522) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqri-00015K-Io for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrh-0000b4-4u for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:18 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:48862 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrh-0000aX-09 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:17 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 92F1F6F4CE; Fri, 15 Jun 2018 15:42:16 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id A013310FD2AF; Fri, 15 Jun 2018 15:42:15 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:41:59 +0100 Message-Id: <20180615154203.11347-8-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 15 Jun 2018 15:42:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 15 Jun 2018 15:42:16 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 07/11] authz: add QAuthZSimple object type for easy whitelist auth checks X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" In many cases a single VM will just need to whilelist a single identity as the allowed user of network services. This is especially the case for TLS live migration (optionally with NBD storage) where we just need to whitelist the x509 certificate distinguished name of the source QEMU host. Signed-off-by: Daniel P. Berrange --- authz/Makefile.objs | 1 + authz/simple.c | 122 +++++++++++++++++++++++++++++++++++++++++ authz/trace-events | 3 + include/authz/simple.h | 80 +++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 authz/simple.c create mode 100644 include/authz/simple.h diff --git a/authz/Makefile.objs b/authz/Makefile.objs index 12597c9528..2a75d53840 100644 --- a/authz/Makefile.objs +++ b/authz/Makefile.objs @@ -1 +1,2 @@ authz-obj-y +=3D base.o +authz-obj-y +=3D simple.o diff --git a/authz/simple.c b/authz/simple.c new file mode 100644 index 0000000000..1ed1605b14 --- /dev/null +++ b/authz/simple.c @@ -0,0 +1,122 @@ +/* + * QEMU simple authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" +#include "authz/simple.h" +#include "authz/trace.h" +#include "qom/object_interfaces.h" + +static bool qauthz_simple_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZSimple *sauthz =3D QAUTHZ_SIMPLE(authz); + + trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity); + return g_str_equal(identity, sauthz->identity); +} + +static void +qauthz_simple_prop_set_identity(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZSimple *authz =3D QAUTHZ_SIMPLE(obj); + + authz->identity =3D g_strdup(value); +} + + +static char * +qauthz_simple_prop_get_identity(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZSimple *authz =3D QAUTHZ_SIMPLE(obj); + + return g_strdup(authz->identity); +} + + +static void +qauthz_simple_complete(UserCreatable *uc, Error **errp) +{ +} + + +static void +qauthz_simple_finalize(Object *obj) +{ + QAuthZSimple *sauthz =3D QAUTHZ_SIMPLE(obj); + + g_free(sauthz->identity); +} + + +static void +qauthz_simple_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc =3D USER_CREATABLE_CLASS(oc); + QAuthZClass *authz =3D QAUTHZ_CLASS(oc); + + ucc->complete =3D qauthz_simple_complete; + authz->is_allowed =3D qauthz_simple_is_allowed; + + object_class_property_add_str(oc, "identity", + qauthz_simple_prop_get_identity, + qauthz_simple_prop_set_identity, + NULL); +} + + +QAuthZSimple *qauthz_simple_new(const char *id, + const char *identity, + Error **errp) +{ + return QAUTHZ_SIMPLE( + object_new_with_props(TYPE_QAUTHZ_SIMPLE, + object_get_objects_root(), + id, errp, + "identity", identity, + NULL)); +} + + +static const TypeInfo qauthz_simple_info =3D { + .parent =3D TYPE_QAUTHZ, + .name =3D TYPE_QAUTHZ_SIMPLE, + .instance_size =3D sizeof(QAuthZSimple), + .instance_finalize =3D qauthz_simple_finalize, + .class_size =3D sizeof(QAuthZSimpleClass), + .class_init =3D qauthz_simple_class_init, + .interfaces =3D (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_simple_register_types(void) +{ + type_register_static(&qauthz_simple_info); +} + + +type_init(qauthz_simple_register_types); diff --git a/authz/trace-events b/authz/trace-events index 481c90f511..1ef796c1e1 100644 --- a/authz/trace-events +++ b/authz/trace-events @@ -2,3 +2,6 @@ =20 # authz/base.c qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ = %p check identity=3D%s allowed=3D%d" + +# auth/simple.c +qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char= *gotidentity) "AuthZ simple %p check want identity=3D%s got identity=3D%s" diff --git a/include/authz/simple.h b/include/authz/simple.h new file mode 100644 index 0000000000..a3d69902ae --- /dev/null +++ b/include/authz/simple.h @@ -0,0 +1,80 @@ +/* + * QEMU simple authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#ifndef QAUTHZ_SIMPLE_H__ +#define QAUTHZ_SIMPLE_H__ + +#include "authz/base.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 based on an exact matched username. + * + * To create an instance of this class via QMP: + * + * { + * "execute": "object-add", + * "arguments": { + * "qom-type": "authz-simple", + * "id": "authz0", + * "parameters": { + * "identity": "fred" + * } + * } + * } + * + */ +struct QAuthZSimple { + QAuthZ parent_obj; + + char *identity; +}; + + +struct QAuthZSimpleClass { + QAuthZClass parent_class; +}; + + +QAuthZSimple *qauthz_simple_new(const char *id, + const char *identity, + Error **errp); + + +#endif /* QAUTHZ_SIMPLE_H__ */ + --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529079809709851.7891665423595; Fri, 15 Jun 2018 09:23:29 -0700 (PDT) Received: from localhost ([::1]:47972 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrVU-00016s-Cq for importer@patchew.org; Fri, 15 Jun 2018 12:23:24 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56552) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrl-00018h-R2 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqri-0000bw-Hv for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:21 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:58236 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqri-0000bY-8d for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:18 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C7B31F68AB; Fri, 15 Jun 2018 15:42:17 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id BD5E610FD2B5; Fri, 15 Jun 2018 15:42:16 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:42:00 +0100 Message-Id: <20180615154203.11347-9-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:17 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 15 Jun 2018 15:42:17 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 08/11] authz: add QAuthZList object type for an access control list X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Daniel P. Berrange" Add a QAuthZList object type that implements the QAuthZ interface. This 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-list", "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" } } } This sets up an authorization rule that allows 'fred', 'bob' and anyone whose name starts with 'dan', except for 'danb'. Everyone unmatched is denied. It is not currently possible to create this via -object, since there is no syntax supported to specify non-scalar properties for objects. This is likely to be addressed by later support for using JSON with -object, or an equivalent approach. In any case the future "authz-listfile" object can be used from the CLI and is likely a better choice, as it allows the ACL to be refreshed automatically on change. Signed-off-by: Daniel P. Berrange --- .gitignore | 4 + MAINTAINERS | 1 + Makefile | 7 +- Makefile.objs | 4 + authz/Makefile.objs | 1 + authz/list.c | 315 ++++++++++++++++++++++++++++++++++++++++ authz/trace-events | 4 + include/authz/list.h | 106 ++++++++++++++ qapi/authz.json | 58 ++++++++ qapi/qapi-schema.json | 1 + tests/.gitignore | 1 + tests/Makefile.include | 4 + tests/test-authz-list.c | 171 ++++++++++++++++++++++ 13 files changed, 676 insertions(+), 1 deletion(-) create mode 100644 authz/list.c create mode 100644 include/authz/list.h create mode 100644 qapi/authz.json create mode 100644 tests/test-authz-list.c diff --git a/.gitignore b/.gitignore index 9da3b3e626..57edbbaacf 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ /qapi/qapi-commands-trace.[ch] /qapi/qapi-commands-transaction.[ch] /qapi/qapi-commands-ui.[ch] +/qapi/qapi-commands-authz.[ch] /qapi/qapi-commands.[ch] /qapi/qapi-events-block-core.[ch] /qapi/qapi-events-block.[ch] @@ -65,6 +66,7 @@ /qapi/qapi-events-trace.[ch] /qapi/qapi-events-transaction.[ch] /qapi/qapi-events-ui.[ch] +/qapi/qapi-events-authz.[ch] /qapi/qapi-events.[ch] /qapi/qapi-introspect.[ch] /qapi/qapi-types-block-core.[ch] @@ -84,6 +86,7 @@ /qapi/qapi-types-trace.[ch] /qapi/qapi-types-transaction.[ch] /qapi/qapi-types-ui.[ch] +/qapi/qapi-types-authz.[ch] /qapi/qapi-types.[ch] /qapi/qapi-visit-block-core.[ch] /qapi/qapi-visit-block.[ch] @@ -102,6 +105,7 @@ /qapi/qapi-visit-trace.[ch] /qapi/qapi-visit-transaction.[ch] /qapi/qapi-visit-ui.[ch] +/qapi/qapi-visit-authz.[ch] /qapi/qapi-visit.[ch] /qapi/qapi-doc.texi /qemu-doc.html diff --git a/MAINTAINERS b/MAINTAINERS index 367b14b84d..788e9b0d7d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1766,6 +1766,7 @@ User authorization M: Daniel P. Berrange S: Maintained F: authz/ +F: qapi/authz.json F: include/authz/ F: tests/test-authz-* =20 diff --git a/Makefile b/Makefile index ea51b10311..5a6107ca09 100644 --- a/Makefile +++ b/Makefile @@ -598,7 +598,8 @@ qapi-modules =3D $(SRC_PATH)/qapi/qapi-schema.json $(SR= C_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/tpm.json \ $(SRC_PATH)/qapi/trace.json \ $(SRC_PATH)/qapi/transaction.json \ - $(SRC_PATH)/qapi/ui.json + $(SRC_PATH)/qapi/ui.json \ + $(SRC_PATH)/qapi/authz.json =20 qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ qapi/qapi-types.c qapi/qapi-types.h \ @@ -619,6 +620,7 @@ qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ +qapi/qapi-types-authz.c qapi/qapi-types-authz.h \ qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ qapi/qapi-visit.c qapi/qapi-visit.h \ qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ @@ -638,6 +640,7 @@ qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ +qapi/qapi-visit-authz.c qapi/qapi-visit-authz.h \ qapi/qapi-commands.h qapi/qapi-commands.c \ qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ @@ -656,6 +659,7 @@ qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ +qapi/qapi-commands-authz.c qapi/qapi-commands-authz.h \ qapi/qapi-events.c qapi/qapi-events.h \ qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ qapi/qapi-events-block.c qapi/qapi-events-block.h \ @@ -674,6 +678,7 @@ qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ +qapi/qapi-events-authz.c qapi/qapi-events-authz.h \ qapi/qapi-introspect.h qapi/qapi-introspect.c \ qapi/qapi-doc.texi: \ qapi-gen-timestamp ; diff --git a/Makefile.objs b/Makefile.objs index 42cdb1c1c3..60c0af70cc 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -21,6 +21,7 @@ util-obj-y +=3D qapi/qapi-types-tpm.o util-obj-y +=3D qapi/qapi-types-trace.o util-obj-y +=3D qapi/qapi-types-transaction.o util-obj-y +=3D qapi/qapi-types-ui.o +util-obj-y +=3D qapi/qapi-types-authz.o util-obj-y +=3D qapi/qapi-builtin-visit.o util-obj-y +=3D qapi/qapi-visit.o util-obj-y +=3D qapi/qapi-visit-block-core.o @@ -40,6 +41,7 @@ util-obj-y +=3D qapi/qapi-visit-tpm.o util-obj-y +=3D qapi/qapi-visit-trace.o util-obj-y +=3D qapi/qapi-visit-transaction.o util-obj-y +=3D qapi/qapi-visit-ui.o +util-obj-y +=3D qapi/qapi-visit-authz.o util-obj-y +=3D qapi/qapi-events.o util-obj-y +=3D qapi/qapi-events-block-core.o util-obj-y +=3D qapi/qapi-events-block.o @@ -58,6 +60,7 @@ util-obj-y +=3D qapi/qapi-events-tpm.o util-obj-y +=3D qapi/qapi-events-trace.o util-obj-y +=3D qapi/qapi-events-transaction.o util-obj-y +=3D qapi/qapi-events-ui.o +util-obj-y +=3D qapi/qapi-events-authz.o util-obj-y +=3D qapi/qapi-introspect.o =20 chardev-obj-y =3D chardev/ @@ -160,6 +163,7 @@ common-obj-y +=3D qapi/qapi-commands-tpm.o common-obj-y +=3D qapi/qapi-commands-trace.o common-obj-y +=3D qapi/qapi-commands-transaction.o common-obj-y +=3D qapi/qapi-commands-ui.o +common-obj-y +=3D qapi/qapi-commands-authz.o common-obj-y +=3D qapi/qapi-introspect.o common-obj-y +=3D qmp.o hmp.o endif diff --git a/authz/Makefile.objs b/authz/Makefile.objs index 2a75d53840..921fa624d7 100644 --- a/authz/Makefile.objs +++ b/authz/Makefile.objs @@ -1,2 +1,3 @@ authz-obj-y +=3D base.o authz-obj-y +=3D simple.o +authz-obj-y +=3D list.o diff --git a/authz/list.c b/authz/list.c new file mode 100644 index 0000000000..1fc83290df --- /dev/null +++ b/authz/list.c @@ -0,0 +1,315 @@ +/* + * QEMU access control list authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" +#include "authz/list.h" +#include "authz/trace.h" +#include "qom/object_interfaces.h" +#include "qapi/qapi-visit-authz.h" + +#ifdef CONFIG_FNMATCH +#include +#endif + +static bool qauthz_list_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZList *sauthz =3D QAUTHZ_LIST(authz); + QAuthZListRuleList *rules =3D sauthz->rules; + + while (rules) { + QAuthZListRule *rule =3D rules->value; + QAuthZListFormat format =3D rule->has_format ? rule->format : + QAUTHZ_LIST_FORMAT_EXACT; + + trace_qauthz_list_check_rule(authz, rule->match, identity, + format, rule->policy); + switch (format) { + case QAUTHZ_LIST_FORMAT_EXACT: + if (strcmp(rule->match, identity) =3D=3D 0) { + return rule->policy =3D=3D QAUTHZ_LIST_POLICY_ALLOW; + } + break; +#ifdef CONFIG_FNMATCH + case QAUTHZ_LIST_FORMAT_GLOB: + if (fnmatch(rule->match, identity, 0) =3D=3D 0) { + return rule->policy =3D=3D QAUTHZ_LIST_POLICY_ALLOW; + } + break; +#else + return false; +#endif + default: + return false; + } + rules =3D rules->next; + } + + trace_qauthz_list_default_policy(authz, identity, sauthz->policy); + return sauthz->policy =3D=3D QAUTHZ_LIST_POLICY_ALLOW; +} + + +static void +qauthz_list_prop_set_policy(Object *obj, + int value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZList *bauthz =3D QAUTHZ_LIST(obj); + + bauthz->policy =3D value; +} + + +static int +qauthz_list_prop_get_policy(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZList *bauthz =3D QAUTHZ_LIST(obj); + + return bauthz->policy; +} + + +static void +qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QAuthZList *bauthz =3D QAUTHZ_LIST(obj); + + visit_type_QAuthZListRuleList(v, name, &bauthz->rules, errp); +} + +static void +qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QAuthZList *bauthz =3D QAUTHZ_LIST(obj); + QAuthZListRuleList *oldrules; +#ifndef CONFIG_FNMATCH + QAuthZListRuleList *rules; +#endif + + oldrules =3D bauthz->rules; + visit_type_QAuthZListRuleList(v, name, &bauthz->rules, errp); + +#ifndef CONFIG_FNMATCH + rules =3D bauthz->rules; + while (rules) { + QAuthZListRule *rule =3D rules->value; + if (rule->has_format && + rule->format =3D=3D QAUTHZ_LIST_FORMAT_GLOB) { + error_setg(errp, "Glob format not supported on this platform"); + qapi_free_QAuthZListRuleList(bauthz->rules); + bauthz->rules =3D oldrules; + return; + } + } +#endif + + qapi_free_QAuthZListRuleList(oldrules); +} + + +static void +qauthz_list_complete(UserCreatable *uc, Error **errp) +{ +} + + +static void +qauthz_list_finalize(Object *obj) +{ +} + + +static void +qauthz_list_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc =3D USER_CREATABLE_CLASS(oc); + QAuthZClass *authz =3D QAUTHZ_CLASS(oc); + + ucc->complete =3D qauthz_list_complete; + + object_class_property_add_enum(oc, "policy", + "QAuthZListPolicy", + &QAuthZListPolicy_lookup, + qauthz_list_prop_get_policy, + qauthz_list_prop_set_policy, + NULL); + + object_class_property_add(oc, "rules", "QAuthZListRule", + qauthz_list_prop_get_rules, + qauthz_list_prop_set_rules, + NULL, NULL, NULL); + + authz->is_allowed =3D qauthz_list_is_allowed; +} + + +QAuthZList *qauthz_list_new(const char *id, + QAuthZListPolicy policy, + Error **errp) +{ + return QAUTHZ_LIST( + object_new_with_props(TYPE_QAUTHZ_LIST, + object_get_objects_root(), + id, errp, + "policy", QAuthZListPolicy_lookup.array[poli= cy], + NULL)); +} + +ssize_t qauthz_list_append_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + Error **errp) +{ + QAuthZListRule *rule; + QAuthZListRuleList *rules, *tmp; + size_t i =3D 0; + +#ifndef CONFIG_FNMATCH + if (format =3D=3D QAUTHZ_LIST_FORMAT_GLOB) { + error_setg(errp, "Glob format not supported on this platform"); + return -1; + } +#endif + + rule =3D g_new0(QAuthZListRule, 1); + rule->policy =3D policy; + rule->match =3D g_strdup(match); + rule->format =3D format; + rule->has_format =3D true; + + tmp =3D g_new0(QAuthZListRuleList, 1); + tmp->value =3D rule; + + rules =3D auth->rules; + if (rules) { + while (rules->next) { + i++; + rules =3D rules->next; + } + rules->next =3D tmp; + return i + 1; + } else { + auth->rules =3D tmp; + return 0; + } +} + + +ssize_t qauthz_list_insert_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + size_t index, + Error **errp) +{ + QAuthZListRule *rule; + QAuthZListRuleList *rules, *tmp; + size_t i =3D 0; + +#ifndef CONFIG_FNMATCH + if (format =3D=3D QAUTHZ_LIST_FORMAT_GLOB) { + error_setg(errp, "Glob format not supported on this platform"); + return -1; + } +#endif + + rule =3D g_new0(QAuthZListRule, 1); + rule->policy =3D policy; + rule->match =3D g_strdup(match); + rule->format =3D format; + rule->has_format =3D true; + + tmp =3D g_new0(QAuthZListRuleList, 1); + tmp->value =3D rule; + + rules =3D auth->rules; + if (rules && index > 0) { + while (rules->next && i < (index - 1)) { + i++; + rules =3D rules->next; + } + tmp->next =3D rules->next; + rules->next =3D tmp; + return i + 1; + } else { + tmp->next =3D auth->rules; + auth->rules =3D tmp; + return 0; + } +} + + +ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match) +{ + QAuthZListRule *rule; + QAuthZListRuleList *rules, *prev; + size_t i =3D 0; + + prev =3D NULL; + rules =3D auth->rules; + while (rules) { + rule =3D rules->value; + if (g_str_equal(rule->match, match)) { + if (prev) { + prev->next =3D rules->next; + } else { + auth->rules =3D rules->next; + } + rules->next =3D NULL; + qapi_free_QAuthZListRuleList(rules); + return i; + } + prev =3D rules; + rules =3D rules->next; + i++; + } + + return -1; +} + + +static const TypeInfo qauthz_list_info =3D { + .parent =3D TYPE_QAUTHZ, + .name =3D TYPE_QAUTHZ_LIST, + .instance_size =3D sizeof(QAuthZList), + .instance_finalize =3D qauthz_list_finalize, + .class_size =3D sizeof(QAuthZListClass), + .class_init =3D qauthz_list_class_init, + .interfaces =3D (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_list_register_types(void) +{ + type_register_static(&qauthz_list_info); +} + + +type_init(qauthz_list_register_types); diff --git a/authz/trace-events b/authz/trace-events index 1ef796c1e1..a896d876e8 100644 --- a/authz/trace-events +++ b/authz/trace-events @@ -5,3 +5,7 @@ qauthz_is_allowed(void *authz, const char *identity, bool a= llowed) "AuthZ %p che =20 # auth/simple.c qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char= *gotidentity) "AuthZ simple %p check want identity=3D%s got identity=3D%s" + +# auth/list.c +qauthz_list_check_rule(void *authz, const char *identity, const char *rule= , int format, int policy) "AuthZ list %p check rule=3D%s identity=3D%s form= at=3D%d policy=3D%d" +qauthz_list_default_policy(void *authz, const char *identity, int policy) = "AuthZ list %p default identity=3D%s policy=3D%d" diff --git a/include/authz/list.h b/include/authz/list.h new file mode 100644 index 0000000000..eb131ba0f0 --- /dev/null +++ b/include/authz/list.h @@ -0,0 +1,106 @@ +/* + * QEMU list authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#ifndef QAUTHZ_LIST_H__ +#define QAUTHZ_LIST_H__ + +#include "authz/base.h" +#include "qapi/qapi-types-authz.h" + +#define TYPE_QAUTHZ_LIST "authz-list" + +#define QAUTHZ_LIST_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZListClass, (klass), \ + TYPE_QAUTHZ_LIST) +#define QAUTHZ_LIST_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZListClass, (obj), \ + TYPE_QAUTHZ_LIST) +#define QAUTHZ_LIST(obj) \ + INTERFACE_CHECK(QAuthZList, (obj), \ + TYPE_QAUTHZ_LIST) + +typedef struct QAuthZList QAuthZList; +typedef struct QAuthZListClass QAuthZListClass; + + +/** + * QAuthZList: + * + * This authorization driver provides a list 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-list", + * "id": "authz0", + * "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" + * } + * } + * } + * + */ +struct QAuthZList { + QAuthZ parent_obj; + + QAuthZListPolicy policy; + QAuthZListRuleList *rules; +}; + + +struct QAuthZListClass { + QAuthZClass parent_class; +}; + + +QAuthZList *qauthz_list_new(const char *id, + QAuthZListPolicy policy, + Error **errp); + +ssize_t qauthz_list_append_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + Error **errp); + +ssize_t qauthz_list_insert_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + size_t index, + Error **errp); + +ssize_t qauthz_list_delete_rule(QAuthZList *auth, + const char *match); + + +#endif /* QAUTHZ_LIST_H__ */ + diff --git a/qapi/authz.json b/qapi/authz.json new file mode 100644 index 0000000000..607839c627 --- /dev/null +++ b/qapi/authz.json @@ -0,0 +1,58 @@ +# -*- Mode: Python -*- +# +# QAPI authz definitions + +## +# @QAuthZListPolicy: +# +# The authorization policy result +# +# @deny: deny access +# @allow: allow access +# +# Since: 3.0 +## +{ 'enum': 'QAuthZListPolicy', + 'prefix': 'QAUTHZ_LIST_POLICY', + 'data': ['deny', 'allow']} + +## +# @QAuthZListFormat: +# +# The authorization policy result +# +# @exact: an exact string match +# @glob: string with ? and * shell wildcard support +# +# Since: 3.0 +## +{ 'enum': 'QAuthZListFormat', + 'prefix': 'QAUTHZ_LIST_FORMAT', + 'data': ['exact', 'glob']} + +## +# @QAuthZListRule: +# +# 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 of the @match rule (default 'exact') +# +# Since: 3.0 +## +{ 'struct': 'QAuthZListRule', + 'data': {'match': 'str', + 'policy': 'QAuthZListPolicy', + '*format': 'QAuthZListFormat'}} + +## +# @QAuthZListRuleListHack: +# +# Not exposed via QMP; hack to generate QAuthZListRuleList +# for use internally by the code. +# +# Since: 3.0 +## +{ 'struct': 'QAuthZListRuleListHack', + 'data': { 'unused': ['QAuthZListRule'] } } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 65b6dc2f6f..6a5a02a388 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -89,6 +89,7 @@ { 'include': 'rocker.json' } { 'include': 'tpm.json' } { 'include': 'ui.json' } +{ 'include': 'authz.json' } { 'include': 'migration.json' } { 'include': 'transaction.json' } { 'include': 'trace.json' } diff --git a/tests/.gitignore b/tests/.gitignore index 2bc61a9a58..176302328b 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -17,6 +17,7 @@ rcutorture test-aio test-aio-multithread test-arm-mptimer +test-authz-simple test-base64 test-bdrv-drain test-bitops diff --git a/tests/Makefile.include b/tests/Makefile.include index 607afe5bed..f869ed4642 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -168,6 +168,7 @@ check-unit-y +=3D tests/ptimer-test$(EXESUF) gcov-files-ptimer-test-y =3D hw/core/ptimer.c check-unit-y +=3D tests/test-qapi-util$(EXESUF) gcov-files-test-qapi-util-y =3D qapi/qapi-util.c +check-unit-y +=3D tests/test-authz-list$(EXESUF) =20 check-block-$(CONFIG_POSIX) +=3D tests/qemu-iotests-quick.sh =20 @@ -662,6 +663,9 @@ tests/test-timed-average$(EXESUF): tests/test-timed-ave= rage.o $(test-util-obj-y) tests/test-base64$(EXESUF): tests/test-base64.o $(test-util-obj-y) tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o = hw/core/ptimer.o =20 +tests/test-authz-list$(EXESUF): tests/test-authz-list.o \ + $(test-util-obj-y) $(authz-obj-y) $(qom-obj-y) + tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y) =20 tests/test-replication$(EXESUF): tests/test-replication.o $(test-util-obj-= y) \ diff --git a/tests/test-authz-list.c b/tests/test-authz-list.c new file mode 100644 index 0000000000..02ce4c5763 --- /dev/null +++ b/tests/test-authz-list.c @@ -0,0 +1,171 @@ +/* + * QEMU list authorization object + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" + +#include "authz/list.h" + +static void test_authz_default_deny(void) +{ + QAuthZList *auth =3D qauthz_list_new("auth0", + QAUTHZ_LIST_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) +{ + QAuthZList *auth =3D qauthz_list_new("auth0", + QAUTHZ_LIST_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) +{ + QAuthZList *auth =3D qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_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) +{ + QAuthZList *auth =3D qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_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 =3D NULL; +#endif + QAuthZList *auth =3D qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "bob", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "dan", QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); +#ifdef CONFIG_FNMATCH + qauthz_list_append_rule(auth, "dan*", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_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_list_append_rule(auth, "dan*", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &local_err) < 0); + g_assert(local_err !=3D NULL); + error_free(local_err); +#endif + + object_unparent(OBJECT(auth)); +} + +static void test_authz_add_remove(void) +{ + QAuthZList *auth =3D qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + g_assert_cmpint(qauthz_list_append_rule(auth, "fred", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + =3D=3D, 0); + g_assert_cmpint(qauthz_list_append_rule(auth, "bob", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + =3D=3D, 1); + g_assert_cmpint(qauthz_list_append_rule(auth, "dan", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + =3D=3D, 2); + g_assert_cmpint(qauthz_list_append_rule(auth, "frank", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + =3D=3D, 3); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + g_assert_cmpint(qauthz_list_delete_rule(auth, "dan"), + =3D=3D, 2); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + g_assert_cmpint(qauthz_list_insert_rule(auth, "dan", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + 2, + &error_abort), + =3D=3D, 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/list/default/deny", test_authz_default_deny); + g_test_add_func("/auth/list/default/allow", test_authz_default_allow); + g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); + g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow= ); + g_test_add_func("/auth/list/complex", test_authz_complex); + g_test_add_func("/auth/list/add-remove", test_authz_add_remove); + + return g_test_run(); +} --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529078404391238.34874229108698; Fri, 15 Jun 2018 09:00:04 -0700 (PDT) Received: from localhost ([::1]:47774 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTr8t-0006zl-GW for importer@patchew.org; Fri, 15 Jun 2018 12:00:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56566) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrm-00019n-PE for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrj-0000cr-Ik for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:22 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:52948 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrj-0000cT-D3 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:19 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E933E81663E0; Fri, 15 Jun 2018 15:42:18 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id F316F10FD2AF; Fri, 15 Jun 2018 15:42:17 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:42:01 +0100 Message-Id: <20180615154203.11347-10-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 15 Jun 2018 15:42:18 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 15 Jun 2018 15:42:18 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 09/11] authz: add QAuthZListFile object type for a file access control list X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Add a QAuthZListFile object type that implements the QAuthZ interface. This built-in implementation is a proxy around the QAtuhZList object type, initializing it from an external file, and optionally, automatically reloading it whenever it changes. To create an instance of this object via the QMP monitor, the syntax used would be: { "execute": "object-add", "arguments": { "qom-type": "authz-list", "id": "auth0", "parameters": { "filename": "/etc/qemu/vnc.acl", "refresh": "yes" } } } If "refresh" is "yes", inotify is used to monitor the file, automatically reloading changes. If an error occurs during reloading, all authorizations will fail until the file is next successfully loaded. The /etc/qemu/vnc.acl file would contain a JSON representation of a QAuthZList object { "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" } This sets up an authorization rule that allows 'fred', 'bob' and anyone whose name starts with 'dan', except for 'danb'. Everyone unmatched is denied. The object can be loaded on the comand line using -object authz-list-file,id=3Dauthz0,filename=3D/etc/qemu/vnc.acl,refresh= =3Dyes Signed-off-by: Daniel P. Berrang=C3=A9 --- authz/Makefile.objs | 1 + authz/listfile.c | 284 +++++++++++++++++++++++++++++++++++++++ authz/trace-events | 4 + include/authz/listfile.h | 105 +++++++++++++++ 4 files changed, 394 insertions(+) create mode 100644 authz/listfile.c create mode 100644 include/authz/listfile.h diff --git a/authz/Makefile.objs b/authz/Makefile.objs index 921fa624d7..8351bf181d 100644 --- a/authz/Makefile.objs +++ b/authz/Makefile.objs @@ -1,3 +1,4 @@ authz-obj-y +=3D base.o authz-obj-y +=3D simple.o authz-obj-y +=3D list.o +authz-obj-y +=3D listfile.o diff --git a/authz/listfile.c b/authz/listfile.c new file mode 100644 index 0000000000..5bd3e37f7e --- /dev/null +++ b/authz/listfile.c @@ -0,0 +1,284 @@ +/* + * QEMU access control list file authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" +#include "authz/listfile.h" +#include "authz/trace.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qemu/filemonitor.h" +#include "qom/object_interfaces.h" +#include "qapi/qapi-visit-authz.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qobject-input-visitor.h" + + +static bool +qauthz_list_file_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZListFile *fauthz =3D QAUTHZ_LIST_FILE(authz); + if (fauthz->list) { + return qauthz_is_allowed(fauthz->list, identity, errp); + } + + return false; +} + + +static QAuthZ * +qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp) +{ + GError *err =3D NULL; + gchar *content =3D NULL; + gsize len; + QObject *obj =3D NULL; + QDict *pdict; + Visitor *v =3D NULL; + QAuthZ *ret =3D NULL; + + trace_qauthz_list_file_load(fauthz, fauthz->filename); + if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) { + error_setg(errp, "Unable to read '%s': %s", + fauthz->filename, err->message); + goto cleanup; + } + + obj =3D qobject_from_json(content, errp); + if (!obj) { + goto cleanup; + } + + pdict =3D qobject_to(QDict, obj); + if (!pdict) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict"); + goto cleanup; + } + + v =3D qobject_input_visitor_new(obj); + + ret =3D (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST, + NULL, pdict, v, errp); + + cleanup: + visit_free(v); + qobject_unref(obj); + if (err) { + g_error_free(err); + } + g_free(content); + return ret; +} + + +static void +qauthz_list_file_event(int wd G_GNUC_UNUSED, + QFileMonitorEvent ev G_GNUC_UNUSED, + const char *name G_GNUC_UNUSED, + void *opaque) +{ + QAuthZListFile *fauthz =3D opaque; + Error *err =3D NULL; + + if (ev !=3D QFILE_MONITOR_EVENT_MODIFIED && + ev !=3D QFILE_MONITOR_EVENT_CREATED) + return; + + object_unref(OBJECT(fauthz->list)); + fauthz->list =3D qauthz_list_file_load(fauthz, &err); + trace_qauthz_list_file_refresh(fauthz, + fauthz->filename, fauthz->list ? 1 : 0); + if (!fauthz->list) { + error_report_err(err); + } +} + +static void +qauthz_list_file_complete(UserCreatable *uc, Error **errp) +{ + QAuthZListFile *fauthz =3D QAUTHZ_LIST_FILE(uc); + + fauthz->list =3D qauthz_list_file_load(fauthz, errp); + + if (fauthz->refresh) { + gchar *dir, *file; + fauthz->file_monitor =3D qemu_file_monitor_get_instance(errp); + if (!fauthz->file_monitor) { + return; + } + + dir =3D g_path_get_dirname(fauthz->filename); + if (g_str_equal(dir, ".")) { + error_setg(errp, "Filename must be an absolute path"); + g_free(dir); + return; + } + file =3D g_path_get_basename(fauthz->filename); + if (g_str_equal(file, ".")) { + error_setg(errp, "Path has no trailing filename component"); + g_free(file); + g_free(dir); + return; + } + + fauthz->file_watch =3D qemu_file_monitor_add_watch( + fauthz->file_monitor, dir, file, + qauthz_list_file_event, fauthz, errp); + g_free(file); + g_free(dir); + if (fauthz->file_watch < 0) { + return; + } + } +} + + +static void +qauthz_list_file_prop_set_filename(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *authz =3D QAUTHZ_LIST_FILE(obj); + + authz->filename =3D g_strdup(value); +} + + +static char * +qauthz_list_file_prop_get_filename(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *authz =3D QAUTHZ_LIST_FILE(obj); + + return g_strdup(authz->filename); +} + + +static void +qauthz_list_file_prop_set_refresh(Object *obj, + bool value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *authz =3D QAUTHZ_LIST_FILE(obj); + + authz->refresh =3D value; +} + + +static bool +qauthz_list_file_prop_get_refresh(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *authz =3D QAUTHZ_LIST_FILE(obj); + + return authz->refresh; +} + + +static void +qauthz_list_file_finalize(Object *obj) +{ + QAuthZListFile *authz =3D QAUTHZ_LIST_FILE(obj); + + if (authz->file_watch !=3D -1 && authz->file_monitor) { + gchar *dir =3D g_path_get_dirname(authz->filename); + qemu_file_monitor_remove_watch(authz->file_monitor, + dir, + authz->file_watch); + g_free(dir); + } + object_unref(OBJECT(authz->list)); + g_free(authz->filename); +} + + +static void +qauthz_list_file_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc =3D USER_CREATABLE_CLASS(oc); + QAuthZClass *authz =3D QAUTHZ_CLASS(oc); + + ucc->complete =3D qauthz_list_file_complete; + + object_class_property_add_str(oc, "filename", + qauthz_list_file_prop_get_filename, + qauthz_list_file_prop_set_filename, + NULL); + object_class_property_add_bool(oc, "refresh", + qauthz_list_file_prop_get_refresh, + qauthz_list_file_prop_set_refresh, + NULL); + + authz->is_allowed =3D qauthz_list_file_is_allowed; +} + + +static void +qauthz_list_file_init(Object *obj) +{ + QAuthZListFile *authz =3D QAUTHZ_LIST_FILE(obj); + + authz->file_watch =3D -1; +#ifdef CONFIG_INOTIFY1 + authz->refresh =3D TRUE; +#endif +} + + +QAuthZListFile *qauthz_list_file_new(const char *id, + const char *filename, + Error **errp) +{ + return QAUTHZ_LIST_FILE( + object_new_with_props(TYPE_QAUTHZ_LIST_FILE, + object_get_objects_root(), + id, errp, + "filename", filename, + NULL)); +} + + +static const TypeInfo qauthz_list_file_info =3D { + .parent =3D TYPE_QAUTHZ_LIST, + .name =3D TYPE_QAUTHZ_LIST_FILE, + .instance_init =3D qauthz_list_file_init, + .instance_size =3D sizeof(QAuthZListFile), + .instance_finalize =3D qauthz_list_file_finalize, + .class_size =3D sizeof(QAuthZListFileClass), + .class_init =3D qauthz_list_file_class_init, + .interfaces =3D (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_list_file_register_types(void) +{ + type_register_static(&qauthz_list_file_info); +} + + +type_init(qauthz_list_file_register_types); diff --git a/authz/trace-events b/authz/trace-events index a896d876e8..fb65349a90 100644 --- a/authz/trace-events +++ b/authz/trace-events @@ -9,3 +9,7 @@ qauthz_simple_is_allowed(void *authz, const char *wantident= ity, const char *goti # auth/list.c qauthz_list_check_rule(void *authz, const char *identity, const char *rule= , int format, int policy) "AuthZ list %p check rule=3D%s identity=3D%s form= at=3D%d policy=3D%d" qauthz_list_default_policy(void *authz, const char *identity, int policy) = "AuthZ list %p default identity=3D%s policy=3D%d" + +# auth/listfile.c +qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p lo= ad filename=3D%s" +qauthz_list_file_refresh(void *authz, const char *filename, int success) "= AuthZ file %p load filename=3D%s success=3D%d" diff --git a/include/authz/listfile.h b/include/authz/listfile.h new file mode 100644 index 0000000000..f6ea0ddd27 --- /dev/null +++ b/include/authz/listfile.h @@ -0,0 +1,105 @@ +/* + * QEMU list file authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#ifndef QAUTHZ_LIST_FILE_H__ +#define QAUTHZ_LIST_FILE_H__ + +#include "authz/list.h" +#include "qapi/qapi-types-authz.h" +#include "qemu/filemonitor.h" + +#define TYPE_QAUTHZ_LIST_FILE "authz-list-file" + +#define QAUTHZ_LIST_FILE_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZListFileClass, (klass), \ + TYPE_QAUTHZ_LIST_FILE) +#define QAUTHZ_LIST_FILE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZListFileClass, (obj), \ + TYPE_QAUTHZ_LIST_FILE) +#define QAUTHZ_LIST_FILE(obj) \ + INTERFACE_CHECK(QAuthZListFile, (obj), \ + TYPE_QAUTHZ_LIST_FILE) + +typedef struct QAuthZListFile QAuthZListFile; +typedef struct QAuthZListFileClass QAuthZListFileClass; + + +/** + * QAuthZListFile: + * + * This authorization driver provides a file mechanism + * for granting access by matching user names against a + * file 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-list-file", + * "id": "authz0", + * "parameters": { + * "filename": "/etc/qemu/myvm-vnc.acl", + * "refresh": "yes" + * } + * } + * } + * + * If 'refresh' is 'yes', inotify is used to monitor for changes + * to the file and auto-reload the rules. + * + * The myvm-vnc.acl file should contain the parameters for + * the QAuthZList object in JSON format: + * + * { + * "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" + * } + * + */ +struct QAuthZListFile { + QAuthZ parent_obj; + + QAuthZ *list; + char *filename; + bool refresh; + QFileMonitor *file_monitor; + int file_watch; +}; + + +struct QAuthZListFileClass { + QAuthZClass parent_class; +}; + + +QAuthZListFile *qauthz_list_file_new(const char *id, + const char *filename, + Error **errp); + + +#endif /* QAUTHZ_LIST_FILE_H__ */ + --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 15290803630351014.8309419003905; Fri, 15 Jun 2018 09:32:43 -0700 (PDT) Received: from localhost ([::1]:48058 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTreQ-0000UA-9z for importer@patchew.org; Fri, 15 Jun 2018 12:32:38 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56570) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrn-0001AC-74 for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrl-0000ej-2m for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:23 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:48870 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrk-0000e2-RQ for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:20 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 65BC46F4CE; Fri, 15 Jun 2018 15:42:20 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1E85710FD2AF; Fri, 15 Jun 2018 15:42:19 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:42:02 +0100 Message-Id: <20180615154203.11347-11-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 15 Jun 2018 15:42:20 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 15 Jun 2018 15:42:20 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 10/11] authz: add QAuthZPAM object type for authorizing using PAM X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Daniel P. Berrange" Add an authorization backend that talks to PAM to check whether the user identity is allowed. This only uses the PAM account validation facility, which is essentially just a check to see if the provided username is permit= ted access. It doesn't use the authentication or session parts of PAM, since that's dealt with by the relevant part of QEMU (eg VNC server). Consider starting QEMU with a VNC server and telling it to use TLS with x509 client certificates and configuring it to use an PAM to validate the x509 distinguished name. In this example we're telling it to use PAM for the QAuthZ impl with a service name of "qemu-vnc" $ qemu-system-x86_64 \ -object tls-creds-x509,id=3Dtls0,dir=3D/home/berrange/security/qemutls= ,\ endpoint=3Dserver,verify-peer=3Dyes \ -object authz-pam,id=3Dauthz0,service=3Dqemu-vnc \ -vnc :1,tls-creds=3Dtls0,tls-authz=3Dauthz0 This requires an /etc/pam/qemu-vnc file to be created with the auth rules. A very simple file based whitelist can be setup using $ cat > /etc/pam/qemu-vnc < /etc/qemu/vnc.allow < --- authz/Makefile.objs | 3 + authz/pamacct.c | 149 ++++++++++++++++++++++++++++++++++++++++ authz/trace-events | 3 + configure | 37 ++++++++++ include/authz/pamacct.h | 97 ++++++++++++++++++++++++++ 5 files changed, 289 insertions(+) create mode 100644 authz/pamacct.c create mode 100644 include/authz/pamacct.h diff --git a/authz/Makefile.objs b/authz/Makefile.objs index 8351bf181d..ed7b273596 100644 --- a/authz/Makefile.objs +++ b/authz/Makefile.objs @@ -2,3 +2,6 @@ authz-obj-y +=3D base.o authz-obj-y +=3D simple.o authz-obj-y +=3D list.o authz-obj-y +=3D listfile.o +authz-obj-$(CONFIG_AUTH_PAM) +=3D pamacct.o + +pamacct.o-libs =3D -lpam diff --git a/authz/pamacct.c b/authz/pamacct.c new file mode 100644 index 0000000000..a070dda217 --- /dev/null +++ b/authz/pamacct.c @@ -0,0 +1,149 @@ +/* + * QEMU PAM authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#include "qemu/osdep.h" +#include "authz/pamacct.h" +#include "authz/trace.h" +#include "qom/object_interfaces.h" + +#include + + +static bool qauthz_pam_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZPam *pauthz =3D QAUTHZ_PAM(authz); + const struct pam_conv pam_conversation =3D { 0 }; + pam_handle_t *pamh =3D NULL; + int ret; + + trace_qauthz_pam_check(authz, identity, pauthz->service); + ret =3D pam_start(pauthz->service, + identity, + &pam_conversation, + &pamh); + if (ret !=3D PAM_SUCCESS) { + error_setg(errp, "Unable to start PAM transaction: %s", + pam_strerror(NULL, ret)); + return false; + } + + ret =3D pam_acct_mgmt(pamh, PAM_SILENT); + if (ret !=3D PAM_SUCCESS) { + error_setg(errp, "Unable to authorize user '%s': %s", + identity, pam_strerror(pamh, ret)); + goto cleanup; + } + + cleanup: + pam_end(pamh, ret); + return ret =3D=3D PAM_SUCCESS; +} + + +static void +qauthz_pam_prop_set_service(Object *obj, + const char *service, + Error **errp G_GNUC_UNUSED) +{ + QAuthZPam *pauthz =3D QAUTHZ_PAM(obj); + + g_free(pauthz->service); + pauthz->service =3D g_strdup(service); +} + + +static char * +qauthz_pam_prop_get_service(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZPam *pauthz =3D QAUTHZ_PAM(obj); + + return g_strdup(pauthz->service); +} + + +static void +qauthz_pam_complete(UserCreatable *uc, Error **errp) +{ +} + + +static void +qauthz_pam_finalize(Object *obj) +{ + QAuthZPam *pauthz =3D QAUTHZ_PAM(obj); + + g_free(pauthz->service); +} + + +static void +qauthz_pam_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc =3D USER_CREATABLE_CLASS(oc); + QAuthZClass *authz =3D QAUTHZ_CLASS(oc); + + ucc->complete =3D qauthz_pam_complete; + authz->is_allowed =3D qauthz_pam_is_allowed; + + object_class_property_add_str(oc, "service", + qauthz_pam_prop_get_service, + qauthz_pam_prop_set_service, + NULL); +} + + +QAuthZPam *qauthz_pam_new(const char *id, + const char *service, + Error **errp) +{ + return QAUTHZ_PAM( + object_new_with_props(TYPE_QAUTHZ_PAM, + object_get_objects_root(), + id, errp, + "service", service, + NULL)); +} + + +static const TypeInfo qauthz_pam_info =3D { + .parent =3D TYPE_QAUTHZ, + .name =3D TYPE_QAUTHZ_PAM, + .instance_size =3D sizeof(QAuthZPam), + .instance_finalize =3D qauthz_pam_finalize, + .class_size =3D sizeof(QAuthZPamClass), + .class_init =3D qauthz_pam_class_init, + .interfaces =3D (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_pam_register_types(void) +{ + type_register_static(&qauthz_pam_info); +} + + +type_init(qauthz_pam_register_types); diff --git a/authz/trace-events b/authz/trace-events index fb65349a90..72c411927d 100644 --- a/authz/trace-events +++ b/authz/trace-events @@ -13,3 +13,6 @@ qauthz_list_default_policy(void *authz, const char *ident= ity, int policy) "AuthZ # auth/listfile.c qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p lo= ad filename=3D%s" qauthz_list_file_refresh(void *authz, const char *filename, int success) "= AuthZ file %p load filename=3D%s success=3D%d" + +# auth/pam.c +qauthz_pam_check(void *authz, const char *identity, const char *service) "= AuthZ PAM %p identity=3D%s service=3D%s" diff --git a/configure b/configure index a8c4094c87..6b505808c9 100755 --- a/configure +++ b/configure @@ -445,6 +445,7 @@ nettle_kdf=3D"no" gcrypt=3D"" gcrypt_hmac=3D"no" gcrypt_kdf=3D"no" +auth_pam=3D"" vte=3D"" virglrenderer=3D"" tpm=3D"yes" @@ -1310,6 +1311,10 @@ for opt do ;; --enable-gcrypt) gcrypt=3D"yes" ;; + --disable-auth-pam) auth_pam=3D"no" + ;; + --enable-auth-pam) auth_pam=3D"yes" + ;; --enable-rdma) rdma=3D"yes" ;; --disable-rdma) rdma=3D"no" @@ -1575,6 +1580,7 @@ disabled with --disable-FEATURE, default is enabled i= f available: gnutls GNUTLS cryptography support nettle nettle cryptography support gcrypt libgcrypt cryptography support + auth-pam PAM access control sdl SDL UI --with-sdlabi select preferred SDL ABI 1.2 or 2.0 gtk gtk UI @@ -2814,6 +2820,33 @@ else fi =20 =20 +########################################## +# PAM probe + +if test "x$auth_pam" !=3D "no"; then + cat > $TMPC < +#include +int main(void) { + const char *service_name =3D "qemu"; + const char *user =3D "frank"; + const struct pam_conv *pam_conv =3D NULL; + pam_handle_t *pamh =3D NULL; + pam_start(service_name, user, pam_conv, &pamh); + return 0; +} +EOF + if compile_prog "" "-lpam" ; then + auth_pam=3Dyes + else + if test "$auth_pam" =3D "yes"; then + feature_not_found "PAM" "Install pam-devel" + else + auth_pam=3Dno + fi + fi +fi + ########################################## # getifaddrs (for tests/test-io-channel-socket ) =20 @@ -5826,6 +5859,7 @@ echo "libgcrypt kdf $gcrypt_kdf" echo "nettle $nettle $(echo_version $nettle $nettle_version)" echo "nettle kdf $nettle_kdf" echo "libtasn1 $tasn1" +echo "PAM $auth_pam" echo "curses support $curses" echo "virgl support $virglrenderer" echo "curl support $curl" @@ -6286,6 +6320,9 @@ fi if test "$tasn1" =3D "yes" ; then echo "CONFIG_TASN1=3Dy" >> $config_host_mak fi +if test "$auth_pam" =3D "yes" ; then + echo "CONFIG_AUTH_PAM=3Dy" >> $config_host_mak +fi if test "$have_ifaddrs_h" =3D "yes" ; then echo "HAVE_IFADDRS_H=3Dy" >> $config_host_mak fi diff --git a/include/authz/pamacct.h b/include/authz/pamacct.h new file mode 100644 index 0000000000..716a455bb9 --- /dev/null +++ b/include/authz/pamacct.h @@ -0,0 +1,97 @@ +/* + * QEMU PAM authorization driver + * + * Copyright (c) 2018 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 . + * + */ + +#ifndef QAUTHZ_PAM_H__ +#define QAUTHZ_PAM_H__ + +#include "authz/base.h" + + +#define TYPE_QAUTHZ_PAM "authz-pam" + +#define QAUTHZ_PAM_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZPamClass, (klass), \ + TYPE_QAUTHZ_PAM) +#define QAUTHZ_PAM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZPamClass, (obj), \ + TYPE_QAUTHZ_PAM) +#define QAUTHZ_PAM(obj) \ + INTERFACE_CHECK(QAuthZPam, (obj), \ + TYPE_QAUTHZ_PAM) + +typedef struct QAuthZPam QAuthZPam; +typedef struct QAuthZPamClass QAuthZPamClass; + + +/** + * QAuthZPam: + * + * This authorization driver provides a PAM 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-pam", + * "id": "auth0", + * "parameters": { + * "service": "qemu-vnc-tls" + * } + * } + * } + * + * The driver only uses the PAM "account" verification + * subsystem. The above config would require a config + * file /etc/pam.d/qemu-vnc-tls. For a simple file + * lookup it would contain + * + * account requisite pam_listfile.so item=3Duser sense=3Dallow \ + * file=3D/etc/qemu/vnc.allow + * + * The external file would then contain a list of usernames. + * If x509 cert was being used as the username, a suitable + * entry would match the distinguish name: + * + * CN=3Dlaptop.berrange.com,O=3DBerrange Home,L=3DLondon,ST=3DLondon,C=3D= GB + * + */ +struct QAuthZPam { + QAuthZ parent_obj; + + char *service; +}; + + +struct QAuthZPamClass { + QAuthZClass parent_class; +}; + + +QAuthZPam *qauthz_pam_new(const char *id, + const char *service, + Error **errp); + + +#endif /* QAUTHZ_PAM_H__ */ + --=20 2.17.0 From nobody Sun Apr 28 00:19:02 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1529078621681171.63696829276842; Fri, 15 Jun 2018 09:03:41 -0700 (PDT) Received: from localhost ([::1]:47804 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTrCO-0001FN-Fk for importer@patchew.org; Fri, 15 Jun 2018 12:03:40 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56599) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fTqrp-0001Cq-HR for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fTqrm-0000fs-6h for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:25 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:52952 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fTqrl-0000f6-Uk for qemu-devel@nongnu.org; Fri, 15 Jun 2018 11:42:22 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7AF4580125D6; Fri, 15 Jun 2018 15:42:21 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8B2D210FD2AF; Fri, 15 Jun 2018 15:42:20 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: qemu-devel@nongnu.org Date: Fri, 15 Jun 2018 16:42:03 +0100 Message-Id: <20180615154203.11347-12-berrange@redhat.com> In-Reply-To: <20180615154203.11347-1-berrange@redhat.com> References: <20180615154203.11347-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 15 Jun 2018 15:42:21 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 15 Jun 2018 15:42:21 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'berrange@redhat.com' RCPT:'' X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [PATCH v2 11/11] authz: delete existing ACL implementation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Dr. David Alan Gilbert" , Markus Armbruster , Gerd Hoffmann , =?UTF-8?q?Andreas=20F=C3=A4rber?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Daniel P. Berrange" The 'qemu_acl' type was a previous non-QOM based attempt to provide an authorization facility in QEMU. Because it is non-QOM based it cannot be created via the command line and requires special monitor commands to manipulate it. The new QAuthZ subclasses provide a superset of the functionality in qemu_acl, so the latter can now be deleted. The HMP 'acl_*' monitor commands are converted to use the new QAuthZSimple data type instead in order to provide temporary backwards compatibility. Signed-off-by: Daniel P. Berrange --- crypto/tlssession.c | 35 ++++--- crypto/trace-events | 2 +- include/qemu/acl.h | 66 ------------ monitor.c | 180 ++++++++++++++++++++++----------- tests/Makefile.include | 4 +- tests/test-crypto-tlssession.c | 15 ++- tests/test-io-channel-tls.c | 16 ++- ui/vnc-auth-sasl.c | 23 +++-- ui/vnc-auth-sasl.h | 5 +- ui/vnc-auth-vencrypt.c | 2 +- ui/vnc-ws.c | 2 +- ui/vnc.c | 37 ++++--- ui/vnc.h | 4 +- util/Makefile.objs | 1 - util/acl.c | 179 -------------------------------- 15 files changed, 210 insertions(+), 361 deletions(-) delete mode 100644 include/qemu/acl.h delete mode 100644 util/acl.c diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 96a02deb69..510af8019e 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -23,7 +23,7 @@ #include "crypto/tlscredsanon.h" #include "crypto/tlscredsx509.h" #include "qapi/error.h" -#include "qemu/acl.h" +#include "authz/base.h" #include "trace.h" =20 #ifdef CONFIG_GNUTLS @@ -36,7 +36,7 @@ struct QCryptoTLSSession { QCryptoTLSCreds *creds; gnutls_session_t handle; char *hostname; - char *aclname; + char *authzid; bool handshakeComplete; QCryptoTLSSessionWriteFunc writeFunc; QCryptoTLSSessionReadFunc readFunc; @@ -55,7 +55,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) gnutls_deinit(session->handle); g_free(session->hostname); g_free(session->peername); - g_free(session->aclname); + g_free(session->authzid); object_unref(OBJECT(session->creds)); g_free(session); } @@ -92,7 +92,7 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t = len) QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds, const char *hostname, - const char *aclname, + const char *authzid, QCryptoTLSCredsEndpoint endpoint, Error **errp) { @@ -102,13 +102,13 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, session =3D g_new0(QCryptoTLSSession, 1); trace_qcrypto_tls_session_new( session, creds, hostname ? hostname : "", - aclname ? aclname : "", endpoint); + authzid ? authzid : "", endpoint); =20 if (hostname) { session->hostname =3D g_strdup(hostname); } - if (aclname) { - session->aclname =3D g_strdup(aclname); + if (authzid) { + session->authzid =3D g_strdup(authzid); } session->creds =3D creds; object_ref(OBJECT(creds)); @@ -220,6 +220,7 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession= *session, unsigned int nCerts, i; time_t now; gnutls_x509_crt_t cert =3D NULL; + Error *err =3D NULL; =20 now =3D time(NULL); if (now =3D=3D ((time_t)-1)) { @@ -307,19 +308,17 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSessi= on *session, gnutls_strerror(ret)); goto error; } - if (session->aclname) { - qemu_acl *acl =3D qemu_acl_find(session->aclname); - int allow; - if (!acl) { - error_setg(errp, "Cannot find ACL %s", - session->aclname); + if (session->authzid) { + bool allow; + + allow =3D qauthz_is_allowed_by_id(session->authzid, + session->peername, &err); + if (err) { + error_propagate(errp, err); goto error; } - - allow =3D qemu_acl_party_is_allowed(acl, session->peername= ); - if (!allow) { - error_setg(errp, "TLS x509 ACL check for %s is denied", + error_setg(errp, "TLS x509 authz check for %s is denie= d", session->peername); goto error; } @@ -506,7 +505,7 @@ qcrypto_tls_session_get_peer_name(QCryptoTLSSession *se= ssion) QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, const char *hostname G_GNUC_UNUSED, - const char *aclname G_GNUC_UNUSED, + const char *authzid G_GNUC_UNUSED, QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, Error **errp) { diff --git a/crypto/trace-events b/crypto/trace-events index e589990359..4254256010 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -16,5 +16,5 @@ qcrypto_tls_creds_x509_load_cert(void *creds, int isServe= r, const char *file) "T qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS = creds x509 load cert list creds=3D%p file=3D%s" =20 # crypto/tlssession.c -qcrypto_tls_session_new(void *session, void *creds, const char *hostname, = const char *aclname, int endpoint) "TLS session new session=3D%p creds=3D%p= hostname=3D%s aclname=3D%s endpoint=3D%d" +qcrypto_tls_session_new(void *session, void *creds, const char *hostname, = const char *authzid, int endpoint) "TLS session new session=3D%p creds=3D%p= hostname=3D%s authzid=3D%s endpoint=3D%d" qcrypto_tls_session_check_creds(void *session, const char *status) "TLS se= ssion check creds session=3D%p status=3D%s" diff --git a/include/qemu/acl.h b/include/qemu/acl.h deleted file mode 100644 index 7c44119a47..0000000000 --- a/include/qemu/acl.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * QEMU access control list management - * - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a= copy - * of this software and associated documentation files (the "Software"), t= o deal - * in the Software without restriction, including without limitation the r= ights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included= in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN - * THE SOFTWARE. - */ - -#ifndef QEMU_ACL_H -#define QEMU_ACL_H - -#include "qemu/queue.h" - -typedef struct qemu_acl_entry qemu_acl_entry; -typedef struct qemu_acl qemu_acl; - -struct qemu_acl_entry { - char *match; - int deny; - - QTAILQ_ENTRY(qemu_acl_entry) next; -}; - -struct qemu_acl { - char *aclname; - unsigned int nentries; - QTAILQ_HEAD(,qemu_acl_entry) entries; - int defaultDeny; -}; - -qemu_acl *qemu_acl_init(const char *aclname); - -qemu_acl *qemu_acl_find(const char *aclname); - -int qemu_acl_party_is_allowed(qemu_acl *acl, - const char *party); - -void qemu_acl_reset(qemu_acl *acl); - -int qemu_acl_append(qemu_acl *acl, - int deny, - const char *match); -int qemu_acl_insert(qemu_acl *acl, - int deny, - const char *match, - int index); -int qemu_acl_remove(qemu_acl *acl, - const char *match); - -#endif /* QEMU_ACL_H */ diff --git a/monitor.c b/monitor.c index 6d0cec552e..67c63013bd 100644 --- a/monitor.c +++ b/monitor.c @@ -50,7 +50,8 @@ #include "sysemu/balloon.h" #include "qemu/timer.h" #include "sysemu/hw_accel.h" -#include "qemu/acl.h" +#include "authz/list.h" +#include "qapi/util.h" #include "sysemu/tpm.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" @@ -2073,61 +2074,87 @@ static void hmp_wavcapture(Monitor *mon, const QDic= t *qdict) QLIST_INSERT_HEAD (&capture_head, s, entries); } =20 -static qemu_acl *find_acl(Monitor *mon, const char *name) +static QAuthZList *find_auth(Monitor *mon, const char *name) { - qemu_acl *acl =3D qemu_acl_find(name); + Object *obj; + Object *container; =20 - if (!acl) { + container =3D object_get_objects_root(); + obj =3D object_resolve_path_component(container, name); + if (!obj) { monitor_printf(mon, "acl: unknown list '%s'\n", name); + return NULL; } - return acl; + + return QAUTHZ_LIST(obj); } =20 static void hmp_acl_show(Monitor *mon, const QDict *qdict) { const char *aclname =3D qdict_get_str(qdict, "aclname"); - qemu_acl *acl =3D find_acl(mon, aclname); - qemu_acl_entry *entry; - int i =3D 0; - - if (acl) { - monitor_printf(mon, "policy: %s\n", - acl->defaultDeny ? "deny" : "allow"); - QTAILQ_FOREACH(entry, &acl->entries, next) { - i++; - monitor_printf(mon, "%d: %s %s\n", i, - entry->deny ? "deny" : "allow", entry->match); - } + QAuthZList *auth =3D find_auth(mon, aclname); + QAuthZListRuleList *rules; + size_t i =3D 0; + + if (!auth) { + return; + } + + monitor_printf(mon, "policy: %s\n", + QAuthZListPolicy_lookup.array[auth->policy]); + + rules =3D auth->rules; + while (rules) { + QAuthZListRule *rule =3D rules->value; + i++; + monitor_printf(mon, "%zu: %s %s\n", i, + QAuthZListPolicy_lookup.array[rule->policy], + rule->match); + rules =3D rules->next; } } =20 static void hmp_acl_reset(Monitor *mon, const QDict *qdict) { const char *aclname =3D qdict_get_str(qdict, "aclname"); - qemu_acl *acl =3D find_acl(mon, aclname); + QAuthZList *auth =3D find_auth(mon, aclname); =20 - if (acl) { - qemu_acl_reset(acl); - monitor_printf(mon, "acl: removed all rules\n"); + if (!auth) { + return; } + + auth->policy =3D QAUTHZ_LIST_POLICY_DENY; + qapi_free_QAuthZListRuleList(auth->rules); + auth->rules =3D NULL; + monitor_printf(mon, "acl: removed all rules\n"); } =20 static void hmp_acl_policy(Monitor *mon, const QDict *qdict) { const char *aclname =3D qdict_get_str(qdict, "aclname"); const char *policy =3D qdict_get_str(qdict, "policy"); - qemu_acl *acl =3D find_acl(mon, aclname); + QAuthZList *auth =3D find_auth(mon, aclname); + int val; + Error *err =3D NULL; + + if (!auth) { + return; + } =20 - if (acl) { - if (strcmp(policy, "allow") =3D=3D 0) { - acl->defaultDeny =3D 0; + val =3D qapi_enum_parse(&QAuthZListPolicy_lookup, + policy, + QAUTHZ_LIST_POLICY_DENY, + &err); + if (err) { + error_free(err); + monitor_printf(mon, "acl: unknown policy '%s', " + "expected 'deny' or 'allow'\n", policy); + } else { + auth->policy =3D val; + if (auth->policy =3D=3D QAUTHZ_LIST_POLICY_ALLOW) { monitor_printf(mon, "acl: policy set to 'allow'\n"); - } else if (strcmp(policy, "deny") =3D=3D 0) { - acl->defaultDeny =3D 1; - monitor_printf(mon, "acl: policy set to 'deny'\n"); } else { - monitor_printf(mon, "acl: unknown policy '%s', " - "expected 'deny' or 'allow'\n", policy); + monitor_printf(mon, "acl: policy set to 'deny'\n"); } } } @@ -2136,30 +2163,60 @@ static void hmp_acl_add(Monitor *mon, const QDict *= qdict) { const char *aclname =3D qdict_get_str(qdict, "aclname"); const char *match =3D qdict_get_str(qdict, "match"); - const char *policy =3D qdict_get_str(qdict, "policy"); + const char *policystr =3D qdict_get_str(qdict, "policy"); int has_index =3D qdict_haskey(qdict, "index"); int index =3D qdict_get_try_int(qdict, "index", -1); - qemu_acl *acl =3D find_acl(mon, aclname); - int deny, ret; - - if (acl) { - if (strcmp(policy, "allow") =3D=3D 0) { - deny =3D 0; - } else if (strcmp(policy, "deny") =3D=3D 0) { - deny =3D 1; - } else { - monitor_printf(mon, "acl: unknown policy '%s', " - "expected 'deny' or 'allow'\n", policy); - return; - } - if (has_index) - ret =3D qemu_acl_insert(acl, deny, match, index); - else - ret =3D qemu_acl_append(acl, deny, match); - if (ret < 0) - monitor_printf(mon, "acl: unable to add acl entry\n"); - else - monitor_printf(mon, "acl: added rule at position %d\n", ret); + QAuthZList *auth =3D find_auth(mon, aclname); + Error *err =3D NULL; + QAuthZListPolicy policy; + QAuthZListFormat format; + size_t i =3D 0; + + if (!auth) { + return; + } + + policy =3D qapi_enum_parse(&QAuthZListPolicy_lookup, + policystr, + QAUTHZ_LIST_POLICY_DENY, + &err); + if (err) { + error_free(err); + monitor_printf(mon, "acl: unknown policy '%s', " + "expected 'deny' or 'allow'\n", policystr); + return; + } + +#ifdef CONFIG_FNMATCH + if (strchr(match, '*')) { + format =3D QAUTHZ_LIST_FORMAT_GLOB; + } else { + format =3D QAUTHZ_LIST_FORMAT_EXACT; + } +#else + /* Historically we silently degraded to plain strcmp + * when fnmatch() was missing */ + format =3D QAUTHZ_LIST_FORMAT_EXACT; +#endif + + if (has_index && index =3D=3D 0) { + monitor_printf(mon, "acl: unable to add acl entry\n"); + return; + } + + if (has_index) { + i =3D qauthz_list_insert_rule(auth, match, policy, + format, index - 1, &err); + } else { + i =3D qauthz_list_append_rule(auth, match, policy, + format, &err); + } + if (err) { + monitor_printf(mon, "acl: unable to add rule: %s", + error_get_pretty(err)); + error_free(err); + } else { + monitor_printf(mon, "acl: added rule at position %zu\n", i + 1); } } =20 @@ -2167,15 +2224,18 @@ static void hmp_acl_remove(Monitor *mon, const QDic= t *qdict) { const char *aclname =3D qdict_get_str(qdict, "aclname"); const char *match =3D qdict_get_str(qdict, "match"); - qemu_acl *acl =3D find_acl(mon, aclname); - int ret; + QAuthZList *auth =3D find_auth(mon, aclname); + ssize_t i =3D 0; =20 - if (acl) { - ret =3D qemu_acl_remove(acl, match); - if (ret < 0) - monitor_printf(mon, "acl: no matching acl entry\n"); - else - monitor_printf(mon, "acl: removed rule at position %d\n", ret); + if (!auth) { + return; + } + + i =3D qauthz_list_delete_rule(auth, match); + if (i >=3D 0) { + monitor_printf(mon, "acl: removed rule at position %zu\n", i + 1); + } else { + monitor_printf(mon, "acl: no matching acl entry\n"); } } =20 diff --git a/tests/Makefile.include b/tests/Makefile.include index f869ed4642..824b84cd29 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -607,8 +607,8 @@ test-qom-obj-y =3D $(qom-obj-y) $(test-util-obj-y) test-qapi-obj-y =3D tests/test-qapi-visit.o tests/test-qapi-types.o \ tests/test-qapi-events.o tests/test-qapi-introspect.o \ $(test-qom-obj-y) -benchmark-crypto-obj-y =3D $(crypto-obj-y) $(test-qom-obj-y) -test-crypto-obj-y =3D $(crypto-obj-y) $(test-qom-obj-y) +benchmark-crypto-obj-y =3D $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y) +test-crypto-obj-y =3D $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y) test-io-obj-y =3D $(io-obj-y) $(test-crypto-obj-y) test-block-obj-y =3D $(block-obj-y) $(test-io-obj-y) tests/iothread.o =20 diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 82f21c27f2..c51297c864 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -26,7 +26,7 @@ #include "qom/object_interfaces.h" #include "qapi/error.h" #include "qemu/sockets.h" -#include "qemu/acl.h" +#include "authz/list.h" =20 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT =20 @@ -112,7 +112,7 @@ static void test_crypto_tls_session(const void *opaque) QCryptoTLSCreds *serverCreds; QCryptoTLSSession *clientSess =3D NULL; QCryptoTLSSession *serverSess =3D NULL; - qemu_acl *acl; + QAuthZList *auth; const char * const *wildcards; int channel[2]; bool clientShake =3D false; @@ -171,11 +171,15 @@ static void test_crypto_tls_session(const void *opaqu= e) &err); g_assert(serverCreds !=3D NULL); =20 - acl =3D qemu_acl_init("tlssessionacl"); - qemu_acl_reset(acl); + auth =3D qauthz_list_new("tlssessionacl", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); wildcards =3D data->wildcards; while (wildcards && *wildcards) { - qemu_acl_append(acl, 0, *wildcards); + qauthz_list_append_rule(auth, *wildcards, + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &error_abort); wildcards++; } =20 @@ -265,6 +269,7 @@ static void test_crypto_tls_session(const void *opaque) =20 object_unparent(OBJECT(serverCreds)); object_unparent(OBJECT(clientCreds)); + object_unparent(OBJECT(auth)); =20 qcrypto_tls_session_free(serverSess); qcrypto_tls_session_free(clientSess); diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index bb88ee870f..6eb002fd56 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -29,7 +29,7 @@ #include "io-channel-helpers.h" #include "crypto/init.h" #include "crypto/tlscredsx509.h" -#include "qemu/acl.h" +#include "authz/list.h" #include "qom/object_interfaces.h" =20 #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -116,7 +116,7 @@ static void test_io_channel_tls(const void *opaque) QIOChannelTLS *serverChanTLS; QIOChannelSocket *clientChanSock; QIOChannelSocket *serverChanSock; - qemu_acl *acl; + QAuthZList *auth; const char * const *wildcards; int channel[2]; struct QIOChannelTLSHandshakeData clientHandshake =3D { false, false }; @@ -167,11 +167,15 @@ static void test_io_channel_tls(const void *opaque) &err); g_assert(serverCreds !=3D NULL); =20 - acl =3D qemu_acl_init("channeltlsacl"); - qemu_acl_reset(acl); + auth =3D qauthz_list_new("channeltlsacl", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); wildcards =3D data->wildcards; while (wildcards && *wildcards) { - qemu_acl_append(acl, 0, *wildcards); + qauthz_list_append_rule(auth, *wildcards, + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &error_abort); wildcards++; } =20 @@ -259,6 +263,8 @@ static void test_io_channel_tls(const void *opaque) object_unref(OBJECT(serverChanSock)); object_unref(OBJECT(clientChanSock)); =20 + object_unparent(OBJECT(auth)); + close(channel[0]); close(channel[1]); } diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 3751a777a4..7b2b09f242 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -24,6 +24,7 @@ =20 #include "qemu/osdep.h" #include "qapi/error.h" +#include "authz/base.h" #include "vnc.h" #include "trace.h" =20 @@ -146,13 +147,14 @@ size_t vnc_client_read_sasl(VncState *vs) static int vnc_auth_sasl_check_access(VncState *vs) { const void *val; - int err; - int allow; + int rv; + Error *err =3D NULL; + bool allow; =20 - err =3D sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); - if (err !=3D SASL_OK) { + rv =3D sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); + if (rv !=3D SASL_OK) { trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username", - sasl_errstring(err, NULL, NULL)); + sasl_errstring(rv, NULL, NULL)); return -1; } if (val =3D=3D NULL) { @@ -163,12 +165,19 @@ static int vnc_auth_sasl_check_access(VncState *vs) vs->sasl.username =3D g_strdup((const char*)val); trace_vnc_auth_sasl_username(vs, vs->sasl.username); =20 - if (vs->vd->sasl.acl =3D=3D NULL) { + if (vs->vd->sasl.authzid =3D=3D NULL) { trace_vnc_auth_sasl_acl(vs, 1); return 0; } =20 - allow =3D qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.usernam= e); + allow =3D qauthz_is_allowed_by_id(vs->vd->sasl.authzid, + vs->sasl.username, &err); + if (err) { + trace_vnc_auth_fail(vs, vs->auth, "Error from authz", + error_get_pretty(err)); + error_free(err); + return -1; + } =20 trace_vnc_auth_sasl_acl(vs, allow); return allow ? 0 : -1; diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h index 2ae224ee3a..fb55fe04ca 100644 --- a/ui/vnc-auth-sasl.h +++ b/ui/vnc-auth-sasl.h @@ -30,8 +30,8 @@ typedef struct VncStateSASL VncStateSASL; typedef struct VncDisplaySASL VncDisplaySASL; =20 -#include "qemu/acl.h" #include "qemu/main-loop.h" +#include "authz/base.h" =20 struct VncStateSASL { sasl_conn_t *conn; @@ -60,7 +60,8 @@ struct VncStateSASL { }; =20 struct VncDisplaySASL { - qemu_acl *acl; + QAuthZ *authz; + char *authzid; }; =20 void vnc_sasl_client_cleanup(VncState *vs); diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index d99ea362c1..f072e16ace 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -109,7 +109,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, = uint8_t *data, size_t len tls =3D qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, - vs->vd->tlsaclname, + vs->vd->tlsauthzid, &err); if (!tls) { trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed", diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 950f1cd2ac..95c9703c72 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -62,7 +62,7 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UN= USED, tls =3D qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, - vs->vd->tlsaclname, + vs->vd->tlsauthzid, &err); if (!tls) { VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); diff --git a/ui/vnc.c b/ui/vnc.c index 359693238b..9fb8430c35 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -33,7 +33,7 @@ #include "qemu/option.h" #include "qemu/sockets.h" #include "qemu/timer.h" -#include "qemu/acl.h" +#include "authz/list.h" #include "qemu/config-file.h" #include "qapi/qapi-events.h" #include "qapi/error.h" @@ -3267,12 +3267,24 @@ static void vnc_display_close(VncDisplay *vd) object_unparent(OBJECT(vd->tlscreds)); vd->tlscreds =3D NULL; } - g_free(vd->tlsaclname); - vd->tlsaclname =3D NULL; + if (vd->tlsauthz) { + object_unparent(OBJECT(vd->tlsauthz)); + vd->tlsauthz =3D NULL; + } + g_free(vd->tlsauthzid); + vd->tlsauthzid =3D NULL; if (vd->lock_key_sync) { qemu_remove_led_event_handler(vd->led); vd->led =3D NULL; } +#ifdef CONFIG_VNC_SASL + if (vd->sasl.authz) { + object_unparent(OBJECT(vd->sasl.authz)); + vd->sasl.authz =3D NULL; + } + g_free(vd->sasl.authzid); + vd->sasl.authzid =3D NULL; +#endif } =20 int vnc_display_password(const char *id, const char *password) @@ -4019,23 +4031,24 @@ void vnc_display_open(const char *id, Error **errp) =20 if (acl) { if (strcmp(vd->id, "default") =3D=3D 0) { - vd->tlsaclname =3D g_strdup("vnc.x509dname"); + vd->tlsauthzid =3D g_strdup("vnc.x509dname"); } else { - vd->tlsaclname =3D g_strdup_printf("vnc.%s.x509dname", vd->id); + vd->tlsauthzid =3D g_strdup_printf("vnc.%s.x509dname", vd->id); } - qemu_acl_init(vd->tlsaclname); + vd->tlsauthz =3D QAUTHZ(qauthz_list_new(vd->tlsauthzid, + QAUTHZ_LIST_POLICY_DENY, + &error_abort)); } #ifdef CONFIG_VNC_SASL if (acl && sasl) { - char *aclname; - if (strcmp(vd->id, "default") =3D=3D 0) { - aclname =3D g_strdup("vnc.username"); + vd->sasl.authzid =3D g_strdup("vnc.username"); } else { - aclname =3D g_strdup_printf("vnc.%s.username", vd->id); + vd->sasl.authzid =3D g_strdup_printf("vnc.%s.username", vd->id= ); } - vd->sasl.acl =3D qemu_acl_init(aclname); - g_free(aclname); + vd->sasl.authz =3D QAUTHZ(qauthz_list_new(vd->sasl.authzid, + QAUTHZ_LIST_POLICY_DENY, + &error_abort)); } #endif =20 diff --git a/ui/vnc.h b/ui/vnc.h index 762632929b..d2efb14620 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -39,6 +39,7 @@ #include "io/channel-socket.h" #include "io/channel-tls.h" #include "io/net-listener.h" +#include "authz/base.h" #include =20 #include "keymaps.h" @@ -177,7 +178,8 @@ struct VncDisplay bool lossy; bool non_adaptive; QCryptoTLSCreds *tlscreds; - char *tlsaclname; + QAuthZ *tlsauthz; + char *tlsauthzid; #ifdef CONFIG_VNC_SASL VncDisplaySASL sasl; #endif diff --git a/util/Makefile.objs b/util/Makefile.objs index 769d295a27..99a808014e 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -19,7 +19,6 @@ util-obj-y +=3D envlist.o path.o module.o util-obj-y +=3D host-utils.o util-obj-y +=3D bitmap.o bitops.o hbitmap.o util-obj-y +=3D fifo8.o -util-obj-y +=3D acl.o util-obj-y +=3D cacheinfo.o util-obj-y +=3D error.o qemu-error.o util-obj-y +=3D id.o diff --git a/util/acl.c b/util/acl.c deleted file mode 100644 index c105addadc..0000000000 --- a/util/acl.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * QEMU access control list management - * - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a= copy - * of this software and associated documentation files (the "Software"), t= o deal - * in the Software without restriction, including without limitation the r= ights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included= in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN - * THE SOFTWARE. - */ - - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/acl.h" - -#ifdef CONFIG_FNMATCH -#include -#endif - - -static unsigned int nacls =3D 0; -static qemu_acl **acls =3D NULL; - - - -qemu_acl *qemu_acl_find(const char *aclname) -{ - int i; - for (i =3D 0 ; i < nacls ; i++) { - if (strcmp(acls[i]->aclname, aclname) =3D=3D 0) - return acls[i]; - } - - return NULL; -} - -qemu_acl *qemu_acl_init(const char *aclname) -{ - qemu_acl *acl; - - acl =3D qemu_acl_find(aclname); - if (acl) - return acl; - - acl =3D g_malloc(sizeof(*acl)); - acl->aclname =3D g_strdup(aclname); - /* Deny by default, so there is no window of "open - * access" between QEMU starting, and the user setting - * up ACLs in the monitor */ - acl->defaultDeny =3D 1; - - acl->nentries =3D 0; - QTAILQ_INIT(&acl->entries); - - acls =3D g_realloc(acls, sizeof(*acls) * (nacls +1)); - acls[nacls] =3D acl; - nacls++; - - return acl; -} - -int qemu_acl_party_is_allowed(qemu_acl *acl, - const char *party) -{ - qemu_acl_entry *entry; - - QTAILQ_FOREACH(entry, &acl->entries, next) { -#ifdef CONFIG_FNMATCH - if (fnmatch(entry->match, party, 0) =3D=3D 0) - return entry->deny ? 0 : 1; -#else - /* No fnmatch, so fallback to exact string matching - * instead of allowing wildcards */ - if (strcmp(entry->match, party) =3D=3D 0) - return entry->deny ? 0 : 1; -#endif - } - - return acl->defaultDeny ? 0 : 1; -} - - -void qemu_acl_reset(qemu_acl *acl) -{ - qemu_acl_entry *entry, *next_entry; - - /* Put back to deny by default, so there is no window - * of "open access" while the user re-initializes the - * access control list */ - acl->defaultDeny =3D 1; - QTAILQ_FOREACH_SAFE(entry, &acl->entries, next, next_entry) { - QTAILQ_REMOVE(&acl->entries, entry, next); - g_free(entry->match); - g_free(entry); - } - acl->nentries =3D 0; -} - - -int qemu_acl_append(qemu_acl *acl, - int deny, - const char *match) -{ - qemu_acl_entry *entry; - - entry =3D g_malloc(sizeof(*entry)); - entry->match =3D g_strdup(match); - entry->deny =3D deny; - - QTAILQ_INSERT_TAIL(&acl->entries, entry, next); - acl->nentries++; - - return acl->nentries; -} - - -int qemu_acl_insert(qemu_acl *acl, - int deny, - const char *match, - int index) -{ - qemu_acl_entry *tmp; - int i =3D 0; - - if (index <=3D 0) - return -1; - if (index > acl->nentries) { - return qemu_acl_append(acl, deny, match); - } - - QTAILQ_FOREACH(tmp, &acl->entries, next) { - i++; - if (i =3D=3D index) { - qemu_acl_entry *entry; - entry =3D g_malloc(sizeof(*entry)); - entry->match =3D g_strdup(match); - entry->deny =3D deny; - - QTAILQ_INSERT_BEFORE(tmp, entry, next); - acl->nentries++; - break; - } - } - - return i; -} - -int qemu_acl_remove(qemu_acl *acl, - const char *match) -{ - qemu_acl_entry *entry; - int i =3D 0; - - QTAILQ_FOREACH(entry, &acl->entries, next) { - i++; - if (strcmp(entry->match, match) =3D=3D 0) { - QTAILQ_REMOVE(&acl->entries, entry, next); - acl->nentries--; - g_free(entry->match); - g_free(entry); - return i; - } - } - return -1; -} --=20 2.17.0