[Fedora-directory-commits] ldapserver/ldap/servers/plugins/replication repl5.h, 1.9, 1.10 windows_connection.c, 1.15, 1.16 windows_private.c, 1.14, 1.15 windows_protocol_util.c, 1.31, 1.32 windowsrepl.h, 1.11, 1.12

Nathan Kinder (nkinder) fedora-directory-commits at redhat.com
Wed Sep 12 23:05:27 UTC 2007


Author: nkinder

Update of /cvs/dirsec/ldapserver/ldap/servers/plugins/replication
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv9727

Modified Files:
	repl5.h windows_connection.c windows_private.c 
	windows_protocol_util.c windowsrepl.h 
Log Message:
Resolves: 243227
Summary: Handle syncing add opererations that have a ntuniqueid present.



Index: repl5.h
===================================================================
RCS file: /cvs/dirsec/ldapserver/ldap/servers/plugins/replication/repl5.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- repl5.h	10 Nov 2006 23:45:17 -0000	1.9
+++ repl5.h	12 Sep 2007 23:05:24 -0000	1.10
@@ -63,8 +63,8 @@
 #define REPLICA_TYPE_WINDOWS 1
 #define REPLICA_TYPE_MULTIMASTER 0
 #define REPL_DIRSYNC_CONTROL_OID "1.2.840.113556.1.4.841"
-#define CONN_SUPPORTS_DIRSYNC 12
-#define CONN_DOES_NOT_SUPPORT_DIRSYNC  13
+#define REPL_RETURN_DELETED_OBJS_CONTROL_OID "1.2.840.113556.1.4.417"
+#define REPL_WIN2K3_AD_OID "1.2.840.113556.1.4.1670"
 
 /* DS 5.0 replication protocol OIDs */
 #define REPL_START_NSDS50_REPLICATION_REQUEST_OID "2.16.840.1.113730.3.5.3"
@@ -358,7 +358,11 @@
 	CONN_SUPPORTS_DS71_REPL,
 	CONN_DOES_NOT_SUPPORT_DS71_REPL,
 	CONN_IS_READONLY,
-	CONN_IS_NOT_READONLY
+	CONN_IS_NOT_READONLY,
+	CONN_SUPPORTS_DIRSYNC,
+	CONN_DOES_NOT_SUPPORT_DIRSYNC,
+	CONN_IS_WIN2K3,
+	CONN_NOT_WIN2K3
 } ConnResult;  
 Repl_Connection *conn_new(Repl_Agmt *agmt);
 ConnResult conn_connect(Repl_Connection *conn);


Index: windows_connection.c
===================================================================
RCS file: /cvs/dirsec/ldapserver/ldap/servers/plugins/replication/windows_connection.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- windows_connection.c	10 Nov 2006 23:45:17 -0000	1.15
+++ windows_connection.c	12 Sep 2007 23:05:24 -0000	1.16
@@ -83,6 +83,7 @@
 	struct timeval timeout;
 	int flag_agmt_changed;
 	char *plain;
+	int is_win2k3; /* 1 if it is win2k3 or later, 0 if not, -1 if not determined */
 } repl_connection;
 
 /* #define DEFAULT_LINGER_TIME (5 * 60) */ /* 5 minutes */
@@ -167,6 +168,7 @@
 	rpc->supports_ds40_repl = -1;
 	rpc->supports_ds50_repl = -1;
 	rpc->supports_dirsync = -1;
+	rpc->is_win2k3 = -1;
 	rpc->linger_active = PR_FALSE;
 	rpc->delete_after_linger = PR_FALSE;
 	rpc->linger_event = NULL;
