From 8db7770486ba443d5e40674cd5c0e8edf7f39b45 Mon Sep 17 00:00:00 2001 From: Nathan Kinder Date: Nov 08 2010 21:25:41 +0000 Subject: Bug 635009 - Add one-way AD sync capability This patch adds one-way AD sync capability. A new "oneWaySync" configuration attribute can be added to a sync agreement to specify which direction one-way sync should work (values of "toWindows" and "fromWindows" will be accepted). Internally, the server will just skip pushing/pulling updates depending on the direction that sync is configured for. We will still update the RUV as if entries were actually synced. One can introduce inconsistencies by updating synced entries on the receiving side when using one way sync. This should be avoided by restricting update access to synced entries. --- diff --git a/ldap/schema/02common.ldif b/ldap/schema/02common.ldif index 3b0ef19..52c837a 100644 --- a/ldap/schema/02common.ldif +++ b/ldap/schema/02common.ldif @@ -139,6 +139,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.1003 NAME 'nsds7NewWinGroupSyncEnabled' attributeTypes: ( 2.16.840.1.113730.3.1.1004 NAME 'nsds7WindowsDomain' 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.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: ( 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' ) @@ -173,7 +174,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) 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.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 a9a1853..da85cbf 100644 --- a/ldap/servers/plugins/replication/repl5.h +++ b/ldap/servers/plugins/replication/repl5.h @@ -156,6 +156,8 @@ extern const char *type_nsds7CreateNewGroups; extern const char *type_nsds7DirsyncCookie; extern const char *type_nsds7WindowsDomain; extern const char *type_winSyncInterval; +extern const char *type_oneWaySync; + /* To Allow Consumer Initialisation when adding an agreement - */ extern const char *type_nsds5BeginReplicaRefresh; diff --git a/ldap/servers/plugins/replication/repl_globals.c b/ldap/servers/plugins/replication/repl_globals.c index ed7f801..f409be6 100644 --- a/ldap/servers/plugins/replication/repl_globals.c +++ b/ldap/servers/plugins/replication/repl_globals.c @@ -134,6 +134,7 @@ const char *type_nsds7CreateNewGroups = "nsds7NewWinGroupSyncEnabled"; const char *type_nsds7WindowsDomain = "nsds7WindowsDomain"; const char *type_nsds7DirsyncCookie = "nsds7DirsyncCookie"; const char *type_winSyncInterval = "winSyncInterval"; +const char *type_oneWaySync = "oneWaySync"; /* To Allow Consumer Initialisation 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 b6e1da3..f85699d 100644 --- a/ldap/servers/plugins/replication/windows_inc_protocol.c +++ b/ldap/servers/plugins/replication/windows_inc_protocol.c @@ -139,7 +139,7 @@ typedef struct windows_inc_private static PRUint32 event_occurred(Private_Repl_Protocol *prp, PRUint32 event); static void reset_events (Private_Repl_Protocol *prp); static void protocol_sleep(Private_Repl_Protocol *prp, PRIntervalTime duration); -static int send_updates(Private_Repl_Protocol *prp, RUV *ruv, PRUint32 *num_changes_sent); +static int send_updates(Private_Repl_Protocol *prp, RUV *ruv, PRUint32 *num_changes_sent, int do_send); static void windows_inc_backoff_expired(time_t timer_fire_time, void *arg); static int windows_examine_update_vector(Private_Repl_Protocol *prp, RUV *ruv); static const char* state2name (int state); @@ -294,6 +294,7 @@ windows_inc_run(Private_Repl_Protocol *prp) long busywaittime = 0; /* Some operations should only be done the first time STATE_START is true. */ static PRBool is_first_start = PR_TRUE; + int one_way; PRBool run_dirsync = PR_FALSE; @@ -303,6 +304,8 @@ windows_inc_run(Private_Repl_Protocol *prp) prp->terminate = 0; windows_private_load_dirsync_cookie(prp->agmt); + + one_way = windows_private_get_one_way(prp->agmt); do { int rc = 0; @@ -829,7 +832,14 @@ windows_inc_run(Private_Repl_Protocol *prp) windows_private_get_windows_subtree(prp->agmt), 0 /* is_total == FALSE */); - rc = send_updates(prp, ruv, &num_changes_sent); + if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_TO_AD)) { + rc = send_updates(prp, ruv, &num_changes_sent, 1 /* actually send updates */); + } else { + /* We call send_updates to fast-forward the RUV + * without actually sending any updates. */ + rc = send_updates(prp, ruv, &num_changes_sent, 0 /* don't send updates */); + } + if (rc == UPDATE_NO_MORE_UPDATES) { dev_debug("windows_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_NO_MORE_UPDATES -> STATE_WAIT_CHANGES"); @@ -877,7 +887,7 @@ windows_inc_run(Private_Repl_Protocol *prp) break; } - if ( run_dirsync ) + if ( run_dirsync && ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_FROM_AD))) { /* Don't run dirsync if we encountered * an error when sending updates to AD. */ @@ -1151,7 +1161,7 @@ w_cl5_operation_parameters_done (struct slapi_operation_parameters *sop) * UPDATE_SCHEDULE_WINDOW_CLOSED - the schedule window closed on us. */ static int -send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *num_changes_sent) +send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *num_changes_sent, int do_send) { CL5Entry entry; slapi_operation_parameters op; @@ -1305,7 +1315,12 @@ send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *nu continue; } /* This is where the work actually happens: */ - replay_crc = windows_replay_update(prp, entry.op); + if (do_send) { + replay_crc = windows_replay_update(prp, entry.op); + } else { + /* Skip sending the updates and just set a success code. */ + replay_crc = CONN_OPERATION_SUCCESS; + } if (CONN_OPERATION_SUCCESS != replay_crc) { int operation, error; diff --git a/ldap/servers/plugins/replication/windows_private.c b/ldap/servers/plugins/replication/windows_private.c index 9ad7681..2d47441 100644 --- a/ldap/servers/plugins/replication/windows_private.c +++ b/ldap/servers/plugins/replication/windows_private.c @@ -75,6 +75,7 @@ struct windowsprivate { int keep_raw_entry; /* flag to control when the raw entry is set */ 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 */ }; static void windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain); @@ -165,6 +166,30 @@ windows_parse_config_entry(Repl_Agmt *ra, const char *type, Slapi_Entry *e) slapi_ch_free_string(&tmpstr); retval = 1; } + if (type == NULL || slapi_attr_types_equivalent(type,type_oneWaySync)) + { + tmpstr = slapi_entry_attr_get_charptr(e, type_oneWaySync); + if (NULL != tmpstr) + { + if (strcasecmp(tmpstr, "fromWindows") == 0) { + windows_private_set_one_way(ra, ONE_WAY_SYNC_FROM_AD); + } else if (strcasecmp(tmpstr, "toWindows") == 0) { + windows_private_set_one_way(ra, ONE_WAY_SYNC_TO_AD); + } else { + slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, + "Ignoring illegal setting for %s attribute in replication " + "agreement \"%s\". Valid values are \"toWindows\" or " + "\"fromWindows\".\n", type_oneWaySync, slapi_entry_get_dn(e)); + windows_private_set_one_way(ra, ONE_WAY_SYNC_DISABLED); + } + } + else + { + windows_private_set_one_way(ra, ONE_WAY_SYNC_DISABLED); + } + slapi_ch_free((void**)&tmpstr); + retval = 1; + } return retval; } @@ -533,6 +558,38 @@ void windows_private_set_create_groups(const Repl_Agmt *ra, PRBool value) } +int windows_private_get_one_way(const Repl_Agmt *ra) +{ + Dirsync_Private *dp; + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_one_way\n" ); + + PR_ASSERT(ra); + dp = (Dirsync_Private *) agmt_get_priv(ra); + PR_ASSERT (dp); + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_one_way\n" ); + + return dp->one_way; +} + + +void windows_private_set_one_way(const Repl_Agmt *ra, int value) +{ + Dirsync_Private *dp; + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_one_way\n" ); + + PR_ASSERT(ra); + dp = (Dirsync_Private *) agmt_get_priv(ra); + PR_ASSERT (dp); + + dp->one_way = value; + + LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_one_way\n" ); +} + + /* This function returns the current Dirsync_Private that's inside Repl_Agmt ra as a ldap control. diff --git a/ldap/servers/plugins/replication/windows_tot_protocol.c b/ldap/servers/plugins/replication/windows_tot_protocol.c index 1390e8f..482d49d 100644 --- a/ldap/servers/plugins/replication/windows_tot_protocol.c +++ b/ldap/servers/plugins/replication/windows_tot_protocol.c @@ -96,9 +96,9 @@ static void windows_tot_delete(Private_Repl_Protocol **prp); static void windows_tot_run(Private_Repl_Protocol *prp) { - int rc; - callback_data cb_data; - Slapi_PBlock *pb; + int rc; + callback_data cb_data; + Slapi_PBlock *pb; char* dn; RUV *ruv = NULL; RUV *starting_ruv = NULL; @@ -108,6 +108,7 @@ windows_tot_run(Private_Repl_Protocol *prp) char *filter = slapi_ch_strdup("(|(objectclass=ntuser)(objectclass=ntgroup))"); char **attrs = NULL; LDAPControl **server_controls = NULL; + int one_way; LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_tot_run\n" ); @@ -120,35 +121,37 @@ windows_tot_run(Private_Repl_Protocol *prp) goto done; } + one_way = windows_private_get_one_way(prp->agmt); + windows_conn_set_timeout(prp->conn, agmt_get_timeout(prp->agmt)); - /* acquire remote replica */ + /* acquire remote replica */ agmt_set_last_init_start(prp->agmt, current_time()); - rc = windows_acquire_replica (prp, &ruv, 0 /* don't check RUV for total protocol */); - /* We never retry total protocol, even in case a transient error. - This is because if somebody already updated the replica we don't - want to do it again */ - if (rc != ACQUIRE_SUCCESS) - { + rc = windows_acquire_replica (prp, &ruv, 0 /* don't check RUV for total protocol */); + /* We never retry total protocol, even in case a transient error. + * This is because if somebody already updated the replica we don't + * want to do it again */ + if (rc != ACQUIRE_SUCCESS) + { int optype, ldaprc; windows_conn_get_error(prp->conn, &optype, &ldaprc); agmt_set_last_init_status(prp->agmt, ldaprc, prp->last_acquire_response_code, NULL); - goto done; - } + goto done; + } else if (prp->terminate) - { - windows_conn_disconnect(prp->conn); - prp->stopped = 1; + { + windows_conn_disconnect(prp->conn); + prp->stopped = 1; goto done; - } + } agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update in progress"); - agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress"); + agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress"); - slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Beginning total update of replica " + slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Beginning total update of replica " "\"%s\".\n", agmt_get_long_name(prp->agmt)); windows_private_null_dirsync_cookie(prp->agmt); @@ -159,8 +162,10 @@ windows_tot_run(Private_Repl_Protocol *prp) windows_private_get_windows_subtree(prp->agmt), 1 /* is_total == TRUE */); - /* get everything */ - windows_dirsync_inc_run(prp); + if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_FROM_AD)) { + /* get everything */ + windows_dirsync_inc_run(prp); + } windows_private_save_dirsync_cookie(prp->agmt); @@ -176,28 +181,33 @@ windows_tot_run(Private_Repl_Protocol *prp) starting_ruv = ruv_dup((RUV*) object_get_data ( local_ruv_obj )); object_release (local_ruv_obj); + /* Set up the callback data. */ + cb_data.prp = prp; + cb_data.rc = 0; + cb_data.num_entries = 0UL; + cb_data.sleep_on_busy = 0UL; + cb_data.last_busy = current_time (); - /* send everything */ - dn = slapi_ch_strdup(slapi_sdn_get_dn( windows_private_get_directory_subtree(prp->agmt))); + /* Don't send anything if one-way is set. */ + if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_TO_AD)) { + /* send everything */ + dn = slapi_ch_strdup(slapi_sdn_get_dn( windows_private_get_directory_subtree(prp->agmt))); - winsync_plugin_call_pre_ds_search_all_cb(prp->agmt, NULL, &dn, &scope, &filter, + winsync_plugin_call_pre_ds_search_all_cb(prp->agmt, NULL, &dn, &scope, &filter, &attrs, &server_controls); - pb = slapi_pblock_new (); - /* Perform a subtree search for any ntuser or ntgroup entries underneath the - * suffix defined in the sync agreement. */ - slapi_search_internal_set_pb (pb, dn, scope, filter, attrs, 0, server_controls, NULL, - repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0); - cb_data.prp = prp; - cb_data.rc = 0; - cb_data.num_entries = 0UL; - cb_data.sleep_on_busy = 0UL; - cb_data.last_busy = current_time (); + pb = slapi_pblock_new (); + /* Perform a subtree search for any ntuser or ntgroup entries underneath the + * suffix defined in the sync agreement. */ + slapi_search_internal_set_pb (pb, dn, scope, filter, attrs, 0, server_controls, NULL, + repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0); + + slapi_search_internal_callback_pb (pb, &cb_data /* callback data */, + get_result /* result callback */, + send_entry /* entry callback */, + NULL /* referral callback*/); + } - slapi_search_internal_callback_pb (pb, &cb_data /* callback data */, - get_result /* result callback */, - send_entry /* entry callback */, - NULL /* referral callback*/); slapi_ch_free_string(&dn); slapi_ch_free_string(&filter); slapi_ch_array_free(attrs); diff --git a/ldap/servers/plugins/replication/windowsrepl.h b/ldap/servers/plugins/replication/windowsrepl.h index f413746..9ecf75d 100644 --- a/ldap/servers/plugins/replication/windowsrepl.h +++ b/ldap/servers/plugins/replication/windowsrepl.h @@ -84,6 +84,8 @@ void *windows_private_get_api_cookie(const Repl_Agmt *ra); void windows_private_set_api_cookie(Repl_Agmt *ra, void *cookie); 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); /* in windows_connection.c */ ConnResult windows_conn_connect(Repl_Connection *conn); @@ -133,6 +135,13 @@ int windows_check_user_password(Repl_Connection *conn, Slapi_DN *sdn, char *pass */ #define PERIODIC_DIRSYNC_INTERVAL 5 * 60 /* default value is 5 minutes */ +/* + * One way sync flags. Used to indicate the direction when one-way sync is used. + */ +#define ONE_WAY_SYNC_DISABLED 0 +#define ONE_WAY_SYNC_FROM_AD 1 +#define ONE_WAY_SYNC_TO_AD 2 + /* called for each replication agreement - so the winsync plugin can be agreement specific and store agreement specific data