Skip to content

Commit

Permalink
Bug 1347760 - CVE-2016-4992 389-ds-base: Information disclosure via r…
Browse files Browse the repository at this point in the history
…epeated use of LDAP ADD operation, etc.

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

https://bugzilla.redhat.com/show_bug.cgi?id=1347760

Reviewed by lkrispen@redhat.com, mreynolds@redhat.com, and tbordaz@redhat.com.
Thank you!!!
  • Loading branch information
nhosoi committed Jul 14, 2016
1 parent 18c6029 commit 0b932d4
Show file tree
Hide file tree
Showing 15 changed files with 311 additions and 114 deletions.
17 changes: 12 additions & 5 deletions ldap/servers/slapd/back-ldbm/dn2entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,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 */
Expand Down Expand Up @@ -190,7 +191,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) {
Expand Down
139 changes: 106 additions & 33 deletions ldap/servers/slapd/back-ldbm/findentry.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,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
Expand All @@ -27,7 +27,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;

Expand Down Expand Up @@ -80,22 +80,28 @@ check_entry_for_referral(Slapi_PBlock *pb, Slapi_Entry *entry, char *matched, co

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;
int managedsait = 0;
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 ))
Expand All @@ -113,6 +119,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 );
}
}
Expand Down Expand Up @@ -151,27 +160,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_MODDN;
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 );
Expand All @@ -183,11 +254,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;
Expand Down Expand Up @@ -243,8 +314,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 */
Expand All @@ -261,11 +333,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" );
Expand All @@ -278,21 +348,23 @@ 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 *
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.
Expand All @@ -304,21 +376,23 @@ 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 *
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 *
Expand All @@ -327,10 +401,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));
}
21 changes: 16 additions & 5 deletions ldap/servers/slapd/back-ldbm/ldbm_add.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ ldbm_back_add( Slapi_PBlock *pb )
int myrc = 0;
PRUint64 conn_id;
int op_id;
int result_sent = 0;
if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {
conn_id = 0; /* connection is NULL */
}
Expand Down Expand Up @@ -379,7 +380,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 */
Expand Down Expand Up @@ -431,6 +432,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) {
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
Expand All @@ -447,7 +456,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 )
{
Expand Down Expand Up @@ -495,7 +504,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;
Expand Down Expand Up @@ -712,7 +721,7 @@ ldbm_back_add( Slapi_PBlock *pb )
LDAPDebug1Arg(LDAP_DEBUG_BACKLDBM, "ldbm_add: Parent \"%s\" does not exist. "
"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;
Expand Down Expand Up @@ -1349,7 +1358,9 @@ ldbm_back_add( Slapi_PBlock *pb )
* And we don't want the supplier to halt sending the updates. */
ldap_result_code = LDAP_SUCCESS;
}
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);
Expand Down
Loading

0 comments on commit 0b932d4

Please sign in to comment.