From 8513fe9e30a65060fc8908f42756e44550176d7f Mon Sep 17 00:00:00 2001
From: Andrew Bartlett <abartlet@samba.org>
Date: Wed, 20 Oct 2021 11:36:58 +1300
Subject: [PATCH 228/266] CVE-2020-25722 Ensure the structural objectclass
 cannot be changed

If the structural objectclass is allowed to change, then the restrictions
locking an object to remaining a user or computer will not be enforcable.

Likewise other LDAP inheritance rules, which allow only certain
child objects can be bypassed, which can in turn allow creation of
(unprivileged) users where only DNS objects were expected.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14753
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14889

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>

Conflict:remove test
Reference:https://gitlab.com/samba-team/samba/-/commit/8513fe9e30a65060fc8908f42756e44550176d7f

---
 source4/dsdb/samdb/ldb_modules/objectclass.c  | 36 +++++++++++++++++++
 1 files changed, 36 insertions(+), 0 deletions(-)

diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c
index 36ab76e19fc..d8feff0262c 100644
--- a/source4/dsdb/samdb/ldb_modules/objectclass.c
+++ b/source4/dsdb/samdb/ldb_modules/objectclass.c
@@ -811,6 +811,7 @@ static int objectclass_do_mod(struct oc_context *ac)
 	struct ldb_message_element *oc_el_entry, *oc_el_change;
 	struct ldb_val *vals;
 	struct ldb_message *msg;
+	const struct dsdb_class *current_structural_objectclass;
 	const struct dsdb_class *objectclass;
 	unsigned int i, j, k;
 	bool found;
@@ -830,6 +831,22 @@ static int objectclass_do_mod(struct oc_context *ac)
 		return ldb_operr(ldb);
 	}
 
+	/*
+	 * Get the current new top-most structural object class
+	 *
+	 * We must not allow this to change
+	 */
+
+	current_structural_objectclass
+		= dsdb_get_last_structural_class(ac->schema,
+						 oc_el_entry);
+	if (current_structural_objectclass == NULL) {
+		ldb_asprintf_errstring(ldb,
+				       "objectclass: cannot find current structural objectclass on %s!",
+				       ldb_dn_get_linearized(ac->search_res->message->dn));
+		return LDB_ERR_OBJECT_CLASS_VIOLATION;
+	}
+
 	/* use a new message structure */
 	msg = ldb_msg_new(ac);
 	if (msg == NULL) {
@@ -939,6 +956,25 @@ static int objectclass_do_mod(struct oc_context *ac)
 			return LDB_ERR_OBJECT_CLASS_VIOLATION;
 		}
 
+		/*
+		 * Has (so far, we re-check for each and every
+		 * "objectclass" in the message) the structural
+		 * objectclass changed?
+		 */
+
+		if (objectclass != current_structural_objectclass) {
+			const char *dn
+				= ldb_dn_get_linearized(ac->search_res->message->dn);
+			ldb_asprintf_errstring(ldb,
+					       "objectclass: not permitted "
+					       "to change the structural "
+					       "objectClass on %s [%s] => [%s]!",
+					       dn,
+					       current_structural_objectclass->lDAPDisplayName,
+					       objectclass->lDAPDisplayName);
+			return LDB_ERR_OBJECT_CLASS_VIOLATION;
+		}
+
 		/* Check for unrelated objectclasses */
 		ret = check_unrelated_objectclasses(ac->module, ac->schema,
 						    objectclass,
-- 
2.23.0