@@ -291,21 +293,18 @@
 static ConnResult
 windows_perform_operation(Repl_Connection *conn, int optype, const char *dn,
 	LDAPMod **attrs, const char *newrdn, const char *newparent,
-	int deleteoldrdn, LDAPControl *update_control,
+	int deleteoldrdn, LDAPControl **server_controls,
 	const char *extop_oid, struct berval *extop_payload, char **retoidp,
 	struct berval **retdatap, LDAPControl ***returned_controls)
 {
 	int rc;
 	ConnResult return_value;
-	LDAPControl *server_controls[1];
 	LDAPControl **loc_returned_controls;
 	const char *op_string = NULL;
 	const char *extra_op_string = NULL;
 
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_perform_operation\n", 0, 0, 0 );
 
-	server_controls[0] = NULL;
-
 	if (windows_conn_connected(conn))
 	{
 		int msgid;
@@ -588,9 +587,17 @@
     return e;
 }
 
+/* Perform a simple search against Windows with no controls */
 ConnResult
 windows_search_entry(Repl_Connection *conn, char* searchbase, char *filter, Slapi_Entry **entry)
 {
+	return windows_search_entry_ext(conn, searchbase, filter, entry, NULL);
+}
+
+/* Perform a simple search against Windows with optional controls */
+ConnResult
+windows_search_entry_ext(Repl_Connection *conn, char* searchbase, char *filter, Slapi_Entry **entry, LDAPControl **serverctrls)
+{
 	ConnResult return_value = 0;
 	int ldap_rc = 0;
 	LDAPMessage *res = NULL;
@@ -607,7 +614,7 @@
 	{
 		ldap_rc = ldap_search_ext_s(conn->ld, searchbase, LDAP_SCOPE_SUBTREE,
 			filter, NULL, 0 /* attrsonly */,
-			NULL , NULL /* client controls */,
+			serverctrls , NULL /* client controls */,
 			&conn->timeout, 0 /* sizelimit */, &res);
 		if (LDAP_SUCCESS == ldap_rc)
 		{
@@ -684,9 +691,7 @@
 			server_controls[0] = NULL; /* unsupported */
 		} else 
 		{
-			/* DBDB: I'm pretty sure that the control is leaked from here */
-			/* Purify agrees */
-			server_controls[0] = windows_private_dirsync_control(conn->agmt); /* yes, or don't know */
+			server_controls[0] = windows_private_dirsync_control(conn->agmt);
 		}
 
 		server_controls[1] = NULL;
@@ -746,12 +751,12 @@
  */
 ConnResult
 windows_conn_send_add(Repl_Connection *conn, const char *dn, LDAPMod **attrs,
-	LDAPControl *update_control, LDAPControl ***returned_controls)
+	LDAPControl **server_controls, LDAPControl ***returned_controls)
 {
 	ConnResult res = 0;
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_conn_send_add\n", 0, 0, 0 );
 	res = windows_perform_operation(conn, CONN_ADD, dn, attrs, NULL /* newrdn */,
-		NULL /* newparent */, 0 /* deleteoldrdn */, update_control,
+		NULL /* newparent */, 0 /* deleteoldrdn */, server_controls,
 		NULL /* extop OID */, NULL /* extop payload */, NULL /* retoidp */,
 		NULL /* retdatap */, returned_controls);
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_conn_send_add\n", 0, 0, 0 );
@@ -764,13 +769,13 @@
  */
 ConnResult
 windows_conn_send_delete(Repl_Connection *conn, const char *dn,
-	LDAPControl *update_control, LDAPControl ***returned_controls)
+	LDAPControl **server_controls, LDAPControl ***returned_controls)
 {
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_conn_send_delete\n", 0, 0, 0 );
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_conn_send_delete\n", 0, 0, 0 );
 	return windows_perform_operation(conn, CONN_DELETE, dn, NULL /* attrs */,
 		NULL /* newrdn */, NULL /* newparent */, 0 /* deleteoldrdn */,
-		update_control, NULL /* extop OID */, NULL /* extop payload */,
+		server_controls, NULL /* extop OID */, NULL /* extop payload */,
 		NULL /* retoidp */, NULL /* retdatap */, returned_controls);
 }
 
@@ -780,12 +785,12 @@
  */
 ConnResult
 windows_conn_send_modify(Repl_Connection *conn, const char *dn, LDAPMod **mods,
-	LDAPControl *update_control, LDAPControl ***returned_controls)
+	LDAPControl **server_controls, LDAPControl ***returned_controls)
 {
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_conn_send_modify\n", 0, 0, 0 );
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_conn_send_modify\n", 0, 0, 0 );
 	return windows_perform_operation(conn, CONN_MODIFY, dn, mods, NULL /* newrdn */,
-		NULL /* newparent */, 0 /* deleteoldrdn */, update_control,
+		NULL /* newparent */, 0 /* deleteoldrdn */, server_controls,
 		NULL /* extop OID */, NULL /* extop payload */, NULL /* retoidp */,
 		NULL /* retdatap */, returned_controls);
 }
@@ -796,12 +801,12 @@
 ConnResult
 windows_conn_send_rename(Repl_Connection *conn, const char *dn,
 	const char *newrdn, const char *newparent, int deleteoldrdn,
-	LDAPControl *update_control, LDAPControl ***returned_controls)
+	LDAPControl **server_controls, LDAPControl ***returned_controls)
 {
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_conn_send_rename\n", 0, 0, 0 );
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_conn_send_rename\n", 0, 0, 0 );
 	return windows_perform_operation(conn, CONN_RENAME, dn, NULL /* attrs */,
-		newrdn, newparent, deleteoldrdn, update_control,
+		newrdn, newparent, deleteoldrdn, server_controls,
 		NULL /* extop OID */, NULL /* extop payload */, NULL /* retoidp */,
 		NULL /* retdatap */, returned_controls);
 }
@@ -878,13 +883,13 @@
 ConnResult
 windows_conn_send_extended_operation(Repl_Connection *conn, const char *extop_oid,
 	struct berval *payload, char **retoidp, struct berval **retdatap,
-	LDAPControl *update_control, LDAPControl ***returned_controls)
+	LDAPControl **server_controls, LDAPControl ***returned_controls)
 {
 	LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_conn_send_extended_operation\n", 0, 0, 0 );
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_conn_send_extended_operation\n", 0, 0, 0 );
 	return windows_perform_operation(conn, CONN_EXTENDED_OPERATION, NULL /* dn */, NULL /* attrs */,
 		NULL /* newrdn */, NULL /* newparent */,  0 /* deleteoldrdn */,
-		update_control, extop_oid, payload, retoidp, retdatap,
+		server_controls, extop_oid, payload, retoidp, retdatap,
 		returned_controls);
 }
 
@@ -1264,6 +1269,16 @@
 		{
 			windows_private_set_isnt4(conn->agmt,0);
 		}
+
+		supports = windows_conn_replica_is_win2k3(conn);
+		if (CONN_IS_WIN2K3 == supports)
+		{
+			windows_private_set_iswin2k3(conn->agmt,1);
+			LDAPDebug( LDAP_DEBUG_REPL, "windows_conn_connect : detected Win2k3 peer\n", 0, 0, 0 );
+		} else
+		{
+			windows_private_set_iswin2k3(conn->agmt,0);
+		}
 	}
 
 	ber_bvfree(creds);
