]> xenbits.xensource.com Git - xen.git/commitdiff
tools/xenstore: allow special watches for privileged callers only
authorJuergen Gross <jgross@suse.com>
Thu, 11 Jun 2020 14:12:45 +0000 (16:12 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 15 Dec 2020 13:36:41 +0000 (14:36 +0100)
The special watches "@introduceDomain" and "@releaseDomain" should be
allowed for privileged callers only, as they allow to gain information
about presence of other guests on the host. So send watch events for
those watches via privileged connections only.

In order to allow for disaggregated setups where e.g. driver domains
need to make use of those special watches add support for calling
"set permissions" for those special nodes, too.

This is part of XSA-115.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Julien Grall <jgrall@amazon.com>
Reviewed-by: Paul Durrant <paul@xen.org>
docs/misc/xenstore.txt
tools/xenstore/xenstored_core.c
tools/xenstore/xenstored_core.h
tools/xenstore/xenstored_domain.c
tools/xenstore/xenstored_domain.h
tools/xenstore/xenstored_watch.c

index 6f8569d5760f366c94f439ed75c83fd889496231..32969eb3fecd72f753ae75a416efc86ec65ca781 100644 (file)
@@ -170,6 +170,9 @@ SET_PERMS           <path>|<perm-as-string>|+?
                n<domid>        no access
        See http://wiki.xen.org/wiki/XenBus section
        `Permissions' for details of the permissions system.
+       It is possible to set permissions for the special watch paths
+       "@introduceDomain" and "@releaseDomain" to enable receiving those
+       watches in unprivileged domains.
 
 ---------- Watches ----------
 
@@ -194,6 +197,8 @@ WATCH                       <wpath>|<token>|?
            @releaseDomain      occurs on any domain crash or
                                shutdown, and also on RELEASE
                                and domain destruction
+       <wspecial> events are sent to privileged callers or explicitly
+       via SET_PERMS enabled domains only.
 
        When a watch is first set up it is triggered once straight
        away, with <path> equal to <wpath>.  Watches may be triggered
index f95f44d594c51af53369cd1f6f9b7b7f70d8bece..0308b57ff70600e4517e8d79b6b2fc8b39dd6389 100644 (file)
@@ -468,8 +468,8 @@ static int write_node(struct connection *conn, struct node *node,
        return write_node_raw(conn, &key, node, no_quota_check);
 }
 
-static enum xs_perm_type perm_for_conn(struct connection *conn,
-                                      const struct node_perms *perms)
+enum xs_perm_type perm_for_conn(struct connection *conn,
+                               const struct node_perms *perms)
 {
        unsigned int i;
        enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER;
@@ -1245,22 +1245,29 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in)
        if (perms.num < 2)
                return EINVAL;
 
-       /* First arg is node name. */
-       /* We must own node to do this (tools can do this too). */
-       node = get_node_canonicalized(conn, in, in->buffer, &name,
-                                     XS_PERM_WRITE | XS_PERM_OWNER);
-       if (!node)
-               return errno;
-
        permstr = in->buffer + strlen(in->buffer) + 1;
        perms.num--;
 
-       perms.p = talloc_array(node, struct xs_permissions, perms.num);
+       perms.p = talloc_array(in, struct xs_permissions, perms.num);
        if (!perms.p)
                return ENOMEM;
        if (!xs_strings_to_perms(perms.p, perms.num, permstr))
                return errno;
 
+       /* First arg is node name. */
+       if (strstarts(in->buffer, "@")) {
+               if (set_perms_special(conn, in->buffer, &perms))
+                       return errno;
+               send_ack(conn, XS_SET_PERMS);
+               return 0;
+       }
+
+       /* We must own node to do this (tools can do this too). */
+       node = get_node_canonicalized(conn, in, in->buffer, &name,
+                                     XS_PERM_WRITE | XS_PERM_OWNER);
+       if (!node)
+               return errno;
+
        /* Unprivileged domains may not change the owner. */
        if (domain_is_unprivileged(conn) &&
            perms.p[0].id != node->perms.p[0].id)
index a43bb555307d1efc61f325f88b5a135740d2b59f..2092067502aaa61a80df552f07baf1fa45bc2960 100644 (file)
@@ -167,6 +167,8 @@ struct node *get_node(struct connection *conn,
 struct connection *new_connection(connwritefn_t *write, connreadfn_t *read);
 void check_store(void);
 void corrupt(struct connection *conn, const char *fmt, ...);
+enum xs_perm_type perm_for_conn(struct connection *conn,
+                               const struct node_perms *perms);
 
 /* Is this a valid node name? */
 bool is_valid_nodename(const char *node);
index 433732b9267063224f0156c376a73150f672e30b..8888db697edbca15bd96b60a96ebc81261fa14e3 100644 (file)
@@ -41,6 +41,9 @@ static evtchn_port_t virq_port;
 
 xenevtchn_handle *xce_handle = NULL;
 
+static struct node_perms dom_release_perms;
+static struct node_perms dom_introduce_perms;
+
 struct domain
 {
        struct list_head list;
@@ -597,6 +600,59 @@ void restore_existing_connections(void)
 {
 }
 
+static int set_dom_perms_default(struct node_perms *perms)
+{
+       perms->num = 1;
+       perms->p = talloc_array(NULL, struct xs_permissions, perms->num);
+       if (!perms->p)
+               return -1;
+       perms->p->id = 0;
+       perms->p->perms = XS_PERM_NONE;
+
+       return 0;
+}
+
+static struct node_perms *get_perms_special(const char *name)
+{
+       if (!strcmp(name, "@releaseDomain"))
+               return &dom_release_perms;
+       if (!strcmp(name, "@introduceDomain"))
+               return &dom_introduce_perms;
+       return NULL;
+}
+
+int set_perms_special(struct connection *conn, const char *name,
+                     struct node_perms *perms)
+{
+       struct node_perms *p;
+
+       p = get_perms_special(name);
+       if (!p)
+               return EINVAL;
+
+       if ((perm_for_conn(conn, p) & (XS_PERM_WRITE | XS_PERM_OWNER)) !=
+           (XS_PERM_WRITE | XS_PERM_OWNER))
+               return EACCES;
+
+       p->num = perms->num;
+       talloc_free(p->p);
+       p->p = perms->p;
+       talloc_steal(NULL, perms->p);
+
+       return 0;
+}
+
+bool check_perms_special(const char *name, struct connection *conn)
+{
+       struct node_perms *p;
+
+       p = get_perms_special(name);
+       if (!p)
+               return false;
+
+       return perm_for_conn(conn, p) & XS_PERM_READ;
+}
+
 static int dom0_init(void) 
 { 
        evtchn_port_t port;
@@ -618,6 +674,10 @@ static int dom0_init(void)
 
        xenevtchn_notify(xce_handle, dom0->port);
 
+       if (set_dom_perms_default(&dom_release_perms) ||
+           set_dom_perms_default(&dom_introduce_perms))
+               return -1;
+
        return 0; 
 }
 
index 56ae01597475672a285a31eb7d301d36ccce96a9..259183962a9c782f1340ff4523df01265930fab6 100644 (file)
@@ -65,6 +65,11 @@ void domain_watch_inc(struct connection *conn);
 void domain_watch_dec(struct connection *conn);
 int domain_watch(struct connection *conn);
 
+/* Special node permission handling. */
+int set_perms_special(struct connection *conn, const char *name,
+                     struct node_perms *perms);
+bool check_perms_special(const char *name, struct connection *conn);
+
 /* Write rate limiting */
 
 #define WRL_FACTOR   1000 /* for fixed-point arithmetic */
index 7ca18e03488ce3c7cbcb18288ce8c70308c5ff58..fc7e5ce3cb14ad550b791df12576b281d17fcb8b 100644 (file)
@@ -133,6 +133,10 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name,
 
        /* Create an event for each watch. */
        list_for_each_entry(i, &connections, list) {
+               /* introduce/release domain watches */
+               if (check_special_event(name) && !check_perms_special(name, i))
+                       continue;
+
                list_for_each_entry(watch, &i->watches, list) {
                        if (exact) {
                                if (streq(name, watch->node))