97374c0
From 67d5ecd609b8f12346eadb40e547cd7e01d825dc Mon Sep 17 00:00:00 2001
97374c0
From: Juergen Gross <jgross@suse.com>
97374c0
Date: Tue, 13 Sep 2022 07:35:10 +0200
97374c0
Subject: tools/xenstore: fix checking node permissions
97374c0
97374c0
Today chk_domain_generation() is being used to check whether a node
97374c0
permission entry is still valid or whether it is referring to a domain
97374c0
no longer existing. This is done by comparing the node's and the
97374c0
domain's generation count.
97374c0
97374c0
In case no struct domain is existing for a checked domain, but the
97374c0
domain itself is valid, chk_domain_generation() assumes it is being
97374c0
called due to the first node created for a new domain and it will
97374c0
return success.
97374c0
97374c0
This might be wrong in case the checked permission is related to an
97374c0
old domain, which has just been replaced with a new domain using the
97374c0
same domid.
97374c0
97374c0
Fix that by letting chk_domain_generation() fail in case a struct
97374c0
domain isn't found. In order to cover the case of the first node for
97374c0
a new domain try to allocate the needed struct domain explicitly when
97374c0
processing the related SET_PERMS command. In case a referenced domain
97374c0
isn't existing, flag the related permission to be ignored right away.
97374c0
97374c0
This is XSA-417 / CVE-2022-42320.
97374c0
97374c0
Reported-by: Juergen Gross <jgross@suse.com>
97374c0
Signed-off-by: Juergen Gross <jgross@suse.com>
97374c0
Reviewed-by: Julien Grall <jgrall@amazon.com>
97374c0
97374c0
diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
97374c0
index 66bbeaf6bfb0..a0c176fa203e 100644
97374c0
--- a/tools/xenstore/xenstored_core.c
97374c0
+++ b/tools/xenstore/xenstored_core.c
97374c0
@@ -1753,6 +1753,11 @@ static int do_set_perms(const void *ctx, struct connection *conn,
97374c0
 	if (!xs_strings_to_perms(perms.p, perms.num, permstr))
97374c0
 		return errno;
97374c0
 
97374c0
+	if (domain_alloc_permrefs(&perms) < 0)
97374c0
+		return ENOMEM;
97374c0
+	if (perms.p[0].perms & XS_PERM_IGNORE)
97374c0
+		return ENOENT;
97374c0
+
97374c0
 	/* First arg is node name. */
97374c0
 	if (strstarts(in->buffer, "@")) {
97374c0
 		if (set_perms_special(conn, in->buffer, &perms))
97374c0
diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c
97374c0
index b9ff4ded8360..98b401fdec30 100644
97374c0
--- a/tools/xenstore/xenstored_domain.c
97374c0
+++ b/tools/xenstore/xenstored_domain.c
97374c0
@@ -907,7 +907,6 @@ int domain_entry_inc(struct connection *conn, struct node *node)
97374c0
  * count (used for testing whether a node permission is older than a domain).
97374c0
  *
97374c0
  * Return values:
97374c0
- * -1: error
97374c0
  *  0: domain has higher generation count (it is younger than a node with the
97374c0
  *     given count), or domain isn't existing any longer
97374c0
  *  1: domain is older than the node
97374c0
@@ -915,20 +914,38 @@ int domain_entry_inc(struct connection *conn, struct node *node)
97374c0
 static int chk_domain_generation(unsigned int domid, uint64_t gen)
97374c0
 {
97374c0
 	struct domain *d;
97374c0
-	xc_dominfo_t dominfo;
97374c0
 
97374c0
 	if (!xc_handle && domid == 0)
97374c0
 		return 1;
97374c0
 
97374c0
 	d = find_domain_struct(domid);
97374c0
-	if (d)
97374c0
-		return (d->generation <= gen) ? 1 : 0;
97374c0
 
97374c0
-	if (!get_domain_info(domid, &dominfo))
97374c0
-		return 0;
97374c0
+	return (d && d->generation <= gen) ? 1 : 0;
97374c0
+}
97374c0
 
97374c0
-	d = alloc_domain(NULL, domid);
97374c0
-	return d ? 1 : -1;
97374c0
+/*
97374c0
+ * Allocate all missing struct domain referenced by a permission set.
97374c0
+ * Any permission entries for not existing domains will be marked to be
97374c0
+ * ignored.
97374c0
+ */
97374c0
+int domain_alloc_permrefs(struct node_perms *perms)
97374c0
+{
97374c0
+	unsigned int i, domid;
97374c0
+	struct domain *d;
97374c0
+	xc_dominfo_t dominfo;
97374c0
+
97374c0
+	for (i = 0; i < perms->num; i++) {
97374c0
+		domid = perms->p[i].id;
97374c0
+		d = find_domain_struct(domid);
97374c0
+		if (!d) {
97374c0
+			if (!get_domain_info(domid, &dominfo))
97374c0
+				perms->p[i].perms |= XS_PERM_IGNORE;
97374c0
+			else if (!alloc_domain(NULL, domid))
97374c0
+				return ENOMEM;
97374c0
+		}
97374c0
+	}
97374c0
+
97374c0
+	return 0;
97374c0
 }
97374c0
 
97374c0
 /*
97374c0
@@ -941,8 +958,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node)
97374c0
 	int ret;
97374c0
 
97374c0
 	ret = chk_domain_generation(node->perms.p[0].id, node->generation);
97374c0
-	if (ret < 0)
97374c0
-		return errno;
97374c0
 
97374c0
 	/* If the owner doesn't exist any longer give it to priv domain. */
97374c0
 	if (!ret) {
97374c0
@@ -959,8 +974,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node)
97374c0
 			continue;
97374c0
 		ret = chk_domain_generation(node->perms.p[i].id,
97374c0
 					    node->generation);
97374c0
-		if (ret < 0)
97374c0
-			return errno;
97374c0
 		if (!ret)
97374c0
 			node->perms.p[i].perms |= XS_PERM_IGNORE;
97374c0
 	}
97374c0
diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h
97374c0
index 209442190911..7fe0a21d9e45 100644
97374c0
--- a/tools/xenstore/xenstored_domain.h
97374c0
+++ b/tools/xenstore/xenstored_domain.h
97374c0
@@ -63,6 +63,7 @@ bool domain_is_unprivileged(struct connection *conn);
97374c0
 
97374c0
 /* Remove node permissions for no longer existing domains. */
97374c0
 int domain_adjust_node_perms(struct connection *conn, struct node *node);
97374c0
+int domain_alloc_permrefs(struct node_perms *perms);
97374c0
 
97374c0
 /* Quota manipulation */
97374c0
 int domain_entry_inc(struct connection *conn, struct node *);