@@ -1472,6 +1487,71 @@
 }
 
 
+/* Checks if the AD server is running win2k3 (or later) */
+ConnResult
+windows_conn_replica_is_win2k3(Repl_Connection *conn)
+{
+        ConnResult return_value;
+        int ldap_rc;
+
+        LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_conn_replica_is_win2k3\n", 0, 0, 0 );
+
+        if (windows_conn_connected(conn))
+        {
+                if (conn->is_win2k3 == -1) {
+                        LDAPMessage *res = NULL;
+                        LDAPMessage *entry = NULL;
+                        char *attrs[] = {"supportedCapabilities", NULL};
+
+                        conn->status = STATUS_SEARCHING;
+                        ldap_rc = ldap_search_ext_s(conn->ld, "", LDAP_SCOPE_BASE,
+                                                                                "(objectclass=*)", attrs, 0 /* attrsonly */,
+                                                                                NULL /* server controls */, NULL /* client controls */,
+                                                                                &conn->timeout, LDAP_NO_LIMIT, &res);
+                        if (LDAP_SUCCESS == ldap_rc)
+                        {
+                                conn->is_win2k3 = 0;
+                                entry = ldap_first_entry(conn->ld, res);
+                                if (!attribute_string_value_present(conn->ld, entry, "supportedCapabilities", REPL_WIN2K3_AD_OID))
+                                {
+                                        return_value = CONN_NOT_WIN2K3;
+                                }
+                                else
+                                {
+
+                                        conn->is_win2k3 =1;
+                                        return_value = CONN_IS_WIN2K3;
+                                }
+                        }
+                        else
+                        {
+                                if (IS_DISCONNECT_ERROR(ldap_rc))
+                                {
+                                        conn->last_ldap_error = ldap_rc;        /* specific reason */
+                                        windows_conn_disconnect(conn);
+                                        return_value = CONN_NOT_CONNECTED;
+                                }
+                                else
+                                {
+                                        return_value = CONN_OPERATION_FAILED;
+                                }
+                        }
+                        if (NULL != res)
+                                ldap_msgfree(res);
+                        }
+                else {
+                        return_value = conn->is_win2k3 ? CONN_IS_WIN2K3 : CONN_NOT_WIN2K3;
+                }
+        }
+        else
+        {
+                /* Not connected */
+                return_value = CONN_NOT_CONNECTED;
+        }
+        LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_conn_replica_is_win2k3\n", 0, 0, 0 );
+        return return_value;
+}
+
 
 /*
  * Return 1 if "value" is a value of attribute type "type" in entry "entry".


Index: windows_private.c
===================================================================
RCS file: /cvs/dirsec/ldapserver/ldap/servers/plugins/replication/windows_private.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- windows_private.c	10 Nov 2006 23:45:17 -0000	1.14
+++ windows_private.c	12 Sep 2007 23:05:24 -0000	1.15
@@ -65,6 +65,7 @@
   PRBool create_groups_from_dirsync;
   char *windows_domain;
   int isnt4;
+  int iswin2k3;
 };
 
 static int
@@ -245,6 +246,38 @@
 		LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_private_set_isnt4\n", 0, 0, 0 );
 }
 
+int windows_private_get_iswin2k3(const Repl_Agmt *ra)
+{
+		Dirsync_Private *dp;
+
+		LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_private_get_iswin2k3\n", 0, 0, 0 );
+
+	PR_ASSERT(ra);
+
+		dp = (Dirsync_Private *) agmt_get_priv(ra);
+		PR_ASSERT (dp);
+
+		LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_private_get_iswin2k3\n", 0, 0, 0 );
+
+		return dp->iswin2k3;
+}
+
+void windows_private_set_iswin2k3(const Repl_Agmt *ra, int isit)
+{
+		Dirsync_Private *dp;
+
+		LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_private_set_iswin2k3\n", 0, 0, 0 );
+
+	PR_ASSERT(ra);
+
+		dp = (Dirsync_Private *) agmt_get_priv(ra);
+		PR_ASSERT (dp);
+
+		dp->iswin2k3 = isit;
+
+		LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_private_set_iswin2k3\n", 0, 0, 0 );
+}
+
 
 /* Returns a copy of the Slapi_DN pointer, no need to free it */
 const Slapi_DN* windows_private_get_windows_subtree (const Repl_Agmt *ra)


Index: windows_protocol_util.c
===================================================================
RCS file: /cvs/dirsec/ldapserver/ldap/servers/plugins/replication/windows_protocol_util.c,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -r1.31 -r1.32
--- windows_protocol_util.c	4 Sep 2007 15:45:57 -0000	1.31
+++ windows_protocol_util.c	12 Sep 2007 23:05:25 -0000	1.32
@@ -71,11 +71,16 @@
 static int windows_get_local_entry_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry);
 static int map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *missing_entry, int want_guid);
 static char* extract_ntuserdomainid_from_entry(Slapi_Entry *e);
+static char* extract_container(const Slapi_DN *entry_dn, const Slapi_DN *suffix_dn);
 static int windows_get_remote_entry (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry);
