[libvirt] [PATCH 0/4] CVE-2014-0028: domain events vs. ACL filtering

I have pushed the following series to the master branch, as well as the the backport of patch 4 to all branches impacted by the CVE (v1.1.0 onwards). Basically, when ACLs permit fine-grained control of what domains a user can manage, a user that was denied domain:getattr for a particular domain, or denied connect:search_domains in general, could use the event registration API to gain access to domains that should have been hidden from that user. The patch was reviewed offlist during the time when the vulnerability was under embargo. In the process of fixing this, I made quite a few improvements to the underlying mechanisms for events. Among other things, I want to switch libvirt over to using server-side filtering rather than the current implementation of client-side filtering, for increased efficiency in the case where a hypervisor hosts many guests but the client only cares about events on a small subset of those guests. The existing RPC calls for domain events did not allow this, but the brand new network events had not yet had their RPC baked with a formal release. At one point, I had tried making the use of connect:search_networks conditional on whether a non-NULL network had been requested, which requires server-side filtering. The final incarnation of the CVE fix no longer bypasses connect:search_networks for a NULL network, so the first three patches are now technically unrelated to the CVE fix; but as the work is already done and reviewed and as it is easier to avoid bloat in the RPC protocol by getting it right from the beginning, I still pushed those patches to the master branch. NOTE: if you were testing network events with libvirt.git or with the 1.2.1 release candidates, you must ensure that you match your client's use of libvirt.so with the libvirtd - early users of network events are unable to communicate with the RPC wire representation that will actually be in 1.2.1 as a result of this series. I will also be posting a followup series, for application after 1.2.1 is released, which adds server-side filtering of domain events, as the counterpart of the network event filtering added in this series. There, we already have existing RPC code baked into releases, so there is no longer a rush to get the patches in before a release freezes a mistake. Eric Blake (4): event: track callbackID on daemon side of RPC event: add notion of remoteID for filtering client network events event: wire up RPC for server-side network event filtering event: filter global events by domain:getattr ACL [CVE-2014-0028] daemon/libvirtd.h | 7 +- daemon/remote.c | 131 +++++++++++++++++-------- src/access/viraccessperm.h | 6 +- src/conf/domain_event.c | 38 ++++++- src/conf/domain_event.h | 10 +- src/conf/network_event.c | 69 ++++++++++++- src/conf/network_event.h | 18 +++- src/conf/object_event.c | 212 ++++++++++++++++++++++++++++++++-------- src/conf/object_event.h | 30 +++--- src/conf/object_event_private.h | 31 ++++++ src/libvirt_private.syms | 1 - src/libxl/libxl_driver.c | 2 + src/lxc/lxc_driver.c | 2 + src/network/bridge_driver.c | 1 + src/qemu/qemu_driver.c | 2 + src/remote/remote_driver.c | 86 +++++++++------- src/remote/remote_protocol.x | 23 ++--- src/remote_protocol-structs | 9 +- src/test/test_driver.c | 6 +- src/uml/uml_driver.c | 2 + src/vbox/vbox_tmpl.c | 4 +- src/xen/xen_driver.c | 2 + 22 files changed, 527 insertions(+), 165 deletions(-) -- 1.8.4.2

