Blame SOURCES/0037-Issue-4609-CVE-info-disclosure-when-authenticating.patch

ef1f48
From 861f17d2cb50fc649feee004be1ce08d2e3873f8 Mon Sep 17 00:00:00 2001
ef1f48
From: Mark Reynolds <mreynolds@redhat.com>
ef1f48
Date: Tue, 9 Feb 2021 14:02:59 -0500
ef1f48
Subject: [PATCH 4/4] Issue 4609 - CVE - info disclosure when authenticating
ef1f48
ef1f48
Description:  If you bind as a user that does not exist.  Error 49 is returned
ef1f48
              instead of error 32.  As error 32 discloses that the entry does
ef1f48
              not exist.  When you bind as an entry that does not have userpassword
ef1f48
              set then error 48 (inappropriate auth) is returned, but this
ef1f48
              discloses that the entry does indeed exist.  Instead we should
ef1f48
              always return error 49, even if the password is not set in the
ef1f48
              entry.  This way we do not disclose to an attacker if the Bind
ef1f48
              DN exists or not.
ef1f48
ef1f48
Relates: https://github.com/389ds/389-ds-base/issues/4609
ef1f48
ef1f48
Reviewed by: tbordaz(Thanks!)
ef1f48
---
ef1f48
 dirsrvtests/tests/suites/basic/basic_test.py | 72 +++++++++++++++++++-
ef1f48
 ldap/servers/slapd/back-ldbm/ldbm_bind.c     |  4 +-
ef1f48
 ldap/servers/slapd/dse.c                     |  7 +-
ef1f48
 3 files changed, 78 insertions(+), 5 deletions(-)
ef1f48
ef1f48
diff --git a/dirsrvtests/tests/suites/basic/basic_test.py b/dirsrvtests/tests/suites/basic/basic_test.py
ef1f48
index e9afa1e7e..6244782fa 100644
ef1f48
--- a/dirsrvtests/tests/suites/basic/basic_test.py
ef1f48
+++ b/dirsrvtests/tests/suites/basic/basic_test.py
ef1f48
@@ -13,7 +13,7 @@
ef1f48
 
ef1f48
 from subprocess import check_output, PIPE, run
ef1f48
 from lib389 import DirSrv
ef1f48
-from lib389.idm.user import UserAccounts
ef1f48
+from lib389.idm.user import UserAccount, UserAccounts
ef1f48
 import pytest
ef1f48
 from lib389.tasks import *
ef1f48
 from lib389.utils import *
ef1f48
@@ -1062,6 +1062,76 @@ def test_search_ou(topology_st):
ef1f48
     assert len(entries) == 0
ef1f48
 
ef1f48
 
ef1f48
+def test_bind_invalid_entry(topology_st):
ef1f48
+    """Test the failing bind does not return information about the entry
ef1f48
+
ef1f48
+    :id: 5cd9b083-eea6-426b-84ca-83c26fc49a6f
ef1f48
+    :customerscenario: True
ef1f48
+    :setup: Standalone instance
ef1f48
+    :steps:
ef1f48
+        1: bind as non existing entry
ef1f48
+        2: check that bind info does not report 'No such entry'
ef1f48
+    :expectedresults:
ef1f48
+        1: pass
ef1f48
+        2: pass
ef1f48
+    """
ef1f48
+
ef1f48
+    topology_st.standalone.restart()
ef1f48
+    INVALID_ENTRY="cn=foooo,%s" % DEFAULT_SUFFIX
ef1f48
+    try:
ef1f48
+        topology_st.standalone.simple_bind_s(INVALID_ENTRY, PASSWORD)
ef1f48
+    except ldap.LDAPError as e:
ef1f48
+        log.info('test_bind_invalid_entry: Failed to bind as %s (expected)' % INVALID_ENTRY)
ef1f48
+        log.info('exception description: ' + e.args[0]['desc'])
ef1f48
+        if 'info' in e.args[0]:
ef1f48
+            log.info('exception info: ' + e.args[0]['info'])
ef1f48
+        assert e.args[0]['desc'] == 'Invalid credentials'
ef1f48
+        assert 'info' not in e.args[0]
ef1f48
+        pass
ef1f48
+
ef1f48
+    log.info('test_bind_invalid_entry: PASSED')
ef1f48
+
ef1f48
+    # reset credentials
ef1f48
+    topology_st.standalone.simple_bind_s(DN_DM, PW_DM)
ef1f48
+
ef1f48
+
ef1f48
+def test_bind_entry_missing_passwd(topology_st):
ef1f48
+    """
ef1f48
+    :id: af209149-8fb8-48cb-93ea-3e82dd7119d2
ef1f48
+    :setup: Standalone Instance
ef1f48
+    :steps:
ef1f48
+        1. Bind as database entry that does not have userpassword set
ef1f48
+        2. Bind as database entry that does not exist
ef1f48
+        1. Bind as cn=config entry that does not have userpassword set
ef1f48
+        2. Bind as cn=config entry that does not exist
ef1f48
+    :expectedresults:
ef1f48
+        1. Fails with error 49
ef1f48
+        2. Fails with error 49
ef1f48
+        3. Fails with error 49
ef1f48
+        4. Fails with error 49
ef1f48
+    """
ef1f48
+    user = UserAccount(topology_st.standalone, DEFAULT_SUFFIX)
ef1f48
+    with pytest.raises(ldap.INVALID_CREDENTIALS):
ef1f48
+        # Bind as the suffix root entry which does not have a userpassword
ef1f48
+        user.bind("some_password")
ef1f48
+
ef1f48
+    user = UserAccount(topology_st.standalone, "cn=not here," + DEFAULT_SUFFIX)
ef1f48
+    with pytest.raises(ldap.INVALID_CREDENTIALS):
ef1f48
+        # Bind as the entry which does not exist
ef1f48
+        user.bind("some_password")
ef1f48
+
ef1f48
+    # Test cn=config since it has its own code path
ef1f48
+    user = UserAccount(topology_st.standalone, "cn=config")
ef1f48
+    with pytest.raises(ldap.INVALID_CREDENTIALS):
ef1f48
+        # Bind as the config entry which does not have a userpassword
ef1f48
+        user.bind("some_password")
ef1f48
+
ef1f48
+    user = UserAccount(topology_st.standalone, "cn=does not exist,cn=config")
ef1f48
+    with pytest.raises(ldap.INVALID_CREDENTIALS):
ef1f48
+        # Bind as an entry under cn=config that does not exist
ef1f48
+        user.bind("some_password")
ef1f48
+
ef1f48
+
ef1f48
 @pytest.mark.bz1044135