+static int windows_get_remote_tombstone(Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry);
+static int windows_reanimate_tombstone(Private_Repl_Protocol *prp, const Slapi_DN* tombstone_dn, const char* new_dn);
 static const char* op2string (int op);
 static int is_subject_of_agreemeent_remote(Slapi_Entry *e, const Repl_Agmt *ra);
 static int map_entry_dn_inbound(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra);
 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);
 
 
 /* Controls the direction of flow for mapped attributes */
@@ -363,7 +368,7 @@
 		slapi_log_error(SLAPI_LOG_REPL, NULL, "Windows sync entry: %s %s\n", string, buffer);
 		if (buffer)
 		{
-			slapi_ch_free((void**)&buffer);
+			slapi_ch_free_string(&buffer);
 		} 
 	}
 }
@@ -953,11 +958,85 @@
 	int rc = 0;
 
 	slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
-		"%s: process_replay_add: dn=\"%s\" (%s,%s)\n",
-		agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn), missing_entry ? "not present" : "already present" , remote_add_allowed ? "add allowed" : "add not allowed");
+		"%s: process_replay_add: dn=\"%s\" (%s,%s)\n", agmt_get_long_name(prp->agmt),
+		slapi_sdn_get_dn(remote_dn), missing_entry ? "not present" : "already present",
+		remote_add_allowed ? "add allowed" : "add not allowed");
 
 	if (missing_entry)
 	{
+		/* If DN is a GUID, we need to attempt to reanimate the tombstone */
+		if (is_guid_dn(remote_dn)) {
+			int tstone_exists = 0;
+			int reanimate_rc = -1;
+			char *new_dn_string = NULL;
+			char *cn_string = NULL;
+			Slapi_DN *tombstone_dn = NULL;
+
+			slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+				"%s: process_replay_add: dn=\"%s\" appears to have been"
+				"  deleted on remote side.  Searching for tombstone.\n",
+				agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn));
+
+			/* Map local entry to tombstone DN and verify that it exists on
+			 * AD side */
+			map_windows_tombstone_dn(local_entry, &tombstone_dn, prp, &tstone_exists);
+
+			/* We can't use a GUID DN, so rewrite to the new mapped DN. */
+			cn_string = slapi_entry_attr_get_charptr(local_entry,"cn");
+			if (!cn_string) {
+				cn_string = slapi_entry_attr_get_charptr(local_entry,"ntuserdomainid");
+			}
+
+			if (cn_string) {
+				char *rdnstr = NULL;
+				char *container_str = NULL;
+				const char *suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
+
+				container_str = extract_container(slapi_entry_get_sdn_const(local_entry),
+					windows_private_get_directory_subtree(prp->agmt));
+				new_dn_string = PR_smprintf("cn=%s,%s%s", cn_string, container_str, suffix);
+
+				if (new_dn_string) {
+					/* If the tombstone exists, reanimate it. If the tombstone
+					 * does not exist, we'll create a new entry in AD, which
+					 * will end up getting a new GUID generated by AD. */
+					if (tstone_exists) {
+						slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+							"%s: process_replay_add: Reanimating tombstone (dn=\"%s\") to"
+							" normal entry (dn=\"%s\").\n", agmt_get_long_name(prp->agmt),
+							slapi_sdn_get_dn(tombstone_dn), new_dn_string);
+						reanimate_rc = windows_reanimate_tombstone(prp, tombstone_dn, (const char *)new_dn_string);
+						if (reanimate_rc != 0) {
+							slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+								"%s: process_replay_add: Reanimation of tombstone"
+								" (dn=\"%s\") failed.  A new entry (dn=\"%s\")"
+								" will be added instead.\n", agmt_get_long_name(prp->agmt),
+								slapi_sdn_get_dn(tombstone_dn), new_dn_string);
+						}
+					}
+
+					/* Clear out the old GUID DN and use the new one. We hand off the memory
+					 * for new_dn_string into the remote_dn. */
+					slapi_sdn_done(remote_dn);
+					slapi_sdn_set_dn_passin(remote_dn, new_dn_string);
+				}
+
+				slapi_ch_free_string(&cn_string);
+				slapi_ch_free_string(&container_str);
+			}
+
+			if (tombstone_dn) {
+				slapi_sdn_free(&tombstone_dn);
+			}
+
+			if (reanimate_rc == 0) {
+				/* We reanimated a tombstone, so an add won't work.  We
+				 * fallback to doing a modify of the newly reanimated
+				 * entry. */
+				goto modify_fallback;
+			}
+		}
+
 		if (remote_add_allowed) {
 			LDAPMod **entryattrs = NULL;
 			Slapi_Entry *mapped_entry = NULL;
@@ -971,17 +1050,26 @@
 				mapped_entry = NULL;
 				if (NULL == entryattrs)
 				{
-					slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_replay_update: Cannot convert entry to LDAPMods.\n",agmt_get_long_name(prp->agmt));
+					slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
+						"%s: windows_replay_update: Cannot convert entry to LDAPMods.\n",
+						agmt_get_long_name(prp->agmt));
 					return_value = CONN_LOCAL_ERROR;
 				}
 				else
 				{
 					windows_log_add_entry_remote(local_dn, remote_dn);
-					return_value = windows_conn_send_add(prp->conn, slapi_sdn_get_dn(remote_dn), entryattrs, NULL, NULL);
-					/* It's possible that the entry already exists in AD, in which case we fall back to modify it */
+					return_value = windows_conn_send_add(prp->conn, slapi_sdn_get_dn(remote_dn),
+						entryattrs, NULL, NULL);
+					/* It's possible that the entry already exists in AD, in which
+					 * case we fall back to modify it */
+					/* NGK - This fallback doesn't seem to happen, at least not at this point
+					 * in the code.  The only chance to fallback to doing a modify is if
+					 * missing_entry is set to 0 at the top of this function. */
 					if (return_value)
 					{
-						slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_replay_update: Cannot replay add operation.\n",agmt_get_long_name(prp->agmt));
+						slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
+							"%s: windows_replay_update: Cannot replay add operation.\n",
+							agmt_get_long_name(prp->agmt));
 					}
 					ldap_mods_free(entryattrs, 1);
 					entryattrs = NULL;
