parent
3eac45e49f
commit
84f2767280
@ -0,0 +1,108 @@
|
|||||||
|
From 23956cfb86a312318667fb9376322574fa8ec7f4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: James Chapman <jachapma@redhat.com>
|
||||||
|
Date: Wed, 1 May 2024 15:01:33 +0100
|
||||||
|
Subject: [PATCH] CVE-2024-2199
|
||||||
|
|
||||||
|
---
|
||||||
|
.../tests/suites/password/password_test.py | 56 +++++++++++++++++++
|
||||||
|
ldap/servers/slapd/modify.c | 8 ++-
|
||||||
|
2 files changed, 62 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dirsrvtests/tests/suites/password/password_test.py b/dirsrvtests/tests/suites/password/password_test.py
|
||||||
|
index 1245feb31..e4abd9907 100644
|
||||||
|
--- a/dirsrvtests/tests/suites/password/password_test.py
|
||||||
|
+++ b/dirsrvtests/tests/suites/password/password_test.py
|
||||||
|
@@ -63,6 +63,62 @@ def test_password_delete_specific_password(topology_st):
|
||||||
|
log.info('test_password_delete_specific_password: PASSED')
|
||||||
|
|
||||||
|
|
||||||
|
+def test_password_modify_non_utf8(topology_st):
|
||||||
|
+ """Attempt a modify of the userPassword attribute with
|
||||||
|
+ an invalid non utf8 value
|
||||||
|
+
|
||||||
|
+ :id: a31af9d5-d665-42b9-8d6e-fea3d0837d36
|
||||||
|
+ :setup: Standalone instance
|
||||||
|
+ :steps:
|
||||||
|
+ 1. Add a user if it doesnt exist and set its password
|
||||||
|
+ 2. Verify password with a bind
|
||||||
|
+ 3. Modify userPassword attr with invalid value
|
||||||
|
+ 4. Attempt a bind with invalid password value
|
||||||
|
+ 5. Verify original password with a bind
|
||||||
|
+ :expectedresults:
|
||||||
|
+ 1. The user with userPassword should be added successfully
|
||||||
|
+ 2. Operation should be successful
|
||||||
|
+ 3. Server returns ldap.UNWILLING_TO_PERFORM
|
||||||
|
+ 4. Server returns ldap.INVALID_CREDENTIALS
|
||||||
|
+ 5. Operation should be successful
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ log.info('Running test_password_modify_non_utf8...')
|
||||||
|
+
|
||||||
|
+ # Create user and set password
|
||||||
|
+ standalone = topology_st.standalone
|
||||||
|
+ users = UserAccounts(standalone, DEFAULT_SUFFIX)
|
||||||
|
+ if not users.exists(TEST_USER_PROPERTIES['uid'][0]):
|
||||||
|
+ user = users.create(properties=TEST_USER_PROPERTIES)
|
||||||
|
+ else:
|
||||||
|
+ user = users.get(TEST_USER_PROPERTIES['uid'][0])
|
||||||
|
+ user.set('userpassword', PASSWORD)
|
||||||
|
+
|
||||||
|
+ # Verify password
|
||||||
|
+ try:
|
||||||
|
+ user.bind(PASSWORD)
|
||||||
|
+ except ldap.LDAPError as e:
|
||||||
|
+ log.fatal('Failed to bind as {}, error: '.format(user.dn) + e.args[0]['desc'])
|
||||||
|
+ assert False
|
||||||
|
+
|
||||||
|
+ # Modify userPassword with an invalid value
|
||||||
|
+ password = b'tes\x82t-password' # A non UTF-8 encoded password
|
||||||
|
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||||
|
+ user.replace('userpassword', password)
|
||||||
|
+
|
||||||
|
+ # Verify a bind fails with invalid pasword
|
||||||
|
+ with pytest.raises(ldap.INVALID_CREDENTIALS):
|
||||||
|
+ user.bind(password)
|
||||||
|
+
|
||||||
|
+ # Verify we can still bind with original password
|
||||||
|
+ try:
|
||||||
|
+ user.bind(PASSWORD)
|
||||||
|
+ except ldap.LDAPError as e:
|
||||||
|
+ log.fatal('Failed to bind as {}, error: '.format(user.dn) + e.args[0]['desc'])
|
||||||
|
+ assert False
|
||||||
|
+
|
||||||
|
+ log.info('test_password_modify_non_utf8: PASSED')
|
||||||
|
+
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Run isolated
|
||||||
|
# -s for DEBUG mode
|
||||||
|
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
|
||||||
|
index a20984e0b..fb65d58b3 100644
|
||||||
|
--- a/ldap/servers/slapd/modify.c
|
||||||
|
+++ b/ldap/servers/slapd/modify.c
|
||||||
|
@@ -762,8 +762,10 @@ op_shared_modify(Slapi_PBlock *pb, int pw_change, char *old_pw)
|
||||||
|
* flagged - leave mod attributes alone */
|
||||||
|
if (!repl_op && !skip_modified_attrs && lastmod) {
|
||||||
|
modify_update_last_modified_attr(pb, &smods);
|
||||||
|
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, slapi_mods_get_ldapmods_byref(&smods));
|
||||||
|
}
|
||||||
|
|
||||||
|
+
|
||||||
|
if (0 == slapi_mods_get_num_mods(&smods)) {
|
||||||
|
/* nothing to do - no mods - this is not an error - just
|
||||||
|
send back LDAP_SUCCESS */
|
||||||
|
@@ -930,8 +932,10 @@ op_shared_modify(Slapi_PBlock *pb, int pw_change, char *old_pw)
|
||||||
|
|
||||||
|
/* encode password */
|
||||||
|
if (pw_encodevals_ext(pb, sdn, va)) {
|
||||||
|
- slapi_log_err(SLAPI_LOG_CRIT, "op_shared_modify", "Unable to hash userPassword attribute for %s.\n", slapi_entry_get_dn_const(e));
|
||||||
|
- send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Unable to store attribute \"userPassword\" correctly\n", 0, NULL);
|
||||||
|
+ slapi_log_err(SLAPI_LOG_CRIT, "op_shared_modify", "Unable to hash userPassword attribute for %s, "
|
||||||
|
+ "check value is utf8 string.\n", slapi_entry_get_dn_const(e));
|
||||||
|
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Unable to hash \"userPassword\" attribute, "
|
||||||
|
+ "check value is utf8 string.\n", 0, NULL);
|
||||||
|
valuearray_free(&va);
|
||||||
|
goto free_and_return;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.41.0
|
||||||
|
|
@ -0,0 +1,213 @@
|
|||||||
|
From 5cfa136c48c477765cb20b007ad441ed21534e86 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Pierre Rogier <progier@redhat.com>
|
||||||
|
Date: Wed, 17 Apr 2024 18:18:04 +0200
|
||||||
|
Subject: [PATCH] CVE-2024-3657
|
||||||
|
|
||||||
|
---
|
||||||
|
.../tests/suites/filter/large_filter_test.py | 34 +++++-
|
||||||
|
ldap/servers/slapd/back-ldbm/index.c | 111 ++++++++++--------
|
||||||
|
2 files changed, 92 insertions(+), 53 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dirsrvtests/tests/suites/filter/large_filter_test.py b/dirsrvtests/tests/suites/filter/large_filter_test.py
|
||||||
|
index 964facae5..5390a0f9c 100644
|
||||||
|
--- a/dirsrvtests/tests/suites/filter/large_filter_test.py
|
||||||
|
+++ b/dirsrvtests/tests/suites/filter/large_filter_test.py
|
||||||
|
@@ -13,19 +13,29 @@ verify and testing Filter from a search
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
+import ldap
|
||||||
|
|
||||||
|
-from lib389._constants import PW_DM
|
||||||
|
+from lib389._constants import PW_DM, DEFAULT_SUFFIX, ErrorLog
|
||||||
|
from lib389.topologies import topology_st as topo
|
||||||
|
from lib389.idm.user import UserAccounts, UserAccount
|
||||||
|
from lib389.idm.account import Accounts
|
||||||
|
from lib389.backend import Backends
|
||||||
|
from lib389.idm.domain import Domain
|
||||||
|
+from lib389.utils import get_ldapurl_from_serverid
|
||||||
|
|
||||||
|
SUFFIX = 'dc=anuj,dc=com'
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.tier1
|
||||||
|
|
||||||
|
|
||||||
|
+def open_new_ldapi_conn(dsinstance):
|
||||||
|
+ ldapurl, certdir = get_ldapurl_from_serverid(dsinstance)
|
||||||
|
+ assert 'ldapi://' in ldapurl
|
||||||
|
+ conn = ldap.initialize(ldapurl)
|
||||||
|
+ conn.sasl_interactive_bind_s("", ldap.sasl.external())
|
||||||
|
+ return conn
|
||||||
|
+
|
||||||
|
+
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def _create_entries(request, topo):
|
||||||
|
"""
|
||||||
|
@@ -159,6 +169,28 @@ def test_large_filter(topo, _create_entries, real_value):
|
||||||
|
assert len(Accounts(conn, SUFFIX).filter(real_value)) == 3
|
||||||
|
|
||||||
|
|
||||||
|
+def test_long_filter_value(topo):
|
||||||
|
+ """Exercise large eq filter with dn syntax attributes
|
||||||
|
+
|
||||||
|
+ :id: b069ef72-fcc3-11ee-981c-482ae39447e5
|
||||||
|
+ :setup: Standalone
|
||||||
|
+ :steps:
|
||||||
|
+ 1. Try to pass filter rules as per the condition.
|
||||||
|
+ :expectedresults:
|
||||||
|
+ 1. Pass
|
||||||
|
+ """
|
||||||
|
+ inst = topo.standalone
|
||||||
|
+ conn = open_new_ldapi_conn(inst.serverid)
|
||||||
|
+ inst.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE,ErrorLog.SEARCH_FILTER))
|
||||||
|
+ filter_value = "a\x1Edmin" * 1025
|
||||||
|
+ conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f'(cn={filter_value})')
|
||||||
|
+ filter_value = "aAdmin" * 1025
|
||||||
|
+ conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f'(cn={filter_value})')
|
||||||
|
+ filter_value = "*"
|
||||||
|
+ conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, f'(cn={filter_value})')
|
||||||
|
+ inst.config.loglevel(vals=(ErrorLog.DEFAULT,))
|
||||||
|
+
|
||||||
|
+
|
||||||
|
if __name__ == '__main__':
|
||||||
|
CURRENT_FILE = os.path.realpath(__file__)
|
||||||
|
pytest.main("-s -v %s" % CURRENT_FILE)
|
||||||
|
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
|
||||||
|
index 86bc825fe..bdac0a616 100644
|
||||||
|
--- a/ldap/servers/slapd/back-ldbm/index.c
|
||||||
|
+++ b/ldap/servers/slapd/back-ldbm/index.c
|
||||||
|
@@ -74,6 +74,32 @@ typedef struct _index_buffer_handle index_buffer_handle;
|
||||||
|
#define INDEX_BUFFER_FLAG_SERIALIZE 1
|
||||||
|
#define INDEX_BUFFER_FLAG_STATS 2
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * space needed to encode a byte:
|
||||||
|
+ * 0x00-0x31 and 0x7f-0xff requires 3 bytes: \xx
|
||||||
|
+ * 0x22 and 0x5C requires 2 bytes: \" and \\
|
||||||
|
+ * other requires 1 byte: c
|
||||||
|
+ */
|
||||||
|
+static char encode_size[] = {
|
||||||
|
+ /* 0x00 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0x10 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0x20 */ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
+ /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
+ /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
+ /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
|
||||||
|
+ /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
+ /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
|
||||||
|
+ /* 0x80 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0x90 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0xA0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0xB0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0xC0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0xD0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0xE0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+ /* 0xF0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+
|
||||||
|
/* Index buffering functions */
|
||||||
|
|
||||||
|
static int
|
||||||
|
@@ -802,65 +828,46 @@ index_add_mods(
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a 'struct berval' into a displayable ASCII string
|
||||||
|
+ * returns the printable string
|
||||||
|
*/
|
||||||
|
-
|
||||||
|
-#define SPECIAL(c) (c < 32 || c > 126 || c == '\\' || c == '"')
|
||||||
|
-
|
||||||
|
const char *
|
||||||
|
encode(const struct berval *data, char buf[BUFSIZ])
|
||||||
|
{
|
||||||
|
- char *s;
|
||||||
|
- char *last;
|
||||||
|
- if (data == NULL || data->bv_len == 0)
|
||||||
|
- return "";
|
||||||
|
- last = data->bv_val + data->bv_len - 1;
|
||||||
|
- for (s = data->bv_val; s < last; ++s) {
|
||||||
|
- if (SPECIAL(*s)) {
|
||||||
|
- char *first = data->bv_val;
|
||||||
|
- char *bufNext = buf;
|
||||||
|
- size_t bufSpace = BUFSIZ - 4;
|
||||||
|
- while (1) {
|
||||||
|
- /* printf ("%lu bytes ASCII\n", (unsigned long)(s - first)); */
|
||||||
|
- if (bufSpace < (size_t)(s - first))
|
||||||
|
- s = first + bufSpace - 1;
|
||||||
|
- if (s != first) {
|
||||||
|
- memcpy(bufNext, first, s - first);
|
||||||
|
- bufNext += (s - first);
|
||||||
|
- bufSpace -= (s - first);
|
||||||
|
- }
|
||||||
|
- do {
|
||||||
|
- if (bufSpace) {
|
||||||
|
- *bufNext++ = '\\';
|
||||||
|
- --bufSpace;
|
||||||
|
- }
|
||||||
|
- if (bufSpace < 2) {
|
||||||
|
- memcpy(bufNext, "..", 2);
|
||||||
|
- bufNext += 2;
|
||||||
|
- goto bail;
|
||||||
|
- }
|
||||||
|
- if (*s == '\\' || *s == '"') {
|
||||||
|
- *bufNext++ = *s;
|
||||||
|
- --bufSpace;
|
||||||
|
- } else {
|
||||||
|
- sprintf(bufNext, "%02x", (unsigned)*(unsigned char *)s);
|
||||||
|
- bufNext += 2;
|
||||||
|
- bufSpace -= 2;
|
||||||
|
- }
|
||||||
|
- } while (++s <= last && SPECIAL(*s));
|
||||||
|
- if (s > last)
|
||||||
|
- break;
|
||||||
|
- first = s;
|
||||||
|
- while (!SPECIAL(*s) && s <= last)
|
||||||
|
- ++s;
|
||||||
|
- }
|
||||||
|
- bail:
|
||||||
|
- *bufNext = '\0';
|
||||||
|
- /* printf ("%lu chars in buffer\n", (unsigned long)(bufNext - buf)); */
|
||||||
|
+ if (!data || !data->bv_val) {
|
||||||
|
+ strcpy(buf, "<NULL>");
|
||||||
|
+ return buf;
|
||||||
|
+ }
|
||||||
|
+ char *endbuff = &buf[BUFSIZ-4]; /* Reserve space to append "...\0" */
|
||||||
|
+ char *ptout = buf;
|
||||||
|
+ unsigned char *ptin = (unsigned char*) data->bv_val;
|
||||||
|
+ unsigned char *endptin = ptin+data->bv_len;
|
||||||
|
+
|
||||||
|
+ while (ptin < endptin) {
|
||||||
|
+ if (ptout >= endbuff) {
|
||||||
|
+ /*
|
||||||
|
+ * BUFSIZ(8K) > SLAPI_LOG_BUFSIZ(2K) so the error log message will be
|
||||||
|
+ * truncated anyway. So there is no real interrest to test if the original
|
||||||
|
+ * data contains no special characters and return it as is.
|
||||||
|
+ */
|
||||||
|
+ strcpy(endbuff, "...");
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
+ switch (encode_size[*ptin]) {
|
||||||
|
+ case 1:
|
||||||
|
+ *ptout++ = *ptin++;
|
||||||
|
+ break;
|
||||||
|
+ case 2:
|
||||||
|
+ *ptout++ = '\\';
|
||||||
|
+ *ptout++ = *ptin++;
|
||||||
|
+ break;
|
||||||
|
+ case 3:
|
||||||
|
+ sprintf(ptout, "\\%02x", *ptin++);
|
||||||
|
+ ptout += 3;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
- /* printf ("%lu bytes, all ASCII\n", (unsigned long)(s - data->bv_val)); */
|
||||||
|
- return data->bv_val;
|
||||||
|
+ *ptout = 0;
|
||||||
|
+ return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
--
|
||||||
|
2.44.0
|
||||||
|
|
Loading…
Reference in new issue