File fb5a3190-CVE-2014-0028.patch of Package libvirt.openSUSE_13.1_Update

commit fb5a3190c6409897744a244c6e0d5e2d52d34b39
Author: Eric Blake <eblake@redhat.com>
Date:   Tue Jan 14 10:29:34 2014 -0700

    event: filter global events by domain:getattr ACL [CVE-2014-0028]
    
    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

Index: libvirt-1.1.2/src/access/viraccessperm.h
===================================================================
--- libvirt-1.1.2.orig/src/access/viraccessperm.h
+++ libvirt-1.1.2/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,
Index: libvirt-1.1.2/src/conf/domain_event.c
===================================================================
--- libvirt-1.1.2.orig/src/conf/domain_event.c
+++ libvirt-1.1.2/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;
@@ -337,6 +353,9 @@ virDomainEventCallbackListPurgeMarked(vi
  * 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
@@ -348,6 +367,8 @@ static int
 virDomainEventCallbackListAddID(virConnectPtr conn,
                                 virDomainEventCallbackListPtr cbList,
                                 virDomainPtr dom,
+                                virObjectEventCallbackFilter filter,
+                                void *filter_opaque,
                                 int eventID,
                                 virConnectDomainEventGenericCallback callback,
                                 void *opaque,
@@ -394,6 +415,8 @@ virDomainEventCallbackListAddID(virConne
         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)
@@ -433,6 +456,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
  *
@@ -441,11 +466,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);
@@ -673,6 +701,32 @@ static virDomainEventPtr virDomainEventN
     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)
@@ -1374,6 +1428,9 @@ static int virDomainEventDispatchMatchCa
     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