@@ -989,14 +1077,15 @@
 			} else 
 			{
 				slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
-					"%s: process_replay_add: failed to create mapped entry dn=\"%s\"\n",agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn));
+					"%s: process_replay_add: failed to create mapped entry dn=\"%s\"\n",
+					agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn));
 			}
 		}
 	} else 
 	{
-
 		Slapi_Entry *remote_entry = NULL;
 
+modify_fallback:
 		/* Fetch the remote entry */
 		rc = windows_get_remote_entry(prp, remote_dn,&remote_entry);
 		if (0 == rc && remote_entry) {
@@ -1018,7 +1107,6 @@
 windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op)
 {
 	ConnResult return_value = 0;
-	LDAPControl *update_control = NULL; /* No controls used for AD */
 	int rc = 0;
 	char *password = NULL;
 	int is_ours = 0;
@@ -1097,7 +1185,7 @@
 							slapi_mod_dump(mapped_mods[i],i);
 						}
 					}
-					return_value = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(remote_dn), mapped_mods, update_control,NULL /* returned controls */);
+					return_value = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(remote_dn), mapped_mods, NULL, NULL /* returned controls */);
 				}
 				if (mapped_mods)
 				{
@@ -1109,7 +1197,7 @@
 		case SLAPI_OPERATION_DELETE:
 			if (delete_remote_entry_allowed(local_entry))
 			{
-				return_value = windows_conn_send_delete(prp->conn, slapi_sdn_get_dn(remote_dn), update_control, NULL /* returned controls */);
+				return_value = windows_conn_send_delete(prp->conn, slapi_sdn_get_dn(remote_dn), NULL, NULL /* returned controls */);
 					slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
 					"%s: windows_replay_update: deleted remote entry, dn=\"%s\", result=%d\n",
 					agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn), return_value);
@@ -1125,7 +1213,7 @@
 				op->p.p_modrdn.modrdn_newrdn,
 				op->p.p_modrdn.modrdn_newsuperior_address.dn,
 				op->p.p_modrdn.modrdn_deloldrdn,
-				update_control, NULL /* returned controls */);
+				NULL, NULL /* returned controls */);
 			break;
 		default:
 			slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "%s: replay_update: Unknown "
@@ -1296,7 +1384,7 @@
 		goto error;
 	}
 	new_entry = slapi_str2entry(entry_string, 0);
-	slapi_ch_free((void**)&entry_string);
+	slapi_ch_free_string(&entry_string);
 	if (NULL == new_entry) 
 	{
 		goto error;
@@ -1399,7 +1487,7 @@
 						slapi_attr_set_type(new_attr, new_type);
 					}
 				}
-				slapi_ch_free((void**)&new_type);
+				slapi_ch_free_string(&new_type);
 			}
 			/* password mods are treated specially */
 			if (0 == slapi_attr_type_cmp(type, PSEUDO_ATTR_UNHASHEDUSERPASSWORD, SLAPI_TYPE_CMP_SUBTYPE) )
@@ -1469,7 +1557,7 @@
 error:
 	if (username)
 	{
-		slapi_ch_free((void**)&username);
+		slapi_ch_free_string(&username);
 	}
 	if (new_entry) 
 	{
@@ -1633,7 +1721,7 @@
 
 					slapi_mods_add_modbvps(&mapped_smods,mod->mod_op,mapped_type,mod->mod_bvalues);
 				}
-				slapi_ch_free((void**)&mapped_type);
+				slapi_ch_free_string(&mapped_type);
 			} else 
 			{
 				/* password mods are treated specially */
@@ -1832,6 +1920,70 @@
 	return retval;
 }
 