ef1f48
 @pytest.mark.ds47319
ef1f48
 def test_connection_buffer_size(topology_st):
ef1f48
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_bind.c b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
ef1f48
index fa450ecd5..38d115a32 100644
ef1f48
--- a/ldap/servers/slapd/back-ldbm/ldbm_bind.c
ef1f48
+++ b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
ef1f48
@@ -76,8 +76,8 @@ ldbm_back_bind(Slapi_PBlock *pb)
ef1f48
     case LDAP_AUTH_SIMPLE: {
ef1f48
         Slapi_Value cv;
ef1f48
         if (slapi_entry_attr_find(e->ep_entry, "userpassword", &attr) != 0) {
ef1f48
-            slapi_send_ldap_result(pb, LDAP_INAPPROPRIATE_AUTH, NULL,
ef1f48
-                                   NULL, 0, NULL);
ef1f48
+            slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Entry does not have userpassword set");
ef1f48
+            slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
ef1f48
             CACHE_RETURN(&inst->inst_cache, &e);
ef1f48
             rc = SLAPI_BIND_FAIL;
ef1f48
             goto bail;
ef1f48
diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c
ef1f48
index 0e22d3cec..0d3268046 100644
ef1f48
--- a/ldap/servers/slapd/dse.c
ef1f48
+++ b/ldap/servers/slapd/dse.c
ef1f48
@@ -1443,7 +1443,8 @@ dse_bind(Slapi_PBlock *pb) /* JCM There should only be one exit point from this
ef1f48
 
ef1f48
     ec = dse_get_entry_copy(pdse, sdn, DSE_USE_LOCK);
ef1f48
     if (ec == NULL) {
ef1f48
-        slapi_send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL);
ef1f48
+        slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Entry does not exist");
ef1f48
+        slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
ef1f48
         return (SLAPI_BIND_FAIL);
ef1f48
     }
ef1f48
 
ef1f48
@@ -1451,7 +1452,8 @@ dse_bind(Slapi_PBlock *pb) /* JCM There should only be one exit point from this
ef1f48
     case LDAP_AUTH_SIMPLE: {
ef1f48
         Slapi_Value cv;
ef1f48
         if (slapi_entry_attr_find(ec, "userpassword", &attr) != 0) {
ef1f48
-            slapi_send_ldap_result(pb, LDAP_INAPPROPRIATE_AUTH, NULL, NULL, 0, NULL);
ef1f48
+            slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Entry does not have userpassword set");
ef1f48
+            slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
ef1f48
             slapi_entry_free(ec);
ef1f48
             return SLAPI_BIND_FAIL;
ef1f48
         }
ef1f48
@@ -1459,6 +1461,7 @@ dse_bind(Slapi_PBlock *pb) /* JCM There should only be one exit point from this
ef1f48
 
ef1f48
         slapi_value_init_berval(&cv, cred);
ef1f48
         if (slapi_pw_find_sv(bvals, &cv) != 0) {
ef1f48
+            slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "Invalid credentials");
ef1f48
             slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
ef1f48
             slapi_entry_free(ec);
ef1f48
             value_done(&cv;;
ef1f48
-- 
ef1f48
2.26.2
ef1f48