From 2443e986d1bee1266412ee324b821ab356301e72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Kuzn=C3=ADk?= <ondra@mistotebe.net>
Date: Thu, 13 Jan 2022 11:03:47 +0000
Subject: [PATCH] ITS#9772 Allow objectClass edits that don't actually change
 them

---
 servers/slapd/bconfig.c | 45 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
index 3b1a4b3bc..7b6840be3 100644
--- a/servers/slapd/bconfig.c
+++ b/servers/slapd/bconfig.c
@@ -5895,8 +5895,49 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
 	if ( !oc_at ) return LDAP_OBJECT_CLASS_VIOLATION;
 
 	for (ml = op->orm_modlist; ml; ml=ml->sml_next) {
-		if (ml->sml_desc == slap_schema.si_ad_objectClass)
-			return rc;
+		if (ml->sml_desc == slap_schema.si_ad_objectClass) {
+			/* We'd be fine comparing the structural objectclass before and
+			 * after, but AUXILIARY ocs exist so we have to check them all */
+			unsigned int i, j;
+
+			if ( ml->sml_numvals != oc_at->a_numvals ) {
+				snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+					"objectclass modification disallowed" );
+				return LDAP_UNWILLING_TO_PERFORM;
+			}
+
+			for ( i = 0; i < oc_at->a_numvals; i++ ) {
+				ObjectClass *new_oc, *old_oc = oc_bvfind( &oc_at->a_vals[i] );
+				int found = 0;
+
+				if ( old_oc == NULL ) {
+					snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+						"no objectClass named %s",
+						oc_at->a_vals[i].bv_val );
+					return LDAP_OBJECT_CLASS_VIOLATION;
+				}
+				for ( j = 0; j < ml->sml_numvals; j++ ) {
+					new_oc = oc_bvfind( &ml->sml_values[j] );
+					if ( new_oc == NULL ) {
+						snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+							"no objectClass named %s",
+							ml->sml_values[j].bv_val );
+						return LDAP_OBJECT_CLASS_VIOLATION;
+					}
+
+					if ( old_oc == new_oc ) {
+						found = 1;
+						break;
+					}
+				}
+
+				if ( !found ) {
+					snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+						"objectclass modification disallowed" );
+					return LDAP_UNWILLING_TO_PERFORM;
+				}
+			}
+		}
 	}
 
 	colst = count_ocs( oc_at, &nocs );
-- 
2.33.0