@@ -1503,6 +1560,7 @@ virDomainEventStateFlush(virDomainEventS
  * 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
@@ -1515,6 +1573,7 @@ virDomainEventStateFlush(virDomainEventS
 int
 virDomainEventStateRegister(virConnectPtr conn,
                             virDomainEventStatePtr state,
+                            virDomainObjListFilter filter,
                             virConnectDomainEventCallback callback,
                             void *opaque,
                             virFreeCallback freecb)
@@ -1535,7 +1594,8 @@ virDomainEventStateRegister(virConnectPt
     }
 
     ret = virDomainEventCallbackListAdd(conn, state->callbacks,
-                                        callback, opaque, freecb);
+                                        filter ? virDomainEventFilter : NULL,
+                                        filter, callback, opaque, freecb);
 
     if (ret == -1 &&
         state->callbacks->count == 0 &&
@@ -1554,6 +1614,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
@@ -1568,6 +1629,7 @@ cleanup:
 int
 virDomainEventStateRegisterID(virConnectPtr conn,
                               virDomainEventStatePtr state,
+                              virDomainObjListFilter filter,
                               virDomainPtr dom,
                               int eventID,
                               virConnectDomainEventGenericCallback cb,
@@ -1590,8 +1652,9 @@ virDomainEventStateRegisterID(virConnect
         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 &&
Index: libvirt-1.1.2/src/conf/domain_event.h
===================================================================
--- libvirt-1.1.2.orig/src/conf/domain_event.h
+++ libvirt-1.1.2/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(virDomainEventS
     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,
Index: libvirt-1.1.2/src/libxl/libxl_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/libxl/libxl_driver.c
+++ libvirt-1.1.2/src/libxl/libxl_driver.c
@@ -4202,6 +4202,7 @@ libxlConnectDomainEventRegister(virConne
     libxlDriverLock(driver);
     ret = virDomainEventStateRegister(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterCheckACL,
                                       callback, opaque, freecb);
     libxlDriverUnlock(driver);
 
@@ -4879,6 +4880,7 @@ libxlConnectDomainEventRegisterAny(virCo
     libxlDriverLock(driver);
     if (virDomainEventStateRegisterID(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterAnyCheckACL,
                                       dom, eventID, callback, opaque,
                                       freecb, &ret) < 0)
         ret = -1;
Index: libvirt-1.1.2/src/lxc/lxc_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/lxc/lxc_driver.c
+++ libvirt-1.1.2/src/lxc/lxc_driver.c
@@ -1295,6 +1295,7 @@ lxcConnectDomainEventRegister(virConnect
 
     ret = virDomainEventStateRegister(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterCheckACL,
                                       callback, opaque, freecb);
 
     return ret;
@@ -1335,6 +1336,7 @@ lxcConnectDomainEventRegisterAny(virConn
 
     if (virDomainEventStateRegisterID(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterAnyCheckACL,
                                       dom, eventID,
                                       callback, opaque, freecb, &ret) < 0)
         ret = -1;
Index: libvirt-1.1.2/src/qemu/qemu_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/qemu/qemu_driver.c
+++ libvirt-1.1.2/src/qemu/qemu_driver.c
@@ -9875,6 +9875,7 @@ qemuConnectDomainEventRegister(virConnec
 
     if (virDomainEventStateRegister(conn,
                                     driver->domainEventState,
+                                    virConnectDomainEventRegisterCheckACL,
                                     callback, opaque, freecb) < 0)
         goto cleanup;
 
@@ -9923,6 +9924,7 @@ qemuConnectDomainEventRegisterAny(virCon
 
     if (virDomainEventStateRegisterID(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterAnyCheckACL,
                                       dom, eventID,
                                       callback, opaque, freecb, &ret) < 0)
         ret = -1;
Index: libvirt-1.1.2/src/remote/remote_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/remote/remote_driver.c
+++ libvirt-1.1.2/src/remote/remote_driver.c
@@ -4292,7 +4292,7 @@ static int remoteConnectDomainEventRegis
 
     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;
@@ -5078,7 +5078,7 @@ static int remoteConnectDomainEventRegis
     remoteDriverLock(priv);
 
     if ((count = virDomainEventStateRegisterID(conn,
-                                               priv->domainEventState,
+                                               priv->domainEventState, NULL,
                                                dom, eventID,
                                                callback, opaque, freecb,
                                                &callbackID)) < 0) {
Index: libvirt-1.1.2/src/remote/remote_protocol.x
===================================================================
--- libvirt-1.1.2.orig/src/remote/remote_protocol.x
+++ libvirt-1.1.2/src/remote/remote_protocol.x
@@ -1952,7 +1952,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.
  */
@@ -3606,7 +3606,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,
 
@@ -4038,7 +4039,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,
 
Index: libvirt-1.1.2/src/test/test_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/test/test_driver.c
+++ libvirt-1.1.2/src/test/test_driver.c
@@ -5628,7 +5628,7 @@ testConnectDomainEventRegister(virConnec
 
     testDriverLock(driver);
     ret = virDomainEventStateRegister(conn,
-                                      driver->domainEventState,
+                                      driver->domainEventState, NULL,
                                       callback, opaque, freecb);
     testDriverUnlock(driver);
 
@@ -5666,7 +5666,7 @@ testConnectDomainEventRegisterAny(virCon
 
     testDriverLock(driver);
     if (virDomainEventStateRegisterID(conn,
-                                      driver->domainEventState,
+                                      driver->domainEventState, NULL,
                                       dom, eventID,
                                       callback, opaque, freecb, &ret) < 0)
         ret = -1;
Index: libvirt-1.1.2/src/uml/uml_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/uml/uml_driver.c
+++ libvirt-1.1.2/src/uml/uml_driver.c
@@ -2618,6 +2618,7 @@ umlConnectDomainEventRegister(virConnect
     umlDriverLock(driver);
     ret = virDomainEventStateRegister(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterCheckACL,
                                       callback, opaque, freecb);
     umlDriverUnlock(driver);
 
@@ -2660,6 +2661,7 @@ umlConnectDomainEventRegisterAny(virConn
     umlDriverLock(driver);
     if (virDomainEventStateRegisterID(conn,
                                       driver->domainEventState,
+                                      virConnectDomainEventRegisterAnyCheckACL,
                                       dom, eventID,
                                       callback, opaque, freecb, &ret) < 0)
         ret = -1;
Index: libvirt-1.1.2/src/vbox/vbox_tmpl.c
===================================================================
--- libvirt-1.1.2.orig/src/vbox/vbox_tmpl.c
+++ libvirt-1.1.2/src/vbox/vbox_tmpl.c
@@ -7265,7 +7265,7 @@ static int vboxConnectDomainEventRegiste
              * 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, "
@@ -7357,7 +7357,7 @@ static int vboxConnectDomainEventRegiste
              * 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;
Index: libvirt-1.1.2/src/xen/xen_driver.c
===================================================================
--- libvirt-1.1.2.orig/src/xen/xen_driver.c
+++ libvirt-1.1.2/src/xen/xen_driver.c
@@ -2306,6 +2306,7 @@ xenUnifiedConnectDomainEventRegister(vir
     }
 
     ret = virDomainEventStateRegister(conn, priv->domainEvents,
+                                      virConnectDomainEventRegisterCheckACL,
                                       callback, opaque, freefunc);
 
     xenUnifiedUnlock(priv);
@@ -2363,6 +2364,7 @@ xenUnifiedConnectDomainEventRegisterAny(
     }
 
     if (virDomainEventStateRegisterID(conn, priv->domainEvents,
+                                      virConnectDomainEventRegisterAnyCheckACL,
                                       dom, eventID,
                                       callback, opaque, freefunc, &ret) < 0)
         ret = -1;
openSUSE Build Service is sponsored by