andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 4 months ago
Clone
Blob Blame History Raw
From 9cbd265f317febe4811a47db6efb2b5e3c0ed8ca Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
Date: Thu, 1 Sep 2016 16:02:53 -0700
Subject: [PATCH 391/394] Subject: [PATCH] Bug 1358559 - CVE-2016-4992
 389-ds-base: Information disclosure via repeated use of LDAP ADD operation,
 etc.

0. Backported the Bug-1347760 patches from the master branch to 1.2.11.

1. Description: If a bind user has no rights, it should not disclose
any information including the existence of the entry.

   Fix description:
   1) ALREADY_EXISTS in add -- If to be added entry is found existing
      in ldbm_back_add, it checks the ACI and if there is no rights,
      it returns INSUFFICIENT_ACCESS instead of ALREADY_EXISTS.
   2) NO_SUCH_OBJECT in other update operations -- If the target entry
      is found not existing, it checks the ancestor entry's access
      rights in find_entry.  If it is not allowed to access the subtree,
      it returns INSUFFICIENT_ACCESS instead of NO_SUC_OBJECT.  Plus,
      it supresses the "Matched" ancestor message.
   3) NO_SUCH_OBJECT in search -- If a bind entry has no rights to read
      a subtree, it returns no search results with SUCCESS.  It should
      be applied to the no existing subtree if the bind entry has no
      rights to the super tree.
   4) If bind fails because of the non-existence of the bind user or
      the parent nodes, the bind returns LDAP_INVALID_CREDENTIALS to
      the client with no other information.
      The detailed cause is logged in the access log as follows:
        RESULT err=49 .. etime=0 - No such suffix (<given suffix>)
        RESULT err=49 .. etime=0 - Invalid credentials
        RESULT err=49 .. etime=0 - No such entry

   Reviewed by lkrispen@redhat.com, mreynolds@redhat.com, and tbordaz@redhat.com.

2. Description:
   1. When an account is inactivated, the error UNWILLING_TO_PERFORM with
      the inactivated message should be returned only when the bind is
      successful.
   2. When SASL bind fails, instead of returning the cause of the failure
      directly to the client, but logging it in the access log.

   Reviewed by wibrown@redhat.com (Thank you, William!)

3. Description: do not overwrite rc used to decide if bind was successful.
   When the bind is through ldapi/autobind, an entry does not exist to be
   checked with slapi_check_account_lock.  In that case, a variable rc is
   not supposed to be modified which confuses the following code path.

   Reviewed by nhosoi@redhat.com.

https://bugzilla.redhat.com/show_bug.cgi?id=1358559
(cherry picked from commit 8078a9e5d067e85dbf1817f5620ecd50cde3f5c2)
(cherry picked from commit 3927f4f8c331c33eeeb773ebf762a51d5e303484)
---
 ldap/servers/slapd/back-ldbm/dn2entry.c        |  17 ++-
 ldap/servers/slapd/back-ldbm/findentry.c       | 139 +++++++++++++++++++------
 ldap/servers/slapd/back-ldbm/ldbm_add.c        |  21 +++-
 ldap/servers/slapd/back-ldbm/ldbm_bind.c       |  11 +-
 ldap/servers/slapd/back-ldbm/ldbm_compare.c    |   2 +-
 ldap/servers/slapd/back-ldbm/ldbm_delete.c     |   9 +-
 ldap/servers/slapd/back-ldbm/ldbm_modify.c     |   5 +-
 ldap/servers/slapd/back-ldbm/ldbm_modrdn.c     |  17 +--
 ldap/servers/slapd/back-ldbm/ldbm_search.c     |   2 +-
 ldap/servers/slapd/back-ldbm/misc.c            |   2 +-
 ldap/servers/slapd/back-ldbm/proto-back-ldbm.h |  14 +--
 ldap/servers/slapd/back-ldbm/vlv_srch.c        |   2 +-
 ldap/servers/slapd/bind.c                      | 120 +++++++++++----------
 ldap/servers/slapd/defbackend.c                |  82 ++++++++++++++-
 ldap/servers/slapd/result.c                    |  16 ++-
 ldap/servers/slapd/saslbind.c                  |   4 +-
 16 files changed, 324 insertions(+), 139 deletions(-)

