From 64d7791829079f868c06895822c4fa4299a8c890 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Aug 28 2012 01:55:14 +0000 Subject: Ticket #355 - winsync should not delete entry that appears to be out of scope https://fedorahosted.org/389/ticket/355 Resolves: Ticket #355 Bug Description: winsync should not delete entry that appears to be out of scope Reviewed by: nhosoi (Thanks!) Branch: 389-ds-base-1.2.11 Fix Description: There is a new winsync config attribute - winSyncMoveAction - this is the action to take on the DS side when the winsync finds an AD entry that has the same name/uid as a DS entry but the AD entry is out of the scope of the sync agreement (winsync has to search out of scope/subtree on AD to support deleted and moved entries). In earlier versions of DS, these entries were ignored. When DS was changed to support entry move/subtree rename, the winsync code was changed to delete entries moved out of scope. The new winSyncMoveAction has 3 values: none - ignore moved entries (like older versions of DS) delete - delete DS entries when the AD entry moves out of scope - like current versions of DS unsync - new behavior - if the DS entry is currently synced with the AD entry this will cause the DS entry to be "unlinked" from the AD entry so that they will no longer be in sync The default value is "none" because we should not unexpectedly delete DS entries (principle of least astonishment). Another problem with winsync is that it allowed you to change the subtree and domain in the middle of a sync update - this can lead to a great deal of confusion if suddenly many entries are out of scope. The fix is to "save" the changes in the entry, and apply those changes when the update is complete. Platforms tested: RHEL6 x86_64 Flag Day: yes - new attribute, schema Doc impact: yes - new attribute, schema (cherry picked from commit 3206571b8ac8308482c20c3866f407079479b8e6) (cherry picked from commit 94f9ceffbc0dba95763b9a27b71955323c58384f) --- diff --git a/ldap/schema/02common.ldif b/ldap/schema/02common.ldif index db8ea19..c209615 100644 --- a/ldap/schema/02common.ldif +++ b/ldap/schema/02common.ldif @@ -144,6 +144,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.1004 NAME 'nsds7WindowsDomain' DESC 'Net attributeTypes: ( 2.16.840.1.113730.3.1.1005 NAME 'nsds7DirsyncCookie' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) attributeTypes: ( 2.16.840.1.113730.3.1.1099 NAME 'winSyncInterval' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) attributeTypes: ( 2.16.840.1.113730.3.1.1100 NAME 'oneWaySync' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) +attributeTypes: ( 2.16.840.1.113730.3.1.2139 NAME 'winSyncMoveAction' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) attributeTypes: ( 1.3.6.1.1.4 NAME 'vendorName' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' ) attributeTypes: ( 1.3.6.1.1.5 NAME 'vendorVersion' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' ) attributeTypes: ( 2.16.840.1.113730.3.1.3023 NAME 'nsViewFilter' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Netscape Directory Server' ) @@ -178,7 +179,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.99 NAME 'cosSuperDefinition' DESC 'Netsca objectClasses: ( 2.16.840.1.113730.3.2.100 NAME 'cosClassicDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn $ cosspecifier ) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.101 NAME 'cosPointerDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn ) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.102 NAME 'cosIndirectDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosIndirectSpecifier ) X-ORIGIN 'Netscape Directory Server' ) -objectClasses: ( 2.16.840.1.113730.3.2.503 NAME 'nsDSWindowsReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5replicaSessionPauseTime $ nsds7WindowsReplicaSubtree $ nsds7DirectoryReplicaSubtree $ nsds7NewWinUserSyncEnabled $ nsds7NewWinGroupSyncEnabled $ nsds7WindowsDomain $ nsds7DirsyncCookie $ winSyncInterval $ oneWaySync) X-ORIGIN 'Netscape Directory Server' ) +objectClasses: ( 2.16.840.1.113730.3.2.503 NAME 'nsDSWindowsReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5replicaSessionPauseTime $ nsds7WindowsReplicaSubtree $ nsds7DirectoryReplicaSubtree $ nsds7NewWinUserSyncEnabled $ nsds7NewWinGroupSyncEnabled $ nsds7WindowsDomain $ nsds7DirsyncCookie $ winSyncInterval $ oneWaySync $ winSyncMoveAction) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.128 NAME 'costemplate' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ cospriority ) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.304 NAME 'nsView' DESC 'Netscape defined objectclass' SUP top AUXILIARY MAY ( nsViewFilter $ description ) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.316 NAME 'nsAttributeEncryption' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsEncryptionAlgorithm ) X-ORIGIN 'Netscape Directory Server' ) diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h index 26d0298..7b6cb8c 100644 --- a/ldap/servers/plugins/replication/repl5.h +++ b/ldap/servers/plugins/replication/repl5.h @@ -169,6 +169,7 @@ extern const char *type_nsds7DirsyncCookie; extern const char *type_nsds7WindowsDomain; extern const char *type_winSyncInterval; extern const char *type_oneWaySync; +extern const char *type_winsyncMoveAction; /* To Allow Consumer Initialisation when adding an agreement - */ extern const char *type_nsds5BeginReplicaRefresh; @@ -342,6 +343,7 @@ void agmt_set_last_update_start (Repl_Agmt *ra, time_t start_time); void agmt_set_last_update_end (Repl_Agmt *ra, time_t end_time); void agmt_set_last_update_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *msg); void agmt_set_update_in_progress (Repl_Agmt *ra, PRBool in_progress); +PRBool agmt_get_update_in_progress (const Repl_Agmt *ra); void agmt_set_last_init_start (Repl_Agmt *ra, time_t start_time); void agmt_set_last_init_end (Repl_Agmt *ra, time_t end_time); void agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *msg); @@ -367,6 +369,7 @@ void agmt_set_cleanruv_notified_from_entry(Repl_Agmt *ra, Slapi_Entry *e); int agmt_set_cleanruv_data(Repl_Agmt *ra, ReplicaId rid, int op); int agmt_is_cleanruv_notified(Repl_Agmt *ra, ReplicaId rid); int agmt_set_timeout(Repl_Agmt *ra, long timeout); +void agmt_update_done(Repl_Agmt *ra, int is_total); typedef struct replica Replica; @@ -679,6 +682,7 @@ int windows_handle_modify_agreement(Repl_Agmt *ra, const char *type, Slapi_Entry void windows_agreement_delete(Repl_Agmt *ra); Repl_Connection *windows_conn_new(Repl_Agmt *agmt); void windows_conn_delete(Repl_Connection *conn); +void windows_update_done(Repl_Agmt *ra, int is_total); /* repl_session_plugin.c */ void repl_session_plugin_init(); diff --git a/ldap/servers/plugins/replication/repl5_agmt.c b/ldap/servers/plugins/replication/repl5_agmt.c index 9002ff3..d25f54b 100644 --- a/ldap/servers/plugins/replication/repl5_agmt.c +++ b/ldap/servers/plugins/replication/repl5_agmt.c @@ -2265,6 +2265,17 @@ agmt_set_update_in_progress (Repl_Agmt *ra, PRBool in_progress) } } +PRBool +agmt_get_update_in_progress (const Repl_Agmt *ra) +{ + PR_ASSERT(NULL != ra); + if (NULL != ra) + { + return ra->update_in_progress; + } + return PR_FALSE; +} + void agmt_inc_last_update_changecount (Repl_Agmt *ra, ReplicaId rid, int skipped) { @@ -2694,3 +2705,18 @@ agmt_set_cleanruv_notified_from_entry(Repl_Agmt *ra, Slapi_Entry *e){ } PR_Unlock(ra->lock); } + +/* this is called whenever an update (total/incremental) + is completed */ +void +agmt_update_done(Repl_Agmt *agmt, int is_total) +{ + /* we could do a lot of stuff here - consolidate all of the other stuff that gets + done at the end of an update - setting status, times, etc. + but for now, all we want to do is "flush" any pending changes made + during the update into the proper structures so they are in place for the + next run + */ + windows_update_done(agmt, is_total); +} + diff --git a/ldap/servers/plugins/replication/repl5_inc_protocol.c b/ldap/servers/plugins/replication/repl5_inc_protocol.c index 025c23f..743be57 100644 --- a/ldap/servers/plugins/replication/repl5_inc_protocol.c +++ b/ldap/servers/plugins/replication/repl5_inc_protocol.c @@ -962,6 +962,7 @@ repl5_inc_run(Private_Repl_Protocol *prp) /* richm: We at least need to let monitors know that the protocol has been * shutdown - maybe they can figure out why */ agmt_set_last_update_status(prp->agmt, 0, 0, "Protocol stopped"); + agmt_update_done(prp->agmt, 0); break; } @@ -1076,6 +1077,7 @@ repl5_inc_run(Private_Repl_Protocol *prp) } agmt_set_last_update_end(prp->agmt, current_time()); agmt_set_update_in_progress(prp->agmt, PR_FALSE); + agmt_update_done(prp->agmt, 0); /* If timed out, close the connection after released the replica */ release_replica(prp); if (rc == UPDATE_TIMEOUT) { diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c index 5eeef5d..b10d2d0 100644 --- a/ldap/servers/plugins/replication/repl5_tot_protocol.c +++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c @@ -475,6 +475,8 @@ repl5_tot_run(Private_Repl_Protocol *prp) slapi_pblock_destroy (pb); agmt_set_last_init_end(prp->agmt, current_time()); rc = cb_data.rc; + agmt_set_update_in_progress(prp->agmt, PR_FALSE); + agmt_update_done(prp->agmt, 1); release_replica(prp); if (rc != LDAP_SUCCESS) diff --git a/ldap/servers/plugins/replication/repl_globals.c b/ldap/servers/plugins/replication/repl_globals.c index 383d562..f31a476 100644 --- a/ldap/servers/plugins/replication/repl_globals.c +++ b/ldap/servers/plugins/replication/repl_globals.c @@ -141,6 +141,7 @@ const char *type_nsds7WindowsDomain = "nsds7WindowsDomain"; const char *type_nsds7DirsyncCookie = "nsds7DirsyncCookie"; const char *type_winSyncInterval = "winSyncInterval"; const char *type_oneWaySync = "oneWaySync"; +const char *type_winsyncMoveAction = "winSyncMoveAction"; /* To Allow Consumer Initialization when adding an agreement - */ const char *type_nsds5BeginReplicaRefresh = "nsds5BeginReplicaRefresh"; diff --git a/ldap/servers/plugins/replication/windows_inc_protocol.c b/ldap/servers/plugins/replication/windows_inc_protocol.c index 3bba77d..e9df9f6 100644 --- a/ldap/servers/plugins/replication/windows_inc_protocol.c +++ b/ldap/servers/plugins/replication/windows_inc_protocol.c @@ -769,6 +769,7 @@ windows_inc_run(Private_Repl_Protocol *prp) /* richm: We at least need to let monitors know that the protocol has been shutdown - maybe they can figure out why */ agmt_set_last_update_status(prp->agmt, 0, 0, "Protocol stopped"); + agmt_update_done(prp->agmt, 0); break; } @@ -903,6 +904,7 @@ windows_inc_run(Private_Repl_Protocol *prp) agmt_set_last_update_end(prp->agmt, current_time()); agmt_set_update_in_progress(prp->agmt, PR_FALSE); + agmt_update_done(prp->agmt, 0); /* If timed out, close the connection after released the replica */ windows_release_replica(prp); if (rc == UPDATE_TIMEOUT) { diff --git a/ldap/servers/plugins/replication/windows_private.c b/ldap/servers/plugins/replication/windows_private.c index 5c7b725..ec619bc 100644 --- a/ldap/servers/plugins/replication/windows_private.c +++ b/ldap/servers/plugins/replication/windows_private.c @@ -76,6 +76,7 @@ struct windowsprivate { void *api_cookie; /* private data used by api callbacks */ time_t sync_interval; /* how often to run the dirsync search, in seconds */ int one_way; /* Indicates if this is a one-way agreement and which direction it is */ + int move_action; /* Indicates what to do with DS entry if AD entry is moved out of scope */ }; static void windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain); @@ -93,11 +94,62 @@ true_value_from_string(char *val) } } +/* yech - can't declare a constant string array because type_nsds7XX variables + are not constant strings - so have to build a lookup table */ +static int +get_next_disallow_attr_type(int *ii, const char **type) +{ + switch (*ii) { + case 0: *type = type_nsds7WindowsReplicaArea; break; + case 1: *type = type_nsds7DirectoryReplicaArea; break; + case 2: *type = type_nsds7WindowsDomain; break; + default: *type = NULL; break; + } + + if (*type) { + (*ii)++; + return 1; + } + return 0; +} + +static int +check_update_allowed(Repl_Agmt *ra, const char *type, Slapi_Entry *e, int *retval) +{ + int rc = 1; + + /* note - it is not an error to defer setting the value in the ra */ + *retval = 1; + if (agmt_get_update_in_progress(ra)) { + const char *distype = NULL; + int ii = 0; + while (get_next_disallow_attr_type(&ii, &distype)) { + if (slapi_attr_types_equivalent(type, distype)) { + char *tmpstr = slapi_entry_attr_get_charptr(e, type); + slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, + "windows_parse_config_entry: setting %s to %s will be " + "deferred until current update is completed\n", + type, tmpstr); + slapi_ch_free_string(&tmpstr); + rc = 0; + break; + } + } + } + + return rc; +} + static int windows_parse_config_entry(Repl_Agmt *ra, const char *type, Slapi_Entry *e) { char *tmpstr = NULL; int retval = 0; + + if (!check_update_allowed(ra, type, e, &retval)) + { + return retval; + } if (type == NULL || slapi_attr_types_equivalent(type,type_nsds7WindowsReplicaArea)) { @@ -190,6 +242,32 @@ windows_parse_config_entry(Repl_Agmt *ra, const char *type, Slapi_Entry *e) slapi_ch_free((void**)&tmpstr); retval = 1; } + if (type == NULL || slapi_attr_types_equivalent(type,type_winsyncMoveAction)) + { + tmpstr = slapi_entry_attr_get_charptr(e, type_winsyncMoveAction); + if (NULL != tmpstr) + { + if (strcasecmp(tmpstr, "delete") == 0) { + windows_private_set_move_action(ra, MOVE_DOES_DELETE); + } else if (strcasecmp(tmpstr, "unsync") == 0) { + windows_private_set_move_action(ra, MOVE_DOES_UNSYNC); + } else if (strcasecmp(tmpstr, "none") == 0) { + windows_private_set_move_action(ra, MOVE_DOES_NOTHING); + } else { + slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, + "Ignoring illegal setting for %s attribute in replication " + "agreement \"%s\". Valid values are \"delete\" or " + "\"unsync\".\n", type_winsyncMoveAction, slapi_entry_get_dn(e)); + windows_private_set_move_action(ra, MOVE_DOES_NOTHING); + } + } + else + { + windows_private_set_move_action(ra, MOVE_DOES_NOTHING); + } + slapi_ch_free((void**)&tmpstr); + retval = 1; + } return retval; } @@ -207,6 +285,26 @@ windows_handle_modify_agreement(Repl_Agmt *ra, const char *type, Slapi_Entry *e) } } +void +windows_update_done(Repl_Agmt *agmt, int is_total) +{ + /* "flush" the changes made during the update to the agmt */ + /* get the agmt entry */ + Slapi_DN *agmtdn = slapi_sdn_dup(agmt_get_dn_byref(agmt)); + Slapi_Entry *agmte = NULL; + int rc = slapi_search_internal_get_entry(agmtdn, NULL, &agmte, + repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION)); + if ((rc == 0) && agmte) { + int ii = 0; + const char *distype = NULL; + while (get_next_disallow_attr_type(&ii, &distype)) { + windows_handle_modify_agreement(agmt, distype, agmte); + } + } + slapi_entry_free(agmte); + slapi_sdn_free(&agmtdn); +} + void windows_init_agreement_from_entry(Repl_Agmt *ra, Slapi_Entry *e) { @@ -1019,6 +1117,39 @@ windows_private_set_sync_interval(Repl_Agmt *ra, char *str) LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_sync_interval\n" ); } +int +windows_private_get_move_action(const Repl_Agmt *ra) +{ + Dirsync_Private *dp; + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_move_action\n" ); + + PR_ASSERT(ra); + + dp = (Dirsync_Private *) agmt_get_priv(ra); + PR_ASSERT (dp); + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_move_action\n" ); + + return dp->move_action; +} + +void +windows_private_set_move_action(const Repl_Agmt *ra, int value) +{ + Dirsync_Private *dp; + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_move_action\n" ); + + PR_ASSERT(ra); + + dp = (Dirsync_Private *) agmt_get_priv(ra); + PR_ASSERT (dp); + dp->move_action = value; + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_move_action\n" ); +} + static PRCallOnceType winsync_callOnce = {0,0}; struct winsync_plugin { diff --git a/ldap/servers/plugins/replication/windows_protocol_util.c b/ldap/servers/plugins/replication/windows_protocol_util.c index 79e5c7c..d831fa0 100644 --- a/ldap/servers/plugins/replication/windows_protocol_util.c +++ b/ldap/servers/plugins/replication/windows_protocol_util.c @@ -4990,6 +4990,85 @@ windows_get_local_entry(const Slapi_DN* local_dn,Slapi_Entry **local_entry) return retval; } +static int +windows_unsync_entry(Private_Repl_Protocol *prp, Slapi_Entry *e) +{ + /* remote the ntuser/ntgroup objectclass and all attributes whose + name begins with "nt" - this will effectively cause the entry + to become "unsynced" with the corresponding windows entry */ + Slapi_Mods *smods = NULL; + Slapi_Value *ntu = NULL, *ntg = NULL; + Slapi_Value *va[2] = {NULL, NULL}; + char **syncattrs = NULL; + PRUint32 ocflags = SLAPI_OC_FLAG_REQUIRED|SLAPI_OC_FLAG_ALLOWED; + Slapi_PBlock *pb = NULL; + int ii; + int rc = -1; + + smods = slapi_mods_new(); + ntu = slapi_value_new_string("ntuser"); + ntg = slapi_value_new_string("ntgroup"); + + if (slapi_entry_attr_has_syntax_value(e, "objectclass", ntu)) { + syncattrs = slapi_schema_list_objectclass_attributes(slapi_value_get_string(ntu), ocflags); + va[0] = ntu; + } else if (slapi_entry_attr_has_syntax_value(e, "objectclass", ntg)) { + syncattrs = slapi_schema_list_objectclass_attributes(slapi_value_get_string(ntg), ocflags); + va[0] = ntg; + } else { + rc = 0; /* not an error */ + goto done; /* nothing to see here, move along */ + } + slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, "objectclass", va); + slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, + "%s: windows_unsync_entry: removing objectclass %s from %s\n", + agmt_get_long_name(prp->agmt), slapi_value_get_string(va[0]), + slapi_entry_get_dn_const(e)); + + for (ii = 0; syncattrs && syncattrs[ii]; ++ii) { + const char *type = syncattrs[ii]; + Slapi_Attr *attr = NULL; + + if (!slapi_entry_attr_find(e, type, &attr) && attr) { + if (!PL_strncasecmp(type, "nt", 2)) { /* begins with "nt" */ + slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, type, NULL); + slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, + "%s: windows_unsync_entry: removing attribute %s from %s\n", + agmt_get_long_name(prp->agmt), type, + slapi_entry_get_dn_const(e)); + } + } + } + + pb = slapi_pblock_new(); + if (!pb) { + goto done; + } + slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, + "%s: windows_unsync_entry: modifying entry %s\n", + agmt_get_long_name(prp->agmt), slapi_entry_get_dn_const(e)); + slapi_modify_internal_set_pb_ext(pb, slapi_entry_get_sdn(e), + slapi_mods_get_ldapmods_byref(smods), NULL, NULL, + repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0); + slapi_modify_internal_pb(pb); + slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); + if (rc) { + slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, + "%s: windows_unsync_entry: failed to modify entry %s - error %d:%s\n", + agmt_get_long_name(prp->agmt), slapi_entry_get_dn_const(e), + rc, ldap_err2string(rc)); + } + slapi_pblock_destroy(pb); + +done: + slapi_ch_array_free(syncattrs); + slapi_mods_free(&smods); + slapi_value_free(&ntu); + slapi_value_free(&ntg); + + return rc; +} + static int windows_process_dirsync_entry(Private_Repl_Protocol *prp,Slapi_Entry *e, int is_total) { @@ -5124,13 +5203,34 @@ retry: rc = windows_get_local_entry(local_sdn, &local_entry); if ((0 == rc) && local_entry) { - /* Need to delete the local entry since the remote counter - * part is now moved out of scope of the agreement. */ - /* Since map_Entry_dn_oubound returned local_sdn, - * the entry is either user or group. */ - rc = windows_delete_local_entry(local_sdn); - slapi_entry_free(local_entry); + if (windows_private_get_move_action(prp->agmt) == MOVE_DOES_DELETE) { + /* Need to delete the local entry since the remote counter + * part is now moved out of scope of the agreement. */ + /* Since map_Entry_dn_oubound returned local_sdn, + * the entry is either user or group. */ + slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, + "%s: windows_process_dirsync_entry: deleting out of " + "scope entry %s\n", agmt_get_long_name(prp->agmt), + slapi_sdn_get_dn(local_sdn)); + rc = windows_delete_local_entry(local_sdn); + } else if (windows_private_get_move_action(prp->agmt) == MOVE_DOES_UNSYNC) { + rc = windows_unsync_entry(prp, local_entry); + } else { + slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name, + "%s: windows_process_dirsync_entry: windows " + "inbound entry %s has the same name as local " + "entry %s but the windows entry is out of the " + "scope of the sync subtree [%s] - if you want " + "these entries to be in sync, add the ntUser/ntGroup " + "objectclass and required attributes to the local " + "entry, and move the windows entry into scope\n", + agmt_get_long_name(prp->agmt), + slapi_entry_get_dn_const(e), + slapi_sdn_get_dn(local_sdn), + slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt))); + } } + slapi_entry_free(local_entry); slapi_sdn_free(&local_sdn); } } diff --git a/ldap/servers/plugins/replication/windows_tot_protocol.c b/ldap/servers/plugins/replication/windows_tot_protocol.c index 6c54004..0791941 100644 --- a/ldap/servers/plugins/replication/windows_tot_protocol.c +++ b/ldap/servers/plugins/replication/windows_tot_protocol.c @@ -151,6 +151,8 @@ windows_tot_run(Private_Repl_Protocol *prp) agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress"); + agmt_set_update_in_progress(prp->agmt, PR_TRUE); + slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Beginning total update of replica " "\"%s\".\n", agmt_get_long_name(prp->agmt)); @@ -216,7 +218,6 @@ windows_tot_run(Private_Repl_Protocol *prp) server_controls = NULL; slapi_pblock_destroy (pb); - agmt_set_last_init_end(prp->agmt, current_time()); rc = cb_data.rc; windows_release_replica(prp); @@ -249,6 +250,10 @@ windows_tot_run(Private_Repl_Protocol *prp) /* Save the dirsync cookie. */ windows_private_save_dirsync_cookie(prp->agmt); + agmt_set_last_init_end(prp->agmt, current_time()); + agmt_set_update_in_progress(prp->agmt, PR_FALSE); + agmt_update_done(prp->agmt, 1); + /* call end total update callback */ winsync_plugin_call_end_update_cb(prp->agmt, windows_private_get_directory_subtree(prp->agmt), diff --git a/ldap/servers/plugins/replication/windowsrepl.h b/ldap/servers/plugins/replication/windowsrepl.h index 5780e74..6047fef 100644 --- a/ldap/servers/plugins/replication/windowsrepl.h +++ b/ldap/servers/plugins/replication/windowsrepl.h @@ -86,6 +86,8 @@ time_t windows_private_get_sync_interval(const Repl_Agmt *ra); void windows_private_set_sync_interval(Repl_Agmt *ra, char *str); PRBool windows_private_get_one_way(const Repl_Agmt *ra); void windows_private_set_one_way(const Repl_Agmt *ra, PRBool value); +int windows_private_get_move_action(const Repl_Agmt *ra); +void windows_private_set_move_action(const Repl_Agmt *ra, int value); /* in windows_connection.c */ ConnResult windows_conn_connect(Repl_Connection *conn); @@ -141,6 +143,14 @@ int windows_check_user_password(Repl_Connection *conn, Slapi_DN *sdn, char *pass #define ONE_WAY_SYNC_FROM_AD 1 #define ONE_WAY_SYNC_TO_AD 2 +/* + * Specifies what action for sync to take if it detects an AD entry has + * moved out of scope + */ +#define MOVE_DOES_NOTHING 0 +#define MOVE_DOES_UNSYNC 1 +#define MOVE_DOES_DELETE 2 + /* called for each replication agreement - so the winsync plugin can be agreement specific and store agreement specific data