Skip to content
Permalink
Browse files Browse the repository at this point in the history
Issue 4609 - CVE - info disclosure when authenticating
Description:  If you bind as a user that does not exist.  Error 49 is returned
              instead of error 32.  As error 32 discloses that the entry does
              not exist.  When you bind as an entry that does not have userpassword
              set then error 48 (inappropriate auth) is returned, but this
              discloses that the entry does indeed exist.  Instead we should
              always return error 49, even if the password is not set in the
              entry.  This way we do not disclose to an attacker if the Bind
              DN exists or not.

Relates: #4609

Reviewed by: tbordaz(Thanks!)
  • Loading branch information
mreynolds389 committed Feb 10, 2021
1 parent 2b17620 commit b6aae4d
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 13 deletions.
51 changes: 42 additions & 9 deletions dirsrvtests/tests/suites/basic/basic_test.py
Expand Up @@ -9,7 +9,7 @@

from subprocess import check_output, PIPE, run
from lib389 import DirSrv
from lib389.idm.user import UserAccounts
from lib389.idm.user import UserAccount, UserAccounts
import pytest
from lib389.tasks import *
from lib389.utils import *
Expand Down Expand Up @@ -1063,18 +1063,14 @@ def test_bind_invalid_entry(topology_st):
"""Test the failing bind does not return information about the entry
:id: 5cd9b083-eea6-426b-84ca-83c26fc49a6f
:customerscenario: True
:setup: Standalone instance
:steps:
1: bind as non existing entry
2: check that bind info does not report 'No such entry'
1: bind as non existing entry
2: check that bind info does not report 'No such entry'
:expectedresults:
1: pass
2: pass
1: pass
2: pass
"""

topology_st.standalone.restart()
Expand All @@ -1096,6 +1092,43 @@ def test_bind_invalid_entry(topology_st):
topology_st.standalone.simple_bind_s(DN_DM, PW_DM)


def test_bind_entry_missing_passwd(topology_st):
"""
:id: af209149-8fb8-48cb-93ea-3e82dd7119d2
:setup: Standalone Instance
:steps:
1. Bind as database entry that does not have userpassword set
2. Bind as database entry that does not exist
1. Bind as cn=config entry that does not have userpassword set
2. Bind as cn=config entry that does not exist
:expectedresults:
1. Fails with error 49
2. Fails with error 49
3. Fails with error 49
4. Fails with error 49
"""
user = UserAccount(topology_st.standalone, DEFAULT_SUFFIX)
with pytest.raises(ldap.INVALID_CREDENTIALS):
# Bind as the suffix root entry which does not have a userpassword
user.bind("some_password")

user = UserAccount(topology_st.standalone, "cn=not here," + DEFAULT_SUFFIX)
with pytest.raises(ldap.INVALID_CREDENTIALS):
# Bind as the entry which does not exist
user.bind("some_password")

# Test cn=config since it has its own code path
user = UserAccount(topology_st.standalone, "cn=config")
with pytest.raises(ldap.INVALID_CREDENTIALS):
# Bind as the config entry which does not have a userpassword
user.bind("some_password")

user = UserAccount(topology_st.standalone, "cn=does not exist,cn=config")
with pytest.raises(ldap.INVALID_CREDENTIALS):
# Bind as an entry under cn=config that does not exist
user.bind("some_password")


@pytest.mark.bz1044135
@pytest.mark.ds47319
def test_connection_buffer_size(topology_st):
Expand Down
4 changes: 2 additions & 2 deletions ldap/servers/slapd/back-ldbm/ldbm_bind.c
Expand Up @@ -76,8 +76,8 @@ ldbm_back_bind(Slapi_PBlock *pb)
case LDAP_AUTH_SIMPLE: {
Slapi_Value cv;
if (slapi_entry_attr_find(e->ep_entry, "userpassword", &attr) != 0) {
slapi_send_ldap_result(pb, LDAP_INAPPROPRIATE_AUTH, NULL,
NULL, 0, NULL);
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Entry does not have userpassword set");
slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
CACHE_RETURN(&inst->inst_cache, &e);
rc = SLAPI_BIND_FAIL;
goto bail;
Expand Down
7 changes: 5 additions & 2 deletions ldap/servers/slapd/dse.c
Expand Up @@ -1446,22 +1446,25 @@ dse_bind(Slapi_PBlock *pb) /* JCM There should only be one exit point from this

ec = dse_get_entry_copy(pdse, sdn, DSE_USE_LOCK);
if (ec == NULL) {
slapi_send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL);
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Entry does not exist");
slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
return (SLAPI_BIND_FAIL);
}

switch (method) {
case LDAP_AUTH_SIMPLE: {
Slapi_Value cv;
if (slapi_entry_attr_find(ec, "userpassword", &attr) != 0) {
slapi_send_ldap_result(pb, LDAP_INAPPROPRIATE_AUTH, NULL, NULL, 0, NULL);
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Entry does not have userpassword set");
slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
slapi_entry_free(ec);
return SLAPI_BIND_FAIL;
}
bvals = attr_get_present_values(attr);

slapi_value_init_berval(&cv, cred);
if (slapi_pw_find_sv(bvals, &cv) != 0) {
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Invalid credentials");
slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
slapi_entry_free(ec);
value_done(&cv);
Expand Down

0 comments on commit b6aae4d

Please sign in to comment.