diff --git a/SOURCES/0048-CVE-2024-2199.patch b/SOURCES/0048-CVE-2024-2199.patch new file mode 100644 index 0000000..d980f8c --- /dev/null +++ b/SOURCES/0048-CVE-2024-2199.patch @@ -0,0 +1,108 @@ +From 23956cfb86a312318667fb9376322574fa8ec7f4 Mon Sep 17 00:00:00 2001 +From: James Chapman +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 + diff --git a/SOURCES/0049-CVE-2024-3657-7.9.patch b/SOURCES/0049-CVE-2024-3657-7.9.patch new file mode 100644 index 0000000..b12d55f --- /dev/null +++ b/SOURCES/0049-CVE-2024-3657-7.9.patch @@ -0,0 +1,148 @@ +From 7f5ac2097be424a55248e391c6b40635d01b1fa6 Mon Sep 17 00:00:00 2001 +From: Pierre Rogier +Date: Wed, 17 Apr 2024 18:18:04 +0200 +Subject: [PATCH] CVE-2024-3657-7.9 + +--- + ldap/servers/slapd/back-ldbm/index.c | 111 ++++++++++++++------------- + 1 file changed, 59 insertions(+), 52 deletions(-) + +diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c +index f0b969ff4..53a041ad1 100644 +--- a/ldap/servers/slapd/back-ldbm/index.c ++++ b/ldap/servers/slapd/back-ldbm/index.c +@@ -71,6 +71,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 +@@ -800,65 +826,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, ""); ++ 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.41.0 + diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec index 06b7f45..8b8058c 100644 --- a/SPECS/389-ds-base.spec +++ b/SPECS/389-ds-base.spec @@ -38,10 +38,28 @@ # set PIE flag %global _hardened_build 1 +# You might be surprised to see the 1.3.11 version in the filename, but inside, it's actually 1.3.10. +# Additionally, all of the patches below look intimidating, especially when one of them is bumping +# the version to 1.3.11, but the tarball already has the 1.3.11 version... +# +# [Simon] Explanation follows: +# It was done so because 389-ds-base-1.3.10+patches in RHEL 7.9 were not equal to +# 389-ds-base-1.3.10+all of the commits before I created 1.3.11. +# (I've made a full directory comparison, and a lot of stuff was missing - something from 2019, 2020 years) +# If we release a full rebase based on Upstream, we will find a lot of new issues, +# and we may break our customers in an unexpected way. +# That's not what we want in a stable, nearly EOF release... +# +# So what I did was I used the old tarball from 7.9, then I added the vendor directory to that and repacked. +# After that, I added the required patches (for the PBKDF2-SHA512 change and a couple more). +# +# Additionally, remember that you should use the following "--target" while doing the rhpkg build. It is needed for Rust: +# --target rhel-7.9-z-389-ds-base-stack-candidate + Summary: 389 Directory Server (%{variant}) Name: 389-ds-base Version: 1.3.11.1 -Release: %{?relprefix}4%{?prerel}%{?dist} +Release: %{?relprefix}5%{?prerel}%{?dist} License: GPLv3+ URL: https://www.port389.org/ Group: System Environment/Daemons @@ -206,6 +224,8 @@ Patch44: 0044-Issue-5565-Add-upgrade-script-for-new-Rust-password-.patc Patch45: 0045-Bump-version-to-1.3.11.1-1.patch Patch46: 0046-Issue-4551-Paged-search-impacts-performance-5838.patch Patch47: 0047-Issue-5984-Crash-when-paged-result-search-are-abando.patch +Patch48: 0048-CVE-2024-2199.patch +Patch49: 0049-CVE-2024-3657-7.9.patch %description 389 Directory Server is an LDAPv3 compliant server. The base package includes @@ -571,6 +591,11 @@ fi %{_sysconfdir}/%{pkgname}/dirsrvtests %changelog +* Wed Apr 24 2024 James Chapman - 1.3.11.1-5 +- Bump version to 1.3.11.1-5 +- Resolves: RHEL-33337 - redhat-ds:11/389-ds-base: potential denial of service via specially crafted kerberos AS-REQ request +- Resolves: RHEL-34817 - redhat-ds:11/389-ds-base: Malformed userPassword may cause crash at do_modify in slapd/modify.c + * Wed Dec 13 2023 Thierry Bordaz - 1.3.11.1-4 - Bump version to 1.3.11.1-4 - Resolves: RHEL-17332 - ns-slapd crash in slapi_attr_basetype