diff --git a/SOURCES/0061-Ticket-48005-ns-slapd-crash-in-shutdown-phase.patch b/SOURCES/0061-Ticket-48005-ns-slapd-crash-in-shutdown-phase.patch new file mode 100644 index 0000000..3d59efe --- /dev/null +++ b/SOURCES/0061-Ticket-48005-ns-slapd-crash-in-shutdown-phase.patch @@ -0,0 +1,1057 @@ +From 2e87dafa9d39e6ab08382612be762c25afa80d4f Mon Sep 17 00:00:00 2001 +From: Noriko Hosoi +Date: Sun, 15 Feb 2015 17:34:12 -0800 +Subject: [PATCH] Ticket #48005 - ns-slapd crash in shutdown phase + +Description: There was a small window that long running tasks access its +own task object after it's aready released by main thread in the shutdown +period. This patch adds refcounter to such threads and make destructor +wait until the counter becomes 0. Plus, the shutdown check is added to +their task callbacks. + +Following tasks are updated by this patch: + slapd/task.c: "fixup tombstones" + posix-winsync/posix-winsync-config.c: "memberuid task" + replication/repl5_replica_config.c: "cleanallruv" + replication/repl5_replica_config.c: "abort cleanallruv" + syntaxes/validate_task.c: "syntax validate" + automember/automember.c: "automember rebuild membership" + automember/automember.c: "automember export updates" + automember/automember.c: "automember map updates" + linkedattrs/linked_attrs.c: "fixup linked attributes" + memberof/memberof.c: "memberof task" + schema_reload/schema_reload.c: "schema reload task" + usn/usn_cleanup.c: "USN tombstone cleanup task" + +Following tasks are already covered: + slapd/task.c: "import" + slapd/task.c: "index" + slapd/task.c: "upgradedb" + +Following tasks are processed in an ordinary worker thread; no need to change + slapd/task.c: "sysconfig reload" + slapd/task.c: "export" + slapd/task.c: "backup" + slapd/task.c: "restore" + +(cherry picked from commit ab2e26de21beb5a92d2a18ab5a20db9637b83c7a) +(cherry picked from commit eebbabbaba8f024671158f527a169fc378ff01d6) + +Conflicts: + ldap/servers/plugins/memberof/memberof.c +--- + ldap/servers/plugins/automember/automember.c | 70 ++++++++++++++++++---- + ldap/servers/plugins/linkedattrs/fixup_task.c | 40 +++++++++++-- + ldap/servers/plugins/memberof/memberof.c | 26 +++++++- + ldap/servers/plugins/memberof/memberof.h | 2 +- + .../plugins/posix-winsync/posix-group-task.c | 40 ++++++++++--- + .../plugins/replication/repl5_replica_config.c | 58 ++++++++++++++++++ + ldap/servers/plugins/schema_reload/schema_reload.c | 23 ++++++- + ldap/servers/plugins/syntaxes/validate_task.c | 26 +++++++- + ldap/servers/plugins/usn/usn_cleanup.c | 58 +++++++++++++----- + ldap/servers/slapd/slapi-plugin.h | 9 +++ + ldap/servers/slapd/slapi-private.h | 2 - + ldap/servers/slapd/task.c | 47 +++++++++++++-- + 12 files changed, 348 insertions(+), 53 deletions(-) + +diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c +index 6a8fd22..b2914db 100644 +--- a/ldap/servers/plugins/automember/automember.c ++++ b/ldap/servers/plugins/automember/automember.c +@@ -119,9 +119,9 @@ static int automember_task_add_map_entries(Slapi_PBlock *pb, Slapi_Entry *e, Sla + void automember_rebuild_task_thread(void *arg); + void automember_export_task_thread(void *arg); + void automember_map_task_thread(void *arg); +-void automember_task_destructor(Slapi_Task *task); +-void automember_task_export_destructor(Slapi_Task *task); +-void automember_task_map_destructor(Slapi_Task *task); ++static void automember_task_destructor(Slapi_Task *task); ++static void automember_task_export_destructor(Slapi_Task *task); ++static void automember_task_map_destructor(Slapi_Task *task); + + #define DEFAULT_FILE_MODE PR_IRUSR | PR_IWUSR + +@@ -1962,11 +1962,15 @@ fetch_attr(Slapi_Entry *e, const char *attrname, const char *default_val) + return slapi_value_get_string(val); + } + +-void ++static void + automember_task_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->bind_dn); + slapi_sdn_free(&mydata->base_dn); +@@ -1976,11 +1980,15 @@ automember_task_destructor(Slapi_Task *task) + } + } + +-void ++static void + automember_task_export_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->ldif_out); + slapi_ch_free_string(&mydata->bind_dn); +@@ -1991,11 +1999,15 @@ automember_task_export_destructor(Slapi_Task *task) + } + } + +-void ++static void + automember_task_map_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->ldif_out); + slapi_ch_free_string(&mydata->ldif_in); +@@ -2114,7 +2126,8 @@ out: + * Search using the basedn, filter, and scope provided from the task data. + * Then loop of each entry, and apply the membership if applicable. + */ +-void automember_rebuild_task_thread(void *arg){ ++void automember_rebuild_task_thread(void *arg) ++{ + Slapi_Task *task = (Slapi_Task *)arg; + struct configEntry *config = NULL; + Slapi_PBlock *search_pb = NULL, *fixup_pb = NULL; +@@ -2124,6 +2137,12 @@ void automember_rebuild_task_thread(void *arg){ + int result = 0; + int i = 0; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error( SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM, ++ "automember_rebuild_task_thread --> refcount incremented.\n" ); + /* + * Fetch our task data from the task + */ +@@ -2192,7 +2211,8 @@ void automember_rebuild_task_thread(void *arg){ + if (slapi_dn_issuffix(slapi_entry_get_dn(entries[i]), config->scope) && + (slapi_filter_test_simple(entries[i], config->filter) == 0)) + { +- if(automember_update_membership(config, entries[i], NULL)){ ++ if (slapi_is_shutting_down() || ++ automember_update_membership(config, entries[i], NULL)) { + result = SLAPI_PLUGIN_FAILURE; + automember_config_unlock(); + goto out; +@@ -2226,6 +2246,9 @@ out: + } + slapi_task_inc_progress(task); + slapi_task_finish(task, result); ++ slapi_task_dec_refcount(task); ++ slapi_log_error( SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM, ++ "automember_rebuild_task_thread <-- refcount decremented.\n" ); + } + + /* +@@ -2328,7 +2351,8 @@ out: + return rv; + } + +-void automember_export_task_thread(void *arg){ ++void automember_export_task_thread(void *arg) ++{ + Slapi_Task *task = (Slapi_Task *)arg; + Slapi_PBlock *search_pb = NULL; + Slapi_Entry **entries = NULL; +@@ -2340,6 +2364,13 @@ void automember_export_task_thread(void *arg){ + int i = 0; + int rc = 0; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error( SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM, ++ "automember_export_task_thread --> refcount incremented.\n" ); ++ + td = (task_data *)slapi_task_get_data(task); + slapi_task_begin(task, 1); + slapi_task_log_notice(task, "Automember export task starting. Exporting changes to (%s)", td->ldif_out); +@@ -2394,7 +2425,8 @@ void automember_export_task_thread(void *arg){ + if (slapi_dn_issuffix(slapi_sdn_get_dn(td->base_dn), config->scope) && + (slapi_filter_test_simple(entries[i], config->filter) == 0)) + { +- if(automember_update_membership(config, entries[i], ldif_fd)){ ++ if (slapi_is_shutting_down() || ++ automember_update_membership(config, entries[i], ldif_fd)) { + result = SLAPI_DSE_CALLBACK_ERROR; + automember_config_unlock(); + goto out; +@@ -2423,6 +2455,9 @@ out: + } + slapi_task_inc_progress(task); + slapi_task_finish(task, result); ++ slapi_task_dec_refcount(task); ++ slapi_log_error( SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM, ++ "automember_export_task_thread <-- refcount decremented.\n" ); + } + + /* +@@ -2507,7 +2542,8 @@ out: + * Read in the text entries from ldif_in, and convert them to slapi_entries. + * Then, write to ldif_out what the updates would be if these entries were added + */ +-void automember_map_task_thread(void *arg){ ++void automember_map_task_thread(void *arg) ++{ + Slapi_Task *task = (Slapi_Task *)arg; + Slapi_Entry *e = NULL; + int result = SLAPI_DSE_CALLBACK_OK; +@@ -2527,6 +2563,12 @@ void automember_map_task_thread(void *arg){ + #endif + int rc = 0; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error( SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM, ++ "automember_map_task_thread --> refcount incremented.\n" ); + td = (task_data *)slapi_task_get_data(task); + slapi_task_begin(task, 1); + slapi_task_log_notice(task, "Automember map task starting... Reading entries from (%s)" +@@ -2586,7 +2628,8 @@ void automember_map_task_thread(void *arg){ + if (slapi_dn_issuffix(slapi_entry_get_dn_const(e), config->scope) && + (slapi_filter_test_simple(e, config->filter) == 0)) + { +- if(automember_update_membership(config, e, ldif_fd_out)){ ++ if (slapi_is_shutting_down() || ++ automember_update_membership(config, e, ldif_fd_out)) { + result = SLAPI_DSE_CALLBACK_ERROR; + slapi_entry_free(e); + slapi_ch_free_string(&entrystr); +@@ -2620,6 +2663,9 @@ out: + } + slapi_task_inc_progress(task); + slapi_task_finish(task, result); ++ slapi_task_dec_refcount(task); ++ slapi_log_error( SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM, ++ "automember_map_task_thread <-- refcount decremented.\n" ); + } + + /* +diff --git a/ldap/servers/plugins/linkedattrs/fixup_task.c b/ldap/servers/plugins/linkedattrs/fixup_task.c +index db3c693..3a01fed 100644 +--- a/ldap/servers/plugins/linkedattrs/fixup_task.c ++++ b/ldap/servers/plugins/linkedattrs/fixup_task.c +@@ -119,6 +119,10 @@ linked_attrs_fixup_task_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->linkdn); + slapi_ch_free_string(&mydata->bind_dn); +@@ -137,6 +141,12 @@ linked_attrs_fixup_task_thread(void *arg) + int found_config = 0; + int rc = 0; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, ++ "linked_attrs_fixup_task_thread --> refcount incremented.\n" ); + /* Fetch our task data from the task */ + td = (task_data *)slapi_task_get_data(task); + +@@ -154,8 +164,8 @@ linked_attrs_fixup_task_thread(void *arg) + linked_attrs_read_lock(); + main_config = linked_attrs_get_config(); + if (!PR_CLIST_IS_EMPTY(main_config)) { +- struct configEntry *config_entry = NULL; +- PRCList *list = PR_LIST_HEAD(main_config); ++ struct configEntry *config_entry = NULL; ++ PRCList *list = PR_LIST_HEAD(main_config); + + while (list != main_config) { + config_entry = (struct configEntry *) list; +@@ -204,6 +214,10 @@ linked_attrs_fixup_task_thread(void *arg) + + /* this will queue the destruction of the task */ + slapi_task_finish(task, rc); ++ ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, ++ "linked_attrs_fixup_task_thread <-- refcount decremented.\n"); + } + + static void +@@ -269,7 +283,7 @@ linked_attrs_fixup_links(struct configEntry *config) + if(rc == 0){ + slapi_back_transaction_commit(fixup_pb); + } else { +- slapi_back_transaction_abort(fixup_pb); ++ slapi_back_transaction_abort(fixup_pb); + } + slapi_pblock_destroy(fixup_pb); + } +@@ -352,11 +366,20 @@ linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data) + int rc = 0; + Slapi_DN *sdn = slapi_entry_get_sdn(e); + char *type = (char *)callback_data; +- Slapi_PBlock *pb = slapi_pblock_new(); ++ Slapi_PBlock *pb = NULL; + char *val[1]; + LDAPMod mod; + LDAPMod *mods[2]; + ++ /* ++ * If the server is ordered to shutdown, stop the fixup and return an error. ++ */ ++ if (slapi_is_shutting_down()) { ++ rc = -1; ++ goto bail; ++ } ++ ++ pb = slapi_pblock_new(); + /* Remove all values of the passed in type. */ + val[0] = 0; + +@@ -377,7 +400,7 @@ linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data) + slapi_modify_internal_pb(pb); + + slapi_pblock_destroy(pb); +- ++bail: + return rc; + } + +@@ -394,6 +417,13 @@ linked_attrs_add_backlinks_callback(Slapi_Entry *e, void *callback_data) + LDAPMod mod; + LDAPMod *mods[2]; + ++ /* ++ * If the server is ordered to shutdown, stop the fixup and return an error. ++ */ ++ if (slapi_is_shutting_down()) { ++ rc = -1; ++ goto done; ++ } + /* Setup the modify operation. Only the target will + * change, so we only need to do this once. */ + val[0] = linkdn; +diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c +index 7e3e308..14bad98 100644 +--- a/ldap/servers/plugins/memberof/memberof.c ++++ b/ldap/servers/plugins/memberof/memberof.c +@@ -2636,6 +2636,12 @@ void memberof_fixup_task_thread(void *arg) + int rc = 0; + Slapi_PBlock *fixup_pb = NULL; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, ++ "memberof_fixup_task_thread --> refcount incremented.\n" ); + /* Fetch our task data from the task */ + td = (task_data *)slapi_task_get_data(task); + +@@ -2702,6 +2708,9 @@ void memberof_fixup_task_thread(void *arg) + + /* this will queue the destruction of the task */ + slapi_task_finish(task, rc); ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, ++ "memberof_fixup_task_thread <-- refcount decremented.\n"); + } + + /* extract a single value from the entry (as a string) -- if it's not in the +@@ -2793,8 +2802,14 @@ out: + void + memberof_task_destructor(Slapi_Task *task) + { ++ slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, ++ "memberof_task_destructor -->\n" ); + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->dn); + slapi_ch_free_string(&mydata->bind_dn); +@@ -2803,6 +2818,8 @@ memberof_task_destructor(Slapi_Task *task) + slapi_ch_free((void **)&mydata); + } + } ++ slapi_log_error( SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, ++ "memberof_task_destructor <--\n" ); + } + + int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str) +@@ -2841,6 +2858,13 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data) + memberof_del_dn_data del_data = {0, config->memberof_attr}; + Slapi_ValueSet *groups = 0; + ++ /* ++ * If the server is ordered to shutdown, stop the fixup and return an error. ++ */ ++ if (slapi_is_shutting_down()) { ++ rc = -1; ++ goto bail; ++ } + /* get a list of all of the groups this user belongs to */ + groups = memberof_get_groups(config, sdn); + +@@ -2889,6 +2913,6 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data) + } + + slapi_valueset_free(groups); +- ++bail: + return rc; + } +diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h +index 6d56081..59029d7 100644 +--- a/ldap/servers/plugins/memberof/memberof.h ++++ b/ldap/servers/plugins/memberof/memberof.h +@@ -81,7 +81,7 @@ typedef struct memberofconfig { + char *memberof_attr; + int allBackends; + Slapi_DN *entryScope; +- Slapi_DN *entryScopeExcludeSubtree; ++ Slapi_DN *entryScopeExcludeSubtree; + Slapi_Filter *group_filter; + Slapi_Attr **group_slapiattrs; + } MemberOfConfig; +diff --git a/ldap/servers/plugins/posix-winsync/posix-group-task.c b/ldap/servers/plugins/posix-winsync/posix-group-task.c +index c5ea729..c76545a 100644 +--- a/ldap/servers/plugins/posix-winsync/posix-group-task.c ++++ b/ldap/servers/plugins/posix-winsync/posix-group-task.c +@@ -165,6 +165,10 @@ posix_group_task_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *) slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->dn); + slapi_ch_free_string(&mydata->filter_str); +@@ -172,6 +176,8 @@ posix_group_task_destructor(Slapi_Task *task) + slapi_ch_free((void **) &mydata); + } + } ++ slapi_log_error(SLAPI_LOG_PLUGIN, POSIX_WINSYNC_PLUGIN_NAME, ++ "posix_group_task_destructor <--\n"); + } + + #if 0 /* NOT USED */ +@@ -245,17 +251,28 @@ posix_group_fix_memberuid_callback(Slapi_Entry *e, void *callback_data) + "_fix_memberuid ==>\n"); + cb_data *the_cb_data = (cb_data *) callback_data; + +- int rc; ++ int rc = 0; + Slapi_Attr *muid_attr = NULL; + Slapi_Value *v = NULL; + +- Slapi_Mods *smods = slapi_mods_new(); +- +- char *dn = slapi_entry_get_dn(e); +- Slapi_DN *sdn = slapi_entry_get_sdn(e); ++ Slapi_Mods *smods = NULL; ++ char *dn = NULL; ++ Slapi_DN *sdn = NULL; + LDAPMod **mods = NULL; + int is_posix_group = 0; + ++ /* ++ * If the server is ordered to shutdown, stop the fixup and return an error. ++ */ ++ if (slapi_is_shutting_down()) { ++ rc = -1; ++ goto bail; ++ } ++ ++ smods = slapi_mods_new(); ++ dn = slapi_entry_get_dn(e); ++ sdn = slapi_entry_get_sdn(e); ++ + if (hasObjectClass(e, "posixGroup")) { + is_posix_group = 1; + } +@@ -441,7 +458,7 @@ posix_group_fix_memberuid_callback(Slapi_Entry *e, void *callback_data) + slapi_pblock_destroy(mod_pb); + } + slapi_mods_free(&smods); +- ++bail: + slapi_log_error(SLAPI_LOG_PLUGIN, POSIX_WINSYNC_PLUGIN_NAME, + "_fix_memberuid <==\n"); + /* +@@ -450,7 +467,7 @@ posix_group_fix_memberuid_callback(Slapi_Entry *e, void *callback_data) + * uniqueMember attribute. But "not found" error shoud not + * be returned, which stops the further fixup task. + */ +- return 0; ++ return rc; + } + + static void +@@ -463,6 +480,12 @@ posix_group_fixup_task_thread(void *arg) + task_data *td = NULL; + int rc = 0; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, POSIX_WINSYNC_PLUGIN_NAME, ++ "posix_group_fixup_task_thread --> refcount incremented.\n" ); + /* Fetch our task data from the task */ + td = (task_data *) slapi_task_get_data(task); + +@@ -491,4 +514,7 @@ posix_group_fixup_task_thread(void *arg) + + slapi_log_error(SLAPI_LOG_PLUGIN, POSIX_WINSYNC_PLUGIN_NAME, + "_task_thread <==\n"); ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, POSIX_WINSYNC_PLUGIN_NAME, ++ "posix_group_fixup_task_thread <-- refcount decremented.\n"); + } +diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c +index 3bc3916..1570ba7 100644 +--- a/ldap/servers/plugins/replication/repl5_replica_config.c ++++ b/ldap/servers/plugins/replication/repl5_replica_config.c +@@ -109,6 +109,8 @@ static CSN* replica_cleanallruv_find_maxcsn(Replica *replica, ReplicaId rid, cha + static int replica_cleanallruv_get_replica_maxcsn(Repl_Agmt *agmt, char *rid_text, char *basedn, CSN **csn); + static void preset_cleaned_rid(ReplicaId rid); + static multimaster_mtnode_extension * _replica_config_get_mtnode_ext (const Slapi_Entry *e); ++static void replica_cleanall_ruv_destructor(Slapi_Task *task); ++static void replica_cleanall_ruv_abort_destructor(Slapi_Task *task); + + /* + * Note: internal add/modify/delete operations should not be run while +@@ -1509,6 +1511,10 @@ replica_cleanall_ruv_task(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, + rc = SLAPI_DSE_CALLBACK_ERROR; + goto out; + } ++ ++ /* register our destructor for waiting the task is done */ ++ slapi_task_set_destructor_fn(task, replica_cleanall_ruv_destructor); ++ + /* + * Get our task settings + */ +@@ -1752,6 +1758,13 @@ replica_cleanallruv_thread(void *arg) + int aborted = 0; + int rc = 0; + ++ if (!data) { ++ return; /* no data */ ++ } ++ if (data->task) { ++ slapi_task_inc_refcount(data->task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "replica_cleanallruv_thread --> refcount incremented.\n"); ++ } + /* + * Initialize our settings + */ +@@ -1974,6 +1987,8 @@ done: + } + if(data->task){ + slapi_task_finish(data->task, rc); ++ slapi_task_dec_refcount(data->task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "replica_cleanallruv_thread <-- refcount decremented.\n"); + } + if(data->payload){ + ber_bvfree(data->payload); +@@ -1989,6 +2004,36 @@ done: + slapi_ch_free((void **)&data); + } + ++static void ++replica_cleanall_ruv_destructor(Slapi_Task *task) ++{ ++ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, ++ "replica_cleanall_ruv_destructor -->\n" ); ++ if (task) { ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } ++ } ++ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, ++ "replica_cleanall_ruv_destructor <--\n" ); ++} ++ ++static void ++replica_cleanall_ruv_abort_destructor(Slapi_Task *task) ++{ ++ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, ++ "replica_cleanall_ruv_abort_destructor -->\n" ); ++ if (task) { ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } ++ } ++ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, ++ "replica_cleanall_ruv_abort_destructor <--\n" ); ++} ++ + /* + * Loop over the agmts, and check if they are in the last phase of cleaning, meaning they have + * released cleanallruv data from the config +@@ -2775,6 +2820,10 @@ replica_cleanall_ruv_abort(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter + + /* allocate new task now */ + task = slapi_new_task(slapi_entry_get_ndn(e)); ++ ++ /* register our destructor for waiting the task is done */ ++ slapi_task_set_destructor_fn(task, replica_cleanall_ruv_abort_destructor); ++ + /* + * Get our task settings + */ +@@ -2921,6 +2970,13 @@ replica_abort_task_thread(void *arg) + int release_it = 0; + int count = 0, rc = 0; + ++ if (!data) { ++ return; /* no data */ ++ } ++ if (data->task) { ++ slapi_task_inc_refcount(data->task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "replica_abort_task_thread --> refcount incremented.\n"); ++ } + cleanruv_log(data->task, ABORT_CLEANALLRUV_ID, "Aborting task for rid(%d)...",data->rid); + + /* +@@ -3028,6 +3084,8 @@ done: + + if(data->task){ + slapi_task_finish(data->task, agmt_not_notified); ++ slapi_task_dec_refcount(data->task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "replica_abort_task_thread <-- refcount incremented.\n"); + } + if(data->repl_obj && release_it) + object_release(data->repl_obj); +diff --git a/ldap/servers/plugins/schema_reload/schema_reload.c b/ldap/servers/plugins/schema_reload/schema_reload.c +index b1a5bb8..25336fe 100644 +--- a/ldap/servers/plugins/schema_reload/schema_reload.c ++++ b/ldap/servers/plugins/schema_reload/schema_reload.c +@@ -86,6 +86,7 @@ static int schemareload_add(Slapi_PBlock *pb, Slapi_Entry *e, + void *arg); + static int schemareload_start(Slapi_PBlock *pb); + static int schemareload_close(Slapi_PBlock *pb); ++static void schemareload_destructor(Slapi_Task *task); + + /* + * Init function +@@ -159,6 +160,12 @@ schemareload_thread(void *arg) + int total_work = 2; + task_data *td = NULL; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, "schemareload", ++ "schemareload_thread --> refcount incremented.\n" ); + /* Fetch our task data from the task */ + td = (task_data *)slapi_task_get_data(task); + +@@ -174,7 +181,11 @@ schemareload_thread(void *arg) + rv = slapi_validate_schema_files(td->schemadir); + slapi_task_inc_progress(task); + +- if (LDAP_SUCCESS == rv) { ++ if (slapi_is_shutting_down()) { ++ slapi_task_log_notice(task, "Server is shuttoing down; Schema validation aborted."); ++ slapi_task_log_status(task, "Server is shuttoing down; Schema validation aborted."); ++ slapi_log_error(SLAPI_LOG_FATAL, "schemareload", "Server is shuttoing down; Schema validation aborted."); ++ } else if (LDAP_SUCCESS == rv) { + slapi_task_log_notice(task, "Schema validation passed."); + slapi_task_log_status(task, "Schema validation passed."); + slapi_log_error(SLAPI_LOG_FATAL, "schemareload", "Schema validation passed.\n"); +@@ -199,16 +210,18 @@ schemareload_thread(void *arg) + slapi_task_log_status(task, "Schema reload task failed."); + slapi_log_error(SLAPI_LOG_FATAL, "schemareload", "Schema reload task failed.\n"); + } +- PR_Unlock(schemareload_lock); + } else { + slapi_task_log_notice(task, "Schema validation failed."); + slapi_task_log_status(task, "Schema validation failed."); + slapi_log_error(SLAPI_LOG_FATAL, "schemareload", "Schema validation failed.\n"); +- PR_Unlock(schemareload_lock); + } ++ PR_Unlock(schemareload_lock); + + /* this will queue the destruction of the task */ + slapi_task_finish(task, rv); ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, "schemareload", ++ "schemareload_thread <-- refcount decremented.\n"); + } + + /* extract a single value from the entry (as a string) -- if it's not in the +@@ -233,6 +246,10 @@ schemareload_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->schemadir); + slapi_ch_free_string(&mydata->bind_dn); +diff --git a/ldap/servers/plugins/syntaxes/validate_task.c b/ldap/servers/plugins/syntaxes/validate_task.c +index 99f6309..71b4b7e 100644 +--- a/ldap/servers/plugins/syntaxes/validate_task.c ++++ b/ldap/servers/plugins/syntaxes/validate_task.c +@@ -179,6 +179,10 @@ syntax_validate_task_destructor(Slapi_Task *task) + { + if (task) { + task_data *mydata = (task_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } + if (mydata) { + slapi_ch_free_string(&mydata->dn); + slapi_ch_free_string(&mydata->filter_str); +@@ -197,6 +201,12 @@ syntax_validate_task_thread(void *arg) + task_data *td = NULL; + Slapi_PBlock *search_pb = slapi_pblock_new(); + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, SYNTAX_PLUGIN_SUBSYSTEM, ++ "syntax_validate_task_thread --> refcount incremented.\n" ); + /* Fetch our task data from the task */ + td = (task_data *)slapi_task_get_data(task); + +@@ -231,16 +241,26 @@ syntax_validate_task_thread(void *arg) + + /* this will queue the destruction of the task */ + slapi_task_finish(task, rc); ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, SYNTAX_PLUGIN_SUBSYSTEM, ++ "syntax_validate_task_thread <-- refcount decremented.\n"); + } + + static int + syntax_validate_task_callback(Slapi_Entry *e, void *callback_data) + { +- int rc = 0; +- char *dn = slapi_entry_get_dn(e); ++ int rc = 0; ++ char *dn = slapi_entry_get_dn(e); + task_data *td = (task_data *)callback_data; + Slapi_PBlock *pb = NULL; + ++ /* ++ * If the server is ordered to shutdown, stop the fixup and return an error. ++ */ ++ if (slapi_is_shutting_down()) { ++ rc = -1; ++ goto bail; ++ } + /* Override the syntax checking config to force syntax checking. */ + if (slapi_entry_syntax_check(NULL, e, 1) != 0) { + char *error_text = NULL; +@@ -261,7 +281,7 @@ syntax_validate_task_callback(Slapi_Entry *e, void *callback_data) + /* Keep a tally of the number of invalid entries found. */ + slapi_counter_increment(td->invalid_entries); + } +- ++bail: + return rc; + } + +diff --git a/ldap/servers/plugins/usn/usn_cleanup.c b/ldap/servers/plugins/usn/usn_cleanup.c +index c12dfd2..dd07b4c 100644 +--- a/ldap/servers/plugins/usn/usn_cleanup.c ++++ b/ldap/servers/plugins/usn/usn_cleanup.c +@@ -49,6 +49,8 @@ struct usn_cleanup_data { + + static int usn_cleanup_add(Slapi_PBlock *pb, Slapi_Entry *e, + Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg); ++static void usn_cleanup_task_destructor(Slapi_Task *task); ++ + + int + usn_cleanup_start(Slapi_PBlock *pb) +@@ -83,8 +85,14 @@ usn_cleanup_thread(void *arg) + Slapi_PBlock *delete_pb = NULL; + char *filter = "objectclass=nsTombstone"; + ++ if (!task) { ++ return; /* no task */ ++ } + slapi_log_error(SLAPI_LOG_TRACE, USN_PLUGIN_SUBSYSTEM, + "--> usn_cleanup_thread\n"); ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, USN_PLUGIN_SUBSYSTEM, ++ "usn_cleanup_thread --> refcount incremented.\n" ); + + if (NULL == usn_get_identity()) { /* plugin is not initialized */ + slapi_task_log_notice(task, "USN plugin is not initialized\n"); +@@ -195,14 +203,12 @@ bail: + if (cleanup_data->maxusn_to_delete) { + slapi_ch_free_string(&filter); + } +- slapi_ch_free_string(&cleanup_data->maxusn_to_delete); +- slapi_ch_free_string(&cleanup_data->suffix); +- slapi_ch_free_string(&cleanup_data->bind_dn); +- slapi_ch_free((void **)&cleanup_data); + + /* this will queue the destruction of the task */ + slapi_task_finish(task, rv); +- ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, USN_PLUGIN_SUBSYSTEM, ++ "usn_cleanup_thread <-- refcount decremented.\n"); + slapi_log_error(SLAPI_LOG_TRACE, USN_PLUGIN_SUBSYSTEM, + "<-- usn_cleanup_thread\n"); + } +@@ -283,7 +289,7 @@ usn_cleanup_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, + backend = slapi_entry_attr_get_charptr(e, "backend"); + maxusn = slapi_entry_attr_get_charptr(e, "maxusn_to_delete"); + +- if (NULL == suffix && NULL == backend) { ++ if (!suffix && !backend) { + slapi_log_error(SLAPI_LOG_FATAL, USN_PLUGIN_SUBSYSTEM, + "USN tombstone cleanup: Both suffix and backend are missing.\n"); + *returncode = LDAP_PARAM_ERROR; +@@ -292,7 +298,7 @@ usn_cleanup_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, + } + + /* suffix is not given, but backend is; get the suffix */ +- if (NULL == suffix && NULL != backend) { ++ if (!suffix && backend) { + be = slapi_be_select_by_instance_name(backend); + be_suffix = slapi_be_getsuffix(be, 0); + if (be_suffix) { +@@ -317,12 +323,6 @@ usn_cleanup_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, + goto bail; + } + +- cleanup_data = +- (struct usn_cleanup_data *)slapi_ch_malloc(sizeof(struct usn_cleanup_data)); +- cleanup_data->suffix = slapi_ch_strdup(suffix); +- cleanup_data->maxusn_to_delete = slapi_ch_strdup(maxusn); +- cleanup_data->bind_dn = slapi_ch_strdup(bind_dn); +- + /* allocate new task now */ + task = slapi_plugin_new_task(slapi_entry_get_ndn(e), arg); + if (task == NULL) { +@@ -330,11 +330,21 @@ usn_cleanup_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, + "USN tombstone cleanup: unable to allocate new task.\n"); + *returncode = LDAP_OPERATIONS_ERROR; + rv = SLAPI_DSE_CALLBACK_ERROR; +- slapi_ch_free((void**)&cleanup_data); + goto bail; + } + ++ /* register our destructor for cleaning up our private data */ ++ slapi_task_set_destructor_fn(task, usn_cleanup_task_destructor); ++ + /* Stash our argument in the task for use by the task thread */ ++ cleanup_data = ++ (struct usn_cleanup_data *)slapi_ch_malloc(sizeof(struct usn_cleanup_data)); ++ cleanup_data->suffix = suffix; ++ suffix = NULL; /* don't free in this function */ ++ cleanup_data->maxusn_to_delete = maxusn; ++ maxusn = NULL; /* don't free in this function */ ++ cleanup_data->bind_dn = bind_dn; ++ bind_dn = NULL; /* don't free in this function */ + slapi_task_set_data(task, cleanup_data); + + /* start the USN tombstone cleanup task as a separate thread */ +@@ -361,3 +371,23 @@ bail: + return rv; + } + ++static void ++usn_cleanup_task_destructor(Slapi_Task *task) ++{ ++ slapi_log_error(SLAPI_LOG_PLUGIN, USN_PLUGIN_SUBSYSTEM, "usn_cleanup_task_destructor -->\n"); ++ if (task) { ++ struct usn_cleanup_data *mydata = (struct usn_cleanup_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } ++ if (mydata) { ++ slapi_ch_free_string(&mydata->suffix); ++ slapi_ch_free_string(&mydata->maxusn_to_delete); ++ slapi_ch_free_string(&mydata->bind_dn); ++ /* Need to cast to avoid a compiler warning */ ++ slapi_ch_free((void **)&mydata); ++ } ++ } ++ slapi_log_error(SLAPI_LOG_PLUGIN, USN_PLUGIN_SUBSYSTEM, "usn_cleanup_task_destructor <--\n"); ++} +diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h +index 0ae3601..dfe75eb 100644 +--- a/ldap/servers/slapd/slapi-plugin.h ++++ b/ldap/servers/slapd/slapi-plugin.h +@@ -7958,6 +7958,15 @@ void slapi_plugin_op_finished(void *arg); + #define RDN_IS_CONFLICT 0x2 + int slapi_is_special_rdn(const char *rdn, int flag); + ++/** ++ * Sleeps for PRIntervalTime ticks defined in NSPR library ++ * ++ * \param PRIntervalTime ticks ++ * ++ * \return Nothing ++ */ ++void DS_Sleep(PRIntervalTime ticks); ++ + #ifdef __cplusplus + } + #endif +diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h +index 921c397..9ca1950 100644 +--- a/ldap/servers/slapd/slapi-private.h ++++ b/ldap/servers/slapd/slapi-private.h +@@ -1245,8 +1245,6 @@ void bervalarray_add_berval_fast(struct berval ***vals, const struct berval *add + + /***** End of items added for the replication plugin. ***********************/ + +-void DS_Sleep(PRIntervalTime ticks); +- + /* macro to specify the behavior of upgradedb & upgradednformat */ + #define SLAPI_UPGRADEDB_FORCE 0x1 /* reindex all (no check w/ idl switch) */ + #define SLAPI_UPGRADEDB_SKIPINIT 0x2 /* call upgradedb as part of other op */ +diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c +index 98ec88c..19a52a3 100644 +--- a/ldap/servers/slapd/task.c ++++ b/ldap/servers/slapd/task.c +@@ -113,6 +113,8 @@ static const char *fetch_attr(Slapi_Entry *e, const char *attrname, + static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn); + static void modify_internal_entry(char *dn, LDAPMod **mods); + ++static void fixup_tombstone_task_destructor(Slapi_Task *task); ++ + /*********************************** + * Public Functions + ***********************************/ +@@ -2218,6 +2220,12 @@ task_fixup_tombstone_thread(void *arg) + int fixup_count = 0; + int rc, i, j; + ++ if (!task) { ++ return; /* no task */ ++ } ++ slapi_task_inc_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, TASK_TOMBSTONE_FIXUP, ++ "fixup_tombstone_task_thread --> refcount incremented.\n" ); + slapi_task_begin(task, 1); + slapi_task_log_notice(task, "Beginning tombstone fixup task...\n"); + slapi_log_error(SLAPI_LOG_REPL, TASK_TOMBSTONE_FIXUP, +@@ -2233,8 +2241,14 @@ task_fixup_tombstone_thread(void *arg) + + /* Okay check the specified backends only */ + for(i = 0; base && base[i]; i++){ +- Slapi_PBlock *search_pb = slapi_pblock_new(); ++ Slapi_PBlock *search_pb = NULL; ++ ++ if (slapi_is_shutting_down()) { ++ rc = -1; ++ goto bail; ++ } + ++ search_pb = slapi_pblock_new(); + /* find entries that need fixing... */ + slapi_search_internal_set_pb(search_pb, base[i], LDAP_SCOPE_SUBTREE, + filter, NULL, 0, NULL, NULL, plugin_get_default_component_id(), 0); +@@ -2247,8 +2261,7 @@ task_fixup_tombstone_thread(void *arg) + slapi_log_error(SLAPI_LOG_REPL, TASK_TOMBSTONE_FIXUP, + "Failed to search backend for tombstones, error %d\n", rc); + slapi_pblock_destroy(search_pb); +- slapi_task_finish(task, rc); +- return; ++ goto bail; + } + + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); +@@ -2281,9 +2294,11 @@ task_fixup_tombstone_thread(void *arg) + slapi_log_error(SLAPI_LOG_REPL, TASK_TOMBSTONE_FIXUP, "%s %d tombstones.\n", + task_data->stripcsn ? "Stripped" : "Fixed", fixup_count); + slapi_task_inc_progress(task); ++bail: + slapi_task_finish(task, rc); +- slapi_ch_array_free(base); +- slapi_ch_free((void **)&task_data); ++ slapi_task_dec_refcount(task); ++ slapi_log_error(SLAPI_LOG_PLUGIN, TASK_TOMBSTONE_FIXUP, ++ "fixup_tombstone_task_thread <-- refcount decremented.\n" ); + } + + +@@ -2387,6 +2402,8 @@ task_fixup_tombstones_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, + } + + task = slapi_new_task(slapi_entry_get_ndn(e)); ++ /* register our destructor for cleaning up our private data */ ++ slapi_task_set_destructor_fn(task, fixup_tombstone_task_destructor); + task_data = (struct task_tombstone_data *)slapi_ch_calloc(1, sizeof(struct task_tombstone_data)); + task_data->base = base; + task_data->task = task; +@@ -2422,6 +2439,26 @@ done: + return SLAPI_DSE_CALLBACK_OK; + } + ++static void ++fixup_tombstone_task_destructor(Slapi_Task *task) ++{ ++ slapi_log_error(SLAPI_LOG_PLUGIN, TASK_TOMBSTONE_FIXUP, ++ "fixup_tombstone_task_destructor -->\n" ); ++ if (task) { ++ struct task_tombstone_data *mydata = (struct task_tombstone_data *)slapi_task_get_data(task); ++ while (slapi_task_get_refcount(task) > 0) { ++ /* Yield to wait for the fixup task finishes. */ ++ DS_Sleep (PR_MillisecondsToInterval(100)); ++ } ++ if (mydata) { ++ slapi_ch_array_free(mydata->base); ++ slapi_ch_free((void **)&mydata); ++ } ++ } ++ slapi_log_error(SLAPI_LOG_PLUGIN, TASK_TOMBSTONE_FIXUP, ++ "fixup_tombstone_task_destructor <--\n" ); ++} ++ + /* cleanup old tasks that may still be in the DSE from a previous session + * (this can happen if the server crashes [no matter how unlikely we like + * to think that is].) +-- +1.9.3 + diff --git a/SOURCES/0062-CVE-2015-1854-389ds-base-access-control-bypass-with-.patch b/SOURCES/0062-CVE-2015-1854-389ds-base-access-control-bypass-with-.patch new file mode 100644 index 0000000..903a69d --- /dev/null +++ b/SOURCES/0062-CVE-2015-1854-389ds-base-access-control-bypass-with-.patch @@ -0,0 +1,347 @@ +From 205bce153c7db8258a8a28498cf54e7374dca588 Mon Sep 17 00:00:00 2001 +From: Thierry Bordaz +Date: Tue, 14 Apr 2015 16:24:44 +0200 +Subject: [PATCH] CVE-2015-1854 389ds-base: access control bypass with modrdn + +Bug Description: + 47553 fix checks the write right access only if the RDN is + modified. This allows to rename entries even if the + authenticated user is not allowed of that. + +Fix Description: + Roll back a wrong optimization that tested the write access + only if RDN value was changed. + +https://fedorahosted.org/389/ticket/47553 + +Reviewed by: ? + +Platforms tested: F17 (upstream test) + +Flag Day: no + +Doc impact: no + +(cherry picked from commit 44e5c0998bdf7dcb167e8472713ff393b776e4e3) + +Conflicts: + dirsrvtests/tickets/ticket47553_single_aci_test.py +--- + dirsrvtests/tickets/ticket47553_rdn_write_test.py | 132 +++++++++++++++++++++ + dirsrvtests/tickets/ticket47553_single_aci_test.py | 52 ++++++-- + ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 32 ++--- + 3 files changed, 184 insertions(+), 32 deletions(-) + create mode 100644 dirsrvtests/tickets/ticket47553_rdn_write_test.py + +diff --git a/dirsrvtests/tickets/ticket47553_rdn_write_test.py b/dirsrvtests/tickets/ticket47553_rdn_write_test.py +new file mode 100644 +index 0000000..f15d9b3 +--- /dev/null ++++ b/dirsrvtests/tickets/ticket47553_rdn_write_test.py +@@ -0,0 +1,132 @@ ++import os ++import sys ++import time ++import ldap ++import logging ++import pytest ++from lib389 import DirSrv, Entry, tools, tasks ++from lib389.tools import DirSrvTools ++from lib389._constants import * ++from lib389.properties import * ++from lib389.tasks import * ++from lib389.utils import * ++from ldap.controls.simple import GetEffectiveRightsControl ++ ++logging.getLogger(__name__).setLevel(logging.DEBUG) ++log = logging.getLogger(__name__) ++ ++installation1_prefix = None ++ ++SRC_ENTRY_CN = "tuser" ++EXT_RDN = "01" ++DST_ENTRY_CN = SRC_ENTRY_CN + EXT_RDN ++ ++SRC_ENTRY_DN = "cn=%s,%s" % (SRC_ENTRY_CN, SUFFIX) ++DST_ENTRY_DN = "cn=%s,%s" % (DST_ENTRY_CN, SUFFIX) ++ ++class TopologyStandalone(object): ++ def __init__(self, standalone): ++ standalone.open() ++ self.standalone = standalone ++ ++ ++#@pytest.fixture(scope="module") ++def topology(request): ++ global installation1_prefix ++ ++ # Creating standalone instance ... ++ standalone = DirSrv(verbose=False) ++ if installation1_prefix: ++ args_instance[SER_DEPLOYED_DIR] = installation1_prefix ++ args_instance[SER_HOST] = HOST_STANDALONE ++ args_instance[SER_PORT] = PORT_STANDALONE ++ args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE ++ args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX ++ args_standalone = args_instance.copy() ++ standalone.allocate(args_standalone) ++ instance_standalone = standalone.exists() ++ if instance_standalone: ++ standalone.delete() ++ standalone.create() ++ standalone.open() ++ ++ # Clear out the tmp dir ++ standalone.clearTmpDir(__file__) ++ ++ return TopologyStandalone(standalone) ++ ++def test_ticket47553_rdn_write_init(topology): ++ topology.standalone.log.info("\n\n######################### Add entry tuser ######################\n") ++ topology.standalone.add_s(Entry((SRC_ENTRY_DN, { ++ 'objectclass': "top person".split(), ++ 'sn': SRC_ENTRY_CN, ++ 'cn': SRC_ENTRY_CN}))) ++ ++def test_ticket47553_rdn_write_get_ger(topology): ++ ANONYMOUS_DN = "" ++ topology.standalone.log.info("\n\n######################### GER rights for anonymous ######################\n") ++ request_ctrl = GetEffectiveRightsControl(criticality=True, authzId="dn:" + ANONYMOUS_DN) ++ msg_id = topology.standalone.search_ext(SUFFIX, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl]) ++ rtype, rdata, rmsgid, response_ctrl = topology.standalone.result3(msg_id) ++ value = '' ++ for dn, attrs in rdata: ++ topology.standalone.log.info("dn: %s" % dn) ++ for value in attrs['entryLevelRights']: ++ topology.standalone.log.info("############### entryLevelRights: %r" % value) ++ assert 'n' not in value ++ ++def test_ticket47553_rdn_write_modrdn_anonymous(topology): ++ ANONYMOUS_DN = "" ++ topology.standalone.close() ++ topology.standalone.binddn = ANONYMOUS_DN ++ topology.standalone.open() ++ msg_id = topology.standalone.search_ext("", ldap.SCOPE_BASE, "objectclass=*") ++ rtype, rdata, rmsgid, response_ctrl = topology.standalone.result3(msg_id) ++ value = '' ++ for dn, attrs in rdata: ++ topology.standalone.log.info("dn: %s" % dn) ++ for attr in attrs: ++ topology.standalone.log.info("############### %r: %r" % (attr, attrs[attr])) ++ ++ ++ try: ++ topology.standalone.rename_s(SRC_ENTRY_DN, "cn=%s" % DST_ENTRY_CN, delold=True) ++ except Exception as e: ++ topology.standalone.log.info("Exception (expected): %s" % type(e).__name__) ++ isinstance(e, ldap.INSUFFICIENT_ACCESS) ++ ++ try: ++ topology.standalone.getEntry(DST_ENTRY_DN, ldap.SCOPE_BASE, "objectclass=*") ++ assert False ++ except Exception as e: ++ topology.standalone.log.info("The entry was not renamed (expected)") ++ isinstance(e, ldap.NO_SUCH_OBJECT) ++ ++def test_ticket47553_rdn_write(topology): ++ ''' ++ Write your testcase here... ++ ''' ++ ++ log.info('Test complete') ++ ++ ++def test_ticket47553_rdn_write_final(topology): ++ topology.standalone.delete() ++ log.info('Testcase PASSED') ++ ++ ++def run_isolated(): ++ global installation1_prefix ++ installation1_prefix = '/home/tbordaz/install_master' ++ ++ topo = topology(True) ++ test_ticket47553_rdn_write_init(topo) ++ test_ticket47553_rdn_write_get_ger(topo) ++ test_ticket47553_rdn_write(topo) ++ test_ticket47553_rdn_write_modrdn_anonymous(topo) ++ test_ticket47553_rdn_write_final(topo) ++ ++ ++if __name__ == '__main__': ++ run_isolated() ++ +diff --git a/dirsrvtests/tickets/ticket47553_single_aci_test.py b/dirsrvtests/tickets/ticket47553_single_aci_test.py +index 4be2470..0c8d7e9 100644 +--- a/dirsrvtests/tickets/ticket47553_single_aci_test.py ++++ b/dirsrvtests/tickets/ticket47553_single_aci_test.py +@@ -276,7 +276,27 @@ def _moddn_aci_deny_tree(topology, mod_type=None, target_from=STAGING_DN, target + #topology.master1.modify_s(SUFFIX, mod) + topology.master1.log.info("Add a DENY aci under %s " % PROD_EXCEPT_DN) + topology.master1.modify_s(PROD_EXCEPT_DN, mod) +- ++ ++def _write_aci_staging(topology, mod_type=None): ++ assert mod_type is not None ++ ++ ACI_TARGET = "(targetattr= \"cn\")(target=\"ldap:///cn=*,%s\")" % STAGING_DN ++ ACI_ALLOW = "(version 3.0; acl \"write staging entries\"; allow (write)" ++ ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN ++ ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT ++ mod = [(mod_type, 'aci', ACI_BODY)] ++ topology.master1.modify_s(SUFFIX, mod) ++ ++def _write_aci_production(topology, mod_type=None): ++ assert mod_type is not None ++ ++ ACI_TARGET = "(targetattr= \"cn\")(target=\"ldap:///cn=*,%s\")" % PRODUCTION_DN ++ ACI_ALLOW = "(version 3.0; acl \"write production entries\"; allow (write)" ++ ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN ++ ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT ++ mod = [(mod_type, 'aci', ACI_BODY)] ++ topology.master1.modify_s(SUFFIX, mod) ++ + def _moddn_aci_staging_to_production(topology, mod_type=None, target_from=STAGING_DN, target_to=PRODUCTION_DN): + assert mod_type != None + +@@ -293,6 +313,8 @@ def _moddn_aci_staging_to_production(topology, mod_type=None, target_from=STAGIN + ACI_BODY = ACI_TARGET_FROM + ACI_TARGET_TO + ACI_ALLOW + ACI_SUBJECT + mod = [(mod_type, 'aci', ACI_BODY)] + topology.master1.modify_s(SUFFIX, mod) ++ ++ _write_aci_staging(topology, mod_type=mod_type) + + def _moddn_aci_from_production_to_staging(topology, mod_type=None): + assert mod_type != None +@@ -303,6 +325,8 @@ def _moddn_aci_from_production_to_staging(topology, mod_type=None): + ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT + mod = [(mod_type, 'aci', ACI_BODY)] + topology.master1.modify_s(SUFFIX, mod) ++ ++ _write_aci_production(topology, mod_type=mod_type) + + + def test_ticket47553_init(topology): +@@ -347,12 +371,9 @@ def test_ticket47553_init(topology): + 'description': "production except DIT"}))) + + # enable acl error logging +- #mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')] +- #topology.master1.modify_s(DN_CONFIG, mod) +- #topology.master2.modify_s(DN_CONFIG, mod) +- +- +- ++ mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', str(128+262144))] ++ topology.master1.modify_s(DN_CONFIG, mod) ++ topology.master2.modify_s(DN_CONFIG, mod) + + + # add dummy entries in the staging DIT +@@ -883,6 +904,7 @@ def test_ticket47553_moddn_staging_prod_9(topology): + _bind_manager(topology) + mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) ++ _write_aci_staging(topology, mod_type=ldap.MOD_ADD) + _bind_normal(topology) + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) +@@ -891,6 +913,7 @@ def test_ticket47553_moddn_staging_prod_9(topology): + _bind_manager(topology) + mod = [(ldap.MOD_DELETE, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) ++ _write_aci_staging(topology, mod_type=ldap.MOD_DELETE) + _bind_normal(topology) + + +@@ -934,6 +957,7 @@ def test_ticket47553_moddn_staging_prod_9(topology): + _bind_manager(topology) + mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) ++ _write_aci_staging(topology, mod_type=ldap.MOD_ADD) + _bind_normal(topology) + + try: +@@ -949,6 +973,7 @@ def test_ticket47553_moddn_staging_prod_9(topology): + _bind_manager(topology) + mod = [(ldap.MOD_DELETE, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) ++ _write_aci_staging(topology, mod_type=ldap.MOD_DELETE) + _bind_normal(topology) + + # Add the moddn aci that will be evaluated because of the config flag +@@ -1009,7 +1034,12 @@ def test_ticket47553_moddn_prod_staging(topology): + old_dn = "%s,%s" % (old_rdn, PRODUCTION_DN) + new_rdn = old_rdn + new_superior = STAGING_DN +- ++ ++ # add the write right because we want to check the moddn ++ _bind_manager(topology) ++ _write_aci_production(topology, mod_type=ldap.MOD_ADD) ++ _bind_normal(topology) ++ + try: + topology.master1.log.info("Try to move back MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) +@@ -1019,7 +1049,11 @@ def test_ticket47553_moddn_prod_staging(topology): + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) +- ++ ++ _bind_manager(topology) ++ _write_aci_production(topology, mod_type=ldap.MOD_DELETE) ++ _bind_normal(topology) ++ + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN) +diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +index 6a4982c..4129318 100644 +--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c ++++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +@@ -677,31 +677,17 @@ ldbm_back_modrdn( Slapi_PBlock *pb ) + + /* JCMACL - Should be performed before the child check. */ + /* JCMACL - Why is the check performed against the copy, rather than the existing entry? */ ++ /* This check must be performed even if the entry is renamed with its own name ++ * No optimization here we need to check we have the write access to the target entry ++ */ ++ ldap_result_code = plugin_call_acl_plugin(pb, ec->ep_entry, ++ NULL /*attr*/, NULL /*value*/, SLAPI_ACL_WRITE, ++ ACLPLUGIN_ACCESS_MODRDN, &errbuf); ++ if (ldap_result_code != LDAP_SUCCESS) + { +- Slapi_RDN *new_rdn; +- Slapi_RDN *old_rdn; +- +- /* Taken from the entry */ +- old_rdn = slapi_entry_get_srdn(ec->ep_entry); +- +- /* Taken from the request */ +- new_rdn = slapi_rdn_new(); +- slapi_sdn_get_rdn(&dn_newrdn, new_rdn); +- +- /* Only if we change the RDN value, we need the write access to the entry */ +- if (slapi_rdn_compare(old_rdn, new_rdn)) { +- ldap_result_code = plugin_call_acl_plugin(pb, ec->ep_entry, +- NULL /*attr*/, NULL /*value*/, SLAPI_ACL_WRITE, +- ACLPLUGIN_ACCESS_MODRDN, &errbuf); +- } +- +- slapi_rdn_free(&new_rdn); ++ goto error_return; ++ } + +- if (ldap_result_code != LDAP_SUCCESS) { +- goto error_return; +- } +- } +- + /* Set the new dn to the copy of the entry */ + slapi_entry_set_sdn( ec->ep_entry, &dn_newdn ); + if (entryrdn_get_switch()) { /* subtree-rename: on */ +-- +1.9.3 + diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec index b9fcb60..25d46e8 100644 --- a/SPECS/389-ds-base.spec +++ b/SPECS/389-ds-base.spec @@ -25,7 +25,7 @@ Summary: 389 Directory Server (base) Name: 389-ds-base Version: 1.3.3.1 -Release: %{?relprefix}13%{?prerel}%{?dist} +Release: %{?relprefix}16%{?prerel}%{?dist} License: GPLv2 with exceptions URL: http://port389.org/ Group: System Environment/Daemons @@ -175,6 +175,8 @@ Patch57: 0057-Ticket-47989-Windows-Sync-accidentally-cleared-raw_e.patc Patch58: 0058-Ticket-47991-upgrade-script-fails-if-etc-and-var-are.patch Patch59: 0059-Ticket-47988-Schema-learning-mechanism-in-replicatio.patch Patch60: 0060-Ticket-47988-Schema-learning-mechanism-in-replicatio.patch +Patch61: 0061-Ticket-48005-ns-slapd-crash-in-shutdown-phase.patch +Patch62: 0062-CVE-2015-1854-389ds-base-access-control-bypass-with-.patch %description 389 Directory Server is an LDAPv3 compliant server. The base package includes @@ -286,6 +288,8 @@ cp %{SOURCE2} README.devel %patch58 -p1 %patch59 -p1 %patch60 -p1 +%patch61 -p1 +%patch62 -p1 %build %if %{use_openldap} @@ -348,35 +352,44 @@ ninst=0 # number of instances found in total if [ -n "$DEBUGPOSTTRANS" ] ; then output=$DEBUGPOSTTRANS fi -echo looking for services in %{_sysconfdir}/systemd/system/%{groupname}.wants/* > $output 2>&1 || : +echo looking for services in %{_sysconfdir}/systemd/system/%{groupname}.wants/* >> $output 2>&1 || : for service in %{_sysconfdir}/systemd/system/%{groupname}.wants/* ; do if [ ! -f "$service" ] ; then continue ; fi # in case nothing matches inst=`echo $service | sed -e 's,%{_sysconfdir}/systemd/system/%{groupname}.wants/,,'` - echo found instance $inst - getting status > $output 2>&1 || : + echo found instance $inst - getting status >> $output 2>&1 || : if /bin/systemctl -q is-active $inst ; then - echo instance $inst is running > $output 2>&1 || : + echo instance $inst is running >> $output 2>&1 || : instances="$instances $inst" else - echo instance $inst is not running > $output 2>&1 || : + echo instance $inst is not running >> $output 2>&1 || : fi ninst=`expr $ninst + 1` done if [ $ninst -eq 0 ] ; then - echo no instances to upgrade > $output 2>&1 || : + echo no instances to upgrade >> $output 2>&1 || : exit 0 # have no instances to upgrade - just skip the rest fi # shutdown all instances -echo shutting down all instances . . . > $output 2>&1 || : -/bin/systemctl stop %{groupname} > $output 2>&1 || : -echo remove pid files . . . > $output 2>&1 || : +echo shutting down all instances . . . >> $output 2>&1 || : +for inst in $instances ; do + echo stopping instance $inst >> $output 2>&1 || : + /bin/systemctl stop $inst >> $output 2>&1 || : +done +echo remove pid files . . . >> $output 2>&1 || : /bin/rm -f /var/run/%{pkgname}*.pid /var/run/%{pkgname}*.startpid # do the upgrade -echo upgrading instances . . . > $output 2>&1 || : -%{_sbindir}/setup-ds.pl -l $output -u -s General.UpdateMode=offline > $output 2>&1 || : +echo upgrading instances . . . >> $output 2>&1 || : +DEBUGPOSTSETUPOPT=`/usr/bin/echo $DEBUGPOSTSETUP | /usr/bin/sed -e "s/[^d]//g"` +if [ -n "$DEBUGPOSTSETUPOPT" ] ; then + %{_sbindir}/setup-ds.pl -l $output -$DEBUGPOSTSETUPOPT -u -s General.UpdateMode=offline >> $output 2>&1 || : +else + %{_sbindir}/setup-ds.pl -l $output -u -s General.UpdateMode=offline >> $output 2>&1 || : +fi + # restart instances that require it for inst in $instances ; do - echo restarting instance $inst > $output 2>&1 || : - /bin/systemctl start $inst > $output 2>&1 || : + echo restarting instance $inst >> $output 2>&1 || : + /bin/systemctl start $inst >> $output 2>&1 || : done exit 0 @@ -437,6 +450,20 @@ fi %{_libdir}/%{pkgname}/libns-dshttpd.so* %changelog +* Tue Apr 21 2015 Noriko Hosoi - 1.3.3.1-16 +- release 1.3.3.1-16 +- Resolves: bug 1212894 - CVE-2015-1854 389ds-base: access control bypass with modrdn + +* Mon Feb 23 2015 Noriko Hosoi - 1.3.3.1-15 +- release 1.3.3.1-15 +- Setting correct build tag 'rhel-7.1-z-candidate' + +* Mon Feb 23 2015 Noriko Hosoi - 1.3.3.1-14 +- release 1.3.3.1-14 +- Resolves: bug 1189154 - DNS errors after IPA upgrade due to broken ReplSync (DS 48030) + Fixes spec file to make sure all the server instances are stopped before upgrade +- Resolves: bug 1186548 - ns-slapd crash in shutdown phase (DS 48005) + * Sun Jan 25 2015 Noriko Hosoi - 1.3.3.1-13 - release 1.3.3.1-13 - Resolves: bug 1183655 - Fixed Covscan FORWARD_NULL defects (DS 47988)