Right now, the daemon side of RPC events is hard-coded to at most one callback per eventID. But when there are hundreds of domains or networks coupled and multiple conections, then sending every event to every connection that wants an event, even for the connections that only care about events for a particular object, is inefficient. In order to track more than one callback in the server, we need to store callbacks by more than just their eventID. This patch rearranges the daemon side to store network callbacks in a dynamic array, which can eventually be used for multiple callbacks of the same eventID, although actual behavior is unchanged without further patches to the RPC protocol. For ease of review, domain events are saved for a later patch, as they touch more code. While at it, fix a bug where a malicious client could send a negative eventID to cause network event registration to access outside of array bounds (thankfully not a CVE, since domain events were already doing the bounds check, and since network events have not been released). * daemon/libvirtd.h (daemonClientPrivate): Alter the tracking of network events. * daemon/remote.c (daemonClientEventCallback): New struct. (remoteEventCallbackFree): New function. (remoteClientInitHook, remoteRelayNetworkEventLifecycle) (remoteClientFreeFunc) (remoteDispatchConnectNetworkEventRegisterAny): Track network callbacks differently. (remoteDispatchConnectNetworkEventDeregisterAny): Enforce bounds. Signed-off-by: Eric Blake <eblake@redhat.com> --- daemon/libvirtd.h | 7 +++- daemon/remote.c | 113 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 35 deletions(-) diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 47f2589..a260ea1 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -1,7 +1,7 @@ /* * libvirtd.h: daemon data structure definitions * - * Copyright (C) 2006-2013 Red Hat, Inc. + * Copyright (C) 2006-2014 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -43,6 +43,8 @@ typedef struct daemonClientStream daemonClientStream; typedef daemonClientStream *daemonClientStreamPtr; typedef struct daemonClientPrivate daemonClientPrivate; typedef daemonClientPrivate *daemonClientPrivatePtr; +typedef struct daemonClientEventCallback daemonClientEventCallback; +typedef daemonClientEventCallback *daemonClientEventCallbackPtr; /* Stores the per-client connection state */ struct daemonClientPrivate { @@ -50,7 +52,8 @@ struct daemonClientPrivate { virMutex lock; int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST]; - int networkEventCallbackID[VIR_NETWORK_EVENT_ID_LAST]; + daemonClientEventCallbackPtr *networkEventCallbacks; + size_t nnetworkEventCallbacks; # if WITH_SASL virNetSASLSessionPtr sasl; diff --git a/daemon/remote.c b/daemon/remote.c index 6ce9ec1..ae318b2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1,7 +1,7 @@ /* * remote.c: handlers for RPC method calls * - * Copyright (C) 2007-2013 Red Hat, Inc. + * Copyright (C) 2007-2014 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 @@ -72,6 +72,12 @@ # define HYPER_TO_ULONG(_to, _from) (_to) = (_from) #endif +struct daemonClientEventCallback { + virNetServerClientPtr client; + int eventID; + int callbackID; +}; + static virDomainPtr get_nonnull_domain(virConnectPtr conn, remote_nonnull_domain domain); static virNetworkPtr get_nonnull_network(virConnectPtr conn, remote_nonnull_network network); static virInterfacePtr get_nonnull_interface(virConnectPtr conn, remote_nonnull_interface iface); @@ -115,6 +121,12 @@ remoteDispatchObjectEventSend(virNetServerClientPtr client, xdrproc_t proc, void *data); +static void +remoteEventCallbackFree(void *opaque) +{ + VIR_FREE(opaque); +} + static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int event, @@ -654,19 +666,21 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); -static int remoteRelayNetworkEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, - virNetworkPtr net, - int event, - int detail, - void *opaque) +static int +remoteRelayNetworkEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, + virNetworkPtr net, + int event, + int detail, + void *opaque) { - virNetServerClientPtr client = opaque; + daemonClientEventCallbackPtr callback = opaque; remote_network_event_lifecycle_msg data; - if (!client) + if (callback->callbackID < 0) return -1; - VIR_DEBUG("Relaying network lifecycle event %d, detail %d", event, detail); + VIR_DEBUG("Relaying network lifecycle event %d, detail %d, callback %d", + event, detail, callback->callbackID); /* build return data */ memset(&data, 0, sizeof(data)); @@ -674,7 +688,7 @@ static int remoteRelayNetworkEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, data.event = event; data.detail = detail; - remoteDispatchObjectEventSend(client, remoteProgram, + remoteDispatchObjectEventSend(callback->client, remoteProgram, REMOTE_PROC_NETWORK_EVENT_LIFECYCLE, (xdrproc_t)xdr_remote_network_event_lifecycle_msg, &data); @@ -714,14 +728,20 @@ void remoteClientFreeFunc(void *data) priv->domainEventCallbackID[i] = -1; } - for (i = 0; i < VIR_NETWORK_EVENT_ID_LAST; i++) { - if (priv->networkEventCallbackID[i] != -1) { - VIR_DEBUG("Deregistering to relay remote events %zu", i); - virConnectNetworkEventDeregisterAny(priv->conn, - priv->networkEventCallbackID[i]); + for (i = 0; i < priv->nnetworkEventCallbacks; i++) { + int callbackID = priv->networkEventCallbacks[i]->callbackID; + if (callbackID < 0) { + VIR_WARN("unexpected incomplete network callback %zu", i); + continue; } - priv->networkEventCallbackID[i] = -1; + VIR_DEBUG("Deregistering remote network event relay %d", + callbackID); + priv->networkEventCallbacks[i]->callbackID = -1; + if (virConnectNetworkEventDeregisterAny(priv->conn, + callbackID) < 0) + VIR_WARN("unexpected network event deregister failure"); } + VIR_FREE(priv->networkEventCallbacks); virConnectClose(priv->conn); @@ -759,9 +779,6 @@ void *remoteClientInitHook(virNetServerClientPtr client, for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) priv->domainEventCallbackID[i] = -1; - for (i = 0; i < VIR_NETWORK_EVENT_ID_LAST; i++) - priv->networkEventCallbackID[i] = -1; - virNetServerClientSetCloseHook(client, remoteClientCloseFunc); return priv; } @@ -5264,7 +5281,7 @@ cleanup: static int remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, - virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetServerClientPtr client, virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_connect_network_event_register_any_args *args, @@ -5272,6 +5289,8 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN { int callbackID; int rv = -1; + daemonClientEventCallbackPtr callback = NULL; + daemonClientEventCallbackPtr ref; struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); @@ -5282,28 +5301,47 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN virMutexLock(&priv->lock); - if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported network event ID %d"), args->eventID); + if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST || args->eventID < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported network event ID %d"), args->eventID); goto cleanup; } - if (priv->networkEventCallbackID[args->eventID] != -1) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("network event %d already registered"), args->eventID); + /* If we call register first, we could append a complete callback + * to our array, but on OOM append failure, we'd have to then hope + * deregister works to undo our register. So instead we append an + * incomplete callback to our array, then register, then fix up + * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on + * success, we use 'ref' to save a copy of the pointer. */ + if (VIR_ALLOC(callback) < 0) + goto cleanup; + callback->client = client; + callback->eventID = args->eventID; + callback->callbackID = -1; + ref = callback; + if (VIR_APPEND_ELEMENT(priv->networkEventCallbacks, + priv->nnetworkEventCallbacks, + callback) < 0) goto cleanup; - } if ((callbackID = virConnectNetworkEventRegisterAny(priv->conn, NULL, args->eventID, networkEventCallbacks[args->eventID], - client, NULL)) < 0) + ref, + remoteEventCallbackFree)) < 0) { + VIR_SHRINK_N(priv->networkEventCallbacks, + priv->nnetworkEventCallbacks, 1); + callback = ref; goto cleanup; + } - priv->networkEventCallbackID[args->eventID] = callbackID; + ref->callbackID = callbackID; rv = 0; cleanup: + VIR_FREE(callback); if (rv < 0) virNetMessageSaveError(rerr); virMutexUnlock(&priv->lock); @@ -5313,7 +5351,7 @@ cleanup: static int remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, - virNetServerClientPtr client ATTRIBUTE_UNUSED, + virNetServerClientPtr client, virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_connect_network_event_deregister_any_args *args, @@ -5321,6 +5359,7 @@ remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_ { int callbackID = -1; int rv = -1; + size_t i; struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); @@ -5331,21 +5370,29 @@ remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_ virMutexLock(&priv->lock); - if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported event ID %d"), args->eventID); + if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST || args->eventID < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported event ID %d"), args->eventID); goto cleanup; } - callbackID = priv->networkEventCallbackID[args->eventID]; + for (i = 0; i < priv->nnetworkEventCallbacks; i++) { + if (priv->networkEventCallbacks[i]->eventID == args->eventID) { + callbackID = priv->networkEventCallbacks[i]->callbackID; + break; + } + } if (callbackID < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, _("network event %d not registered"), args->eventID); + virReportError(VIR_ERR_INTERNAL_ERROR, + _("network event %d not registered"), args->eventID); goto cleanup; } if (virConnectNetworkEventDeregisterAny(priv->conn, callbackID) < 0) goto cleanup; - priv->networkEventCallbackID[args->eventID] = -1; + VIR_DELETE_ELEMENT(priv->networkEventCallbacks, i, + priv->nnetworkEventCallbacks); rv = 0; -- 1.8.4.2

In order to mirror a server with per-object filtering, the client needs to track which server callbackID is servicing the client callback. This patch introduces the notion of a serverID, as well as the plumbing to use it for network events, although the actual complexity of using per-object filtering in the remote driver is deferred to a later patch. * src/conf/object_event.h (virObjectEventStateEventID): Add parameter. (virObjectEventStateQueueRemote, virObjectEventStateSetRemote): New prototypes. (virObjectEventStateRegisterID): Move... * src/conf/object_event_private.h: ...here, and add parameter. (_virObjectEvent): Add field. * src/conf/network_event.h (virNetworkEventStateRegisterClient): New prototype. * src/conf/object_event.c (_virObjectEventCallback): Add field. (virObjectEventStateSetRemote): New function. (virObjectEventStateQueue): Make wrapper around... (virObjectEventStateQueueRemote): New function. (virObjectEventCallbackListCount): Tweak return count when remote id matching is used. (virObjectEventCallbackLookup, virObjectEventStateRegisterID): Tweak registration when remote id matching will be used. (virObjectEventNew): Default to no remote id. (virObjectEventCallbackListAddID): Likewise, but set remote id when one is available. (virObjectEventCallbackListRemoveID) (virObjectEventCallbackListMarkDeleteID): Adjust return value when remote id was set. (virObjectEventStateEventID): Query existing id. (virObjectEventDispatchMatchCallback): Require matching event id. (virObjectEventStateCallbackID): Adjust caller. * src/conf/network_event.c (virNetworkEventStateRegisterClient): New function. (virNetworkEventStateRegisterID): Update caller. * src/conf/domain_event.c (virDomainEventStateRegister) (virDomainEventStateRegisterID): Update callers. * src/remote/remote_driver.c (remoteConnectNetworkEventRegisterAny) (remoteConnectNetworkEventDeregisterAny) (remoteConnectDomainEventDeregisterAny): Likewise. (remoteEventQueue): Hoist earlier to avoid forward declaration, and add parameter. Adjust all callers. * src/libvirt_private.syms (conf/object_event.h): Drop function. Signed-off-by: Eric Blake <eblake@redhat.com> --- src/conf/domain_event.c | 4 +- src/conf/network_event.c | 42 ++++++++- src/conf/network_event.h | 16 +++- src/conf/object_event.c | 189 ++++++++++++++++++++++++++++++++-------- src/conf/object_event.h | 30 ++++--- src/conf/object_event_private.h | 15 ++++ src/libvirt_private.syms | 1 - src/remote/remote_driver.c | 67 +++++++------- 8 files changed, 280 insertions(+), 84 deletions(-) diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 35212ef..b934cc7 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -1288,7 +1288,7 @@ virDomainEventStateRegister(virConnectPtr conn, virDomainEventClass, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_OBJECT_EVENT_CALLBACK(callback), - opaque, freecb, NULL); + opaque, freecb, NULL, false); } @@ -1325,7 +1325,7 @@ virDomainEventStateRegisterID(virConnectPtr conn, return virObjectEventStateRegisterID(conn, state, dom ? dom->uuid : NULL, virDomainEventClass, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), - opaque, freecb, callbackID); + opaque, freecb, callbackID, false); } diff --git a/src/conf/network_event.c b/src/conf/network_event.c index 3cd884d..d9c47b8 100644 --- a/src/conf/network_event.c +++ b/src/conf/network_event.c @@ -154,7 +154,47 @@ virNetworkEventStateRegisterID(virConnectPtr conn, return virObjectEventStateRegisterID(conn, state, net ? net->uuid : NULL, virNetworkEventClass, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), - opaque, freecb, callbackID); + opaque, freecb, callbackID, false); +} + + +/** + * virNetworkEventStateRegisterClient: + * @conn: connection to associate with callback + * @state: object event state + * @net: network to filter on or NULL for all networks + * @eventID: ID of the event type to register for + * @cb: function to invoke when event occurs + * @opaque: data blob to pass to @callback + * @freecb: callback to free @opaque + * @callbackID: filled with callback ID + * + * Register the function @cb with connection @conn, from @state, for + * events of type @eventID, and return the registration handle in + * @callbackID. This version is intended for use on the client side + * of RPC. + * + * Returns: the number of callbacks now registered, or -1 on error + */ +int +virNetworkEventStateRegisterClient(virConnectPtr conn, + virObjectEventStatePtr state, + virNetworkPtr net, + int eventID, + virConnectNetworkEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) +{ + if (virNetworkEventsInitialize() < 0) + return -1; + + /* FIXME: All servers that support network events should also support + * per-object filtering. */ + return virObjectEventStateRegisterID(conn, state, net ? net->uuid : NULL, + virNetworkEventClass, eventID, + VIR_OBJECT_EVENT_CALLBACK(cb), + opaque, freecb, callbackID, false); } diff --git a/src/conf/network_event.h b/src/conf/network_event.h index 843faf9..51bd949 100644 --- a/src/conf/network_event.h +++ b/src/conf/network_event.h @@ -36,7 +36,21 @@ virNetworkEventStateRegisterID(virConnectPtr conn, virConnectNetworkEventGenericCallback cb, void *opaque, virFreeCallback freecb, - int *callbackID); + int *callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) + ATTRIBUTE_NONNULL(8); + +int +virNetworkEventStateRegisterClient(virConnectPtr conn, + virObjectEventStatePtr state, + virNetworkPtr net, + int eventID, + virConnectNetworkEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) + ATTRIBUTE_NONNULL(8); virObjectEventPtr virNetworkEventLifecycleNew(const char *name, diff --git a/src/conf/object_event.c b/src/conf/object_event.c index b69387a..fa6af5c 100644 --- a/src/conf/object_event.c +++ b/src/conf/object_event.c @@ -66,6 +66,7 @@ struct _virObjectEventCallback { virClassPtr klass; int eventID; virConnectPtr conn; + int remoteID; bool uuid_filter; unsigned char uuid[VIR_UUID_BUFLEN]; virConnectObjectEventGenericCallback cb; @@ -148,19 +149,29 @@ virObjectEventCallbackListFree(virObjectEventCallbackListPtr list) * @cbList: the list * @klass: the base event class * @eventID: the event ID + * @uuid: optional uuid of per-object filtering + * @serverFilter: true if server supports object filtering * * Internal function to count how many callbacks remain registered for - * the given @eventID; knowing this allows the client side of the - * remote driver know when it must send an RPC to adjust the callbacks - * on the server. Note that this function intentionally ignores - * the legacy field, since RPC calls use only a single callback on - * the server to manage both legacy and modern domain lifecycle events. + * the given @eventID and @uuid; knowing this allows the client side + * of the remote driver know when it must send an RPC to adjust the + * callbacks on the server. When @serverFilter is false, this function + * returns a count that includes both global and per-object callbacks, + * since the remote side will use a single global event to feed both. + * When true, the count is limited to the callbacks with the same + * @uuid, and where a remoteID has already been set on the callback + * with virObjectEventStateSetRemote(). Note that this function + * intentionally ignores the legacy field, since RPC calls use only a + * single callback on the server to manage both legacy and modern + * global domain lifecycle events. */ static int virObjectEventCallbackListCount(virConnectPtr conn, virObjectEventCallbackListPtr cbList, virClassPtr klass, - int eventID) + int eventID, + unsigned char uuid[VIR_UUID_BUFLEN], + bool serverFilter) { size_t i; int ret = 0; @@ -171,7 +182,12 @@ virObjectEventCallbackListCount(virConnectPtr conn, if (cb->klass == klass && cb->eventID == eventID && cb->conn == conn && - !cb->deleted) + !cb->deleted && + (!serverFilter || + (cb->remoteID >= 0 && + ((uuid && cb->uuid_filter && + memcmp(cb->uuid, uuid, VIR_UUID_BUFLEN) == 0) || + (!uuid && !cb->uuid_filter))))) ret++; } return ret; @@ -196,17 +212,19 @@ virObjectEventCallbackListRemoveID(virConnectPtr conn, virObjectEventCallbackPtr cb = cbList->callbacks[i]; if (cb->callbackID == callbackID && cb->conn == conn) { - virClassPtr klass = cb->klass; - int eventID = cb->eventID; + int ret; + + ret = virObjectEventCallbackListCount(conn, cbList, cb->klass, + cb->eventID, + cb->uuid_filter ? cb->uuid : NULL, + cb->remoteID >= 0) - 1; if (cb->freecb) (*cb->freecb)(cb->opaque); virObjectUnref(cb->conn); VIR_FREE(cb); VIR_DELETE_ELEMENT(cbList->callbacks, i, cbList->count); - - return virObjectEventCallbackListCount(conn, cbList, klass, - eventID); + return ret; } } @@ -230,7 +248,9 @@ virObjectEventCallbackListMarkDeleteID(virConnectPtr conn, if (cb->callbackID == callbackID && cb->conn == conn) { cb->deleted = true; return virObjectEventCallbackListCount(conn, cbList, cb->klass, - cb->eventID); + cb->eventID, + cb->uuid_filter ? cb->uuid : NULL, + cb->remoteID >= 0); } } @@ -280,9 +300,13 @@ virObjectEventCallbackListPurgeMarked(virObjectEventCallbackListPtr cbList) * @eventID: the event ID * @callback: the callback to locate * @legacy: true if callback is tracked by function instead of callbackID + * @remoteID: optionally return a known remoteID * * Internal function to determine if @callback already has a - * callbackID in @cbList for the given @conn and other filters. + * callbackID in @cbList for the given @conn and other filters. If + * @remoteID is non-NULL, and another callback exists that can be + * serviced by the same remote event, then set it to that remote ID. + * * Return the id if found, or -1 with no error issued if not present. */ static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) @@ -292,29 +316,33 @@ virObjectEventCallbackLookup(virConnectPtr conn, virClassPtr klass, int eventID, virConnectObjectEventGenericCallback callback, - bool legacy) + bool legacy, + int *remoteID) { - int ret = -1; size_t i; + if (remoteID) + *remoteID = -1; + for (i = 0; i < cbList->count; i++) { virObjectEventCallbackPtr cb = cbList->callbacks[i]; if (cb->deleted) continue; - if (cb->cb == callback && - cb->klass == klass && + if (cb->klass == klass && cb->eventID == eventID && cb->conn == conn && cb->legacy == legacy && ((uuid && cb->uuid_filter && memcmp(cb->uuid, uuid, VIR_UUID_BUFLEN) == 0) || (!uuid && !cb->uuid_filter))) { - ret = cb->callbackID; - break; + if (remoteID) + *remoteID = cb->remoteID; + if (cb->cb == callback) + return cb->callbackID; } } - return ret; + return -1; } @@ -329,6 +357,7 @@ virObjectEventCallbackLookup(virConnectPtr conn, * @opaque: opaque data to pass to @callback * @freecb: callback to free @opaque * @callbackID: filled with callback ID + * @serverFilter: true if server supports object filtering * * Internal function to add a callback from a virObjectEventCallbackListPtr */ @@ -341,10 +370,12 @@ virObjectEventCallbackListAddID(virConnectPtr conn, virConnectObjectEventGenericCallback callback, void *opaque, virFreeCallback freecb, - int *callbackID) + int *callbackID, + bool serverFilter) { virObjectEventCallbackPtr event; int ret = -1; + int remoteID = -1; VIR_DEBUG("conn=%p cblist=%p uuid=%p " "klass=%p eventID=%d callback=%p opaque=%p", @@ -358,7 +389,8 @@ virObjectEventCallbackListAddID(virConnectPtr conn, /* check if we already have this callback on our list */ if (virObjectEventCallbackLookup(conn, cbList, uuid, klass, eventID, callback, - !callbackID) != -1) { + !callbackID, + serverFilter ? &remoteID : NULL) != -1) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("event callback already tracked")); return -1; @@ -373,6 +405,7 @@ virObjectEventCallbackListAddID(virConnectPtr conn, event->eventID = eventID; event->opaque = opaque; event->freecb = freecb; + event->remoteID = remoteID; /* Only need 'uuid' for matching; 'id' can change as domain * switches between running and shutoff, and 'name' can change in @@ -390,7 +423,10 @@ virObjectEventCallbackListAddID(virConnectPtr conn, if (VIR_APPEND_ELEMENT(cbList->callbacks, cbList->count, event) < 0) goto cleanup; - ret = virObjectEventCallbackListCount(conn, cbList, klass, eventID); + ret = virObjectEventCallbackListCount(conn, cbList, klass, eventID, + uuid, serverFilter); + if (serverFilter && remoteID < 0) + ret++; cleanup: if (event) @@ -588,6 +624,7 @@ virObjectEventNew(virClassPtr klass, event->dispatch = dispatcher; event->eventID = eventID; + event->remoteID = -1; if (VIR_STRDUP(event->meta.name, name) < 0) { VIR_FREE(event); @@ -635,6 +672,8 @@ virObjectEventDispatchMatchCallback(virObjectEventPtr event, return false; if (cb->eventID != event->eventID) return false; + if (cb->remoteID != event->remoteID) + return false; if (cb->uuid_filter) { /* Deliberately ignoring 'id' for matching, since that @@ -692,17 +731,21 @@ virObjectEventStateQueueDispatch(virObjectEventStatePtr state, /** - * virObjectEventStateQueue: + * virObjectEventStateQueueRemote: * @state: the event state object * @event: event to add to the queue + * @remoteID: limit dispatch to callbacks with the same remote id * * Adds @event to the queue of events to be dispatched at the next * safe moment. The caller should no longer use @event after this - * call. + * call. If @remoteID is non-negative, the event will only be sent to + * callbacks where virObjectEventStateSetRemote() registered a remote + * id. */ void -virObjectEventStateQueue(virObjectEventStatePtr state, - virObjectEventPtr event) +virObjectEventStateQueueRemote(virObjectEventStatePtr state, + virObjectEventPtr event, + int remoteID) { if (state->timer < 0) { virObjectUnref(event); @@ -711,6 +754,7 @@ virObjectEventStateQueue(virObjectEventStatePtr state, virObjectEventStateLock(state); + event->remoteID = remoteID; if (virObjectEventQueuePush(state->queue, event) < 0) { VIR_DEBUG("Error adding event to queue"); virObjectUnref(event); @@ -722,6 +766,23 @@ virObjectEventStateQueue(virObjectEventStatePtr state, } +/** + * virObjectEventStateQueue: + * @state: the event state object + * @event: event to add to the queue + * + * Adds @event to the queue of events to be dispatched at the next + * safe moment. The caller should no longer use @event after this + * call. + */ +void +virObjectEventStateQueue(virObjectEventStatePtr state, + virObjectEventPtr event) +{ + virObjectEventStateQueueRemote(state, event, -1); +} + + static void virObjectEventStateFlush(virObjectEventStatePtr state) { @@ -761,12 +822,27 @@ virObjectEventStateFlush(virObjectEventStatePtr state) * @opaque: data blob to pass to @callback * @freecb: callback to free @opaque * @callbackID: filled with callback ID + * @serverFilter: true if server supports object filtering * * Register the function @cb with connection @conn, from @state, for * events of type @eventID, and return the registration handle in * @callbackID. * - * Returns: the number of callbacks now registered, or -1 on error + * The return value is only important when registering client-side + * mirroring of remote events (since the public API is documented to + * return the callbackID rather than a count). A return of 1 means + * that this is the first use of this type of event, so a remote event + * must be enabled; a return larger than 1 means that an existing + * remote event can already feed this callback. If @serverFilter is + * false, the return count assumes that a single global remote feeds + * both generic and per-object callbacks, and that the event queue + * will be fed with virObjectEventStateQueue(). If it is true, then + * the return count assumes that the remote side is capable of per- + * object filtering; the user must call virObjectEventStateSetRemote() + * to record the remote id, and queue events with + * virObjectEventStateQueueRemote(). + * + * Returns: the number of callbacks now registered, or -1 on error. */ int virObjectEventStateRegisterID(virConnectPtr conn, @@ -777,7 +853,8 @@ virObjectEventStateRegisterID(virConnectPtr conn, virConnectObjectEventGenericCallback cb, void *opaque, virFreeCallback freecb, - int *callbackID) + int *callbackID, + bool serverFilter) { int ret = -1; @@ -797,7 +874,7 @@ virObjectEventStateRegisterID(virConnectPtr conn, ret = virObjectEventCallbackListAddID(conn, state->callbacks, uuid, klass, eventID, cb, opaque, freecb, - callbackID); + callbackID, serverFilter); if (ret == -1 && state->callbacks->count == 0 && @@ -874,7 +951,7 @@ virObjectEventStateCallbackID(virConnectPtr conn, virObjectEventStateLock(state); ret = virObjectEventCallbackLookup(conn, state->callbacks, NULL, - klass, eventID, callback, true); + klass, eventID, callback, true, NULL); virObjectEventStateUnlock(state); if (ret < 0) @@ -890,16 +967,20 @@ virObjectEventStateCallbackID(virConnectPtr conn, * @conn: connection associated with the callback * @state: object event state * @callbackID: the callback to query + * @remoteID: optionally output remote ID of the callback * - * Query what event ID type is associated with the - * callback @callbackID for connection @conn + * Query what event ID type is associated with the callback + * @callbackID for connection @conn. If @remoteID is non-null, it + * will be set to the remote id previously registered with + * virObjectEventStateSetRemote(). * * Returns 0 on success, -1 on error */ int virObjectEventStateEventID(virConnectPtr conn, virObjectEventStatePtr state, - int callbackID) + int callbackID, + int *remoteID) { int ret = -1; size_t i; @@ -913,6 +994,8 @@ virObjectEventStateEventID(virConnectPtr conn, continue; if (cb->callbackID == callbackID && cb->conn == conn) { + if (remoteID) + *remoteID = cb->remoteID; ret = cb->eventID; break; } @@ -925,3 +1008,39 @@ virObjectEventStateEventID(virConnectPtr conn, callbackID); return ret; } + + +/** + * virObjectEventStateSetRemote: + * @conn: connection associated with the callback + * @state: object event state + * @callbackID: the callback to adjust + * @remoteID: the remote ID to associate with the callback + * + * Update @callbackID for connection @conn to record that it is now + * tied to @remoteID, and will therefore only match events that are + * sent with virObjectEventStateQueueRemote() with the same remote ID. + * Silently does nothing if @callbackID is invalid. + */ +void +virObjectEventStateSetRemote(virConnectPtr conn, + virObjectEventStatePtr state, + int callbackID, + int remoteID) +{ + size_t i; + + virObjectEventStateLock(state); + for (i = 0; i < state->callbacks->count; i++) { + virObjectEventCallbackPtr cb = state->callbacks->callbacks[i]; + + if (cb->deleted) + continue; + + if (cb->callbackID == callbackID && cb->conn == conn) { + cb->remoteID = remoteID; + break; + } + } + virObjectEventStateUnlock(state); +} diff --git a/src/conf/object_event.h b/src/conf/object_event.h index 39e92d5..7654799 100644 --- a/src/conf/object_event.h +++ b/src/conf/object_event.h @@ -67,27 +67,31 @@ void virObjectEventStateQueue(virObjectEventStatePtr state, virObjectEventPtr event) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); -int -virObjectEventStateRegisterID(virConnectPtr conn, - virObjectEventStatePtr state, - unsigned char *uuid, - virClassPtr klass, - int eventID, - virConnectObjectEventGenericCallback cb, - void *opaque, - virFreeCallback freecb, - int *callbackID) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) - ATTRIBUTE_NONNULL(6); + +void +virObjectEventStateQueueRemote(virObjectEventStatePtr state, + virObjectEventPtr event, + int remoteID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + int virObjectEventStateDeregisterID(virConnectPtr conn, virObjectEventStatePtr state, int callbackID) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + int virObjectEventStateEventID(virConnectPtr conn, virObjectEventStatePtr state, - int callbackID) + int callbackID, + int *remoteID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +void +virObjectEventStateSetRemote(virConnectPtr conn, + virObjectEventStatePtr state, + int callbackID, + int remoteID) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif diff --git a/src/conf/object_event_private.h b/src/conf/object_event_private.h index 571fc40..445d726 100644 --- a/src/conf/object_event_private.h +++ b/src/conf/object_event_private.h @@ -48,6 +48,7 @@ struct _virObjectEvent { virObject parent; int eventID; virObjectMeta meta; + int remoteID; virObjectEventDispatchFunc dispatch; }; @@ -55,6 +56,20 @@ virClassPtr virClassForObjectEvent(void); int +virObjectEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + unsigned char *uuid, + virClassPtr klass, + int eventID, + virConnectObjectEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID, + bool remoteFilter) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) + ATTRIBUTE_NONNULL(6); + +int virObjectEventStateCallbackID(virConnectPtr conn, virObjectEventStatePtr state, virClassPtr klass, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3b3de15..91d1304 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -634,7 +634,6 @@ virObjectEventStateEventID; virObjectEventStateFree; virObjectEventStateNew; virObjectEventStateQueue; -virObjectEventStateRegisterID; # conf/secret_conf.h diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index da9c1c9..d719a49 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -151,7 +151,7 @@ static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst, virSto static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr secret_src); static void make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr nwfilter_src); static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst, virDomainSnapshotPtr snapshot_src); -static void remoteEventQueue(struct private_data *priv, virObjectEventPtr event); + /*----------------------------------------------------------------------*/ /* Helper functions for remoteOpen. */ @@ -2928,10 +2928,10 @@ remoteConnectNetworkEventRegisterAny(virConnectPtr conn, remoteDriverLock(priv); - if ((count = virNetworkEventStateRegisterID(conn, priv->eventState, - net, eventID, callback, - opaque, freecb, - &callbackID)) < 0) + if ((count = virNetworkEventStateRegisterClient(conn, priv->eventState, + net, eventID, callback, + opaque, freecb, + &callbackID)) < 0) goto done; /* If this is the first callback for this eventID, we need to enable @@ -2964,12 +2964,13 @@ remoteConnectNetworkEventDeregisterAny(virConnectPtr conn, int rv = -1; remote_connect_network_event_deregister_any_args args; int eventID; + int remoteID; int count; remoteDriverLock(priv); if ((eventID = virObjectEventStateEventID(conn, priv->eventState, - callbackID)) < 0) + callbackID, &remoteID)) < 0) goto done; if ((count = virObjectEventStateDeregisterID(conn, priv->eventState, @@ -4467,6 +4468,15 @@ done: static void +remoteEventQueue(struct private_data *priv, virObjectEventPtr event, + int remoteID) +{ + if (event) + virObjectEventStateQueueRemote(priv->eventState, event, remoteID); +} + + +static void remoteDomainBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque) @@ -4484,7 +4494,7 @@ remoteDomainBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, event = virDomainEventLifecycleNewFromDom(dom, msg->event, msg->detail); virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4506,7 +4516,7 @@ remoteDomainBuildEventReboot(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, event = virDomainEventRebootNewFromDom(dom); virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4528,7 +4538,7 @@ remoteDomainBuildEventRTCChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, event = virDomainEventRTCChangeNewFromDom(dom, msg->offset); virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4550,7 +4560,7 @@ remoteDomainBuildEventWatchdog(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, event = virDomainEventWatchdogNewFromDom(dom, msg->action); virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4575,7 +4585,7 @@ remoteDomainBuildEventIOError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, msg->action); virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4602,7 +4612,7 @@ remoteDomainBuildEventIOErrorReason(virNetClientProgramPtr prog ATTRIBUTE_UNUSED virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } static void @@ -4625,7 +4635,7 @@ remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } static void @@ -4681,7 +4691,7 @@ remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); return; error: @@ -4727,7 +4737,7 @@ remoteDomainBuildEventControlError(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4754,7 +4764,7 @@ remoteDomainBuildEventDiskChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4779,7 +4789,7 @@ remoteDomainBuildEventTrayChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } static void @@ -4801,7 +4811,7 @@ remoteDomainBuildEventPMWakeup(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } static void @@ -4823,7 +4833,7 @@ remoteDomainBuildEventPMSuspend(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4845,7 +4855,7 @@ remoteDomainBuildEventBalloonChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED event = virDomainEventBalloonChangeNewFromDom(dom, msg->actual); virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4868,7 +4878,7 @@ remoteDomainBuildEventPMSuspendDisk(virNetClientProgramPtr prog ATTRIBUTE_UNUSED virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4891,7 +4901,7 @@ remoteDomainBuildEventDeviceRemoved(virNetClientProgramPtr prog ATTRIBUTE_UNUSED virDomainFree(dom); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -4910,10 +4920,11 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, if (!net) return; - event = virNetworkEventLifecycleNew(net->name, net->uuid, msg->event, msg->detail); + event = virNetworkEventLifecycleNew(net->name, net->uuid, msg->event, + msg->detail); virNetworkFree(net); - remoteEventQueue(priv, event); + remoteEventQueue(priv, event, -1); } @@ -5264,7 +5275,7 @@ remoteConnectDomainEventDeregisterAny(virConnectPtr conn, remoteDriverLock(priv); if ((eventID = virObjectEventStateEventID(conn, priv->eventState, - callbackID)) < 0) + callbackID, NULL)) < 0) goto done; if ((count = virObjectEventStateDeregisterID(conn, priv->eventState, @@ -6798,12 +6809,6 @@ done: return rv; } -static void -remoteEventQueue(struct private_data *priv, virObjectEventPtr event) -{ - if (event) - virObjectEventStateQueue(priv->eventState, event); -} /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. -- 1.8.4.2

We haven't had a release with network events yet, so we are free to fix the RPC so that it actually does what we want. Doing client-side filtering of per-network events is inefficient if a connection is only interested in events on a single network out of hundreds available on the server. But to do server-side per-network filtering, the server needs to know which network to filter on - so we need to pass an optional network over on registration. Furthermore, it is possible to have a client with both a global and per-network filter; in the existing code, the server sends only one event and the client replicates to both callbacks. But with server-side filtering, the server will send the event twice, so we need a way for the client to know which callbackID is sending an event, to ensure that the client can filter out events from a registration that does not match the callbackID from the server. Likewise, the existing style of deregistering by eventID alone is fine; but in the new style, we have to remember which callbackID to delete. This patch fixes the RPC wire definition to contain all the needed pieces of information, and hooks into the server and client side improvements of the previous patches, in order to switch over to full server-side filtering of network events. Also, since we fixed this in time, all released versions of libvirtd that support network events also support per-network filtering, so we can hard-code that assumption into network_event.c. Converting domain events to server-side filtering will require the introduction of new RPC numbers, as well as a server feature bit that the client can use to tell whether to use old-style (server only supports global events) or new-style (server supports filtered events), so that is deferred to a later set of patches. * src/conf/network_event.c (virNetworkEventStateRegisterClient): Assume server-side filtering. * src/remote/remote_protocol.x (remote_connect_network_event_register_any_args): Add network argument. (remote_connect_network_event_register_any_ret): Return callbackID instead of count. (remote_connect_network_event_deregister_any_args): Pass callbackID instead of eventID. (remote_connect_network_event_deregister_any_ret): Drop unused type. (remote_network_event_lifecycle_msg): Add callbackID. * daemon/remote.c (remoteDispatchConnectNetworkEventDeregisterAny): Drop unused arg, and deal with callbackID from client. (remoteRelayNetworkEventLifecycle): Pass callbackID. (remoteDispatchConnectNetworkEventRegisterAny): Likewise, and recognize non-NULL network. * src/remote/remote_driver.c (remoteConnectNetworkEventRegisterAny): Pass network, and track server side id. (remoteConnectNetworkEventDeregisterAny): Deregister by callback id. (remoteNetworkBuildEventLifecycle): Pass remote id to event queue. * src/remote_protocol-structs: Regenerate. Signed-off-by: Eric Blake <eblake@redhat.com> --- daemon/remote.c | 34 +++++++++++++++++----------------- src/conf/network_event.c | 4 +--- src/remote/remote_driver.c | 17 ++++++++++++++--- src/remote/remote_protocol.x | 12 +++++------- src/remote_protocol-structs | 9 ++++----- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index ae318b2..d2aafbe 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -685,6 +685,7 @@ remoteRelayNetworkEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, /* build return data */ memset(&data, 0, sizeof(data)); make_nonnull_network(&data.net, net); + data.callbackID = callback->callbackID; data.event = event; data.detail = detail; @@ -5285,7 +5286,7 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, remote_connect_network_event_register_any_args *args, - remote_connect_network_event_register_any_ret *ret ATTRIBUTE_UNUSED) + remote_connect_network_event_register_any_ret *ret) { int callbackID; int rv = -1; @@ -5293,6 +5294,7 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN daemonClientEventCallbackPtr ref; struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + virNetworkPtr net = NULL; if (!priv->conn) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); @@ -5301,6 +5303,10 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN virMutexLock(&priv->lock); + if (args->net && + !(net = get_nonnull_network(priv->conn, *args->net))) + goto cleanup; + if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST || args->eventID < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported network event ID %d"), args->eventID); @@ -5325,7 +5331,7 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN goto cleanup; if ((callbackID = virConnectNetworkEventRegisterAny(priv->conn, - NULL, + net, args->eventID, networkEventCallbacks[args->eventID], ref, @@ -5337,6 +5343,7 @@ remoteDispatchConnectNetworkEventRegisterAny(virNetServerPtr server ATTRIBUTE_UN } ref->callbackID = callbackID; + ret->callbackID = callbackID; rv = 0; @@ -5344,6 +5351,8 @@ cleanup: VIR_FREE(callback); if (rv < 0) virNetMessageSaveError(rerr); + if (net) + virNetworkFree(net); virMutexUnlock(&priv->lock); return rv; } @@ -5354,10 +5363,8 @@ remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_ virNetServerClientPtr client, virNetMessagePtr msg ATTRIBUTE_UNUSED, virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, - remote_connect_network_event_deregister_any_args *args, - remote_connect_network_event_deregister_any_ret *ret ATTRIBUTE_UNUSED) + remote_connect_network_event_deregister_any_args *args) { - int callbackID = -1; int rv = -1; size_t i; struct daemonClientPrivate *priv = @@ -5370,25 +5377,18 @@ remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_ virMutexLock(&priv->lock); - if (args->eventID >= VIR_NETWORK_EVENT_ID_LAST || args->eventID < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported event ID %d"), args->eventID); - goto cleanup; - } - for (i = 0; i < priv->nnetworkEventCallbacks; i++) { - if (priv->networkEventCallbacks[i]->eventID == args->eventID) { - callbackID = priv->networkEventCallbacks[i]->callbackID; + if (priv->networkEventCallbacks[i]->callbackID == args->callbackID) break; - } } - if (callbackID < 0) { + if (i == priv->nnetworkEventCallbacks) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("network event %d not registered"), args->eventID); + _("network event callback %d not registered"), + args->callbackID); goto cleanup; } - if (virConnectNetworkEventDeregisterAny(priv->conn, callbackID) < 0) + if (virConnectNetworkEventDeregisterAny(priv->conn, args->callbackID) < 0) goto cleanup; VIR_DELETE_ELEMENT(priv->networkEventCallbacks, i, diff --git a/src/conf/network_event.c b/src/conf/network_event.c index d9c47b8..a0b60a0 100644 --- a/src/conf/network_event.c +++ b/src/conf/network_event.c @@ -189,12 +189,10 @@ virNetworkEventStateRegisterClient(virConnectPtr conn, if (virNetworkEventsInitialize() < 0) return -1; - /* FIXME: All servers that support network events should also support - * per-object filtering. */ return virObjectEventStateRegisterID(conn, state, net ? net->uuid : NULL, virNetworkEventClass, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), - opaque, freecb, callbackID, false); + opaque, freecb, callbackID, true); } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d719a49..18eb454 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2923,8 +2923,10 @@ remoteConnectNetworkEventRegisterAny(virConnectPtr conn, int rv = -1; struct private_data *priv = conn->privateData; remote_connect_network_event_register_any_args args; + remote_connect_network_event_register_any_ret ret; int callbackID; int count; + remote_nonnull_network network; remoteDriverLock(priv); @@ -2938,14 +2940,23 @@ remoteConnectNetworkEventRegisterAny(virConnectPtr conn, * events on the server */ if (count == 1) { args.eventID = eventID; + if (net) { + make_nonnull_network(&network, net); + args.net = &network; + } else { + args.net = NULL; + } + memset(&ret, 0, sizeof(ret)); if (call(conn, priv, 0, REMOTE_PROC_CONNECT_NETWORK_EVENT_REGISTER_ANY, (xdrproc_t) xdr_remote_connect_network_event_register_any_args, (char *) &args, - (xdrproc_t) xdr_void, (char *)NULL) == -1) { + (xdrproc_t) xdr_remote_connect_network_event_register_any_ret, (char *) &ret) == -1) { virObjectEventStateDeregisterID(conn, priv->eventState, callbackID); goto done; } + virObjectEventStateSetRemote(conn, priv->eventState, callbackID, + ret.callbackID); } rv = callbackID; @@ -2980,7 +2991,7 @@ remoteConnectNetworkEventDeregisterAny(virConnectPtr conn, /* If that was the last callback for this eventID, we need to disable * events on the server */ if (count == 0) { - args.eventID = eventID; + args.callbackID = remoteID; if (call(conn, priv, 0, REMOTE_PROC_CONNECT_NETWORK_EVENT_DEREGISTER_ANY, (xdrproc_t) xdr_remote_connect_network_event_deregister_any_args, (char *) &args, @@ -4924,7 +4935,7 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, msg->detail); virNetworkFree(net); - remoteEventQueue(priv, event, -1); + remoteEventQueue(priv, event, msg->callbackID); } diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index c3d544f..ae27a77 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3,7 +3,7 @@ * remote_internal driver and libvirtd. This protocol is * internal and may change at any time. * - * Copyright (C) 2006-2013 Red Hat, Inc. + * Copyright (C) 2006-2014 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 @@ -2851,21 +2851,19 @@ struct remote_connect_get_cpu_model_names_ret { struct remote_connect_network_event_register_any_args { int eventID; + remote_network net; }; struct remote_connect_network_event_register_any_ret { - int cb_registered; + int callbackID; }; struct remote_connect_network_event_deregister_any_args { - int eventID; -}; - -struct remote_connect_network_event_deregister_any_ret { - int cb_registered; + int callbackID; }; struct remote_network_event_lifecycle_msg { + int callbackID; remote_nonnull_network net; int event; int detail; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 7373d65..e58482e 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2330,17 +2330,16 @@ struct remote_connect_get_cpu_model_names_ret { }; struct remote_connect_network_event_register_any_args { int eventID; + remote_network net; }; struct remote_connect_network_event_register_any_ret { - int cb_registered; + int callbackID; }; struct remote_connect_network_event_deregister_any_args { - int eventID; -}; -struct remote_connect_network_event_deregister_any_ret { - int cb_registered; + int callbackID; }; struct remote_network_event_lifecycle_msg { + int callbackID; remote_nonnull_network net; int event; int detail; -- 1.8.4.2

Ever since ACL filtering was added in commit 7639736 (v1.1.1), a user could still use event registration to obtain access to a domain that they could not normally access via virDomainLookup* or virConnectListAllDomains and friends. We already have the framework in the RPC generator for creating the filter, and previous cleanup patches got us to the point that we can now wire the filter through the entire object event stack. Furthermore, whether or not domain:getattr is honored, use of global events is a form of obtaining a list of networks, which is covered by connect:search_domains added in a93cd08 (v1.1.0). Ideally, we'd have a way to enforce connect:search_domains when doing global registrations while omitting that check on a per-domain registration. But this patch just unconditionally requires connect:search_domains, even when no list could be obtained, based on the following observations: 1. Administrators are unlikely to grant domain:getattr for one or all domains while still denying connect:search_domains - a user that is able to manage domains will want to be able to manage them efficiently, but efficient management includes being able to list the domains they can access. The idea of denying connect:search_domains while still granting access to individual domains is therefore not adding any real security, but just serves as a layer of obscurity to annoy the end user. 2. In the current implementation, domain events are filtered on the client; the server has no idea if a domain filter was requested, and must therefore assume that all domain event requests are global. Even if we fix the RPC protocol to allow for server-side filtering for newer client/server combos, making the connect:serach_domains ACL check conditional on whether the domain argument was NULL won't benefit older clients. Therefore, we choose to document that connect:search_domains is a pre-requisite to any domain event management. Network events need the same treatment, with the obvious change of using connect:search_networks and network:getattr. * src/access/viraccessperm.h (VIR_ACCESS_PERM_CONNECT_SEARCH_DOMAINS) (VIR_ACCESS_PERM_CONNECT_SEARCH_NETWORKS): Document additional effect of the permission. * src/conf/domain_event.h (virDomainEventStateRegister) (virDomainEventStateRegisterID): Add new parameter. * src/conf/network_event.h (virNetworkEventStateRegisterID): Likewise. * src/conf/object_event_private.h (virObjectEventStateRegisterID): Likewise. * src/conf/object_event.c (_virObjectEventCallback): Track a filter. (virObjectEventDispatchMatchCallback): Use filter. (virObjectEventCallbackListAddID): Register filter. * src/conf/domain_event.c (virDomainEventFilter): New function. (virDomainEventStateRegister, virDomainEventStateRegisterID): Adjust callers. * src/conf/network_event.c (virNetworkEventFilter): New function. (virNetworkEventStateRegisterID): Adjust caller. * src/remote/remote_protocol.x (REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER) (REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER_ANY) (REMOTE_PROC_CONNECT_NETWORK_EVENT_REGISTER_ANY): Generate a filter, and require connect:search_domains instead of weaker connect:read. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventRegisterAny) (testConnectNetworkEventRegisterAny): Update callers. * src/remote/remote_driver.c (remoteConnectDomainEventRegister) (remoteConnectDomainEventRegisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventRegisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainGetXMLDesc): Likewise. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventRegisterAny): Likewise. * src/qemu/qemu_driver.c (qemuConnectDomainEventRegister) (qemuConnectDomainEventRegisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventRegisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventRegisterAny): Likewise. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventRegisterAny): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com> --- src/access/viraccessperm.h | 6 +++--- src/conf/domain_event.c | 34 ++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 10 +++++++--- src/conf/network_event.c | 31 ++++++++++++++++++++++++++++++- src/conf/network_event.h | 6 ++++-- src/conf/object_event.c | 23 +++++++++++++++++++---- src/conf/object_event_private.h | 20 ++++++++++++++++++-- src/libxl/libxl_driver.c | 2 ++ src/lxc/lxc_driver.c | 2 ++ src/network/bridge_driver.c | 1 + src/qemu/qemu_driver.c | 2 ++ src/remote/remote_driver.c | 4 ++-- src/remote/remote_protocol.x | 11 +++++++---- src/test/test_driver.c | 6 +++--- src/uml/uml_driver.c | 2 ++ src/vbox/vbox_tmpl.c | 4 ++-- src/xen/xen_driver.c | 2 ++ 17 files changed, 138 insertions(+), 28 deletions(-) diff --git a/src/access/viraccessperm.h b/src/access/viraccessperm.h index fdc461b..6d14f05 100644 --- a/src/access/viraccessperm.h +++ b/src/access/viraccessperm.h @@ -1,7 +1,7 @@ /* * viraccessperm.h: access control permissions * - * Copyright (C) 2012-2013 Red Hat, Inc. + * Copyright (C) 2012-2014 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 @@ -47,14 +47,14 @@ typedef enum { /** * @desc: List domains - * @message: Listing domains requires authorization + * @message: Listing domains or using domain events requires authorization * @anonymous: 1 */ VIR_ACCESS_PERM_CONNECT_SEARCH_DOMAINS, /** * @desc: List networks - * @message: Listing networks requires authorization + * @message: Listing networks or using network events requires authorization * @anonymous: 1 */ VIR_ACCESS_PERM_CONNECT_SEARCH_NETWORKS, diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index b934cc7..f56aed3 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -360,6 +360,30 @@ virDomainEventDeviceRemovedDispose(void *obj) } +/** + * virDomainEventFilter: + * @conn: pointer to the connection + * @event: the event to check + * @opaque: opaque data holding ACL filter to use + * + * Internal function to run ACL filtering before dispatching an event + */ +static bool +virDomainEventFilter(virConnectPtr conn, virObjectEventPtr event, void *opaque) +{ + virDomainDef dom; + virDomainObjListFilter filter = opaque; + + /* For now, we just create a virDomainDef with enough contents to + * satisfy what viraccessdriverpolkit.c references. This is a bit + * fragile, but I don't know of anything better. */ + dom.name = event->meta.name; + memcpy(dom.uuid, event->meta.uuid, VIR_UUID_BUFLEN); + + return (filter)(conn, &dom); +} + + static void * virDomainEventNew(virClassPtr klass, int eventID, @@ -1265,6 +1289,7 @@ cleanup: * virDomainEventStateRegister: * @conn: connection to associate with callback * @state: object event state + * @filter: optional ACL filter to limit which events can be sent * @callback: the callback to add * @opaque: data blob to pass to @callback * @freecb: callback to free @opaque @@ -1277,6 +1302,7 @@ cleanup: int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, + virDomainObjListFilter filter, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) @@ -1285,7 +1311,8 @@ virDomainEventStateRegister(virConnectPtr conn, return -1; return virObjectEventStateRegisterID(conn, state, NULL, - virDomainEventClass, + filter ? virDomainEventFilter : NULL, + filter, virDomainEventClass, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_OBJECT_EVENT_CALLBACK(callback), opaque, freecb, NULL, false); @@ -1296,6 +1323,7 @@ virDomainEventStateRegister(virConnectPtr conn, * virDomainEventStateRegisterID: * @conn: connection to associate with callback * @state: object event state + * @filter: optional ACL filter to limit which events can be sent * @dom: optional domain for filtering the event * @eventID: ID of the event type to register for * @cb: function to invoke when event fires @@ -1312,6 +1340,7 @@ virDomainEventStateRegister(virConnectPtr conn, int virDomainEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, + virDomainObjListFilter filter, virDomainPtr dom, int eventID, virConnectDomainEventGenericCallback cb, @@ -1323,7 +1352,8 @@ virDomainEventStateRegisterID(virConnectPtr conn, return -1; return virObjectEventStateRegisterID(conn, state, dom ? dom->uuid : NULL, - virDomainEventClass, eventID, + filter ? virDomainEventFilter : NULL, + filter, virDomainEventClass, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), opaque, freecb, callbackID, false); } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index b2ea580..b39d5cd 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -1,7 +1,7 @@ /* * domain_event.h: domain event queue processing helpers * - * Copyright (C) 2012 Red Hat, Inc. + * Copyright (C) 2012-2014 Red Hat, Inc. * Copyright (C) 2008 VirtualIron * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. * @@ -177,20 +177,24 @@ virDomainEventDeviceRemovedNewFromDom(virDomainPtr dom, int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, + virDomainObjListFilter filter, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4); + int virDomainEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, + virDomainObjListFilter filter, virDomainPtr dom, int eventID, virConnectDomainEventGenericCallback cb, void *opaque, virFreeCallback freecb, int *callbackID) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6); + int virDomainEventStateDeregister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/conf/network_event.c b/src/conf/network_event.c index a0b60a0..f27b7e9 100644 --- a/src/conf/network_event.c +++ b/src/conf/network_event.c @@ -122,9 +122,35 @@ cleanup: /** + * virNetworkEventFilter: + * @conn: pointer to the connection + * @event: the event to check + * @opaque: opaque data holding ACL filter to use + * + * Internal function to run ACL filtering before dispatching an event + */ +static bool +virNetworkEventFilter(virConnectPtr conn, virObjectEventPtr event, + void *opaque) +{ + virNetworkDef net; + virNetworkObjListFilter filter = opaque; + + /* For now, we just create a virNetworkDef with enough contents to + * satisfy what viraccessdriverpolkit.c references. This is a bit + * fragile, but I don't know of anything better. */ + net.name = event->meta.name; + memcpy(net.uuid, event->meta.uuid, VIR_UUID_BUFLEN); + + return (filter)(conn, &net); +} + + +/** * virNetworkEventStateRegisterID: * @conn: connection to associate with callback * @state: object event state + * @filter: optional ACL filter to limit which events can be sent * @net: network to filter on or NULL for all networks * @eventID: ID of the event type to register for * @cb: function to invoke when event occurs @@ -141,6 +167,7 @@ cleanup: int virNetworkEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, + virNetworkObjListFilter filter, virNetworkPtr net, int eventID, virConnectNetworkEventGenericCallback cb, @@ -152,7 +179,8 @@ virNetworkEventStateRegisterID(virConnectPtr conn, return -1; return virObjectEventStateRegisterID(conn, state, net ? net->uuid : NULL, - virNetworkEventClass, eventID, + filter ? virNetworkEventFilter : NULL, + filter, virNetworkEventClass, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), opaque, freecb, callbackID, false); } @@ -190,6 +218,7 @@ virNetworkEventStateRegisterClient(virConnectPtr conn, return -1; return virObjectEventStateRegisterID(conn, state, net ? net->uuid : NULL, + NULL, NULL, virNetworkEventClass, eventID, VIR_OBJECT_EVENT_CALLBACK(cb), opaque, freecb, callbackID, true); diff --git a/src/conf/network_event.h b/src/conf/network_event.h index 51bd949..0812752 100644 --- a/src/conf/network_event.h +++ b/src/conf/network_event.h @@ -24,6 +24,7 @@ #include "internal.h" #include "object_event.h" #include "object_event_private.h" +#include "network_conf.h" #ifndef __NETWORK_EVENT_H__ # define __NETWORK_EVENT_H__ @@ -31,14 +32,15 @@ int virNetworkEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, + virNetworkObjListFilter filter, virNetworkPtr net, int eventID, virConnectNetworkEventGenericCallback cb, void *opaque, virFreeCallback freecb, int *callbackID) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) - ATTRIBUTE_NONNULL(8); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6) + ATTRIBUTE_NONNULL(9); int virNetworkEventStateRegisterClient(virConnectPtr conn, diff --git a/src/conf/object_event.c b/src/conf/object_event.c index fa6af5c..aff4956 100644 --- a/src/conf/object_event.c +++ b/src/conf/object_event.c @@ -69,6 +69,8 @@ struct _virObjectEventCallback { int remoteID; bool uuid_filter; unsigned char uuid[VIR_UUID_BUFLEN]; + virObjectEventCallbackFilter filter; + void *filter_opaque; virConnectObjectEventGenericCallback cb; void *opaque; virFreeCallback freecb; @@ -350,7 +352,9 @@ virObjectEventCallbackLookup(virConnectPtr conn, * virObjectEventCallbackListAddID: * @conn: pointer to the connection * @cbList: the list - * @uuid: the uuid of the object to filter on + * @uuid: the optional uuid of the object to filter on + * @filter: optional last-ditch filter callback + * @filter_opaque: opaque data to pass to @filter * @klass: the base event class * @eventID: the event ID * @callback: the callback to add @@ -365,6 +369,8 @@ static int virObjectEventCallbackListAddID(virConnectPtr conn, virObjectEventCallbackListPtr cbList, unsigned char uuid[VIR_UUID_BUFLEN], + virObjectEventCallbackFilter filter, + void *filter_opaque, virClassPtr klass, int eventID, virConnectObjectEventGenericCallback callback, @@ -377,9 +383,10 @@ virObjectEventCallbackListAddID(virConnectPtr conn, int ret = -1; int remoteID = -1; - VIR_DEBUG("conn=%p cblist=%p uuid=%p " + VIR_DEBUG("conn=%p cblist=%p uuid=%p filter=%p filter_opaque=%p " "klass=%p eventID=%d callback=%p opaque=%p", - conn, cbList, uuid, klass, eventID, callback, opaque); + conn, cbList, uuid, filter, filter_opaque, + klass, eventID, callback, opaque); /* Check incoming */ if (!cbList) { @@ -414,6 +421,8 @@ virObjectEventCallbackListAddID(virConnectPtr conn, event->uuid_filter = true; memcpy(event->uuid, uuid, VIR_UUID_BUFLEN); } + event->filter = filter; + event->filter_opaque = filter_opaque; if (callbackID) *callbackID = event->callbackID; @@ -675,6 +684,9 @@ virObjectEventDispatchMatchCallback(virObjectEventPtr event, if (cb->remoteID != event->remoteID) return false; + if (cb->filter && !(cb->filter)(cb->conn, event, cb->filter_opaque)) + return false; + if (cb->uuid_filter) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between @@ -848,6 +860,8 @@ int virObjectEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, unsigned char *uuid, + virObjectEventCallbackFilter filter, + void *filter_opaque, virClassPtr klass, int eventID, virConnectObjectEventGenericCallback cb, @@ -872,7 +886,8 @@ virObjectEventStateRegisterID(virConnectPtr conn, } ret = virObjectEventCallbackListAddID(conn, state->callbacks, - uuid, klass, eventID, + uuid, filter, filter_opaque, + klass, eventID, cb, opaque, freecb, callbackID, serverFilter); diff --git a/src/conf/object_event_private.h b/src/conf/object_event_private.h index 445d726..8e265f9 100644 --- a/src/conf/object_event_private.h +++ b/src/conf/object_event_private.h @@ -52,6 +52,20 @@ struct _virObjectEvent { virObjectEventDispatchFunc dispatch; }; +/** + * virObjectEventCallbackFilter: + * @conn: the connection pointer + * @event: the event about to be dispatched + * @opaque: opaque data registered with the filter + * + * Callback to do final filtering for a reason not tracked directly by + * virObjectEventStateRegisterID(). Return false if @event must not + * be sent to @conn. + */ +typedef bool (*virObjectEventCallbackFilter)(virConnectPtr conn, + virObjectEventPtr event, + void *opaque); + virClassPtr virClassForObjectEvent(void); @@ -59,6 +73,8 @@ int virObjectEventStateRegisterID(virConnectPtr conn, virObjectEventStatePtr state, unsigned char *uuid, + virObjectEventCallbackFilter filter, + void *filter_opaque, virClassPtr klass, int eventID, virConnectObjectEventGenericCallback cb, @@ -66,8 +82,8 @@ virObjectEventStateRegisterID(virConnectPtr conn, virFreeCallback freecb, int *callbackID, bool remoteFilter) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) - ATTRIBUTE_NONNULL(6); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6) + ATTRIBUTE_NONNULL(8); int virObjectEventStateCallbackID(virConnectPtr conn, diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 61e3516..4115fff 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3654,6 +3654,7 @@ libxlConnectDomainEventRegister(virConnectPtr conn, if (virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb) < 0) return -1; @@ -4259,6 +4260,7 @@ libxlConnectDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eve if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 7e56a59..5ae4b65 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1293,6 +1293,7 @@ lxcConnectDomainEventRegister(virConnectPtr conn, if (virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb) < 0) return -1; @@ -1334,6 +1335,7 @@ lxcConnectDomainEventRegisterAny(virConnectPtr conn, if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 95e4b65..0b43a67 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2310,6 +2310,7 @@ networkConnectNetworkEventRegisterAny(virConnectPtr conn, goto cleanup; if (virNetworkEventStateRegisterID(conn, driver->networkEventState, + virConnectNetworkEventRegisterAnyCheckACL, net, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1949abe..ebb77dc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9959,6 +9959,7 @@ qemuConnectDomainEventRegister(virConnectPtr conn, if (virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb) < 0) goto cleanup; @@ -10007,6 +10008,7 @@ qemuConnectDomainEventRegisterAny(virConnectPtr conn, if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 18eb454..ca86e3c 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4427,7 +4427,7 @@ remoteConnectDomainEventRegister(virConnectPtr conn, remoteDriverLock(priv); - if ((count = virDomainEventStateRegister(conn, priv->eventState, + if ((count = virDomainEventStateRegister(conn, priv->eventState, NULL, callback, opaque, freecb)) < 0) goto done; @@ -5245,7 +5245,7 @@ remoteConnectDomainEventRegisterAny(virConnectPtr conn, remoteDriverLock(priv); - if ((count = virDomainEventStateRegisterID(conn, priv->eventState, + if ((count = virDomainEventStateRegisterID(conn, priv->eventState, NULL, dom, eventID, callback, opaque, freecb, &callbackID)) < 0) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ae27a77..f94a38a 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1955,7 +1955,7 @@ struct remote_node_device_destroy_args { /* * Events Register/Deregister: - * It would seem rpcgen does not like both args, and ret + * It would seem rpcgen does not like both args and ret * to be null. It will not generate the prototype otherwise. * Pass back a redundant boolean to force prototype generation. */ @@ -3642,7 +3642,8 @@ enum remote_procedure { /** * @generate: none * @priority: high - * @acl: connect:read + * @acl: connect:search_domains + * @aclfilter: domain:getattr */ REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER = 105, @@ -4074,7 +4075,8 @@ enum remote_procedure { /** * @generate: none * @priority: high - * @acl: connect:read + * @acl: connect:search_domains + * @aclfilter: domain:getattr */ REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER_ANY = 167, @@ -5045,7 +5047,8 @@ enum remote_procedure { /** * @generate: none * @priority: high - * @acl: connect:read + * @acl: connect:search_networks + * @aclfilter: network:getattr */ REMOTE_PROC_CONNECT_NETWORK_EVENT_REGISTER_ANY = 313, diff --git a/src/test/test_driver.c b/src/test/test_driver.c index b724f82..4c277bd 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -6145,7 +6145,7 @@ testConnectDomainEventRegister(virConnectPtr conn, int ret = 0; testDriverLock(driver); - if (virDomainEventStateRegister(conn, driver->eventState, + if (virDomainEventStateRegister(conn, driver->eventState, NULL, callback, opaque, freecb) < 0) ret = -1; testDriverUnlock(driver); @@ -6183,7 +6183,7 @@ testConnectDomainEventRegisterAny(virConnectPtr conn, int ret; testDriverLock(driver); - if (virDomainEventStateRegisterID(conn, driver->eventState, + if (virDomainEventStateRegisterID(conn, driver->eventState, NULL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; @@ -6221,7 +6221,7 @@ testConnectNetworkEventRegisterAny(virConnectPtr conn, int ret; testDriverLock(driver); - if (virNetworkEventStateRegisterID(conn, driver->eventState, + if (virNetworkEventStateRegisterID(conn, driver->eventState, NULL, net, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index ad29ebf..31ebf4a 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2618,6 +2618,7 @@ umlConnectDomainEventRegister(virConnectPtr conn, umlDriverLock(driver); if (virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb) < 0) ret = -1; umlDriverUnlock(driver); @@ -2662,6 +2663,7 @@ umlConnectDomainEventRegisterAny(virConnectPtr conn, umlDriverLock(driver); if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 0fcaf8e..241eb5e 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7328,7 +7328,7 @@ vboxConnectDomainEventRegister(virConnectPtr conn, * later you can iterate over them */ - ret = virDomainEventStateRegister(conn, data->domainEvents, + ret = virDomainEventStateRegister(conn, data->domainEvents, NULL, callback, opaque, freecb); VIR_DEBUG("virObjectEventStateRegister (ret = %d) (conn: %p, " "callback: %p, opaque: %p, " @@ -7425,7 +7425,7 @@ static int vboxConnectDomainEventRegisterAny(virConnectPtr conn, * later you can iterate over them */ - if (virDomainEventStateRegisterID(conn, data->domainEvents, + if (virDomainEventStateRegisterID(conn, data->domainEvents, NULL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index c45d10f..7a57624 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -2323,6 +2323,7 @@ xenUnifiedConnectDomainEventRegister(virConnectPtr conn, } if (virDomainEventStateRegister(conn, priv->domainEvents, + virConnectDomainEventRegisterCheckACL, callback, opaque, freefunc) < 0) ret = -1; @@ -2382,6 +2383,7 @@ xenUnifiedConnectDomainEventRegisterAny(virConnectPtr conn, } if (virDomainEventStateRegisterID(conn, priv->domainEvents, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freefunc, &ret) < 0) ret = -1; -- 1.8.4.2

Ever since ACL filtering was added in commit 7639736 (v1.1.1), a user could still use event registration to obtain access to a domain that they could not normally access via virDomainLookup* or virConnectListAllDomains and friends. We already have the framework in the RPC generator for creating the filter, and previous cleanup patches got us to the point that we can now wire the filter through the entire object event stack. Furthermore, whether or not domain:getattr is honored, use of global events is a form of obtaining a list of networks, which is covered by connect:search_domains added in a93cd08 (v1.1.0). Ideally, we'd have a way to enforce connect:search_domains when doing global registrations while omitting that check on a per-domain registration. But this patch just unconditionally requires connect:search_domains, even when no list could be obtained, based on the following observations: 1. Administrators are unlikely to grant domain:getattr for one or all domains while still denying connect:search_domains - a user that is able to manage domains will want to be able to manage them efficiently, but efficient management includes being able to list the domains they can access. The idea of denying connect:search_domains while still granting access to individual domains is therefore not adding any real security, but just serves as a layer of obscurity to annoy the end user. 2. In the current implementation, domain events are filtered on the client; the server has no idea if a domain filter was requested, and must therefore assume that all domain event requests are global. Even if we fix the RPC protocol to allow for server-side filtering for newer client/server combos, making the connect:serach_domains ACL check conditional on whether the domain argument was NULL won't benefit older clients. Therefore, we choose to document that connect:search_domains is a pre-requisite to any domain event management. Network events need the same treatment, with the obvious change of using connect:search_networks and network:getattr. * src/access/viraccessperm.h (VIR_ACCESS_PERM_CONNECT_SEARCH_DOMAINS) (VIR_ACCESS_PERM_CONNECT_SEARCH_NETWORKS): Document additional effect of the permission. * src/conf/domain_event.h (virDomainEventStateRegister) (virDomainEventStateRegisterID): Add new parameter. * src/conf/network_event.h (virNetworkEventStateRegisterID): Likewise. * src/conf/object_event_private.h (virObjectEventStateRegisterID): Likewise. * src/conf/object_event.c (_virObjectEventCallback): Track a filter. (virObjectEventDispatchMatchCallback): Use filter. (virObjectEventCallbackListAddID): Register filter. * src/conf/domain_event.c (virDomainEventFilter): New function. (virDomainEventStateRegister, virDomainEventStateRegisterID): Adjust callers. * src/conf/network_event.c (virNetworkEventFilter): New function. (virNetworkEventStateRegisterID): Adjust caller. * src/remote/remote_protocol.x (REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER) (REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER_ANY) (REMOTE_PROC_CONNECT_NETWORK_EVENT_REGISTER_ANY): Generate a filter, and require connect:search_domains instead of weaker connect:read. * src/test/test_driver.c (testConnectDomainEventRegister) (testConnectDomainEventRegisterAny) (testConnectNetworkEventRegisterAny): Update callers. * src/remote/remote_driver.c (remoteConnectDomainEventRegister) (remoteConnectDomainEventRegisterAny): Likewise. * src/xen/xen_driver.c (xenUnifiedConnectDomainEventRegister) (xenUnifiedConnectDomainEventRegisterAny): Likewise. * src/vbox/vbox_tmpl.c (vboxDomainGetXMLDesc): Likewise. * src/libxl/libxl_driver.c (libxlConnectDomainEventRegister) (libxlConnectDomainEventRegisterAny): Likewise. * src/qemu/qemu_driver.c (qemuConnectDomainEventRegister) (qemuConnectDomainEventRegisterAny): Likewise. * src/uml/uml_driver.c (umlConnectDomainEventRegister) (umlConnectDomainEventRegisterAny): Likewise. * src/network/bridge_driver.c (networkConnectNetworkEventRegisterAny): Likewise. * src/lxc/lxc_driver.c (lxcConnectDomainEventRegister) (lxcConnectDomainEventRegisterAny): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com> (cherry picked from commit f9f56340539d609cdc2e9d4ab812b9f146c3f100) Conflicts: src/conf/object_event.c - not backporting event refactoring src/conf/object_event_private.h - likewise src/conf/network_event.c - not backporting network events src/conf/network_event.h - likewise src/network/bridge_driver.c - likewise src/access/viraccessperm.h - likewise src/remote/remote_protocol.x - likewise src/conf/domain_event.c - includes code that upstream has in object_event src/conf/domain_event.h - context src/libxl/libxl_driver.c - context src/lxc/lxc_driver.c - context src/remote/remote_driver.c - context, not backporting network events src/test/test_driver.c - context, not backporting network events src/uml/uml_driver.c - context src/xen/xen_driver.c - context --- Posting as a courtesy - this is the backported version that I pushed to v1.1.1-maint through v1.2.0-maint. (v1.1.0-maint was much smaller - just the viraccessperm.h and remote_protocol.x changes, since 1.1.0 didn't have any filtering when using connect:search_domains). src/access/viraccessperm.h | 4 +-- src/conf/domain_event.c | 69 ++++++++++++++++++++++++++++++++++++++++++-- src/conf/domain_event.h | 8 +++-- src/libxl/libxl_driver.c | 2 ++ src/lxc/lxc_driver.c | 2 ++ src/qemu/qemu_driver.c | 2 ++ src/remote/remote_driver.c | 4 +-- src/remote/remote_protocol.x | 8 +++-- src/test/test_driver.c | 4 +-- src/uml/uml_driver.c | 2 ++ src/vbox/vbox_tmpl.c | 4 +-- src/xen/xen_driver.c | 2 ++ 12 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/access/viraccessperm.h b/src/access/viraccessperm.h index fdc461b..1036b08 100644 --- a/src/access/viraccessperm.h +++ b/src/access/viraccessperm.h @@ -1,7 +1,7 @@ /* * viraccessperm.h: access control permissions * - * Copyright (C) 2012-2013 Red Hat, Inc. + * Copyright (C) 2012-2014 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 @@ -47,7 +47,7 @@ typedef enum { /** * @desc: List domains - * @message: Listing domains requires authorization + * @message: Listing domains or using domain events requires authorization * @anonymous: 1 */ VIR_ACCESS_PERM_CONNECT_SEARCH_DOMAINS, diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index de55d08..1736aa0 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -32,6 +32,20 @@ #define VIR_FROM_THIS VIR_FROM_NONE +/** + * virObjectEventCallbackFilter: + * @conn: the connection pointer + * @event: the event about to be dispatched + * @opaque: opaque data registered with the filter + * + * Callback to do final filtering for a reason not tracked directly by + * virObjectEventStateRegisterID(). Return false if @event must not + * be sent to @conn. + */ +typedef bool (*virObjectEventCallbackFilter)(virConnectPtr conn, + virDomainEventPtr event, + void *opaque); + struct _virDomainMeta { int id; char *name; @@ -68,6 +82,8 @@ struct _virDomainEventCallback { int eventID; virConnectPtr conn; virDomainMetaPtr dom; + virObjectEventCallbackFilter filter; + void *filter_opaque; virConnectDomainEventGenericCallback cb; void *opaque; virFreeCallback freecb; @@ -344,6 +360,9 @@ virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) * virDomainEventCallbackListAddID: * @conn: pointer to the connection * @cbList: the list + * @dom: optional domain to filter on + * @filter optional last-ditch filter callback + * @filter_opaque: opaque data to pass to @filter * @eventID: the event ID * @callback: the callback to add * @opaque: opaque data tio pass to callback @@ -355,6 +374,8 @@ static int virDomainEventCallbackListAddID(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virDomainPtr dom, + virObjectEventCallbackFilter filter, + void *filter_opaque, int eventID, virConnectDomainEventGenericCallback callback, void *opaque, @@ -401,6 +422,8 @@ virDomainEventCallbackListAddID(virConnectPtr conn, memcpy(event->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); event->dom->id = dom->id; } + event->filter = filter; + event->filter_opaque = filter_opaque; /* Make space on list */ if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) @@ -440,6 +463,8 @@ error: * virDomainEventCallbackListAdd: * @conn: pointer to the connection * @cbList: the list + * @filter optional last-ditch filter callback + * @filter_opaque: opaque data to pass to @filter * @callback: the callback to add * @opaque: opaque data tio pass to callback * @@ -448,11 +473,14 @@ error: static int virDomainEventCallbackListAdd(virConnectPtr conn, virDomainEventCallbackListPtr cbList, + virObjectEventCallbackFilter filter, + void *filter_opaque, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) { return virDomainEventCallbackListAddID(conn, cbList, NULL, + filter, filter_opaque, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_DOMAIN_EVENT_CALLBACK(callback), opaque, freecb, NULL); @@ -680,6 +708,32 @@ static virDomainEventPtr virDomainEventNewInternal(int eventID, return event; } + +/** + * virDomainEventFilter: + * @conn: pointer to the connection + * @event: the event to check + * @opaque: opaque data holding ACL filter to use + * + * Internal function to run ACL filtering before dispatching an event + */ +static bool +virDomainEventFilter(virConnectPtr conn, virDomainEventPtr event, + void *opaque) +{ + virDomainDef dom; + virDomainObjListFilter filter = opaque; + + /* For now, we just create a virDomainDef with enough contents to + * satisfy what viraccessdriverpolkit.c references. This is a bit + * fragile, but I don't know of anything better. */ + dom.name = event->dom.name; + memcpy(dom.uuid, event->dom.uuid, VIR_UUID_BUFLEN); + + return (filter)(conn, &dom); +} + + virDomainEventPtr virDomainEventNew(int id, const char *name, const unsigned char *uuid, int type, int detail) @@ -1381,6 +1435,9 @@ static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, if (cb->eventID != event->eventID) return 0; + if (cb->filter && !(cb->filter)(cb->conn, event, cb->filter_opaque)) + return 0; + if (cb->dom) { /* Deliberately ignoring 'id' for matching, since that * will cause problems when a domain switches between @@ -1510,6 +1567,7 @@ virDomainEventStateFlush(virDomainEventStatePtr state) * virDomainEventStateRegister: * @conn: connection to associate with callback * @state: domain event state + * @filter: optional ACL filter to limit which events can be sent * @callback: function to remove from event * @opaque: data blob to pass to callback * @freecb: callback to free @opaque @@ -1522,6 +1580,7 @@ virDomainEventStateFlush(virDomainEventStatePtr state) int virDomainEventStateRegister(virConnectPtr conn, virDomainEventStatePtr state, + virDomainObjListFilter filter, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) @@ -1542,7 +1601,8 @@ virDomainEventStateRegister(virConnectPtr conn, } ret = virDomainEventCallbackListAdd(conn, state->callbacks, - callback, opaque, freecb); + filter ? virDomainEventFilter : NULL, + filter, callback, opaque, freecb); if (ret == -1 && state->callbacks->count == 0 && @@ -1561,6 +1621,7 @@ cleanup: * virDomainEventStateRegisterID: * @conn: connection to associate with callback * @state: domain event state + * @filter: optional ACL filter to limit which events can be sent * @eventID: ID of the event type to register for * @cb: function to remove from event * @opaque: data blob to pass to callback @@ -1575,6 +1636,7 @@ cleanup: int virDomainEventStateRegisterID(virConnectPtr conn, virDomainEventStatePtr state, + virDomainObjListFilter filter, virDomainPtr dom, int eventID, virConnectDomainEventGenericCallback cb, @@ -1597,8 +1659,9 @@ virDomainEventStateRegisterID(virConnectPtr conn, goto cleanup; } - ret = virDomainEventCallbackListAddID(conn, state->callbacks, - dom, eventID, cb, opaque, freecb, + ret = virDomainEventCallbackListAddID(conn, state->callbacks, dom, + filter ? virDomainEventFilter : NULL, + filter, eventID, cb, opaque, freecb, callbackID); if (ret == -1 && diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index f6b957d..9dc65e2 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -1,7 +1,7 @@ /* * domain_event.h: domain event queue processing helpers * - * Copyright (C) 2012 Red Hat, Inc. + * Copyright (C) 2012-2014 Red Hat, Inc. * Copyright (C) 2008 VirtualIron * * This library is free software; you can redistribute it and/or @@ -149,19 +149,21 @@ virDomainEventStateQueue(virDomainEventStatePtr state, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int virDomainEventStateRegister(virConnectPtr conn, virDomainEventStatePtr state, + virDomainObjListFilter filter, virConnectDomainEventCallback callback, void *opaque, virFreeCallback freecb) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4); int virDomainEventStateRegisterID(virConnectPtr conn, virDomainEventStatePtr state, + virDomainObjListFilter filter, virDomainPtr dom, int eventID, virConnectDomainEventGenericCallback cb, void *opaque, virFreeCallback freecb, int *callbackID) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(6); int virDomainEventStateDeregister(virConnectPtr conn, virDomainEventStatePtr state, diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 8b3f209..fabeb1a 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3529,6 +3529,7 @@ libxlConnectDomainEventRegister(virConnectPtr conn, ret = virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb); return ret; @@ -4128,6 +4129,7 @@ libxlConnectDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, int eve if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index fde05ec..8d70d21 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1294,6 +1294,7 @@ lxcConnectDomainEventRegister(virConnectPtr conn, ret = virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb); return ret; @@ -1334,6 +1335,7 @@ lxcConnectDomainEventRegisterAny(virConnectPtr conn, if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 31cf011..de510db 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9886,6 +9886,7 @@ qemuConnectDomainEventRegister(virConnectPtr conn, if (virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb) < 0) goto cleanup; @@ -9934,6 +9935,7 @@ qemuConnectDomainEventRegisterAny(virConnectPtr conn, if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c43ce1d..8872f6f 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4320,7 +4320,7 @@ static int remoteConnectDomainEventRegister(virConnectPtr conn, remoteDriverLock(priv); - if ((count = virDomainEventStateRegister(conn, priv->domainEventState, + if ((count = virDomainEventStateRegister(conn, priv->domainEventState, NULL, callback, opaque, freecb)) < 0) { virReportError(VIR_ERR_RPC, "%s", _("adding cb to list")); goto done; @@ -5106,7 +5106,7 @@ static int remoteConnectDomainEventRegisterAny(virConnectPtr conn, remoteDriverLock(priv); if ((count = virDomainEventStateRegisterID(conn, - priv->domainEventState, + priv->domainEventState, NULL, dom, eventID, callback, opaque, freecb, &callbackID)) < 0) { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f942670..8c40208 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1955,7 +1955,7 @@ struct remote_node_device_destroy_args { /* * Events Register/Deregister: - * It would seem rpcgen does not like both args, and ret + * It would seem rpcgen does not like both args and ret * to be null. It will not generate the prototype otherwise. * Pass back a redundant boolean to force prototype generation. */ @@ -3620,7 +3620,8 @@ enum remote_procedure { /** * @generate: none * @priority: high - * @acl: connect:read + * @acl: connect:search_domains + * @aclfilter: domain:getattr */ REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER = 105, @@ -4052,7 +4053,8 @@ enum remote_procedure { /** * @generate: none * @priority: high - * @acl: connect:read + * @acl: connect:search_domains + * @aclfilter: domain:getattr */ REMOTE_PROC_CONNECT_DOMAIN_EVENT_REGISTER_ANY = 167, diff --git a/src/test/test_driver.c b/src/test/test_driver.c index c1f1cd4..2025709 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5963,7 +5963,7 @@ testConnectDomainEventRegister(virConnectPtr conn, testDriverLock(driver); ret = virDomainEventStateRegister(conn, - driver->domainEventState, + driver->domainEventState, NULL, callback, opaque, freecb); testDriverUnlock(driver); @@ -6001,7 +6001,7 @@ testConnectDomainEventRegisterAny(virConnectPtr conn, testDriverLock(driver); if (virDomainEventStateRegisterID(conn, - driver->domainEventState, + driver->domainEventState, NULL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 9ac896a..65ed0d3 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2618,6 +2618,7 @@ umlConnectDomainEventRegister(virConnectPtr conn, umlDriverLock(driver); ret = virDomainEventStateRegister(conn, driver->domainEventState, + virConnectDomainEventRegisterCheckACL, callback, opaque, freecb); umlDriverUnlock(driver); @@ -2660,6 +2661,7 @@ umlConnectDomainEventRegisterAny(virConnectPtr conn, umlDriverLock(driver); if (virDomainEventStateRegisterID(conn, driver->domainEventState, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 9336514..390845a 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7326,7 +7326,7 @@ static int vboxConnectDomainEventRegister(virConnectPtr conn, * later you can iterate over them */ - ret = virDomainEventStateRegister(conn, data->domainEvents, + ret = virDomainEventStateRegister(conn, data->domainEvents, NULL, callback, opaque, freecb); VIR_DEBUG("virDomainEventStateRegister (ret = %d) (conn: %p, " "callback: %p, opaque: %p, " @@ -7418,7 +7418,7 @@ static int vboxConnectDomainEventRegisterAny(virConnectPtr conn, * later you can iterate over them */ - if (virDomainEventStateRegisterID(conn, data->domainEvents, + if (virDomainEventStateRegisterID(conn, data->domainEvents, NULL, dom, eventID, callback, opaque, freecb, &ret) < 0) ret = -1; diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 9ba1b10..f06891d 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -2326,6 +2326,7 @@ xenUnifiedConnectDomainEventRegister(virConnectPtr conn, } ret = virDomainEventStateRegister(conn, priv->domainEvents, + virConnectDomainEventRegisterCheckACL, callback, opaque, freefunc); xenUnifiedUnlock(priv); @@ -2383,6 +2384,7 @@ xenUnifiedConnectDomainEventRegisterAny(virConnectPtr conn, } if (virDomainEventStateRegisterID(conn, priv->domainEvents, + virConnectDomainEventRegisterAnyCheckACL, dom, eventID, callback, opaque, freefunc, &ret) < 0) ret = -1; -- 1.8.4.2
participants (1)
-
Eric Blake