+/* Search for a tombstone entry in AD by DN */
+static int
+windows_get_remote_tombstone (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry)
+{
+	int retval = 0;
+	ConnResult cres = 0;
+	char *filter = "(objectclass=*)";
+	const char *searchbase = NULL;
+	Slapi_Entry *found_entry = NULL;
+	LDAPControl *server_controls[2];
+
+	/* We need to send the "Return Deleted Objects" control to search
+	 * for tombstones. */
+	slapi_build_control(REPL_RETURN_DELETED_OBJS_CONTROL_OID, NULL, PR_TRUE,
+			&server_controls[0]);
+	server_controls[1] = NULL;
+
+	searchbase = slapi_sdn_get_dn(remote_dn);
+	cres = windows_search_entry_ext(prp->conn, (char*)searchbase, filter,
+				&found_entry, server_controls);
+	if (cres) {
+		retval = -1;
+	} else {
+		if (found_entry) {
+			*remote_entry = found_entry;
+		}
+	}
+
+	ldap_control_free(server_controls[0]);
+	return retval;
+}
+
+/* Reanimate a tombstone in AD.  Returns 0 on success, otherwise you get the
+ * LDAP return code from the modify operation.  */
+static int
+windows_reanimate_tombstone(Private_Repl_Protocol *prp, const Slapi_DN* tombstone_dn, const char* new_dn)
+{
+	int retval = 0;
+	LDAPControl *server_controls[2];
+	Slapi_Mods smods = {0};
+
+	/* We need to send the "Return Deleted Objects" control to modify
+	 * tombstone entries. */
+	slapi_build_control(REPL_RETURN_DELETED_OBJS_CONTROL_OID, NULL, PR_TRUE,
+			&server_controls[0]);
+	server_controls[1] = NULL;
+
+	/* To reanimate a tombstone in AD, you need to send a modify
+	 * operation that does two things.  It must remove the isDeleted
+	 * attribute from the entry and it must modify the DN.  This DN
+	 * does not have to be the same place in the tree that the entry
+	 * previously existed. */ 
+	slapi_mods_init (&smods, 0);
+	slapi_mods_add_mod_values(&smods, LDAP_MOD_DELETE, "isDeleted", NULL);
+	slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "distinguishedName", new_dn);
+
+	retval = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(tombstone_dn),
+				 slapi_mods_get_ldapmods_byref(&smods), server_controls, NULL );
+
+	slapi_mods_done(&smods);
+	ldap_control_free(server_controls[0]);
+	return retval;
+}
+
 static int
 find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry **e,  const Repl_Agmt *ra)
 {
@@ -1858,7 +2010,7 @@
         LDAP_SCOPE_SUBTREE, query, NULL, 0, NULL, NULL,
         (void *)plugin_get_default_component_id(), 0);
     slapi_search_internal_pb(pb);
-    slapi_ch_free((void **)&query);
+    slapi_ch_free_string(&query);
 
     slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rval);
     if (rval != LDAP_SUCCESS)
@@ -1906,9 +2058,9 @@
 		return find_entry_by_attr_value("ntUniqueId",guid,e,ra);
 }
 
-/* Remove dashes from a GUID string */
+/* Remove dashes from a GUID string. */
 static void
-dedash(char *str)
+dedash_guid(char *str)
 {
 	char *p = str;
 	char c = '\0';
@@ -1930,10 +2082,41 @@
 	}
 }
 
+/* Add dashes into a GUID string.  If the guid is not formatted properly,
+ * we will free it and set the pointer to NULL. */
+static void
+dash_guid(char **str)
+{
+	if (strlen(*str) == NTUNIQUEID_LENGTH) {
+		char *p = NULL;
+		/* Add extra room for the dashes */
+		*str = slapi_ch_realloc(*str, AD_GUID_LENGTH + 1);
+
+		/* GUID needs to be in 8-4-4-4-12 format */
+		p = *str + 23;
+		memmove(p + 1, *str + 20, 12);
+		*p = '-';
+		p = *str + 18;
+		memmove(p + 1, *str + 16, 4);
+		*p = '-';
+		p = *str + 13;
+		memmove(p + 1, *str + 12, 4);
+		*p = '-';
+		p = *str + 8;
+		memmove(p + 1, *str + 8, 4);
+		*p = '-';
+		p = *str + 36;
+		*p = '\0';
+	} else {
+		/* This GUID does not appear to be valid */
+		slapi_ch_free_string(str);
+	}
+}
+
 /* For reasons not clear, the GUID returned in the tombstone DN is all 
  * messed up, like the guy in the movie 'the fly' after he want in the tranporter device */
 static void
-decrypt(char *guid)
+decrypt_guid(char *guid)
 {
 	static int decrypt_offsets[] = {6,7,4,5,2,3,0,1,10,11,8,9,14,15,12,13,16,17,18,19,
 		20,21,22,23,24,25,26,27,28,29,30,31};
@@ -1948,7 +2131,7 @@
 		p++;
 		i++;
 	}
-	slapi_ch_free((void**)&cpy);
+	slapi_ch_free_string(&cpy);
 }
 
 static char*
@@ -1971,8 +2154,8 @@
 		strncpy(guid,colon_offset+1,(comma_offset-colon_offset)-1);
 		guid[comma_offset-colon_offset-1] = '\0';
 		/* Finally remove the dashes since we don't store them on our side */
-		dedash(guid);
-		decrypt(guid);
+		dedash_guid(guid);
+		decrypt_guid(guid);
 	}
 	return guid;
 }
@@ -2004,6 +2187,82 @@
 	return result;
 }
 
