summaryrefslogtreecommitdiffstats
path: root/ldap/servers
diff options
context:
space:
mode:
authorNathan Kinder <nkinder@redhat.com>2007-09-12 23:05:25 +0000
committerNathan Kinder <nkinder@redhat.com>2007-09-12 23:05:25 +0000
commitf236db01a582398977853a7dacf0161ccc140c7f (patch)
tree973128d83f96683bd34e669887e34a6ecc862d4a /ldap/servers
parent2ee49b2fe9f96893809d4dbf2c2362a0e018694f (diff)
downloadds-f236db01a582398977853a7dacf0161ccc140c7f.tar.gz
ds-f236db01a582398977853a7dacf0161ccc140c7f.tar.xz
ds-f236db01a582398977853a7dacf0161ccc140c7f.zip
Resolves: 243227
Summary: Handle syncing add opererations that have a ntuniqueid present.
Diffstat (limited to 'ldap/servers')
-rw-r--r--ldap/servers/plugins/replication/repl5.h10
-rw-r--r--ldap/servers/plugins/replication/windows_connection.c116
-rw-r--r--ldap/servers/plugins/replication/windows_private.c33
-rw-r--r--ldap/servers/plugins/replication/windows_protocol_util.c375
-rw-r--r--ldap/servers/plugins/replication/windowsrepl.h17
5 files changed, 490 insertions, 61 deletions
diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
index 304f1267..ed27f4e7 100644
--- a/ldap/servers/plugins/replication/repl5.h
+++ b/ldap/servers/plugins/replication/repl5.h
@@ -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 @@ typedef enum
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);
diff --git a/ldap/servers/plugins/replication/windows_connection.c b/ldap/servers/plugins/replication/windows_connection.c
index 8ef08419..a898a8ce 100644
--- a/ldap/servers/plugins/replication/windows_connection.c
+++ b/ldap/servers/plugins/replication/windows_connection.c
@@ -83,6 +83,7 @@ typedef struct repl_connection
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 @@ windows_conn_new(Repl_Agmt *agmt)
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 @@ windows_conn_get_error(Repl_Connection *conn, int *operation, int *error)
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 @@ windows_LDAPMessage2Entry(LDAP * ld, LDAPMessage * msg, int attrsonly) {
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 @@ windows_search_entry(Repl_Connection *conn, char* searchbase, char *filter, Slap
{
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 @@ send_dirsync_search(Repl_Connection *conn)
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 @@ send_dirsync_search(Repl_Connection *conn)
*/
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 @@ windows_conn_send_add(Repl_Connection *conn, const char *dn, LDAPMod **attrs,
*/
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 @@ windows_conn_send_delete(Repl_Connection *conn, const char *dn,
*/
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 @@ windows_conn_send_modify(Repl_Connection *conn, const char *dn, LDAPMod **mods,
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 @@ Slapi_Entry * windows_conn_get_search_result(Repl_Connection *conn)
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_conn_connect(Repl_Connection *conn)
{
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 @@ windows_conn_replica_supports_dirsync(Repl_Connection *conn)
}
+/* 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".
diff --git a/ldap/servers/plugins/replication/windows_private.c b/ldap/servers/plugins/replication/windows_private.c
index 340047f7..1c2e0fbe 100644
--- a/ldap/servers/plugins/replication/windows_private.c
+++ b/ldap/servers/plugins/replication/windows_private.c
@@ -65,6 +65,7 @@ struct windowsprivate {
PRBool create_groups_from_dirsync;
char *windows_domain;
int isnt4;
+ int iswin2k3;
};
static int
@@ -245,6 +246,38 @@ void windows_private_set_isnt4(const Repl_Agmt *ra, int isit)
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)
diff --git a/ldap/servers/plugins/replication/windows_protocol_util.c b/ldap/servers/plugins/replication/windows_protocol_util.c
index 4ff364af..3e28c4cc 100644
--- a/ldap/servers/plugins/replication/windows_protocol_util.c
+++ b/ldap/servers/plugins/replication/windows_protocol_util.c
@@ -71,11 +71,16 @@ static int windows_get_local_entry(const Slapi_DN* local_dn,Slapi_Entry **local_
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 @@ windows_dump_entry(const char *string, Slapi_Entry *e)
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 @@ process_replay_add(Private_Repl_Protocol *prp, slapi_operation_parameters *op, S
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 @@ process_replay_add(Private_Repl_Protocol *prp, slapi_operation_parameters *op, S
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 @@ process_replay_add(Private_Repl_Protocol *prp, slapi_operation_parameters *op, S
} 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 @@ ConnResult
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 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
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 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
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 @@ windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op
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 @@ windows_create_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *original_ent
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 @@ windows_create_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *original_ent
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 @@ windows_create_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *original_ent
error:
if (username)
{
- slapi_ch_free((void**)&username);
+ slapi_ch_free_string(&username);
}
if (new_entry)
{
@@ -1633,7 +1721,7 @@ windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod **original_mods,
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 @@ windows_get_remote_entry (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,
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 @@ find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry *
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 @@ find_entry_by_guid(const char *guid, Slapi_Entry **e, const Repl_Agmt *ra)
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 @@ dedash(char *str)
}
}
+/* 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 @@ decrypt(char *guid)
p++;
i++;
}
- slapi_ch_free((void**)&cpy);
+ slapi_ch_free_string(&cpy);
}
static char*
@@ -1971,8 +2154,8 @@ extract_guid_from_tombstone_dn(const char *dn)
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 @@ convert_to_hex(Slapi_Value *val)
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 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
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 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
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 @@ map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp,
retval = -1;
}
}
- slapi_ch_free((void**)&username);
+ slapi_ch_free_string(&username);
}
if (remote_entry)
{
@@ -2280,7 +2585,7 @@ map_tombstone_dn_inbound(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra)
if (guid)
{
- slapi_ch_free((void**)&guid);
+ slapi_ch_free_string(&guid);
}
if (matching_entry)
{
@@ -2387,7 +2692,7 @@ map_entry_dn_inbound(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra)
}
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 @@ error:
}
if (username)
{
- slapi_ch_free((void **) &username);
+ slapi_ch_free_string(&username);
}
return retval;
}
@@ -2542,7 +2847,7 @@ windows_create_local_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,
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 @@ windows_create_local_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,
{
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 @@ error:
}
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 @@ windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entr
if (guid)
{
slapi_mods_add_string(smods,LDAP_MOD_ADD,local_type,guid);
- slapi_ch_free((void**)&guid);
+ slapi_ch_free_string(&guid);
}
} else
{
diff --git a/ldap/servers/plugins/replication/windowsrepl.h b/ldap/servers/plugins/replication/windowsrepl.h
index 121fbd74..a7d3267b 100644
--- a/ldap/servers/plugins/replication/windowsrepl.h
+++ b/ldap/servers/plugins/replication/windowsrepl.h
@@ -51,6 +51,7 @@ const Slapi_DN* windows_private_get_directory_subtree (const Repl_Agmt *ra);
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 @@ const char *windows_private_get_windows_domain(const Repl_Agmt *ra);
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_disconnect(Repl_Connection *conn);
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 @@ void windows_conn_set_agmt_changed(Repl_Connection *conn);
/* 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