diff --git a/ldap/servers/slapd/back-ldbm/dn2entry.c b/ldap/servers/slapd/back-ldbm/dn2entry.c
index 3cf37b6..c7c3ec2 100644
--- a/ldap/servers/slapd/back-ldbm/dn2entry.c
+++ b/ldap/servers/slapd/back-ldbm/dn2entry.c
@@ -180,14 +180,15 @@ struct backentry *
 dn2ancestor(
     Slapi_Backend *be,
     const Slapi_DN	*sdn,
-	Slapi_DN *ancestordn,
+    Slapi_DN *ancestordn,
     back_txn		*txn,
-    int			*err
+    int			*err,
+    int allow_suffix
 )
 {
-	struct backentry *e = NULL;
+    struct backentry *e = NULL;
 
-	LDAPDebug( LDAP_DEBUG_TRACE, "=> dn2ancestor \"%s\"\n", slapi_sdn_get_dn(sdn), 0, 0 );
+    LDAPDebug( LDAP_DEBUG_TRACE, "=> dn2ancestor \"%s\"\n", slapi_sdn_get_dn(sdn), 0, 0 );
 
     /* first, check to see if the given sdn is empty or a root suffix of the
        given backend - if so, it has no parent */
@@ -219,7 +220,13 @@ dn2ancestor(
         */
 
         /* stop when we get to "", or a backend suffix point */
-        while (!e && !slapi_sdn_isempty(&ancestorndn) && !slapi_be_issuffix( be, &ancestorndn )) {
+        while (!e && !slapi_sdn_isempty(&ancestorndn)) {
+            if (!allow_suffix) {
+                /* Original behavior. */
+                if (slapi_be_issuffix(be, &ancestorndn)) {
+                    break;
+                }
+            }
             /* find the entry - it uses the ndn, so no further conversion is necessary */
             e= dn2entry(be,&ancestorndn,txn,err);
             if (!e) {
diff --git a/ldap/servers/slapd/back-ldbm/findentry.c b/ldap/servers/slapd/back-ldbm/findentry.c
index 7174c08..a667ff8 100644
--- a/ldap/servers/slapd/back-ldbm/findentry.c
+++ b/ldap/servers/slapd/back-ldbm/findentry.c
@@ -45,8 +45,8 @@
 #include "back-ldbm.h"
 
 
-static struct backentry *find_entry_internal_dn(Slapi_PBlock *pb, backend *be, const Slapi_DN *sdn, int lock, back_txn *txn, int flags);
-static struct backentry * find_entry_internal(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, int lock, back_txn *txn, int flags);
+static struct backentry *find_entry_internal_dn(Slapi_PBlock *pb, backend *be, const Slapi_DN *sdn, int lock, back_txn *txn, int flags, int *rc);
+static struct backentry * find_entry_internal(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, int lock, back_txn *txn, int flags, int *rc);
 /* The flags take these values */
 #define FE_TOMBSTONE_INCLUDED TOMBSTONE_INCLUDED /* :1 defined in back-ldbm.h */
 #define FE_REALLY_INTERNAL 0x2
@@ -56,7 +56,7 @@ check_entry_for_referral(Slapi_PBlock *pb, Slapi_Entry *entry, char *matched, co
 {
 	int rc=0, i=0, numValues=0;
 	Slapi_Attr *attr;
-	Slapi_Value *val=NULL;	
+	Slapi_Value *val=NULL;
 	struct berval **refscopy=NULL;
 	struct berval **url=NULL;
 
@@ -109,12 +109,13 @@ out:
 
 static struct backentry *
 find_entry_internal_dn(
-	Slapi_PBlock	*pb,
+    Slapi_PBlock	*pb,
     backend			*be,
     const Slapi_DN *sdn,
     int				lock,
-	back_txn		*txn,
-	int				flags
+    back_txn		*txn,
+    int				flags,
+    int				*rc /* return code */
 )
 { 
 	struct backentry *e;
@@ -122,9 +123,14 @@ find_entry_internal_dn(
 	int	err;
 	ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
 	size_t tries = 0;
+	int isroot = 0;
+	int op_type;
+	char *errbuf = NULL;
 
 	/* get the managedsait ldap message control */
-	slapi_pblock_get( pb, SLAPI_MANAGEDSAIT, &managedsait );
+	slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+	slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, &isroot);
+	slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
 
 	while ( (tries < LDBM_CACHE_RETRY_COUNT) && 
 	        (e = dn2entry_ext( be, sdn, txn, flags & TOMBSTONE_INCLUDED, &err ))
@@ -142,6 +148,9 @@ find_entry_internal_dn(
 			if(check_entry_for_referral(pb, e->ep_entry, NULL, "find_entry_internal_dn"))
 			{
 				CACHE_RETURN( &inst->inst_cache, &e );
+				if (rc) { /* if check_entry_for_referral returns non-zero, result is sent. */
+					*rc = FE_RC_SENT_RESULT;
+				}
 				return( NULL );
 			}
 		}
@@ -180,27 +189,89 @@ find_entry_internal_dn(
 		struct backentry *me;
 		Slapi_DN ancestorsdn;
 		slapi_sdn_init(&ancestorsdn);
-		me= dn2ancestor(pb->pb_backend,sdn,&ancestorsdn,txn,&err);
+		me = dn2ancestor(pb->pb_backend, sdn, &ancestorsdn, txn, &err, 1 /* allow_suffix */);
 		if ( !managedsait && me != NULL ) {
 			/* if the entry is a referral send the referral */
 			if(check_entry_for_referral(pb, me->ep_entry, (char*)slapi_sdn_get_dn(&ancestorsdn), "find_entry_internal_dn"))
 			{
 				CACHE_RETURN( &inst->inst_cache, &me );
 				slapi_sdn_done(&ancestorsdn);
+				if (rc) { /* if check_entry_for_referral returns non-zero, result is sent. */
+					*rc = FE_RC_SENT_RESULT;
+				}
 				return( NULL );
 			}
 			/* else fall through to no such object */
 		}
 
 		/* entry not found */
-		slapi_send_ldap_result( pb, ( 0 == err || DB_NOTFOUND == err ) ?
-			LDAP_NO_SUCH_OBJECT : ( LDAP_INVALID_DN_SYNTAX == err ) ?
-			LDAP_INVALID_DN_SYNTAX : LDAP_OPERATIONS_ERROR,
-			(char*)slapi_sdn_get_dn(&ancestorsdn), NULL, 0, NULL );
+		if ((0 == err) || (DB_NOTFOUND == err)) {
+			if (me && !isroot) {
+				/* If not root, you may not want to reveal it. */
+				int acl_type = -1;
+				int return_err = LDAP_NO_SUCH_OBJECT;
+				err = LDAP_SUCCESS;
+				switch (op_type) {
+				case SLAPI_OPERATION_ADD:
+					acl_type = SLAPI_ACL_ADD;
+					return_err = LDAP_INSUFFICIENT_ACCESS;
+					break;
+				case SLAPI_OPERATION_DELETE:
+					acl_type = SLAPI_ACL_DELETE;
+					return_err = LDAP_INSUFFICIENT_ACCESS;
+					break;
+				case SLAPI_OPERATION_MODDN:
+					acl_type = SLAPI_ACL_WRITE;
+					return_err = LDAP_INSUFFICIENT_ACCESS;
+					break;
+				case SLAPI_OPERATION_MODIFY:
+					acl_type = SLAPI_ACL_WRITE;
+					return_err = LDAP_INSUFFICIENT_ACCESS;
+					break;
+				case SLAPI_OPERATION_SEARCH:
+				case SLAPI_OPERATION_COMPARE:
+					return_err = LDAP_SUCCESS;
+					acl_type = SLAPI_ACL_READ;
+					break;
+				case SLAPI_OPERATION_BIND:
+					acl_type = -1; /* skip acl check. acl is not set up for bind. */
+					return_err = LDAP_INVALID_CREDENTIALS;
+					slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "No such entry");
+					break;
+				}
+				if (acl_type > 0) {
+					err = plugin_call_acl_plugin(pb, me->ep_entry, NULL, NULL, acl_type,
+					                             ACLPLUGIN_ACCESS_DEFAULT, &errbuf);
+				}
+				if (((acl_type > 0) && err) || (op_type == SLAPI_OPERATION_BIND)) {
+					/*
+					 * Operations to be checked && ACL returns disallow.
+					 * Not to disclose the info about the entry's existence,
+					 * do not return the "matched" DN.
+					 * Plus, the bind case returns LDAP_INAPPROPRIATE_AUTH.
+					 */
+					slapi_send_ldap_result(pb, return_err, NULL, NULL, 0, NULL);
+				} else {
+					slapi_send_ldap_result(pb, LDAP_NO_SUCH_OBJECT,
+						(char*)slapi_sdn_get_dn(&ancestorsdn), NULL, 0, NULL);
+				}
+			} else {
+				slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT,
+					(char*)slapi_sdn_get_dn(&ancestorsdn), NULL, 0, NULL);
+			}
+		} else {
+			slapi_send_ldap_result( pb, ( LDAP_INVALID_DN_SYNTAX == err ) ?
+				LDAP_INVALID_DN_SYNTAX : LDAP_OPERATIONS_ERROR,
+				(char*)slapi_sdn_get_dn(&ancestorsdn), NULL, 0, NULL );
+		}
+		if (rc) {
+			*rc = FE_RC_SENT_RESULT;
+		}
 		slapi_sdn_done(&ancestorsdn);
 		CACHE_RETURN( &inst->inst_cache, &me );
 	}
 
+	slapi_ch_free_string(&errbuf);
 	LDAPDebug( LDAP_DEBUG_TRACE, "<= find_entry_internal_dn not found (%s)\n",
 	    slapi_sdn_get_dn(sdn), 0, 0 );
 	return( NULL );
@@ -212,11 +283,11 @@ find_entry_internal_dn(
  */
 static struct backentry *
 find_entry_internal_uniqueid(
-	Slapi_PBlock	*pb,
+    Slapi_PBlock	*pb,
     backend *be,
-	const char 			*uniqueid,
+    const char 			*uniqueid,
     int				lock,
-	back_txn		*txn
+    back_txn		*txn
 )
 {
 	ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
@@ -272,8 +343,9 @@ find_entry_internal(
     Slapi_Backend *be,
     const entry_address *addr,
     int			lock,
-	back_txn *txn,
-	int flags
+    back_txn *txn,
+    int flags,
+    int *rc
 )
 {
 	/* check if we should search based on uniqueid or dn */
@@ -290,11 +362,9 @@ find_entry_internal(
 		LDAPDebug( LDAP_DEBUG_TRACE, "=> find_entry_internal (dn=%s) lock %d\n",
 		           slapi_sdn_get_dn(addr->sdn), lock, 0 );
 		if (addr->sdn) {
-			entry = find_entry_internal_dn (pb, be, addr->sdn, 
-			                                lock, txn, flags);
+			entry = find_entry_internal_dn (pb, be, addr->sdn, lock, txn, flags, rc);
 		} else {
-			LDAPDebug0Args( LDAP_DEBUG_ANY,
-			                "find_entry_internal: Null target dn\n" );
+			LDAPDebug0Args( LDAP_DEBUG_ANY, "find_entry_internal: Null target dn\n" );
 		}
 
 		LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= find_entry_internal\n" );
@@ -307,10 +377,11 @@ find_entry(
     Slapi_PBlock		*pb,
     Slapi_Backend *be,
     const entry_address *addr,
-	back_txn *txn
+    back_txn *txn,
+    int *rc
 )
 {
-	return( find_entry_internal( pb, be, addr, 0/*!lock*/, txn, 0/*flags*/ ) );
+	return(find_entry_internal(pb, be, addr, 0/*!lock*/, txn, 0/*flags*/, rc));
 }
 
 struct backentry *
@@ -318,10 +389,11 @@ find_entry2modify(
     Slapi_PBlock		*pb,
     Slapi_Backend *be,
     const entry_address *addr,
-	back_txn *txn
+    back_txn *txn,
+    int *rc
 )
 {
-	return( find_entry_internal( pb, be, addr, 1/*lock*/, txn, 0/*flags*/ ) );
+	return(find_entry_internal(pb, be, addr, 1/*lock*/, txn, 0/*flags*/, rc));
 }
 
 /* New routines which do not do any referral stuff.
@@ -333,10 +405,11 @@ find_entry_only(
     Slapi_PBlock		*pb,
     Slapi_Backend *be,
     const entry_address *addr,
-	back_txn *txn
+    back_txn *txn,
+    int *rc
 )
 {
-	return( find_entry_internal( pb, be, addr, 0/*!lock*/, txn, FE_REALLY_INTERNAL ) );
+	return(find_entry_internal(pb, be, addr, 0/*!lock*/, txn, FE_REALLY_INTERNAL, rc));
 }
 
 struct backentry *
@@ -344,10 +417,11 @@ find_entry2modify_only(
     Slapi_PBlock		*pb,
     Slapi_Backend *be,
     const entry_address *addr,
-    back_txn *txn
+    back_txn *txn,
+    int *rc
 )
 {
-	return( find_entry_internal( pb, be, addr, 1/*lock*/, txn, FE_REALLY_INTERNAL ) );
+	return(find_entry_internal(pb, be, addr, 1/*lock*/, txn, 0 /* to check aci, disable INTERNAL */, rc));
 }
 
 struct backentry *
@@ -356,10 +430,9 @@ find_entry2modify_only_ext(
     Slapi_Backend *be,
     const entry_address *addr,
     int flags,
-    back_txn *txn
-
+    back_txn *txn,
+    int *rc
 )
 {
-	return( find_entry_internal( pb, be, addr, 1/*lock*/, txn, 
-		                         FE_REALLY_INTERNAL | flags ));
+	return(find_entry_internal(pb, be, addr, 1/*lock*/, txn, FE_REALLY_INTERNAL | flags, rc));
 }
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
index b915bfe..30fb8f2 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
@@ -118,6 +118,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 	int parent_switched = 0;
 	int noabort = 1;
 	const char *dn = NULL;
+	int result_sent = 0;
 
 	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
 	slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e );
@@ -302,7 +303,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 		addr.sdn = &parentsdn;
 		addr.udn = NULL;
 		addr.uniqueid = operation->o_params.p.p_add.parentuniqueid;
-		parententry = find_entry2modify_only(pb,be,&addr,&txn);
+		parententry = find_entry2modify_only(pb, be, &addr, &txn, &result_sent);
 		if (parententry && parententry->ep_entry) {
 			if (!operation->o_params.p.p_add.parentuniqueid){
 				/* Set the parentuniqueid now */
@@ -356,6 +357,14 @@ ldbm_back_add( Slapi_PBlock *pb )
 				/* The entry already exists */ 
 				ldap_result_code = LDAP_ALREADY_EXISTS;
 			}
+			if ((LDAP_ALREADY_EXISTS == ldap_result_code) && !isroot && !is_replicated_operation) {
+				int myrc = plugin_call_acl_plugin(pb, e, NULL, NULL, SLAPI_ACL_ADD,
+				                                  ACLPLUGIN_ACCESS_DEFAULT, &errbuf);
+				if (myrc) {
+					ldap_result_code = myrc;
+					ldap_result_message = errbuf;
+				}
+			}
 			goto error_return;
 		} 
 		else 
@@ -372,7 +381,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 				Slapi_DN ancestorsdn;
 				struct backentry *ancestorentry;
 				slapi_sdn_init(&ancestorsdn);
-				ancestorentry= dn2ancestor(pb->pb_backend,sdn,&ancestorsdn,&txn,&err);
+				ancestorentry = dn2ancestor(pb->pb_backend, sdn, &ancestorsdn, &txn, &err, 0);
 				slapi_sdn_done(&ancestorsdn);
 				if ( ancestorentry != NULL )
 				{
@@ -419,7 +428,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 		addr.udn = NULL;
 		addr.sdn = NULL;
 		addr.uniqueid = (char *)slapi_entry_get_uniqueid(e); /* jcm - cast away const */
-		tombstoneentry = find_entry2modify( pb, be, &addr, &txn );
+		tombstoneentry = find_entry2modify(pb, be, &addr, &txn, &result_sent);
 		if ( tombstoneentry==NULL )
 		{
 			ldap_result_code= -1;
@@ -617,7 +626,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 			              "It might be a conflict entry.\n", slapi_sdn_get_dn(&parentsdn));
 
 			slapi_sdn_init(&ancestorsdn);
-			ancestorentry = dn2ancestor(be, &parentsdn, &ancestorsdn, &txn, &err );
+			ancestorentry = dn2ancestor(be, &parentsdn, &ancestorsdn, &txn, &err, 1);
 			CACHE_RETURN( &inst->inst_cache, &ancestorentry );
 
 			ldap_result_code= LDAP_NO_SUCH_OBJECT;
@@ -1257,7 +1266,9 @@ common_return:
 	}
 	if(ldap_result_code!=-1)
 	{
-		slapi_send_ldap_result( pb, ldap_result_code, ldap_result_matcheddn, ldap_result_message, 0, NULL );
+		if (!result_sent) {
+			slapi_send_ldap_result( pb, ldap_result_code, ldap_result_matcheddn, ldap_result_message, 0, NULL );
+		}
 	}
 	backentry_free(&originalentry);
 	backentry_free(&tmpentry);
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_bind.c b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
index 24c0b4f..00a2021 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_bind.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
@@ -211,6 +211,7 @@ ldbm_back_bind( Slapi_PBlock *pb )
 	Slapi_Value **bvals;
 	entry_address *addr;
 	back_txn txn = {NULL};
+	int result_sent = 0;
 
 	/* get parameters */
 	slapi_pblock_get( pb, SLAPI_BACKEND, &be );
@@ -236,7 +237,11 @@ ldbm_back_bind( Slapi_PBlock *pb )
 	 * find the target entry.  find_entry() takes care of referrals
 	 *   and sending errors if the entry does not exist.
 	 */
-	if (( e = find_entry( pb, be, addr, &txn )) == NULL ) {
+	if ((e = find_entry( pb, be, addr, &txn, &result_sent)) == NULL) {
+		/* In the failure case, the result is supposed to be sent in the backend. */
+		if (!result_sent) {
+			slapi_send_ldap_result(pb, LDAP_INAPPROPRIATE_AUTH, NULL, NULL, 0, NULL);
+		}
 		return( SLAPI_BIND_FAIL );
 	}
 
@@ -265,8 +270,8 @@ ldbm_back_bind( Slapi_PBlock *pb )
 				break;
 			}
 #endif
-			slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL,
-			    NULL, 0, NULL );
+			slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Invalid credentials");
+			slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL );
 			CACHE_RETURN( &inst->inst_cache, &e );
 			value_done(&cv);
 			return( SLAPI_BIND_FAIL );
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_compare.c b/ldap/servers/slapd/back-ldbm/ldbm_compare.c
index e9761ec..1849fc4 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_compare.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_compare.c
@@ -78,7 +78,7 @@ ldbm_back_compare( Slapi_PBlock *pb )
 	/* get the namespace dn */
 	namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
 
-	if ( (e = find_entry( pb, be, addr, &txn )) == NULL ) {
+	if ((e = find_entry(pb, be, addr, &txn, NULL)) == NULL) {
 		return( -1 );	/* error result sent by find_entry() */
 	}
 
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index f30e2a6..4113da2 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -100,6 +100,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
 	int free_delete_existing_entry = 0;
 	ID ep_id = 0;
 	ID tomb_ep_id = 0;
+	int result_sent = 0;
 
 	slapi_pblock_get( pb, SLAPI_BACKEND, &be);
 	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
@@ -188,7 +189,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
 	 * deleted.  That is, the entry 'e' found with "addr" is a tomb-
 	 * stone.  If it is the case, we need to back off.
 	 */
-	if ( (e = find_entry2modify( pb, be, addr, &txn )) == NULL )
+	if ((e = find_entry2modify(pb, be, addr, &txn, &result_sent)) == NULL)
 	{
 		ldap_result_code= LDAP_NO_SUCH_OBJECT; 
 		/* retval is -1 */
@@ -398,7 +399,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
 				parent_addr.uniqueid = NULL;
 			}
 			parent_addr.sdn = &parentsdn;
-			parent = find_entry2modify_only_ext(pb, be, &parent_addr, TOMBSTONE_INCLUDED, &txn);
+			parent = find_entry2modify_only_ext(pb, be, &parent_addr, TOMBSTONE_INCLUDED, &txn, &result_sent);
 		}
 		if (parent) {
 			int isglue;
@@ -1323,7 +1324,9 @@ common_return:
 diskfull_return:
 	if(ldap_result_code!=-1)
 	{
-		slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
+		if (!result_sent) {
+			slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
+		}
 	}
 	modify_term(&parent_modify_c, be);
 	if(dblock_acquired)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
index 15cfe2c..e5b3cf2 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
@@ -388,6 +388,7 @@ ldbm_back_modify( Slapi_PBlock *pb )
 	int opreturn = 0;
 	int mod_count = 0;
 	int ec_locked = 0;
+	int result_sent = 0;
 
 	slapi_pblock_get( pb, SLAPI_BACKEND, &be);
 	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
@@ -447,7 +448,7 @@ ldbm_back_modify( Slapi_PBlock *pb )
 	}
 
 	/* find and lock the entry we are about to modify */
-	if ( (e = find_entry2modify( pb, be, addr, &txn )) == NULL ) {
+	if ( (e = find_entry2modify( pb, be, addr, &txn, &result_sent )) == NULL ) {
 		ldap_result_code= -1;
 		goto error_return;	  /* error result sent by find_entry2modify() */
 	}
@@ -890,7 +891,7 @@ common_return:
 	{
 		dblayer_unlock_backend(be);
 	}
-	if(ldap_result_code!=-1)
+	if ((ldap_result_code!=-1) && !result_sent)
 	{
 		slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
 	}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
index d8379ab..0fa72bf 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
@@ -120,6 +120,7 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
     char *newrdn = NULL;
     int opreturn = 0;
     int free_modrdn_existing_entry = 0;
+    int result_sent = 0;
 
     /* sdn & parentsdn need to be initialized before "goto *_return" */
     slapi_sdn_init(&dn_newdn);
@@ -352,7 +353,7 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
     /* find and lock the entry we are about to modify */
     /* JCMREPL - Argh, what happens about the stinking referrals? */
     slapi_pblock_get (pb, SLAPI_TARGET_ADDRESS, &old_addr);
-    e = find_entry2modify( pb, be, old_addr, &txn );
+    e = find_entry2modify( pb, be, old_addr, &txn, &result_sent );
     if ( e == NULL )
     {
         ldap_result_code= -1;
@@ -389,7 +390,7 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
     } else {
         oldparent_addr.uniqueid = NULL;            
     }
-    parententry = find_entry2modify_only( pb, be, &oldparent_addr, &txn );
+    parententry = find_entry2modify_only( pb, be, &oldparent_addr, &txn, &result_sent );
     modify_init(&parent_modify_context,parententry);
 
     /* Fetch and lock the new parent of the entry that is moving */            
@@ -399,7 +400,7 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
         if (is_resurect_operation) {
             newsuperior_addr->uniqueid = slapi_entry_attr_get_charptr(e->ep_entry, SLAPI_ATTR_VALUE_PARENT_UNIQUEID);
         }
-        newparententry = find_entry2modify_only( pb, be, newsuperior_addr, &txn );
+        newparententry = find_entry2modify_only( pb, be, newsuperior_addr, &txn, &result_sent );
         slapi_ch_free_string(&newsuperior_addr->uniqueid);
         modify_init(&newparent_modify_context,newparententry);
     }
@@ -460,7 +461,7 @@ ldbm_back_modrdn( Slapi_PBlock *pb )
                 Slapi_DN ancestorsdn;
                 struct backentry *ancestorentry;
                 slapi_sdn_init(&ancestorsdn);
-                ancestorentry= dn2ancestor(be,&dn_newdn,&ancestorsdn,&txn,&err);
+                ancestorentry = dn2ancestor(be, &dn_newdn, &ancestorsdn, &txn, &err, 0);
                 CACHE_RETURN( &inst->inst_cache, &ancestorentry );
                 ldap_result_matcheddn= slapi_ch_strdup((char *) slapi_sdn_get_dn(&ancestorsdn));
                 ldap_result_code= LDAP_NO_SUCH_OBJECT;
@@ -1418,10 +1419,10 @@ common_return:
         modify_term(&ruv_c, be);
     }
 
-    if (ldap_result_code!=-1)
-    {
-        slapi_send_ldap_result( pb, ldap_result_code, ldap_result_matcheddn,
-                    ldap_result_message, 0,NULL );
+    if ((ldap_result_code!=-1) && !result_sent)
+    { 
+        slapi_send_ldap_result(pb, ldap_result_code, ldap_result_matcheddn,
+                               ldap_result_message, 0, NULL);
     }
     slapi_mods_done(&smods_operation_wsi);
     slapi_mods_done(&smods_generated);
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
index bbcbe0e..d468481 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
@@ -603,7 +603,7 @@ ldbm_back_search( Slapi_PBlock *pb )
     }
     else
     {
-        if ( ( e = find_entry( pb, be, addr, &txn )) == NULL )
+        if ((e = find_entry(pb, be, addr, &txn, NULL)) == NULL)
         {
             /* error or referral sent by find_entry */
             return ldbm_back_search_cleanup(pb, li, sort_control, 
diff --git a/ldap/servers/slapd/back-ldbm/misc.c b/ldap/servers/slapd/back-ldbm/misc.c
index 7a90742..11e97bc 100644
--- a/ldap/servers/slapd/back-ldbm/misc.c
+++ b/ldap/servers/slapd/back-ldbm/misc.c
@@ -433,7 +433,7 @@ ldbm_txn_ruv_modify_context( Slapi_PBlock *pb, modify_context *mc )
 
     /* Note: if we find the bentry, it will stay locked until someone calls
      * modify_term on the mc we'll be associating the bentry with */
-    bentry = find_entry2modify_only( pb, be, &bentry_addr, &txn );
+    bentry = find_entry2modify_only(pb, be, &bentry_addr, &txn, NULL);
 
     if (NULL == bentry) {
 	/* Uh oh, we couldn't find and lock the RUV entry! */
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
index 688f293..eeeb7eb 100644
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
@@ -190,7 +190,7 @@ int ldbm_back_ctrl_info(Slapi_Backend *be, int cmd, void *info);
 struct backentry *dn2entry(Slapi_Backend *be, const Slapi_DN *sdn, back_txn *txn, int    *err);
 struct backentry *dn2entry_ext(Slapi_Backend *be, const Slapi_DN *sdn, back_txn *txn, int flags, int *err);
 struct backentry *dn2entry_or_ancestor(Slapi_Backend *be, const Slapi_DN *sdn, Slapi_DN *ancestor, back_txn *txn, int *err);
-struct backentry *dn2ancestor(Slapi_Backend *be,const Slapi_DN *sdn,Slapi_DN *ancestordn,back_txn *txn,int *err);
+struct backentry *dn2ancestor(Slapi_Backend *be,const Slapi_DN *sdn,Slapi_DN *ancestordn,back_txn *txn,int *err, int allow_suffix);
 int get_copy_of_entry(Slapi_PBlock *pb, const entry_address *addr, back_txn *txn, int plock_parameter, int must_exist);
 int get_copy_of_entry_ext(Slapi_PBlock *pb, ID id, const entry_address *addr, back_txn *txn, int plock_parameter, int must_exist);
 void done_with_pblock_entry(Slapi_PBlock *pb, int plock_parameter);
@@ -210,11 +210,13 @@ IDList * filter_candidates_ext( Slapi_PBlock *pb, backend *be, const char *base,
 /*
  * findentry.c
  */
-struct backentry * find_entry2modify( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn );
-struct backentry * find_entry( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn );
-struct backentry * find_entry2modify_only( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn);
-struct backentry * find_entry2modify_only_ext( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, int flags, back_txn *txn);
-struct backentry * find_entry_only( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn);
+/* Return code */
+#define FE_RC_SENT_RESULT 1
+struct backentry *find_entry2modify(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn, int *rc);
+struct backentry *find_entry(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn, int *rc);
+struct backentry *find_entry2modify_only(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn, int *rc);
+struct backentry *find_entry2modify_only_ext(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, int flags, back_txn *txn, int *rc);
+struct backentry *find_entry_only(Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn, int *rc);
 int check_entry_for_referral(Slapi_PBlock *pb, Slapi_Entry *entry, char *matched, const char *callingfn);
 
 /*
diff --git a/ldap/servers/slapd/back-ldbm/vlv_srch.c b/ldap/servers/slapd/back-ldbm/vlv_srch.c
index d7e28c1..8edf295 100644
--- a/ldap/servers/slapd/back-ldbm/vlv_srch.c
+++ b/ldap/servers/slapd/back-ldbm/vlv_srch.c
@@ -191,7 +191,7 @@ vlvSearch_init(struct vlvSearch* p, Slapi_PBlock *pb, const Slapi_Entry *e, ldbm
 
             addr.sdn = p->vlv_base;
             addr.uniqueid = NULL;
-            e = find_entry( pb, inst->inst_be, &addr, &txn );
+            e = find_entry(pb, inst->inst_be, &addr, &txn, NULL);
             /* Check to see if the entry is absent. If it is, mark this search
              * as not initialized */
             if (NULL == e) {
diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c
index bc8040f..eb6ee83 100644
--- a/ldap/servers/slapd/bind.c
+++ b/ldap/servers/slapd/bind.c
@@ -470,8 +470,8 @@ do_bind( Slapi_PBlock *pb )
              * to an LDAP DN, fail and return an invalidCredentials error.
              */
             if ( NULL == pb->pb_conn->c_external_dn ) {
-                send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL,
-                                  "client certificate mapping failed", 0, NULL );
+                slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Client certificate mapping failed");
+                send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, "", 0, NULL);
                 /* call postop plugins */
                 plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
                 goto free_and_return;
@@ -588,33 +588,32 @@ do_bind( Slapi_PBlock *pb )
         /* Check if simple binds are allowed over an insecure channel.  We only check
          * this for authenticated binds. */
         } else if (config_get_require_secure_binds() == 1) {
-                Connection *conn = NULL;
-                int sasl_ssf = 0;
-                int local_ssf = 0;
-
-                /* Allow simple binds only for SSL/TLS established connections
-                 * or connections using SASL privacy layers */
-                conn = pb->pb_conn;
-                if ( slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) {
-                    slapi_log_error( SLAPI_LOG_PLUGIN, "do_bind",
-                                     "Could not get SASL SSF from connection\n" );
-                    sasl_ssf = 0;
-                }
+            Connection *conn = NULL;
+            int sasl_ssf = 0;
+            int local_ssf = 0;
+
+            /* Allow simple binds only for SSL/TLS established connections
+             * or connections using SASL privacy layers */
+            conn = pb->pb_conn;
+            if ( slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) {
+                slapi_log_error( SLAPI_LOG_PLUGIN, "do_bind",
+                                 "Could not get SASL SSF from connection\n" );
+                sasl_ssf = 0;
+            }
 
-                if ( slapi_pblock_get(pb, SLAPI_CONN_LOCAL_SSF, &local_ssf) != 0) {
-                    slapi_log_error( SLAPI_LOG_PLUGIN, "do_bind",
-                                     "Could not get local SSF from connection\n" );
-                    local_ssf = 0;
-                }
+            if ( slapi_pblock_get(pb, SLAPI_CONN_LOCAL_SSF, &local_ssf) != 0) {
+                slapi_log_error( SLAPI_LOG_PLUGIN, "do_bind",
+                                 "Could not get local SSF from connection\n" );
+                local_ssf = 0;
+            }
 
-                if (((conn->c_flags & CONN_FLAG_SSL) != CONN_FLAG_SSL) &&
-                    (sasl_ssf <= 1) && (local_ssf <= 1)) {
-                        send_ldap_result(pb, LDAP_CONFIDENTIALITY_REQUIRED, NULL,
-                                         "Operation requires a secure connection",
-                                         0, NULL);
-                        slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
-                        goto free_and_return;
-                }
+            if (((conn->c_flags & CONN_FLAG_SSL) != CONN_FLAG_SSL) &&
+                (sasl_ssf <= 1) && (local_ssf <= 1)) {
+                send_ldap_result(pb, LDAP_CONFIDENTIALITY_REQUIRED, NULL,
+                                 "Operation requires a secure connection", 0, NULL);
+                slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
+                goto free_and_return;
+            }
         }
         break;
     default:
@@ -659,6 +658,7 @@ do_bind( Slapi_PBlock *pb )
                 /*
                  *  right dn, wrong passwd - reject with invalid credentials
                  */
+                slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Invalid credentials");
                 send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL );
                 /* increment BindSecurityErrorcount */
                 slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
@@ -681,6 +681,7 @@ do_bind( Slapi_PBlock *pb )
             /* call postop plugins */
             plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
         } else {
+            slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Pre-bind plug-in failed");
             send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "", 0, NULL);
         }
         goto free_and_return;
@@ -713,31 +714,11 @@ do_bind( Slapi_PBlock *pb )
         if ( plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN )
              == 0 )  {
             rc = 0;
-
-            /*
-             * Is this account locked ?
-             *	could be locked through the account inactivation
-             *	or by the password policy
-             *
-             * rc=0: account not locked
-             * rc=1: account locked, can not bind, result has been sent
-             * rc!=0 and rc!=1: error. Result was not sent, lets be_bind
-             * 		deal with it.
-             *
-             */
-
-            /* get the entry now, so that we can give it to slapi_check_account_lock and reslimit_update_from_dn */
-            if (! slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
-                bind_target_entry = get_entry(pb,  slapi_sdn_get_ndn(sdn));
-                rc = slapi_check_account_lock ( pb, bind_target_entry, pw_response_requested, 1, 1);
-            }
-
             slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
             set_db_default_result_handlers(pb);
-            if ( (rc != 1) && 
-                 (auto_bind || 
-                  (((rc = (*be->be_bind)( pb )) == SLAPI_BIND_SUCCESS) ||
-                   (rc == SLAPI_BIND_ANONYMOUS))) ) {
+            if (auto_bind || 
+                (((rc = (*be->be_bind)( pb )) == SLAPI_BIND_SUCCESS) ||
+                 (rc == SLAPI_BIND_ANONYMOUS))) {
                 long t;
                 char* authtype = NULL;
                 /* rc is SLAPI_BIND_SUCCESS or SLAPI_BIND_ANONYMOUS */
@@ -770,6 +751,28 @@ do_bind( Slapi_PBlock *pb )
 
                 if ( rc == SLAPI_BIND_SUCCESS ) {
                     int myrc = 0;
+                    /*
+                     * Is this account locked ?
+                     *	could be locked through the account inactivation
+                     *	or by the password policy
+                     *
+                     * rc=0: account not locked
+                     * rc=1: account locked, can not bind, result has been sent
+                     * rc!=0 and rc!=1: error. Result was not sent, lets be_bind
+                     * 		deal with it.
+                     *
+                     */
+
+                    /* get the entry now, so that we can give it to slapi_check_account_lock and reslimit_update_from_dn */
+                    if (! slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+                        bind_target_entry = get_entry(pb,  slapi_sdn_get_ndn(sdn));
+                        myrc = slapi_check_account_lock ( pb, bind_target_entry, pw_response_requested, 1, 1);
+                        if (1 == myrc) { /* account is locked */
+                            rc = myrc;
+                            goto account_locked;
+                        }
+                        myrc = 0;                        
+                    }
                     if (!auto_bind) {
                         /* 
                          * There could be a race that bind_target_entry was not added 
@@ -780,14 +783,9 @@ do_bind( Slapi_PBlock *pb )
                         if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA) && 
                             !bind_target_entry) {
                             bind_target_entry = get_entry(pb, slapi_sdn_get_ndn(sdn));
-                            if (bind_target_entry) {
-                                myrc = slapi_check_account_lock(pb, bind_target_entry,
-                                                              pw_response_requested, 1, 1);
-                                if (1 == myrc) { /* account is locked */
-                                    goto account_locked;
-                                }
-                            } else {
-                                send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL, "", 0, NULL);
+                            if (!bind_target_entry) {
+                                slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "No such entry");
+                                send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, "", 0, NULL);
                                 goto free_and_return;
                             }
                         }
@@ -847,8 +845,7 @@ account_locked:
              * the front end.
              */
             if ( rc == SLAPI_BIND_SUCCESS || rc == SLAPI_BIND_ANONYMOUS) {
-                send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL,
-                                  0, NULL );
+                send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
             }
 
             slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &rc );
@@ -871,8 +868,7 @@ free_and_return:;
     slapi_sdn_free(&sdn);
     slapi_ch_free_string( &saslmech );
     slapi_ch_free( (void **)&cred.bv_val );
-    if ( bind_target_entry != NULL )
-        slapi_entry_free(bind_target_entry);
+    slapi_entry_free(bind_target_entry);
 }
 
 
diff --git a/ldap/servers/slapd/defbackend.c b/ldap/servers/slapd/defbackend.c
index dd948d0..13d5919 100644
--- a/ldap/servers/slapd/defbackend.c
+++ b/ldap/servers/slapd/defbackend.c
@@ -200,6 +200,51 @@ defbackend_abandon( Slapi_PBlock *pb )
 }
 
 