+/* Given a local entry, map it to it's AD tombstone DN. An AD
+ * tombstone DN is formatted like:
+ *
+ *     cn=<cn>\0ADEL:<guid>,cn=Deleted Objects,<suffix>
+ * 
+ * This function will allocate a new Slapi_DN.  It is up to the
+ * caller to free it when they are finished with it. */
+static int 
+map_windows_tombstone_dn(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *exists)
+{
+	int rc = 0;
+	char *cn = NULL;
+	char *guid = NULL;
+	const char *suffix = NULL;
+	char *tombstone_dn = NULL;
+	Slapi_Entry *tombstone = NULL;
+
+	/* Initialize the output values */
+	*dn = NULL;
+	*exists = 0;
+
+	cn = slapi_entry_attr_get_charptr(e,"cn");
+	if (!cn) {
+		cn = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
+	}
+
+	guid = slapi_entry_attr_get_charptr(e,"ntUniqueId");
+	if (guid) {
+		/* the GUID is in a different form in the tombstone DN, so
+		 * we need to transform it from the way we store it. */
+		decrypt_guid(guid);
+		dash_guid(&guid);
+	}
+
+	/* The tombstone suffix discards any containers, so we need
+	 * to trim the DN to only dc components. */
+	if (suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt))) {
+		/* If this isn't found, it is treated as an error below. */
+		suffix = (const char *) strcasestr(suffix,"dc=");
+	}
+
+	if (cn && guid && suffix) {
+		tombstone_dn = PR_smprintf("cn=%s\\0ADEL:%s,cn=Deleted Objects,%s",
+				cn, guid, suffix);
+
+		/* Hand off the memory to the Slapi_DN */
+		*dn = slapi_sdn_new_dn_passin(tombstone_dn);
+
+		windows_get_remote_tombstone(prp, *dn, &tombstone);
+		if (tombstone) {
+			*exists = 1;
+			slapi_entry_free(tombstone);
+		}
+	} else {
+		slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+			"%s: map_windows_tombstone_dn: Failed to map dn=\"%s\" "
+			"to windows tombstone dn.\n", agmt_get_long_name(prp->agmt),
+			slapi_entry_get_dn(e));
+		rc = 1;
+	}
+
+	slapi_ch_free_string(&cn);
+	slapi_ch_free_string(&guid);
+	return rc;
+}
+
+static int is_guid_dn(Slapi_DN *remote_dn)
+{
+	if ((remote_dn != NULL) && (strncasecmp("<GUID=",
+			slapi_sdn_get_dn(remote_dn), 6) == 0)) {
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
 static char*
 extract_guid_from_entry(Slapi_Entry *e, int is_nt4)
 {
@@ -2151,8 +2410,54 @@
 	guid = slapi_entry_attr_get_charptr(e,"ntUniqueId");
 	if (guid && guid_form) 
 	{
+		int rc = 0;
+		Slapi_Entry *remote_entry = NULL;
 		new_dn = make_dn_from_guid(guid, is_nt4, suffix);
-		slapi_ch_free((void**)&guid);
+		slapi_ch_free_string(&guid);
+		/* There are certain cases where we will have a GUID, but the entry does not exist in
+		 * AD.  This happens when you delete an entry, then add it back elsewhere in the tree
+		 * without removing the ntUniqueID attribute.  We should verify that the entry really
+		 * exists in AD. */
+		rc = windows_get_remote_entry(prp, new_dn, &remote_entry);
+		if (0 == rc && remote_entry) {
+			slapi_entry_free(remote_entry);
+		} else {
+			/* We need to re-write the DN to a non-GUID DN if we're syncing to a
+			 * Windows 2000 Server since tombstone reanimation is not supported.
+			 * If we're syncing with Windows 2003 Server, we'll just use the GUID
+			 * to reanimate the tombstone when processing the add operation. */
+			*missing_entry = 1;
+			if (!windows_private_get_iswin2k3(prp->agmt)) {
+				char *new_dn_string = NULL;
+				char *cn_string = NULL;
+
+				/* We can't use a GUID DN, so rewrite to the mapped DN. */
+				cn_string = slapi_entry_attr_get_charptr(e,"cn");
+				if (!cn_string) {
+					cn_string = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
+				}
+
+				if (cn_string) {
+					char *rdnstr = NULL;
+					char *container_str = NULL;
+
+					container_str = extract_container(slapi_entry_get_sdn_const(e),
+						windows_private_get_directory_subtree(prp->agmt));
+					new_dn_string = PR_smprintf("cn=%s,%s%s", cn_string, container_str, suffix);
+
+					if (new_dn_string) {
+						if (new_dn) {
+							slapi_sdn_free(&new_dn);
+						}
+						new_dn = slapi_sdn_new_dn_byval(new_dn_string);
+						PR_smprintf_free(new_dn_string);
+					}
+
+					slapi_ch_free_string(&cn_string);
+					slapi_ch_free_string(&container_str);
+                                 }
+			}
+		}
 	} else 
 	{
 		/* No GUID found, try ntUserDomainId */
@@ -2204,8 +2509,8 @@
 							new_dn = slapi_sdn_new_dn_byval(new_dn_string);
 							PR_smprintf_free(new_dn_string);
 						}
-						slapi_ch_free((void**)&cn_string);
-						slapi_ch_free((void**)&container_str);
+						slapi_ch_free_string(&cn_string);
+						slapi_ch_free_string(&container_str);
 					}
 				} else 
 				{
@@ -2217,7 +2522,7 @@
 					retval = -1;
 				}
 			}
-			slapi_ch_free((void**)&username);
+			slapi_ch_free_string(&username);
 		}
 		if (remote_entry)
 		{
@@ -2280,7 +2585,7 @@
 
 	if (guid) 
 	{
-		slapi_ch_free((void**)&guid);
+		slapi_ch_free_string(&guid);
 	}
 	if (matching_entry)
 	{
@@ -2387,7 +2692,7 @@
 			}
 			new_dn = slapi_sdn_new_dn_byval(new_dn_string);
 			PR_smprintf_free(new_dn_string);
-			slapi_ch_free((void**)&container_str);
+			slapi_ch_free_string(&container_str);
 			/* Clear any earlier error */
 			retval = 0;
 		} else 
@@ -2410,7 +2715,7 @@
 	}
 	if (username)
 	{
-		slapi_ch_free((void **) &username);
+		slapi_ch_free_string(&username);
 	}
 	return retval;
 }
