[Fedora-directory-commits] ldapserver/ldap/servers/plugins/replication windows_protocol_util.c, 1.45, 1.46

Nathan Kinder nkinder at fedoraproject.org
Fri Jan 9 17:24:32 UTC 2009


Author: nkinder

Update of /cvs/dirsec/ldapserver/ldap/servers/plugins/replication
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv4170/ldap/servers/plugins/replication

Modified Files:
	windows_protocol_util.c 
Log Message:
Resolves: 381361
Summary: Add support for synchronizing the cn attribute between DS and AD.



Index: windows_protocol_util.c
===================================================================
RCS file: /cvs/dirsec/ldapserver/ldap/servers/plugins/replication/windows_protocol_util.c,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -r1.45 -r1.46
--- windows_protocol_util.c	7 Jan 2009 21:45:55 -0000	1.45
+++ windows_protocol_util.c	9 Jan 2009 17:24:29 -0000	1.46
@@ -81,6 +81,8 @@
 static int windows_update_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry);
 static int is_guid_dn(Slapi_DN *remote_dn);
 static int map_windows_tombstone_dn(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *exists);
+static int windows_check_mods_for_rdn_change(Private_Repl_Protocol *prp, LDAPMod **original_mods, 
+		Slapi_Entry *local_entry, Slapi_DN *remote_dn, char **newrdn);
 
 
 /* Controls the direction of flow for mapped attributes */
@@ -207,13 +209,13 @@
 	{ FAKE_STREET_ATTR_NAME, "street", fromwindowsonly, always, normal},
 	{ "userParameters", "ntUserParms", bidirectional, always, normal},
 	{ "userWorkstations", "ntUserWorkstations", bidirectional, always, normal},
-    { "sAMAccountName", "ntUserDomainId", bidirectional, always, normal},
-	/* cn is a naming attribute in AD, so we don't want to change it after entry creation */
-    { "cn", "cn", towindowsonly, createonly, normal},
+	{ "sAMAccountName", "ntUserDomainId", bidirectional, always, normal},
+	/* AD uses cn as it's naming attribute.  We handle it as a special case */
+	{ "cn", "cn", towindowsonly, createonly, normal},
 	/* However, it isn't a naming attribute in DS (we use uid) so it's safe to accept changes inbound */
-    { "name", "cn", fromwindowsonly, always, normal},
-    { "manager", "manager", bidirectional, always, dnmap},
-    { "seealso", "seealso", bidirectional, always, dnmap},
+	{ "name", "cn", fromwindowsonly, always, normal},
+	{ "manager", "manager", bidirectional, always, dnmap},
+	{ "seealso", "seealso", bidirectional, always, dnmap},
 	{NULL, NULL, -1}
 };
 
@@ -224,7 +226,7 @@
 	/* IETF schema has 'street' and 'streetaddress' as aliases, but Microsoft does not */
 	{ "streetAddress", "street", towindowsonly, always, normal},
 	{ FAKE_STREET_ATTR_NAME, "street", fromwindowsonly, always, normal},
-    { "member", "uniquemember", bidirectional, always, dnmap},
+	{ "member", "uniquemember", bidirectional, always, dnmap},
 	{NULL, NULL, -1}
 };
 
@@ -1229,6 +1231,7 @@
 		case SLAPI_OPERATION_MODIFY:
 			{
 				LDAPMod **mapped_mods = NULL;
+				char *newrdn = NULL;
 
 				windows_map_mods_for_replay(prp,op->p.p_modify.modify_mods, &mapped_mods, is_user, &password);
 				if (is_user) {
@@ -1249,6 +1252,17 @@
 																 &mapped_mods);
 				}
 
+				/* Check if a naming attribute is being modified. */
+				if (windows_check_mods_for_rdn_change(prp, op->p.p_modify.modify_mods, local_entry, remote_dn, &newrdn)) {
+					/* Issue MODRDN */
+					slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s: renaming remote entry \"%s\" with new RDN of \"%s\"\n",
+							agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn), newrdn);
+					return_value = windows_conn_send_rename(prp->conn, slapi_sdn_get_dn(remote_dn),
+						newrdn, NULL, 1 /* delete old RDN */,
+						NULL, NULL /* returned controls */);
+					slapi_ch_free_string(&newrdn);
+				}
+
 				/* It's possible that the mapping process results in an empty mod list, in which case we don't bother with the replay */
 				if ( mapped_mods == NULL || *(mapped_mods)== NULL )
 				{
@@ -1550,11 +1564,12 @@
 				{
 					Slapi_Attr *new_attr = NULL;
 
-					/* AD treats streetAddress as a single-valued attribute, while we define it
-					 * as a multi-valued attribute as it's defined in rfc 4519.  We only
+					/* AD treats cn and streetAddress as a single-valued attributes, while we define
+					 * them as multi-valued attribute as it's defined in rfc 4519.  We only
 					 * sync the first value to AD to avoid a constraint violation.
 					 */
-					if (0 == slapi_attr_type_cmp(new_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE)) {
+					if ((0 == slapi_attr_type_cmp(new_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE)) ||
+						(0 == slapi_attr_type_cmp(new_type, "cn", SLAPI_TYPE_CMP_SUBTYPE))) {
 						if (slapi_valueset_count(vs) > 1) {
 							int i = 0;
 							Slapi_Value *value = NULL;
@@ -1570,7 +1585,7 @@
 								slapi_valueset_add_value_ext(vs, new_value, SLAPI_VALUE_FLAG_PASSIN);
 							}
 						}
-                        		}
+					}
 					
 					slapi_entry_add_valueset(new_entry,type,vs);
 
@@ -1716,6 +1731,166 @@
 	return return_value;
 }
 