+#define DEFBE_NO_SUCH_SUFFIX "No such suffix"
+/*
+ * Generate a "No such suffix" return text
+ * Example:
+ *   cn=X,dc=bogus,dc=com ==> "No such suffix (dc=bogus,dc=com)" 
+ *     if the last rdn starts with "dc=", print all last dc= rdn's.
+ *   cn=X,cn=bogus ==> "No such suffix (cn=bogus)"
+ *     otherwise, print the very last rdn.
+ *   cn=X,z=bogus ==> "No such suffix (x=bogus)"
+ *     it is true even if it is an invalid rdn.
+ *   cn=X,bogus ==> "No such suffix (bogus)"
+ *     another example of invalid rdn.
+ */
+static void
+_defbackend_gen_returntext(char *buffer, size_t buflen, char **dns)
+{
+    int dnidx;
+    int sidx;
+    struct suffix_repeat {
+        char *suffix;
+        int size;
+    } candidates[] = {
+        {"dc=", 3}, /* dc could be repeated.  otherwise the last rdn is used. */
+        {NULL, 0}
+    };
+    PR_snprintf(buffer, buflen, "%s (", DEFBE_NO_SUCH_SUFFIX);
+    for (dnidx = 0; dns[dnidx]; dnidx++) ; /* finding the last */
+    dnidx--; /* last rdn */
+    for (sidx = 0; candidates[sidx].suffix; sidx++) {
+        if (!PL_strncasecmp(dns[dnidx], candidates[sidx].suffix, candidates[sidx].size)) {
+            while (!PL_strncasecmp(dns[--dnidx], candidates[sidx].suffix, candidates[sidx].size)) ;
+            PL_strcat(buffer, dns[++dnidx]); /* the first "dn=", e.g. */
+            for (++dnidx; dns[dnidx]; dnidx++) {
+                PL_strcat(buffer, ",");
+                PL_strcat(buffer, dns[dnidx]);
+            }
+            PL_strcat(buffer, ")");
+            return; /* finished the task */
+        }
+    }
+    PL_strcat(buffer, dns[dnidx]);
+    PL_strcat(buffer, ")");
+    return;
+}
+
 static int
 defbackend_bind( Slapi_PBlock *pb )
 {
@@ -216,11 +261,40 @@ defbackend_bind( Slapi_PBlock *pb )
     slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
     slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred );
     if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 ) {
-	slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds);
-	rc = SLAPI_BIND_ANONYMOUS;
+        slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds);
+        rc = SLAPI_BIND_ANONYMOUS;
     } else {
-	send_nobackend_ldap_result( pb );
-	rc = SLAPI_BIND_FAIL;
+        Slapi_DN *sdn = NULL;
+        char *suffix = NULL;
+        char **dns = NULL;
+        
+        if (pb->pb_op) {
+            sdn = operation_get_target_spec(pb->pb_op);
+            if (sdn) {
+                dns = slapi_ldap_explode_dn(slapi_sdn_get_dn(sdn), 0);
+                if (dns) {
+                    size_t dnlen = slapi_sdn_get_ndn_len(sdn);
+                    size_t len = dnlen + sizeof(DEFBE_NO_SUCH_SUFFIX) + 4;
+                    suffix = slapi_ch_malloc(len);
+                    if (dnlen) {
+                        _defbackend_gen_returntext(suffix, len, dns);
+                    } else {
+                        PR_snprintf(suffix, len, "%s", DEFBE_NO_SUCH_SUFFIX);
+                    }
+                }
+            }
+        }
+        if (suffix) {
+            slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, suffix);
+        } else {
+            slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, DEFBE_NO_SUCH_SUFFIX);
+        }
+        send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, "", 0, NULL);
+        if (dns) {
+            slapi_ldap_value_free(dns);
+        }
+        slapi_ch_free_string(&suffix);
+        rc = SLAPI_BIND_FAIL;
     }
 
     return( rc );
diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c
index 6ed6c55..c385368 100644
--- a/ldap/servers/slapd/result.c
+++ b/ldap/servers/slapd/result.c
@@ -1815,14 +1815,26 @@ log_result( Slapi_PBlock *pb, Operation *op, int err, ber_tag_t tag,
 		} else {
 			if ( !internal_op )
 			{
+				char *pbtxt = NULL;
+				char *ext_str = NULL;
+				slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &pbtxt);
+				if (pbtxt) {
+					ext_str = slapi_ch_smprintf(" - %s", pbtxt);
+				} else {
+					ext_str = "";
+				}
 				slapi_log_access( LDAP_DEBUG_STATS,
 								  "conn=%" NSPRIu64 " op=%d RESULT err=%d"
-								  " tag=%" BERTAG_T " nentries=%d etime=%s%s%s\n",
+								  " tag=%" BERTAG_T " nentries=%d etime=%s%s%s%s\n",
 								  op->o_connid, 
 								  op->o_opid,
 								  err, tag, nentries, 
 								  etime, 
-								  notes_str, csn_str );
+								  notes_str, csn_str, ext_str );
+				if (pbtxt) {
+					/* if !pbtxt ==> ext_str == "".  Don't free ext_str. */
+					slapi_ch_free_string(&ext_str);
+				}
 			}
 			else
 			{
diff --git a/ldap/servers/slapd/saslbind.c b/ldap/servers/slapd/saslbind.c
index c1e5f2b..5ef098e 100644
--- a/ldap/servers/slapd/saslbind.c
+++ b/ldap/servers/slapd/saslbind.c
@@ -1049,8 +1049,8 @@ void ids_sasl_check_bind(Slapi_PBlock *pb)
         errstr = sasl_errdetail(sasl_conn);
 
         PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
-        send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
-                         (char*)errstr, 0, NULL);
+        slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, (void *)errstr);
+        send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
         break;
     }
 
-- 
2.4.11