From 91e8872841e18eb96f2680fba180d636bb0a2a67 Mon Sep 17 00:00:00 2001 From: Noriko Hosoi Date: Fri, 18 Sep 2015 15:19:51 -0700 Subject: [PATCH 61/61] Ticket #48188 - segfault in ns-slapd due to accessing Slapi_DN freed in pre bind plug-in Description: Additional fixes based upon the comments by rmeggins@redhat.com (Thank you, Rich!!). https://fedorahosted.org/389/ticket/48188?replyto=24#comment:24 1. Implemented the case 2) If the plugin changes the SLAPI_BIND_TARGET_SDN *value*, we need to select a different backend. It is possible (but not very useful) for the plugin to change the pointer, but use the same value. 2. Added an api slapi_be_select_exact which returns NULL if there is no matching backend. https://fedorahosted.org/389/ticket/48188 Reviewed by rmeggins@redhat.com (Thank you!) (cherry picked from commit 8212a8913b748cd1f5e986a754c37ef41db8272a) (cherry picked from commit a215c006e0900caaa555def9e047e295844d8652) --- ldap/servers/slapd/bind.c | 47 +++++++++++++++++++++++++++++---------- ldap/servers/slapd/mapping_tree.c | 19 ++++++++++++++++ ldap/servers/slapd/slapi-plugin.h | 1 + 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c index 4ec276a..474b508 100644 --- a/ldap/servers/slapd/bind.c +++ b/ldap/servers/slapd/bind.c @@ -107,6 +107,7 @@ do_bind( Slapi_PBlock *pb ) int auto_bind = 0; int minssf = 0; int minssf_exclude_rootdse = 0; + Slapi_DN *original_sdn = NULL; LDAPDebug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 0 ); @@ -660,10 +661,9 @@ do_bind( Slapi_PBlock *pb ) goto free_and_return; } - if (referral) - { - send_referrals_from_entry(pb,referral); - slapi_entry_free(referral); + if (referral) { + send_referrals_from_entry(pb,referral); + slapi_entry_free(referral); goto free_and_return; } @@ -671,29 +671,50 @@ do_bind( Slapi_PBlock *pb ) /* not root dn - pass to the backend */ if ( be->be_bind != NULL ) { - + original_sdn = slapi_sdn_dup(sdn); /* * call the pre-bind plugins. if they succeed, call * the backend bind function. then call the post-bind * plugins. */ if ( plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) == 0 ) { + int sdn_updated = 0; rc = 0; /* Check if a pre_bind plugin mapped the DN to another backend */ Slapi_DN *pb_sdn; slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &pb_sdn); - if (pb_sdn != sdn) { + if (!pb_sdn) { + PR_snprintf(errorbuf, sizeof(errorbuf), "Pre-bind plug-in set NULL dn\n"); + send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, errorbuf, 0, NULL); + goto free_and_return; + } else if ((pb_sdn != sdn) || (sdn_updated = slapi_sdn_compare(original_sdn, pb_sdn))) { /* * Slapi_DN set in pblock was changed by a pre bind plug-in. * It is a plug-in's responsibility to free the original Slapi_DN. */ sdn = pb_sdn; dn = slapi_sdn_get_dn(sdn); - - slapi_be_Unlock(be); - be = slapi_be_select(sdn); - slapi_be_Rlock(be); + if (!dn) { + PR_snprintf(errorbuf, sizeof(errorbuf), "Pre-bind plug-in set corrupted dn\n"); + send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, errorbuf, 0, NULL); + goto free_and_return; + } + if (!sdn_updated) { /* pb_sdn != sdn; need to compare the dn's. */ + sdn_updated = slapi_sdn_compare(original_sdn, sdn); + } + if (sdn_updated) { /* call slapi_be_select only when the DN is updated. */ + slapi_be_Unlock(be); + be = slapi_be_select_exact(sdn); + if (be) { + slapi_be_Rlock(be); + slapi_pblock_set( pb, SLAPI_BACKEND, be ); + } else { + PR_snprintf(errorbuf, sizeof(errorbuf), "No matching backend for %s\n", dn); + send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, errorbuf, 0, NULL); + goto free_and_return; + } + } } /* @@ -845,10 +866,12 @@ account_locked: } free_and_return:; - if (be) + slapi_sdn_free(&original_sdn); + if (be) { slapi_be_Unlock(be); + } if (bind_sdn_in_pb) { - slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn); + slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn); } slapi_sdn_free(&sdn); slapi_ch_free_string( &saslmech ); diff --git a/ldap/servers/slapd/mapping_tree.c b/ldap/servers/slapd/mapping_tree.c index 165eba1..20c2cc3 100644 --- a/ldap/servers/slapd/mapping_tree.c +++ b/ldap/servers/slapd/mapping_tree.c @@ -3095,6 +3095,25 @@ slapi_be_select( const Slapi_DN *sdn ) /* JCM - The name of this should change?? return be; } +Slapi_Backend * +slapi_be_select_exact(const Slapi_DN *sdn) +{ + Slapi_Backend *be = NULL; + mapping_tree_node *node = NULL; + + if (!sdn) { + LDAPDebug0Args(LDAP_DEBUG_ANY, "slapi_be_select_exact: Empty Slapi_DN is given.\n"); + return NULL; + } + node = slapi_get_mapping_tree_node_by_dn(sdn); + + if (node && node->mtn_be) { + be = node->mtn_be[0]; + } + + return be; +} + /* Check if the dn targets an internal reserved backends */ int slapi_on_internal_backends(const Slapi_DN *sdn) diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 6b04610..564da44 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -6338,6 +6338,7 @@ Slapi_Backend *slapi_be_new( const char *type, const char *name, int isprivate, int logchanges ); void slapi_be_free(Slapi_Backend **be); Slapi_Backend *slapi_be_select( const Slapi_DN *sdn ); +Slapi_Backend *slapi_be_select_exact(const Slapi_DN *sdn); Slapi_Backend *slapi_be_select_by_instance_name( const char *name ); int slapi_be_exist(const Slapi_DN *sdn); void slapi_be_delete_onexit(Slapi_Backend *be); -- 1.9.3