+
+static int
+windows_check_mods_for_rdn_change(Private_Repl_Protocol *prp, LDAPMod **original_mods, 
+		Slapi_Entry *local_entry, Slapi_DN *remote_dn, char **newrdn)
+{
+	int ret = 0;
+	int need_rename = 0;
+	int got_entry = 0;
+	Slapi_Entry *remote_entry = NULL;
+	Slapi_Attr *remote_rdn_attr = NULL;
+	Slapi_Value *remote_rdn_val = NULL;
+	Slapi_Mods smods = {0};
+	Slapi_Mod *smod = slapi_mod_new();
+	Slapi_Mod *last_smod = smod;
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_check_mods_for_rdn_change\n", 0, 0, 0 );
+
+	/* Iterate through the original mods, looking for a modification to the RDN attribute */
+	slapi_mods_init_byref(&smods, original_mods);
+	smod = slapi_mods_get_first_smod(&smods, last_smod);
+	while(smod) {
+		/* Check if this is modifying the naming attribute (cn) */
+		if (slapi_attr_types_equivalent(slapi_mod_get_type(smod), "cn")) {
+			/* Fetch the remote entry so we can compare the new values
+			 * against the existing remote value.  We only need to do
+			 * this once for all mods. */
+			if (!got_entry) {
+				windows_get_remote_entry(prp, remote_dn, &remote_entry);
+				if (remote_entry) {
+					/* Fetch and duplicate the cn attribute so we can perform comparisions */
+					slapi_entry_attr_find(remote_entry, "cn", &remote_rdn_attr);
+					if (remote_rdn_attr) {
+						remote_rdn_attr = slapi_attr_dup(remote_rdn_attr);
+						slapi_attr_first_value(remote_rdn_attr, &remote_rdn_val);
+					}
+					slapi_entry_free(remote_entry);
+				}
+				got_entry = 1;
+
+				/* If we didn't get the remote value for some odd reason, just bail out. */
+				if (!remote_rdn_val) {
+					slapi_mod_done(smod);
+					goto done;
+				}
+			}
+
+			if (SLAPI_IS_MOD_REPLACE(slapi_mod_get_operation(smod))) {
+				/* For a replace, we just need to check if the old value that AD
+				 * has is still present after the operation.  If not, we rename
+				 * the entry in AD using the first new value as the RDN. */
+				Slapi_Value *new_val = NULL;
+				struct berval *bval = NULL;
+
+				/* Assume that we're going to need to do a rename. */
+				ret = 1;
+
+				/* Get the first new value, which is to be used as the RDN if we decide
+				 * that a rename is necessary. */
+				bval = slapi_mod_get_first_value(smod);
+				if (bval && bval->bv_val) {
+					/* Fill in new RDN to return to caller. */
+					slapi_ch_free_string(newrdn);
+					*newrdn = slapi_ch_smprintf("cn=%s", bval->bv_val);
+
+					/* Loop through all new values to check if they match
+					 * the value present in AD. */
+					do {
+						new_val = slapi_value_new_berval(bval);
+						if (slapi_value_compare(remote_rdn_attr, remote_rdn_val, new_val) == 0) {
+							/* We have a match.  This means we don't want to rename the entry in AD. */
+							slapi_ch_free_string(newrdn);
+							slapi_value_free(&new_val);
+							ret = 0;
+							break;
+						}
+						slapi_value_free(&new_val);
+						bval = slapi_mod_get_next_value(smod);
+					} while (bval && bval->bv_val);
+				}
+			} else if (SLAPI_IS_MOD_DELETE(slapi_mod_get_operation(smod))) {
+				/* We need to check if the cn in AD is the value being deleted.  If
+				 * so, set a flag saying that we will need to do a rename.  We will either
+				 * get a new value added from another mod in this op, or we will need to
+				 * use an old value that is left over after the delete operation. */
+				if (slapi_mod_get_num_values(smod) == 0) {
+					/* All values are being deleted, so a rename will be needed.  One
+					 * of the other mods will be adding the new values(s). */
+					need_rename = 1;
+				} else {
+					Slapi_Value *del_val = NULL;
+					struct berval *bval = NULL;
+
+					bval = slapi_mod_get_first_value(smod);
+					while (bval && bval->bv_val) {
+						/* Is this value the same one that is used as the RDN in AD? */
+						del_val = slapi_value_new_berval(bval);
+						if (slapi_value_compare(remote_rdn_attr, remote_rdn_val, del_val) == 0) {
+							/* We have a match.  This means we need to rename the entry in AD. */
+							need_rename = 1;
+							slapi_value_free(&del_val);
+							break;
+						}
+						slapi_value_free(&del_val);
+						bval = slapi_mod_get_next_value(smod);
+					}
+				}
+			} else if (SLAPI_IS_MOD_ADD(slapi_mod_get_operation(smod))) {
+				/* We only need to care about an add if the old value was deleted. */
+				if (need_rename) {
+					/* Just grab the first new value and use it to create the new RDN. */
+					struct berval *bval = slapi_mod_get_first_value(smod);
+
+					if (bval && bval->bv_val) {
+						/* Fill in new RDN to return to caller. */
+						slapi_ch_free_string(newrdn);
+						*newrdn = slapi_ch_smprintf("cn=%s", bval->bv_val);
+						need_rename = 0;
+						ret = 1;
+					}
+				}
+			}
+		}
+
+		/* Get the next mod from this op. */
+		slapi_mod_done(smod);
+
+		/* Need to prevent overwriting old smod with NULL return value and causing a leak. */
+		smod = slapi_mods_get_next_smod(&smods, last_smod);
+	}
+
+done:
+	/* We're done with the mods and the copied cn attr from the remote entry. */
+	slapi_attr_free(&remote_rdn_attr);
+	if (last_smod) {
+		slapi_mod_free(&last_smod);
+	}
+	slapi_mods_done (&smods);
+
+	if (need_rename) {
+		/* We need to perform a rename, but we didn't get the value for the
+		 * new RDN from this operation.  We fetch the first value from the local
+		 * entry to create the new RDN. */
+		if (local_entry) {
+			char *newval = slapi_entry_attr_get_charptr(local_entry, "cn");
+			if (newval) {
+				/* Fill in new RDN to return to caller. */
+				slapi_ch_free_string(newrdn);
+				*newrdn = slapi_ch_smprintf("cn=%s", newval);
+				slapi_ch_free_string(&newval);
+				ret = 1;
+			}
+		}
+	}
+
+	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_check_mods_for_rdn_change: %d\n", ret, 0, 0 );
+
+	return ret;
+}
+
+
 static void 
 windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod **original_mods, LDAPMod ***returned_mods, int is_user, char** password) 
 {
@@ -3247,9 +3422,16 @@
 			if (!mapdn)
 			{
 				int values_equal = 0;
-                                /* AD has a legth contraint on the initials attribute,
-                                 * so treat is as a special case. */
-				if (0 == slapi_attr_type_cmp(type, "initials", SLAPI_TYPE_CMP_SUBTYPE)) {
+				/* We only have to deal with processing the cn here for
+				 * operations coming from AD since the mapping for the
+				 * to_windows case has the create only flag set.  We
+				 * just need to check if the value from the AD entry
+				 * is already present in the DS entry. */
+				if (0 == slapi_attr_type_cmp(type, "name", SLAPI_TYPE_CMP_SUBTYPE) && !to_windows) {
+					values_equal = attr_compare_present(attr, local_attr);
+				/* AD has a legth contraint on the initials attribute,
+				 * so treat is as a special case. */
+				} else if (0 == slapi_attr_type_cmp(type, "initials", SLAPI_TYPE_CMP_SUBTYPE)) {
 					values_equal = attr_compare_equal(attr, local_attr, AD_INITIALS_LENGTH);
 				/* If we're getting a streetAddress (a fake attr name is used) from AD, then
 				 * we just check if the value in AD is present in our entry in DS.  In this
@@ -3320,6 +3502,7 @@
 							i = slapi_valueset_next_value(vs, i, &value);
 						}
 					}
+
 					slapi_mods_add_mod_values(smods,LDAP_MOD_REPLACE,
 						local_type,valueset_get_valuearray(vs));
 					*do_modify = 1;




More information about the Fedora-directory-commits mailing list