Thursday, September 1, 2016

[389-commits] Branch '389-ds-base-1.2.11' - ldap/servers

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(-)

New commits:
commit 8078a9e5d067e85dbf1817f5620ecd50cde3f5c2
Author: Noriko Hosoi <nhosoi@redhat.com>
Date: Thu Sep 1 16:02:53 2016 -0700

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

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 3397c0f..d48b299 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;
@@ -1258,7 +1267,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;
}

No comments:

Post a Comment