@@ -2542,7 +2847,7 @@
 		goto error;
 	}
 	local_entry = slapi_str2entry(entry_string, 0);
-	slapi_ch_free((void**)&entry_string);
+	slapi_ch_free_string(&entry_string);
 	if (NULL == local_entry) 
 	{
 		goto error;
@@ -2583,7 +2888,7 @@
 				{
 					slapi_entry_add_valueset(local_entry,new_type,vs);
 				}
-				slapi_ch_free((void**)&new_type);
+				slapi_ch_free_string(&new_type);
 			}
 		}
 		if (vs) 
@@ -2627,7 +2932,7 @@
 	}
 	if (username) 
 	{
-		slapi_ch_free((void**)&username);
+		slapi_ch_free_string(&username);
 	}
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_create_local_entry\n", 0, 0, 0 );
 	return retval;
@@ -2812,7 +3117,7 @@
 					if (guid)
 					{
 						slapi_mods_add_string(smods,LDAP_MOD_ADD,local_type,guid);
-						slapi_ch_free((void**)&guid);
+						slapi_ch_free_string(&guid);
 					}
 				} else
 				{


Index: windowsrepl.h
===================================================================
RCS file: /cvs/dirsec/ldapserver/ldap/servers/plugins/replication/windowsrepl.h,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- windowsrepl.h	27 Aug 2007 17:16:48 -0000	1.11
+++ windowsrepl.h	12 Sep 2007 23:05:25 -0000	1.12
@@ -51,6 +51,7 @@
 LDAPControl* windows_private_dirsync_control(const Repl_Agmt *ra);
 ConnResult send_dirsync_search(Repl_Connection *conn);
 ConnResult windows_search_entry(Repl_Connection *conn, char* searchbase, char *filter, Slapi_Entry **entry);
+ConnResult windows_search_entry_ext(Repl_Connection *conn, char* searchbase, char *filter, Slapi_Entry **entry, LDAPControl **serverctrls);
 Slapi_Entry *windows_conn_get_search_result(Repl_Connection *conn );
 void windows_private_update_dirsync_control(const Repl_Agmt *ra,LDAPControl **controls );
 PRBool windows_private_dirsync_has_more(const Repl_Agmt *ra);
@@ -65,6 +66,8 @@
 static void windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain);
 int windows_private_get_isnt4(const Repl_Agmt *ra);
 void windows_private_set_isnt4(const Repl_Agmt *ra, int isit);
+int windows_private_get_iswin2k3(const Repl_Agmt *ra);
+void windows_private_set_iswin2k3(const Repl_Agmt *ra, int isit);
 const char* windows_private_get_purl(const Repl_Agmt *ra);
 
 /* in windows_connection.c */
@@ -73,22 +76,23 @@
 void windows_conn_delete(Repl_Connection *conn);
 void windows_conn_get_error(Repl_Connection *conn, int *operation, int *error);
 ConnResult windows_conn_send_add(Repl_Connection *conn, const char *dn, LDAPMod **attrs,
-	LDAPControl *update_control, LDAPControl ***returned_controls);
+	LDAPControl **server_controls, LDAPControl ***returned_controls);
 ConnResult windows_conn_send_delete(Repl_Connection *conn, const char *dn,
-	LDAPControl *update_control, LDAPControl ***returned_controls);
+	LDAPControl **server_controls, LDAPControl ***returned_controls);
 ConnResult windows_conn_send_modify(Repl_Connection *conn, const char *dn, LDAPMod **mods,
-	LDAPControl *update_control, LDAPControl ***returned_controls);
+	LDAPControl **server_controls, LDAPControl ***returned_controls);
 ConnResult windows_conn_send_rename(Repl_Connection *conn, const char *dn,
 	const char *newrdn, const char *newparent, int deleteoldrdn,
-	LDAPControl *update_control, LDAPControl ***returned_controls);
+	LDAPControl **server_controls, LDAPControl ***returned_controls);
 ConnResult windows_conn_send_extended_operation(Repl_Connection *conn, const char *extop_oid,
 	struct berval *payload, char **retoidp, struct berval **retdatap,
-	LDAPControl *update_control, LDAPControl ***returned_controls);
+	LDAPControl **server_controls, LDAPControl ***returned_controls);
 const char *windows_conn_get_status(Repl_Connection *conn);
 void windows_conn_start_linger(Repl_Connection *conn);
 void windows_conn_cancel_linger(Repl_Connection *conn);
 ConnResult windows_conn_replica_supports_ds5_repl(Repl_Connection *conn);
 ConnResult windows_conn_replica_supports_dirsync(Repl_Connection *conn);
+ConnResult windows_conn_replica_is_win2k3(Repl_Connection *conn);
 ConnResult windows_conn_read_entry_attribute(Repl_Connection *conn, const char *dn, char *type,
 	struct berval ***returned_bvals);
 ConnResult windows_conn_push_schema(Repl_Connection *conn, CSN **remotecsn);
@@ -102,3 +106,6 @@
 /* Used to check for pre-hashed passwords when syncing */
 #define PASSWD_CLEAR_PREFIX "{clear}"
 #define PASSWD_CLEAR_PREFIX_LEN 7
+/* Used for GUID format conversion */
+#define NTUNIQUEID_LENGTH 32
+#define AD_GUID_LENGTH 36




More information about the Fedora-directory-commits mailing list